netcdf-c/oc2/ocrc.c

559 lines
16 KiB
C

/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
See the COPYRIGHT file for more information. */
#include "config.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ocinternal.h"
#include "ocdebug.h"
#include "oclog.h"
#include "ocrc.h"
#define RTAG ']'
#define LTAG '['
#define TRIMCHARS " \t\r\n"
#define TRIM(x) rctrimright(rctrimleft((x),TRIMCHARS),TRIMCHARS)
#define HTTPPREFIXDEPRECATED "CURL."
#define HTTPPREFIX "HTTP."
static int parseproxy(OCstate* state, char* v);
static int rcreadline(FILE* f, char* more, int morelen);
static char* rctrimright(char* more, char* trimchars);
static char* rctrimleft(char* more, char* trimchars);
static void ocdodsrcdump(char* msg, struct OCTriple*, int ntriples);
static char* curllookup(char* suffix,char* url);
/* The Username and password are in the URL if the URL is of the form:
* http://<name>:<passwd>@<host>/....
*/
int
occredentials_in_url(const char *url)
{
char *pos = strstr(url, "http://");
if (!pos)
return 0;
pos += 7;
if (strchr(pos, '@') && strchr(pos, ':'))
return 1;
return 0;
}
int
ocextract_credentials(const char *url, char **name, char **pw, char **result_url)
{
char *pos;
char *end;
char *middle;
int up_len = 0;
int mid_len = 0;
int midpas_len = 0;
int url_len = 0;
if (strchr(url, '@')) {
pos = strstr(url, "http://");
if (pos)
pos += 7;
middle = strchr(pos, ':');
mid_len = middle - pos;
*name = malloc(sizeof(char) * (mid_len + 1));
strncpy(*name, pos, mid_len);
(*name)[mid_len] = '\0';
if (middle)
middle += 1;
end = strchr(middle, '@');
midpas_len = end - middle;
*pw = malloc(sizeof(char) * (midpas_len + 1));
strncpy(*pw, middle, midpas_len);
(*pw)[midpas_len] = '\0';
up_len = end - pos;
url_len = strlen(url) - up_len;
*result_url = malloc(sizeof(char) * (url_len + 1));
if (!result_url)
return OC_ENOMEM;
strncpy(*result_url, url, pos - url);
strncpy(*result_url + (pos - url), end + 1, url_len - (pos - url));
#if 0
fprintf(stderr, "URL without username and password: %s:%d\n", sURL, url_len );
fprintf(stderr, "URL username and password: %s:%d\n", sUP, up_len);
fprintf(stderr, "URL username: %s:%d\n", sUser, mid_len);
fprintf(stderr, "URL password: %s:%d\n", sPassword, midpas_len);
#endif
(*result_url)[url_len] = '\0';
return OC_NOERR;
}
else {
return OC_EIO;
}
}
static int
rcreadline(FILE* f, char* more, int morelen)
{
int i = 0;
int c = getc(f);
if(c < 0) return 0;
for(;;) {
if(i < morelen) /* ignore excess characters */
more[i++]=c;
c = getc(f);
if(c < 0) break; /* eof */
if(c == '\n') break; /* eol */
}
/* null terminate more */
more[i] = '\0';
return 1;
}
/* Trim specified characters from front/left */
static char*
rctrimleft(char* more, char* trimchars)
{
char* p = more;
int c;
while((c=*p) != '\0') {if(strchr(trimchars,c) != NULL) p++; else break;}
return p;
}
/* Trim specified characters from end/right */
static char*
rctrimright(char* more, char* trimchars)
{
int len = strlen(more);
char* p = more + (len - 1);
while(p != more) {if(strchr(trimchars,*p) != NULL) p--; else break;}
/* null terminate */
p[1] = '\0';
return more;
}
static int
parseproxy(OCstate* state, char* v)
{
char *host_pos = NULL;
char *port_pos = NULL;
if(strlen(v) == 0) return OC_NOERR; /* nothing there*/
if (occredentials_in_url(v)) {
char *result_url = NULL;
ocextract_credentials(v, &state->creds.username,
&state->creds.password,
&result_url);
v = result_url;
}
/* allocating a bit more than likely needed ... */
host_pos = strstr(v, "http://");
if (host_pos)
host_pos += strlen("http://");
else
host_pos = v;
port_pos = strchr(host_pos, ':');
if (port_pos) {
int host_len;
char *port_sep = port_pos;
port_pos++;
*port_sep = '\0';
host_len = strlen(host_pos);
state->proxy.host = malloc(sizeof(char) * host_len + 1);
if (!state->proxy.host)
return OC_ENOMEM;
strncpy(state->proxy.host, host_pos, host_len);
state->proxy.host[host_len] = '\0';
state->proxy.port = atoi(port_pos);
} else {
int host_len = strlen(host_pos);
state->proxy.host = malloc(sizeof(char) * host_len + 1);
if (!state->proxy.host)
return OC_ENOMEM;
strncpy(state->proxy.host, host_pos, host_len);
state->proxy.host[host_len] = '\0';
state->proxy.port = 80;
}
#if 0
state->proxy.host[v_len] = '\0';
state->proxy.port = atoi(v);
s_len = strlen(v);
state->proxy.user = malloc(sizeof(char) * s_len + 1);
if (!state->proxy.user)
return OC_ENOMEM;
strncpy(state->proxy.user, v, s_len);
state->proxy.user[s_len] = '\0';
p_len = strlen(v);
state->proxy.password = malloc(sizeof(char) * p_len + 1);
if (!state->proxy.password)
return OC_ENOMEM;
strncpy(state->proxy.password, v, p_len);
state->proxy.password[p_len] = '\0';
#endif /*0*/
if (ocdebug > 1) {
oclog(OCLOGNOTE,"host name: %s", state->proxy.host);
oclog(OCLOGNOTE,"user name: %s", state->creds.username);
#ifdef INSECURE
oclog(OCLOGNOTE,"password: %s", state->creds.password);
#endif
oclog(OCLOGNOTE,"port number: %d", state->proxy.port);
}
if(v) free(v);
return OC_NOERR;
}
/* insertion sort the triplestore based on url */
static void
sorttriplestore(void)
{
int i, nsorted;
struct OCTriple* sorted = NULL;
struct OCTriplestore* ocdodsrc = ocglobalstate.ocdodsrc;
if(ocdodsrc == NULL) return; /* nothing to sort */
if(ocdodsrc->ntriples <= 1) return; /* nothing to sort */
if(ocdebug > 2)
ocdodsrcdump("initial:",ocdodsrc->triples,ocdodsrc->ntriples);
sorted = (struct OCTriple*)malloc(sizeof(struct OCTriple)*ocdodsrc->ntriples);
if(sorted == NULL) {
oclog(OCLOGERR,"sorttriplestore: out of memory");
return;
}
nsorted = 0;
while(nsorted < ocdodsrc->ntriples) {
int largest;
/* locate first non killed entry */
for(largest=0;largest<ocdodsrc->ntriples;largest++) {
if(ocdodsrc->triples[largest].key[0] != '\0') break;
}
OCASSERT(ocdodsrc->triples[largest].key[0] != '\0');
for(i=0;i<ocdodsrc->ntriples;i++) {
if(ocdodsrc->triples[i].key[0] != '\0') { /* avoid empty slots */
int lexorder = strcmp(ocdodsrc->triples[i].url,ocdodsrc->triples[largest].url);
int leni = strlen(ocdodsrc->triples[i].url);
int lenlarge = strlen(ocdodsrc->triples[largest].url);
/* this defines the ordering */
if(leni == 0 && lenlarge == 0) continue; /* if no urls, then leave in order */
if(leni != 0 && lenlarge == 0) largest = i;
else if(lexorder > 0) largest = i;
}
}
/* Move the largest entry */
OCASSERT(ocdodsrc->triples[largest].key[0] != 0);
sorted[nsorted] = ocdodsrc->triples[largest];
ocdodsrc->triples[largest].key[0] = '\0'; /* kill entry */
nsorted++;
if(ocdebug > 2)
ocdodsrcdump("pass:",sorted,nsorted);
}
memcpy((void*)ocdodsrc->triples,(void*)sorted,sizeof(struct OCTriple)*nsorted);
free(sorted);
if(ocdebug > 0)
ocdodsrcdump("final .dodsrc order:",ocdodsrc->triples,ocdodsrc->ntriples);
}
/* Create a triple store from a file */
int
ocdodsrc_read(char* basename, char* path)
{
char line0[MAXRCLINESIZE];
FILE *in_file = NULL;
int linecount = 0;
struct OCTriplestore* ocdodsrc = ocglobalstate.ocdodsrc;
if(ocdodsrc == NULL) {
ocdodsrc = (struct OCTriplestore*)malloc(sizeof(struct OCTriplestore));
if(ocdodsrc == NULL) {
oclog(OCLOGERR,"ocdodsrc_read: out of memory");
return 0;
}
ocglobalstate.ocdodsrc = ocdodsrc;
}
ocdodsrc->ntriples = 0;
in_file = fopen(path, "r"); /* Open the file to read it */
if (in_file == NULL) {
oclog(OCLOGERR, "Could not open configuration file: %s",basename);
return OC_EPERM;
}
for(;;) {
char *line,*key,*value;
if(!rcreadline(in_file,line0,sizeof(line0))) break;
linecount++;
if(linecount >= MAXRCLINES) {
oclog(OCLOGERR, ".dodsrc has too many lines");
return 0;
}
line = line0;
/* check for comment */
if (line[0] == '#') continue;
/* trim leading blanks */
line = rctrimleft(line,TRIMCHARS);
if(strlen(line) >= MAXRCLINESIZE) {
oclog(OCLOGERR, "%s line too long: %s",basename,line0);
return 0;
}
/* parse the line */
ocdodsrc->triples[ocdodsrc->ntriples].url[0] = '\0'; /*assume no url*/
if(line[0] == LTAG) {
char* url = ++line;
char* rtag = strchr(line,RTAG);
if(rtag == NULL) {
oclog(OCLOGERR, "Malformed [url] in %s entry: %s",basename,line);
continue;
}
line = rtag + 1;
*rtag = '\0';
/* trim again */
line = rctrimleft(line,TRIMCHARS);
/* save the url */
strncpy(ocdodsrc->triples[ocdodsrc->ntriples].url,TRIM(url),strlen(url));
}
if(strlen(line)==0) continue; /* empty line */
/* split off key and value */
key=line;
value = strchr(line, '=');
if(value == NULL) {
/* add fake '=1' */
if(strlen(line) + strlen("=1") >= MAXRCLINESIZE) {
oclog(OCLOGERR, "%s entry too long: %s",basename,line);
continue;
}
strcat(line,"=1");
value = strchr(line,'=');
}
*value = '\0';
value++;
strncpy(ocdodsrc->triples[ocdodsrc->ntriples].key,TRIM(key),strlen(key));
strncpy(ocdodsrc->triples[ocdodsrc->ntriples].value,TRIM(value),strlen(value));
ocdodsrc->ntriples++;
}
fclose(in_file);
sorttriplestore();
return 1;
}
int
ocdodsrc_process(OCstate* state)
{
int stat = 0;
char* value;
char* url = ocuribuild(state->uri,NULL,NULL,OCURIENCODE);
struct OCTriplestore* ocdodsrc = ocglobalstate.ocdodsrc;
if(ocdodsrc == NULL) goto done;
value = curllookup("DEFLATE",url);
if(value != NULL) {
if(atoi(value)) state->curlflags.compress = 1;
if(ocdebug > 0)
oclog(OCLOGNOTE,"Compression: %ld", state->curlflags.compress);
}
if((value = curllookup("VERBOSE",url)) != NULL) {
if(atoi(value)) state->curlflags.verbose = 1;
if(ocdebug > 0)
oclog(OCLOGNOTE,"curl.verbose: %ld", state->curlflags.verbose);
}
if((value = curllookup("TIMEOUT",url)) != NULL) {
if(atoi(value)) state->curlflags.timeout = atoi(value);
if(ocdebug > 0)
oclog(OCLOGNOTE,"curl.timeout: %ld", state->curlflags.timeout);
}
if((value = curllookup("USERAGENT",url)) != NULL) {
if(atoi(value)) state->curlflags.useragent = strdup(TRIM(value));
if(!state->curlflags.useragent) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"USERAGENT: %s", state->curlflags.useragent);
}
if((value = curllookup("COOKIEFILE",url)) != NULL) {
state->curlflags.cookiefile = strdup(TRIM(value));
if(!state->curlflags.cookiefile) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"COOKIEFILE: %s", state->curlflags.cookiefile);
}
if((value = curllookup("COOKIEJAR",url))
|| (value = curllookup("COOKIE_JAR",url))) {
state->curlflags.cookiejar = strdup(TRIM(value));
if(!state->curlflags.cookiejar) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"COOKIEJAR: %s", state->curlflags.cookiejar);
}
/* Some servers (e.g. thredds) appear to require a place
to put cookies in order for some security functions to work
*/
if(state->curlflags.cookiejar == NULL
&& state->curlflags.cookiefile == NULL) {
state->curlflags.cookiefile = strdup("");
}
if((value = curllookup("PROXY_SERVER",url)) != NULL) {
stat = parseproxy(state,TRIM(value));
if(stat != OC_NOERR) goto done;
}
if((value = curllookup("SSL.VALIDATE",url)) != NULL) {
if(atoi(value)) state->ssl.validate = 1;
if(ocdebug > 0)
oclog(OCLOGNOTE,"CURL.SSL.VALIDATE: %ld", state->ssl.validate);
}
if((value = curllookup("SSL.CERTIFICATE",url)) != NULL) {
state->ssl.certificate = strdup(TRIM(value));
if(!state->ssl.certificate) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"CREDENTIALS.SSL.CERTIFICATE: %s", state->ssl.certificate);
}
if((value = curllookup("SSL.KEY",url)) != NULL) {
state->ssl.key = strdup(TRIM(value));
if(!state->ssl.key) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"CREDENTIALS.SSL.KEY: %s", state->ssl.key);
}
if((value = curllookup("SSL.KEYPASSWORD",url)) != NULL) {
state->ssl.keypasswd = strdup(TRIM(value));
if(!state->ssl.keypasswd) {stat = OC_ENOMEM; goto done;}
#ifdef INSECURE
if(ocdebug > 0)
oclog(OCLOGNOTE,"CREDENTIALS.SSL.KEYPASSWORD: %s", state->ssl.keypasswd);
#endif
}
if((value = curllookup("SSL.CAINFO",url)) != NULL) {
state->ssl.cainfo = strdup(TRIM(value));
if(!state->ssl.cainfo) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"SSL.CAINFO: %s", state->ssl.cainfo);
}
if((value = curllookup("SSL.CAPATH",url)) != NULL) {
state->ssl.capath = strdup(TRIM(value));
if(!state->ssl.capath) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"SSL.CAPATH: %s", state->ssl.capath);
}
if((value = curllookup("SSL.VERIFYPEER",url)) != NULL) {
char* s = strdup(TRIM(value));
int tf = 0;
if(s == NULL || strcmp(s,"0")==0 || strcasecmp(s,"false")==0)
tf = 0;
else if(strcmp(s,"1")==0 || strcasecmp(s,"true")==0)
tf = 1;
else
tf = 1; /* default if not null */
state->ssl.verifypeer = tf;
if(ocdebug > 0)
oclog(OCLOGNOTE,"SSL.VERIFYPEER: %d", state->ssl.verifypeer);
}
if((value = curllookup("CREDENTIALS.USER",url)) != NULL) {
state->creds.username = strdup(TRIM(value));
if(!state->creds.username) {stat = OC_ENOMEM; goto done;}
if(ocdebug > 0)
oclog(OCLOGNOTE,"CREDENTIALS.USER: %s", state->creds.username);
}
if((value = curllookup("CREDENTIALS.PASSWORD",url)) != NULL) {
state->creds.password = strdup(TRIM(value));
if(!state->creds.password) {stat = OC_ENOMEM; goto done;}
}
/* else ignore */
done:
if(url != NULL) free(url);
return stat;
}
char*
ocdodsrc_lookup(char* key, char* url)
{
int i,found;
struct OCTriplestore* ocdodsrc = ocglobalstate.ocdodsrc;
struct OCTriple* triple = ocdodsrc->triples;
if(key == NULL || ocdodsrc == NULL) return NULL;
if(url == NULL) url = "";
/* Assume that the triple store has been properly sorted */
for(found=0,i=0;i<ocdodsrc->ntriples;i++,triple++) {
int triplelen = strlen(triple->url);
int t;
if(strcmp(key,triple->key) != 0) continue; /* keys do not match */
/* If the triple entry has no url, then use it (because we have checked all other cases)*/
if(triplelen == 0) {found=1;break;}
/* do url prefix comparison */
t = ocstrncmp(url,triple->url,triplelen);
if(t == 0) {found=1; break;}
}
if(ocdebug > 2)
{
if(found) {
fprintf(stderr,"lookup %s: [%s]%s = %s\n",url,triple->url,triple->key,triple->value);
}
}
return (found ? triple->value : NULL);
}
static void
ocdodsrcdump(char* msg, struct OCTriple* triples, int ntriples)
{
int i;
struct OCTriplestore* ocdodsrc = ocglobalstate.ocdodsrc;
if(msg != NULL) fprintf(stderr,"%s\n",msg);
if(ocdodsrc == NULL) {
fprintf(stderr,"<EMPTY>\n");
return;
}
if(triples == NULL) triples= ocdodsrc->triples;
if(ntriples < 0 ) ntriples= ocdodsrc->ntriples;
for(i=0;i<ntriples;i++) {
fprintf(stderr,"\t%s\t%s\t%s\n",
(strlen(triples[i].url)==0?"--":triples[i].url),
triples[i].key,
triples[i].value);
}
}
/* Isolate the "CURL." prefix to allow changing to something else */
static char*
curllookup(char* suffix, char* url)
{
char key[2048];
char* value = NULL;
if(!occopycat(key,sizeof(key),2,HTTPPREFIX,suffix))
return NULL;
value = ocdodsrc_lookup(key,url);
if(value == NULL) {
if(!occopycat(key,sizeof(key),2,HTTPPREFIXDEPRECATED,suffix))
return NULL;
value = ocdodsrc_lookup(key,url);
}
return value;
}