netcdf-c/oc2/occurlfunctions.c
dmh 06a11e51df 1. Allow for the user specified rc file via the env variable
NCRCFILE.  Note that the value of this environment
   variable should be the absolute path of the rc file, not
   the path to its containing directory.
2015-03-03 20:17:27 -07:00

449 lines
12 KiB
C

/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
See the COPYRIGHT file for more information. */
#include "config.h"
#include "ocinternal.h"
#include "ocdebug.h"
#include "occurlfunctions.h"
#define OC_MAX_REDIRECTS 20L
/* Mnemonic */
#define OPTARG void*
/* Condition on libcurl version */
/* Set up an alias as needed */
#ifndef HAVE_CURLOPT_KEYPASSWD
#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
#endif
#define NETRCFILETAG "HTTP.NETRC"
#define CHECK(state,flag,value) {if(check(state,flag,(void*)value) != OC_NOERR) {goto done;}}
/* forward */
static OCerror oc_set_curl_options(OCstate* state);
static void* cvt(char* value, enum OCCURLFLAGTYPE type);
static OCerror
check(OCstate* state, int flag, void* value)
{
OCerror stat = ocset_curlopt(state,flag,value);
#ifdef OCDEBUG
long l = (long)value;
const char* name = occurlflagbyflag(flag)->name;
if(l <= 1000) {
OCDBG2("%s=%ld",name,l);
} else {
char show[65];
char* s = (char*)value;
strncpy(show,s,64);
show[64] = '\0';
OCDBG2("%s=%s",name,show);
}
#endif
return stat;
}
#if 0
static void
showopt(int flag, void* value)
{
struct OCCURLFLAG* f = occurlflagbyflag(flag);
if(f == NULL) {
OCDBG1("Unsupported flag: %d",flag);
} else switch (f->type) {
case CF_LONG:
OCDBG2("%s=%ld",f->name,(long)value);
break;
case CF_STRING: {
char show[65];
char* s = (char*)value;
strncpy(show,s,64);
show[64] = '\0';
OCDBG2("%s=%s",f->name,show);
} break;
case CF_UNKNOWN: case CF_OTHER:
OCDBG1("%s=<something>",f->name);
break;
}
}
#endif
/*
Set a specific curl flag; primary wrapper for curl_easy_setopt
*/
OCerror
ocset_curlopt(OCstate* state, int flag, void* value)
{
OCerror stat = OC_NOERR;
CURLcode cstat = CURLE_OK;
cstat = OCCURLERR(state,curl_easy_setopt(state->curl,flag,value));
if(cstat != CURLE_OK)
stat = OC_ECURL;
return stat;
}
/*
Update a specific flag from state
*/
OCerror
ocset_curlflag(OCstate* state, int flag)
{
OCerror stat = OC_NOERR;
switch (flag) {
case CURLOPT_USERPWD:
if(state->creds.userpwd != NULL) {
CHECK(state, CURLOPT_USERPWD, state->creds.userpwd);
CHECK(state, CURLOPT_HTTPAUTH, (OPTARG)CURLAUTH_ANY);
}
break;
case CURLOPT_COOKIEJAR: case CURLOPT_COOKIEFILE:
if(state->curlflags.cookiejar) {
/* Assume we will read and write cookies to same place */
CHECK(state, CURLOPT_COOKIEJAR, state->curlflags.cookiejar);
CHECK(state, CURLOPT_COOKIEFILE, state->curlflags.cookiejar);
}
break;
case CURLOPT_NETRC: case CURLOPT_NETRC_FILE:
if(state->curlflags.netrc) {
CHECK(state, CURLOPT_NETRC, (OPTARG)CURL_NETRC_REQUIRED);
CHECK(state, CURLOPT_NETRC_FILE, state->curlflags.netrc);
}
break;
case CURLOPT_VERBOSE:
if(state->curlflags.verbose)
CHECK(state, CURLOPT_VERBOSE, (OPTARG)1L);
break;
case CURLOPT_TIMEOUT:
if(state->curlflags.timeout)
CHECK(state, CURLOPT_TIMEOUT, (OPTARG)((long)state->curlflags.timeout));
break;
case CURLOPT_USERAGENT:
if(state->curlflags.useragent)
CHECK(state, CURLOPT_USERAGENT, state->curlflags.useragent);
break;
case CURLOPT_FOLLOWLOCATION:
CHECK(state, CURLOPT_FOLLOWLOCATION, (OPTARG)1L);
break;
case CURLOPT_MAXREDIRS:
CHECK(state, CURLOPT_MAXREDIRS, (OPTARG)OC_MAX_REDIRECTS);
break;
case CURLOPT_ERRORBUFFER:
CHECK(state, CURLOPT_ERRORBUFFER, state->error.curlerrorbuf);
break;
case CURLOPT_ENCODING:
#ifdef CURLOPT_ENCODING
if(state->curlflags.compress) {
CHECK(state, CURLOPT_ENCODING,"deflate, gzip");
}
#endif
break;
case CURLOPT_PROXY:
if(state->proxy.host != NULL) {
CHECK(state, CURLOPT_PROXY, state->proxy.host);
CHECK(state, CURLOPT_PROXYPORT, (OPTARG)(long)state->proxy.port);
if(state->proxy.userpwd) {
CHECK(state, CURLOPT_PROXYUSERPWD, state->proxy.userpwd);
#ifdef CURLOPT_PROXYAUTH
CHECK(state, CURLOPT_PROXYAUTH, (long)CURLAUTH_ANY);
#endif
}
}
break;
case CURLOPT_USE_SSL:
case CURLOPT_SSLCERT: case CURLOPT_SSLKEY:
case CURLOPT_SSL_VERIFYPEER: case CURLOPT_SSL_VERIFYHOST:
{
struct OCSSL* ssl = &state->ssl;
CHECK(state, CURLOPT_SSL_VERIFYPEER, (OPTARG)(ssl->verifypeer?1L:0L));
CHECK(state, CURLOPT_SSL_VERIFYHOST, (OPTARG)(ssl->verifyhost?1L:0L));
if(ssl->certificate)
CHECK(state, CURLOPT_SSLCERT, ssl->certificate);
if(ssl->key)
CHECK(state, CURLOPT_SSLKEY, ssl->key);
if(ssl->keypasswd)
/* libcurl prior to 7.16.4 used 'CURLOPT_SSLKEYPASSWD' */
CHECK(state, CURLOPT_KEYPASSWD, ssl->keypasswd);
if(ssl->cainfo)
CHECK(state, CURLOPT_CAINFO, ssl->cainfo);
if(ssl->capath)
CHECK(state, CURLOPT_CAPATH, ssl->capath);
}
break;
default: {
struct OCCURLFLAG* f = occurlflagbyflag(flag);
if(f != NULL)
oclog(OCLOGWARN,"Attempt to update unexpected curl flag: %s",
f->name);
} break;
}
done:
return stat;
}
/* Set various general curl flags per fetch */
OCerror
ocset_flags_perfetch(OCstate* state)
{
OCerror stat = OC_NOERR;
/* currently none */
return stat;
}
/* Set various general curl flags per link */
OCerror
ocset_flags_perlink(OCstate* state)
{
OCerror stat = OC_NOERR;
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_ENCODING);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_NETRC);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_VERBOSE);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_TIMEOUT);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_USERAGENT);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_COOKIEJAR);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_USERPWD);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_PROXY);
if(stat == OC_NOERR) stat = ocset_curlflag(state,CURLOPT_USE_SSL);
if(stat != OC_NOERR)
return stat;
/* Following are always set */
ocset_curlflag(state, CURLOPT_FOLLOWLOCATION);
ocset_curlflag(state, CURLOPT_MAXREDIRS);
ocset_curlflag(state, CURLOPT_ERRORBUFFER);
/* Set the CURL. options */
stat = oc_set_curl_options(state);
return stat;
}
/**
Directly set any options starting with 'CURL.'
*/
static OCerror
oc_set_curl_options(OCstate* state)
{
OCerror stat = OC_NOERR;
struct OCTriplestore* store = NULL;
struct OCTriple* triple = NULL;
int i;
char* hostport = NULL;
struct OCCURLFLAG* ocflag = NULL;
hostport = occombinehostport(state->uri);
if(hostport == NULL) {
hostport = (char*)malloc(sizeof(char)*1);
*hostport = '\0';
}
store = &ocglobalstate.rc.daprc;
triple = store->triples;
/* Assume that the triple store has been properly sorted */
for(i=0;i<store->ntriples;i++,triple++) {
size_t hostlen = strlen(triple->host);
const char* flagname;
if(ocstrncmp("CURL.",triple->key,5) != 0) continue; /* not a curl flag */
/* do hostport prefix comparison */
if(hostlen > 0) {
int t = ocstrncmp(hostport,triple->host,hostlen);
if(t != 0) continue;
}
flagname = triple->key+5; /* 5 == strlen("CURL."); */
ocflag = occurlflagbyname(flagname);
if(ocflag == NULL) {stat = OC_ECURL; goto done;}
stat = ocset_curlopt(state,ocflag->flag,cvt(triple->value,ocflag->type));
}
done:
if(hostport && strlen(hostport) > 0) free(hostport);
return stat;
}
static void*
cvt(char* value, enum OCCURLFLAGTYPE type)
{
switch (type) {
case CF_LONG: {
/* Try to convert to long value */
const char* p = value;
char* q = NULL;
long longvalue = strtol(p,&q,10);
if(*q != '\0')
return NULL;
return (void*)longvalue;
}
case CF_STRING:
return (void*)value;
case CF_UNKNOWN: case CF_OTHER:
return (void*)value;
}
return NULL;
}
void
oc_curl_debug(OCstate* state)
{
state->curlflags.verbose = 1;
ocset_curlflag(state,CURLOPT_VERBOSE);
ocset_curlflag(state,CURLOPT_ERRORBUFFER);
}
/* Misc. */
int
ocrc_netrc_required(OCstate* state)
{
char* netrcfile = ocrc_lookup(NETRCFILETAG,state->uri->uri);
return (netrcfile != NULL || state->curlflags.netrc != NULL ? 0 : 1);
}
void
oc_curl_printerror(OCstate* state)
{
fprintf(stderr,"curl error details: %s\n",state->curlerror);
}
/* Determine if this version of curl supports
"file://..." &/or "https://..." urls.
*/
void
oc_curl_protocols(struct OCGLOBALSTATE* state)
{
const char* const* proto; /*weird*/
curl_version_info_data* curldata;
curldata = curl_version_info(CURLVERSION_NOW);
for(proto=curldata->protocols;*proto;proto++) {
if(strcmp("file",*proto)==0) {state->curl.proto_file=1;}
if(strcmp("http",*proto)==0) {state->curl.proto_https=1;}
}
if(ocdebug > 0) {
oclog(OCLOGNOTE,"Curl file:// support = %d",state->curl.proto_file);
oclog(OCLOGNOTE,"Curl https:// support = %d",state->curl.proto_https);
}
}
/*
"Inverse" of ocset_curlflag;
Given a flag and value, it updates state.
Update a specific flag from state->curlflags.
*/
OCerror
ocset_curlstate(OCstate* state, int flag, void* value)
{
OCerror stat = OC_NOERR;
switch (flag) {
case CURLOPT_USERPWD:
if(state->creds.userpwd != NULL) free(state->creds.userpwd);
state->creds.userpwd = strdup((char*)value);
break;
case CURLOPT_COOKIEJAR: case CURLOPT_COOKIEFILE:
if(state->curlflags.cookiejar != NULL) free(state->curlflags.cookiejar);
state->curlflags.cookiejar = strdup((char*)value);
break;
case CURLOPT_NETRC: case CURLOPT_NETRC_FILE:
if(state->curlflags.netrc != NULL) free(state->curlflags.netrc);
state->curlflags.netrc = strdup((char*)value);
break;
case CURLOPT_VERBOSE:
state->curlflags.verbose = (long)value;
break;
case CURLOPT_TIMEOUT:
state->curlflags.timeout = (long)value;
break;
case CURLOPT_USERAGENT:
if(state->curlflags.useragent != NULL) free(state->curlflags.useragent);
state->curlflags.useragent = strdup((char*)value);
break;
case CURLOPT_FOLLOWLOCATION:
/* no need to store; will always be set */
break;
case CURLOPT_MAXREDIRS:
/* no need to store; will always be set */
break;
case CURLOPT_ERRORBUFFER:
/* no need to store; will always be set */
break;
case CURLOPT_ENCODING:
/* no need to store; will always be set to fixed value */
break;
case CURLOPT_PROXY:
/* We assume that the value is the proxy url */
if(state->proxy.host != NULL) free(state->proxy.host);
if(state->proxy.userpwd != NULL) free(state->proxy.userpwd);
state->proxy.host = NULL;
state->proxy.userpwd = NULL;
if(!ocparseproxy(state,(char*)value))
{stat = OC_EINVAL; goto done;}
break;
case CURLOPT_SSLCERT:
if(state->ssl.certificate != NULL) free(state->ssl.certificate);
state->ssl.certificate = strdup((char*)value);
break;
case CURLOPT_SSLKEY:
if(state->ssl.key != NULL) free(state->ssl.key);
state->ssl.key = strdup((char*)value);
break;
case CURLOPT_KEYPASSWD:
if(state->ssl.keypasswd!= NULL) free(state->ssl.keypasswd);
state->ssl.keypasswd = strdup((char*)value);
break;
case CURLOPT_SSL_VERIFYPEER:
state->ssl.verifypeer = (long)value;
break;
case CURLOPT_SSL_VERIFYHOST:
state->ssl.verifyhost = (long)value;
break;
case CURLOPT_CAINFO:
if(state->ssl.cainfo != NULL) free(state->ssl.cainfo);
state->ssl.cainfo = strdup((char*)value);
break;
case CURLOPT_CAPATH:
if(state->ssl.capath != NULL) free(state->ssl.capath);
state->ssl.capath = strdup((char*)value);
break;
default: {
struct OCCURLFLAG* f = occurlflagbyflag(flag);
if(f != NULL)
oclog(OCLOGWARN,"Attempt to add unexpected curl flag to state: %s",
f->name);
} break;
}
done:
return stat;
}