/* / wmslite / / a light-weight WMS server supporting RasterLite2 DataSources / / version 1.0, 2014 January 29 / / Author: Sandro Furieri a.furieri@lqt.it / / Copyright (C) 2014 Alessandro Furieri / / 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 3 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. / / You should have received a copy of the GNU General Public License / along with this program. If not, see . / */ #include #include #include #include #include #include #include #ifdef _WIN32 /* This code is for win32 only */ #include #include #else /* this code is for any sane minded system (*nix) */ #include #include #include #include #include #include #endif #include #include #define ARG_NONE 0 #define ARG_DB_PATH 1 #define ARG_IP_PORT 2 #define ARG_CACHE_SIZE 3 #define WMS_ILLEGAL_REQUEST 0 #define WMS_GET_CAPABILITIES 1 #define WMS_GET_MAP 2 #define WMS_UNKNOWN -1 #define WMS_TRANSPARENT 10 #define WMS_OPAQUE 11 #define WMS_INVALID_CRS 101 #define WMS_INVALID_DIMENSION 102 #define WMS_INVALID_BBOX 103 #define WMS_INVALID_LAYER 104 #define WMS_INVALID_GROUP 105 #define WMS_INVALID_BGCOLOR 106 #define WMS_INVALID_STYLE 107 #define WMS_INVALID_FORMAT 108 #define WMS_INVALID_TRANSPARENT 109 #define WMS_NOT_EXISTING_LAYER 110 #define WMS_LAYER_OUT_OF_BBOX 111 #define WMS_MISMATCHING_SRID 112 #define WMS_VERSION_UNKNOWN 0 #define WMS_VERSION_100 100 #define WMS_VERSION_110 110 #define WMS_VERSION_111 111 #define WMS_VERSION_130 130 #define CONNECTION_INVALID 0 #define CONNECTION_AVAILABLE 1 #define CONNECTION_BUSY 2 #define LOG_SLOT_AVAILABLE -100 #define LOG_SLOT_BUSY -200 #define LOG_SLOT_READY -300 #define SEND_BLOK_SZ 8192 #define MAX_CONN 8 #define MAX_LOG 256 #ifndef timersub #define timersub(a, b, result) \ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ if ((result)->tv_usec < 0) { \ --(result)->tv_sec; \ (result)->tv_usec += 1000000; \ } \ #endif int debug_mode = 0; struct glob_var { /* global objects */ sqlite3 *handle; sqlite3_stmt *stmt_log; char *cached_capabilities; struct server_log *log; struct wms_list *list; struct connections_pool *pool; void *cache; } glob; struct neutral_socket { #ifdef _WIN32 SOCKET socket; #else int socket; #endif }; struct wms_style { /* a struct wrapping a WMS style */ int valid; char *name; char *title; char *abstract; struct wms_style *next; }; struct wms_layer { /* a struct wrapping a WMS layer */ int valid; char *layer_name; char *title; char *abstract; int srid; int is_geographic; double minx; double miny; double maxx; double maxy; double geo_minx; double geo_miny; double geo_maxx; double geo_maxy; unsigned char sample; unsigned char pixel; unsigned char num_bands; int jpeg; int png; int child_layer; struct wms_style *first_style; struct wms_style *last_style; struct wms_layer *next; }; struct read_connection { void *cache; sqlite3 *handle; sqlite3_stmt *stmt_get_map; int status; }; struct connections_pool { struct read_connection connections[MAX_CONN]; }; struct wms_layer_ref { /* a struct wrapping a WMS layer reference */ struct wms_layer *layer_ref; struct wms_layer_ref *next; }; struct wms_group { /* a struct wrapping a WMS group of layers */ int valid; char *group_name; char *title; char *abstract; int srid; int is_geographic; double minx; double miny; double maxx; double maxy; double geo_minx; double geo_miny; double geo_maxx; double geo_maxy; struct wms_style *first_style; struct wms_style *last_style; struct wms_layer_ref *first_child; struct wms_layer_ref *last_child; struct wms_group *next; }; struct wms_list { /* a struct wrapping a list of WMS layers */ struct wms_layer *first_layer; struct wms_layer *last_layer; struct wms_group *first_group; struct wms_group *last_group; }; struct wms_argument { /* a struct wrapping a single WMS arg */ char *arg_name; char *arg_value; struct wms_argument *next; }; struct wms_args { /* a struct wrapping a WMS request URL */ sqlite3 *db_handle; sqlite3_stmt *stmt_get_map; char *service_name; struct wms_argument *first; struct wms_argument *last; int request_type; int wms_version; int error; const char *layer; int srid; int swap_xy; double minx; double miny; double maxx; double maxy; unsigned short width; unsigned short height; char *style; unsigned char format; int transparent; int has_bgcolor; unsigned char red; unsigned char green; unsigned char blue; }; struct http_request { /* a struct wrapping an HTTP request */ unsigned int id; /* request ID */ int port_no; #ifdef _WIN32 SOCKET socket; /* Socket on which to receive data */ #else int socket; /* Socket on which to receive data */ #endif struct wms_list *list; struct read_connection *conn; sqlite3 *log_handle; char *cached_capabilities; int cached_capabilities_len; struct server_log_item *log; }; struct server_log_item { /* a struct supporting log infos */ char *client_ip_addr; unsigned short client_ip_port; char *timestamp; char *http_method; char *request_url; int http_status; int response_length; int wms_request; int wms_version; const char *wms_layer; int wms_srid; double wms_bbox_minx; double wms_bbox_miny; double wms_bbox_maxx; double wms_bbox_maxy; unsigned short wms_width; unsigned short wms_height; char *wms_style; unsigned char wms_format; int wms_transparent; int has_bgcolor; unsigned char wms_bgcolor_red; unsigned char wms_bgcolor_green; unsigned char wms_bgcolor_blue; struct timeval begin_time; int milliseconds; int status; }; struct server_log { /* log container */ struct server_log_item items[MAX_LOG]; int next_item; time_t last_update; }; static struct wms_style * alloc_wms_style (const char *name, const char *title, const char *abstract) { /* creating a WMS Raster Style */ int len; struct wms_style *style = malloc (sizeof (struct wms_style)); style->valid = 1; len = strlen (name); style->name = malloc (len + 1); strcpy (style->name, name); if (title == NULL) style->title = NULL; else { len = strlen (title); style->title = malloc (len + 1); strcpy (style->title, title); } if (abstract == NULL) style->abstract = NULL; else { len = strlen (abstract); style->abstract = malloc (len + 1); strcpy (style->abstract, abstract); } style->next = NULL; return style; } static void destroy_wms_style (struct wms_style *style) { /* memory cleanup - destroying a Raster Style */ if (style == NULL) return; if (style->name != NULL) free (style->name); if (style->title != NULL) free (style->title); if (style->abstract != NULL) free (style->abstract); free (style); } static struct wms_layer * alloc_wms_layer (const char *layer, const char *title, const char *abstract, int srid, int is_geographic, double minx, double miny, double maxx, double maxy, unsigned char sample, unsigned char pixel, unsigned char num_bands) { /* creating a WMS layer item */ int len; struct wms_layer *lyr = malloc (sizeof (struct wms_layer)); lyr->valid = 1; len = strlen (layer); lyr->layer_name = malloc (len + 1); strcpy (lyr->layer_name, layer); len = strlen (title); lyr->title = malloc (len + 1); strcpy (lyr->title, title); len = strlen (abstract); lyr->abstract = malloc (len + 1); strcpy (lyr->abstract, abstract); lyr->srid = srid; lyr->is_geographic = is_geographic; lyr->minx = minx; lyr->miny = miny; lyr->maxx = maxx; lyr->maxy = maxy; lyr->sample = sample; lyr->pixel = pixel; lyr->num_bands = num_bands; if (pixel == RL2_PIXEL_MONOCHROME || pixel == RL2_PIXEL_PALETTE) { lyr->png = 1; lyr->jpeg = 0; } else { lyr->png = 1; lyr->jpeg = 1; } lyr->child_layer = 0; lyr->first_style = NULL; lyr->last_style = NULL; lyr->next = NULL; return lyr; } static void destroy_wms_layer (struct wms_layer *lyr) { /* memory cleanup - freeing a WMS layer item */ struct wms_style *style; struct wms_style *style_n; if (lyr == NULL) return; if (lyr->layer_name != NULL) free (lyr->layer_name); if (lyr->title != NULL) free (lyr->title); if (lyr->abstract != NULL) free (lyr->abstract); style = lyr->first_style; while (style != NULL) { style_n = style->next; destroy_wms_style (style); style = style_n; } free (lyr); } static struct wms_layer_ref * alloc_wms_layer_ref (struct wms_layer *layer_ref) { /* creating a WMS layer_ref item */ struct wms_layer_ref *ref = malloc (sizeof (struct wms_layer_ref)); ref->layer_ref = layer_ref; ref->next = NULL; return ref; } static void destroy_wms_layer_ref (struct wms_layer_ref *ref) { /* memory cleanup - freeing a WMS layer_ref item */ if (ref == NULL) return; free (ref); } static struct wms_group * alloc_wms_group (const char *name, const char *title, const char *abstract) { /* creating a WMS group item */ int len; struct wms_group *grp = malloc (sizeof (struct wms_group)); grp->valid = 1; len = strlen (name); grp->group_name = malloc (len + 1); strcpy (grp->group_name, name); len = strlen (title); grp->title = malloc (len + 1); strcpy (grp->title, title); len = strlen (abstract); grp->abstract = malloc (len + 1); strcpy (grp->abstract, abstract); grp->first_style = NULL; grp->last_style = NULL; grp->first_child = NULL; grp->last_child = NULL; grp->next = NULL; return grp; } static void destroy_wms_group (struct wms_group *grp) { /* memory cleanup - freeing a WMS group item */ struct wms_style *style; struct wms_style *style_n; struct wms_layer_ref *child; struct wms_layer_ref *child_n; if (grp == NULL) return; if (grp->group_name != NULL) free (grp->group_name); if (grp->title != NULL) free (grp->title); if (grp->abstract != NULL) free (grp->abstract); style = grp->first_style; while (style != NULL) { style_n = style->next; destroy_wms_style (style); style = style_n; } child = grp->first_child; while (child != NULL) { child_n = child->next; destroy_wms_layer_ref (child); child = child_n; } free (grp); } static struct wms_list * alloc_wms_list () { /* allocating a list of WMS layers */ struct wms_list *list = malloc (sizeof (struct wms_list)); list->first_layer = NULL; list->last_layer = NULL; list->first_group = NULL; list->last_group = NULL; return list; } static void destroy_wms_list (struct wms_list *list) { /* memory cleanup - destroying a list of WMS layers */ struct wms_layer *pl; struct wms_layer *pln; struct wms_group *pg; struct wms_group *pgn; if (list == NULL) return; pl = list->first_layer; while (pl != NULL) { pln = pl->next; destroy_wms_layer (pl); pl = pln; } pg = list->first_group; while (pg != NULL) { pgn = pg->next; destroy_wms_group (pg); pg = pgn; } free (list); } static void add_style_to_wms_layer (struct wms_list *list, const char *coverage_name, const char *name, const char *title, const char *abstract) { /* appending a Raster Style to a WMS Layer */ struct wms_layer *lyr; if (list == NULL) return; lyr = list->first_layer; while (lyr != NULL) { if (strcmp (lyr->layer_name, coverage_name) == 0) { struct wms_style *style = alloc_wms_style (name, title, abstract); if (lyr->first_style == NULL) lyr->first_style = style; if (lyr->last_style != NULL) lyr->last_style->next = style; lyr->last_style = style; return; } lyr = lyr->next; } } static void add_default_styles (struct wms_list *list) { /* appending an implicit Default Style to each WMS Layer */ struct wms_layer *lyr; if (list == NULL) return; lyr = list->first_layer; while (lyr != NULL) { struct wms_style *style; int has_default = 0; int count = 0; style = lyr->first_style; while (style != NULL) { if (strcasecmp (style->name, "default") == 0) has_default = 1; count++; style = style->next; } if (count && !has_default) { /* appending a Default style */ struct wms_style *style = alloc_wms_style ("default", NULL, NULL); if (lyr->first_style == NULL) lyr->first_style = style; if (lyr->last_style != NULL) lyr->last_style->next = style; lyr->last_style = style; } lyr = lyr->next; } } static void add_style_to_wms_group (struct wms_list *list, const char *group_name, const char *name, const char *title, const char *abstract) { /* appending a Style to a WMS Group */ struct wms_group *grp; if (list == NULL) return; grp = list->first_group; while (grp != NULL) { if (strcmp (grp->group_name, group_name) == 0) { struct wms_style *style = alloc_wms_style (name, title, abstract); if (grp->first_style == NULL) grp->first_style = style; if (grp->last_style != NULL) grp->last_style->next = style; grp->last_style = style; return; } grp = grp->next; } } static void add_default_group_styles (struct wms_list *list) { /* appending an implicit Default Style to each WMS Group */ struct wms_group *grp; if (list == NULL) return; grp = list->first_group; while (grp != NULL) { struct wms_style *style; int has_default = 0; int count = 0; style = grp->first_style; while (style != NULL) { if (strcasecmp (style->name, "default") == 0) has_default = 1; count++; style = style->next; } if (count && !has_default) { /* appending a Default style */ struct wms_style *style = alloc_wms_style ("default", NULL, NULL); if (grp->first_style == NULL) grp->first_style = style; if (grp->last_style != NULL) grp->last_style->next = style; grp->last_style = style; } grp = grp->next; } } static struct wms_argument * alloc_wms_argument (char *name, char *value) { /* allocating a WMS argument */ struct wms_argument *arg; arg = malloc (sizeof (struct wms_argument)); arg->arg_name = name; arg->arg_value = value; arg->next = NULL; return arg; } static void destroy_wms_argument (struct wms_argument *arg) { /* memory cleanup - destroying a WMS arg struct */ if (arg == NULL) return; if (arg->arg_name != NULL) free (arg->arg_name); if (arg->arg_value != NULL) free (arg->arg_value); free (arg); } static void close_connection (struct read_connection *conn) { /* closing a connection */ if (conn == NULL) return; if (conn->stmt_get_map != NULL) sqlite3_finalize (conn->stmt_get_map); if (conn->handle != NULL) sqlite3_close (conn->handle); if (conn->cache != NULL) spatialite_cleanup_ex (conn->cache); conn->status = CONNECTION_INVALID; } static void destroy_connections_pool (struct connections_pool *pool) { /* memory clean-up: destroying a connections pool */ int i; struct read_connection *conn; if (pool == NULL) return; for (i = 0; i < MAX_CONN; i++) { /* closing all connections */ conn = &(pool->connections[i]); close_connection (conn); } free (pool); } static void connection_init (struct read_connection *conn, const char *path) { /* creating a read connection */ int ret; sqlite3 *db_handle; sqlite3_stmt *stmt; void *cache; const char *sql; ret = sqlite3_open_v2 (path, &db_handle, SQLITE_OPEN_READONLY | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_SHAREDCACHE, NULL); if (ret != SQLITE_OK) { fprintf (stderr, "cannot open '%s': %s\n", path, sqlite3_errmsg (db_handle)); sqlite3_close (db_handle); return; } cache = spatialite_alloc_connection (); spatialite_init_ex (db_handle, cache, 0); rl2_init (db_handle, 0); /* creating the GetMap SQL statement */ sql = "SELECT RL2_GetMapImage(?, BuildMbr(?, ?, ?, ?), ?, ?, ?, ?, ?, ?, ?)"; ret = sqlite3_prepare_v2 (db_handle, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) { sqlite3_close (db_handle); spatialite_cleanup_ex (cache); } conn->handle = db_handle; conn->stmt_get_map = stmt; conn->cache = cache; conn->status = CONNECTION_AVAILABLE; } static struct connections_pool * alloc_connections_pool (const char *path) { /* creating and initializing the connections pool */ int i; int count; struct read_connection *conn; struct connections_pool *pool = malloc (sizeof (struct read_connection) * MAX_CONN); if (pool == NULL) return NULL; for (i = 0; i < MAX_CONN; i++) { /* initializing empty connections */ conn = &(pool->connections[i]); conn->handle = NULL; conn->stmt_get_map = NULL; conn->cache = NULL; conn->status = CONNECTION_INVALID; } for (i = 0; i < MAX_CONN; i++) { /* creating the connections */ conn = &(pool->connections[i]); connection_init (conn, path); } count = 0; for (i = 0; i < MAX_CONN; i++) { /* final validity check */ conn = &(pool->connections[i]); if (conn->status == CONNECTION_AVAILABLE) count++; } if (count == 0) { /* invalid pool ... sorry ... */ destroy_connections_pool (pool); return NULL; } return pool; } static void log_cleanup (struct server_log_item *log) { /* memory cleanup - resetting a log slot */ if (log == NULL) return; if (log->client_ip_addr != NULL) free (log->client_ip_addr); if (log->timestamp != NULL) sqlite3_free (log->timestamp); if (log->http_method != NULL) free (log->http_method); if (log->request_url != NULL) free (log->request_url); if (log->wms_style != NULL) free (log->wms_style); log->client_ip_addr = NULL; log->timestamp = NULL; log->http_method = NULL; log->request_url = NULL; log->wms_layer = NULL; log->wms_style = NULL; log->status = LOG_SLOT_AVAILABLE; } static struct server_log * alloc_server_log () { /* allocating an empty server log helper struct */ int i; struct server_log *log = malloc (sizeof (struct server_log)); if (log == NULL) return NULL; for (i = 0; i < MAX_LOG; i++) { struct server_log_item *item = &(log->items[i]); item->client_ip_addr = NULL; item->timestamp = NULL; item->http_method = NULL; item->request_url = NULL; item->wms_layer = NULL; item->wms_style = NULL; item->status = LOG_SLOT_AVAILABLE; } log->next_item = 0; time (&(log->last_update)); return log; } static void destroy_server_log (struct server_log *log) { /* memory cleanup - destroying the server log helper struct */ int i; if (log == NULL) return; for (i = 0; i < MAX_LOG; i++) { struct server_log_item *item = &(log->items[i]); log_cleanup (item); } free (log); } static void log_error (struct server_log_item *log, char *timestamp, int status, char *method, char *url, int size) { /* logging an ERROR event */ struct timeval stop_time; struct timeval res; if (log == NULL) return; log->timestamp = timestamp; log->http_status = status; log->http_method = method; log->request_url = url; log->response_length = size; log->wms_request = WMS_ILLEGAL_REQUEST; log->wms_version = WMS_VERSION_UNKNOWN; log->status = LOG_SLOT_READY; gettimeofday (&stop_time, NULL); timersub (&(log->begin_time), &stop_time, &res); log->milliseconds = res.tv_usec / 1000; } static void log_get_capabilities_1 (struct server_log_item *log, char *timestamp, int status, char *method, char *url) { /* logging a GetCapabilities event (take #1) */ if (log == NULL) return; log->timestamp = timestamp; log->http_status = status; log->http_method = method; log->request_url = url; log->wms_request = WMS_GET_CAPABILITIES; log->wms_version = WMS_VERSION_UNKNOWN; } static void log_get_capabilities_2 (struct server_log_item *log, int size) { /* logging a GetCapabilities event (take #2) */ struct timeval stop_time; struct timeval res; if (log == NULL) return; log->response_length = size; log->status = LOG_SLOT_READY; gettimeofday (&stop_time, NULL); timersub (&(log->begin_time), &stop_time, &res); log->milliseconds = res.tv_usec / 1000; } static void log_get_map_1 (struct server_log_item *log, char *timestamp, int status, char *method, char *url, struct wms_args *args) { /* logging a GetMap event (take #1) */ int len; if (log == NULL) return; log->timestamp = timestamp; log->http_status = status; log->http_method = method; log->request_url = url; log->wms_request = WMS_GET_MAP; log->wms_version = args->wms_version; log->wms_layer = args->layer; log->wms_srid = args->srid; log->wms_bbox_minx = args->minx; log->wms_bbox_miny = args->miny; log->wms_bbox_maxx = args->maxx; log->wms_bbox_maxy = args->maxy; log->wms_width = args->width; log->wms_height = args->height; if (args->style == NULL) log->wms_style = NULL; else { len = strlen (args->style); log->wms_style = malloc (len + 1); strcpy (log->wms_style, args->style); } log->wms_format = args->format; log->wms_transparent = args->transparent; log->has_bgcolor = args->has_bgcolor; log->wms_bgcolor_red = args->red; log->wms_bgcolor_green = args->green; log->wms_bgcolor_blue = args->blue; } static void log_get_map_2 (struct server_log_item *log, int size) { /* logging a GetMap event (take #2) */ struct timeval stop_time; struct timeval res; if (log == NULL) return; log->response_length = size; log->status = LOG_SLOT_READY; gettimeofday (&stop_time, NULL); timersub (&(log->begin_time), &stop_time, &res); log->milliseconds = res.tv_usec / 1000; } static void flush_log (sqlite3 * handle, sqlite3_stmt * stmt, struct server_log *log) { /* flushing the LOG */ int ret; int i; char dummy[32]; if (handle == NULL || stmt == NULL || log == NULL) return; while (1) { /* looping until all Log slots are ready */ int wait = 0; for (i = 0; i < MAX_LOG; i++) { struct server_log_item *item = &(log->items[i]); if (item->status == LOG_SLOT_BUSY) wait = 1; } if (wait) usleep (50); else break; } /* starting a DBMS Transaction */ ret = sqlite3_exec (handle, "BEGIN", NULL, NULL, NULL); if (ret != SQLITE_OK) goto error; for (i = 0; i < MAX_LOG; i++) { struct server_log_item *item = &(log->items[i]); if (item->status != LOG_SLOT_READY) continue; /* binding the INSERT values */ sqlite3_reset (stmt); sqlite3_clear_bindings (stmt); if (item->timestamp == NULL) sqlite3_bind_null (stmt, 1); else sqlite3_bind_text (stmt, 1, item->timestamp, strlen (item->timestamp), SQLITE_STATIC); if (item->client_ip_addr == NULL) sqlite3_bind_null (stmt, 2); else sqlite3_bind_text (stmt, 2, item->client_ip_addr, strlen (item->client_ip_addr), SQLITE_STATIC); sqlite3_bind_int (stmt, 3, item->client_ip_port); if (item->http_method == NULL) sqlite3_bind_null (stmt, 4); else sqlite3_bind_text (stmt, 4, item->http_method, strlen (item->http_method), SQLITE_STATIC); if (item->request_url == NULL) sqlite3_bind_null (stmt, 5); else sqlite3_bind_text (stmt, 5, item->request_url, strlen (item->request_url), SQLITE_STATIC); sqlite3_bind_int (stmt, 6, item->http_status); sqlite3_bind_int (stmt, 7, item->response_length); switch (item->wms_request) { case WMS_GET_CAPABILITIES: sqlite3_bind_text (stmt, 8, "GetCapabilities", 17, SQLITE_STATIC); break; case WMS_GET_MAP: sqlite3_bind_text (stmt, 8, "GetMap", 8, SQLITE_STATIC); break; default: sqlite3_bind_null (stmt, 8); }; switch (item->wms_version) { case WMS_VERSION_100: sqlite3_bind_text (stmt, 9, "1.0.0", 5, SQLITE_TRANSIENT); break; case WMS_VERSION_110: sqlite3_bind_text (stmt, 9, "1.1.0", 5, SQLITE_TRANSIENT); break; case WMS_VERSION_111: sqlite3_bind_text (stmt, 9, "1.1.1", 5, SQLITE_TRANSIENT); break; case WMS_VERSION_130: sqlite3_bind_text (stmt, 9, "1.3.0", 5, SQLITE_TRANSIENT); break; default: sqlite3_bind_null (stmt, 9); }; if (item->wms_request == WMS_GET_MAP) { if (item->wms_layer == NULL) sqlite3_bind_null (stmt, 10); else sqlite3_bind_text (stmt, 10, item->wms_layer, strlen (item->wms_layer), SQLITE_STATIC); sqlite3_bind_int (stmt, 11, item->wms_srid); sqlite3_bind_double (stmt, 12, item->wms_bbox_minx); sqlite3_bind_double (stmt, 13, item->wms_bbox_miny); sqlite3_bind_double (stmt, 14, item->wms_bbox_maxx); sqlite3_bind_double (stmt, 15, item->wms_bbox_maxy); sqlite3_bind_int (stmt, 16, item->wms_width); sqlite3_bind_int (stmt, 17, item->wms_height); if (item->wms_style == NULL) sqlite3_bind_null (stmt, 18); else sqlite3_bind_text (stmt, 18, item->wms_style, strlen (item->wms_style), SQLITE_STATIC); switch (item->wms_format) { case RL2_OUTPUT_FORMAT_JPEG: sqlite3_bind_text (stmt, 19, "image/jpeg", 10, SQLITE_TRANSIENT); break; case RL2_OUTPUT_FORMAT_PNG: sqlite3_bind_text (stmt, 19, "image/png", 9, SQLITE_TRANSIENT); break; case RL2_OUTPUT_FORMAT_TIFF: sqlite3_bind_text (stmt, 19, "image/tiff", 10, SQLITE_TRANSIENT); break; case RL2_OUTPUT_FORMAT_PDF: sqlite3_bind_text (stmt, 19, "application/x-pdf", 9, SQLITE_TRANSIENT); break; default: sqlite3_bind_null (stmt, 19); }; switch (item->wms_transparent) { case WMS_TRANSPARENT: sqlite3_bind_int (stmt, 20, 1); break; case WMS_OPAQUE: sqlite3_bind_int (stmt, 20, 0); break; default: sqlite3_bind_null (stmt, 20); break; }; if (item->has_bgcolor == 0) sqlite3_bind_null (stmt, 21); else { sprintf (dummy, "#%02x%02x%02x\n", item->wms_bgcolor_red, item->wms_bgcolor_green, item->wms_bgcolor_blue); sqlite3_bind_text (stmt, 21, dummy, strlen (dummy), SQLITE_TRANSIENT); } } else { sqlite3_bind_null (stmt, 10); sqlite3_bind_null (stmt, 11); sqlite3_bind_null (stmt, 12); sqlite3_bind_null (stmt, 13); sqlite3_bind_null (stmt, 14); sqlite3_bind_null (stmt, 15); sqlite3_bind_null (stmt, 16); sqlite3_bind_null (stmt, 17); sqlite3_bind_null (stmt, 18); sqlite3_bind_null (stmt, 19); sqlite3_bind_null (stmt, 20); sqlite3_bind_null (stmt, 21); } sqlite3_bind_int (stmt, 22, item->milliseconds); ret = sqlite3_step (stmt); if (ret == SQLITE_DONE || ret == SQLITE_ROW) ; else { fprintf (stderr, "INSERT INTO WMS-LOG; sqlite3_step() error: %s\n", sqlite3_errmsg (handle)); goto error; } log_cleanup (item); } /* committing the still pending transaction */ ret = sqlite3_exec (handle, "COMMIT", NULL, NULL, NULL); if (ret != SQLITE_OK) goto error; log->next_item = 0; time (&(log->last_update)); return; error: sqlite3_exec (handle, "ROLLBACK", NULL, NULL, NULL); fprintf (stderr, "ERROR: unable to update the server log\n"); } static void clean_shutdown () { /* performing a clean shutdown */ fprintf (stderr, "wmslite server shutdown in progress\n"); flush_log (glob.handle, glob.stmt_log, glob.log); if (glob.stmt_log != NULL) sqlite3_finalize (glob.stmt_log); if (glob.cached_capabilities != NULL) free (glob.cached_capabilities); if (glob.list != NULL) destroy_wms_list (glob.list); if (glob.log != NULL) destroy_server_log (glob.log); if (glob.pool != NULL) destroy_connections_pool (glob.pool); if (glob.handle != NULL) sqlite3_close (glob.handle); if (glob.cache != NULL) spatialite_cleanup_ex (glob.cache); spatialite_shutdown (); fprintf (stderr, "wmslite shutdown completed ... bye bye\n\n"); } #ifdef _WIN32 BOOL WINAPI signal_handler (DWORD dwCtrlType) { /* intercepting some Windows signal */ clean_shutdown (); return FALSE; } #else static void signal_handler (int signo) { /* intercepting some signal */ if (signo == SIGINT) signo = SIGINT; /* suppressing compiler warnings */ clean_shutdown (); exit (0); } #endif static struct wms_args * alloc_wms_args (const char *service_name) { /* allocating an empty WMS args struct */ int len; struct wms_args *args; if (service_name == NULL) return NULL; args = malloc (sizeof (struct wms_args)); args->db_handle = NULL; args->stmt_get_map = NULL; len = strlen (service_name); args->service_name = malloc (len + 1); strcpy (args->service_name, service_name); args->first = NULL; args->last = NULL; args->style = NULL; args->request_type = WMS_ILLEGAL_REQUEST; args->error = WMS_UNKNOWN; return args; } static void destroy_wms_args (struct wms_args *args) { /* memory cleanup - destroying a WMS args struct */ struct wms_argument *pa; struct wms_argument *pan; if (args == NULL) return; if (args->service_name != NULL) free (args->service_name); if (args->style != NULL) free (args->style); pa = args->first; while (pa != NULL) { pan = pa->next; destroy_wms_argument (pa); pa = pan; } free (args); } static int add_wms_argument (struct wms_args *args, const char *token) { /* attempting to add a WMS argument */ int len; struct wms_argument *arg; char *name; char *value; const char *ptr = strstr (token, "="); if (ptr == NULL) return 0; len = strlen (ptr + 1); value = malloc (len + 1); strcpy (value, ptr + 1); len = ptr - token; name = malloc (len + 1); memcpy (name, token, len); *(name + len) = '\0'; arg = alloc_wms_argument (name, value); if (args->first == NULL) args->first = arg; if (args->last != NULL) args->last->next = arg; args->last = arg; return 1; } static int parse_srs (const char *srs) { /* parsing the EPSG:x item */ int srid; if (strlen (srs) < 6) return -1; if (strncmp (srs, "EPSG:", 4) != 0) return -1; srid = atoi (srs + 5); return srid; } static const char * parse_layers (const char *layers) { /* only a single layer for each request is supported */ int comma = 0; const char *p = layers; while (*p != '\0') { if (*p++ == ',') comma++; } if (!comma) return layers; return NULL; } static int parse_dim (const char *dim, unsigned short *value) { /* parsing the Width / Height items */ int x; for (x = 0; x < (int) strlen (dim); x++) { if (*(dim + x) < '0' || *(dim + x) > '9') return 0; } x = atoi (dim); if (x <= 0 || x > UINT16_MAX) return 0; *value = x; return 1; } static char * parse_style (const char *style) { /* parsing the current style */ char *out_style = NULL; if (strlen (style) == 0) style = "default"; out_style = malloc (strlen (style) + 1); strcpy (out_style, style); return out_style; } static int parse_format (const char *format) { /* parsing the output format */ if (strcasecmp (format, "image/png") == 0) return RL2_OUTPUT_FORMAT_PNG; if (strcasecmp (format, "image/jpeg") == 0) return RL2_OUTPUT_FORMAT_JPEG; if (strcasecmp (format, "image/tiff") == 0) return RL2_OUTPUT_FORMAT_TIFF; if (strcasecmp (format, "application/x-pdf") == 0) return RL2_OUTPUT_FORMAT_PDF; return WMS_UNKNOWN; } static int parse_transparent (const char *str) { /* parsing the Transparent value */ if (strcasecmp (str, "TRUE") == 0) return WMS_TRANSPARENT; if (strcasecmp (str, "FALSE") == 0) return WMS_OPAQUE; return WMS_UNKNOWN; } static int parse_bbox (const char *bbox, double *minx, double *miny, double *maxx, double *maxy) { /* attempting to parse the BBOX */ char buf[2000]; int count = 0; char *out = buf; const char *in = bbox; while (1) { if (*in == '\0') { if (count == 3) { *maxy = atof (buf); } else return 0; break; } if (*in == ',') { *out = '\0'; switch (count) { case 0: *minx = atof (buf); break; case 1: *miny = atof (buf); break; case 2: *maxx = atof (buf); break; default: return 0; }; out = buf; in++; count++; continue; } *out++ = *in++; } return 1; } static int parse_hex (char hi, char lo, unsigned char *value) { /* parsing an Hex byte */ unsigned char x; switch (hi) { case '0': x = 0; break; case '1': x = 1 * 16; break; case '2': x = 2 * 16; break; case '3': x = 3 * 16; break; case '4': x = 4 * 16; break; case '5': x = 5 * 16; break; case '6': x = 6 * 16; break; case '7': x = 7 * 16; break; case '8': x = 8 * 16; break; case '9': x = 9 * 16; break; case 'a': case 'A': x = 10 * 16; break; case 'b': case 'B': x = 11 * 16; break; case 'c': case 'C': x = 12 * 16; break; case 'd': case 'D': x = 13 * 16; break; case 'e': case 'E': x = 14 * 16; break; case 'f': case 'F': x = 15 * 16; break; default: return 0; }; switch (lo) { case '0': x += 0; break; case '1': x += 1; break; case '2': x += 2; break; case '3': x += 3; break; case '4': x += 4; break; case '5': x += 5; break; case '6': x += 6; break; case '7': x += 7; break; case '8': x += 8; break; case '9': x += 9; break; case 'a': case 'A': x += 10; break; case 'b': case 'B': x += 11; break; case 'c': case 'C': x += 12; break; case 'd': case 'D': x += 13; break; case 'e': case 'E': x += 14; break; case 'f': case 'F': x += 15; break; default: return 0; }; *value = x; return 1; } static int parse_bgcolor (const char *bgcolor, unsigned char *red, unsigned char *green, unsigned char *blue) { /* attempting to parse an RGB color */ if (strlen (bgcolor) != 8) return 0; if (bgcolor[0] != '0') return 0; if (bgcolor[1] == 'x' || bgcolor[1] == 'X') ; else return 0; if (!parse_hex (bgcolor[2], bgcolor[3], red)) return 0; if (!parse_hex (bgcolor[4], bgcolor[5], green)) return 0; if (!parse_hex (bgcolor[6], bgcolor[7], blue)) return 0; return 1; } static int exists_layer (struct wms_list *list, const char *layer, int srid, int wms_version, int *swap_xy, double minx, double miny, double maxx, double maxy, const char **layer_name) { /* checking a required layer for validity */ struct wms_group *grp; struct wms_layer *lyr = list->first_layer; while (lyr != NULL) { /* searching a genuine Layer */ if (strcmp (lyr->layer_name, layer) == 0) { if (lyr->srid != srid) return WMS_MISMATCHING_SRID; if (wms_version == WMS_VERSION_130 && lyr->is_geographic) *swap_xy = 1; else *swap_xy = 0; if (*swap_xy) { if (lyr->minx > maxy) return WMS_LAYER_OUT_OF_BBOX; if (lyr->maxx < miny) return WMS_LAYER_OUT_OF_BBOX; if (lyr->miny > maxx) return WMS_LAYER_OUT_OF_BBOX; if (lyr->maxy < minx) return WMS_LAYER_OUT_OF_BBOX; } else { if (lyr->minx > maxx) return WMS_LAYER_OUT_OF_BBOX; if (lyr->maxx < minx) return WMS_LAYER_OUT_OF_BBOX; if (lyr->miny > maxy) return WMS_LAYER_OUT_OF_BBOX; if (lyr->maxy < miny) return WMS_LAYER_OUT_OF_BBOX; } *layer_name = lyr->layer_name; return 0; } lyr = lyr->next; } grp = list->first_group; while (grp != NULL) { /* fallback case: searching a Group of Layers */ if (strcmp (grp->group_name, layer) == 0) { if (grp->valid == 0) return WMS_INVALID_GROUP; if (grp->srid != srid) return WMS_MISMATCHING_SRID; if (wms_version == WMS_VERSION_130 && grp->is_geographic) *swap_xy = 1; else *swap_xy = 0; if (*swap_xy) { if (grp->minx > maxy) return WMS_LAYER_OUT_OF_BBOX; if (grp->maxx < miny) return WMS_LAYER_OUT_OF_BBOX; if (grp->miny > maxx) return WMS_LAYER_OUT_OF_BBOX; if (grp->maxy < minx) return WMS_LAYER_OUT_OF_BBOX; } else { if (grp->minx > maxx) return WMS_LAYER_OUT_OF_BBOX; if (grp->maxx < minx) return WMS_LAYER_OUT_OF_BBOX; if (grp->miny > maxy) return WMS_LAYER_OUT_OF_BBOX; if (grp->maxy < miny) return WMS_LAYER_OUT_OF_BBOX; } *layer_name = grp->group_name; return 0; } grp = grp->next; } return WMS_NOT_EXISTING_LAYER; } static int check_wms_request (struct wms_list *list, struct wms_args *args) { /* checking for a valid WMS request */ struct wms_argument *arg; int wms_version = WMS_VERSION_130; int ok_wms = 0; int ok_version = 0; int is_get_capabilities = 0; int is_get_map = 0; const char *p_layers = NULL; const char *p_srs = NULL; const char *p_bbox = NULL; const char *p_width = NULL; const char *p_height = NULL; const char *p_styles = NULL; const char *p_format = NULL; const char *p_bgcolor = NULL; const char *p_transparent = NULL; if (strcasecmp (args->service_name, "GET /wmslite") != 0) return 400; arg = args->first; while (arg != NULL) { if (strcasecmp (arg->arg_name, "SERVICE") == 0) { if (strcasecmp (arg->arg_value, "WMS") == 0) ok_wms = 1; } if (strcasecmp (arg->arg_name, "VERSION") == 0) { wms_version = WMS_VERSION_UNKNOWN; if (strcasecmp (arg->arg_value, "1.3.0") == 0) { wms_version = WMS_VERSION_130; ok_version = 1; } if (strcasecmp (arg->arg_value, "1.1.1") == 0) { wms_version = WMS_VERSION_111; ok_version = 1; } if (strcasecmp (arg->arg_value, "1.1.0") == 0) { wms_version = WMS_VERSION_110; ok_version = 1; } if (strcasecmp (arg->arg_value, "1.0.0") == 0) { wms_version = WMS_VERSION_100; ok_version = 1; } } if (strcasecmp (arg->arg_name, "REQUEST") == 0) { if (strcasecmp (arg->arg_value, "GetCapabilities") == 0) is_get_capabilities = 1; if (strcasecmp (arg->arg_value, "GetMap") == 0) is_get_map = 1; } if (strcasecmp (arg->arg_name, "LAYERS") == 0) p_layers = arg->arg_value; if (strcasecmp (arg->arg_name, "SRS") == 0 || strcasecmp (arg->arg_name, "CRS") == 0) p_srs = arg->arg_value; if (strcasecmp (arg->arg_name, "BBOX") == 0) p_bbox = arg->arg_value; if (strcasecmp (arg->arg_name, "WIDTH") == 0) p_width = arg->arg_value; if (strcasecmp (arg->arg_name, "HEIGHT") == 0) p_height = arg->arg_value; if (strcasecmp (arg->arg_name, "STYLES") == 0) p_styles = arg->arg_value; if (strcasecmp (arg->arg_name, "FORMAT") == 0) p_format = arg->arg_value; if (strcasecmp (arg->arg_name, "BGCOLOR") == 0) p_bgcolor = arg->arg_value; if (strcasecmp (arg->arg_name, "TRANSPARENT") == 0) p_transparent = arg->arg_value; arg = arg->next; } if (is_get_capabilities && !is_get_map && wms_version != WMS_VERSION_UNKNOWN) { /* testing for valid GetCapabilities */ if (ok_wms) { args->request_type = WMS_GET_CAPABILITIES; args->wms_version = wms_version; return 200; } } if (is_get_map && !is_get_capabilities && wms_version != WMS_VERSION_UNKNOWN) { /* testing for valid GetMap */ if (ok_wms && ok_version && p_layers && p_srs && p_bbox && p_width && p_height && p_styles && p_format) { int ret; int srid; const char *layer; const char *layer_name; int swap_xy = 0; double minx; double miny; double maxx; double maxy; unsigned short width; unsigned short height; char *style = NULL; int format; int transparent = WMS_OPAQUE; unsigned char red = 255; unsigned char green = 255; unsigned char blue = 255; srid = parse_srs (p_srs); if (srid < -1) { args->error = WMS_INVALID_CRS; return 200; } layer = parse_layers (p_layers); if (layer == NULL) { args->error = WMS_INVALID_LAYER; return 200; } if (!parse_bbox (p_bbox, &minx, &miny, &maxx, &maxy)) { args->error = WMS_INVALID_BBOX; return 200; } if (!parse_dim (p_width, &width)) { args->error = WMS_INVALID_DIMENSION; return 200; } if (!parse_dim (p_height, &height)) { args->error = WMS_INVALID_DIMENSION; return 200; } style = parse_style (p_styles); if (style == NULL) { args->error = WMS_INVALID_STYLE; return 200; } args->style = style; format = parse_format (p_format); if (format == WMS_UNKNOWN) { args->error = WMS_INVALID_FORMAT; return 200; } if (p_transparent != NULL) { transparent = parse_transparent (p_transparent); if (transparent == WMS_UNKNOWN) { args->error = WMS_INVALID_TRANSPARENT; return 200; } } args->has_bgcolor = 0; if (p_bgcolor != NULL) { if (!parse_bgcolor (p_bgcolor, &red, &green, &blue)) { args->error = WMS_INVALID_BGCOLOR; return 200; } args->has_bgcolor = 1; } ret = exists_layer (list, layer, srid, wms_version, &swap_xy, minx, miny, maxx, maxy, &layer_name); if (ret == WMS_NOT_EXISTING_LAYER || ret == WMS_INVALID_GROUP || ret == WMS_LAYER_OUT_OF_BBOX || ret == WMS_MISMATCHING_SRID) { args->error = ret; return 200; } args->request_type = WMS_GET_MAP; args->wms_version = wms_version; args->layer = layer_name; args->srid = srid; args->swap_xy = swap_xy; if (wms_version == WMS_VERSION_130 && swap_xy) { /* swapping X and Y axis */ args->minx = miny; args->miny = minx; args->maxx = maxy; args->maxy = maxx; } else { /* normal XY axis ordering */ args->minx = minx; args->miny = miny; args->maxx = maxx; args->maxy = maxy; } args->width = width; args->height = height; args->format = format; args->transparent = transparent; args->red = red; args->green = green; args->blue = blue; } } return 200; } static struct wms_args * parse_http_request (const char *http_hdr, char **method, char **url) { /* attempting to parse an HTTP Request */ int ok = 1; int len; struct wms_args *args = NULL; char token[2000]; char *out; const char *p; const char *start = strstr (http_hdr, "GET "); const char *end = NULL; *url = NULL; if (start == NULL) ok = 0; if (ok) { end = strstr (start, " HTTP/1.1"); if (end == NULL) end = strstr (start, " HTTP/1.0"); if (end == NULL) return NULL; } if (ok) { *method = malloc (4); strcpy (*method, "GET"); len = end - start; len -= 4; len++; *url = malloc (end - start); memcpy (*url, start + 4, len); *(*url + len) = '\0'; } else { start = strstr (http_hdr, "POST "); if (start != NULL) { end = strstr (start, " HTTP/1.1"); if (end == NULL) end = strstr (start, " HTTP/1.0"); if (end == NULL) return NULL; } *method = malloc (5); strcpy (*method, "POST"); len = end - start; len -= 5; len++; *url = malloc (end - start); memcpy (*url, start + 5, len); *(*url + len) = '\0'; } p = start; out = token; while (p < end) { if (*p == '?') { /* the service name */ *out = '\0'; if (args != NULL) goto error; args = alloc_wms_args (token); out = token; p++; continue; } if (*p == '&') { /* a key-value pair ends here */ *out = '\0'; if (args == NULL) goto error; if (!add_wms_argument (args, token)) goto error; out = token; p++; continue; } *out++ = *p++; } if (out > token) { /* processing the last arg */ *out = '\0'; if (args == NULL) goto error; if (!add_wms_argument (args, token)) goto error; } if (debug_mode) printf ("%s\n", *url); return args; error: if (args != NULL) destroy_wms_args (args); return NULL; } static char * get_current_timestamp () { /* formatting the current timestamp */ char *dummy; struct tm *xtm; time_t now; const char *day; const char *month; time (&now); xtm = gmtime (&now); switch (xtm->tm_wday) { case 0: day = "Sun"; break; case 1: day = "Mon"; break; case 2: day = "Tue"; break; case 3: day = "Wed"; break; case 4: day = "Thu"; break; case 5: day = "Fri"; break; case 6: day = "Sat"; break; }; switch (xtm->tm_mon) { case 0: month = "Jan"; break; case 1: month = "Feb"; break; case 2: month = "Mar"; break; case 3: month = "Apr"; break; case 4: month = "May"; break; case 5: month = "Jun"; break; case 6: month = "Jul"; break; case 7: month = "Aug"; break; case 8: month = "Sep"; break; case 9: month = "Oct"; break; case 10: month = "Nov"; break; case 11: month = "Dec"; break; }; dummy = sqlite3_mprintf ("Date: %s, %02d %s %04d %02d:%02d:%02d GMT\r\n", day, xtm->tm_mday, month, xtm->tm_year + 1900, xtm->tm_hour, xtm->tm_min, xtm->tm_sec); return dummy; } static char * get_sql_timestamp () { /* formatting the current SQL timestamp */ char *dummy; struct tm *xtm; time_t now; time (&now); xtm = gmtime (&now); dummy = sqlite3_mprintf ("%04d-%02d-%02dT%02d:%02d:%02d", xtm->tm_year + 1900, xtm->tm_mon + 1, xtm->tm_mday, xtm->tm_hour, xtm->tm_min, xtm->tm_sec); return dummy; } static void build_wms_exception (struct wms_args *args, gaiaOutBufferPtr xml_response) { /* preparing an XML exception */ char *dummy; gaiaOutBuffer xml_text; gaiaOutBufferInitialize (&xml_text); gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n"); if (args == NULL) { gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "General error.\r\n"); } else { switch (args->error) { case WMS_INVALID_DIMENSION: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Request is for a Layer not offered by the service instance.\r\n"); break; case WMS_INVALID_BBOX: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Invalid BBOX parameter.\r\n"); break; case WMS_NOT_EXISTING_LAYER: case WMS_INVALID_GROUP: case WMS_INVALID_LAYER: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Request is for a Layer not offered by the service instance.\r\n"); break; case WMS_INVALID_BGCOLOR: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Invalid BGCOLOR parameter.\r\n"); break; case WMS_INVALID_STYLE: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Request is for a Layer in a Style not offered by the service instance.\r\n"); break; case WMS_INVALID_FORMAT: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Request contains a Format not offered by the service instance.\r\n"); break; case WMS_INVALID_TRANSPARENT: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Invalid TRANSPARENT parameter.\r\n"); break; case WMS_LAYER_OUT_OF_BBOX: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "The BBOX parameter is outside the layer's extent.\r\n"); break; case WMS_INVALID_CRS: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Request contains a malformed SRS.\r\n"); break; case WMS_MISMATCHING_SRID: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Request contains an SRS not offered by the service "); gaiaAppendToOutBuffer (&xml_text, "instance for one or more of the Layers in the request.\r\n"); break; default: gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "Malformed request.\r\n"); break; } } gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, ""); gaiaAppendToOutBuffer (xml_response, "HTTP/1.1 200 OK\r\n"); dummy = get_current_timestamp (); gaiaAppendToOutBuffer (xml_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (xml_response, "Content-Type: text/xml;charset=UTF-8\r\n"); dummy = sqlite3_mprintf ("Content-Length: %d\r\n", xml_text.WriteOffset); gaiaAppendToOutBuffer (xml_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (xml_response, "Connection: close\r\n\r\n"); gaiaAppendToOutBuffer (xml_response, xml_text.Buffer); gaiaOutBufferReset (&xml_text); } static void build_http_error (int http_status, gaiaOutBufferPtr xml_response, int port_no) { /* preparing an HTTP error */ char *dummy; gaiaOutBuffer http_text; gaiaOutBufferInitialize (&http_text); gaiaAppendToOutBuffer (&http_text, "\r\n"); gaiaAppendToOutBuffer (&http_text, "\r\n"); if (http_status == 400) { gaiaAppendToOutBuffer (xml_response, "HTTP/1.1 400 Bad Request\r\n"); gaiaAppendToOutBuffer (&http_text, "400 Bad Request\r\n"); gaiaAppendToOutBuffer (&http_text, "\r\n"); gaiaAppendToOutBuffer (&http_text, "

