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

Last change on this file since 854 was 854, checked in by djay, 6 years ago

HPC support update. Add inputs for create options in Gdal_Dem.

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