source: trunk/zoo-project/zoo-kernel/service_internal_js.c @ 336

Last change on this file since 336 was 336, checked in by djay, 13 years ago

Add support for importScript javascript function

File size: 16.0 KB
Line 
1/**
2 * Author : Gérald FENOY
3 *
4 * Copyright (c) 2009-2010 GeoLabs SARL
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 "service_internal.h"
26
27static char dbg[1024];
28
29JSBool
30JSAlert(JSContext *cx, uintN argc, jsval *argv1)
31{
32  jsval *argv = JS_ARGV(cx,argv1);
33  int i=0;
34  JS_MaybeGC(cx);
35  for(i=0;i<argc;i++){
36    JSString* jsmsg = JS_ValueToString(cx,argv[i]);
37    fprintf(stderr,"[ZOO-API:JS] %s\n",JS_EncodeString(cx,jsmsg));
38  }
39  JS_MaybeGC(cx);
40 
41  return JS_TRUE;
42}
43
44JSBool
45JSLoadScripts(JSContext *cx, uintN argc, jsval *argv1)
46{
47  map* request = JS_GetContextPrivate(cx);
48  map* tmpm1=getMap(request,"metapath");
49  JS_MaybeGC(cx);
50
51  char ntmp[1024];
52  getcwd(ntmp,1024);
53
54  jsval *argv = JS_ARGV(cx,argv1);
55  int i=0;
56  JS_MaybeGC(cx);
57  for(i=0;i<argc;i++){
58    JSString* jsmsg = JS_ValueToString(cx,argv[i]);
59    char *filename = JSValToChar(cx,&argv[i]);
60    char api0[strlen(tmpm1->value)+strlen(ntmp)+strlen(filename)+2];
61    sprintf(api0,"%s/%s/%s",ntmp,tmpm1->value,filename);
62#ifdef JS_DEBUG
63    fprintf(stderr,"Trying to load %s\n",api0);
64#endif
65    JSObject *api_script1=loadZooApiFile(cx,JS_GetGlobalObject(cx),api0);
66    fflush(stderr);
67  }
68  JS_MaybeGC(cx);
69  JS_SET_RVAL(cx, argv1, JSVAL_VOID);
70 
71  return JS_TRUE;
72}
73
74
75int zoo_js_support(maps** main_conf,map* request,service* s,
76                   maps **inputs,maps **outputs)
77{
78  maps* main=*main_conf;
79  maps* _inputs=*inputs;
80  maps* _outputs=*outputs;
81
82  /* The class of the global object. */
83  JSClass global_class = {
84    "global", JSCLASS_GLOBAL_FLAGS,
85    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
86    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
87    JSCLASS_NO_OPTIONAL_MEMBERS
88  };
89
90  /* JS variables. */
91  JSRuntime *rt;
92  JSContext *cx;
93  JSObject  *global;
94
95  /* Create a JS runtime. */
96  rt = JS_NewRuntime(8L * 1024L * 1024L);
97  if (rt == NULL)
98    return 1;
99 
100  /* Create a context. */
101  cx = JS_NewContext(rt,8192);
102  if (cx == NULL){
103    return 1;
104  }
105  JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT );//| JSOPTION_METHODJIT);
106  JS_SetVersion(cx, JSVERSION_LATEST);
107  JS_SetErrorReporter(cx, reportError);
108
109  /* Create the global object. */
110  //#ifdef JS_NewCompartmentAndGlobalObject
111  global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
112  //#else
113  //global = JS_NewObject(cx, &global_class, NULL,NULL);
114  //#endif
115
116  /* Populate the global object with the standard globals,
117     like Object and Array. */
118  if (!JS_InitStandardClasses(cx, global)){
119    return 1;
120  }
121
122  if (!JS_DefineFunction(cx, global, "ZOORequest", JSRequest, 4, 0))
123    return 1;
124  if (!JS_DefineFunction(cx, global, "ZOOUpdateStatus", JSUpdateStatus, 2, 0))
125    return 1;
126  if (!JS_DefineFunction(cx, global, "alert", JSAlert, 2, 0))
127    return 1; 
128  if (!JS_DefineFunction(cx, global, "importScripts", JSLoadScripts, 1, 0))
129    return 1;
130
131  /**
132   * Add private context object
133   */
134  void* cxPrivate = request;
135  JS_SetContextPrivate(cx,cxPrivate);
136   
137  map* tmpm1=getMap(request,"metapath");
138  char ntmp[1024];
139  getcwd(ntmp,1024);
140
141  /**
142   * Load the first part of the ZOO-API
143   */
144  char api0[strlen(tmpm1->value)+strlen(ntmp)+16];
145  sprintf(api0,"%s/%s/ZOO-proj4js.js",ntmp,tmpm1->value);
146#ifdef JS_DEBUG
147  fprintf(stderr,"Trying to load %s\n",api0);
148#endif
149  JSObject *api_script1=loadZooApiFile(cx,global,api0);
150  fflush(stderr);
151
152  char api1[strlen(tmpm1->value)+strlen(ntmp)+11];
153  sprintf(api1,"%s/%s/ZOO-api.js",ntmp,tmpm1->value);
154#ifdef JS_DEBUG
155  fprintf(stderr,"Trying to load %s\n",api1);
156#endif
157  JSObject *api_script2=loadZooApiFile(cx,global,api1);
158  fflush(stderr);
159
160  /* Your application code here. This may include JSAPI calls
161     to create your own custom JS objects and run scripts. */
162  maps* out=*outputs;
163  int res=SERVICE_FAILED;
164  maps* mc=*main_conf;
165  map* tmpm2=getMap(s->content,"serviceProvider");
166
167  char filename[strlen(tmpm1->value)+strlen(tmpm2->value)+strlen(ntmp)+3];
168  sprintf(filename,"%s/%s/%s",ntmp,tmpm1->value,tmpm2->value);
169  filename[strlen(tmpm1->value)+strlen(tmpm2->value)+strlen(ntmp)+2]=0;
170#ifdef JS_DEBUG
171  fprintf(stderr,"FILENAME %s\n",filename);
172#endif
173  struct stat file_status;
174  stat(filename, &file_status);
175  char source[file_status.st_size];
176  uint16 lineno;
177  jsval rval;
178  JSBool ok ;
179  JSObject *script = JS_CompileFile(cx, global, filename);
180  if(script!=NULL){
181    (void)JS_ExecuteScript(cx, global, script, &rval);
182  }
183  else{
184    char tmp1[1024];
185    sprintf(tmp1,"Unable to load JavaScript file %s",filename);
186    map* err=createMap("text",tmp1);
187    addMapToMap(&err,createMap("code","NoApplicableCode"));
188    printExceptionReportResponse(mc,err);
189    JS_DestroyContext(cx);
190    JS_DestroyRuntime(rt);
191    JS_ShutDown();
192    exit(-1);
193  }
194  /* Call a function in obj's scope. */
195  jsval argv[3];
196  JSObject *jsargv1=JSObject_FromMaps(cx,*main_conf);
197  argv[0] = OBJECT_TO_JSVAL(jsargv1);
198  JSObject *jsargv2=JSObject_FromMaps(cx,*inputs);
199  argv[1] = OBJECT_TO_JSVAL(jsargv2);
200  JSObject *jsargv3=JSObject_FromMaps(cx,*outputs);
201  argv[2] = OBJECT_TO_JSVAL(jsargv3);
202  jsval rval1=JSVAL_NULL;
203#ifdef JS_DEBUG
204  fprintf(stderr, "object %p\n", (void *) argv[2]);
205#endif
206
207  ok = JS_CallFunctionName(cx, global, s->name, 3, argv, &rval1);
208
209#ifdef JS_DEBUG
210  fprintf(stderr, "object %p\n", (void *) argv[2]);
211#endif
212
213  JSObject *d;
214  if (ok==JS_TRUE && JSVAL_IS_OBJECT(rval1)==JS_TRUE) {
215#ifdef JS_DEBUG
216    fprintf(stderr,"Function run sucessfully !\n");
217#endif
218    /* Should get a number back from the service function call. */
219    ok = JS_ValueToObject(cx, rval1, &d);
220  }else{
221    /* Unable to run JS function */
222    char tmp1[1024];
223    if(strlen(dbg)==0)
224      sprintf(dbg,"No result was found after the function call");
225    sprintf(tmp1,"Unable to run %s from the JavaScript file %s : \n %s",s->name,filename,dbg);
226#ifdef JS_DEBUG
227    fprintf(stderr,"%s",tmp1);
228#endif
229    map* err=createMap("text",tmp1);
230    addToMap(err,"code","NoApplicableCode");
231    printExceptionReportResponse(*main_conf,err);
232    freeMap(&err);
233    free(err);
234    JS_DestroyContext(cx);
235    JS_DestroyRuntime(rt);
236    JS_ShutDown();
237    // Should return -1 here but the unallocation won't work from zoo_service_loader.c line 1847
238    exit(-1);
239  }
240
241  jsval t=OBJECT_TO_JSVAL(d);
242  if(JS_IsArrayObject(cx,d)){
243#ifdef JS_DEBUG
244    fprintf(stderr,"An array was returned !\n");
245#endif
246    jsint len;
247    if((JS_GetArrayLength(cx, d, &len)==JS_FALSE)){
248#ifdef JS_DEBUG
249      fprintf(stderr,"outputs array is empty\n");
250#endif
251    }
252    jsval tmp1;
253    JSBool hasResult=JS_GetElement(cx,d,0,&tmp1);
254    res=JSVAL_TO_INT(tmp1);
255#ifdef JS_DEBUG
256    fprintf(stderr," * %d * \n",res);
257#endif
258    jsval tmp2;
259    JSBool hasElement=JS_GetElement(cx,d,1,&tmp2);
260    if(hasElement==JS_TRUE){
261      *outputs=mapsFromJSObject(cx,tmp2);
262    }
263  }
264  else{
265#ifdef JS_DEBUG
266    fprintf(stderr,"The serice didn't return an array !\n");
267#endif
268    jsval tmp1;
269    JSBool hasResult=JS_GetProperty(cx,d,"result",&tmp1);
270    res=JSVAL_TO_INT(tmp1);
271
272#ifdef JS_DEBUG
273    fprintf(stderr," * %d * \n",res);
274#endif
275    jsval tmp2;
276    JSBool hasElement=JS_GetProperty(cx,d,"outputs",&tmp2);
277#ifdef JS_DEBUG
278    if(!hasElement)
279      fprintf(stderr,"No outputs property returned\n");
280    if(JS_IsArrayObject(cx,JSVAL_TO_OBJECT(tmp2)))
281      fprintf(stderr,"outputs is array an as expected\n");
282    else
283      fprintf(stderr,"outputs is not an array as expected\n");
284#endif
285    *outputs=mapsFromJSObject(cx,tmp2);
286#ifdef JS_DEBUG
287    dumpMaps(outputs);
288#endif
289  }
290
291  /* Cleanup. */
292  JS_DestroyContext(cx);
293  JS_DestroyRuntime(rt);
294  JS_ShutDown();
295#ifdef JS_DEBUG
296  fprintf(stderr,"Returned value %d\n",res);
297#endif
298  return res;
299}
300
301JSObject * loadZooApiFile(JSContext *cx,JSObject  *global, char* filename){
302  struct stat api_status;
303  int s=stat(filename, &api_status);
304  if(s==0){
305    jsval rval;
306    JSBool ok ;
307    JSObject *script = JS_CompileFile(cx, JS_GetGlobalObject(cx), filename);
308    if(script!=NULL){
309      (void)JS_ExecuteScript(cx, JS_GetGlobalObject(cx), script, &rval);
310#ifdef JS_DEBUG
311      fprintf(stderr,"**************\n%s correctly loaded\n**************\n",filename);
312#endif
313      return script;
314    }
315#ifdef JS_DEBUG
316    else
317      fprintf(stderr,"\n**************\nUnable to run %s\n**************\n",filename);
318#endif
319  }
320#ifdef JS_DEBUG
321  else
322    fprintf(stderr,"\n**************\nUnable to load %s\n**************\n",filename);
323#endif
324  return NULL;
325}
326
327JSObject* JSObject_FromMaps(JSContext *cx,maps* t){
328
329  JSObject* res=JS_NewObject(cx, NULL, NULL, NULL);
330  //JSObject *res = JS_NewArrayObject(cx, 0, NULL);
331  if(res==NULL)
332    fprintf(stderr,"Array Object is NULL!\n");
333  maps* tmp=t;
334
335  while(tmp!=NULL){
336    jsuint len;
337    JSObject* res1=JS_NewObject(cx, NULL, NULL, NULL);
338    JSObject *pval=JSObject_FromMap(cx,tmp->content);
339    jsval pvalj=OBJECT_TO_JSVAL(pval);
340    JS_SetProperty(cx, res, tmp->name, &pvalj);
341
342#ifdef JS_DEBUG
343    fprintf(stderr,"Length of the Array %d, element : %s added \n",len,tmp->name);
344#endif
345    tmp=tmp->next;
346  } 
347  return res;
348}
349
350JSObject* JSObject_FromMap(JSContext *cx,map* t){
351  JSObject* res=JS_NewObject(cx, NULL, NULL, NULL);
352  jsval resf =  OBJECT_TO_JSVAL(res);
353  map* tmpm=t;
354  while(tmpm!=NULL){
355    jsval jsstr = STRING_TO_JSVAL(JS_NewStringCopyN(cx,tmpm->value,strlen(tmpm->value)));
356    JS_SetProperty(cx, res, tmpm->name,&jsstr);
357#ifdef JS_DEBUG
358    fprintf(stderr,"%s => %s\n",tmpm->name,tmpm->value);
359#endif
360    tmpm=tmpm->next;
361  }
362  return res;
363}
364
365maps* mapsFromJSObject(JSContext *cx,jsval t){
366  maps *res=NULL;
367  maps *tres=NULL;
368  jsint oi=0;
369  JSObject* tt=JSVAL_TO_OBJECT(t);
370  if(JS_IsArrayObject(cx,tt)){
371#ifdef JS_DEBUG
372    fprintf(stderr,"Is finally an array !\n");
373#endif
374  }
375  else{
376#ifdef JS_DEBUG
377    fprintf(stderr,"Is not an array !\n");
378#endif
379    JSIdArray *idp=JS_Enumerate(cx,tt);
380    if(idp!=NULL) {
381      int index;
382      jsdouble argNum;
383#ifdef JS_DEBUG
384      fprintf(stderr,"Properties length :  %d \n",idp->length);
385#endif
386     
387      for (index=0,argNum=idp->length;index<argNum;index++) { 
388        jsval id = idp->vector[index];
389        jsval vp;
390        JSString* str; 
391        JS_IdToValue(cx,id,&vp);
392        char *c, *tmp;
393        JSString *jsmsg;
394        size_t len1;
395        jsmsg = JS_ValueToString(cx,vp);
396        len1 = JS_GetStringLength(jsmsg);
397
398        tres=(maps*)malloc(MAPS_SIZE);
399        tres->name=strdup(JS_EncodeString(cx,jsmsg));
400        tres->content=NULL;
401        tres->next=NULL;
402
403        jsval nvp=JSVAL_NULL;
404        if((JS_GetProperty(cx, JSVAL_TO_OBJECT(tt), JS_EncodeString(cx,jsmsg), &nvp)==JS_FALSE)){
405#ifdef JS_DEBUG
406          fprintf(stderr,"Enumerate id : %d => %s => No more value\n",oi,JS_EncodeString(cx,jsmsg));
407#endif
408        }
409       
410        JSObject *nvp1=JSVAL_NULL;
411        JS_ValueToObject(cx,nvp,&nvp1);
412        jsval nvp1j=OBJECT_TO_JSVAL(nvp1);
413        if(JSVAL_IS_OBJECT(nvp1j)){
414          tres->content=mapFromJSObject(cx,nvp1j);
415        }
416
417        if(res==NULL)
418          res=dupMaps(&tres);
419        else
420          addMapsToMaps(&res,tres);
421        freeMaps(&tres);
422        free(tres);
423        tres=NULL;
424       
425       
426      }
427    }
428  }
429
430  jsint len;
431  JSBool hasLen=JS_GetArrayLength(cx, tt, &len);
432  if(hasLen==JS_FALSE){
433#ifdef JS_DEBUG
434    fprintf(stderr,"outputs array is empty\n");
435#endif
436  }
437#ifdef JS_DEBUG
438  fprintf(stderr,"outputs array length : %d\n",len);
439#endif
440  for(oi=0;oi < len;oi++){
441#ifdef JS_DEBUG
442    fprintf(stderr,"outputs array length : %d step %d \n",len,oi);
443#endif
444    jsval tmp1;
445    JSBool hasElement=JS_GetElement(cx,tt,oi,&tmp1);
446    JSObject *otmp1=JSVAL_TO_OBJECT(tmp1);
447    JSIdArray *idp=JS_Enumerate(cx,otmp1);
448    if(idp!=NULL) {
449      int index;
450      jsdouble argNum;
451#ifdef JS_DEBUG
452      fprintf(stderr,"Properties length :  %d \n",idp->length);
453#endif
454      tres=(maps*)malloc(MAPS_SIZE);
455      tres->name=NULL;
456      tres->content=NULL;
457      tres->next=NULL;
458
459      for (index=0,argNum=idp->length;index<argNum;index++) { 
460        jsval id = idp->vector[index];
461        jsval vp;
462        JSString* str; 
463        JS_IdToValue(cx,id,&vp);
464        char *c, *tmp;
465        JSString *jsmsg;
466        size_t len1;
467        jsmsg = JS_ValueToString(cx,vp);
468        len1 = JS_GetStringLength(jsmsg);
469#ifdef JS_DEBUG
470        fprintf(stderr,"Enumerate id : %d => %s\n",oi,JS_EncodeString(cx,jsmsg));
471#endif
472        jsval nvp=JSVAL_NULL;
473        if((JS_GetProperty(cx, JSVAL_TO_OBJECT(tmp1), JS_EncodeString(cx,jsmsg), &nvp)==JS_FALSE)){
474#ifdef JS_DEBUG
475          fprintf(stderr,"Enumerate id : %d => %s => No more value\n",oi,JS_EncodeString(cx,jsmsg));
476#endif
477        }
478       
479        if(JSVAL_IS_OBJECT(nvp)){
480#ifdef JS_DEBUG
481          fprintf(stderr,"JSVAL NVP IS OBJECT\n");
482#endif
483        }
484
485        JSObject *nvp1=JSVAL_NULL;
486        JS_ValueToObject(cx,nvp,&nvp1);
487        jsval nvp1j=OBJECT_TO_JSVAL(nvp1);
488        if(JSVAL_IS_OBJECT(nvp1j)){
489          JSString *jsmsg1;
490          JSObject *nvp2=JSVAL_NULL;
491          jsmsg1 = JS_ValueToString(cx,nvp1j);
492          len1 = JS_GetStringLength(jsmsg1);
493#ifdef JS_DEBUG
494          fprintf(stderr,"JSVAL NVP1J IS OBJECT %s = %s\n",JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
495#endif
496          if(strcasecmp(JS_EncodeString(cx,jsmsg1),"[object Object]")==0){
497            tres->name=strdup(JS_EncodeString(cx,jsmsg));
498            tres->content=mapFromJSObject(cx,nvp1j);
499          }
500          else
501            if(strcasecmp(JS_EncodeString(cx,jsmsg),"name")==0){
502              tres->name=strdup(JS_EncodeString(cx,jsmsg1));
503            }
504            else{
505              if(tres->content==NULL)
506                tres->content=createMap(JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
507              else
508                addToMap(tres->content,JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
509            }
510        }
511#ifdef JS_DEBUG
512        else
513          fprintf(stderr,"JSVAL NVP1J IS NOT OBJECT !!\n");
514#endif
515
516      }
517#ifdef JS_DEBUG
518      dumpMaps(tres);
519#endif
520      if(res==NULL)
521        res=dupMaps(&tres);
522      else
523        addMapsToMaps(&res,tres);
524      freeMaps(&tres);
525      free(tres);
526      tres=NULL;
527
528    }
529  }
530#ifdef JS_DEBUG
531  dumpMaps(res);
532#endif
533  return res;
534}
535
536map* mapFromJSObject(JSContext *cx,jsval t){
537  map *res=NULL;
538  JSIdArray *idp=JS_Enumerate(cx,JSVAL_TO_OBJECT(t));
539#ifdef JS_DEBUG
540  fprintf(stderr,"Properties %p\n",(void*)t);
541#endif
542  if(idp!=NULL) {
543    int index;
544    jsdouble argNum;
545#ifdef JS_DEBUG
546    fprintf(stderr,"Properties length :  %d \n",idp->length);
547#endif
548    for (index=0,argNum=idp->length;index<argNum;index++) { 
549      jsval id = idp->vector[index];
550      jsval vp;
551      JSString* str; 
552      JS_IdToValue(cx,id,&vp);
553      char *c, *tmp;
554      JSString *jsmsg,*jsmsg1;
555      size_t len,len1;
556      jsmsg = JS_ValueToString(cx,vp);
557      len = JS_GetStringLength(jsmsg);
558      jsval nvp;
559      JS_GetProperty(cx, JSVAL_TO_OBJECT(t), JS_EncodeString(cx,jsmsg), &nvp);
560      jsmsg1 = JS_ValueToString(cx,nvp);
561      len1 = JS_GetStringLength(jsmsg1);
562#ifdef JS_DEBUG
563      fprintf(stderr,"Enumerate id : %d [ %s => %s ]\n",index,JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
564#endif
565      if(res!=NULL){
566#ifdef JS_DEBUG
567        fprintf(stderr,"%s - %s\n",JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
568#endif
569        addToMap(res,JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
570      }
571      else{
572        res=createMap(JS_EncodeString(cx,jsmsg),JS_EncodeString(cx,jsmsg1));
573        res->next=NULL;
574      }
575#ifdef JS_DEBUG
576      dumpMap(res);
577#endif
578    }
579  }
580#ifdef JS_DEBUG
581  dumpMap(res);
582#endif
583  return res;
584}
585
586/* The error reporter callback. */
587void reportError(JSContext *cx, const char *message, JSErrorReport *report)
588{
589  sprintf(dbg,"%s:%u:%s\n",
590          report->filename ? report->filename : "<no filename>",
591          (unsigned int) report->lineno,
592          message);
593#ifdef JS_DEBUG
594  fprintf(stderr,"%s",dbg);
595#endif
596  fflush(stderr);
597}
598
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