source: XIOS/dev/dev_cmip6_omp/extern/src_netcdf4/ocinternal.c @ 1606

Last change on this file since 1606 was 409, checked in by ymipsl, 11 years ago

Add improved nectdf internal library src

YM

  • Property svn:eol-style set to native
File size: 16.2 KB
Line 
1/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
2   See the COPYRIGHT file for more information. */
3
4#include "config.h"
5#include <stdio.h>
6#include <fcntl.h>
7#include <errno.h>
8
9#ifdef HAVE_UNISTD_H
10#include <unistd.h>
11#endif
12
13#include "ocinternal.h"
14#include "ocdebug.h"
15#include "ocdata.h"
16#include "occontent.h"
17#include "occlientparams.h"
18#include "ocrc.h"
19#include "occurlfunctions.h"
20
21#include "ochttp.h"
22#include "ocread.h"
23
24/* Note: TMPPATH must end in '/' */
25#ifdef __CYGWIN__
26#define TMPPATH1 "/cygdrive/c/temp/"
27#define TMPPATH2 "./"
28#elifdef WIN32
29#define TMPPATH1 "c:\\temp/"
30#define TMPPATH2 ".\\"
31#else
32#define TMPPATH1 "/tmp/"
33#define TMPPATH2 "./"
34#endif
35
36/* Define default rc files and aliases*/
37static char* rcfilenames[3] = {".dodsrc",".ocrc",NULL};
38
39static int ocextractddsinmemory(OCstate*,OCtree*,int);
40static int ocextractddsinfile(OCstate*,OCtree*,int);
41static char* constraintescape(const char* url);
42static OCerror createtempfile(OCstate*,OCtree*);
43static int createtempfile1(char*,char**);
44
45static void ocsetcurlproperties(OCstate*);
46
47extern OCnode* makeunlimiteddimension(void);
48
49#ifdef WIN32
50#include <fcntl.h>
51#define _S_IREAD 256
52#define _S_IWRITE 128
53#else
54#include <sys/stat.h>
55#endif
56
57/* Global flags*/
58int oc_curl_file_supported;
59int oc_curl_https_supported;
60
61int
62ocinternalinitialize(void)
63{
64    int stat = OC_NOERR;
65
66    /* Compute some xdr related flags */
67    xxdr_init();
68
69    oc_loginit();
70
71    /* Determine if this version of curl supports
72       "file://..." &/or "https://..." urls.
73    */
74    {
75        const char* const* proto; /*weird*/
76        curl_version_info_data* curldata;
77        curldata = curl_version_info(CURLVERSION_NOW);
78        oc_curl_file_supported = 0;
79        oc_curl_https_supported = 0;
80        for(proto=curldata->protocols;*proto;proto++) {
81            if(strcmp("file",*proto)==0) {oc_curl_file_supported=1;break;}
82            if(strcmp("https",*proto)==0) {oc_curl_https_supported=1;break;}
83        }
84        if(ocdebug > 0) {
85            oc_log(LOGNOTE,"Curl file:// support = %d",oc_curl_file_supported);
86            oc_log(LOGNOTE,"Curl https:// support = %d",oc_curl_file_supported);
87        }
88    }
89
90    /* compile the .dodsrc, if any */
91    {
92        char* path = NULL;
93        char* homepath = NULL;
94        char** alias;
95        FILE* f = NULL;
96        /* locate the configuration files: . first in '.',  then $HOME */
97        for(alias=rcfilenames;*alias;alias++) {
98            path = (char*)malloc(strlen("./")+strlen(*alias)+1);
99            if(path == NULL) return OC_ENOMEM;
100            strcpy(path,"./");
101            strcat(path,*alias);
102            /* see if file is readable */
103            f = fopen(path,"r");
104            if(f != NULL) break;
105            if(path != NULL) {free(path); path = NULL;} /* cleanup */
106        }
107        if(f == NULL) { /* try $HOME */
108            OCASSERT(path == NULL);
109            homepath = getenv("HOME");
110            if (homepath!= NULL) {
111                for(alias=rcfilenames;*alias;alias++) {
112                    path = (char*)malloc(strlen(homepath)+1+strlen(*alias)+1);
113                    if(path == NULL) return OC_ENOMEM;
114                    strcpy(path,homepath);
115                    strcat(path,"/");
116                    strcat(path,*alias);
117                    f = fopen(path,"r");
118                    if(f != NULL) break;
119                    if(path != NULL) {free(path); path=NULL;}
120                }
121            }
122        }
123        if(f == NULL) {
124            oc_log(LOGDBG,"Cannot find runtime configuration file");
125        } else {
126            OCASSERT(path != NULL);
127            fclose(f);
128            if(ocdebug > 1)
129                fprintf(stderr, "DODS RC file: %s\n", path);
130            if(ocdodsrc_read(*alias,path) == 0)
131                oc_log(LOGERR, "Error parsing %s\n",path);
132        }
133        if(path != NULL) free(path);
134    }
135    return OCTHROW(stat);
136}
137
138/**************************************************/
139OCerror
140ocopen(OCstate** statep, const char* url)
141{
142    int stat = OC_NOERR;
143    OCstate * state = NULL;
144    OCURI* tmpurl = NULL;
145    CURL* curl = NULL; /* curl handle*/
146
147    if(!ocuriparse(url,&tmpurl)) {OCTHROWCHK(stat=OC_EBADURL); goto fail;}
148   
149    stat = occurlopen(&curl);
150    if(stat != OC_NOERR) {OCTHROWCHK(stat); goto fail;}
151
152    state = (OCstate*)ocmalloc(sizeof(OCstate)); /* ocmalloc zeros memory*/
153    if(state == NULL) {OCTHROWCHK(stat=OC_ENOMEM); goto fail;}
154
155    /* Setup DAP state*/
156    state->magic = OCMAGIC;
157    state->curl = curl;
158    state->trees = oclistnew();
159    state->uri = tmpurl;
160    if(!ocuridecodeparams(state->uri)) {
161        oc_log(LOGWARN,"Could not parse client parameters");
162    }
163    state->packet = ocbytesnew();
164    ocbytessetalloc(state->packet,DFALTPACKETSIZE); /*initial reasonable size*/
165
166    /* set curl properties for this link */
167    ocsetcurlproperties(state);
168
169    /* Set up list to support reuse/reclamation of OCcontent objects. */
170    state->contentlist = NULL;
171
172    if(statep) *statep = state;
173    return OCTHROW(stat);   
174
175fail:
176    ocurifree(tmpurl);
177    if(state != NULL) ocfree(state);
178    if(curl != NULL) occurlclose(curl);
179    return OCTHROW(stat);
180}
181
182OCerror
183ocfetchf(OCstate* state, const char* constraint, OCdxd kind, OCflags flags,
184        OCnode** rootp)
185{
186    OCtree* tree = NULL;
187    OCnode* root = NULL;
188    OCerror stat = OC_NOERR;
189   
190    tree = (OCtree*)ocmalloc(sizeof(OCtree));
191    MEMCHECK(tree,OC_ENOMEM);
192    memset((void*)tree,0,sizeof(OCtree));
193    tree->dxdclass = kind;
194    tree->state = state;
195    tree->constraint = constraintescape(constraint);
196    if(tree->constraint == NULL)
197        tree->constraint = nulldup(constraint);
198
199    /* Set curl properties: pwd, flags, proxies, ssl */
200    if((stat=ocset_user_password(state))!= OC_NOERR) goto fail;
201    if((stat=ocset_curl_flags(state)) != OC_NOERR) goto fail;
202    if((stat=ocset_proxy(state)) != OC_NOERR) goto fail;
203    if((stat=ocset_ssl(state)) != OC_NOERR) goto fail;
204
205    ocbytesclear(state->packet);
206
207    switch (kind) {
208    case OCDAS:
209        stat = readDAS(state,tree);
210        if(stat == OC_NOERR) {
211            tree->text = ocbytesdup(state->packet);
212            if(tree->text == NULL) stat = OC_EDAS;
213        }
214        break;
215    case OCDDS:
216        stat = readDDS(state,tree);
217        if(stat == OC_NOERR) {
218            tree->text = ocbytesdup(state->packet);
219            if(tree->text == NULL) stat = OC_EDDS;
220        }
221        break;
222    case OCDATADDS:
223        if((flags & OCONDISK) != 0) {/* store in file */
224            /* Create the datadds file immediately
225               so that DRNO can reference it*/
226            /* Make the tmp file*/
227            stat = createtempfile(state,tree);
228            if(stat) {OCTHROWCHK(stat); goto unwind;}
229            stat = readDATADDS(state,tree,flags);
230            if(stat == OC_NOERR) {
231                /* Separate the DDS from data and return the dds;
232                   will modify packet */
233                stat = ocextractddsinfile(state,tree,flags);
234            }
235        } else { /*in memory*/
236            stat = readDATADDS(state,tree,flags);
237            if(stat == OC_NOERR) {
238                /* Separate the DDS from data and return the dds;
239               will modify packet */
240            stat = ocextractddsinmemory(state,tree,flags);
241        }
242        }
243        break;
244    }/*switch*/
245    if(stat != OC_NOERR) {
246        /* Obtain any http code */
247        state->error.httpcode = ocfetchhttpcode(state->curl);
248        if(state->error.httpcode >= 400) {
249            oc_log(LOGWARN,"oc_open: Could not read url; http error = %l",state->error.httpcode);
250        } else {
251            oc_log(LOGWARN,"oc_open: Could not read url");
252        }
253        return OCTHROW(stat);
254    }
255
256    tree->nodes = NULL;
257    stat = DAPparse(state,tree,tree->text);
258    /* Check and report on an error return from the server */
259    if(stat == OC_EDAPSVC  && state->error.code != NULL) {
260        oc_log(LOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
261                  state->error.code,   
262                  (state->error.message?state->error.message:""));
263    }
264    if(stat) {OCTHROWCHK(stat); goto unwind;}
265    root = tree->root;
266    /* make sure */
267    tree->root = root;
268    root->tree = tree;
269
270    /* Verify the parse */
271    switch (kind) {
272    case OCDAS:
273        if(root->octype != OC_Attributeset)
274            {OCTHROWCHK(stat=OC_EDAS); goto unwind;}
275        break;
276    case OCDDS:
277        if(root->octype != OC_Dataset)
278            {OCTHROWCHK(stat=OC_EDDS); goto unwind;}
279        break;
280    case OCDATADDS:
281        if(root->octype != OC_Dataset)
282            {OCTHROWCHK(stat=OC_EDATADDS); goto unwind;}
283        /* Modify the tree kind */
284        tree->dxdclass = OCDATADDS;
285        break;
286    default: return OC_EINVAL;
287    }
288
289    if(kind != OCDAS) {
290        /* Process ocnodes to assign offsets and sizes where possible */
291        occomputeskipdata(state,root);
292        /* Process ocnodes to mark those that are cacheable */
293        ocmarkcacheable(state,root);
294        /* Process ocnodes to handle various semantic issues*/
295        occomputesemantics(tree->nodes);
296    }
297
298    /* Process ocnodes to compute name info*/
299    occomputefullnames(tree->root);
300
301     if(kind == OCDATADDS) {
302        if((flags & OCONDISK) != 0) {
303            tree->data.xdrs = xxdr_filecreate(tree->data.file,tree->data.bod);
304        } else {
305            /* Switch to zero based memory */
306            tree->data.xdrs
307                = xxdr_memcreate(tree->data.memory,tree->data.datasize,tree->data.bod);
308        }
309        MEMCHECK(tree->data.xdrs,OC_ENOMEM);
310    }
311
312    /* Put root into the state->trees list */
313    oclistpush(state->trees,(ocelem)root);
314
315    if(rootp) *rootp = root;
316    return stat;
317
318unwind:
319    ocfreetree(tree);
320fail:
321    return OCTHROW(stat);
322}
323
324void
325occlose(OCstate* state)
326{
327    unsigned int i;
328    if(state == NULL) return;
329
330    /* Warning: ocfreeroot will attempt to remove the root from state->trees */
331    /* Ok in this case because we are popping the root out of state->trees */
332    for(i=0;i<oclistlength(state->trees);i++) {
333        OCnode* root = (OCnode*)oclistpop(state->trees);
334        ocfreeroot(root);
335    }
336    oclistfree(state->trees);
337    ocurifree(state->uri);
338    ocbytesfree(state->packet);
339    ocfree(state->error.code);
340    ocfree(state->error.message);
341    if(state->contentlist != NULL) {
342        struct OCcontent* next;
343        struct OCcontent* curr = state->contentlist;
344        while(curr != NULL) {
345            next = curr->next;
346            ocfree(curr);
347            curr = next;
348        }
349    }
350    ocfree(state->curlflags.useragent);
351    ocfree(state->curlflags.cookiejar);
352    ocfree(state->curlflags.cookiefile);
353    ocfree(state->ssl.certificate);
354    ocfree(state->ssl.key);
355    ocfree(state->ssl.keypasswd);
356    ocfree(state->ssl.cainfo);
357    ocfree(state->ssl.capath); 
358    ocfree(state->proxy.host);
359    ocfree(state->creds.username);
360    ocfree(state->creds.password);
361    if(state->curl != NULL) occurlclose(state->curl);
362    ocfree(state);
363}
364
365static OCerror
366ocextractddsinmemory(OCstate* state, OCtree* tree, OCflags flags)
367{
368    OCerror stat = OC_NOERR;
369    size_t ddslen, bod, bodfound;
370    /* Read until we find the separator (or EOF)*/
371    bodfound = findbod(state->packet,&bod,&ddslen);
372    if(!bodfound) {/* No BOD; pretend */
373        bod = tree->data.bod;
374        ddslen = tree->data.datasize;
375    }
376    tree->data.bod = bod;
377    tree->data.ddslen = ddslen;
378    /* copy out the dds */
379    if(ddslen > 0) {
380        tree->text = (char*)ocmalloc(ddslen+1);
381        memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
382        tree->text[ddslen] = '\0';
383    } else
384        tree->text = NULL;
385    /* Extract the inmemory contents */
386    tree->data.memory = ocbytesextract(state->packet);
387#ifdef OCIGNORE
388    /* guarantee the data part is on an 8 byte boundary */
389    if(tree->data.bod % 8 != 0) {
390        unsigned long count = tree->data.datasize - tree->data.bod;
391        memcpy(tree->xdrmemory,tree->xdrmemory+tree->data.bod,count);
392        tree->data.datasize = count;
393        tree->data.bod = 0;
394        tree->data.ddslen = 0;
395    }
396#endif
397    if(tree->text == NULL) stat = OC_EDATADDS;
398    return OCTHROW(stat);
399}
400
401static OCerror
402ocextractddsinfile(OCstate* state, OCtree* tree, OCflags flags)
403{
404    OCerror stat = OC_NOERR;
405    size_t ddslen, bod, bodfound;
406
407    /* Read until we find the separator (or EOF)*/
408    ocbytesclear(state->packet);
409    rewind(tree->data.file);
410    bodfound = 0;
411    do {
412        char chunk[1024];
413        size_t count;
414        /* read chunks of the file until we find the separator*/
415        count = fread(chunk,1,sizeof(chunk),tree->data.file);
416        if(count <= 0) break; /* EOF;*/
417        ocbytesappendn(state->packet,chunk,count);
418        bodfound = findbod(state->packet,&bod,&ddslen);
419    } while(!bodfound);
420    if(!bodfound) {/* No BOD; pretend */
421        bod = tree->data.bod;
422        ddslen = tree->data.datasize;
423    }
424    tree->data.bod = bod;
425    tree->data.ddslen = ddslen;
426    /* copy out the dds */
427    if(ddslen > 0) {
428        tree->text = (char*)ocmalloc(ddslen+1);
429        memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
430        tree->text[ddslen] = '\0';
431    } else
432        tree->text = NULL;
433    /* reset the position of the tmp file*/
434    fseek(tree->data.file,tree->data.bod,SEEK_SET);
435    if(tree->text == NULL) stat = OC_EDATADDS;
436    return OCTHROW(stat);
437}
438
439static OCerror
440createtempfile(OCstate* state, OCtree* tree)
441{
442    int fd;
443    char* name = NULL;
444    fd = createtempfile1(TMPPATH1,&name);
445    if(fd < 0)
446        fd = createtempfile1(TMPPATH2,&name);
447    if(fd < 0) {
448        oc_log(LOGERR,"oc_open: attempt to open tmp file failed: %s",name);
449        return errno;
450    }
451#ifdef OCDEBUG
452    oc_log(LOGNOTE,"oc_open: using tmp file: %s",name);
453#endif
454    tree->data.filename = name; /* remember our tmp file name */
455    tree->data.file = fdopen(fd,"w+");
456    if(tree->data.file == NULL) return OC_EOPEN;
457    /* unlink the temp file so it will automatically be reclaimed */
458    if(ocdebug == 0) unlink(tree->data.filename);
459    return OC_NOERR;
460}
461
462int
463createtempfile1(char* tmppath, char** tmpnamep)
464{
465    int fd = 0;
466    char* tmpname = NULL;
467    tmpname = (char*)malloc(strlen(tmppath)+strlen("dataddsXXXXXX")+1);
468    if(tmpname == NULL) return -1;
469    strcpy(tmpname,tmppath);
470#ifdef HAVE_MKSTEMP
471    strcat(tmpname,"dataddsXXXXXX");
472    /* Note Potential problem: old versions of this function
473       leave the file in mode 0666 instead of 0600 */
474    fd = mkstemp(tmpname);
475#else /* !HAVE_MKSTEMP */
476    /* Need to simulate by using some kind of pseudo-random number */
477    strcat(tmpname,"datadds");
478    {
479        int rno = rand();
480        char spid[7];
481        if(rno < 0) rno = -rno;
482        sprintf(spid,"%06d",rno);
483        strcat(tmpname,spid);
484#  ifdef WIN32
485        fd=open(tmpname,O_RDWR|O_BINARY|O_CREAT|O_EXCL|FILE_ATTRIBUTE_TEMPORARY, _S_IREAD|_S_IWRITE);
486#  else
487        fd=open(tmpname,O_RDWR|O_CREAT|O_EXCL, S_IRWXU);
488#  endif
489    }
490#endif /* !HAVE_MKSTEMP */
491    if(tmpname == NULL) return -1;
492    if(tmpnamep) *tmpnamep = tmpname;
493    return fd;
494}
495
496/* Allow these (non-alpha-numerics) to pass thru */
497static char okchars[] = "&/:;,.=?@'\"<>{}!|\\^[]`~";
498static char hexdigits[] = "0123456789abcdef";
499
500
501/* Modify constraint to use %XX escapes */
502static char*
503constraintescape(const char* url)
504{
505    size_t len;
506    char* p;
507    int c;
508    char* eurl;
509
510    if(url == NULL) return NULL;
511    len = strlen(url);
512    eurl = ocmalloc(1+3*len); /* worst case: c -> %xx */
513    MEMCHECK(eurl,NULL);
514    p = eurl;
515    *p = '\0';
516    while((c=*url++)) {
517        if(c >= '0' && c <= '9') {*p++ = c;}
518        else if(c >= 'a' && c <= 'z') {*p++ = c;}
519        else if(c >= 'A' && c <= 'Z') {*p++ = c;}
520        else if(strchr(okchars,c) != NULL) {*p++ = c;}
521        else {
522            *p++ = '%';
523            *p++ = hexdigits[(c & 0xf0)>>4];
524            *p++ = hexdigits[(c & 0xf)];
525        }
526    }
527    *p = '\0';
528    return eurl;
529}
530
531OCerror
532ocupdatelastmodifieddata(OCstate* state)
533{
534    OCerror status = OC_NOERR;
535    long lastmodified;
536    char* base = NULL;
537    base = ocuribuild(state->uri,NULL,NULL,OCURIENCODE);
538    status = ocfetchlastmodified(state->curl, base, &lastmodified);
539    free(base);
540    if(status == OC_NOERR) {
541        state->datalastmodified = lastmodified;
542    }
543    return status;
544}
545
546/*
547    Set curl properties for link based on rc files
548*/
549static void
550ocsetcurlproperties(OCstate* state)
551{
552    CURLcode cstat = CURLE_OK;
553
554    /* process the triple store wrt to this state */
555    if(ocdodsrc_process(state) != OC_NOERR) {
556        oc_log(LOGERR,"Malformed .opendaprc configuration file");
557        goto fail;
558    }
559    if(state->creds.username == NULL && state->creds.password == NULL) {
560        if(state->uri->user != NULL && state->uri->password != NULL) {
561            /* this overrides .dodsrc */
562            if(state->creds.password) free(state->creds.password);
563            state->creds.password = nulldup(state->uri->password);
564            if(state->creds.username) free(state->creds.username);
565            state->creds.username = nulldup(state->uri->user);
566        }
567    }
568    return;
569
570fail:
571    if(cstat != CURLE_OK)
572        oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
573    return;
574}
Note: See TracBrowser for help on using the repository browser.