2018-12-07 06:47:47 +08:00
|
|
|
/* Copyright 2018, UCAR/Unidata and OPeNDAP, Inc.
|
2012-08-01 04:34:13 +08:00
|
|
|
See the COPYRIGHT file for more information. */
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
2017-03-09 08:01:10 +08:00
|
|
|
#ifdef HAVE_SYS_STAT_H
|
|
|
|
#include <sys/stat.h>
|
2013-11-15 06:13:20 +08:00
|
|
|
#endif
|
2017-07-06 00:03:48 +08:00
|
|
|
#if _MSC_VER
|
2017-03-09 08:01:10 +08:00
|
|
|
#include <io.h>
|
|
|
|
#include <direct.h>
|
|
|
|
#include <process.h>
|
2015-05-30 07:31:42 +08:00
|
|
|
#endif
|
|
|
|
|
2017-07-06 00:03:48 +08:00
|
|
|
#ifdef HAVE_FTW_H
|
|
|
|
#include <ftw.h>
|
|
|
|
#endif
|
|
|
|
|
2017-03-09 08:01:10 +08:00
|
|
|
#include <errno.h>
|
|
|
|
|
2017-08-31 07:44:57 +08:00
|
|
|
#include "ncrc.h"
|
2012-08-01 04:34:13 +08:00
|
|
|
#include "ocinternal.h"
|
|
|
|
#include "ocdebug.h"
|
|
|
|
#include "occurlfunctions.h"
|
|
|
|
#include "ochttp.h"
|
|
|
|
#include "ocread.h"
|
|
|
|
#include "dapparselex.h"
|
2017-07-14 00:40:07 +08:00
|
|
|
#include "ncwinpath.h"
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
#define DATADDSFILE "datadds"
|
|
|
|
|
2013-02-08 06:14:57 +08:00
|
|
|
#define CLBRACE '{'
|
|
|
|
#define CRBRACE '}'
|
|
|
|
|
2018-08-27 07:04:46 +08:00
|
|
|
#define OCBUFFERSIZE "HTTP.READ.BUFFERSIZE"
|
|
|
|
#define OCKEEPALIVE "HTTP.KEEPALIVE"
|
|
|
|
|
|
|
|
#ifdef HAVE_CURLOPT_BUFFERSIZE
|
|
|
|
#ifndef CURL_MAX_READ_SIZE
|
|
|
|
#define CURL_MAX_READ_SIZE (512*1024)
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2017-07-06 00:03:48 +08:00
|
|
|
/*Forward*/
|
2014-05-07 03:10:59 +08:00
|
|
|
static OCerror ocextractddsinmemory(OCstate*,OCtree*,int);
|
|
|
|
static OCerror ocextractddsinfile(OCstate*,OCtree*,int);
|
2012-08-01 04:34:13 +08:00
|
|
|
static char* constraintescape(const char* url);
|
|
|
|
static OCerror createtempfile(OCstate*,OCtree*);
|
2013-02-08 06:14:57 +08:00
|
|
|
static int dataError(XXDR* xdrs, OCstate*);
|
2017-07-06 00:03:48 +08:00
|
|
|
static void ocremovefile(const char* path);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
static OCerror ocset_curlproperties(OCstate*);
|
2018-08-27 07:04:46 +08:00
|
|
|
static OCerror ocget_rcproperties(OCstate*);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
extern OCnode* makeunlimiteddimension(void);
|
|
|
|
|
2017-08-31 07:44:57 +08:00
|
|
|
int ocinitialized = 0;
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2014-05-09 04:13:51 +08:00
|
|
|
OCerror
|
2012-08-01 04:34:13 +08:00
|
|
|
ocinternalinitialize(void)
|
|
|
|
{
|
|
|
|
int stat = OC_NOERR;
|
2017-11-23 05:10:01 +08:00
|
|
|
CURLcode cstat = CURLE_OK;
|
2014-12-25 01:22:47 +08:00
|
|
|
|
2017-08-31 07:44:57 +08:00
|
|
|
if(ocinitialized) return OC_NOERR;
|
|
|
|
ocinitialized = 1;
|
|
|
|
|
2015-03-03 11:26:39 +08:00
|
|
|
#if 0
|
2015-01-16 05:19:51 +08:00
|
|
|
if(sizeof(off_t) != sizeof(void*)) {
|
2015-01-17 00:18:39 +08:00
|
|
|
fprintf(stderr,"OC xxdr depends on the assumption that sizeof(off_t) == sizeof(void*)\n");
|
2015-08-16 06:26:35 +08:00
|
|
|
/*
|
|
|
|
Commenting out for now, as this does not hold true on 32-bit linux systems.
|
|
|
|
OCASSERT(sizeof(off_t) == sizeof(void*));
|
|
|
|
*/
|
2014-12-25 01:22:47 +08:00
|
|
|
}
|
2015-03-03 11:26:39 +08:00
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2017-08-31 07:44:57 +08:00
|
|
|
cstat = curl_global_init(CURL_GLOBAL_ALL);
|
|
|
|
if(cstat != CURLE_OK)
|
2017-03-09 08:01:10 +08:00
|
|
|
fprintf(stderr,"curl_global_init failed!\n");
|
2013-11-15 06:13:20 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
/* Compute some xdr related flags */
|
|
|
|
xxdr_init();
|
|
|
|
|
2018-08-27 07:04:46 +08:00
|
|
|
/* Make sure that the rc file has been loaded */
|
|
|
|
(void)NC_rcload();
|
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
return OCTHROW(stat);
|
|
|
|
}
|
|
|
|
|
2014-12-01 11:30:23 +08:00
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
/**************************************************/
|
|
|
|
OCerror
|
|
|
|
ocopen(OCstate** statep, const char* url)
|
|
|
|
{
|
|
|
|
int stat = OC_NOERR;
|
|
|
|
OCstate * state = NULL;
|
2017-03-09 08:01:10 +08:00
|
|
|
NCURI* tmpurl = NULL;
|
2012-08-01 04:34:13 +08:00
|
|
|
CURL* curl = NULL; /* curl handle*/
|
|
|
|
|
2019-02-11 05:00:40 +08:00
|
|
|
if(!ocinitialized)
|
|
|
|
ocinternalinitialize();
|
|
|
|
|
2017-09-03 08:09:36 +08:00
|
|
|
if(ncuriparse(url,&tmpurl) != NCU_OK) {
|
|
|
|
OCTHROWCHK(stat=OC_EBADURL);
|
|
|
|
goto fail;
|
|
|
|
}
|
2015-01-13 03:09:34 +08:00
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
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->header.magic = OCMAGIC;
|
|
|
|
state->header.occlass = OC_State;
|
|
|
|
state->curl = curl;
|
2017-03-09 08:01:10 +08:00
|
|
|
state->trees = nclistnew();
|
2012-08-01 04:34:13 +08:00
|
|
|
state->uri = tmpurl;
|
2017-03-09 08:01:10 +08:00
|
|
|
|
|
|
|
state->packet = ncbytesnew();
|
|
|
|
ncbytessetalloc(state->packet,DFALTPACKETSIZE); /*initial reasonable size*/
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2017-09-01 04:19:56 +08:00
|
|
|
/* Initialize auth info from rc file */
|
|
|
|
stat = NC_authsetup(&state->auth, state->uri);
|
|
|
|
|
2018-08-27 07:04:46 +08:00
|
|
|
/* Initialize misc info from rc file */
|
|
|
|
stat = ocget_rcproperties(state);
|
|
|
|
|
|
|
|
/* Apply curl properties for this link;
|
|
|
|
assumes state has been initialized */
|
2014-12-25 01:22:47 +08:00
|
|
|
stat = ocset_curlproperties(state);
|
|
|
|
if(stat != OC_NOERR) goto fail;
|
|
|
|
|
|
|
|
/* Set the one-time curl flags */
|
|
|
|
if((stat=ocset_flags_perlink(state))!= OC_NOERR) goto fail;
|
|
|
|
#if 1 /* temporarily make per-link */
|
|
|
|
if((stat=ocset_flags_perfetch(state))!= OC_NOERR) goto fail;
|
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2017-09-03 08:09:36 +08:00
|
|
|
oc_curl_protocols(state);
|
2012-08-01 04:34:13 +08:00
|
|
|
if(statep) *statep = state;
|
2015-01-21 01:53:42 +08:00
|
|
|
else {
|
|
|
|
if(state != NULL) ocfree(state);
|
|
|
|
}
|
2015-01-13 03:09:34 +08:00
|
|
|
return OCTHROW(stat);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
fail:
|
2017-03-09 08:01:10 +08:00
|
|
|
ncurifree(tmpurl);
|
2012-08-01 04:34:13 +08:00
|
|
|
if(state != NULL) ocfree(state);
|
|
|
|
if(curl != NULL) occurlclose(curl);
|
|
|
|
return OCTHROW(stat);
|
|
|
|
}
|
|
|
|
|
|
|
|
OCerror
|
|
|
|
ocfetch(OCstate* state, const char* constraint, OCdxd kind, OCflags flags,
|
|
|
|
OCnode** rootp)
|
|
|
|
{
|
|
|
|
OCtree* tree = NULL;
|
|
|
|
OCnode* root = NULL;
|
|
|
|
OCerror stat = OC_NOERR;
|
2015-01-13 03:09:34 +08:00
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
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);
|
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
/* Set per-fetch curl properties */
|
|
|
|
#if 0 /* temporarily make per-link */
|
|
|
|
if((stat=ocset_flags_perfetch(state))!= OC_NOERR) goto fail;
|
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2017-03-09 08:01:10 +08:00
|
|
|
ncbytesclear(state->packet);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
switch (kind) {
|
|
|
|
case OCDAS:
|
|
|
|
stat = readDAS(state,tree);
|
|
|
|
if(stat == OC_NOERR) {
|
2017-03-09 08:01:10 +08:00
|
|
|
tree->text = ncbytesdup(state->packet);
|
2012-08-01 04:34:13 +08:00
|
|
|
if(tree->text == NULL) stat = OC_EDAS;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case OCDDS:
|
|
|
|
stat = readDDS(state,tree);
|
|
|
|
if(stat == OC_NOERR) {
|
2017-03-09 08:01:10 +08:00
|
|
|
tree->text = ncbytesdup(state->packet);
|
2012-08-01 04:34:13 +08:00
|
|
|
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);
|
2012-08-03 02:43:21 +08:00
|
|
|
if(stat) {OCTHROWCHK(stat); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
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 { /*inmemory*/
|
|
|
|
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;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}/*switch*/
|
2014-03-12 01:58:22 +08:00
|
|
|
/* Obtain any http code */
|
|
|
|
state->error.httpcode = ocfetchhttpcode(state->curl);
|
2012-08-01 04:34:13 +08:00
|
|
|
if(stat != OC_NOERR) {
|
|
|
|
if(state->error.httpcode >= 400) {
|
2017-09-03 08:09:36 +08:00
|
|
|
nclog(NCLOGWARN,"oc_open: Could not read url (%s); http error = %l",
|
|
|
|
state->uri,state->error.httpcode);
|
2012-08-01 04:34:13 +08:00
|
|
|
} else {
|
2017-03-09 08:01:10 +08:00
|
|
|
nclog(NCLOGWARN,"oc_open: Could not read url");
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
2012-08-03 02:43:21 +08:00
|
|
|
goto fail;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2017-03-09 08:01:10 +08:00
|
|
|
nclog(NCLOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
|
2015-01-13 03:09:34 +08:00
|
|
|
state->error.code,
|
2012-08-01 04:34:13 +08:00
|
|
|
(state->error.message?state->error.message:""));
|
|
|
|
}
|
2012-08-03 02:43:21 +08:00
|
|
|
if(stat) {OCTHROWCHK(stat); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
root = tree->root;
|
|
|
|
/* make sure */
|
|
|
|
tree->root = root;
|
|
|
|
root->tree = tree;
|
|
|
|
|
|
|
|
/* Verify the parse */
|
|
|
|
switch (kind) {
|
|
|
|
case OCDAS:
|
|
|
|
if(root->octype != OC_Attributeset)
|
2012-08-03 02:43:21 +08:00
|
|
|
{OCTHROWCHK(stat=OC_EDAS); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
break;
|
|
|
|
case OCDDS:
|
|
|
|
if(root->octype != OC_Dataset)
|
2012-08-03 02:43:21 +08:00
|
|
|
{OCTHROWCHK(stat=OC_EDDS); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
break;
|
|
|
|
case OCDATADDS:
|
|
|
|
if(root->octype != OC_Dataset)
|
2012-08-03 02:43:21 +08:00
|
|
|
{OCTHROWCHK(stat=OC_EDATADDS); goto fail;}
|
2012-08-01 04:34:13 +08:00
|
|
|
/* Modify the tree kind */
|
|
|
|
tree->dxdclass = OCDATADDS;
|
|
|
|
break;
|
|
|
|
default: return OC_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(kind != OCDAS) {
|
|
|
|
/* 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 {
|
2012-08-22 00:46:10 +08:00
|
|
|
#ifdef OCDEBUG
|
|
|
|
fprintf(stderr,"ocfetch.datadds.memory: datasize=%lu bod=%lu\n",
|
|
|
|
(unsigned long)tree->data.datasize,(unsigned long)tree->data.bod);
|
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
/* 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);
|
2013-02-08 06:14:57 +08:00
|
|
|
/* Do a quick check to see if server returned an ERROR {}
|
|
|
|
at the beginning of the data
|
|
|
|
*/
|
|
|
|
if(dataError(tree->data.xdrs,state)) {
|
|
|
|
stat = OC_EDATADDS;
|
2017-03-09 08:01:10 +08:00
|
|
|
nclog(NCLOGERR,"oc_open: server error retrieving url: code=%s message=\"%s\"",
|
2015-01-13 03:09:34 +08:00
|
|
|
state->error.code,
|
2013-02-08 06:14:57 +08:00
|
|
|
(state->error.message?state->error.message:""));
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2012-08-01 04:34:13 +08:00
|
|
|
/* Compile the data into a more accessible format */
|
|
|
|
stat = occompile(state,tree->root);
|
|
|
|
if(stat != OC_NOERR)
|
2012-08-03 02:43:21 +08:00
|
|
|
goto fail;
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Put root into the state->trees list */
|
2017-03-09 08:01:10 +08:00
|
|
|
nclistpush(state->trees,(void*)root);
|
2012-08-01 04:34:13 +08:00
|
|
|
|
|
|
|
if(rootp) *rootp = root;
|
|
|
|
return stat;
|
|
|
|
|
|
|
|
fail:
|
2012-08-03 02:43:21 +08:00
|
|
|
if(root != NULL)
|
|
|
|
ocroot_free(root);
|
|
|
|
else if(tree != NULL)
|
|
|
|
octree_free(tree);
|
2012-08-01 04:34:13 +08:00
|
|
|
return OCTHROW(stat);
|
|
|
|
}
|
|
|
|
|
|
|
|
static OCerror
|
|
|
|
createtempfile(OCstate* state, OCtree* tree)
|
|
|
|
{
|
2013-11-15 06:13:20 +08:00
|
|
|
int stat = OC_NOERR;
|
2014-12-25 01:22:47 +08:00
|
|
|
char* path = NULL;
|
2017-09-03 08:09:36 +08:00
|
|
|
char* tmppath = NULL;
|
2014-12-25 01:22:47 +08:00
|
|
|
int len;
|
|
|
|
|
2015-01-13 03:09:34 +08:00
|
|
|
len =
|
2017-08-31 07:44:57 +08:00
|
|
|
strlen(ncrc_globalstate.tempdir)
|
2014-12-25 01:22:47 +08:00
|
|
|
+ 1 /* '/' */
|
|
|
|
+ strlen(DATADDSFILE);
|
|
|
|
path = (char*)malloc(len+1);
|
|
|
|
if(path == NULL) return OC_ENOMEM;
|
2017-08-31 07:44:57 +08:00
|
|
|
occopycat(path,len,3,ncrc_globalstate.tempdir,"/",DATADDSFILE);
|
2017-09-03 08:09:36 +08:00
|
|
|
tmppath = NC_mktmp(path);
|
2014-12-25 01:22:47 +08:00
|
|
|
free(path);
|
2013-11-15 06:13:20 +08:00
|
|
|
if(stat != OC_NOERR) goto fail;
|
2012-08-01 04:34:13 +08:00
|
|
|
#ifdef OCDEBUG
|
2017-09-03 08:09:36 +08:00
|
|
|
nclog(NCLOGNOTE,"oc_open: creating tmp file: %s",tmppath);
|
2012-08-01 04:34:13 +08:00
|
|
|
#endif
|
2017-09-03 08:09:36 +08:00
|
|
|
tree->data.filename = tmppath; /* remember our tmp file name */
|
|
|
|
tmppath = NULL;
|
2017-07-14 00:40:07 +08:00
|
|
|
tree->data.file = NCfopen(tree->data.filename,"w+");
|
2012-08-01 04:34:13 +08:00
|
|
|
if(tree->data.file == NULL) return OC_EOPEN;
|
2017-07-06 00:03:48 +08:00
|
|
|
/* make the temp file so it will automatically be reclaimed on close */
|
|
|
|
if(ocdebug == 0)
|
|
|
|
ocremovefile(tree->data.filename);
|
2013-11-15 06:13:20 +08:00
|
|
|
return stat;
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2013-11-15 06:13:20 +08:00
|
|
|
fail:
|
2017-09-03 08:09:36 +08:00
|
|
|
if(tmppath != NULL) {
|
|
|
|
nclog(NCLOGERR,"oc_open: attempt to create tmp file failed: %s",tmppath);
|
|
|
|
free(tmppath);
|
2014-03-29 07:11:26 +08:00
|
|
|
} else {
|
2017-03-09 08:01:10 +08:00
|
|
|
nclog(NCLOGERR,"oc_open: attempt to create tmp file failed: NULL");
|
2014-03-29 07:11:26 +08:00
|
|
|
}
|
2014-11-22 07:20:44 +08:00
|
|
|
return OCTHROW(stat);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
2017-03-09 08:01:10 +08:00
|
|
|
for(i=0;i<nclistlength(state->trees);i++) {
|
|
|
|
OCnode* root = (OCnode*)nclistpop(state->trees);
|
2012-08-01 04:34:13 +08:00
|
|
|
ocroot_free(root);
|
|
|
|
}
|
2017-03-09 08:01:10 +08:00
|
|
|
nclistfree(state->trees);
|
|
|
|
ncurifree(state->uri);
|
|
|
|
ncbytesfree(state->packet);
|
2012-08-01 04:34:13 +08:00
|
|
|
ocfree(state->error.code);
|
|
|
|
ocfree(state->error.message);
|
|
|
|
if(state->curl != NULL) occurlclose(state->curl);
|
2017-09-01 04:19:56 +08:00
|
|
|
NC_authclear(&state->auth);
|
2012-08-01 04:34:13 +08:00
|
|
|
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 = ocfindbod(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);
|
2017-03-09 08:01:10 +08:00
|
|
|
memcpy((void*)tree->text,(void*)ncbytescontents(state->packet),ddslen);
|
2012-08-01 04:34:13 +08:00
|
|
|
tree->text[ddslen] = '\0';
|
|
|
|
} else
|
|
|
|
tree->text = NULL;
|
|
|
|
/* Extract the inmemory contents */
|
2017-03-09 08:01:10 +08:00
|
|
|
tree->data.memory = ncbytesextract(state->packet);
|
2012-08-01 04:34:13 +08:00
|
|
|
#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)*/
|
2017-03-09 08:01:10 +08:00
|
|
|
ncbytesclear(state->packet);
|
2012-08-01 04:34:13 +08:00
|
|
|
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;*/
|
2017-03-09 08:01:10 +08:00
|
|
|
ncbytesappendn(state->packet,chunk,count);
|
2012-08-01 04:34:13 +08:00
|
|
|
bodfound = ocfindbod(state->packet,&bod,&ddslen);
|
|
|
|
} while(!bodfound);
|
|
|
|
if(!bodfound) {/* No BOD; pretend */
|
|
|
|
bod = tree->data.bod;
|
|
|
|
ddslen = tree->data.datasize;
|
2012-08-18 04:51:03 +08:00
|
|
|
#ifdef OCDEBUG
|
2012-08-22 00:46:10 +08:00
|
|
|
fprintf(stderr,"missing bod: ddslen=%lu bod=%lu\n",
|
2012-08-20 05:12:35 +08:00
|
|
|
(unsigned long)ddslen,(unsigned long)bod);
|
2012-08-18 04:51:03 +08:00
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
tree->data.bod = bod;
|
|
|
|
tree->data.ddslen = ddslen;
|
|
|
|
/* copy out the dds */
|
|
|
|
if(ddslen > 0) {
|
|
|
|
tree->text = (char*)ocmalloc(ddslen+1);
|
2017-03-09 08:01:10 +08:00
|
|
|
memcpy((void*)tree->text,(void*)ncbytescontents(state->packet),ddslen);
|
2012-08-01 04:34:13 +08:00
|
|
|
tree->text[ddslen] = '\0';
|
|
|
|
} else
|
|
|
|
tree->text = NULL;
|
|
|
|
/* reset the position of the tmp file*/
|
2014-11-22 07:20:44 +08:00
|
|
|
if(fseek(tree->data.file,(long)tree->data.bod,SEEK_SET) < 0
|
|
|
|
|| tree->text == NULL)
|
|
|
|
stat = OC_EDATADDS;
|
2012-08-01 04:34:13 +08:00
|
|
|
return OCTHROW(stat);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
2017-03-09 08:01:10 +08:00
|
|
|
base = ncuribuild(state->uri,NULL,NULL,NCURIENCODE);
|
2012-08-01 04:34:13 +08:00
|
|
|
status = ocfetchlastmodified(state->curl, base, &lastmodified);
|
|
|
|
free(base);
|
|
|
|
if(status == OC_NOERR) {
|
|
|
|
state->datalastmodified = lastmodified;
|
|
|
|
}
|
2014-11-22 07:20:44 +08:00
|
|
|
return OCTHROW(status);
|
2012-08-01 04:34:13 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-08-27 07:04:46 +08:00
|
|
|
Extract state values from .rc file
|
|
|
|
*/
|
|
|
|
static OCerror
|
|
|
|
ocget_rcproperties(OCstate* state)
|
|
|
|
{
|
|
|
|
OCerror ocerr = OC_NOERR;
|
|
|
|
char* option = NULL;
|
|
|
|
#ifdef HAVE_CURLOPT_BUFFERSIZE
|
|
|
|
option = NC_rclookup(OCBUFFERSIZE,state->uri->uri);
|
|
|
|
if(option != NULL && strlen(option) != 0) {
|
|
|
|
long bufsize;
|
|
|
|
if(strcasecmp(option,"max")==0)
|
|
|
|
bufsize = CURL_MAX_READ_SIZE;
|
|
|
|
else if(sscanf(option,"%ld",&bufsize) != 1 || bufsize <= 0)
|
|
|
|
fprintf(stderr,"Illegal %s size\n",OCBUFFERSIZE);
|
|
|
|
state->curlbuffersize = bufsize;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_CURLOPT_KEEPALIVE
|
|
|
|
option = NC_rclookup(OCKEEPALIVE,state->uri->uri);
|
|
|
|
if(option != NULL && strlen(option) != 0) {
|
|
|
|
/* The keepalive value is of the form 0 or n/m,
|
|
|
|
where n is the idle time and m is the interval time;
|
|
|
|
setting either to zero will prevent that field being set. */
|
|
|
|
if(strcasecmp(option,"on")==0) {
|
|
|
|
state->curlkeepalive.active = 1;
|
|
|
|
} else {
|
|
|
|
unsigned long idle=0;
|
|
|
|
unsigned long interval=0;
|
|
|
|
if(sscanf(option,"%lu/%lu",&idle,&interval) != 2)
|
|
|
|
fprintf(stderr,"Illegal KEEPALIVE VALUE: %s\n",option);
|
|
|
|
state->curlkeepalive.idle = idle;
|
|
|
|
state->curlkeepalive.interval = interval;
|
|
|
|
state->curlkeepalive.active = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return ocerr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Set curl properties for link based on fields in the state.
|
2012-08-01 04:34:13 +08:00
|
|
|
*/
|
2014-05-09 04:13:51 +08:00
|
|
|
static OCerror
|
2014-12-25 01:22:47 +08:00
|
|
|
ocset_curlproperties(OCstate* state)
|
2012-08-01 04:34:13 +08:00
|
|
|
{
|
2014-12-25 01:22:47 +08:00
|
|
|
OCerror stat = OC_NOERR;
|
2017-09-01 04:19:56 +08:00
|
|
|
if(state->auth.curlflags.useragent == NULL) {
|
2012-12-19 05:08:23 +08:00
|
|
|
size_t len = strlen(DFALTUSERAGENT) + strlen(VERSION) + 1;
|
2012-12-07 05:17:36 +08:00
|
|
|
char* agent = (char*)malloc(len+1);
|
2013-05-12 03:36:31 +08:00
|
|
|
if(occopycat(agent,len,2,DFALTUSERAGENT,VERSION))
|
2017-09-01 04:19:56 +08:00
|
|
|
state->auth.curlflags.useragent = agent;
|
2013-08-06 04:36:33 +08:00
|
|
|
else
|
2013-11-15 06:13:20 +08:00
|
|
|
free(agent);
|
2012-12-04 11:32:41 +08:00
|
|
|
}
|
2013-11-15 06:13:20 +08:00
|
|
|
|
|
|
|
/* Some servers (e.g. thredds and columbia) appear to require a place
|
|
|
|
to put cookies in order for some security functions to work
|
|
|
|
*/
|
2017-09-01 04:19:56 +08:00
|
|
|
if(state->auth.curlflags.cookiejar != NULL
|
|
|
|
&& strlen(state->auth.curlflags.cookiejar) == 0) {
|
|
|
|
free(state->auth.curlflags.cookiejar);
|
|
|
|
state->auth.curlflags.cookiejar = NULL;
|
2014-12-29 06:54:02 +08:00
|
|
|
}
|
|
|
|
|
2017-09-01 04:19:56 +08:00
|
|
|
if(state->auth.curlflags.cookiejar == NULL) {
|
2013-11-15 06:13:20 +08:00
|
|
|
/* If no cookie file was defined, define a default */
|
2017-09-03 08:09:36 +08:00
|
|
|
int stat = NC_NOERR;
|
2017-07-06 00:03:48 +08:00
|
|
|
char* path = NULL;
|
2017-09-03 08:09:36 +08:00
|
|
|
char* tmppath = NULL;
|
2017-07-06 00:03:48 +08:00
|
|
|
int len;
|
2013-11-15 06:13:20 +08:00
|
|
|
errno = 0;
|
2014-12-25 01:22:47 +08:00
|
|
|
/* Create the unique cookie file name */
|
2017-07-06 00:03:48 +08:00
|
|
|
len =
|
2017-08-31 07:44:57 +08:00
|
|
|
strlen(ncrc_globalstate.tempdir)
|
2017-07-06 00:03:48 +08:00
|
|
|
+ 1 /* '/' */
|
|
|
|
+ strlen("occookies");
|
2017-09-03 08:09:36 +08:00
|
|
|
path = (char*)calloc(1,len+1);
|
2017-07-06 00:03:48 +08:00
|
|
|
if(path == NULL) return OC_ENOMEM;
|
2017-08-31 07:44:57 +08:00
|
|
|
occopycat(path,len,3,ncrc_globalstate.tempdir,"/","occookies");
|
2017-09-03 08:09:36 +08:00
|
|
|
tmppath = NC_mktmp(path);
|
2017-07-06 00:03:48 +08:00
|
|
|
free(path);
|
2017-09-03 08:09:36 +08:00
|
|
|
state->auth.curlflags.cookiejar = tmppath;
|
2017-09-01 04:19:56 +08:00
|
|
|
state->auth.curlflags.cookiejarcreated = 1;
|
2014-12-29 06:54:02 +08:00
|
|
|
if(stat != OC_NOERR && errno != EEXIST) {
|
|
|
|
fprintf(stderr,"Cannot create cookie file\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
2015-03-03 11:26:39 +08:00
|
|
|
errno = 0;
|
2014-12-25 01:22:47 +08:00
|
|
|
}
|
2017-09-01 04:19:56 +08:00
|
|
|
OCASSERT(state->auth.curlflags.cookiejar != NULL);
|
2015-01-13 03:09:34 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
/* Make sure the cookie jar exists and can be read and written */
|
|
|
|
{
|
2015-01-13 03:09:34 +08:00
|
|
|
FILE* f = NULL;
|
2017-09-01 04:19:56 +08:00
|
|
|
char* fname = state->auth.curlflags.cookiejar;
|
2014-12-25 01:22:47 +08:00
|
|
|
/* See if the file exists already */
|
2017-07-14 00:40:07 +08:00
|
|
|
f = NCfopen(fname,"r");
|
2014-12-25 01:22:47 +08:00
|
|
|
if(f == NULL) {
|
|
|
|
/* Ok, create it */
|
2017-07-14 00:40:07 +08:00
|
|
|
f = NCfopen(fname,"w+");
|
2014-12-25 01:22:47 +08:00
|
|
|
if(f == NULL) {
|
|
|
|
fprintf(stderr,"Cookie file cannot be read and written: %s\n",fname);
|
|
|
|
{stat = OC_EPERM; goto fail;}
|
|
|
|
}
|
|
|
|
} else { /* test if file can be written */
|
|
|
|
fclose(f);
|
2017-07-14 00:40:07 +08:00
|
|
|
f = NCfopen(fname,"r+");
|
2014-12-25 01:22:47 +08:00
|
|
|
if(f == NULL) {
|
|
|
|
fprintf(stderr,"Cookie file is cannot be written: %s\n",fname);
|
|
|
|
{stat = OC_EPERM; goto fail;}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(f != NULL) fclose(f);
|
|
|
|
}
|
2013-11-16 02:38:54 +08:00
|
|
|
|
|
|
|
#if 0
|
2014-12-25 01:22:47 +08:00
|
|
|
/* Create a netrc file if specified and required,
|
|
|
|
where required => >1 NETRC triples exist */
|
|
|
|
if(ocrc_netrc_required(state)) {
|
|
|
|
/* WARNING: it appears that a user+pwd was specified specifically, then
|
|
|
|
the netrc file will be completely disabled. */
|
2017-09-01 04:19:56 +08:00
|
|
|
if(state->auth.creds.userpwd != NULL) {
|
2017-03-09 08:01:10 +08:00
|
|
|
nclog(NCLOGWARN,"The rc file specifies both netrc and user+pwd; this will cause curl to ignore the netrc file");
|
2014-12-25 01:22:47 +08:00
|
|
|
}
|
|
|
|
stat = oc_build_netrc(state);
|
2013-11-16 02:38:54 +08:00
|
|
|
}
|
2014-12-25 01:22:47 +08:00
|
|
|
#endif
|
2012-08-01 04:34:13 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
return stat;
|
2012-12-04 11:32:41 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
fail:
|
|
|
|
return OCTHROW(stat);
|
2012-12-04 11:32:41 +08:00
|
|
|
}
|
2013-02-08 06:14:57 +08:00
|
|
|
|
|
|
|
static char* ERROR_TAG = "Error ";
|
|
|
|
|
|
|
|
static int
|
|
|
|
dataError(XXDR* xdrs, OCstate* state)
|
|
|
|
{
|
2013-02-20 07:29:41 +08:00
|
|
|
int depth=0;
|
2013-02-12 07:32:46 +08:00
|
|
|
int errfound = 0;
|
2014-03-09 11:41:30 +08:00
|
|
|
off_t ckp=0,avail=0;
|
|
|
|
int i=0;
|
2013-02-20 07:29:41 +08:00
|
|
|
char* errmsg = NULL;
|
2018-03-16 22:38:40 +08:00
|
|
|
char errortext[16]; /* bigger than |ERROR_TAG|*/
|
2013-02-08 06:14:57 +08:00
|
|
|
avail = xxdr_getavail(xdrs);
|
|
|
|
if(avail < strlen(ERROR_TAG))
|
2013-02-12 07:32:46 +08:00
|
|
|
goto done; /* assume it is ok */
|
2013-02-08 06:14:57 +08:00
|
|
|
ckp = xxdr_getpos(xdrs);
|
|
|
|
/* Read enough characters to test for 'ERROR ' */
|
|
|
|
errortext[0] = '\0';
|
|
|
|
xxdr_getbytes(xdrs,errortext,(off_t)strlen(ERROR_TAG));
|
|
|
|
if(ocstrncmp(errortext,ERROR_TAG,strlen(ERROR_TAG)) != 0)
|
2013-02-12 07:32:46 +08:00
|
|
|
goto done; /* not an immediate error */
|
2013-02-08 06:14:57 +08:00
|
|
|
/* Try to locate the whole error body */
|
|
|
|
xxdr_setpos(xdrs,ckp);
|
|
|
|
for(depth=0,i=0;i<avail;i++) {
|
2014-03-09 11:41:30 +08:00
|
|
|
xxdr_getbytes(xdrs,errortext,(off_t)1);
|
2013-02-08 06:14:57 +08:00
|
|
|
if(errortext[0] == CLBRACE) depth++;
|
|
|
|
else if(errortext[0] == CRBRACE) {
|
|
|
|
depth--;
|
|
|
|
if(depth == 0) {i++; break;}
|
|
|
|
}
|
2015-01-13 03:09:34 +08:00
|
|
|
}
|
2014-03-09 11:41:30 +08:00
|
|
|
errmsg = (char*)malloc((size_t)i+1);
|
2013-02-12 07:32:46 +08:00
|
|
|
if(errmsg == NULL) {errfound = 1; goto done;}
|
2013-02-08 06:14:57 +08:00
|
|
|
xxdr_setpos(xdrs,ckp);
|
2014-03-09 11:41:30 +08:00
|
|
|
xxdr_getbytes(xdrs,errmsg,(off_t)i);
|
2013-02-08 06:14:57 +08:00
|
|
|
errmsg[i] = '\0';
|
|
|
|
state->error.message = errmsg;
|
|
|
|
state->error.code = strdup("?");
|
|
|
|
state->error.httpcode = 404;
|
|
|
|
xxdr_setpos(xdrs,ckp);
|
2013-02-12 07:32:46 +08:00
|
|
|
errfound = 1;
|
|
|
|
done:
|
|
|
|
xxdr_setpos(xdrs,ckp);
|
|
|
|
return errfound;
|
2013-02-08 06:14:57 +08:00
|
|
|
}
|
2014-12-01 11:30:23 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
/**************************************************/
|
2015-01-13 03:09:34 +08:00
|
|
|
/* Curl option functions */
|
2014-12-25 01:22:47 +08:00
|
|
|
/*
|
|
|
|
Note that if we set the option in curlflags,
|
|
|
|
then we need to also invoke the ocset_curlopt
|
|
|
|
to update the curl flags in libcurl.
|
|
|
|
*/
|
|
|
|
|
2014-12-01 11:30:23 +08:00
|
|
|
OCerror
|
2014-12-25 01:22:47 +08:00
|
|
|
ocset_useragent(OCstate* state, const char* agent)
|
2014-12-01 11:30:23 +08:00
|
|
|
{
|
|
|
|
OCerror stat = OC_NOERR;
|
2017-09-01 04:19:56 +08:00
|
|
|
if(state->auth.curlflags.useragent != NULL)
|
|
|
|
free(state->auth.curlflags.useragent);
|
|
|
|
state->auth.curlflags.useragent = strdup(agent);
|
|
|
|
if(state->auth.curlflags.useragent == NULL)
|
2014-12-25 01:22:47 +08:00
|
|
|
return OCTHROW(OC_ENOMEM);
|
|
|
|
stat = ocset_curlflag(state,CURLOPT_USERAGENT);
|
|
|
|
return stat;
|
|
|
|
}
|
2014-12-01 11:30:23 +08:00
|
|
|
|
2014-12-25 01:22:47 +08:00
|
|
|
OCerror
|
|
|
|
ocset_netrc(OCstate* state, const char* path)
|
|
|
|
{
|
|
|
|
OCerror stat = OC_NOERR;
|
2017-09-01 04:19:56 +08:00
|
|
|
if(state->auth.curlflags.netrc != NULL)
|
|
|
|
free(state->auth.curlflags.netrc);
|
|
|
|
state->auth.curlflags.netrc = strdup(path);
|
|
|
|
if(state->auth.curlflags.netrc == NULL)
|
2014-12-25 01:22:47 +08:00
|
|
|
return OCTHROW(OC_ENOMEM);
|
|
|
|
stat = ocset_curlflag(state,CURLOPT_NETRC);
|
|
|
|
return stat;
|
2014-12-01 11:30:23 +08:00
|
|
|
}
|
2017-07-06 00:03:48 +08:00
|
|
|
|
|
|
|
static void
|
|
|
|
ocremovefile(const char* path)
|
|
|
|
{
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
DeleteFile(path);
|
|
|
|
#else
|
|
|
|
remove(path);
|
|
|
|
#endif
|
|
|
|
}
|
2017-09-03 08:09:36 +08:00
|
|
|
|