1/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
2   See the COPYRIGHT file for more information. */
3
4#include "config.h"
5#include "ocinternal.h"
6#include "ocdebug.h"
7#include "occurlfunctions.h"
8
9#define OC_MAX_REDIRECTS 20L
10
11/* Mnemonic */
12#define OPTARG void*
13
14/* Condition on libcurl version */
15/* Set up an alias as needed */
16#ifndef HAVE_CURLOPT_KEYPASSWD
17#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
18#endif
19
20#define NETRCFILETAG "HTTP.NETRC"
21
22#define CHECK(state,flag,value) {if(check(state,flag,(void*)value) != OC_NOERR) {goto done;}}
23
24/* forward */
25static OCerror oc_set_curl_options(OCstatestate);
26static void* cvt(char* value, enum OCCURLFLAGTYPE type);
27
28static OCerror
29check(OCstatestate, int flag, void* value)
30{
31    OCerror stat = ocset_curlopt(state,flag,value);
32#ifdef OCDEBUG
33    long l = (long)value;
34    const char* name = occurlflagbyflag(flag)->name;
35    if(l <= 1000) {
36 OCDBG2("%s=%ld",name,l);
37    } else {
38 char show[65];
39 char* s = (char*)value;
40 strncpy(show,s,64);
41 show[64] = '\0';
42 OCDBG2("%s=%s",name,show);
43    }
44#endif
45    return stat;
46}
47
48#if 0
49static void
50showopt(int flag, void* value)
51{
52    struct OCCURLFLAGf = occurlflagbyflag(flag);
53    if(f == NULL)  {
54 OCDBG1("Unsupported flag: %d",flag);
55    } else switch (f->type) {
56 case CF_LONG:
57     OCDBG2("%s=%ld",f->name,(long)value);
58     break;
59 case CF_STRING: {
60        char show[65];
61     char* s = (char*)value;
62     strncpy(show,s,64);
63     show[64] = '\0';
64     OCDBG2("%s=%s",f->name,show);
65            } break;
66 case CF_UNKNOWN: case CF_OTHER:
67     OCDBG1("%s=<something>",f->name);
68     break;
69    }
70}
71#endif
72
73/*
74Set a specific curl flag; primary wrapper for curl_easy_setopt
75*/
76OCerror
77ocset_curlopt(OCstatestate, int flag, void* value)
78{
79    OCerror stat = OC_NOERR;
80    CURLcode cstat = CURLE_OK;
81    cstat = OCCURLERR(state,curl_easy_setopt(state->curl,flag,value));
82    if(cstat != CURLE_OK)
83 stat = OC_ECURL;
84    return stat;
85}
86
87/*
88Update a specific flag from state
89*/
90OCerror
91ocset_curlflag(OCstatestate, int flag)
92{
93    OCerror stat = OC_NOERR;
94
95    switch (flag) {
96
97    case CURLOPT_USERPWD:
98        if(state->creds.userpwd != NULL) {
99     CHECK(stateCURLOPT_USERPWDstate->creds.userpwd);
100            CHECK(stateCURLOPT_HTTPAUTH, (OPTARG)CURLAUTH_ANY);
101 }
102 break;
103
104    case CURLOPT_COOKIEJAR: case CURLOPT_COOKIEFILE:
105        if(state->curlflags.cookiejar) {
106     /* Assume we will read and write cookies to same place */
107     CHECK(stateCURLOPT_COOKIEJARstate->curlflags.cookiejar);
108     CHECK(stateCURLOPT_COOKIEFILEstate->curlflags.cookiejar);
109        }
110 break;
111
112    case CURLOPT_NETRC: case CURLOPT_NETRC_FILE:
113 if(state->curlflags.netrc) {
114     CHECK(stateCURLOPT_NETRC, (OPTARG)CURL_NETRC_REQUIRED);
115     CHECK(stateCURLOPT_NETRC_FILEstate->curlflags.netrc);
116        }
117 break;
118
119    case CURLOPT_VERBOSE:
120 if(state->curlflags.verbose)
121     CHECK(stateCURLOPT_VERBOSE, (OPTARG)1L);
122 break;
123
124    case CURLOPT_TIMEOUT:
125 if(state->curlflags.timeout)
126     CHECK(stateCURLOPT_TIMEOUT, (OPTARG)((long)state->curlflags.timeout));
127 break;
128
129    case CURLOPT_USERAGENT:
130        if(state->curlflags.useragent)
131     CHECK(stateCURLOPT_USERAGENTstate->curlflags.useragent);
132 break;
133
134    case CURLOPT_FOLLOWLOCATION:
135        CHECK(stateCURLOPT_FOLLOWLOCATION, (OPTARG)1L);
136 break;
137
138    case CURLOPT_MAXREDIRS:
139 CHECK(stateCURLOPT_MAXREDIRS, (OPTARG)OC_MAX_REDIRECTS);
140 break;
141
142    case CURLOPT_ERRORBUFFER:
143 CHECK(stateCURLOPT_ERRORBUFFERstate->error.curlerrorbuf);
144 break;
145
146    case CURLOPT_ENCODING:
147#ifdef CURLOPT_ENCODING
148 if(state->curlflags.compress) {
149     CHECK(stateCURLOPT_ENCODING,"deflate, gzip");
150        }
151#endif
152 break;
153
154    case CURLOPT_PROXY:
155 if(state->proxy.host != NULL) {
156     CHECK(stateCURLOPT_PROXYstate->proxy.host);
157     CHECK(stateCURLOPT_PROXYPORT, (OPTARG)(long)state->proxy.port);
158     if(state->proxy.userpwd) {
159                CHECK(stateCURLOPT_PROXYUSERPWDstate->proxy.userpwd);
160#ifdef CURLOPT_PROXYAUTH
161         CHECK(stateCURLOPT_PROXYAUTH, (long)CURLAUTH_ANY);
162#endif
163     }
164 }
165 break;
166
167    case CURLOPT_USE_SSL:
168    case CURLOPT_SSLCERT: case CURLOPT_SSLKEY:
169    case CURLOPT_SSL_VERIFYPEER: case CURLOPT_SSL_VERIFYHOST:
170    {
171        struct OCSSLssl = &state->ssl;
172        CHECK(stateCURLOPT_SSL_VERIFYPEER, (OPTARG)(ssl->verifypeer?1L:0L));
173        CHECK(stateCURLOPT_SSL_VERIFYHOST, (OPTARG)(ssl->verifyhost?1L:0L));
174        if(ssl->certificate)
175            CHECK(stateCURLOPT_SSLCERTssl->certificate);
176        if(ssl->key)
177            CHECK(stateCURLOPT_SSLKEYssl->key);
178        if(ssl->keypasswd)
179            /* libcurl prior to 7.16.4 used 'CURLOPT_SSLKEYPASSWD' */
180            CHECK(stateCURLOPT_KEYPASSWDssl->keypasswd);
181        if(ssl->cainfo)
182            CHECK(stateCURLOPT_CAINFOssl->cainfo);
183        if(ssl->capath)
184            CHECK(stateCURLOPT_CAPATHssl->capath);
185    }
186    break;
187
188    default: {
189 struct OCCURLFLAGf = occurlflagbyflag(flag);
190 if(f != NULL)
191     oclog(OCLOGWARN,"Attempt to update unexpected curl flag: %s",
192 f->name);
193 } break;
194    }
195done:
196    return stat;
197}
198
199
200/* Set various general curl flags per fetch  */
201OCerror
202ocset_flags_perfetch(OCstatestate)
203{
204    OCerror stat = OC_NOERR;
205    /* currently none */
206    return stat;
207}
208
209/* Set various general curl flags per link */
210
211OCerror
212ocset_flags_perlink(OCstatestate)
213{
214    OCerror stat = OC_NOERR;
215
216    /* Following are always set */
217    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_ENCODING);
218    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_NETRC);
219    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_VERBOSE);
220    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_TIMEOUT);
221    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_USERAGENT);
222    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_COOKIEJAR);
223    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_USERPWD);
224    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_PROXY);
225    if(stat == OC_NOERRstat = ocset_curlflag(state,CURLOPT_USE_SSL);
226    if(stat == OC_NOERRstat = ocset_curlflag(stateCURLOPT_FOLLOWLOCATION);
227    if(stat == OC_NOERRstat = ocset_curlflag(stateCURLOPT_MAXREDIRS);
228    if(stat == OC_NOERRstat = ocset_curlflag(stateCURLOPT_ERRORBUFFER);
229
230    /* Set the CURL. options */
231    if(stat == OC_NOERRstat = oc_set_curl_options(state);
232
233    return stat;
234}
235
236/**
237Directly set any options starting with 'CURL.'
238*/
239static OCerror
240oc_set_curl_options(OCstatestate)
241{
242    OCerror stat = OC_NOERR;
243    struct OCTriplestorestore = NULL;
244    struct OCTripletriple = NULL;
245    int i;
246    char* hostport = NULL;
247    struct OCCURLFLAGocflag = NULL;
248
249    hostport = occombinehostport(state->uri);
250    if(hostport == NULL) {
251      hostport = (char*)malloc(sizeof(char)*1);
252      *hostport = '\0';
253    }
254
255    store = &ocglobalstate.rc.daprc;
256    triple = store->triples;
257
258    /* Assume that the triple store has been properly sorted */
259    for(i=0;i<store->ntriples;i++,triple++) {
260        size_t hostlen = strlen(triple->host);
261        const char* flagname;
262
263        if(ocstrncmp("CURL.",triple->key,5) != 0) continue; /* not a curl flag */
264        /* do hostport prefix comparison */
265        if(hostlen > 0) {
266          int t = ocstrncmp(hostport,triple->host,hostlen);
267          if(t !=  0) continue;
268        }
269        flagname = triple->key+5; /* 5 == strlen("CURL."); */
270        ocflag = occurlflagbyname(flagname);
271        if(ocflag == NULL) {stat = OC_ECURL; goto done;}
272        stat = ocset_curlopt(state,ocflag->flag,cvt(triple->value,ocflag->type));
273    }
274 done:
275    if(hostport && strlen(hostport) > 0) free(hostport);
276    return stat;
277}
278
279static void*
280cvt(char* value, enum OCCURLFLAGTYPE type)
281{
282    switch (type) {
283    case CF_LONG: {
284 /* Try to convert to long value */
285 const char* p = value;
286 char* q = NULL;
287 long longvalue = strtol(p,&q,10);
288 if(*q != '\0')
289     return NULL;
290 return (void*)longvalue;
291 }
292    case CF_STRING:
293 return (void*)value;
294    case CF_UNKNOWN: case CF_OTHER:
295 return (void*)value;
296    }
297    return NULL;
298}
299
300void
301oc_curl_debug(OCstatestate)
302{
303    state->curlflags.verbose = 1;
304    ocset_curlflag(state,CURLOPT_VERBOSE);
305    ocset_curlflag(state,CURLOPT_ERRORBUFFER);
306}
307
308/* Misc. */
309
310int
311ocrc_netrc_required(OCstatestate)
312{
313    char* netrcfile = ocrc_lookup(NETRCFILETAG,state->uri->uri);
314    return (netrcfile != NULL || state->curlflags.netrc != NULL ? 0 : 1);
315}
316
317void
318oc_curl_printerror(OCstatestate)
319{
320    fprintf(stderr,"curl error details: %s\n",state->curlerror);
321}
322
323/* Determine if this version of curl supports
324       "file://..." &/or "https://..." urls.
325*/
326void
327oc_curl_protocols(struct OCGLOBALSTATEstate)
328{
329    const char* const* proto; /*weird*/
330    curl_version_info_datacurldata;
331    curldata = curl_version_info(CURLVERSION_NOW);
332    for(proto=curldata->protocols;*proto;proto++) {
333        if(strcmp("file",*proto)==0) {state->curl.proto_file=1;}
334        if(strcmp("http",*proto)==0) {state->curl.proto_https=1;}
335    }
336    if(ocdebug > 0) {
337        oclog(OCLOGNOTE,"Curl file:// support = %d",state->curl.proto_file);
338        oclog(OCLOGNOTE,"Curl https:// support = %d",state->curl.proto_https);
339    }
340}
341
342
343/*
344"Inverse" of ocset_curlflag;
345Given a flag and value, it updates state.
346Update a specific flag from state->curlflags.
347*/
348OCerror
349ocset_curlstate(OCstatestate, int flag, void* value)
350{
351    OCerror stat = OC_NOERR;
352
353    switch (flag) {
354
355    case CURLOPT_USERPWD:
356        if(state->creds.userpwd != NULL) free(state->creds.userpwd);
357 state->creds.userpwd = strdup((char*)value);
358 break;
359
360    case CURLOPT_COOKIEJAR: case CURLOPT_COOKIEFILE:
361        if(state->curlflags.cookiejar != NULL) free(state->curlflags.cookiejar);
362 state->curlflags.cookiejar = strdup((char*)value);
363 break;
364
365    case CURLOPT_NETRC: case CURLOPT_NETRC_FILE:
366        if(state->curlflags.netrc != NULL) free(state->curlflags.netrc);
367 state->curlflags.netrc = strdup((char*)value);
368 break;
369
370    case CURLOPT_VERBOSE:
371 state->curlflags.verbose = (long)value;
372 break;
373
374    case CURLOPT_TIMEOUT:
375 state->curlflags.timeout = (long)value;
376 break;
377
378    case CURLOPT_USERAGENT:
379        if(state->curlflags.useragent != NULL) free(state->curlflags.useragent);
380        state->curlflags.useragent = strdup((char*)value);
381 break;
382
383    case CURLOPT_FOLLOWLOCATION:
384 /* no need to store; will always be set */
385 break;
386
387    case CURLOPT_MAXREDIRS:
388 /* no need to store; will always be set */
389 break;
390
391    case CURLOPT_ERRORBUFFER:
392 /* no need to store; will always be set */
393 break;
394
395    case CURLOPT_ENCODING:
396 /* no need to store; will always be set to fixed value */
397 break;
398
399    case CURLOPT_PROXY:
400 /* We assume that the value is the proxy url */
401 if(state->proxy.host != NULL) free(state->proxy.host);
402 if(state->proxy.userpwd != NULL) free(state->proxy.userpwd);
403 state->proxy.host = NULL;
404 state->proxy.userpwd = NULL;
405 if(!ocparseproxy(state,(char*)value))
406 {stat = OC_EINVAL; goto done;}
407 break;
408
409    case CURLOPT_SSLCERT:
410 if(state->ssl.certificate != NULL) free(state->ssl.certificate);
411 state->ssl.certificate = strdup((char*)value);
412 break;
413    case CURLOPT_SSLKEY:
414 if(state->ssl.key != NULL) free(state->ssl.key);
415 state->ssl.key = strdup((char*)value);
416 break;
417    case CURLOPT_KEYPASSWD:
418 if(state->ssl.keypasswd!= NULL) free(state->ssl.keypasswd);
419 state->ssl.keypasswd = strdup((char*)value);
420 break;
421    case CURLOPT_SSL_VERIFYPEER:
422      state->ssl.verifypeer = (long)value;
423      break;
424    case CURLOPT_SSL_VERIFYHOST:
425      state->ssl.verifyhost = (long)value;
426      break;
427    case CURLOPT_CAINFO:
428      if(state->ssl.cainfo != NULL) free(state->ssl.cainfo);
429      state->ssl.cainfo = strdup((char*)value);
430      break;
431    case CURLOPT_CAPATH:
432 if(state->ssl.capath != NULL) free(state->ssl.capath);
433 state->ssl.capath = strdup((char*)value);
434 break;
435
436    default: {
437 struct OCCURLFLAGf = occurlflagbyflag(flag);
438 if(f != NULL)
439     oclog(OCLOGWARN,"Attempt to add unexpected curl flag to state: %s",
440 f->name);
441 } break;
442    }
443done:
444    return stat;
445}


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