123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574 |
- /* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
- See the COPYRIGHT file for more information. */
- #include "config.h"
- #include <stdio.h>
- #include <fcntl.h>
- #include <errno.h>
- #ifdef HAVE_UNISTD_H
- #include <unistd.h>
- #endif
- #include "ocinternal.h"
- #include "ocdebug.h"
- #include "ocdata.h"
- #include "occontent.h"
- #include "occlientparams.h"
- #include "ocrc.h"
- #include "occurlfunctions.h"
- #include "ochttp.h"
- #include "ocread.h"
- /* Note: TMPPATH must end in '/' */
- #ifdef __CYGWIN__
- #define TMPPATH1 "/cygdrive/c/temp/"
- #define TMPPATH2 "./"
- #elifdef WIN32
- #define TMPPATH1 "c:\\temp/"
- #define TMPPATH2 ".\\"
- #else
- #define TMPPATH1 "/tmp/"
- #define TMPPATH2 "./"
- #endif
- /* Define default rc files and aliases*/
- static char* rcfilenames[3] = {".dodsrc",".ocrc",NULL};
- static int ocextractddsinmemory(OCstate*,OCtree*,int);
- static int ocextractddsinfile(OCstate*,OCtree*,int);
- static char* constraintescape(const char* url);
- static OCerror createtempfile(OCstate*,OCtree*);
- static int createtempfile1(char*,char**);
- static void ocsetcurlproperties(OCstate*);
- extern OCnode* makeunlimiteddimension(void);
- #ifdef WIN32
- #include <fcntl.h>
- #define _S_IREAD 256
- #define _S_IWRITE 128
- #else
- #include <sys/stat.h>
- #endif
- /* Global flags*/
- int oc_curl_file_supported;
- int oc_curl_https_supported;
- int
- ocinternalinitialize(void)
- {
- int stat = OC_NOERR;
- /* Compute some xdr related flags */
- xxdr_init();
- oc_loginit();
- /* Determine if this version of curl supports
- "file://..." &/or "https://..." urls.
- */
- {
- const char* const* proto; /*weird*/
- curl_version_info_data* curldata;
- curldata = curl_version_info(CURLVERSION_NOW);
- oc_curl_file_supported = 0;
- oc_curl_https_supported = 0;
- for(proto=curldata->protocols;*proto;proto++) {
- if(strcmp("file",*proto)==0) {oc_curl_file_supported=1;break;}
- if(strcmp("https",*proto)==0) {oc_curl_https_supported=1;break;}
- }
- if(ocdebug > 0) {
- oc_log(LOGNOTE,"Curl file:// support = %d",oc_curl_file_supported);
- oc_log(LOGNOTE,"Curl https:// support = %d",oc_curl_file_supported);
- }
- }
- /* compile the .dodsrc, if any */
- {
- char* path = NULL;
- char* homepath = NULL;
- char** alias;
- FILE* f = NULL;
- /* locate the configuration files: . first in '.', then $HOME */
- for(alias=rcfilenames;*alias;alias++) {
- path = (char*)malloc(strlen("./")+strlen(*alias)+1);
- if(path == NULL) return OC_ENOMEM;
- strcpy(path,"./");
- strcat(path,*alias);
- /* see if file is readable */
- f = fopen(path,"r");
- if(f != NULL) break;
- if(path != NULL) {free(path); path = NULL;} /* cleanup */
- }
- if(f == NULL) { /* try $HOME */
- OCASSERT(path == NULL);
- homepath = getenv("HOME");
- if (homepath!= NULL) {
- for(alias=rcfilenames;*alias;alias++) {
- path = (char*)malloc(strlen(homepath)+1+strlen(*alias)+1);
- if(path == NULL) return OC_ENOMEM;
- strcpy(path,homepath);
- strcat(path,"/");
- strcat(path,*alias);
- f = fopen(path,"r");
- if(f != NULL) break;
- if(path != NULL) {free(path); path=NULL;}
- }
- }
- }
- if(f == NULL) {
- oc_log(LOGDBG,"Cannot find runtime configuration file");
- } else {
- OCASSERT(path != NULL);
- fclose(f);
- if(ocdebug > 1)
- fprintf(stderr, "DODS RC file: %s\n", path);
- if(ocdodsrc_read(*alias,path) == 0)
- oc_log(LOGERR, "Error parsing %s\n",path);
- }
- if(path != NULL) free(path);
- }
- return OCTHROW(stat);
- }
- /**************************************************/
- OCerror
- ocopen(OCstate** statep, const char* url)
- {
- int stat = OC_NOERR;
- OCstate * state = NULL;
- OCURI* tmpurl = NULL;
- CURL* curl = NULL; /* curl handle*/
- if(!ocuriparse(url,&tmpurl)) {OCTHROWCHK(stat=OC_EBADURL); goto fail;}
-
- stat = occurlopen(&curl);
- if(stat != OC_NOERR) {OCTHROWCHK(stat); goto fail;}
- state = (OCstate*)ocmalloc(sizeof(OCstate)); /* ocmalloc zeros memory*/
- if(state == NULL) {OCTHROWCHK(stat=OC_ENOMEM); goto fail;}
- /* Setup DAP state*/
- state->magic = OCMAGIC;
- state->curl = curl;
- state->trees = oclistnew();
- state->uri = tmpurl;
- if(!ocuridecodeparams(state->uri)) {
- oc_log(LOGWARN,"Could not parse client parameters");
- }
- state->packet = ocbytesnew();
- ocbytessetalloc(state->packet,DFALTPACKETSIZE); /*initial reasonable size*/
- /* set curl properties for this link */
- ocsetcurlproperties(state);
- /* Set up list to support reuse/reclamation of OCcontent objects. */
- state->contentlist = NULL;
- if(statep) *statep = state;
- return OCTHROW(stat);
- fail:
- ocurifree(tmpurl);
- if(state != NULL) ocfree(state);
- if(curl != NULL) occurlclose(curl);
- return OCTHROW(stat);
- }
- OCerror
- ocfetchf(OCstate* state, const char* constraint, OCdxd kind, OCflags flags,
- OCnode** rootp)
- {
- OCtree* tree = NULL;
- OCnode* root = NULL;
- OCerror stat = OC_NOERR;
-
- tree = (OCtree*)ocmalloc(sizeof(OCtree));
- MEMCHECK(tree,OC_ENOMEM);
- memset((void*)tree,0,sizeof(OCtree));
- tree->dxdclass = kind;
- tree->state = state;
- tree->constraint = constraintescape(constraint);
- if(tree->constraint == NULL)
- tree->constraint = nulldup(constraint);
- /* Set curl properties: pwd, flags, proxies, ssl */
- if((stat=ocset_user_password(state))!= OC_NOERR) goto fail;
- if((stat=ocset_curl_flags(state)) != OC_NOERR) goto fail;
- if((stat=ocset_proxy(state)) != OC_NOERR) goto fail;
- if((stat=ocset_ssl(state)) != OC_NOERR) goto fail;
- ocbytesclear(state->packet);
- switch (kind) {
- case OCDAS:
- stat = readDAS(state,tree);
- if(stat == OC_NOERR) {
- tree->text = ocbytesdup(state->packet);
- if(tree->text == NULL) stat = OC_EDAS;
- }
- break;
- case OCDDS:
- stat = readDDS(state,tree);
- if(stat == OC_NOERR) {
- tree->text = ocbytesdup(state->packet);
- if(tree->text == NULL) stat = OC_EDDS;
- }
- break;
- case OCDATADDS:
- if((flags & OCONDISK) != 0) {/* store in file */
- /* Create the datadds file immediately
- so that DRNO can reference it*/
- /* Make the tmp file*/
- stat = createtempfile(state,tree);
- if(stat) {OCTHROWCHK(stat); goto unwind;}
- stat = readDATADDS(state,tree,flags);
- if(stat == OC_NOERR) {
- /* Separate the DDS from data and return the dds;
- will modify packet */
- stat = ocextractddsinfile(state,tree,flags);
- }
- } else { /*in memory*/
- stat = readDATADDS(state,tree,flags);
- if(stat == OC_NOERR) {
- /* Separate the DDS from data and return the dds;
- will modify packet */
- stat = ocextractddsinmemory(state,tree,flags);
- }
- }
- break;
- }/*switch*/
- if(stat != OC_NOERR) {
- /* Obtain any http code */
- state->error.httpcode = ocfetchhttpcode(state->curl);
- if(state->error.httpcode >= 400) {
- oc_log(LOGWARN,"oc_open: Could not read url; http error = %l",state->error.httpcode);
- } else {
- oc_log(LOGWARN,"oc_open: Could not read url");
- }
- return OCTHROW(stat);
- }
- tree->nodes = NULL;
- stat = DAPparse(state,tree,tree->text);
- /* Check and report on an error return from the server */
- if(stat == OC_EDAPSVC && state->error.code != NULL) {
- oc_log(LOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
- state->error.code,
- (state->error.message?state->error.message:""));
- }
- if(stat) {OCTHROWCHK(stat); goto unwind;}
- root = tree->root;
- /* make sure */
- tree->root = root;
- root->tree = tree;
- /* Verify the parse */
- switch (kind) {
- case OCDAS:
- if(root->octype != OC_Attributeset)
- {OCTHROWCHK(stat=OC_EDAS); goto unwind;}
- break;
- case OCDDS:
- if(root->octype != OC_Dataset)
- {OCTHROWCHK(stat=OC_EDDS); goto unwind;}
- break;
- case OCDATADDS:
- if(root->octype != OC_Dataset)
- {OCTHROWCHK(stat=OC_EDATADDS); goto unwind;}
- /* Modify the tree kind */
- tree->dxdclass = OCDATADDS;
- break;
- default: return OC_EINVAL;
- }
- if(kind != OCDAS) {
- /* Process ocnodes to assign offsets and sizes where possible */
- occomputeskipdata(state,root);
- /* Process ocnodes to mark those that are cacheable */
- ocmarkcacheable(state,root);
- /* Process ocnodes to handle various semantic issues*/
- occomputesemantics(tree->nodes);
- }
- /* Process ocnodes to compute name info*/
- occomputefullnames(tree->root);
- if(kind == OCDATADDS) {
- if((flags & OCONDISK) != 0) {
- tree->data.xdrs = xxdr_filecreate(tree->data.file,tree->data.bod);
- } else {
- /* Switch to zero based memory */
- tree->data.xdrs
- = xxdr_memcreate(tree->data.memory,tree->data.datasize,tree->data.bod);
- }
- MEMCHECK(tree->data.xdrs,OC_ENOMEM);
- }
- /* Put root into the state->trees list */
- oclistpush(state->trees,(ocelem)root);
- if(rootp) *rootp = root;
- return stat;
- unwind:
- ocfreetree(tree);
- fail:
- return OCTHROW(stat);
- }
- void
- occlose(OCstate* state)
- {
- unsigned int i;
- if(state == NULL) return;
- /* Warning: ocfreeroot will attempt to remove the root from state->trees */
- /* Ok in this case because we are popping the root out of state->trees */
- for(i=0;i<oclistlength(state->trees);i++) {
- OCnode* root = (OCnode*)oclistpop(state->trees);
- ocfreeroot(root);
- }
- oclistfree(state->trees);
- ocurifree(state->uri);
- ocbytesfree(state->packet);
- ocfree(state->error.code);
- ocfree(state->error.message);
- if(state->contentlist != NULL) {
- struct OCcontent* next;
- struct OCcontent* curr = state->contentlist;
- while(curr != NULL) {
- next = curr->next;
- ocfree(curr);
- curr = next;
- }
- }
- ocfree(state->curlflags.useragent);
- ocfree(state->curlflags.cookiejar);
- ocfree(state->curlflags.cookiefile);
- ocfree(state->ssl.certificate);
- ocfree(state->ssl.key);
- ocfree(state->ssl.keypasswd);
- ocfree(state->ssl.cainfo);
- ocfree(state->ssl.capath);
- ocfree(state->proxy.host);
- ocfree(state->creds.username);
- ocfree(state->creds.password);
- if(state->curl != NULL) occurlclose(state->curl);
- ocfree(state);
- }
- static OCerror
- ocextractddsinmemory(OCstate* state, OCtree* tree, OCflags flags)
- {
- OCerror stat = OC_NOERR;
- size_t ddslen, bod, bodfound;
- /* Read until we find the separator (or EOF)*/
- bodfound = findbod(state->packet,&bod,&ddslen);
- if(!bodfound) {/* No BOD; pretend */
- bod = tree->data.bod;
- ddslen = tree->data.datasize;
- }
- tree->data.bod = bod;
- tree->data.ddslen = ddslen;
- /* copy out the dds */
- if(ddslen > 0) {
- tree->text = (char*)ocmalloc(ddslen+1);
- memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
- tree->text[ddslen] = '\0';
- } else
- tree->text = NULL;
- /* Extract the inmemory contents */
- tree->data.memory = ocbytesextract(state->packet);
- #ifdef OCIGNORE
- /* guarantee the data part is on an 8 byte boundary */
- if(tree->data.bod % 8 != 0) {
- unsigned long count = tree->data.datasize - tree->data.bod;
- memcpy(tree->xdrmemory,tree->xdrmemory+tree->data.bod,count);
- tree->data.datasize = count;
- tree->data.bod = 0;
- tree->data.ddslen = 0;
- }
- #endif
- if(tree->text == NULL) stat = OC_EDATADDS;
- return OCTHROW(stat);
- }
- static OCerror
- ocextractddsinfile(OCstate* state, OCtree* tree, OCflags flags)
- {
- OCerror stat = OC_NOERR;
- size_t ddslen, bod, bodfound;
- /* Read until we find the separator (or EOF)*/
- ocbytesclear(state->packet);
- rewind(tree->data.file);
- bodfound = 0;
- do {
- char chunk[1024];
- size_t count;
- /* read chunks of the file until we find the separator*/
- count = fread(chunk,1,sizeof(chunk),tree->data.file);
- if(count <= 0) break; /* EOF;*/
- ocbytesappendn(state->packet,chunk,count);
- bodfound = findbod(state->packet,&bod,&ddslen);
- } while(!bodfound);
- if(!bodfound) {/* No BOD; pretend */
- bod = tree->data.bod;
- ddslen = tree->data.datasize;
- }
- tree->data.bod = bod;
- tree->data.ddslen = ddslen;
- /* copy out the dds */
- if(ddslen > 0) {
- tree->text = (char*)ocmalloc(ddslen+1);
- memcpy((void*)tree->text,(void*)ocbytescontents(state->packet),ddslen);
- tree->text[ddslen] = '\0';
- } else
- tree->text = NULL;
- /* reset the position of the tmp file*/
- fseek(tree->data.file,tree->data.bod,SEEK_SET);
- if(tree->text == NULL) stat = OC_EDATADDS;
- return OCTHROW(stat);
- }
- static OCerror
- createtempfile(OCstate* state, OCtree* tree)
- {
- int fd;
- char* name = NULL;
- fd = createtempfile1(TMPPATH1,&name);
- if(fd < 0)
- fd = createtempfile1(TMPPATH2,&name);
- if(fd < 0) {
- oc_log(LOGERR,"oc_open: attempt to open tmp file failed: %s",name);
- return errno;
- }
- #ifdef OCDEBUG
- oc_log(LOGNOTE,"oc_open: using tmp file: %s",name);
- #endif
- tree->data.filename = name; /* remember our tmp file name */
- tree->data.file = fdopen(fd,"w+");
- if(tree->data.file == NULL) return OC_EOPEN;
- /* unlink the temp file so it will automatically be reclaimed */
- if(ocdebug == 0) unlink(tree->data.filename);
- return OC_NOERR;
- }
- int
- createtempfile1(char* tmppath, char** tmpnamep)
- {
- int fd = 0;
- char* tmpname = NULL;
- tmpname = (char*)malloc(strlen(tmppath)+strlen("dataddsXXXXXX")+1);
- if(tmpname == NULL) return -1;
- strcpy(tmpname,tmppath);
- #ifdef HAVE_MKSTEMP
- strcat(tmpname,"dataddsXXXXXX");
- /* Note Potential problem: old versions of this function
- leave the file in mode 0666 instead of 0600 */
- fd = mkstemp(tmpname);
- #else /* !HAVE_MKSTEMP */
- /* Need to simulate by using some kind of pseudo-random number */
- strcat(tmpname,"datadds");
- {
- int rno = rand();
- char spid[7];
- if(rno < 0) rno = -rno;
- sprintf(spid,"%06d",rno);
- strcat(tmpname,spid);
- # ifdef WIN32
- fd=open(tmpname,O_RDWR|O_BINARY|O_CREAT|O_EXCL|FILE_ATTRIBUTE_TEMPORARY, _S_IREAD|_S_IWRITE);
- # else
- fd=open(tmpname,O_RDWR|O_CREAT|O_EXCL, S_IRWXU);
- # endif
- }
- #endif /* !HAVE_MKSTEMP */
- if(tmpname == NULL) return -1;
- if(tmpnamep) *tmpnamep = tmpname;
- return fd;
- }
- /* Allow these (non-alpha-numerics) to pass thru */
- static char okchars[] = "&/:;,.=?@'\"<>{}!|\\^[]`~";
- static char hexdigits[] = "0123456789abcdef";
- /* Modify constraint to use %XX escapes */
- static char*
- constraintescape(const char* url)
- {
- size_t len;
- char* p;
- int c;
- char* eurl;
- if(url == NULL) return NULL;
- len = strlen(url);
- eurl = ocmalloc(1+3*len); /* worst case: c -> %xx */
- MEMCHECK(eurl,NULL);
- p = eurl;
- *p = '\0';
- while((c=*url++)) {
- if(c >= '0' && c <= '9') {*p++ = c;}
- else if(c >= 'a' && c <= 'z') {*p++ = c;}
- else if(c >= 'A' && c <= 'Z') {*p++ = c;}
- else if(strchr(okchars,c) != NULL) {*p++ = c;}
- else {
- *p++ = '%';
- *p++ = hexdigits[(c & 0xf0)>>4];
- *p++ = hexdigits[(c & 0xf)];
- }
- }
- *p = '\0';
- return eurl;
- }
- OCerror
- ocupdatelastmodifieddata(OCstate* state)
- {
- OCerror status = OC_NOERR;
- long lastmodified;
- char* base = NULL;
- base = ocuribuild(state->uri,NULL,NULL,OCURIENCODE);
- status = ocfetchlastmodified(state->curl, base, &lastmodified);
- free(base);
- if(status == OC_NOERR) {
- state->datalastmodified = lastmodified;
- }
- return status;
- }
- /*
- Set curl properties for link based on rc files
- */
- static void
- ocsetcurlproperties(OCstate* state)
- {
- CURLcode cstat = CURLE_OK;
- /* process the triple store wrt to this state */
- if(ocdodsrc_process(state) != OC_NOERR) {
- oc_log(LOGERR,"Malformed .opendaprc configuration file");
- goto fail;
- }
- if(state->creds.username == NULL && state->creds.password == NULL) {
- if(state->uri->user != NULL && state->uri->password != NULL) {
- /* this overrides .dodsrc */
- if(state->creds.password) free(state->creds.password);
- state->creds.password = nulldup(state->uri->password);
- if(state->creds.username) free(state->creds.username);
- state->creds.username = nulldup(state->uri->user);
- }
- }
- return;
- fail:
- if(cstat != CURLE_OK)
- oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
- return;
- }
|