Bad Request

\n"); } else { gaiaAppendToOutBuffer (xml_response, "HTTP/1.1 500 Internal Server Error\r\n"); gaiaAppendToOutBuffer (&http_text, "500 Internal Server Error\r\n"); gaiaAppendToOutBuffer (&http_text, "\r\n"); gaiaAppendToOutBuffer (&http_text, "

Internal Server Error

\n"); } dummy = sqlite3_mprintf ("
WmsLite/%s [%s] at localhost (127.0.0.1) Port %d
\r\n", rl2_version (), rl2_target_cpu (), port_no); gaiaAppendToOutBuffer (&http_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&http_text, "\r\n"); gaiaAppendToOutBuffer (&http_text, ""); dummy = get_current_timestamp (); gaiaAppendToOutBuffer (xml_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (xml_response, "Content-Type: text/html;charset=UTF-8\r\n"); dummy = sqlite3_mprintf ("Content-Length: %d\r\n", http_text.WriteOffset); gaiaAppendToOutBuffer (xml_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (xml_response, "Connection: close\r\n\r\n"); gaiaAppendToOutBuffer (xml_response, http_text.Buffer); gaiaOutBufferReset (&http_text); } static void build_get_capabilities (struct wms_list *list, char **cached, int *cached_len) { /* preparing the WMS GetCapabilities XML document */ struct wms_layer *lyr; struct wms_group *grp; struct wms_style *style; char *dummy; gaiaOutBuffer xml_text; gaiaOutBufferInitialize (&xml_text); gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nWMS\r\n"); gaiaAppendToOutBuffer (&xml_text, "WmsLite test server\r\n"); gaiaAppendToOutBuffer (&xml_text, "A simple light-weight WMS server for testing RasterLite2 Coverages.\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nmaps\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "James T. Kirk\r\n"); gaiaAppendToOutBuffer (&xml_text, "United Federation of Planets, Starfleet\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nStarship Captain.\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nstellar\r\n"); gaiaAppendToOutBuffer (&xml_text, "
USS Enterprise
\r\n"); gaiaAppendToOutBuffer (&xml_text, "Planet Earth\r\n"); gaiaAppendToOutBuffer (&xml_text, "Solar System\r\n"); gaiaAppendToOutBuffer (&xml_text, "12345#WYZ47NL@512\r\n"); gaiaAppendToOutBuffer (&xml_text, "Milky Way Galaxy\r\n"); gaiaAppendToOutBuffer (&xml_text, "
\r\n
\r\n"); gaiaAppendToOutBuffer (&xml_text, "none\r\n"); gaiaAppendToOutBuffer (&xml_text, "none\r\n"); gaiaAppendToOutBuffer (&xml_text, "1\r\n"); gaiaAppendToOutBuffer (&xml_text, "5000\r\n"); gaiaAppendToOutBuffer (&xml_text, "5000\r\n"); gaiaAppendToOutBuffer (&xml_text, "
\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "text/xml\r\n\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nimage/png\r\n"); gaiaAppendToOutBuffer (&xml_text, "image/jpeg\r\n"); gaiaAppendToOutBuffer (&xml_text, "application/x-pdf\r\n"); gaiaAppendToOutBuffer (&xml_text, "image/tiff\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nXML\r\n\r\n"); gaiaAppendToOutBuffer (&xml_text, "\r\nRasterLite2 Coverages\r\n"); gaiaAppendToOutBuffer (&xml_text, "OGC WMS compliant Service\r\n"); /* publishing any valid first-level Layer */ lyr = list->first_layer; while (lyr != NULL) { /* available Coverages */ if (lyr->valid == 0) { /* skipping any invalid Layer */ lyr = lyr->next; continue; } if (lyr->child_layer == 1) { /* skipping any child layer */ lyr = lyr->next; continue; } gaiaAppendToOutBuffer (&xml_text, "\r\n"); dummy = sqlite3_mprintf ("%s\r\n", lyr->layer_name); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%s\r\n", lyr->title); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%s\r\n", lyr->abstract); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("EPSG:%d\r\n", lyr->srid); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_text, ""); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_minx); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_maxx); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_miny); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_maxy); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_text, "\r\n"); if (lyr->is_geographic) dummy = sqlite3_mprintf ("\r\n", lyr->srid, lyr->miny, lyr->minx, lyr->maxy, lyr->maxx); else dummy = sqlite3_mprintf ("\r\n", lyr->srid, lyr->minx, lyr->miny, lyr->maxx, lyr->maxy); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); style = lyr->first_style; while (style != NULL) { if (style->valid == 0) { style = style->next; continue; } dummy = sqlite3_mprintf ("\r\n"); style = style->next; } gaiaAppendToOutBuffer (&xml_text, "\r\n"); lyr = lyr->next; } /* publishing any valid Layer Group */ grp = list->first_group; while (grp != NULL) { /* available Groups */ struct wms_layer_ref *ref; if (grp->valid == 0) { /* skipping any invalid Layer */ grp = grp->next; continue; } gaiaAppendToOutBuffer (&xml_text, "\r\n"); dummy = sqlite3_mprintf ("%s\r\n", grp->group_name); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%s\r\n", grp->title); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%s\r\n", grp->abstract); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("EPSG:%d\r\n", grp->srid); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_text, ""); dummy = sqlite3_mprintf ("%1.6f", grp->geo_minx); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", grp->geo_maxx); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", grp->geo_miny); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", grp->geo_maxy); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_text, "\r\n"); if (grp->is_geographic) dummy = sqlite3_mprintf ("\r\n", grp->srid, grp->miny, grp->minx, grp->maxy, grp->maxx); else dummy = sqlite3_mprintf ("\r\n", grp->srid, grp->minx, grp->miny, grp->maxx, grp->maxy); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); style = grp->first_style; while (style != NULL) { if (style->valid == 0) { style = style->next; continue; } dummy = sqlite3_mprintf ("\r\n"); style = style->next; } /* publishing all valid children Layers */ ref = grp->first_child; while (ref != NULL) { /* available Coverages */ if (ref->layer_ref->valid == 0) { /* skipping any invalid Child */ ref = ref->next; continue; } lyr = ref->layer_ref; gaiaAppendToOutBuffer (&xml_text, "\r\n"); dummy = sqlite3_mprintf ("%s\r\n", lyr->layer_name); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%s\r\n", lyr->title); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%s\r\n", lyr->abstract); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("EPSG:%d\r\n", lyr->srid); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_text, ""); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_minx); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_maxx); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_miny); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); dummy = sqlite3_mprintf ("%1.6f", lyr->geo_maxy); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_text, "\r\n"); if (lyr->is_geographic) dummy = sqlite3_mprintf ("\r\n", lyr->srid, lyr->miny, lyr->minx, lyr->maxy, lyr->maxx); else dummy = sqlite3_mprintf ("\r\n", lyr->srid, lyr->minx, lyr->miny, lyr->maxx, lyr->maxy); gaiaAppendToOutBuffer (&xml_text, dummy); sqlite3_free (dummy); style = lyr->first_style; while (style != NULL) { if (style->valid == 0) { style = style->next; continue; } dummy = sqlite3_mprintf ("\r\n"); style = style->next; } gaiaAppendToOutBuffer (&xml_text, "\r\n"); ref = ref->next; } gaiaAppendToOutBuffer (&xml_text, "\r\n"); grp = grp->next; } gaiaAppendToOutBuffer (&xml_text, "\r\n\r\n
\r\n"); gaiaAppendToOutBuffer (&xml_text, ""); *cached = xml_text.Buffer; *cached_len = xml_text.WriteOffset; xml_text.Buffer = NULL; gaiaOutBufferReset (&xml_text); } static int get_xml_bytes (gaiaOutBufferPtr xml_response, int curr, int block_sz) { /* determining how many bytes will be sent on the output socket */ int wr = block_sz; if (xml_response->Buffer == NULL || xml_response->Error) return 0; if (curr + wr > xml_response->WriteOffset) wr = xml_response->WriteOffset - curr; return wr; } static int get_payload_bytes (int total, int curr, int block_sz) { /* determining how many bytes will be sent on the output socket */ int wr = block_sz; if (curr + wr > total) wr = total - curr; return wr; } static void #ifdef _WIN32 wms_get_capabilities (SOCKET socket, const char *cached, int cached_len, struct server_log_item *log) #else wms_get_capabilities (int socket, const char *cached, int cached_len, struct server_log_item *log) #endif { /* preparing the WMS GetCapabilities XML document */ gaiaOutBuffer xml_response; char *dummy; int curr; int rd; int wr; gaiaOutBufferInitialize (&xml_response); gaiaAppendToOutBuffer (&xml_response, "HTTP/1.1 200 OK\r\n"); dummy = get_current_timestamp (); gaiaAppendToOutBuffer (&xml_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_response, "Content-Type: text/xml;charset=UTF-8\r\n"); dummy = sqlite3_mprintf ("Content-Length: %d\r\n", cached_len); gaiaAppendToOutBuffer (&xml_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&xml_response, "Connection: close\r\n\r\n"); gaiaAppendToOutBuffer (&xml_response, cached); /* uploading the HTTP response */ curr = 0; while (1) { rd = get_xml_bytes (&xml_response, curr, SEND_BLOK_SZ); if (rd == 0) break; wr = send (socket, xml_response.Buffer + curr, rd, 0); if (wr < 0) break; curr += wr; } log_get_capabilities_2 (log, xml_response.WriteOffset); gaiaOutBufferReset (&xml_response); } static void #ifdef _WIN32 wms_get_map (struct wms_args *args, SOCKET socket, struct server_log_item *log) #else wms_get_map (struct wms_args *args, int socket, struct server_log_item *log) #endif { /* preparing the WMS GetMap payload */ int ret; char *dummy; int curr; int rd; int wr; int save_sz; sqlite3_stmt *stmt = NULL; gaiaOutBuffer http_response; unsigned char *payload; int payload_size; unsigned char *black; int black_sz; char bgcolor[16]; stmt = args->stmt_get_map; sqlite3_reset (stmt); sqlite3_clear_bindings (stmt); sqlite3_bind_text (stmt, 1, args->layer, strlen (args->layer), SQLITE_STATIC); sqlite3_bind_double (stmt, 2, args->minx); sqlite3_bind_double (stmt, 3, args->miny); sqlite3_bind_double (stmt, 4, args->maxx); sqlite3_bind_double (stmt, 5, args->maxy); sqlite3_bind_int (stmt, 6, args->width); sqlite3_bind_int (stmt, 7, args->height); sqlite3_bind_text (stmt, 8, args->style, strlen (args->style), SQLITE_STATIC); if (args->format == RL2_OUTPUT_FORMAT_TIFF) sqlite3_bind_text (stmt, 9, "image/tiff", strlen ("image/tiff"), SQLITE_TRANSIENT); else if (args->format == RL2_OUTPUT_FORMAT_PDF) sqlite3_bind_text (stmt, 9, "application/x-pdf", strlen ("application/x-pdf"), SQLITE_TRANSIENT); else if (args->format == RL2_OUTPUT_FORMAT_JPEG) sqlite3_bind_text (stmt, 9, "image/jpeg", strlen ("image/jpeg"), SQLITE_TRANSIENT); else sqlite3_bind_text (stmt, 9, "image/png", strlen ("image/png"), SQLITE_TRANSIENT); if (args->has_bgcolor) sprintf (bgcolor, "#%02x%02x%02x", args->red, args->green, args->blue); else strcpy (bgcolor, "#ffffff"); sqlite3_bind_text (stmt, 10, bgcolor, strlen (bgcolor), SQLITE_TRANSIENT); sqlite3_bind_int (stmt, 11, args->transparent); if (args->format == RL2_OUTPUT_FORMAT_JPEG) sqlite3_bind_int (stmt, 12, 80); else sqlite3_bind_int (stmt, 12, 100); while (1) { ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; if (ret == SQLITE_ROW) { if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB) { payload = (unsigned char *) sqlite3_column_blob (stmt, 0); payload_size = sqlite3_column_bytes (stmt, 0); /* preparing the HTTP response */ gaiaOutBufferInitialize (&http_response); gaiaAppendToOutBuffer (&http_response, "HTTP/1.1 200 OK\r\n"); dummy = get_current_timestamp (); gaiaAppendToOutBuffer (&http_response, dummy); sqlite3_free (dummy); if (args->format == RL2_OUTPUT_FORMAT_JPEG) gaiaAppendToOutBuffer (&http_response, "Content-Type: image/jpeg\r\n"); if (args->format == RL2_OUTPUT_FORMAT_PNG) gaiaAppendToOutBuffer (&http_response, "Content-Type: image/png\r\n"); if (args->format == RL2_OUTPUT_FORMAT_TIFF) gaiaAppendToOutBuffer (&http_response, "Content-Type: image/tiff\r\n"); if (args->format == RL2_OUTPUT_FORMAT_PDF) gaiaAppendToOutBuffer (&http_response, "Content-Type: application/x-pdf\r\n"); dummy = sqlite3_mprintf ("Content-Length: %d\r\n", payload_size); gaiaAppendToOutBuffer (&http_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&http_response, "Connection: close\r\n\r\n"); /* uploading the HTTP response header */ curr = 0; while (1) { rd = get_xml_bytes (&http_response, curr, SEND_BLOK_SZ); if (rd == 0) break; wr = send (socket, http_response.Buffer + curr, rd, 0); if (wr < 0) break; curr += wr; } save_sz = http_response.WriteOffset; gaiaOutBufferReset (&http_response); /* uploading the image payload */ curr = 0; while (1) { rd = get_payload_bytes (payload_size, curr, SEND_BLOK_SZ); if (rd == 0) break; wr = send (socket, payload + curr, rd, 0); if (wr < 0) break; curr += wr; } log_get_map_2 (log, save_sz + payload_size); return; } } } /* preparing a default black image */ black_sz = args->width * args->height; black = malloc (black_sz); memset (black, 0, black_sz); if (args->format == RL2_OUTPUT_FORMAT_JPEG) rl2_gray_to_jpeg (args->width, args->height, black, 80, &payload, &payload_size); if (args->format == RL2_OUTPUT_FORMAT_PNG) rl2_gray_to_png (args->width, args->height, black, &payload, &payload_size); if (args->format == RL2_OUTPUT_FORMAT_TIFF) rl2_gray_to_tiff (args->width, args->height, black, &payload, &payload_size); if (args->format == RL2_OUTPUT_FORMAT_PDF) rl2_gray_pdf (args->width, args->height, &payload, &payload_size); free (black); /* preparing the HTTP response */ gaiaOutBufferInitialize (&http_response); gaiaAppendToOutBuffer (&http_response, "HTTP/1.1 200 OK\r\n"); dummy = get_current_timestamp (); gaiaAppendToOutBuffer (&http_response, dummy); sqlite3_free (dummy); if (args->format == RL2_OUTPUT_FORMAT_JPEG) gaiaAppendToOutBuffer (&http_response, "Content-Type: image/jpeg\r\n"); if (args->format == RL2_OUTPUT_FORMAT_PNG) gaiaAppendToOutBuffer (&http_response, "Content-Type: image/png\r\n"); if (args->format == RL2_OUTPUT_FORMAT_TIFF) gaiaAppendToOutBuffer (&http_response, "Content-Type: image/tiff\r\n"); if (args->format == RL2_OUTPUT_FORMAT_TIFF) gaiaAppendToOutBuffer (&http_response, "Content-Type: application/x-pdf\r\n"); dummy = sqlite3_mprintf ("Content-Length: %d\r\n", payload_size); gaiaAppendToOutBuffer (&http_response, dummy); sqlite3_free (dummy); gaiaAppendToOutBuffer (&http_response, "Connection: close\r\n\r\n"); /* uploading the HTTP response header */ curr = 0; while (1) { rd = get_xml_bytes (&http_response, curr, SEND_BLOK_SZ); if (rd == 0) break; wr = send (socket, http_response.Buffer + curr, rd, 0); if (wr < 0) break; curr += wr; } save_sz = http_response.WriteOffset; gaiaOutBufferReset (&http_response); /* uploading the image payload */ curr = 0; while (1) { rd = get_payload_bytes (payload_size, curr, SEND_BLOK_SZ); if (rd == 0) break; wr = send (socket, payload + curr, rd, 0); if (wr < 0) break; curr += wr; } free (payload); log_get_map_2 (log, save_sz + payload_size); } #ifdef _WIN32 /* Winsockets - some king of Windows */ static void win32_http_request (void *data) { /* Processing an incoming HTTP request */ gaiaOutBuffer xml_response; struct http_request *req = (struct http_request *) data; struct wms_args *args = NULL; int curr; int rd; int wr; char *ptr; char http_hdr[2000]; /* The HTTP request header */ int http_status; char *method = NULL; char *url = NULL; char *timestamp = get_sql_timestamp (); curr = 0; while ((unsigned int) curr < sizeof (http_hdr)) { rd = recv (req->socket, &http_hdr[curr], sizeof (http_hdr) - 1 - curr, 0); if (rd == SOCKET_ERROR) goto end_request; if (rd == 0) break; curr += rd; http_hdr[curr] = '\0'; ptr = strstr (http_hdr, "\r\n\r\n"); if (ptr) break; } if ((unsigned int) curr >= sizeof (http_hdr)) { http_status = 400; goto http_error; } args = parse_http_request (http_hdr, &method, &url); if (args == NULL) { http_status = 400; goto http_error; } http_status = check_wms_request (req->list, args); if (http_status != 200) goto http_error; if (args->request_type == WMS_ILLEGAL_REQUEST) goto illegal_request; if (args->request_type == WMS_GET_CAPABILITIES) { /* preparing the XML WMS GetCapabilities */ log_get_capabilities_1 (req->log, timestamp, http_status, method, url); wms_get_capabilities (req->socket, req->cached_capabilities, req->cached_capabilities_len, req->log); } if (args->request_type == WMS_GET_MAP) { /* preparing the WMS GetMap payload */ args->db_handle = req->conn->handle; args->stmt_get_map = req->conn->stmt_get_map; log_get_map_1 (req->log, timestamp, http_status, method, url, args); wms_get_map (args, req->socket, req->log); } goto end_request; /* preparing an HTTP error code */ http_error: gaiaOutBufferInitialize (&xml_response); build_http_error (http_status, &xml_response, req->port_no); curr = 0; while (1) { rd = get_xml_bytes (&xml_response, curr, SEND_BLOK_SZ); if (rd == 0) break; send (req->socket, xml_response.Buffer + curr, rd, 0); curr += rd; } log_error (req->log, timestamp, http_status, method, url, xml_response.WriteOffset); gaiaOutBufferReset (&xml_response); goto end_request; /* preparing an XML WMS Exception message */ illegal_request: gaiaOutBufferInitialize (&xml_response); build_wms_exception (args, &xml_response); curr = 0; while (1) { rd = get_xml_bytes (&xml_response, curr, SEND_BLOK_SZ); if (rd == 0) break; send (req->socket, xml_response.Buffer + curr, rd, 0); curr += rd; } log_error (req->log, timestamp, http_status, method, url, xml_response.WriteOffset); gaiaOutBufferReset (&xml_response); end_request: if (args != NULL) destroy_wms_args (args); closesocket (req->socket); req->conn->status = CONNECTION_AVAILABLE; free (req); } #else /* standard Berkeley Sockets - may be Linux or *nix */ static void * berkeley_http_request (void *data) { /* Processing an incoming HTTP request */ gaiaOutBuffer xml_response; struct http_request *req = (struct http_request *) data; struct wms_args *args = NULL; int curr; int rd; char *ptr; char http_hdr[2000]; /* The HTTP request header */ int http_status; char *method = NULL; char *url = NULL; char *timestamp = get_sql_timestamp (); curr = 0; while ((unsigned int) curr < sizeof (http_hdr)) { rd = recv (req->socket, &http_hdr[curr], sizeof (http_hdr) - 1 - curr, 0); if (rd == -1) goto end_request; if (rd == 0) break; curr += rd; http_hdr[curr] = '\0'; ptr = strstr (http_hdr, "\r\n\r\n"); if (ptr) break; } if ((unsigned int) curr >= sizeof (http_hdr)) { http_status = 400; goto http_error; } args = parse_http_request (http_hdr, &method, &url); if (args == NULL) { http_status = 400; goto http_error; } http_status = check_wms_request (req->list, args); if (http_status != 200) goto http_error; if (args->request_type == WMS_ILLEGAL_REQUEST) goto illegal_request; if (args->request_type == WMS_GET_CAPABILITIES) { /* uploading the XML WMS GetCapabilities */ log_get_capabilities_1 (req->log, timestamp, http_status, method, url); wms_get_capabilities (req->socket, req->cached_capabilities, req->cached_capabilities_len, req->log); } if (args->request_type == WMS_GET_MAP) { /* preparing the WMS GetMap payload */ args->db_handle = req->conn->handle; args->stmt_get_map = req->conn->stmt_get_map; log_get_map_1 (req->log, timestamp, http_status, method, url, args); wms_get_map (args, req->socket, req->log); } goto end_request; /* preparing an HTTP error code */ http_error: gaiaOutBufferInitialize (&xml_response); build_http_error (http_status, &xml_response, req->port_no); curr = 0; while (1) { rd = get_xml_bytes (&xml_response, curr, SEND_BLOK_SZ); if (rd == 0) break; send (req->socket, xml_response.Buffer + curr, rd, 0); curr += rd; } log_error (req->log, timestamp, http_status, method, url, xml_response.WriteOffset); gaiaOutBufferReset (&xml_response); goto end_request; /* preparing an XML WMS Exception message */ illegal_request: gaiaOutBufferInitialize (&xml_response); build_wms_exception (args, &xml_response); curr = 0; while (1) { rd = get_xml_bytes (&xml_response, curr, SEND_BLOK_SZ); if (rd == 0) break; send (req->socket, xml_response.Buffer + curr, rd, 0); curr += rd; } log_error (req->log, timestamp, http_status, method, url, xml_response.WriteOffset); gaiaOutBufferReset (&xml_response); end_request: if (args != NULL) destroy_wms_args (args); close (req->socket); req->conn->status = CONNECTION_AVAILABLE; free (req); pthread_exit (NULL); } #endif static void do_accept_loop (struct neutral_socket *skt, struct wms_list *list, int port_no, sqlite3 * db_handle, sqlite3_stmt * stmt_log, struct connections_pool *pool, struct server_log *log, char *cached_capab, int cached_capab_len) { /* implementing the ACCEPT loop */ unsigned int id = 0; struct read_connection *conn; int ic; time_t diff; time_t now; char *ip_addr; char *ip_addr2; #ifdef _WIN32 SOCKET socket = skt->socket; SOCKET client; SOCKADDR_IN client_addr; int len = sizeof (client_addr); int wsaError; struct http_request *req; while (1) { /* never ending loop */ client = accept (socket, (struct sockaddr *) &client_addr, &len); if (client == INVALID_SOCKET) { wsaError = WSAGetLastError (); if (wsaError == WSAEINTR || wsaError == WSAENOTSOCK) { WSACleanup (); fprintf (stderr, "accept error: %d\n", wsaError); return; } else { closesocket (socket); WSACleanup (); fprintf (stderr, "error from accept()\n"); return; } } req = malloc (sizeof (struct http_request)); req->id = id++; req->port_no = port_no; req->socket = client; req->list = list; req->cached_capabilities = cached_capab; req->cached_capabilities_len = cached_capab_len; while (1) { /* looping until an available read connection is found */ for (ic = 0; ic < MAX_CONN; ic++) { /* selecting an available connection (if any) */ struct read_connection *ptr = &(pool->connections[ic]); if (ptr->status == CONNECTION_AVAILABLE) { conn = ptr; conn->status = CONNECTION_BUSY; goto conn_found; } } Sleep (50); } conn_found: req->conn = conn; req->log_handle = db_handle; time (&now); diff = now - log->last_update; if (log->next_item < MAX_LOG && diff < 30) { /* reserving a free log slot */ req->log = &(log->items[log->next_item++]); req->log->status = LOG_SLOT_BUSY; ip_addr = inet_ntoa (client_addr.sin_addr); ip_addr2 = NULL; if (ip_addr != NULL) { int len = strlen (ip_addr); ip_addr2 = malloc (len + 1); strcpy (ip_addr2, ip_addr); } req->log->client_ip_addr = ip_addr2; req->log->client_ip_port = client_addr.sin_port; gettimeofday (&(req->log->begin_time), NULL); } else { /* flushing the log */ flush_log (db_handle, stmt_log, log); /* reserving a free log slot */ req->log = &(log->items[log->next_item++]); req->log->status = LOG_SLOT_BUSY; ip_addr = inet_ntoa (client_addr.sin_addr); ip_addr2 = NULL; if (ip_addr != NULL) { int len = strlen (ip_addr); ip_addr2 = malloc (len + 1); strcpy (ip_addr2, ip_addr); } req->log->client_ip_addr = ip_addr2; req->log->client_ip_port = client_addr.sin_port; gettimeofday (&(req->log->begin_time), NULL); } _beginthread (win32_http_request, 0, (void *) req); } return; #else pthread_t thread_id; int socket = skt->socket; int client; struct sockaddr_in client_addr; socklen_t len = sizeof (struct sockaddr_in); struct http_request *req; while (1) { /* never ending loop */ client = accept (socket, (struct sockaddr *) &client_addr, &len); if (client == -1) { close (socket); fprintf (stderr, "error from accept()\n"); return; } req = malloc (sizeof (struct http_request)); req->id = id++; req->port_no = port_no; req->socket = client; req->list = list; req->cached_capabilities = cached_capab; req->cached_capabilities_len = cached_capab_len; while (1) { /* looping until an available read connection is found */ for (ic = 0; ic < MAX_CONN; ic++) { /* selecting an available connection (if any) */ struct read_connection *ptr = &(pool->connections[ic]); if (ptr->status == CONNECTION_AVAILABLE) { conn = ptr; conn->status = CONNECTION_BUSY; goto conn_found; } } usleep (50); } conn_found: req->conn = conn; req->log_handle = db_handle; time (&now); diff = now - log->last_update; if (log->next_item < MAX_LOG && diff < 30) { /* reserving a free log slot */ req->log = &(log->items[log->next_item++]); req->log->status = LOG_SLOT_BUSY; ip_addr = inet_ntoa (client_addr.sin_addr); ip_addr2 = NULL; if (ip_addr != NULL) { int len = strlen (ip_addr); ip_addr2 = malloc (len + 1); strcpy (ip_addr2, ip_addr); } req->log->client_ip_addr = ip_addr2; req->log->client_ip_port = client_addr.sin_port; gettimeofday (&(req->log->begin_time), NULL); } else { /* flushing the log */ flush_log (db_handle, stmt_log, log); /* reserving a free log slot */ req->log = &(log->items[log->next_item++]); req->log->status = LOG_SLOT_BUSY; ip_addr = inet_ntoa (client_addr.sin_addr); ip_addr2 = NULL; if (ip_addr != NULL) { int len = strlen (ip_addr); ip_addr2 = malloc (len + 1); strcpy (ip_addr2, ip_addr); } req->log->client_ip_addr = ip_addr2; req->log->client_ip_port = client_addr.sin_port; gettimeofday (&(req->log->begin_time), NULL); } pthread_create (&thread_id, NULL, berkeley_http_request, (void *) req); } #endif } static int do_start_http (int port_no, struct neutral_socket *srv_skt) { /* starting the HTTP server */ #ifdef _WIN32 /* Winsockets */ WSADATA wd; SOCKET skt = INVALID_SOCKET; SOCKADDR_IN addr; if (WSAStartup (MAKEWORD (1, 1), &wd)) { fprintf (stderr, "unable to initialize winsock\n"); return 0; } skt = socket (AF_INET, SOCK_STREAM, 0); if (skt == INVALID_SOCKET) { fprintf (stderr, "unable to create a socket\n"); return 0; } addr.sin_family = AF_INET; addr.sin_port = htons (port_no); addr.sin_addr.s_addr = inet_addr ("127.0.0.1"); if (bind (skt, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR) { fprintf (stderr, "unable to bind the socket\n"); closesocket (skt); return 0; } if (listen (skt, SOMAXCONN) == SOCKET_ERROR) { fprintf (stderr, "unable to listen on the socket\n"); closesocket (skt); return 0; } srv_skt->socket = skt; #else /* standard Berkeley sockets */ int skt = -1; struct sockaddr_in addr; skt = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); if (skt == -1) { fprintf (stderr, "unable to create a socket\n"); return 0; } addr.sin_family = AF_INET; addr.sin_port = htons (port_no); addr.sin_addr.s_addr = htonl (INADDR_ANY); if (bind (skt, (struct sockaddr *) &addr, sizeof (addr)) == -1) { fprintf (stderr, "unable to bind the socket\n"); close (skt); return 0; } if (listen (skt, SOMAXCONN) == -1) { fprintf (stderr, "unable to listen on the socket\n"); close (skt); return 0; } srv_skt->socket = skt; #endif fprintf (stderr, "======================================================\n"); fprintf (stderr, " HTTP micro-server listening on port: %d\n", port_no); fprintf (stderr, "======================================================\n"); return 1; } static int get_geographic_extent (sqlite3 * handle, struct wms_layer *lyr) { /* computing the Geographic Bounding Box for some WMS layer */ int retcode = 0; const char *sql; sqlite3_stmt *stmt = NULL; int ret; sql = "SELECT MbrMinX(g.g), MbrMinY(g.g), MbrMaxX(g.g), MbrMaxY(g.g) " "FROM (SELECT ST_Transform(BuildMbr(?, ?, ?, ?, ?), 4326) AS g) AS g"; ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) { lyr->geo_minx = -180.0; lyr->geo_miny = -90.0; lyr->geo_maxx = 180.0; lyr->geo_maxy = 90.0; return 0; } sqlite3_reset (stmt); sqlite3_clear_bindings (stmt); sqlite3_bind_double (stmt, 1, lyr->minx); sqlite3_bind_double (stmt, 2, lyr->miny); sqlite3_bind_double (stmt, 3, lyr->maxx); sqlite3_bind_double (stmt, 4, lyr->maxy); sqlite3_bind_int (stmt, 5, lyr->srid); while (1) { ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; if (ret == SQLITE_ROW) { lyr->geo_minx = sqlite3_column_double (stmt, 0); lyr->geo_miny = sqlite3_column_double (stmt, 1); lyr->geo_maxx = sqlite3_column_double (stmt, 2); lyr->geo_maxy = sqlite3_column_double (stmt, 3); retcode = 1; } } sqlite3_finalize (stmt); return retcode; } static void compute_geographic_extents (sqlite3 * handle, struct wms_list *list) { /* computing the Geographic Bounding Box for every WMS layer */ struct wms_group *grp; struct wms_layer *lyr; /* first level layers */ lyr = list->first_layer; while (lyr != NULL) { if (get_geographic_extent (handle, lyr)) fprintf (stderr, "Publishing layer \"%s\"\n", lyr->layer_name); else lyr->valid = 0; lyr = lyr->next; } grp = list->first_group; while (grp != NULL) { /* validating layer groups */ int valid; int count; int srid; struct wms_style *style; struct wms_layer_ref *ref; ref = grp->first_child; valid = 1; count = 0; if (ref != NULL) srid = ref->layer_ref->srid; while (ref != NULL) { /* preliminary check */ if (ref->layer_ref->valid == 0) valid = 0; if (ref->layer_ref->srid != srid) valid = 0; count++; ref = ref->next; } if (count < 1 || valid == 0) { /* reporting an error condition */ fprintf (stderr, "ERROR: layer group \"%s\" has mismatching SRIDs !!!\n" "\t... will not be published\n", grp->group_name); grp->valid = 0; } else { /* final refinement pass */ double minx = DBL_MAX; double maxx = 0.0 - DBL_MAX; double miny = DBL_MAX; double maxy = 0.0 - DBL_MAX; double geo_minx = DBL_MAX; double geo_maxx = 0.0 - DBL_MAX; double geo_miny = DBL_MAX; double geo_maxy = 0.0 - DBL_MAX; ref = grp->first_child; while (ref != NULL) { struct wms_layer *l = ref->layer_ref; if (ref == grp->first_child) { grp->srid = l->srid; grp->is_geographic = l->is_geographic; if (l->minx < minx) minx = l->minx; if (l->maxx > maxx) maxx = l->maxx; if (l->miny < miny) miny = l->miny; if (l->maxy > maxy) maxy = l->maxy; if (l->geo_minx < geo_minx) geo_minx = l->geo_minx; if (l->geo_maxx > geo_maxx) geo_maxx = l->geo_maxx; if (l->geo_miny < geo_miny) geo_miny = l->geo_miny; if (l->geo_maxy > geo_maxy) geo_maxy = l->geo_maxy; } ref = ref->next; } grp->minx = minx; grp->maxx = maxx; grp->miny = miny; grp->maxy = maxy; grp->geo_minx = geo_minx; grp->geo_maxx = geo_maxx; grp->geo_miny = geo_miny; grp->geo_maxy = geo_maxy; /* validating Group Styles */ style = grp->first_style; while (style != NULL) { int valid; rl2GroupStylePtr stl; if (strcmp (style->name, "default") == 0) { /* ignoring default styles */ style->valid = 0; style = style->next; continue; } stl = rl2_create_group_style_from_dbms (handle, grp->group_name, style->name); if (stl == NULL) { /* reporting an error condition */ fprintf (stderr, "ERROR: layer group \"%s\" NULL style !!!\n" "\t... will be ignored\n", grp->group_name); style->valid = 0; style = style->next; continue; } rl2_is_valid_group_style (stl, &valid); if (!valid) { /* reporting an error condition */ int count; int idx; fprintf (stderr, "ERROR: layer group \"%s\" invalid style \"%s\" !!!\n" "\t... will be ignored\n", grp->group_name, rl2_get_group_style_name (stl)); rl2_get_group_style_count (stl, &count); for (idx = 0; idx < count; idx++) { rl2_is_valid_group_named_layer (stl, idx, &valid); if (!valid) fprintf (stderr, "\t%d/%d\tNamedLayer \"%s\" does not exists !!!\n", idx, count, rl2_get_group_named_layer (stl, idx)); rl2_is_valid_group_named_style (stl, idx, &valid); if (!valid) fprintf (stderr, "\t\tNamedStyle \"%s\" does not exists !!!\n", rl2_get_group_named_style (stl, idx)); } style->valid = 0; style = style->next; continue; } rl2_destroy_group_style (stl); style = style->next; } if (grp->valid) fprintf (stderr, "Publishing Layer Group \"%s\"\n", grp->group_name); grp = grp->next; } } } static int check_geographic_srid (sqlite3 * handle, int srid) { /* testing if some SRID is of the Geographic type */ int ret; char *sql; char **results; int rows; int columns; int i; int geo = -1; sql = sqlite3_mprintf ("SELECT Count(*) FROM spatial_ref_sys " "WHERE srid = %d AND proj4text LIKE '%%+proj=longlat%%'", srid); ret = sqlite3_get_table (handle, sql, &results, &rows, &columns, NULL); sqlite3_free (sql); if (ret != SQLITE_OK) goto skip; for (i = 1; i <= rows; i++) geo = atoi (results[(i * columns) + 0]); sqlite3_free_table (results); skip: if (geo > 0) return 1; return 0; } static struct wms_layer * load_layer (sqlite3 * handle, sqlite3_stmt * stmt) { /* creating a WMS layer from the resultset */ struct wms_layer *lyr = NULL; const char *coverage_name = (const char *) sqlite3_column_text (stmt, 0); const char *title = (const char *) sqlite3_column_text (stmt, 1); const char *abstract = (const char *) sqlite3_column_text (stmt, 2); const char *sample_type = (const char *) sqlite3_column_text (stmt, 3); const char *pixel_type = (const char *) sqlite3_column_text (stmt, 4); int num_bands = sqlite3_column_int (stmt, 5); int srid = sqlite3_column_int (stmt, 6); double minx = sqlite3_column_double (stmt, 7); double miny = sqlite3_column_double (stmt, 8); double maxx = sqlite3_column_double (stmt, 9); double maxy = sqlite3_column_double (stmt, 10); int is_geographic = check_geographic_srid (handle, srid); if (strcmp (sample_type, "1-BIT") == 0 && strcmp (pixel_type, "MONOCHROME") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_1_BIT, RL2_PIXEL_MONOCHROME, 1); if (strcmp (sample_type, "1-BIT") == 0 && strcmp (pixel_type, "PALETTE") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_1_BIT, RL2_PIXEL_PALETTE, 1); if (strcmp (sample_type, "2-BIT") == 0 && strcmp (pixel_type, "PALETTE") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_2_BIT, RL2_PIXEL_PALETTE, 1); if (strcmp (sample_type, "4-BIT") == 0 && strcmp (pixel_type, "PALETTE") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_4_BIT, RL2_PIXEL_PALETTE, 1); if (strcmp (sample_type, "UINT8") == 0 && strcmp (pixel_type, "PALETTE") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT8, RL2_PIXEL_PALETTE, 1); if (strcmp (sample_type, "UINT8") == 0 && strcmp (pixel_type, "GRAYSCALE") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT8, RL2_PIXEL_GRAYSCALE, 1); if (strcmp (sample_type, "UINT8") == 0 && strcmp (pixel_type, "RGB") == 0 && num_bands == 3) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT8, RL2_PIXEL_RGB, 3); if (strcmp (sample_type, "UINT16") == 0 && strcmp (pixel_type, "RGB") == 0 && num_bands == 3) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT16, RL2_PIXEL_RGB, 3); if (strcmp (sample_type, "UINT8") == 0 && strcmp (pixel_type, "MULTIBAND") == 0 && num_bands >= 2 && num_bands < 255) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT8, RL2_PIXEL_MULTIBAND, num_bands); if (strcmp (sample_type, "UINT16") == 0 && strcmp (pixel_type, "MULTIBAND") == 0 && num_bands >= 2 && num_bands < 255) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT16, RL2_PIXEL_MULTIBAND, num_bands); if (strcmp (sample_type, "INT8") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_INT8, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "UINT8") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT8, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "INT16") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_INT16, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "UINT16") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT16, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "INT32") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_INT32, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "UINT32") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_UINT32, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "FLOAT") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_FLOAT, RL2_PIXEL_DATAGRID, 1); if (strcmp (sample_type, "DOUBLE") == 0 && strcmp (pixel_type, "DATAGRID") == 0 && num_bands == 1) lyr = alloc_wms_layer (coverage_name, title, abstract, srid, is_geographic, minx, miny, maxx, maxy, RL2_SAMPLE_DOUBLE, RL2_PIXEL_DATAGRID, 1); return lyr; } static struct wms_list * get_raster_coverages (sqlite3 * handle) { /* preparing a list of available Raster Coverages */ struct wms_list *list = NULL; int ret; sqlite3_stmt *stmt; const char *sql; /* loading all layers */ sql = "SELECT coverage_name, title, abstract, sample_type, " "pixel_type, num_bands, srid, extent_minx, extent_miny, extent_maxx, extent_maxy " "FROM raster_coverages " "WHERE extent_minx IS NOT NULL AND extent_miny IS NOT NULL " "AND extent_maxx IS NOT NULL AND extent_maxy IS NOT NULL"; ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) return NULL; list = alloc_wms_list (); while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { struct wms_layer *lyr = load_layer (handle, stmt); if (lyr != NULL) { if (list->first_layer == NULL) list->first_layer = lyr; if (list->last_layer != NULL) list->last_layer->next = lyr; list->last_layer = lyr; } } } sqlite3_finalize (stmt); /* loading layer groups */ sql = "SELECT g.group_name, g.title, g.abstract, c.coverage_name " "FROM SE_styled_groups AS g " "LEFT JOIN SE_styled_group_refs AS c ON (g.group_name = c.group_name) " "ORDER BY c.paint_order"; ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) goto error; while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { struct wms_group *grp = NULL; struct wms_layer_ref *ref = NULL; struct wms_layer *lyr = NULL; const char *group_name = (const char *) sqlite3_column_text (stmt, 0); const char *title = (const char *) sqlite3_column_text (stmt, 1); const char *abstract = (const char *) sqlite3_column_text (stmt, 2); const char *coverage_name = (const char *) sqlite3_column_text (stmt, 3); grp = list->first_group; while (grp != NULL) { if (strcmp (grp->group_name, group_name) == 0) goto group_found; grp = grp->next; } grp = alloc_wms_group (group_name, title, abstract); if (list->first_group == NULL) list->first_group = grp; if (list->last_group != NULL) list->last_group->next = grp; list->last_group = grp; group_found: lyr = list->first_layer; while (lyr != NULL) { if (strcmp (lyr->layer_name, coverage_name) == 0) { /* child layer match */ lyr->child_layer = 1; ref = alloc_wms_layer_ref (lyr); break; } lyr = lyr->next; } if (ref != NULL) { if (grp->first_child == NULL) grp->first_child = ref; if (grp->last_child != NULL) grp->last_child->next = ref; grp->last_child = ref; } } } sqlite3_finalize (stmt); if (list->first_layer == NULL && list->first_group == NULL) goto error; return list; error: if (list != NULL) destroy_wms_list (list); return NULL; } static void get_raster_styles (sqlite3 * handle, struct wms_list *list) { /* retrieving all declared Raster Styles */ int ret; sqlite3_stmt *stmt; const char *sql = "SELECT coverage_name, name, title, abstract " "FROM SE_raster_styled_layers_view " "ORDER BY coverage_name, style_id"; ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) return; while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { const char *title = NULL; const char *abstract = NULL; const char *coverage_name = (const char *) sqlite3_column_text (stmt, 0); const char *name = (const char *) sqlite3_column_text (stmt, 1); if (sqlite3_column_type (stmt, 2) == SQLITE_TEXT) title = (const char *) sqlite3_column_text (stmt, 2); if (sqlite3_column_type (stmt, 3) == SQLITE_TEXT) abstract = (const char *) sqlite3_column_text (stmt, 3); add_style_to_wms_layer (list, coverage_name, name, title, abstract); } } sqlite3_finalize (stmt); add_default_styles (list); } static void get_group_styles (sqlite3 * handle, struct wms_list *list) { /* retrieving all declared Group Styles */ int ret; sqlite3_stmt *stmt; const char *sql = "SELECT group_name, name, title, abstract " "FROM SE_group_styles_view " "ORDER BY group_name, style_id"; ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL); if (ret != SQLITE_OK) return; while (1) { /* scrolling the result set rows */ ret = sqlite3_step (stmt); if (ret == SQLITE_DONE) break; /* end of result set */ if (ret == SQLITE_ROW) { const char *title = NULL; const char *abstract = NULL; const char *group_name = (const char *) sqlite3_column_text (stmt, 0); const char *name = (const char *) sqlite3_column_text (stmt, 1); if (sqlite3_column_type (stmt, 2) == SQLITE_TEXT) title = (const char *) sqlite3_column_text (stmt, 2); if (sqlite3_column_type (stmt, 3) == SQLITE_TEXT) abstract = (const char *) sqlite3_column_text (stmt, 3); add_style_to_wms_group (list, group_name, name, title, abstract); } } sqlite3_finalize (stmt); add_default_group_styles (list); } static void open_db (const char *path, sqlite3 ** handle, int cache_size, void *cache) { /* opening the DB */ sqlite3 *db_handle; int ret; char sql[1024]; *handle = NULL; fprintf (stderr, "\n======================================================\n"); fprintf (stderr, " WmsLite server startup\n"); fprintf (stderr, "======================================================\n"); fprintf (stderr, " SQLite version: %s\n", sqlite3_libversion ()); fprintf (stderr, " SpatiaLite version: %s\n", spatialite_version ()); fprintf (stderr, " RasterLite2 version: %s\n", rl2_version ()); fprintf (stderr, "======================================================\n"); /* enabling the SQLite's shared cache */ ret = sqlite3_enable_shared_cache (1); if (ret != SQLITE_OK) { fprintf (stderr, "unable to enable SQLite's shared cache: ERROR %d\n", ret); sqlite3_close (db_handle); return; } /* opening the main READ-WRITE connection */ ret = sqlite3_open_v2 (path, &db_handle, SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_SHAREDCACHE, NULL); if (ret != SQLITE_OK) { fprintf (stderr, "cannot open '%s': %s\n", path, sqlite3_errmsg (db_handle)); sqlite3_close (db_handle); return; } spatialite_init_ex (db_handle, cache, 0); rl2_init (db_handle, 0); /* enabling WAL journaling */ sprintf (sql, "PRAGMA journal_mode=WAL"); sqlite3_exec (db_handle, sql, NULL, NULL, NULL); if (cache_size > 0) { /* setting the CACHE-SIZE */ sprintf (sql, "PRAGMA cache_size=%d", cache_size); sqlite3_exec (db_handle, sql, NULL, NULL, NULL); } *handle = db_handle; return; } static void do_help () { /* printing the argument list */ fprintf (stderr, "\n\nusage: wmslite ARGLIST ]\n"); fprintf (stderr, "==============================================================\n"); fprintf (stderr, "-db or --db-path pathname RasterLite2 DB path\n"); fprintf (stderr, "-p or --ip-port number IP port number [default: 8080]\n\n"); fprintf (stderr, "-cs or --cache-size num DB cache size (how many pages)\n"); fprintf (stderr, "-dbg or --debug verbose debugginh mode\n"); } int main (int argc, char *argv[]) { /* the MAIN function simply perform arguments checking */ struct neutral_socket skt_ptr; sqlite3 *handle; sqlite3_stmt *stmt_log = NULL; const char *sql; int ret; int i; int error = 0; int next_arg = ARG_NONE; const char *db_path = NULL; int port_no = 8080; int cache_size = 0; void *cache; struct wms_list *list = NULL; struct connections_pool *pool; struct server_log *log; char *cached_capabilities = NULL; int cached_capabilities_len = 0; /* installing the signal handlers */ glob.handle = NULL; glob.stmt_log = NULL; glob.cached_capabilities = NULL; glob.log = NULL; glob.list = NULL; glob.pool = NULL; glob.cache = NULL; #ifdef _WIN32 SetConsoleCtrlHandler (signal_handler, TRUE); #else signal (SIGINT, signal_handler); signal (SIGTERM, signal_handler); #endif for (i = 1; i < argc; i++) { /* parsing the invocation arguments */ if (next_arg != ARG_NONE) { switch (next_arg) { case ARG_DB_PATH: db_path = argv[i]; break; case ARG_IP_PORT: port_no = atoi (argv[i]); break; case ARG_CACHE_SIZE: cache_size = atoi (argv[i]); break; }; next_arg = ARG_NONE; continue; } if (strcasecmp (argv[i], "--help") == 0 || strcmp (argv[i], "-h") == 0) { do_help (); return -1; } if (strcmp (argv[i], "-db") == 0 || strcasecmp (argv[i], "--db-path") == 0) { next_arg = ARG_DB_PATH; continue; } if (strcmp (argv[i], "-p") == 0 || strcasecmp (argv[i], "--ip-port") == 0) { next_arg = ARG_IP_PORT; continue; } if (strcasecmp (argv[i], "--cache-size") == 0 || strcmp (argv[i], "-cs") == 0) { next_arg = ARG_CACHE_SIZE; continue; } if (strcasecmp (argv[i], "--debug") == 0 || strcmp (argv[i], "-dbg") == 0) { debug_mode = 1; continue; } fprintf (stderr, "unknown argument: %s\n", argv[i]); error = 1; } if (error) { do_help (); return -1; } /* checking the arguments */ if (db_path == NULL) { fprintf (stderr, "did you forget to specify the --db-path arg ?\n"); error = 1; } if (error) { do_help (); return -1; } /* opening the DB */ cache = spatialite_alloc_connection (); glob.cache = cache; open_db (db_path, &handle, cache_size, cache); if (!handle) return -1; glob.handle = handle; list = get_raster_coverages (handle); if (list == NULL) { fprintf (stderr, "the DB \"%s\" doesn't contain any valid Raster Coverage\n", db_path); goto stop; } get_raster_styles (handle, list); get_group_styles (handle, list); glob.list = list; compute_geographic_extents (handle, list); build_get_capabilities (list, &cached_capabilities, &cached_capabilities_len); glob.cached_capabilities = cached_capabilities; /* creating the read connections pool */ pool = alloc_connections_pool (db_path); if (pool == NULL) { fprintf (stderr, "ERROR: unable to initialize a connections pool\n"); goto stop; } glob.pool = pool; /* creating the server log helper struct */ log = alloc_server_log (); if (log == NULL) { fprintf (stderr, "ERROR: unable to initialize the server log\n"); goto stop; } glob.log = log; /* starting the HTTP server */ if (!do_start_http (port_no, &skt_ptr)) goto stop; /* starting the logging facility */ sql = "CREATE TABLE IF NOT EXISTS wms_server_log (\n" "\tid INTEGER PRIMARY KEY AUTOINCREMENT,\n" "\ttimestamp TEXT NOT NULL,\n" "\tclient_ip_addr TEXT NOT NULL,\n" "\tclient_ip_port INTEGER NOT NULL,\n" "\thttp_method TEXT NOT NULL,\n" "\trequest_url TEXT NOT NULL,\n" "\thttp_status INTEGER NOT NULL,\n" "\tresponse_length INTEGER NOT NULL,\n" "\twms_request TEXT,\n" "\twms_version TEXT,\n" "\twms_layer TEXT,\n" "\twms_srid INTEGER,\n" "\twms_bbox_minx DOUBLE,\n" "\twms_bbox_miny DOUBLE,\n" "\twms_bbox_maxx DOUBLE,\n" "\twms_bbox_maxy DOUBLE,\n" "\twms_width INTEGER,\n" "\twms_height INTEGER,\n" "\twms_style TEXT,\n" "\twms_format TEXT,\n" "\twms_transparent INTEGER,\n" "\twms_bgcolor TEXT,\n" "\tmilliseconds INTEGER)"; sqlite3_exec (handle, sql, NULL, NULL, NULL); sql = "INSERT INTO wms_server_log (id, timestamp, client_ip_addr, " "client_ip_port, http_method, request_url, http_status, " "response_length, wms_request, wms_version, wms_layer, " "wms_srid, wms_bbox_minx, wms_bbox_miny, wms_bbox_maxx, " "wms_bbox_maxy, wms_width, wms_height, wms_style, " "wms_format, wms_transparent, wms_bgcolor, milliseconds) " "VALUES (NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt_log, NULL); if (ret != SQLITE_OK) { printf ("INSERT INTO LOG error: %s\n", sqlite3_errmsg (handle)); goto stop; } glob.stmt_log = stmt_log; /* looping on requests */ do_accept_loop (&skt_ptr, list, port_no, handle, stmt_log, pool, log, cached_capabilities, cached_capabilities_len); stop: destroy_wms_list (list); list = NULL; glob.list = NULL; clean_shutdown (); return 0; }