diff options
Diffstat (limited to 'map-server')
| -rw-r--r-- | map-server/Makefile.am | 1 | ||||
| -rw-r--r-- | map-server/altos-map/.gitignore | 1 | ||||
| -rw-r--r-- | map-server/altos-map/Makefile.am | 6 | ||||
| -rwxr-xr-x | map-server/altos-map/altos-map-fake | 5 | ||||
| -rw-r--r-- | map-server/altos-map/altos-map.c | 293 | ||||
| -rw-r--r-- | map-server/altos-mapd/.gitignore | 6 | ||||
| -rw-r--r-- | map-server/altos-mapd/AltosMapd.java | 235 | ||||
| -rw-r--r-- | map-server/altos-mapd/AltosMapdClient.java | 156 | ||||
| -rw-r--r-- | map-server/altos-mapd/AltosMapdPreferences.java | 85 | ||||
| -rw-r--r-- | map-server/altos-mapd/AltosMapdServer.java | 24 | ||||
| -rw-r--r-- | map-server/altos-mapd/Makefile.am | 73 | ||||
| -rw-r--r-- | map-server/altos-mapd/Manifest.txt | 2 | ||||
| -rw-r--r-- | map-server/altos-mapd/altos-mapd-default | 4 | ||||
| -rw-r--r-- | map-server/altos-mapd/altos-mapd.service | 14 | ||||
| -rw-r--r-- | map-server/altos-mapj/.gitignore | 6 | ||||
| -rw-r--r-- | map-server/altos-mapj/AltosMap.java | 182 | ||||
| -rw-r--r-- | map-server/altos-mapj/Makefile.am | 62 | ||||
| -rw-r--r-- | map-server/altos-mapj/Manifest.txt | 2 | ||||
| -rwxr-xr-x | map-server/altos-mapj/altos-mapj-fake | 6 | 
19 files changed, 1163 insertions, 0 deletions
diff --git a/map-server/Makefile.am b/map-server/Makefile.am new file mode 100644 index 00000000..39ff8014 --- /dev/null +++ b/map-server/Makefile.am @@ -0,0 +1 @@ +SUBDIRS=altos-mapd altos-map altos-mapj diff --git a/map-server/altos-map/.gitignore b/map-server/altos-map/.gitignore new file mode 100644 index 00000000..1158b56a --- /dev/null +++ b/map-server/altos-map/.gitignore @@ -0,0 +1 @@ +altos-map diff --git a/map-server/altos-map/Makefile.am b/map-server/altos-map/Makefile.am new file mode 100644 index 00000000..6925957b --- /dev/null +++ b/map-server/altos-map/Makefile.am @@ -0,0 +1,6 @@ +bin_PROGRAMS = altos-map + +altos_map_SOURCES = altos-map.c + +altos_map_LDADD = $(JANSSON_LIBS) +altos_map_CFLAGS = $(JANSSON_CFLAGS) $(WARN_CFLAGS) diff --git a/map-server/altos-map/altos-map-fake b/map-server/altos-map/altos-map-fake new file mode 100755 index 00000000..1bf5f8e2 --- /dev/null +++ b/map-server/altos-map/altos-map-fake @@ -0,0 +1,5 @@ +#!/bin/sh +# map-N43.799102,W120.586281-hybrid-20.jpg +export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20" +export REMOTE_ADDR="127.0.0.1" +./altos-map diff --git a/map-server/altos-map/altos-map.c b/map-server/altos-map/altos-map.c new file mode 100644 index 00000000..38701076 --- /dev/null +++ b/map-server/altos-map/altos-map.c @@ -0,0 +1,293 @@ +/* + * Copyright © 2018 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +#include <jansson.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/ip.h> + +#define ALTOS_MAP_PORT	16717 + +#define MISSING		0x7fffffff + +#define ALTOS_MAP_PROTOCOL_VERSION	"1.0.0" + +static char * +reason_string(int code) +{ +	switch (code) { +	case 200: +		return "OK"; +	case 400: +		return "Bad Request"; +	case 403: +		return "Forbidden"; +	case 404: +		return "Not Found"; +	case 408: +		return "Request Timeout"; +	default: +		return "Failure"; +	} +} + +static void +write_status(int status) +{ +	printf("Status: %d %s\n", status, reason_string(status)); +} + +static void +write_type(char * type) +{ +	printf("Content-Type: %s\n", type); +} + +static void +fail(int status, char *format, ...) +{ +	va_list ap; + +	write_status(status); +	write_type("text/html"); +	printf("\n"); +	printf("<html>\n"); +	printf("<head><title>Map Fetch Failure</title></head>\n"); +	printf("<body>\n"); +	va_start(ap, format); +	vprintf(format, ap); +	va_end(ap); +	printf("</body>\n"); +	printf("</html>\n"); +	exit(1); +} + +static char * +getenv_copy(const char *name) +{ +	const char *value = getenv(name); + +	if (!value) +		return NULL; + +	return strdup(value); +} + +static double +parse_double(char *string) +{ +	char *end; +	double value; + +	value = strtod(string, &end); +	if (*end) +		fail(400, "Invalid double %s", string); +	return value; +} + +static int +parse_int(char *string) +{ +	char *end; +	long int value; + +	value = strtol(string, &end, 10); +	if (*end) +		fail(400, "Invalid int %s", string); +	if (value < INT_MIN || INT_MAX < value) +		fail(400, "Int value out of range %ld", value); +	return (int) value; +} + + +static int +connect_service(void) +{ +	struct sockaddr_in	altos_map_addr = { +		.sin_family = AF_INET, +		.sin_port = htons(ALTOS_MAP_PORT), +		.sin_addr = { +			.s_addr = htonl(INADDR_LOOPBACK), +		}, +	}; + +	int	s = socket(AF_INET, SOCK_STREAM, 0); + +	if (s < 0) +		return -1; + +	if (connect (s, (const struct sockaddr *) &altos_map_addr, sizeof (altos_map_addr)) < 0) { +		close (s); +		return -1; +	} + +	return s; +} + +int main(int argc, char **argv) +{ + +	char *query_string = getenv_copy("QUERY_STRING"); + +	if (query_string == NULL) +		fail(400, "%s", "Missing query string"); + +	char *remote_addr = getenv_copy("REMOTE_ADDR"); + +	if (remote_addr == NULL) +		fail(400, "%s", "Missing remote address"); + +	double	lon = MISSING; +	double	lat = MISSING; +	int	zoom = MISSING; +	char	*version = NULL; + +	char	*query, *query_save = NULL; +	char	*query_start = query_string; + +	while ((query = strtok_r(query_start, "&", &query_save)) != NULL) { +		query_start = NULL; + +		char	*token, *token_save = NULL; +		char	*token_start = query; + +		char	*name = NULL; +		char	*value = NULL; + +		while ((token = strtok_r(token_start, "=", &token_save)) != NULL) { +			token_start = NULL; +			if (name == NULL) +				name = token; +			else if (value == NULL) +				value = token; +			else +				break; +		} + +		if (name && value) { +			if (!strcmp(name, "lon")) +				lon = parse_double(value); +			else if (!strcmp(name, "lat")) +				lat = parse_double(value); +			else if (!strcmp(name, "zoom")) +				zoom = parse_int(value); +			else if (!strcmp(name, "version")) +				version = value; +			else +				fail(400, "Extra query param \"%s\"", query); +		} +	} + +	if (version != NULL) { +		printf("Content-Type: text/plain\n"); +		printf("\n"); +		printf("%s\n", ALTOS_MAP_PROTOCOL_VERSION); +		return 0; +	} +	if (lon == MISSING) +		fail(400, "Missing longitude"); +	if (lat == MISSING) +		fail(400, "Missing latitude"); +	if (zoom == MISSING) +		fail(400, "Missing zoom"); + +	int	s = -1; +	int	tries = 0; + +	while (tries < 10 && s < 0) { +		s = connect_service(); +		if (s < 0) { +			usleep(100 * 1000); +			tries++; +		} +	} + +	if (s < 0) +		fail(408, "Cannot connect AltOS map daemon"); + +	FILE	*sf = fdopen(s, "r+"); + +	if (sf == NULL) +		fail(400, "allocation failure"); + +	json_t *request = json_pack("{s:f s:f s:i s:s}", "lat", lat, "lon", lon, "zoom", zoom, "remote_addr", remote_addr); + +	if (request == NULL) +		fail(400, "Cannot create JSON request"); + +	if (json_dumpf(request, sf, 0) < 0) +		fail(400, "Cannot write JSON request"); + +	fflush(sf); + +	json_error_t	error; +	json_t 		*reply = json_loadf(sf, 0, &error); + +	if (!reply) +		fail(400, "Cannot read JSON reply"); + +	int	status; + +	if (json_unpack(reply, "{s:i}", "status", &status) < 0) +		fail(400, "No status returned"); + +	if (status != 200) +		fail(status, "Bad cache status"); + +	char	*filename, *content_type; + +	if (json_unpack(reply, "{s:s s:s}", "filename", &filename, "content_type", &content_type) < 0) +		fail(400, "JSON reply parse failure"); + +	int	fd = open(filename, O_RDONLY); + +	if (fd < 0) +		fail(400, "%s: %s", filename, strerror(errno)); + +	struct stat	statb; + +	if (fstat(fd, &statb) < 0) +		fail(400, "%s: %s", filename, strerror(errno)); + +	printf("Content-Type: %s\n", content_type); +	printf("Content-Length: %lu\n", (unsigned long) statb.st_size); +	printf("\n"); +	fflush(stdout); + +	char	buf[4096]; +	ssize_t	bytes_read; + +	while ((bytes_read = read(fd, buf, sizeof (buf))) > 0) { +		ssize_t total_write = 0; +		while (total_write < bytes_read) { +			ssize_t bytes_write = write(1, buf + total_write, bytes_read - total_write); +			if (bytes_write <= 0) +				return 1; +			total_write += bytes_write; +		} +	} +	if (bytes_read < 0) +		return 1; +	return 0; +} diff --git a/map-server/altos-mapd/.gitignore b/map-server/altos-mapd/.gitignore new file mode 100644 index 00000000..5f5ce0ae --- /dev/null +++ b/map-server/altos-mapd/.gitignore @@ -0,0 +1,6 @@ +*.stamp +*.jar +altos-mapd +altos-mapd-jdb +altos-mapd-test +classes diff --git a/map-server/altos-mapd/AltosMapd.java b/map-server/altos-mapd/AltosMapd.java new file mode 100644 index 00000000..29528541 --- /dev/null +++ b/map-server/altos-mapd/AltosMapd.java @@ -0,0 +1,235 @@ +/* + * Copyright © 2018 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +package altosmapd; + +import java.net.*; +import java.io.*; +import java.text.*; +import java.util.*; +import java.util.concurrent.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapd implements AltosLaunchSiteListener { + +	public static int port = 16717; + +	public final static int maptype = AltosMap.maptype_hybrid; + +	public final static int px_size = 512; + +	public final static int scale = 1; + +	public static int max_zoom = 17; + +	public static double valid_radius = 17000;	/* 17km */ + +	public String map_dir = null; +	public String launch_sites_file = null; +	public String key_file = null; + +	public void usage() { +		System.out.printf("usage: altos-mapd [--mapdir <map-directory] [--launch-sites <launch-sites-file>]\n" + +				  "                  [--radius <valid-radius-m> [--port <port>] [--key <key-file>]\n" + +				  "                  [--max-zoom <max-zoom-level>\n"); +		System.exit(1); +	} + +	private static Semaphore launch_sites_ready; + +	private static List<AltosLaunchSite> launch_sites; + +	public void notify_launch_sites(List<AltosLaunchSite> sites) { +		synchronized (launch_sites_ready) { +			if (sites != null) { +				launch_sites = sites; +				launch_sites_ready.release(); +			} +		} +	} + +	private static boolean west_of(double a, double b) { +		double	diff = b - a; + +		while (diff >= 360.0) +			diff -= 360.0; +		while (diff <= -360.0) +			diff += 360.0; + +		return diff >= 0; +	} + +	public static boolean check_lat_lon(double lat, double lon, int zoom) { + +		if (zoom > max_zoom) +			return false; + +		AltosMapTransform	transform = new AltosMapTransform(px_size, px_size, zoom, new AltosLatLon(lat, lon)); + +		AltosLatLon		upper_left = transform.screen_lat_lon(new AltosPointInt(0, 0)); +		AltosLatLon		lower_right = transform.screen_lat_lon(new AltosPointInt(px_size, px_size)); + +		synchronized (launch_sites_ready) { +			if (launch_sites == null) { +				try { +					launch_sites_ready.acquire(); +				} catch (InterruptedException ie) { +					return false; +				} +			} +		} +		if (launch_sites == null) { +			System.out.printf("No launch site data available, refusing all requests\n"); +			return false; +		} + +		for (AltosLaunchSite site : launch_sites) { + +			/* Figure out which point in the tile to +			 * measure to the site That's one of the edges +			 * or the site location depend on where the +			 * site is in relation to the tile +			 */ + +			double		check_lon; + +			if (west_of(site.longitude, upper_left.lon)) +				check_lon = upper_left.lon; +			else if (west_of(lower_right.lon, site.longitude)) +				check_lon = lower_right.lon; +			else +				check_lon = site.longitude; + +			double		check_lat; + +			if (site.latitude < lower_right.lat) +				check_lat = lower_right.lat; +			else if (upper_left.lat < site.latitude) +				check_lat = upper_left.lat; +			else +				check_lat = site.latitude; + +			AltosGreatCircle gc = new AltosGreatCircle(site.latitude, site.longitude, +								   check_lat, check_lon); + +			if (gc.distance <= valid_radius) +				return true; +		} + +		return false; +	} + +	AltosMapdServer	server; + +	public void process(String[] args) { + +		AltosPreferences.init(new AltosMapdPreferences()); + +		int skip = 1; +		for (int i = 0; i < args.length; i += skip) { +			skip = 1; +			if (args[i].equals("--mapdir") && i < args.length-1) { +				map_dir = args[i+1]; +				skip = 2; +			} else if (args[i].equals("--launch-sites") && i < args.length-1) { +				launch_sites_file = args[i+1]; +				skip = 2; +			} else if (args[i].equals("--radius") && i < args.length-1) { +				try { +					valid_radius = AltosParse.parse_double_locale(args[i+1]); +				} catch (ParseException pe) { +					usage(); +				} +				skip = 2; +			} else if (args[i].equals("--port") && i < args.length-1) { +				try { +					port = AltosParse.parse_int(args[i+1]); +				} catch (ParseException pe) { +					usage(); +				} +				skip = 2; +			} else if (args[i].equals("--key") && i < args.length-1) { +				key_file = args[i+1]; +				skip = 2; +			} else if (args[i].equals("--max-zoom") && i < args.length-1) { +				try { +					max_zoom = AltosParse.parse_int(args[i+1]); +				} catch (ParseException pe) { +					usage(); +				} +				skip = 2; +			} else { +				usage(); +			} +		} + +		if (map_dir == null) +			usage(); + +		if (key_file != null) { +			try { +				BufferedReader key_reader = new BufferedReader(new FileReader(key_file)); + +				String line = key_reader.readLine(); +				if (line == null || line.length() != 39) { +					System.out.printf("%s: invalid contents %d \"%s\"\n", +							  key_file, line.length(), line); +					usage(); +				} +				key_reader.close(); +				AltosMapStore.google_maps_api_key = line; +			} catch (Exception e) { +				System.out.printf("%s: %s\n", key_file, e.toString()); +				usage(); +			} +		} + +		AltosPreferences.mapdir = new File(map_dir); + +		if (launch_sites_file != null) +			AltosLaunchSites.launch_sites_url = "file://" + launch_sites_file; + +		launch_sites_ready = new Semaphore(0); + +		new AltosLaunchSites(this); + +		try { +			server = new AltosMapdServer(port); +		} catch (IOException ie) { +			System.out.printf("Cannot bind to port %d: %s\n", port, ie.toString()); +			usage(); +		} + +		for (;;) { +			try { +				Socket client = server.accept(); +				if (client == null) { +					System.out.printf("accept failed\n"); +					continue; +				} +				new AltosMapdClient(client); +			} catch (Exception e) { +				System.out.printf("Exception %s\n", e.toString()); +			} +		} +	} + +	public void AltosMapd() { +	} + +	public static void main(final String[] args) { +		new AltosMapd().process(args); +	} +} diff --git a/map-server/altos-mapd/AltosMapdClient.java b/map-server/altos-mapd/AltosMapdClient.java new file mode 100644 index 00000000..6c95da8f --- /dev/null +++ b/map-server/altos-mapd/AltosMapdClient.java @@ -0,0 +1,156 @@ +/* + * Copyright © 2018 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +package altosmapd; + +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.io.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapdClient extends Thread implements AltosMapStoreListener { +	private Socket		socket; +	private AltosJson	request; +	private AltosJson	reply; + +	private int		http_status; + +	private void set_status(int status) { +		http_status = status; +		reply.put("status", status); +	} + +	private void set_filename(String filename) { +		reply.put("filename", filename); + +	} + +	private void set_content_type(String content_type) { +		reply.put("content_type", content_type); +	} + +	private String content_type(File file) { +		String content_type = "application/octet-stream"; +		String basename = file.getName(); +		if (basename.endsWith(".jpg")) +			content_type = "image/jpeg"; +		if (basename.endsWith(".png")) +			content_type = "image/png"; +		return content_type; +	} + +	private void set_file(File file) { +		set_filename(file.getAbsolutePath()); +		set_content_type(content_type(file)); +	} + +	private Semaphore store_ready; + +	public void notify_store(AltosMapStore map_store, int status) { +		if (status != AltosMapTile.fetching) +			store_ready.release(); +	} + +	public void run() { +		reply = new AltosJson(); +		try { +			request = AltosJson.fromInputStream(socket.getInputStream()); + +			if (request == null) { +				set_status(400); +				System.out.printf("client failed %d\n", http_status); +			} else { + +				double	lat = request.get_double("lat", AltosLib.MISSING); +				double	lon = request.get_double("lon", AltosLib.MISSING); +				int	zoom = request.get_int("zoom", AltosLib.MISSING); +				String	addr = request.get_string("remote_addr", null); + +				if (lat == AltosLib.MISSING || +				    lon == AltosLib.MISSING || +				    zoom == AltosLib.MISSING || +				    addr == null) +				{ +					set_status(400); +				} else if (!AltosMapd.check_lat_lon(lat, lon, zoom)) { +					set_status(403);	/* Forbidden */ +				} else { + +					store_ready = new Semaphore(0); + +					AltosMapStore	map_store = AltosMapStore.get(new AltosLatLon(lat, lon), +										      zoom, +										      AltosMapd.maptype, +										      AltosMapd.px_size, +										      AltosMapd.scale); +					int status; + +					if (map_store == null) { +						status = AltosMapTile.failed; +					} else { +						map_store.add_listener(this); + +						try { +							store_ready.acquire(); +						} catch (Exception ie) { +						} + +						status = map_store.status(); +					} + +					if (status == AltosMapTile.fetched || status == AltosMapTile.loaded) { +						set_status(200); +						set_file(map_store.file); +					} else if (status == AltosMapTile.failed) { +						set_status(404); +					} else if (status == AltosMapTile.fetching) { +						set_status(408); +					} else if (status == AltosMapTile.bad_request) { +						set_status(400); +					} else if (status == AltosMapTile.forbidden) { +						set_status(403); +					} else { +						set_status(400); +					} +				} +				System.out.printf("%s: %.6f %.6f %d status %d\n", +						  addr, lat, lon, zoom, http_status); +			} +		} catch (Exception e) { +			System.out.printf("client exception %s\n", e.toString()); +			e.printStackTrace(System.out); +			set_status(400); + +		} finally { +			try { +				Writer writer = new PrintWriter(socket.getOutputStream()); +				reply.write(writer); +				writer.write('\n'); +				writer.flush(); +			} catch (IOException ie) { +			} +			try { +				socket.close(); +			} catch (IOException ie) { +			} +		} +	} + +	public AltosMapdClient(Socket socket) { +		this.socket = socket; +		start(); +	} +} diff --git a/map-server/altos-mapd/AltosMapdPreferences.java b/map-server/altos-mapd/AltosMapdPreferences.java new file mode 100644 index 00000000..fcfe3261 --- /dev/null +++ b/map-server/altos-mapd/AltosMapdPreferences.java @@ -0,0 +1,85 @@ +/* + * Copyright © 2018 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +package altosmapd; + +import java.io.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMapdPreferences extends AltosPreferencesBackend { + +	public String  getString(String key, String def) { +		return def; +	} +	public void    putString(String key, String value) { +	} + +	public int     getInt(String key, int def) { +		return def; +	} + +	public void    putInt(String key, int value) { +	} + +	public double  getDouble(String key, double def) { +		return def; +	} + +	public void    putDouble(String key, double value) { +	} + +	public boolean getBoolean(String key, boolean def) { +		return def; +	} + +	public void    putBoolean(String key, boolean value) { +	} + +	public byte[]  getBytes(String key, byte[] def) { +		return def; +	} + +	public void    putBytes(String key, byte[] value) { +	} + +	public boolean nodeExists(String key) { +		return false; +	} + +	public AltosPreferencesBackend node(String key) { +		return this; +	} + +	public String[] keys() { +		return null; +	} + +	public void    remove(String key) { +	} + +	public void    flush() { +	} + +	public File homeDirectory() { +		return new File ("."); +	} + +	public void debug(String format, Object ... arguments) { +		System.out.printf(format, arguments); +	} + +	public AltosMapdPreferences() { +	} +} diff --git a/map-server/altos-mapd/AltosMapdServer.java b/map-server/altos-mapd/AltosMapdServer.java new file mode 100644 index 00000000..db7f7dc1 --- /dev/null +++ b/map-server/altos-mapd/AltosMapdServer.java @@ -0,0 +1,24 @@ +/* + * Copyright © 2018 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +package altosmapd; + +import java.net.*; +import java.io.*; + +public class AltosMapdServer extends ServerSocket { +	public AltosMapdServer(int port) throws IOException { +		super(port, 256, InetAddress.getLoopbackAddress()); +	} +} diff --git a/map-server/altos-mapd/Makefile.am b/map-server/altos-mapd/Makefile.am new file mode 100644 index 00000000..39f67536 --- /dev/null +++ b/map-server/altos-mapd/Makefile.am @@ -0,0 +1,73 @@ +JAVAROOT=classes +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 + +altoslibdir=$(libdir)/altos + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*" + +bin_SCRIPTS=altos-mapd + +altosmapd_JAVA = \ +	AltosMapd.java \ +	AltosMapdServer.java \ +	AltosMapdClient.java \ +	AltosMapdPreferences.java + +ALTOSLIB_CLASS=\ +	altoslib_$(ALTOSLIB_VERSION).jar + +JAR=altosmapd.jar + +FATJAR=altosmapd-fat.jar + +all-local: classes/altosmapd $(JAR) altos-mapd altos-mapd-test altos-mapd-jdb + +defaultsdir=$(sysconfdir)/default + +defaults_DATA=altos-mapd-default + +systemddir=$(libdir)/systemd/system + +systemd_DATA=altos-mapd.service + +install-altosmapdJAVA: altosmapd.jar +	@$(NORMAL_INSTALL) +	test -z "$(altosmapddir)" || $(MKDIR_P) "$(DESTDIR)$(altosmapddir)" +	echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosmapddir)/altosdmap.jar'"; \ +	$(INSTALL_DATA) "$<" "$(DESTDIR)$(altosmapddir)" + +classes/altosmapd: +	mkdir -p classes/altosmapd + +$(JAR): classaltosmapd.stamp Manifest.txt $(ALTOSLIB_CLASS) +	jar cfm $@ Manifest.txt \ +		-C classes altosmapd + +altosmapddir=$(datadir)/java + +$(FATJAR): classaltosmapd.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) +	jar cfm $@ Manifest-fat.txt \ +		-C classes altosmapd + +altos-mapd: Makefile +	echo "#!/bin/sh" > $@ +	echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapddir)/altosmapd.jar" "$$@"' >> $@ +	chmod +x $@ + +altos-mapd-test: Makefile +	echo '#!/bin/sh' > $@ +	echo 'dir="$$(dirname $$0)"' >> $@ +	echo 'cd "$$dir"' >> $@ +	echo 'altosmapd="$$(pwd -P)"' >> $@ +	echo 'exec java -jar "$$altosmapd/altosmapd.jar" "$$@"' >> $@ +	chmod +x $@ + +altos-mapd-jdb: Makefile +	echo "#!/bin/sh" > $@ +	echo 'exec jdb altosmapd/AltosMapd "$$@"' >> $@ +	chmod +x $@ + +$(ALTOSLIB_CLASS): +	-rm -f "$@" +	$(LN_S) ../../altoslib/"$@" . + diff --git a/map-server/altos-mapd/Manifest.txt b/map-server/altos-mapd/Manifest.txt new file mode 100644 index 00000000..42c0313b --- /dev/null +++ b/map-server/altos-mapd/Manifest.txt @@ -0,0 +1,2 @@ +Main-Class: altosmapd.AltosMapd +Class-Path: altoslib_13.jar diff --git a/map-server/altos-mapd/altos-mapd-default b/map-server/altos-mapd/altos-mapd-default new file mode 100644 index 00000000..1611f0b6 --- /dev/null +++ b/map-server/altos-mapd/altos-mapd-default @@ -0,0 +1,4 @@ +MAPKEY=/home/altos-mapd/google-maps-api-key +MAPDIR=/home/altos-mapd/maps +LAUNCHSITES=/var/www/html/launch-sites.txt +MAXZOOM=17 diff --git a/map-server/altos-mapd/altos-mapd.service b/map-server/altos-mapd/altos-mapd.service new file mode 100644 index 00000000..ba263c2a --- /dev/null +++ b/map-server/altos-mapd/altos-mapd.service @@ -0,0 +1,14 @@ +[Unit] +Description=AltOS Map Cache +Requires=network-online.target +After=network-online.target + +[Service] +Type=simple +User=altos-mapd +Restart=always +EnvironmentFile=/etc/default/altos-mapd-default +ExecStart=/usr/bin/altos-mapd --key $MAPKEY --mapdir $MAPDIR --launch-sites $LAUNCHSITES --max-zoom $MAXZOOM + +[Install] +WantedBy=multi-user.target diff --git a/map-server/altos-mapj/.gitignore b/map-server/altos-mapj/.gitignore new file mode 100644 index 00000000..c5d593e6 --- /dev/null +++ b/map-server/altos-mapj/.gitignore @@ -0,0 +1,6 @@ +altos-mapj +altos-mapj-jdb +altos-mapj-test +*.jar +*.stamp +classes diff --git a/map-server/altos-mapj/AltosMap.java b/map-server/altos-mapj/AltosMap.java new file mode 100644 index 00000000..ad0a4f5f --- /dev/null +++ b/map-server/altos-mapj/AltosMap.java @@ -0,0 +1,182 @@ +/* + * Copyright © 2018 Keith Packard <keithp@keithp.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + */ + +package altosmap; + +import java.net.*; +import java.io.*; +import java.util.*; +import java.text.*; + +import org.altusmetrum.altoslib_13.*; + +public class AltosMap { + +	public final static int port = 16717; + +	public final static String protocol_version = "1.0.0"; + +	String	query_string; +	String	remote_addr; + +	public String reason_string(int code) { +		switch (code) { +		case 200: +			return "OK"; +		case 400: +			return "Bad Request"; +		case 403: +			return "Forbidden"; +		case 404: +			return "Not Found"; +		case 408: +			return "Request Timeout"; +		default: +			return "Failure"; +		} +	} + +	public void write_status(int status) { +		System.out.printf("Status: %d %s\n", status, reason_string(status)); +	} + +	public void write_type(String type) { +		System.out.printf("Content-Type: %s\n", type); +	} + +	public void fail(int status, String reason) { +		write_status(status); +		write_type("text/html"); +		System.out.printf("\n"); +		System.out.printf("<html>\n"); +		System.out.printf("<head><title>Map Fetch Failure</title></head>\n"); +		System.out.printf("<body>%s</body>\n", reason); +		System.out.printf("</html>\n"); +		System.exit(1); +	} + +	public void process() { +		query_string = System.getenv("QUERY_STRING"); + +		if (query_string == null) +			fail(400, "Missing query string"); + +		remote_addr = System.getenv("REMOTE_ADDR"); + +		if (remote_addr == null) +			fail(400, "Missing remote address"); + +		String[] queries = query_string.split("&"); + +		double	lon = AltosLib.MISSING; +		double	lat = AltosLib.MISSING; +		int	zoom = AltosLib.MISSING; +		String	version = null; + +		try { +			for (String query : queries) { +				String[] q = query.split("="); +				if (q.length >= 2) { +					String name = q[0]; +					String value = q[1]; +					if (name.equals("lon")) +						lon = AltosParse.parse_double_net(value); +					else if (name.equals("lat")) +						lat = AltosParse.parse_double_net(value); +					else if (name.equals("zoom")) +						zoom = AltosParse.parse_int(value); +					else if (name.equals("version")) +						version = value; +					else +						fail(400, String.format("Extra query param \"%s\"", query)); +				} +			} +		} catch (ParseException pe) { +			fail(400, String.format("Invalid query: %s", pe.toString())); +		} + +		if (version != null) { +			System.out.printf("Content-Type: text/plain\n"); +			System.out.printf("\n"); +			System.out.printf("%s\n", protocol_version); +		} else { +			if (lon == AltosLib.MISSING) +				fail(400, "Missing longitude"); +			if (lat == AltosLib.MISSING) +				fail(400, "Missing latitude"); +			if (zoom == AltosLib.MISSING) +				fail(400, "Missing zoom"); + +			try { +				Socket	socket = null; +				int tries = 0; + +				while (tries < 10 && socket == null) { +					try { +						socket = new Socket(InetAddress.getLoopbackAddress(), port); +					} catch (IOException ie) { +						Thread.sleep(100); +						tries++; +					} +				} + +				AltosJson	request = new AltosJson(); + +				request.put("lat", lat); +				request.put("lon", lon); +				request.put("zoom", zoom); +				request.put("remote_addr", remote_addr); + +				Writer writer = new PrintWriter(socket.getOutputStream()); +				request.write(writer); +				writer.flush(); + +				AltosJson	reply = AltosJson.fromInputStream(socket.getInputStream()); + +				int status = reply.get_int("status", 400); + +				if (status != 200) +					fail(status, "Bad cache status"); + +				String filename = reply.get_string("filename", null); +				try { +					File file = new File(filename); +					long length = file.length(); +					FileInputStream in = new FileInputStream(file); +					String content_type = reply.get_string("content_type", null); +					System.out.printf("Content-Type: %s\n", content_type); +					System.out.printf("Content-Length: %d\n", file.length()); +					System.out.printf("\n"); +					byte[] buf = new byte[4096]; +					int bytes_read; +					while ((bytes_read = in.read(buf)) > 0) +						System.out.write(buf); +				} catch (IOException ie) { +					fail(404, String.format("IO Exception: %s", ie.toString())); +				} +			} catch (Exception e) { +				fail(404, String.format("Exception %s", e.toString())); +			} +		} +	} + +	public AltosMap() { +	} + +	public static void main(final String[] args) { + +		new AltosMap().process(); + +	} +} diff --git a/map-server/altos-mapj/Makefile.am b/map-server/altos-mapj/Makefile.am new file mode 100644 index 00000000..f447adb6 --- /dev/null +++ b/map-server/altos-mapj/Makefile.am @@ -0,0 +1,62 @@ +JAVAROOT=classes +AM_JAVACFLAGS=-target 1.6 -encoding UTF-8 -Xlint:deprecation -Xlint:unchecked -source 6 + +altoslibdir=$(libdir)/altos + +CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="$(JAVAROOT):../../altoslib/*" + +bin_SCRIPTS=altos-mapj + +altosmap_JAVA = \ +	AltosMap.java + +ALTOSLIB_CLASS=\ +	altoslib_$(ALTOSLIB_VERSION).jar + +JAR=altosmap.jar + +FATJAR=altosmap-fat.jar + +all-local: classes/altosmap $(JAR) altos-mapj altos-mapj-test altos-mapj-jdb + +install-altosmapJAVA: altosmap.jar +	@$(NORMAL_INSTALL) +	test -z "$(altosmapdir)" || $(MKDIR_P) "$(DESTDIR)$(altosmapdir)" +	echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosmapdir)/altosmap.jar'"; \ +	$(INSTALL_DATA) "$<" "$(DESTDIR)$(altosmapdir)" + +classes/altosmap: +	mkdir -p classes/altosmap + +$(JAR): classaltosmap.stamp Manifest.txt $(ALTOSLIB_CLASS) +	jar cfm $@ Manifest.txt \ +		-C classes altosmap + +altosmapdir=$(datadir)/java + +$(FATJAR): classaltosmap.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) +	jar cfm $@ Manifest-fat.txt \ +		-C classes altosmap + +altos-mapj: Makefile +	echo "#!/bin/sh" > $@ +	echo 'exec java -Djava.library.path="$(altoslibdir)" -jar "$(altosmapdir)/altosmap.jar" "$$@"' >> $@ +	chmod +x $@ + +altos-mapj-test: Makefile +	echo '#!/bin/sh' > $@ +	echo 'dir="$$(dirname $$0)"' >> $@ +	echo 'cd "$$dir"' >> $@ +	echo 'altosmap="$$(pwd -P)"' >> $@ +	echo 'exec java -jar "$$altosmap/altosmap.jar" "$$@"' >> $@ +	chmod +x $@ + +altos-mapj-jdb: Makefile +	echo "#!/bin/sh" > $@ +	echo 'exec jdb altosmap/AltosMap "$$@"' >> $@ +	chmod +x $@ + +$(ALTOSLIB_CLASS): +	-rm -f "$@" +	$(LN_S) ../../altoslib/"$@" . + diff --git a/map-server/altos-mapj/Manifest.txt b/map-server/altos-mapj/Manifest.txt new file mode 100644 index 00000000..1a285b40 --- /dev/null +++ b/map-server/altos-mapj/Manifest.txt @@ -0,0 +1,2 @@ +Main-Class: altosmap.AltosMap +Class-Path: altoslib_13.jar diff --git a/map-server/altos-mapj/altos-mapj-fake b/map-server/altos-mapj/altos-mapj-fake new file mode 100755 index 00000000..f600fec7 --- /dev/null +++ b/map-server/altos-mapj/altos-mapj-fake @@ -0,0 +1,6 @@ +#!/bin/sh +# map-N43.799102,W120.586281-hybrid-20.jpg +export QUERY_STRING="lat=43.799102&lon=-120.586281&zoom=20" +export REMOTE_ADDR="127.0.0.1" +./altos-mapj-test +  | 
