source: branches/prototype-v0/zoo-project/zoo-kernel/server_internal.c @ 839

Last change on this file since 839 was 839, checked in by djay, 7 years ago

Update the source code for HPC support. Automatically adding nested outputs for the HPC support (should this be available for every support?). Add capability to store the metadata in the Collection DataBase?. Addition of the zcfg2sql to import any existing ZCFG file into the Collection DB. Add the support to invoke a callback (for history purpose) in case a [callback] section contains at least one parameter defined (url). Add support to convert maps and map to JSON (for callback use only by now). Fix some memory leaks (some are still there).

  • Property svn:keywords set to Id
File size: 35.5 KB
Line 
1/*
2 * Author : Gérald Fenoy
3 *
4 *  Copyright 2008-2015 GeoLabs SARL. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25#include "server_internal.h"
26#include "service_internal.h"
27#include "response_print.h"
28#include "mimetypes.h"
29#ifndef WIN32
30#include <dlfcn.h>
31#include <uuid/uuid.h>
32#else
33#include <rpc.h>
34#define ERROR_MSG_MAX_LENGTH 1024
35#endif
36#include <signal.h>
37
38// #include <stdlib.h>
39/*
40 * Compare two file path strings to see if they refer to the same file.
41 *
42 * @param path1 the first file path
43 * @param path2 the second file path
44 *
45 * @return 0 if the files are identical
46 */
47#define PATHBUFSIZE 4096
48int zoo_path_compare(char* path1, char* path2) {
49
50  if (path1 == NULL || path2 == NULL) {
51    return -1;
52  }
53
54  char realpath1[PATHBUFSIZE];
55  char realpath2[PATHBUFSIZE];
56
57#ifdef WIN32
58  int res1 = GetFullPathName(path1, PATHBUFSIZE, realpath1, NULL);
59  int res2 = GetFullPathName(path2, PATHBUFSIZE, realpath2, NULL);
60
61  if (res1 == 0 || res2 == 0) {
62    return -1;
63  }
64  else {
65    return strncasecmp(realpath1, realpath2, PATHBUFSIZE);
66  }
67#else
68  char* ptr1 = realpath(path1, realpath1);
69  char* ptr2 = realpath(path2, realpath2);
70
71  if (ptr1 == NULL || ptr2 == NULL) {
72    return -1;
73  }
74  else {
75    return strncmp(realpath1, realpath2, PATHBUFSIZE);
76  }
77#endif
78}
79
80/**
81 * Detect WPS version used (1.0.0 or 2.0.0).
82 *
83 * @param version number as char* (1.0.0 or 2.0.0)
84 * @return 0 in case of version 1.0.0, 1 for 2.0.0, -1 in other case
85 */
86int getVersionId(const char* version){
87  int schemaId=0;
88  for(;schemaId<2;schemaId++){
89    if(strncasecmp(version,schemas[schemaId][0],5)==0)
90      return schemaId;
91  }
92  return -1;
93}
94
95/**
96 * Generate a UUID.
97 * ref: https://www.ietf.org/rfc/rfc4122.txt / 4.2
98 *
99 * @return a new char* containing the UUID, make sure to free the returned
100 *  resource once used.
101 */
102char *get_uuid(){
103  char *res=(char*)malloc(37*sizeof(char));
104#ifdef WIN32
105  UUID uuid;
106  UuidCreate(&uuid);
107  RPC_CSTR rest = NULL;
108  UuidToString(&uuid,&rest);
109#else
110  uuid_t uuid;
111  uuid_generate_time(uuid);
112  char rest[128];
113  uuid_unparse(uuid,rest);
114#endif
115  sprintf(res,"%s",rest);
116#ifdef WIN32
117  RpcStringFree(&rest);
118#endif
119  return res;
120}
121
122/**
123 * Extract the service identifier from the full service identifier
124 * ie:
125 *  - Full service name: OTB.BandMath
126 *  - Service name: BandMath
127 *
128 * @param conf the maps containing the settings of the main.cfg file
129 * @param conf_dir the full path to the ZOO-Kernel directory
130 * @param identifier the full service name (potentialy including a prefix, ie:
131 *  Prefix.MyService)
132 * @param buffer the resulting service identifier (without any prefix)
133 */
134void parseIdentifier(maps* conf,char* conf_dir,char *identifier,char* buffer){
135  setMapInMaps(conf,"lenv","oIdentifier",identifier);
136  char *lid=zStrdup(identifier);
137  char *saveptr1;
138  char *tmps1=strtok_r(lid,".",&saveptr1);
139  int level=0;
140  char key[25];
141  char levels[18];
142  while(tmps1!=NULL){
143    char *test=zStrdup(tmps1);
144    char* tmps2=(char*)malloc((strlen(test)+2)*sizeof(char));
145    sprintf(key,"sprefix_%d",level);
146    sprintf(tmps2,"%s.",test);
147    sprintf(levels,"%d",level);
148    setMapInMaps(conf,"lenv","level",levels);
149    setMapInMaps(conf,"lenv",key,tmps2);
150    free(tmps2);
151    free(test);
152    level++;
153    tmps1=strtok_r(NULL,".",&saveptr1);
154  }
155  int i=0;
156  sprintf(buffer,"%s",conf_dir);
157  for(i=0;i<level;i++){
158    char *tmp0=zStrdup(buffer);
159    sprintf(key,"sprefix_%d",i);
160    map* tmp00=getMapFromMaps(conf,"lenv",key);
161    if(tmp00!=NULL)
162      sprintf(buffer,"%s/%s",tmp0,tmp00->value);
163    free(tmp0);
164    buffer[strlen(buffer)-1]=0;
165    if(i+1<level){ 
166      #ifdef IGNORE_METAPATH
167        map* tmpMap = createMap("metapath", "");
168      #else 
169        map* tmpMap=getMapFromMaps(conf,"lenv","metapath");
170      #endif     
171      if(tmpMap==NULL || strlen(tmpMap->value)==0){
172        char *tmp01=zStrdup(tmp00->value);
173        tmp01[strlen(tmp01)-1]=0;
174        setMapInMaps(conf,"lenv","metapath",tmp01);
175        free(tmp01);
176        tmp01=NULL;
177      }
178      else{
179        if(tmp00!=NULL && tmpMap!=NULL){
180          char *tmp00s=zStrdup(tmp00->value);
181          tmp00s[strlen(tmp00s)-1]=0;
182          char *value=(char*)malloc((strlen(tmp00s)+strlen(tmpMap->value)+2)*sizeof(char));
183          sprintf(value,"%s/%s",tmpMap->value,tmp00s);
184          setMapInMaps(conf,"lenv","metapath",value);
185          free(value);
186          free(tmp00s);
187          value=NULL;
188        }
189      }
190    }else{
191      char *tmp01=zStrdup(tmp00->value);
192      tmp01[strlen(tmp01)-1]=0;
193      setMapInMaps(conf,"lenv","Identifier",tmp01);
194      free(tmp01);
195    }
196  }
197  char *tmp0=zStrdup(buffer);
198  sprintf(buffer,"%s.zcfg",tmp0);
199  free(tmp0);
200  free(lid);
201}
202
203/**
204 * Converts a hex character to its integer value
205 *
206 * @param ch the char to convert
207 * @return the converted char
208 */
209char from_hex(char ch) {
210  return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
211}
212
213/**
214 * Converts an integer value to its hec character
215 *
216 * @param code the char to convert
217 * @return the converted char
218 */
219char to_hex(char code) {
220  static char hex[] = "0123456789abcdef";
221  return hex[code & 15];
222}
223
224/**
225 * URLEncode an url
226 *
227 * @param str the url to encode
228 * @return a url-encoded version of str
229 * @warning be sure to free() the returned string after use
230 */
231char *url_encode(char *str) {
232  char *pstr = str, *buf = (char*) malloc(strlen(str) * 3 + 1), *pbuf = buf;
233  while (*pstr) {
234    if (isalnum(*pstr) || *pstr == '-' || *pstr == '_' || *pstr == '.' || *pstr == '~') 
235      *pbuf++ = *pstr;
236    else if (*pstr == ' ') 
237      *pbuf++ = '+';
238    else 
239      *pbuf++ = '%', *pbuf++ = to_hex(*pstr >> 4), *pbuf++ = to_hex(*pstr & 15);
240    pstr++;
241  }
242  *pbuf = '\0';
243  return buf;
244}
245
246/**
247 * Decode an URLEncoded url
248 *
249 * @param str the URLEncoded url to decode
250 * @return a url-decoded version of str
251 * @warning be sure to free() the returned string after use
252 */
253char *url_decode(char *str) {
254  char *pstr = str, *buf = (char*) malloc(strlen(str) + 1), *pbuf = buf;
255  while (*pstr) {
256    if (*pstr == '%') {
257      if (pstr[1] && pstr[2]) {
258        *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]);
259        pstr += 2;
260      }
261    } else if (*pstr == '+') { 
262      *pbuf++ = ' ';
263    } else {
264      *pbuf++ = *pstr;
265    }
266    pstr++;
267  }
268  *pbuf = '\0';
269  return buf;
270}
271
272/**
273 * Verify if a given language is listed in the lang list defined in the [main]
274 * section of the main.cfg file.
275 *
276 * @param conf the map containing the settings from the main.cfg file
277 * @param str the specific language
278 * @return 1 if the specific language is listed, -1 in other case.
279 */
280int isValidLang(maps* conf,const char *str){
281  map *tmpMap=getMapFromMaps(conf,"main","language");
282  char *tmp0=NULL,*tmp=NULL,*tmp1=NULL;
283  if(tmpMap!=NULL)
284    tmp0=zStrdup(tmpMap->value);
285  tmpMap=getMapFromMaps(conf,"main","lang");
286  if(tmpMap!=NULL)
287    tmp=zStrdup(tmpMap->value);
288  if(tmp0!=NULL && tmp!=NULL){
289    tmp1=(char*)malloc((strlen(tmp0)+strlen(tmp)+2)*sizeof(char));
290    sprintf(tmp1,"%s,%s",tmp0,tmp);
291    free(tmp0);
292    free(tmp);
293  }else{
294    if(tmp!=NULL){
295      tmp1=zStrdup(tmp);
296      free(tmp);
297    }else{
298      if(tmp0!=NULL){
299        tmp1=zStrdup(tmp0);
300        free(tmp0);
301      }
302    }
303  }
304  char *pToken,*saveptr;
305  pToken=strtok_r(tmp1,",",&saveptr);
306  int res=-1;
307  while(pToken!=NULL){
308    if(strcasecmp(str,pToken)==0){
309      res=1;
310      break;
311    }
312    pToken=strtok_r(NULL,",",&saveptr);
313  }
314  if(tmp1!=NULL)
315    free(tmp1);
316  return res;
317}
318
319
320/**
321 * Access the value of the encoding key in a maps
322 *
323 * @param m the maps to search for the encoding key
324 * @return the value of the encoding key in a maps if encoding key exists,
325 *  "UTF-8" in other case.
326 */
327char* getEncoding(maps* m){
328  if(m!=NULL){
329    map* tmp=getMap(m->content,"encoding");
330    if(tmp!=NULL){
331      return tmp->value;
332    }
333    else
334      return (char*)"UTF-8";
335  }
336  else
337    return (char*)"UTF-8"; 
338}
339
340/**
341 * Access the value of the version key in a maps
342 *
343 * @param m the maps to search for the version key
344 * @return the value of the version key in a maps if encoding key exists,
345 *  "1.0.0" in other case.
346 */
347char* getVersion(maps* m){
348  if(m!=NULL){
349    map* tmp=getMap(m->content,"version");
350    if(tmp!=NULL){
351      return tmp->value;
352    }
353    else
354      return (char*)"1.0.0";
355  }
356  else
357    return (char*)"1.0.0";
358}
359
360/**
361 * Read a file generated by a service.
362 *
363 * @param m the conf maps
364 * @param content the output item
365 * @param filename the file to read
366 */
367void readGeneratedFile(maps* m,map* content,char* filename){
368  FILE * file=fopen(filename,"rb");
369  if(file==NULL){
370    setMapInMaps(m,"lenv","message","Unable to read produced file. Please try again later");
371    return ;
372  }
373  fseek(file, 0, SEEK_END);
374  long count = ftell(file);
375  rewind(file);
376  struct stat file_status; 
377  stat(filename, &file_status);
378  map* tmpMap1=getMap(content,"value");
379  if(tmpMap1==NULL){
380    addToMap(content,"value","");
381    tmpMap1=getMap(content,"value");
382  }
383  free(tmpMap1->value);
384  tmpMap1->value=(char*) malloc((count+1)*sizeof(char)); 
385  fread(tmpMap1->value,1,count,file);
386  tmpMap1->value[count]=0;
387  fclose(file);
388  char rsize[1000];
389  sprintf(rsize,"%ld",count);
390  addToMap(content,"size",rsize);
391}
392
393
394/**
395 * Write a file from value and length
396 *
397 * @param fname the file name
398 * @param val the value
399 * @param length the value length
400 */
401int writeFile(char* fname,char* val,int length){
402  FILE* of=fopen(fname,"wb");
403  if(of==NULL){
404    return -1;
405  }
406  size_t ret=fwrite(val,sizeof(char),length,of);
407  if(ret<length){
408    fprintf(stderr,"Write error occurred!\n");
409    fclose(of);
410    return -1;
411  }
412  fclose(of);
413  return 1;
414}
415
416/**
417 * Dump all values in a maps as files
418 *
419 * @param main_conf the maps containing the settings of the main.cfg file
420 * @param in the maps containing values to dump as files
421 */
422void dumpMapsValuesToFiles(maps** main_conf,maps** in){
423  map* tmpPath=getMapFromMaps(*main_conf,"main","tmpPath");
424  map* tmpSid=getMapFromMaps(*main_conf,"lenv","usid");
425  maps* inputs=*in;
426  int length=0;
427  while(inputs!=NULL){
428    if(getMap(inputs->content,"mimeType")!=NULL &&
429       getMap(inputs->content,"cache_file")==NULL){
430      map* cMap=inputs->content;
431      if(getMap(cMap,"length")!=NULL){
432        map* tmpLength=getMap(cMap,"length");
433        int len=atoi(tmpLength->value);
434        int k=0;
435        for(k=0;k<len;k++){
436          map* cMimeType=getMapArray(cMap,"mimeType",k);
437          map* cValue=getMapArray(cMap,"value",k);
438          map* cSize=getMapArray(cMap,"size",k);
439          char file_ext[32];
440          getFileExtension(cMimeType != NULL ? cMimeType->value : NULL, file_ext, 32);
441          char* val=(char*)malloc((strlen(tmpPath->value)+strlen(inputs->name)+strlen(tmpSid->value)+strlen(file_ext)+16)*sizeof(char));
442          sprintf(val,"%s/Input_%s_%s_%d.%s",tmpPath->value,inputs->name,tmpSid->value,k,file_ext);
443          length=0;
444          if(cSize!=NULL){
445            length=atoi(cSize->value);
446          }
447          writeFile(val,cValue->value,length);
448          setMapArray(cMap,"cache_file",k,val);
449          free(val);
450        }
451      }else{
452        int length=0;
453        map* cMimeType=getMap(cMap,"mimeType");
454        map* cValue=getMap(cMap,"value");
455        map* cSize=getMap(cMap,"size");
456        char file_ext[32];
457        getFileExtension(cMimeType != NULL ? cMimeType->value : NULL, file_ext, 32);
458        char *val=(char*)malloc((strlen(tmpPath->value)+strlen(inputs->name)+strlen(tmpSid->value)+strlen(file_ext)+16)*sizeof(char));
459        sprintf(val,"%s/Input_%s_%s_%d.%s",tmpPath->value,inputs->name,tmpSid->value,0,file_ext);
460        if(cSize!=NULL){
461          length=atoi(cSize->value);
462        }
463        writeFile(val,cValue->value,length);
464        addToMap(cMap,"cache_file",val);
465        free(val);
466      }
467    }
468    inputs=inputs->next;
469  }
470}
471
472
473/**
474 * Base64 encoding of a char*
475 *
476 * @param input the value to encode
477 * @param length the value length
478 * @return the buffer containing the base64 value
479 * @warning make sure to free the returned value
480 */
481char *base64(const char *input, int length)
482{
483  BIO *bmem, *b64;
484  BUF_MEM *bptr;
485
486  b64 = BIO_new(BIO_f_base64());
487  BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
488  bmem = BIO_new(BIO_s_mem());
489  b64 = BIO_push(b64, bmem);
490  BIO_write(b64, input, length);
491  BIO_flush(b64);
492  BIO_get_mem_ptr(b64, &bptr);
493
494  char *buff = (char *)malloc((bptr->length+1)*sizeof(char));
495  memcpy(buff, bptr->data, bptr->length);
496  buff[bptr->length] = 0;
497
498  BIO_free_all(b64);
499
500  return buff;
501}
502
503/**
504 * Base64 decoding of a char*
505 *
506 * @param input the value to decode
507 * @param length the value length
508 * @param red the value length
509 * @return the buffer containing the base64 value
510 * @warning make sure to free the returned value
511 */
512char *base64d(const char *input, int length,int* red)
513{
514  BIO *b64, *bmem;
515
516  char *buffer = (char *)malloc(length);
517  if(buffer){
518    memset(buffer, 0, length);
519    b64 = BIO_new(BIO_f_base64());
520    if(b64){
521      bmem = BIO_new_mem_buf((unsigned char*)input,length);
522      bmem = BIO_push(b64, bmem);
523      *red=BIO_read(bmem, buffer, length);
524      buffer[length-1]=0;
525      BIO_free_all(bmem);
526    }
527  }
528  return buffer;
529}
530
531/**
532 * Read Base64 value and split it value by lines of 64 char.
533 *
534 * @param in the map containing the value to split
535 */
536void readBase64(map **in){
537  char *res = NULL;
538  char *curs = (*in)->value;
539  int i = 0;
540  for (i = 0; i <= strlen ((*in)->value) / 64;
541       i++)
542    {
543      if (res == NULL)
544        res =
545          (char *) malloc (65 * sizeof (char));
546      else
547        res =
548          (char *) realloc (res,
549                            (((i + 1) * 65) +
550                             i) * sizeof (char));
551      int csize = i * 65;
552      strncpy (res + csize, curs, 64);
553      if (i == strlen ((*in)->value) / 64)
554        strcat (res, "\n\0");
555      else
556        {
557          strncpy (res + (((i + 1) * 64) + i),
558                   "\n\0", 2);
559          curs += 64;
560        }
561    }
562  free ((*in)->value);
563  (*in)->value = zStrdup (res);
564  free (res);
565}
566
567
568/**
569 * Add the default values defined in the zcfg to a maps.
570 *
571 * @param out the maps containing the inputs or outputs given in the initial
572 *  HTTP request
573 * @param in the description of all inputs or outputs available for a service
574 * @param m the maps containing the settings of the main.cfg file
575 * @param type 0 for inputs and 1 for outputs
576 * @param err the map to store potential missing mandatory input parameters or
577 *  wrong output names depending on the type.
578 * @return "" if no error was detected, the name of last input or output causing
579 *  an error.
580 */
581char* addDefaultValues(maps** out,elements* in,maps* m,int type,map** err){
582  map *res=*err;
583  elements* tmpInputs=in;
584  elements* tmpInputss=NULL;
585  maps* out1=*out;
586  maps* out1s=NULL;
587  char *result=NULL;
588  int nb=0;
589  int inb=0;
590 loopOnInputs:
591  if(type==1){
592    while(out1!=NULL){
593      if(getElements(in,out1->name)==NULL){
594        if(res==NULL){
595          res=createMap("value",out1->name);
596        }else{
597          setMapArray(res,"value",nb,out1->name);
598        }
599        nb++;
600        result=out1->name;
601      }
602      inb++;
603      out1=out1->next;
604    }
605    if(res!=NULL){
606      fflush(stderr);
607      *err=res;
608      return result;
609    }
610    if(out1==NULL && inb>=1)
611      out1=*out;
612  }
613  while(tmpInputs!=NULL){
614    maps *tmpMaps=getMaps(out1,tmpInputs->name);
615    if(tmpMaps==NULL){
616      maps* tmpMaps2=createMaps(tmpInputs->name);
617      if(type==0){
618        map* tmpMapMinO=getMap(tmpInputs->content,"minOccurs");
619        if(tmpMapMinO!=NULL){
620          if(atoi(tmpMapMinO->value)>=1){
621            freeMaps(&tmpMaps2);
622            free(tmpMaps2);
623            if(res==NULL){
624              res=createMap("value",tmpInputs->name);
625            }else{
626              setMapArray(res,"value",nb,tmpInputs->name);
627            }
628            nb++;
629            result=tmpInputs->name;
630          }
631          else{
632            if(tmpMaps2->content==NULL)
633              tmpMaps2->content=createMap("minOccurs",tmpMapMinO->value);
634            else
635              addToMap(tmpMaps2->content,"minOccurs",tmpMapMinO->value);
636          }
637        }
638        if(res==NULL){
639          map* tmpMaxO=getMap(tmpInputs->content,"maxOccurs");
640          if(tmpMaxO!=NULL){
641            if(tmpMaps2->content==NULL)
642              tmpMaps2->content=createMap("maxOccurs",tmpMaxO->value);
643            else
644              addToMap(tmpMaps2->content,"maxOccurs",tmpMaxO->value);
645          }
646          map* tmpMaxMB=getMap(tmpInputs->content,"maximumMegabytes");
647          if(tmpMaxMB!=NULL){
648            if(tmpMaps2->content==NULL)
649              tmpMaps2->content=createMap("maximumMegabytes",tmpMaxMB->value);
650            else
651              addToMap(tmpMaps2->content,"maximumMegabytes",tmpMaxMB->value);
652          }
653        }
654      }
655     
656      if(res==NULL){
657        iotype* tmpIoType=tmpInputs->defaults;
658        if(tmpIoType!=NULL){
659          map* tmpm=tmpIoType->content;
660          while(tmpm!=NULL){
661            if(tmpMaps2->content==NULL)
662              tmpMaps2->content=createMap(tmpm->name,tmpm->value);
663            else{
664              addToMap(tmpMaps2->content,tmpm->name,tmpm->value);
665            }
666            tmpm=tmpm->next;
667          }
668        }
669        if(tmpMaps2->content==NULL){
670          tmpMaps2->content=createMap("inRequest","false");
671          dumpMaps(tmpMaps2);
672        }
673        else
674          addToMap(tmpMaps2->content,"inRequest","false");
675        if(type==0){
676          map *tmpMap=getMap(tmpMaps2->content,"value");
677          if(tmpMap==NULL)
678            addToMap(tmpMaps2->content,"value","NULL");
679        }
680        elements* tmpElements=getElements(in,tmpMaps2->name);
681        if(tmpElements!=NULL && tmpElements->child!=NULL){
682          char *res=addDefaultValues(&tmpMaps2->child,tmpElements->child,m,type,err);
683          if(strlen(res)>0){
684            return res;
685          }
686        }
687
688        if(out1==NULL){
689          *out=dupMaps(&tmpMaps2);
690          out1=*out;
691        }
692        else
693          addMapsToMaps(&out1,tmpMaps2);
694        freeMap(&tmpMaps2->content);
695        free(tmpMaps2->content);
696        tmpMaps2->content=NULL;
697        freeMaps(&tmpMaps2);
698        free(tmpMaps2);
699        tmpMaps2=NULL;
700      }
701    }
702    else /*toto*/{ 
703      iotype* tmpIoType=NULL;
704      if(tmpMaps->content!=NULL){
705        tmpIoType=getIoTypeFromElement(tmpInputs,tmpInputs->name,
706                                       tmpMaps->content);
707        if(type==0) {
708          /**
709           * In case of an Input maps, then add the minOccurs and maxOccurs to the
710           * content map.
711           */
712          map* tmpMap1=getMap(tmpInputs->content,"minOccurs");
713          if(tmpMap1!=NULL){
714            if(tmpMaps->content==NULL)
715              tmpMaps->content=createMap("minOccurs",tmpMap1->value);
716            else
717              addToMap(tmpMaps->content,"minOccurs",tmpMap1->value);
718          }
719          map* tmpMaxO=getMap(tmpInputs->content,"maxOccurs");
720          if(tmpMaxO!=NULL){
721            if(tmpMaps->content==NULL)
722              tmpMaps->content=createMap("maxOccurs",tmpMaxO->value);
723            else
724              addToMap(tmpMaps->content,"maxOccurs",tmpMaxO->value);
725          }
726          map* tmpMaxMB=getMap(tmpInputs->content,"maximumMegabytes");
727          if(tmpMaxMB!=NULL){
728            if(tmpMaps->content==NULL)
729              tmpMaps->content=createMap("maximumMegabytes",tmpMaxMB->value);
730            else
731              addToMap(tmpMaps->content,"maximumMegabytes",tmpMaxMB->value);
732          }
733          /**
734           * Parsing BoundingBoxData, fill the following map and then add it to
735           * the content map of the Input maps:
736           * lowerCorner, upperCorner, srs and dimensions
737           * cf. parseBoundingBox
738           */
739          if(tmpInputs->format!=NULL && strcasecmp(tmpInputs->format,"BoundingBoxData")==0){
740            maps* tmpI=getMaps(*out,tmpInputs->name);
741            if(tmpI!=NULL){
742              map* tmpV=getMap(tmpI->content,"value");
743              if(tmpV!=NULL){
744                char *tmpVS=strdup(tmpV->value);
745                map* tmp=parseBoundingBox(tmpVS);
746                free(tmpVS);
747                map* tmpC=tmp;
748                while(tmpC!=NULL){
749                  addToMap(tmpMaps->content,tmpC->name,tmpC->value);
750                  tmpC=tmpC->next;
751                }
752                freeMap(&tmp);
753                free(tmp);
754              }
755            }
756          }
757        }
758      }else{
759        if(tmpInputs!=NULL){
760          tmpIoType=tmpInputs->defaults;
761        }
762      }
763
764      if(tmpIoType!=NULL){
765        map* tmpContent=tmpIoType->content;
766        map* cval=NULL;
767        int hasPassed=-1;
768        while(tmpContent!=NULL){
769          if((cval=getMap(tmpMaps->content,tmpContent->name))==NULL){
770#ifdef DEBUG
771            fprintf(stderr,"addDefaultValues %s => %s\n",tmpContent->name,tmpContent->value);
772#endif
773            if(tmpMaps->content==NULL)
774              tmpMaps->content=createMap(tmpContent->name,tmpContent->value);
775            else
776              addToMap(tmpMaps->content,tmpContent->name,tmpContent->value);
777           
778            if(hasPassed<0 && type==0 && getMap(tmpMaps->content,"isArray")!=NULL){
779              map* length=getMap(tmpMaps->content,"length");
780              int i;
781              char *tcn=strdup(tmpContent->name);
782              for(i=1;i<atoi(length->value);i++){
783#ifdef DEBUG
784                dumpMap(tmpMaps->content);
785                fprintf(stderr,"addDefaultValues %s_%d => %s\n",tcn,i,tmpContent->value);
786#endif
787                int len=strlen((char*) tcn);
788                char *tmp1=(char *)malloc((len+10)*sizeof(char));
789                sprintf(tmp1,"%s_%d",tcn,i);
790#ifdef DEBUG
791                fprintf(stderr,"addDefaultValues %s => %s\n",tmp1,tmpContent->value);
792#endif
793                addToMap(tmpMaps->content,tmp1,tmpContent->value);
794                free(tmp1);
795                hasPassed=1;
796              }
797              free(tcn);
798            }
799          }
800          tmpContent=tmpContent->next;
801        }
802#ifdef USE_MS
803        /**
804         * check for useMapServer presence
805         */
806        if(tmpIoType!=NULL){
807          map* tmpCheck=getMap(tmpIoType->content,"useMapserver");
808          if(tmpCheck!=NULL && strncasecmp(tmpCheck->value,"true",4)==0){
809            // Get the default value
810            tmpIoType=getIoTypeFromElement(tmpInputs,tmpInputs->name,NULL);
811            tmpCheck=getMap(tmpMaps->content,"mimeType");
812            addToMap(tmpMaps->content,"requestedMimeType",tmpCheck->value);
813            map* cursor=tmpIoType->content;
814            while(cursor!=NULL){
815              addToMap(tmpMaps->content,cursor->name,cursor->value);
816              cursor=cursor->next;
817            }
818         
819            cursor=tmpInputs->content;
820            while(cursor!=NULL){
821              if(strcasecmp(cursor->name,"Title")==0 ||
822                 strcasecmp(cursor->name,"Abstract")==0)
823                addToMap(tmpMaps->content,cursor->name,cursor->value);
824              cursor=cursor->next;
825            }
826          }
827        }
828#endif
829      }
830      if(tmpMaps->content==NULL)
831        tmpMaps->content=createMap("inRequest","true");
832      else
833        addToMap(tmpMaps->content,"inRequest","true");
834      elements* tmpElements=getElements(in,tmpMaps->name);
835      if(/*tmpMaps->child!=NULL && */tmpElements!=NULL && tmpElements->child!=NULL){
836        char *res=addDefaultValues(&tmpMaps->child,tmpElements->child,m,type,err);
837        if(strlen(res)>0){
838          return res;
839        }
840      }
841    }
842    if(tmpInputs->child!=NULL){
843      tmpInputss=tmpInputs->next;
844      tmpInputs=tmpInputs->child;
845      if(tmpMaps!=NULL){
846        out1=tmpMaps->child;
847        out1s=tmpMaps;
848      }
849    }
850    tmpInputs=tmpInputs->next;
851  }
852  if(tmpInputss!=NULL){
853    out1=out1s;
854    tmpInputs=tmpInputss;
855    tmpInputss=NULL;
856    out1s=NULL;
857    goto loopOnInputs;
858  }
859  if(res!=NULL){
860    *err=res;
861    return result;
862  }
863  return "";
864}
865
866/**
867 * Access the last error message returned by the OS when trying to dynamically
868 * load a shared library.
869 *
870 * @return the last error message
871 * @warning The character string returned from getLastErrorMessage resides
872 * in a static buffer. The application should not write to this
873 * buffer or attempt to free() it.
874 */ 
875char* getLastErrorMessage() {                                             
876#ifdef WIN32
877  LPVOID lpMsgBuf;
878  DWORD errCode = GetLastError();
879  static char msg[ERROR_MSG_MAX_LENGTH];
880  size_t i;
881 
882  DWORD length = FormatMessage(
883                               FORMAT_MESSAGE_ALLOCATE_BUFFER | 
884                               FORMAT_MESSAGE_FROM_SYSTEM |
885                               FORMAT_MESSAGE_IGNORE_INSERTS,
886                               NULL,
887                               errCode,
888                               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
889                               (LPTSTR) &lpMsgBuf,
890                               0, NULL );       
891 
892#ifdef UNICODE         
893  wcstombs_s( &i, msg, ERROR_MSG_MAX_LENGTH,
894              (wchar_t*) lpMsgBuf, _TRUNCATE );
895#else
896  strcpy_s( msg, ERROR_MSG_MAX_LENGTH,
897            (char *) lpMsgBuf );               
898#endif 
899  LocalFree(lpMsgBuf);
900
901  return msg;
902#else
903  return dlerror();
904#endif
905}
906
907#include <dirent.h>
908#ifndef RELY_ON_DB
909/**
910 * Read the Result file (.res).
911 *
912 * @param conf the maps containing the setting of the main.cfg file
913 * @param pid the service identifier (usid key from the [lenv] section)
914 */
915void readFinalRes(maps* conf,char* pid,map* statusInfo){
916  map* r_inputs = getMapFromMaps (conf, "main", "tmpPath");
917  char* fbkpid =
918    (char *)
919    malloc ((strlen (r_inputs->value) + strlen (pid) + 7) * sizeof (char));
920  sprintf (fbkpid, "%s/%s.res", r_inputs->value, pid);
921  struct stat file_status;
922  int istat = stat (fbkpid, &file_status);
923  if (istat == 0 && file_status.st_size > 0)
924    {
925      maps *res = (maps *) malloc (MAPS_SIZE);
926      conf_read (fbkpid, res);
927      res->child=NULL;
928      map* status=getMapFromMaps(res,"status","status");
929      addToMap(statusInfo,"Status",status->value);
930      freeMaps(&res);
931      free(res);
932    }
933  else
934    addToMap(statusInfo,"Status","Failed"); 
935  free(fbkpid);
936}
937
938/**
939 * Check if a service is running.
940 *
941 * @param conf the maps containing the setting of the main.cfg file
942 * @param pid the unique service identifier (usid from the lenv section)
943 * @return 1 in case the service is still running, 0 otherwise
944 */
945int isRunning(maps* conf,char* pid){
946  int res=0;
947  map* r_inputs = getMapFromMaps (conf, "main", "tmpPath");
948  char* fbkpid =
949    (char *)
950    malloc ((strlen (r_inputs->value) + strlen (pid) + 7) * sizeof (char));
951  sprintf (fbkpid, "%s/%s.pid", r_inputs->value, pid);
952  FILE* f0 = fopen (fbkpid, "r");
953  if(f0!=NULL){
954    fclose(f0);
955    res=1;
956  }
957  free(fbkpid);
958  return res;
959}
960#else
961#include "sqlapi.h"
962#endif
963
964/**
965 * Run GetStatus requests.
966 *
967 * @param conf the maps containing the setting of the main.cfg file
968 * @param pid the service identifier (usid key from the [lenv] section)
969 * @param req the request (GetStatus / GetResult)
970 */
971void runGetStatus(maps* conf,char* pid,char* req){
972  map* r_inputs = getMapFromMaps (conf, "main", "tmpPath");
973  char *sid=getStatusId(conf,pid);
974  if(sid==NULL){
975    errorException (conf, _("The JobID from the request does not match any of the Jobs running on this server"),
976                    "NoSuchJob", pid);
977  }else{
978    map* statusInfo=createMap("JobID",pid);
979    if(isRunning(conf,pid)>0){
980      if(strncasecmp(req,"GetResult",strlen(req))==0){
981        errorException (conf, _("The result for the requested JobID has not yet been generated. (1)"),
982                        "ResultNotReady", pid);
983        return;
984      }
985      else
986        if(strncasecmp(req,"GetStatus",strlen(req))==0){
987          addToMap(statusInfo,"Status","Running");
988          char* tmpStr=_getStatus(conf,pid);
989          if(tmpStr!=NULL && strncmp(tmpStr,"-1",2)!=0){
990            char *tmpStr1=strdup(tmpStr);
991            char *tmpStr0=strdup(strstr(tmpStr,"|")+1);
992            free(tmpStr);
993            tmpStr1[strlen(tmpStr1)-strlen(tmpStr0)-1]='\0';
994            addToMap(statusInfo,"PercentCompleted",tmpStr1);
995            addToMap(statusInfo,"Message",tmpStr0);
996            free(tmpStr0);
997            free(tmpStr1);
998          }
999        }
1000    }
1001    else{
1002      if(strncasecmp(req,"GetResult",strlen(req))==0){
1003        char* result=_getStatusFile(conf,pid);
1004        if(result!=NULL){
1005          char *encoding=getEncoding(conf);
1006          printf("Content-Type: text/xml; charset=%s\r\nStatus: 200 OK\r\n\r\n",encoding);
1007          printf("%s",result);
1008          fflush(stdout);
1009          freeMap(&statusInfo);
1010          free(statusInfo);
1011          return;
1012        }else{
1013          errorException (conf, _("The result for the requested JobID has not yet been generated. (2)"),
1014                          "ResultNotReady", pid);
1015          freeMap(&statusInfo);
1016          free(statusInfo);
1017          return;
1018        }
1019      }else
1020        if(strncasecmp(req,"GetStatus",strlen(req))==0){
1021          readFinalRes(conf,pid,statusInfo);
1022          char* tmpStr=_getStatus(conf,pid);
1023          if(tmpStr!=NULL && strncmp(tmpStr,"-1",2)!=0){
1024            char *tmpStr1=strdup(tmpStr);
1025            char *tmpStr0=strdup(strstr(tmpStr,"|")+1);
1026            free(tmpStr);
1027            tmpStr1[strlen(tmpStr1)-strlen(tmpStr0)-1]='\0';
1028            addToMap(statusInfo,"PercentCompleted",tmpStr1);
1029            addToMap(statusInfo,"Message",tmpStr0);
1030            free(tmpStr0);
1031            free(tmpStr1);
1032          }
1033        }
1034    }
1035    free(sid);
1036    printStatusInfo(conf,statusInfo,req);
1037    freeMap(&statusInfo);
1038    free(statusInfo);
1039  }
1040  return;
1041}
1042
1043/**
1044 * Run Dismiss requests.
1045 *
1046 * @param conf the maps containing the setting of the main.cfg file
1047 * @param pid the service identifier (usid key from the [lenv] section)
1048 */
1049void runDismiss(maps* conf,char* pid){
1050  map* r_inputs = getMapFromMaps (conf, "main", "tmpPath");
1051  char *sid=getStatusId(conf,pid);
1052  if(sid==NULL){
1053    errorException (conf, _("The JobID from the request does not match any of the Jobs running on this server"),
1054                    "NoSuchJob", pid);
1055  }else{
1056    // We should send the Dismiss request to the target host if it differs
1057    char* fbkpid =
1058      (char *)
1059      malloc ((strlen (r_inputs->value) + strlen (pid) + 7) * sizeof (char));
1060    sprintf (fbkpid, "%s/%s.pid", r_inputs->value, pid);
1061    FILE* f0 = fopen (fbkpid, "r");
1062    if(f0!=NULL){
1063      long flen;
1064      char *fcontent;
1065      fseek (f0, 0, SEEK_END);
1066      flen = ftell (f0);
1067      fseek (f0, 0, SEEK_SET);
1068      fcontent = (char *) malloc ((flen + 1) * sizeof (char));
1069      fread(fcontent,flen,1,f0);
1070      fcontent[flen]=0;
1071      fclose(f0);
1072#ifndef WIN32
1073      kill(atoi(fcontent),SIGKILL);
1074#else
1075      HANDLE myZooProcess=OpenProcess(PROCESS_ALL_ACCESS,false,atoi(fcontent));
1076      TerminateProcess(myZooProcess,1);
1077      CloseHandle(myZooProcess);
1078#endif
1079      free(fcontent);
1080    }
1081    free(fbkpid);
1082    struct dirent *dp;
1083    DIR *dirp = opendir(r_inputs->value);
1084    char fileName[1024];
1085    int hasFile=-1;
1086    if(dirp!=NULL){
1087      while ((dp = readdir(dirp)) != NULL){
1088#ifdef DEBUG
1089        fprintf(stderr,"File : %s searched : %s\n",dp->d_name,tmp);
1090#endif
1091        if(strstr(dp->d_name,pid)!=0){
1092          sprintf(fileName,"%s/%s",r_inputs->value,dp->d_name);
1093          if(unlink(fileName)!=0){
1094            errorException (conf, 
1095                            _("The job cannot be removed, a file cannot be removed"),
1096                            "NoApplicableCode", NULL);
1097            return;
1098          }
1099        }
1100      }
1101    }
1102#ifdef RELY_ON_DB
1103    removeService(conf,pid);
1104#endif
1105    map* statusInfo=createMap("JobID",pid);
1106    addToMap(statusInfo,"Status","Dismissed");
1107    printStatusInfo(conf,statusInfo,"Dismiss");
1108    free(statusInfo);
1109  }
1110  return;
1111}
1112
1113extern int getServiceFromFile (maps *, const char *, service **);
1114
1115/**
1116 * Parse the service file using getServiceFromFile or use getServiceFromYAML
1117 * if YAML support was activated.
1118 *
1119 * @param conf the conf maps containing the main.cfg settings
1120 * @param file the file name to parse
1121 * @param service the service to update witht the file content
1122 * @param name the service name
1123 * @return true if the file can be parsed or false
1124 * @see getServiceFromFile, getServiceFromYAML
1125 */
1126int readServiceFile (maps * conf, char *file, service ** service, char *name){
1127  int t = getServiceFromFile (conf, file, service);
1128#ifdef YAML
1129  if (t < 0){
1130    t = getServiceFromYAML (conf, file, service, name);
1131  }
1132#endif
1133  return t;
1134}
1135
1136/**
1137 * Create the profile registry.
1138 *
1139 * The profile registry is optional (created only if the registry key is
1140 * available in the [main] section of the main.cfg file) and can be used to
1141 * store the profiles hierarchy. The registry is a directory which should
1142 * contain the following sub-directories:
1143 *  * concept: direcotry containing .html files describing concept
1144 *  * generic: directory containing .zcfg files for wps:GenericProcess
1145 *  * implementation: directory containing .zcfg files for wps:Process
1146 *
1147 * @param m the conf maps containing the main.cfg settings
1148 * @param r the registry to update
1149 * @param reg_dir the resgitry
1150 * @return 0 if the resgitry is null or was correctly updated, -1 on failure
1151 */
1152int createRegistry (maps* m,registry ** r, char *reg_dir)
1153{
1154  char registryKeys[3][15]={
1155    "concept",
1156    "generic",
1157    "implementation"
1158  };
1159  int scount = 0,i=0;
1160  if (reg_dir == NULL)
1161    return 0;
1162  for(i=0;i<3;i++){
1163    char * tmpName =
1164      (char *) malloc ((strlen (reg_dir) + strlen (registryKeys[i]) + 2) *
1165                       sizeof (char));
1166    sprintf (tmpName, "%s/%s", reg_dir, registryKeys[i]);
1167   
1168    DIR *dirp1 = opendir (tmpName);
1169    if(dirp1==NULL){
1170      setMapInMaps(m,"lenv","message",_("Unable to open the registry directory."));
1171      setMapInMaps(m,"lenv","type","InternalError");
1172      return -1;
1173    }
1174    struct dirent *dp1;
1175    while ((dp1 = readdir (dirp1)) != NULL){
1176      char* extn = strstr(dp1->d_name, ".zcfg");
1177      if(dp1->d_name[0] != '.' && extn != NULL && strlen(extn) == 5)
1178        {
1179          int t;
1180          char *tmps1=
1181            (char *) malloc ((strlen (tmpName) + strlen (dp1->d_name) + 2) *
1182                             sizeof (char));
1183          sprintf (tmps1, "%s/%s", tmpName, dp1->d_name);
1184          char *tmpsn = zStrdup (dp1->d_name);
1185          tmpsn[strlen (tmpsn) - 5] = 0;
1186          service* s1 = (service *) malloc (SERVICE_SIZE);
1187          if (s1 == NULL)
1188            {
1189              setMapInMaps(m,"lenv","message",_("Unable to allocate memory."));
1190              setMapInMaps(m,"lenv","type","InternalError");
1191              return -2;
1192            }
1193          t = readServiceFile (m, tmps1, &s1, tmpsn);
1194          free (tmpsn);
1195          if (t < 0)
1196            {
1197              map *tmp00 = getMapFromMaps (m, "lenv", "message");
1198              char tmp01[1024];
1199              if (tmp00 != NULL)
1200                sprintf (tmp01, _("Unable to parse the ZCFG file: %s (%s)"),
1201                         dp1->d_name, tmp00->value);
1202              else
1203                sprintf (tmp01, _("Unable to parse the ZCFG file: %s."),
1204                         dp1->d_name);
1205              setMapInMaps(m,"lenv","message",tmp01);
1206              setMapInMaps(m,"lenv","type","InternalError");
1207              return -1;
1208            }
1209          if(strncasecmp(registryKeys[i],"implementation",14)==0){
1210            inheritance(*r,&s1);
1211          }
1212          addServiceToRegistry(r,registryKeys[i],s1);
1213          freeService (&s1);
1214          free (s1);
1215          scount++;
1216        }
1217    }
1218    (void) closedir (dirp1);
1219  }
1220  return 0;
1221}
1222
1223#ifdef WIN32
1224/**
1225 * Create a KVP request for executing background task.
1226 * TODO: use the XML request in case of input POST request.
1227 *
1228 * @param m the maps containing the parameters from the main.cfg file
1229 * @param length the total length of the KVP parameters
1230 * @param type
1231 */
1232char* getMapsAsKVP(maps* m,int length,int type){
1233  char *dataInputsKVP=(char*) malloc(length*sizeof(char));
1234  char *dataInputsKVPi=NULL;
1235  maps* curs=m;
1236  int i=0;
1237  while(curs!=NULL){
1238    map *inRequest=getMap(curs->content,"inRequest");
1239    map *hasLength=getMap(curs->content,"length");
1240    if((inRequest!=NULL && strncasecmp(inRequest->value,"true",4)==0) ||
1241       inRequest==NULL){
1242      if(i==0)
1243        if(type==0){
1244          sprintf(dataInputsKVP,"%s=",curs->name);
1245          if(hasLength!=NULL){
1246            dataInputsKVPi=(char*)malloc((strlen(curs->name)+2)*sizeof(char));
1247            sprintf(dataInputsKVPi,"%s=",curs->name);
1248          }
1249        }
1250        else
1251          sprintf(dataInputsKVP,"%s",curs->name);
1252      else{
1253        char *temp=zStrdup(dataInputsKVP);
1254        if(type==0)
1255          sprintf(dataInputsKVP,"%s;%s=",temp,curs->name);
1256        else
1257          sprintf(dataInputsKVP,"%s;%s",temp,curs->name);
1258      }
1259      map* icurs=curs->content;
1260      if(type==0){
1261        char *temp=zStrdup(dataInputsKVP);
1262        if(getMap(curs->content,"xlink:href")!=NULL)
1263          sprintf(dataInputsKVP,"%sReference",temp);
1264        else{
1265          if(hasLength!=NULL){
1266            int j;
1267            for(j=0;j<atoi(hasLength->value);j++){
1268              map* tmp0=getMapArray(curs->content,"value",j);
1269              if(j==0)
1270                free(temp);
1271              temp=zStrdup(dataInputsKVP);
1272              if(j==0)
1273                sprintf(dataInputsKVP,"%s%s",temp,tmp0->value);
1274              else
1275                sprintf(dataInputsKVP,"%s;%s%s",temp,dataInputsKVPi,tmp0->value);
1276            }
1277          }
1278          else
1279            sprintf(dataInputsKVP,"%s%s",temp,icurs->value);
1280        }
1281        free(temp);
1282      }
1283      while(icurs!=NULL){
1284        if(strncasecmp(icurs->name,"value",5)!=0 &&
1285           strncasecmp(icurs->name,"mimeType_",9)!=0 &&
1286           strncasecmp(icurs->name,"dataType_",9)!=0 &&
1287           strncasecmp(icurs->name,"size",4)!=0 &&
1288           strncasecmp(icurs->name,"length",4)!=0 &&
1289           strncasecmp(icurs->name,"isArray",7)!=0 &&
1290           strcasecmp(icurs->name,"Reference")!=0 &&
1291           strcasecmp(icurs->name,"minOccurs")!=0 &&
1292           strcasecmp(icurs->name,"maxOccurs")!=0 &&
1293           strncasecmp(icurs->name,"fmimeType",9)!=0 &&
1294           strcasecmp(icurs->name,"inRequest")!=0){
1295          char *itemp=zStrdup(dataInputsKVP);
1296          if(strcasecmp(icurs->name,"xlink:href")!=0)
1297            sprintf(dataInputsKVP,"%s@%s=%s",itemp,icurs->name,icurs->value);
1298          else
1299            sprintf(dataInputsKVP,"%s@%s=%s",itemp,icurs->name,url_encode(icurs->value));
1300          free(itemp);
1301        }
1302        icurs=icurs->next;
1303      }
1304    }
1305    curs=curs->next;
1306    i++;
1307  }
1308  return dataInputsKVP;
1309}
1310#endif
Note: See TracBrowser for help on using the repository browser.

Search

ZOO Sponsors

http://www.zoo-project.org/trac/chrome/site/img/geolabs-logo.pnghttp://www.zoo-project.org/trac/chrome/site/img/neogeo-logo.png http://www.zoo-project.org/trac/chrome/site/img/apptech-logo.png http://www.zoo-project.org/trac/chrome/site/img/3liz-logo.png http://www.zoo-project.org/trac/chrome/site/img/gateway-logo.png

Become a sponsor !

Knowledge partners

http://www.zoo-project.org/trac/chrome/site/img/ocu-logo.png http://www.zoo-project.org/trac/chrome/site/img/gucas-logo.png http://www.zoo-project.org/trac/chrome/site/img/polimi-logo.png http://www.zoo-project.org/trac/chrome/site/img/fem-logo.png http://www.zoo-project.org/trac/chrome/site/img/supsi-logo.png http://www.zoo-project.org/trac/chrome/site/img/cumtb-logo.png

Become a knowledge partner

Related links

http://zoo-project.org/img/ogclogo.png http://zoo-project.org/img/osgeologo.png