1 /*
2   Copyright (c) <2007-2012> <Barbara Philippot - Olivier Courtin>
3 
4   Permission is hereby granted, free of charge, to any person obtaining a copy
5   of this software and associated documentation files (the "Software"), to deal
6   in the Software without restriction, including without limitation the rights
7   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8   copies of the Software, and to permit persons to whom the Software is
9   furnished to do so, subject to the following conditions:
10 
11   The above copyright notice and this permission notice shall be included in
12   all copies or substantial portions of the Software.
13 
14   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20   IN THE SOFTWARE.
21 */
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <time.h>
29 
30 #include "../ows_define.h"
31 #include "ows.h"
32 
33 
34 /*
35  * Connect the ows to the database specified in configuration file
36  */
ows_pg(ows * o,char * con_str)37 static void ows_pg(ows * o, char *con_str)
38 {
39   assert(o);
40   assert(con_str);
41 
42   o->pg = PQconnectdb(con_str);
43 
44   if (PQstatus(o->pg) != CONNECTION_OK) {
45     ows_log(o, 1, PQerrorMessage(o->pg));
46     ows_error(o, OWS_ERROR_CONNECTION_FAILED, "Connection to database failed", "init_OWS");
47     return;
48   }
49 
50   if (PQsetClientEncoding(o->pg, o->db_encoding->buf)) {
51     ows_log(o, 1, PQerrorMessage(o->pg));
52     ows_error(o, OWS_ERROR_CONNECTION_FAILED, "Wrong database encoding", "init_OWS");
53     return;
54   }
55 
56   o->postgis_version = ows_psql_postgis_version(o);
57   if (!o->postgis_version)
58     ows_error(o, OWS_ERROR_CONNECTION_FAILED, "No PostGIS available in database", "init_OWS");
59   else if (ows_version_get(o->postgis_version) < 150)
60     ows_error(o, OWS_ERROR_CONNECTION_FAILED, "PostGIS version must be at least 1.5.0", "init_OWS");
61 }
62 
63 
64 /*
65  * Initialize an ows struct
66  */
ows_init()67 static ows *ows_init()
68 {
69   ows *o;
70 
71   o = malloc(sizeof(ows));
72   assert(o);
73 
74   o->init = true;
75   o->exit = false;
76   o->request = NULL;
77   o->cgi = NULL;
78   o->psql_requests = NULL;
79   o->pg = NULL;
80   o->pg_dsn = buffer_init();
81   o->output = stdout;
82   o->config_file = NULL;
83   o->mapfile = false;
84   o->online_resource = buffer_init();
85   o->schema_dir = buffer_init();
86   o->log_file = NULL;
87   o->log = NULL;
88   o->log_level=0;
89   o->encoding = buffer_init();
90   o->db_encoding = buffer_init();
91   o->layers = NULL;
92   o->max_features = 0;
93   o->degree_precision = 6;
94   o->meter_precision = 0;
95   o->max_geobbox = NULL;
96   o->display_bbox = true;
97   o->estimated_extent = false;
98   o->expose_pk = false;
99   o->check_schema = true;
100   o->check_valid_geom = true;
101   o->metadata = NULL;
102   o->contact = NULL;
103   o->postgis_version = NULL;
104   o->schema_wfs_100 = NULL;
105   o->schema_wfs_110 = NULL;
106   o->wfs_default_version = ows_version_init();
107   ows_version_set(o->wfs_default_version, 1, 1, 0);
108 
109   return o;
110 }
111 
112 
113 /*
114  * Flush an ows structure
115  * Used for debug purpose
116  */
117 #ifdef OWS_DEBUG
ows_flush(ows * o,FILE * output)118 void ows_flush(ows * o, FILE * output)
119 {
120   assert(o);
121   assert(output);
122 
123   fprintf(output, "exit : %d\n", o->exit?1:0);
124 
125   if (o->config_file)     fprintf(output, "config_file: %s\nmapfile %d\n", (char *) o->config_file->buf, o->mapfile?1:0);
126   if (o->schema_dir)      fprintf(output, "schema_dir: %s\n", (char *) o->schema_dir->buf);
127   if (o->online_resource) fprintf(output, "online_resource: %s\n", (char *) o->online_resource->buf);
128   if (o->pg_dsn)          fprintf(output, "pg: %s\n", (char *) o->pg_dsn->buf);
129   if (o->log_file)        fprintf(output, "log file: %s\n", (char *) o->log_file->buf);
130   if (o->encoding)        fprintf(output, "encoding: %s\n", (char *) o->encoding->buf);
131   if (o->db_encoding)     fprintf(output, "db_encoding: %s\n", (char *) o->db_encoding->buf);
132 
133   if (o->postgis_version) {
134     fprintf(output, "PostGIS version: %d.%d.%d\n", o->postgis_version->major,
135             o->postgis_version->minor,
136             o->postgis_version->release);
137   }
138 
139   if (o->wfs_default_version) {
140     fprintf(output, "WFS default version: %d.%d.%d\n", o->wfs_default_version->major,
141             o->wfs_default_version->minor,
142             o->wfs_default_version->release);
143   }
144 
145   if (o->metadata) {
146     fprintf(output, "metadata: ");
147     ows_metadata_flush(o->metadata, output);
148     fprintf(output, "\n");
149   }
150 
151   if (o->contact) {
152     fprintf(output, "contact: ");
153     ows_contact_flush(o->contact, output);
154     fprintf(output, "\n");
155   }
156 
157   if (o->cgi) {
158     fprintf(output, "cgi: ");
159     array_flush(o->cgi, output);
160     fprintf(output, "\n");
161   }
162 
163   if (o->psql_requests) {
164     fprintf(output, "SQL requests: ");
165     list_flush(o->psql_requests, output);
166     fprintf(output, "\n");
167   }
168 
169   if (o->layers) {
170     fprintf(output, "layers: ");
171     ows_layer_list_flush(o->layers, output);
172     fprintf(output, "\n");
173   }
174 
175   if (o->request) {
176     fprintf(output, "request: ");
177     ows_request_flush(o->request, output);
178     fprintf(output, "\n");
179   }
180 
181   fprintf(output, "max_features: %d\n", o->max_features);
182   fprintf(output, "degree_precision: %d\n", o->degree_precision);
183   fprintf(output, "meter_precision: %d\n", o->meter_precision);
184   fprintf(output, "expose_pk: %d\n", o->expose_pk?1:0);
185 
186   if (o->max_geobbox) {
187     fprintf(output, "max_geobbox: ");
188     ows_geobbox_flush(o->max_geobbox, output);
189     fprintf(output, "\n");
190   }
191   fprintf(output, "display_bbox: %d\n", o->display_bbox?1:0);
192   fprintf(output, "estimated_extent: %d\n", o->estimated_extent?1:0);
193   fprintf(output, "check_schema: %d\n", o->check_schema?1:0);
194   fprintf(output, "check_valid_geom: %d\n", o->check_valid_geom?1:0);
195 
196   fprintf(output, "schema WFS 1.0: %d\n", o->schema_wfs_100?1:0);
197   fprintf(output, "schema WFS 1.1: %d\n", o->schema_wfs_110?1:0);
198 }
199 #endif
200 
201 
202 /*
203  * Release ows struct
204  */
ows_free(ows * o)205 void ows_free(ows * o)
206 {
207   assert(o);
208 
209   if (o->config_file)          buffer_free(o->config_file);
210   if (o->schema_dir)           buffer_free(o->schema_dir);
211   if (o->online_resource)      buffer_free(o->online_resource);
212   if (o->pg)                   PQfinish(o->pg);
213   if (o->log_file)             buffer_free(o->log_file);
214   if (o->log)                  fclose(o->log);
215   if (o->pg_dsn)               buffer_free(o->pg_dsn);
216   if (o->cgi)                  array_free(o->cgi);
217   if (o->psql_requests)        list_free(o->psql_requests);
218   if (o->layers)               ows_layer_list_free(o->layers);
219   if (o->request)              ows_request_free(o->request);
220   if (o->max_geobbox)          ows_geobbox_free(o->max_geobbox);
221   if (o->metadata)             ows_metadata_free(o->metadata);
222   if (o->contact)              ows_contact_free(o->contact);
223   if (o->encoding)             buffer_free(o->encoding);
224   if (o->db_encoding)          buffer_free(o->db_encoding);
225   if (o->wfs_default_version)  ows_version_free(o->wfs_default_version);
226   if (o->postgis_version)      ows_version_free(o->postgis_version);
227   if (o->schema_wfs_100)       xmlSchemaFree(o->schema_wfs_100);
228   if (o->schema_wfs_110)       xmlSchemaFree(o->schema_wfs_110);
229 
230   free(o);
231   o = NULL;
232 }
233 
234 
ows_log(ows * o,int log_level,const char * log)235 void ows_log(ows *o, int log_level, const char *log)
236 {
237   char *t, *p;
238   time_t ts;
239 
240   if (o->log_file && !o->log) o->log = fopen(o->log_file->buf, "a");
241   if (!o->log || !(o->log_level & log_level)) return;
242 
243   ts = time(NULL);
244   t = ctime(&ts);
245 
246   /* Suppress ctime \n last char */
247   for (p = t; *p && *p != '\n'; p++);
248   *p = '\0';
249 
250   if      (log_level & 1) fprintf(o->log, "[%s] [ERROR] %s\n", t, log);
251   else if (log_level & 2) fprintf(o->log, "[%s] [EVENT] %s\n", t, log);
252   else if (log_level & 4) fprintf(o->log, "[%s] [QUERY] %s\n", t, log);
253   else if (log_level & 8) fprintf(o->log, "[%s] [SQL] %s\n", t, log);
254 
255   fflush(o->log);
256 }
257 
258 
ows_usage(ows * o)259 void ows_usage(ows * o)
260 {
261   fprintf(stdout, "TinyOWS version:   %s\n", TINYOWS_VERSION);
262 #ifdef TINYOWS_GIT_COMMIT
263   fprintf(stdout, "TinyOWS revision:  %s\n", TINYOWS_GIT_COMMIT);
264 #endif
265 #if TINYOWS_FCGI
266   fprintf(stdout, "FCGI support:      Yes\n");
267 #else
268   fprintf(stdout, "FCGI support:      No\n");
269 #endif
270   if (o->mapfile)
271     fprintf(stdout, "Config File Path:  %s (Mapfile)\n", o->config_file->buf);
272   else
273     fprintf(stdout, "Config File Path:  %s (TinyOWS XML)\n", o->config_file->buf);
274 
275   fprintf(stdout, "PostGIS Version:   %d.%d.%d\n", o->postgis_version->major,
276           o->postgis_version->minor,
277           o->postgis_version->release);
278 
279   fprintf(stdout, "PostGIS dsn:       %s\n", o->pg_dsn->buf);
280   fprintf(stdout, "Output Encoding:   %s\n", o->encoding->buf);
281   fprintf(stdout, "Database Encoding: %s\n", o->db_encoding->buf);
282   fprintf(stdout, "Schema dir:        %s\n", o->schema_dir->buf);
283   if (o->log_file) {
284     fprintf(stdout, "Log file:          %s\n", o->log_file->buf);
285     fprintf(stdout, "Log level:         %s%s%s%s\n",(o->log_level & 1)?"ERROR ":"",
286             (o->log_level & 2)?"EVENT ":"",
287             (o->log_level & 4)?"QUERY ":"",
288             (o->log_level & 8)?"SQL":"" );
289   }
290 
291   fprintf(stdout, "Display bbox:      %s\n", o->display_bbox?"Yes":"No");
292   fprintf(stdout, "Estimated extent:  %s\n", o->estimated_extent?"Yes":"No");
293   fprintf(stdout, "Check schema:      %s\n", o->check_schema?"Yes":"No");
294   fprintf(stdout, "Check valid geoms: %s\n", o->check_valid_geom?"Yes":"No");
295   if (o->max_features)
296     fprintf(stdout, "Max features:      %d\n", o->max_features);
297 
298   fprintf(stdout, "Available layers:\n");
299   ows_layers_storage_flush(o, stdout);
300 }
301 
302 
ows_kvp_or_xml(ows * o,char * query)303 static void ows_kvp_or_xml(ows *o, char *query)
304 {
305   /*
306    * Request encoding and HTTP method WFS 1.1.0 -> 6.5
307    */
308 
309   /* GET could only handle KVP */
310   if (cgi_method_get()) o->request->method = OWS_METHOD_KVP;
311 
312   /* POST could handle KVP or XML encoding */
313   else if (cgi_method_post()) {
314     /* WFS 1.1.0 mandatory */
315     if (       !strcmp(getenv("CONTENT_TYPE"), "application/x-www-form-urlencoded")
316                || !strncmp(getenv("CONTENT_TYPE"), "application/x-www-form-urlencoded;", 34))
317       o->request->method = OWS_METHOD_KVP;
318     else if (    !strcmp(getenv("CONTENT_TYPE"), "text/xml")
319                  || !strncmp(getenv("CONTENT_TYPE"), "text/xml;", 9))        /* Allowing charset */
320       o->request->method = OWS_METHOD_XML;
321 
322     /* WFS 1.0.0 && CITE Test compliant */
323     else if (    !strcmp(getenv("CONTENT_TYPE"),  "application/xml")
324                  || !strcmp(getenv("CONTENT_TYPE"), "text/plain")
325                  || !strncmp(getenv("CONTENT_TYPE"), "application/xml;", 16) /* Allowing charset */
326                  || !strncmp(getenv("CONTENT_TYPE"), "text/plain;", 11))     /* Allowing charset */
327       o->request->method = OWS_METHOD_XML;
328 
329     /* Command line Unit Test cases with XML values (not HTTP) */
330   } else if (!cgi_method_post() && !cgi_method_get() && query[0] == '<')
331     o->request->method = OWS_METHOD_XML;
332   else if (!cgi_method_post() && !cgi_method_get())
333     o->request->method = OWS_METHOD_KVP;
334 
335   else ows_error(o, OWS_ERROR_REQUEST_HTTP, "Wrong HTTP request Method", "http");
336 }
337 
338 
main(int argc,char * argv[])339 int main(int argc, char *argv[])
340 {
341   ows *o;
342   char *query;
343 
344   o = ows_init();
345   o->config_file = buffer_init();
346 
347   /* Config Files */
348   if (getenv("TINYOWS_CONFIG_FILE"))
349     buffer_add_str(o->config_file, getenv("TINYOWS_CONFIG_FILE"));
350   else if (getenv("TINYOWS_MAPFILE")) {
351     buffer_add_str(o->config_file, getenv("TINYOWS_MAPFILE"));
352     o->mapfile = true;
353   } else
354     buffer_add_str(o->config_file, OWS_CONFIG_FILE_PATH);
355 
356   LIBXML_TEST_VERSION
357   xmlInitParser();
358 
359   /* Parse the configuration file and initialize ows struct */
360   if (!o->exit) ows_parse_config(o, o->config_file->buf);
361   if (!o->exit) ows_log(o, 2, "== TINYOWS STARTUP ==");
362 
363   /* Connect the ows to the database */
364   if (!o->exit) ows_pg(o, o->pg_dsn->buf);
365   if (!o->exit) ows_log(o, 2, "== Connection PostGIS ==");
366 
367   /* Fill layers storage metadata */
368   if (!o->exit) ows_layers_storage_fill(o);
369   if (!o->exit) ows_log(o, 2, "== Filling Storage ==");
370 
371   o->init = false;
372 
373 #if TINYOWS_FCGI
374   if (!o->exit) ows_log(o, 2, "== FCGI START ==");
375   while (FCGI_Accept() >= 0) {
376 #endif
377 
378     query=NULL;
379     if (!o->exit) query = cgi_getback_query(o);  /* Retrieve safely query string */
380     if (!o->exit) ows_log(o, 4, query);          /* Log input query if asked */
381 
382     if (!o->exit && (!query || !strlen(query))) {
383       /* Usage or Version command line options */
384       if (argc > 1) {
385         if (    !strncmp(argv[1], "--help", 6)
386                 || !strncmp(argv[1], "-h", 2)
387                 || !strncmp(argv[1], "--check", 7)) ows_usage(o);
388 
389         else if (    !strncmp(argv[1], "--version", 9)
390                      || !strncmp(argv[1], "-v", 2))
391           fprintf(stdout, "%s\n", TINYOWS_VERSION);
392 
393         else ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "Service Unknown", "service");
394 
395       } else ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "Service Unknown", "service");
396 
397       o->exit=true;  /* Have done what we have to */
398     }
399 
400     if (!o->exit) o->request = ows_request_init();
401     if (!o->exit) ows_kvp_or_xml(o, query);  /* Method is KVP or XML ? */
402 
403     if (!o->exit) {
404 
405       switch (o->request->method) {
406         case OWS_METHOD_KVP:
407           o->cgi = cgi_parse_kvp(o, query);
408           break;
409         case OWS_METHOD_XML:
410           o->cgi = cgi_parse_xml(o, query);
411           break;
412 
413         default:
414           ows_error(o, OWS_ERROR_REQUEST_HTTP, "Wrong HTTP request Method", "http");
415       }
416     }
417 
418     if (!o->exit) o->psql_requests = list_init();
419     if (!o->exit) ows_metadata_fill(o, o->cgi);                    /* Fill service's metadata */
420     if (!o->exit) ows_request_check(o, o->request, o->cgi, query); /* Process service request */
421 
422     /* Run the right OWS service */
423     if (!o->exit) {
424       switch (o->request->service) {
425         case WFS:
426           o->request->request.wfs = wfs_request_init();
427           wfs_request_check(o, o->request->request.wfs, o->cgi);
428           if (!o->exit) wfs(o, o->request->request.wfs);
429           break;
430         default:
431           ows_error(o, OWS_ERROR_INVALID_PARAMETER_VALUE, "Service Unknown", "service");
432       }
433     }
434 
435     if (o->request) {
436       ows_request_free(o->request);
437       o->request=NULL;
438     }
439 
440     /* We allocated memory only on post case */
441     if (cgi_method_post() && query) free(query);
442 
443 #if TINYOWS_FCGI
444     fflush(stdout);
445     o->exit = false;
446   }
447   ows_log(o, 2, "== FCGI SHUTDOWN ==");
448   OS_LibShutdown();
449 #endif
450   ows_log(o, 2, "== TINYOWS SHUTDOWN ==");
451   ows_free(o);
452 
453   xmlCleanupParser();
454 
455   return EXIT_SUCCESS;
456 }
457