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
7#ifdef HAVE_UNISTD_H
8#include <unistd.h>
9#endif
10#ifdef HAVE_FCNTL_H
11#include <fcntl.h>
12#endif
13#include <errno.h>
14
15#ifdef _MSC_VER
16typedef int pid_t;
17#endif
18
19#include "ocinternal.h"
20#include "ocdebug.h"
21#include "occlientparams.h"
22#include "occurlfunctions.h"
23#include "ochttp.h"
24#include "ocread.h"
25#include "dapparselex.h"
26
27#define DATADDSFILE "datadds"
28
29#if 0
30/* Note: TMPPATH must end in '/' */
31#ifdef __CYGWIN__
32#define TMPPATH1 "/cygdrive/c/temp/datadds"
33#define TMPPATH2 "./datadds"
34#elif defined(_WIN32) || defined(_WIN64)
35#define TMPPATH1 "c:\\temp\\datadds"
36#define TMPPATH2 ".\\datadds"
37#else
38#define TMPPATH1 "/tmp/datadds"
39#define TMPPATH2 "./datadds"
40#endif
41#endif
42
43#define CLBRACE '{'
44#define CRBRACE '}'
45
46static OCerror ocextractddsinmemory(OCstate*,OCtree*,int);
47static OCerror ocextractddsinfile(OCstate*,OCtree*,int);
48static char* constraintescape(const char* url);
49static OCerror createtempfile(OCstate*,OCtree*);
50static int dataError(XXDRxdrsOCstate*);
51
52static OCerror ocset_curlproperties(OCstate*);
53
54extern OCnodemakeunlimiteddimension(void);
55
56#if defined(_WIN32) || defined(_WIN64)
57#ifdef HAVE_FCNTL_H
58#include <fcntl.h>
59#endif
60#define _S_IREAD 256
61#define _S_IWRITE 128
62#else
63#ifdef HAVE_SYS_STAT_H
64#include <sys/stat.h>
65#endif
66#endif
67
68/* Collect global state info in one place */
69struct OCGLOBALSTATE ocglobalstate;
70
71OCerror
72ocinternalinitialize(void)
73{
74    int stat = OC_NOERR;
75
76#if 0
77    if(sizeof(off_t) != sizeof(void*)) {
78      fprintf(stderr,"OC xxdr depends on the assumption that sizeof(off_t) == sizeof(void*)\n");
79      /*
80 Commenting out for now, as this does not hold true on 32-bit linux systems.
81      OCASSERT(sizeof(off_t) == sizeof(void*));
82 */
83    }
84#endif
85
86    if(!ocglobalstate.initialized) {
87      memset((void*)&ocglobalstate,0,sizeof(ocglobalstate));
88      ocglobalstate.initialized = 1;
89    }
90
91    /* Capture temp dir*/
92    {
93 char* tempdir;
94 char* p;
95 char* q;
96 char cwd[4096];
97#if defined(_WIN32) || defined(_WIN64)
98        tempdir = getenv("TEMP");
99#else
100 tempdir = "/tmp";
101#endif
102        if(tempdir == NULL) {
103     fprintf(stderr,"Cannot find a temp dir; using ./\n");
104     tempdir = getcwd(cwd,sizeof(cwd));
105     if(tempdir == NULL || *tempdir == '\0') tempdir = ".";
106 }
107        ocglobalstate.tempdir= (char*)malloc(strlen(tempdir) + 1);
108 for(p=tempdir,q=ocglobalstate.tempdir;*p;p++,q++) {
109     if((*p == '/' && *(p+1) == '/')
110        || (*p == '\\' && *(p+1) == '\\')) {p++;}
111     *q = *p;
112 }
113 *q = '\0';
114#if defined(_WIN32) || defined(_WIN64)
115#else
116        /* Canonicalize */
117 for(p=ocglobalstate.tempdir;*p;p++) {
118     if(*p == '\\') {*p = '/'; };
119 }
120 *q = '\0';
121#endif
122    }
123
124    /* Capture $HOME */
125    {
126 char* p;
127 char* q;
128        char* home = getenv("HOME");
129
130        if(home == NULL) {
131     /* use tempdir */
132     home = ocglobalstate.tempdir;
133 }
134        ocglobalstate.home = (char*)malloc(strlen(home) + 1);
135 for(p=home,q=ocglobalstate.home;*p;p++,q++) {
136     if((*p == '/' && *(p+1) == '/')
137        || (*p == '\\' && *(p+1) == '\\')) {p++;}
138     *q = *p;
139 }
140 *q = '\0';
141#if defined(_WIN32) || defined(_WIN64)
142#else
143        /* Canonicalize */
144 for(p=home;*p;p++) {
145     if(*p == '\\') {*p = '/'; };
146 }
147#endif
148    }
149
150    /* Compute some xdr related flags */
151    xxdr_init();
152
153    ocloginit();
154
155    oc_curl_protocols(&ocglobalstate); /* see what protocols are supported */
156
157    return OCTHROW(stat);
158}
159
160
161/**************************************************/
162OCerror
163ocopen(OCstate** statep, const char* url)
164{
165    int stat = OC_NOERR;
166    OCstate * state = NULL;
167    OCURItmpurl = NULL;
168    CURLcurl = NULL; /* curl handle*/
169
170    if(!ocuriparse(url,&tmpurl)) {OCTHROWCHK(stat=OC_EBADURL); goto fail;}
171
172    stat = occurlopen(&curl);
173    if(stat != OC_NOERR) {OCTHROWCHK(stat); goto fail;}
174
175    state = (OCstate*)ocmalloc(sizeof(OCstate)); /* ocmalloc zeros memory*/
176    if(state == NULL) {OCTHROWCHK(stat=OC_ENOMEM); goto fail;}
177
178    /* Setup DAP state*/
179    state->header.magic = OCMAGIC;
180    state->header.occlass = OC_State;
181    state->curl = curl;
182    state->trees = oclistnew();
183    state->uri = tmpurl;
184    if(!ocuridecodeparams(state->uri)) {
185 oclog(OCLOGWARN,"Could not parse client parameters");
186    }
187    state->packet = ocbytesnew();
188    ocbytessetalloc(state->packet,DFALTPACKETSIZE); /*initial reasonable size*/
189
190    /* capture curl properties for this link from rc file1*/
191    stat = ocset_curlproperties(state);
192    if(stat != OC_NOERR) goto fail;
193
194    /* Set the one-time curl flags */
195    if((stat=ocset_flags_perlink(state))!= OC_NOERR) goto fail;
196#if 1 /* temporarily make per-link */
197    if((stat=ocset_flags_perfetch(state))!= OC_NOERR) goto fail;
198#endif
199
200    if(statep) *statep = state;
201    else {
202      if(state != NULLocfree(state);
203    }
204    return OCTHROW(stat);
205
206fail:
207    ocurifree(tmpurl);
208    if(state != NULLocfree(state);
209    if(curl != NULLoccurlclose(curl);
210    return OCTHROW(stat);
211}
212
213OCerror
214ocfetch(OCstatestate, const char* constraintOCdxd kindOCflags flags,
215        OCnode** rootp)
216{
217    OCtreetree = NULL;
218    OCnoderoot = NULL;
219    OCerror stat = OC_NOERR;
220
221    tree = (OCtree*)ocmalloc(sizeof(OCtree));
222    MEMCHECK(tree,OC_ENOMEM);
223    memset((void*)tree,0,sizeof(OCtree));
224    tree->dxdclass = kind;
225    tree->state = state;
226    tree->constraint = constraintescape(constraint);
227    if(tree->constraint == NULL)
228 tree->constraint = nulldup(constraint);
229
230    /* Set per-fetch curl properties */
231#if 0 /* temporarily make per-link */
232    if((stat=ocset_flags_perfetch(state))!= OC_NOERR) goto fail;
233#endif
234
235    ocbytesclear(state->packet);
236
237    switch (kind) {
238    case OCDAS:
239        stat = readDAS(state,tree);
240 if(stat == OC_NOERR) {
241            tree->text = ocbytesdup(state->packet);
242     if(tree->text == NULLstat = OC_EDAS;
243 }
244 break;
245    case OCDDS:
246        stat = readDDS(state,tree);
247 if(stat == OC_NOERR) {
248            tree->text = ocbytesdup(state->packet);
249     if(tree->text == NULLstat = OC_EDDS;
250 }
251 break;
252    case OCDATADDS:
253 if((flags & OCONDISK) != 0) {/* store in file */
254     /* Create the datadds file immediately
255               so that DRNO can reference it*/
256            /* Make the tmp file*/
257            stat = createtempfile(state,tree);
258            if(stat) {OCTHROWCHK(stat); goto fail;}
259            stat = readDATADDS(state,tree,flags);
260     if(stat == OC_NOERR) {
261                /* Separate the DDS from data and return the dds;
262                   will modify packet */
263                stat = ocextractddsinfile(state,tree,flags);
264     }
265 } else { /*inmemory*/
266            stat = readDATADDS(state,tree,flags);
267     if(stat == OC_NOERR) {
268                /* Separate the DDS from data and return the dds;
269               will modify packet */
270            stat = ocextractddsinmemory(state,tree,flags);
271 }
272 }
273 break;
274    default:
275 break;
276    }/*switch*/
277    /* Obtain any http code */
278    state->error.httpcode = ocfetchhttpcode(state->curl);
279    if(stat != OC_NOERR) {
280 if(state->error.httpcode >= 400) {
281     oclog(OCLOGWARN,"oc_open: Could not read url; http error = %l",state->error.httpcode);
282 } else {
283     oclog(OCLOGWARN,"oc_open: Could not read url");
284 }
285 goto fail;
286    }
287
288    tree->nodes = NULL;
289    stat = DAPparse(state,tree,tree->text);
290    /* Check and report on an error return from the server */
291    if(stat == OC_EDAPSVC  && state->error.code != NULL) {
292 oclog(OCLOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
293   state->error.code,
294   (state->error.message?state->error.message:""));
295    }
296    if(stat) {OCTHROWCHK(stat); goto fail;}
297    root = tree->root;
298    /* make sure */
299    tree->root = root;
300    root->tree = tree;
301
302    /* Verify the parse */
303    switch (kind) {
304    case OCDAS:
305        if(root->octype != OC_Attributeset)
306     {OCTHROWCHK(stat=OC_EDAS); goto fail;}
307 break;
308    case OCDDS:
309        if(root->octype != OC_Dataset)
310     {OCTHROWCHK(stat=OC_EDDS); goto fail;}
311 break;
312    case OCDATADDS:
313        if(root->octype != OC_Dataset)
314     {OCTHROWCHK(stat=OC_EDATADDS); goto fail;}
315 /* Modify the tree kind */
316 tree->dxdclass = OCDATADDS;
317 break;
318    default: return OC_EINVAL;
319    }
320
321    if(kind != OCDAS) {
322        /* Process ocnodes to mark those that are cacheable */
323        ocmarkcacheable(state,root);
324        /* Process ocnodes to handle various semantic issues*/
325        occomputesemantics(tree->nodes);
326    }
327
328    /* Process ocnodes to compute name info*/
329    occomputefullnames(tree->root);
330
331     if(kind == OCDATADDS) {
332 if((flags & OCONDISK) != 0) {
333            tree->data.xdrs = xxdr_filecreate(tree->data.file,tree->data.bod);
334 } else {
335#ifdef OCDEBUG
336fprintf(stderr,"ocfetch.datadds.memory: datasize=%lu bod=%lu\n",
337 (unsigned long)tree->data.datasize,(unsigned long)tree->data.bod);
338#endif
339     /* Switch to zero based memory */
340            tree->data.xdrs
341xxdr_memcreate(tree->data.memory,tree->data.datasize,tree->data.bod);
342 }
343        MEMCHECK(tree->data.xdrs,OC_ENOMEM);
344 /* Do a quick check to see if server returned an ERROR {}
345           at the beginning of the data
346         */
347 if(dataError(tree->data.xdrs,state)) {
348     stat = OC_EDATADDS;
349     oclog(OCLOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
350   state->error.code,
351   (state->error.message?state->error.message:""));
352     goto fail;
353 }
354
355 /* Compile the data into a more accessible format */
356 stat = occompile(state,tree->root);
357 if(stat != OC_NOERR)
358     goto fail;
359    }
360
361    /* Put root into the state->trees list */
362    oclistpush(state->trees,(void*)root);
363
364    if(rootp) *rootp = root;
365    return stat;
366
367fail:
368    if(root != NULL)
369 ocroot_free(root);
370    else if(tree != NULL)
371 octree_free(tree);
372    return OCTHROW(stat);
373}
374
375static OCerror
376createtempfile(OCstatestateOCtreetree)
377{
378    int stat = OC_NOERR;
379    char* path = NULL;
380    char* name = NULL;
381    int len;
382
383    len =
384   strlen(ocglobalstate.tempdir)
385   + 1 /* '/' */
386   + strlen(DATADDSFILE);
387    path = (char*)malloc(len+1);
388    if(path == NULL) return OC_ENOMEM;
389    occopycat(path,len,3,ocglobalstate.tempdir,"/",DATADDSFILE);
390    stat = ocmktmp(path,&name);
391    free(path);
392    if(stat != OC_NOERR) goto fail;
393#ifdef OCDEBUG
394    oclog(OCLOGNOTE,"oc_open: creating tmp file: %s",name);
395#endif
396    tree->data.filename = name; /* remember our tmp file name */
397    name = NULL;
398    tree->data.file = fopen(tree->data.filename,"w+");
399    if(tree->data.file == NULL) return OC_EOPEN;
400    /* unlink the temp file so it will automatically be reclaimed */
401    if(ocdebug == 0) unlink(tree->data.filename);
402    return stat;
403
404fail:
405    if(name != NULL) {
406        oclog(OCLOGERR,"oc_open: attempt to create tmp file failed: %s",name);
407 free(name);
408    } else {
409        oclog(OCLOGERR,"oc_open: attempt to create tmp file failed: NULL");
410    }
411    return OCTHROW(stat);
412}
413
414void
415occlose(OCstatestate)
416{
417    unsigned int i;
418    if(state == NULL) return;
419
420    /* Warning: ocfreeroot will attempt to remove the root from state->trees */
421    /* Ok in this case because we are popping the root out of state->trees */
422    for(i=0;i<oclistlength(state->trees);i++) {
423 OCnoderoot = (OCnode*)oclistpop(state->trees);
424 ocroot_free(root);
425    }
426    oclistfree(state->trees);
427    ocurifree(state->uri);
428    ocbytesfree(state->packet);
429    ocfree(state->error.code);
430    ocfree(state->error.message);
431    ocfree(state->curlflags.useragent);
432    if(state->curlflags.cookiejar) {
433#if 0
434        if(state->curlflags.createdflags & COOKIECREATED)
435     unlink(state->curlflags.cookiejar);
436#endif
437 ocfree(state->curlflags.cookiejar);
438    }
439    if(state->curlflags.netrc != NULL) {
440#if 0
441        if(state->curlflags.createdflags & NETRCCREATED)
442     unlink(state->curlflags.netrc);
443#endif
444 ocfree(state->curlflags.netrc);
445    }
446    ocfree(state->ssl.certificate);
447    ocfree(state->ssl.key);
448    ocfree(state->ssl.keypasswd);
449    ocfree(state->ssl.cainfo);
450    ocfree(state->ssl.capath);
451    ocfree(state->proxy.host);
452    ocfree(state->proxy.userpwd);
453    ocfree(state->creds.userpwd);
454    if(state->curl != NULLoccurlclose(state->curl);
455    ocfree(state);
456}
457
458static OCerror
459ocextractddsinmemory(OCstatestateOCtreetreeOCflags flags)
460{
461    OCerror stat = OC_NOERR;
462    size_t ddslenbodbodfound;
463    /* Read until we find the separator (or EOF)*/
464    bodfound = ocfindbod(state->packet,&bod,&ddslen);
465    if(!bodfound) {/* No BOD; pretend */
466 bod = tree->data.bod;
467 ddslen = tree->data.datasize;
468    }
469    tree->data.bod = bod;
470    tree->data.ddslen = ddslen;
471    /* copy out the dds */
472    if(ddslen > 0) {
473        tree->text = (char*)ocmalloc(ddslen+1);
474        memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
475        tree->text[ddslen] = '\0';
476    } else
477 tree->text = NULL;
478    /* Extract the inmemory contents */
479    tree->data.memory = ocbytesextract(state->packet);
480#ifdef OCIGNORE
481    /* guarantee the data part is on an 8 byte boundary */
482    if(tree->data.bod % 8 != 0) {
483        unsigned long count = tree->data.datasize - tree->data.bod;
484        memcpy(tree->xdrmemory,tree->xdrmemory+tree->data.bod,count);
485        tree->data.datasize = count;
486 tree->data.bod = 0;
487 tree->data.ddslen = 0;
488    }
489#endif
490    if(tree->text == NULLstat = OC_EDATADDS;
491    return OCTHROW(stat);
492}
493
494static OCerror
495ocextractddsinfile(OCstatestateOCtreetreeOCflags flags)
496{
497    OCerror stat = OC_NOERR;
498    size_t ddslenbodbodfound;
499
500    /* Read until we find the separator (or EOF)*/
501    ocbytesclear(state->packet);
502    rewind(tree->data.file);
503    bodfound = 0;
504    do {
505        char chunk[1024];
506 size_t count;
507 /* read chunks of the file until we find the separator*/
508        count = fread(chunk,1,sizeof(chunk),tree->data.file);
509 if(count <= 0) break; /* EOF;*/
510        ocbytesappendn(state->packet,chunk,count);
511 bodfound = ocfindbod(state->packet,&bod,&ddslen);
512    } while(!bodfound);
513    if(!bodfound) {/* No BOD; pretend */
514 bod = tree->data.bod;
515 ddslen = tree->data.datasize;
516#ifdef OCDEBUG
517fprintf(stderr,"missing bod: ddslen=%lu bod=%lu\n",
518(unsigned long)ddslen,(unsigned long)bod);
519#endif
520    }
521    tree->data.bod = bod;
522    tree->data.ddslen = ddslen;
523    /* copy out the dds */
524    if(ddslen > 0) {
525        tree->text = (char*)ocmalloc(ddslen+1);
526        memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
527        tree->text[ddslen] = '\0';
528    } else
529 tree->text = NULL;
530    /* reset the position of the tmp file*/
531    if(fseek(tree->data.file,(long)tree->data.bod,SEEK_SET) < 0
532       || tree->text == NULL)
533 stat = OC_EDATADDS;
534    return OCTHROW(stat);
535}
536
537/* Allow these (non-alpha-numerics) to pass thru */
538static char okchars[] = "&/:;,.=?@'\"<>{}!|\\^[]`~";
539static char hexdigits[] = "0123456789abcdef";
540
541/* Modify constraint to use %XX escapes */
542static char*
543constraintescape(const char* url)
544{
545    size_t len;
546    char* p;
547    int c;
548    char* eurl;
549
550    if(url == NULL) return NULL;
551    len = strlen(url);
552    eurl = ocmalloc(1+3*len); /* worst case: c -> %xx */
553    MEMCHECK(eurl,NULL);
554    p = eurl;
555    *p = '\0';
556    while((c=*url++)) {
557 if(c >= '0' && c <= '9') {*p++ = c;}
558 else if(c >= 'a' && c <= 'z') {*p++ = c;}
559 else if(c >= 'A' && c <= 'Z') {*p++ = c;}
560 else if(strchr(okchars,c) != NULL) {*p++ = c;}
561 else {
562     *p++ = '%';
563     *p++ = hexdigits[(c & 0xf0)>>4];
564     *p++ = hexdigits[(c & 0xf)];
565 }
566    }
567    *p = '\0';
568    return eurl;
569}
570
571OCerror
572ocupdatelastmodifieddata(OCstatestate)
573{
574    OCerror status = OC_NOERR;
575    long lastmodified;
576    char* base = NULL;
577    base = ocuribuild(state->uri,NULL,NULL,OCURIENCODE);
578    status = ocfetchlastmodified(state->curlbase, &lastmodified);
579    free(base);
580    if(status == OC_NOERR) {
581 state->datalastmodified = lastmodified;
582    }
583    return OCTHROW(status);
584}
585
586/*
587    Set curl properties for link based on rc files etc.
588*/
589static OCerror
590ocset_curlproperties(OCstatestate)
591{
592    OCerror stat = OC_NOERR;
593
594    /* extract the relevant triples int state */
595    ocrc_process(state);
596
597    if(state->curlflags.useragent == NULL) {
598        size_t len = strlen(DFALTUSERAGENT) + strlen(VERSION) + 1;
599 char* agent = (char*)malloc(len+1);
600 if(occopycat(agent,len,2,DFALTUSERAGENT,VERSION))
601     state->curlflags.useragent = agent;
602 else
603     free(agent);
604    }
605
606    /* Some servers (e.g. thredds and columbia) appear to require a place
607       to put cookies in order for some security functions to work
608    */
609    if(state->curlflags.cookiejar != NULL
610       && strlen(state->curlflags.cookiejar) == 0) {
611 free(state->curlflags.cookiejar);
612 state->curlflags.cookiejar = NULL;
613    }
614
615    if(state->curlflags.cookiejar == NULL) {
616 /* If no cookie file was defined, define a default */
617 char tmp[OCPATHMAX+1];
618        int stat;
619 pid_t pid = getpid();
620 snprintf(tmp,sizeof(tmp)-1,"%s/%s.%ld/",ocglobalstate.tempdir,OCDIR,(long)pid);
621#ifdef _WIN32
622 stat = mkdir(tmp);
623#else
624 stat = mkdir(tmp,S_IRUSR | S_IWUSR | S_IXUSR);
625#endif
626 if(stat != 0 && errno != EEXIST) {
627     fprintf(stderr,"Cannot create cookie directory\n");
628     goto fail;
629 }
630 errno = 0;
631 /* Create the unique cookie file name */
632 stat = ocmktmp(tmp,&state->curlflags.cookiejar);
633 state->curlflags.createdflags |= COOKIECREATED;
634 if(stat != OC_NOERR && errno != EEXIST) {
635     fprintf(stderr,"Cannot create cookie file\n");
636     goto fail;
637 }
638 errno = 0;
639    }
640    OCASSERT(state->curlflags.cookiejar != NULL);
641
642    /* Make sure the cookie jar exists and can be read and written */
643    {
644 FILEf = NULL;
645 char* fname = state->curlflags.cookiejar;
646 /* See if the file exists already */
647        f = fopen(fname,"r");
648 if(f == NULL) {
649     /* Ok, create it */
650     f = fopen(fname,"w+");
651     if(f == NULL) {
652         fprintf(stderr,"Cookie file cannot be read and written: %s\n",fname);
653         {stat = OC_EPERM; goto fail;}
654     }
655 } else { /* test if file can be written */
656     fclose(f);
657     f = fopen(fname,"r+");
658     if(f == NULL) {
659         fprintf(stderr,"Cookie file is cannot be written: %s\n",fname);
660         {stat = OC_EPERM; goto fail;}
661     }
662 }
663 if(f != NULL) fclose(f);
664    }
665
666#if 0
667    /* Create a netrc file if specified  and required,
668       where required => >1 NETRC triples exist */
669    if(ocrc_netrc_required(state)) {
670 /* WARNING: it appears that a user+pwd was specified specifically, then
671           the netrc file will be completely disabled. */
672 if(state->creds.userpwd != NULL) {
673       oclog(OCLOGWARN,"The rc file specifies both netrc and user+pwd; this will cause curl to ignore the netrc file");
674 }
675 stat = oc_build_netrc(state);
676    }
677#endif
678
679    return stat;
680
681fail:
682    return OCTHROW(stat);
683}
684
685static char* ERROR_TAG = "Error ";
686
687static int
688dataError(XXDRxdrsOCstatestate)
689{
690    int depth=0;
691    int errfound = 0;
692    off_t ckp=0,avail=0;
693    int i=0;
694    char* errmsg = NULL;
695    char errortext[16]; /* bigger thant |ERROR_TAG|*/
696    avail = xxdr_getavail(xdrs);
697    if(avail < strlen(ERROR_TAG))
698 goto done; /* assume it is ok */
699    ckp = xxdr_getpos(xdrs);
700    /* Read enough characters to test for 'ERROR ' */
701    errortext[0] = '\0';
702    xxdr_getbytes(xdrs,errortext,(off_t)strlen(ERROR_TAG));
703    if(ocstrncmp(errortext,ERROR_TAG,strlen(ERROR_TAG)) != 0)
704 goto done; /* not an immediate error */
705    /* Try to locate the whole error body */
706    xxdr_setpos(xdrs,ckp);
707    for(depth=0,i=0;i<avail;i++) {
708 xxdr_getbytes(xdrs,errortext,(off_t)1);
709 if(errortext[0] == CLBRACEdepth++;
710 else if(errortext[0] == CRBRACE) {
711     depth--;
712     if(depth == 0) {i++; break;}
713 }
714    }
715    errmsg = (char*)malloc((size_t)i+1);
716    if(errmsg == NULL) {errfound = 1; goto done;}
717    xxdr_setpos(xdrs,ckp);
718    xxdr_getbytes(xdrs,errmsg,(off_t)i);
719    errmsg[i] = '\0';
720    state->error.message = errmsg;
721    state->error.code = strdup("?");
722    state->error.httpcode = 404;
723    xxdr_setpos(xdrs,ckp);
724    errfound = 1;
725done:
726    xxdr_setpos(xdrs,ckp);
727    return errfound;
728}
729
730/**************************************************/
731/* Curl option functions */
732/*
733Note that if we set the option in curlflags,
734then we need to also invoke the ocset_curlopt
735to update the curl flags in libcurl.
736*/
737
738OCerror
739ocset_useragent(OCstatestate, const char* agent)
740{
741    OCerror stat = OC_NOERR;
742    if(state->curlflags.useragent != NULL)
743 free(state->curlflags.useragent);
744    state->curlflags.useragent = strdup(agent);
745    if(state->curlflags.useragent == NULL)
746 return OCTHROW(OC_ENOMEM);
747    stat = ocset_curlflag(state,CURLOPT_USERAGENT);
748    return stat;
749}
750
751OCerror
752ocset_netrc(OCstatestate, const char* path)
753{
754    OCerror stat = OC_NOERR;
755    if(state->curlflags.netrc != NULL)
756 free(state->curlflags.netrc);
757    state->curlflags.netrc = strdup(path);
758    if(state->curlflags.netrc == NULL)
759 return OCTHROW(OC_ENOMEM);
760    stat = ocset_curlflag(state,CURLOPT_NETRC);
761    return stat;
762}


HyperKWIC - Version 7.20DA executed at 11:37 on 27 Oct 2017 | Polyhedron Solutions - INTERNAL USE | COMMERCIAL (Any O/S) SN 4AKIed