Coverage Report

Created: 2023-05-28 06:42

/src/netcdf-c/libdispatch/dhttp.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * @file
3
 *
4
 * Read a range of data from a remote dataset.
5
 *
6
 * Copyright 2018 University Corporation for Atmospheric
7
 * Research/Unidata. See COPYRIGHT file for more info.
8
*/
9
10
#include "config.h"
11
#include <stdlib.h>
12
#include <stdio.h>
13
#include <string.h>
14
#ifdef HAVE_UNISTD_H
15
#include <unistd.h>
16
#endif
17
18
#define CURL_DISABLE_TYPECHECK 1
19
#include <curl/curl.h>
20
21
#include "netcdf.h"
22
#include "nclog.h"
23
#include "ncbytes.h"
24
#include "nclist.h"
25
#include "ncuri.h"
26
#include "ncauth.h"
27
28
#ifdef ENABLE_S3
29
#include "ncs3sdk.h"
30
#endif
31
#include "nchttp.h"
32
33
#undef TRACE
34
35
0
#define CURLERR(e) reporterror(state,(e))
36
37
#if 0
38
static const char* LENGTH_ACCEPT[] = {"content-length","accept-ranges",NULL};
39
#endif
40
static const char* CONTENTLENGTH[] = {"content-length",NULL};
41
42
/* Forward */
43
static int nc_http_set_method(NC_HTTP_STATE* state, HTTPMETHOD method);
44
static int nc_http_set_response(NC_HTTP_STATE* state, NCbytes* buf);
45
static int nc_http_set_payload(NC_HTTP_STATE* state, size_t len, void* payload);
46
47
static int setupconn(NC_HTTP_STATE* state, const char* objecturl);
48
static int execute(NC_HTTP_STATE* state);
49
static int headerson(NC_HTTP_STATE* state, const char** which);
50
static void headersoff(NC_HTTP_STATE* state);
51
static void showerrors(NC_HTTP_STATE* state);
52
static int reporterror(NC_HTTP_STATE* state, CURLcode cstat);
53
static int lookupheader(NC_HTTP_STATE* state, const char* key, const char** valuep);
54
static int my_trace(CURL *handle, curl_infotype type, char *data, size_t size,void *userp);
55
56
#ifdef TRACE
57
static void
58
dbgflush() {
59
fflush(stderr);
60
fflush(stdout);
61
}
62
63
static void
64
Trace(const char* fcn)
65
{
66
    fprintf(stdout,"xxx: %s\n",fcn);
67
    dbgflush();
68
}
69
#else
70
#define dbgflush()
71
#define Trace(fcn)
72
#endif /*TRACE*/
73
74
/**************************************************/
75
76
/**
77
@param statep return a pointer to an allocated NC_HTTP_STATE
78
*/
79
80
int
81
nc_http_open(const char* url, NC_HTTP_STATE** statep)
82
0
{
83
0
    return nc_http_open_verbose(url,0,statep);
84
0
}
85
86
int
87
nc_http_open_verbose(const char* path, int verbose, NC_HTTP_STATE** statep)
88
0
{
89
0
    int stat = NC_NOERR;
90
0
    NC_HTTP_STATE* state = NULL;
91
0
    NCURI* uri = NULL;
92
93
0
    Trace("open");
94
95
0
    ncuriparse(path,&uri);
96
0
    if(uri == NULL) {stat = NCTHROW(NC_EURL); goto done;}
97
98
0
    if((state = calloc(1,sizeof(NC_HTTP_STATE))) == NULL)
99
0
        {stat = NCTHROW(NC_ENOMEM); goto done;}
100
0
    state->path = strdup(path);
101
0
    state->url = uri; uri = NULL;    
102
0
    state->format = (NC_iss3(state->url)?HTTPS3:HTTPCURL);
103
104
0
    switch (state->format) {
105
0
    case HTTPCURL: {
106
        /* initialize curl*/
107
0
        state->curl.curl = curl_easy_init();
108
0
        if (state->curl.curl == NULL) {stat = NCTHROW(NC_ECURL); goto done;}
109
0
        showerrors(state);
110
0
  state->errmsg = state->curl.errbuf;
111
0
        if(verbose) {
112
0
            long onoff = 1;
113
0
            CURLcode cstat = CURLE_OK;
114
0
            cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_VERBOSE, onoff));
115
0
            if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
116
0
            cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_DEBUGFUNCTION, my_trace));
117
0
            if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
118
0
        }
119
0
        } break;
120
#ifdef ENABLE_S3
121
    case HTTPS3: {
122
  if((state->s3.info = (NCS3INFO*)calloc(1,sizeof(NCS3INFO)))==NULL)
123
      {stat = NCTHROW(NC_ENOMEM); goto done;}
124
        if((stat = NC_s3urlprocess(state->url,state->s3.info))) goto done;
125
  if((state->s3.s3client = NC_s3sdkcreateclient(state->s3.info))==NULL)
126
      {stat = NCTHROW(NC_EURL); goto done;}
127
        } break;
128
#endif
129
0
    default: return NCTHROW(NC_ENOTBUILT);
130
0
    }
131
0
    stat = nc_http_reset(state);
132
0
    if(statep) {*statep = state; state = NULL;}
133
0
done:
134
0
    if(state) nc_http_close(state);
135
0
dbgflush();
136
0
    return NCTHROW(stat);
137
0
}
138
139
int
140
nc_http_close(NC_HTTP_STATE* state)
141
0
{
142
0
    int stat = NC_NOERR;
143
144
0
    Trace("close");
145
146
0
    if(state == NULL) return NCTHROW(stat);
147
0
    switch (state->format) {
148
0
    case HTTPCURL:
149
0
        if(state->curl.curl != NULL)
150
0
            (void)curl_easy_cleanup(state->curl.curl);
151
0
        nclistfreeall(state->curl.response.headset); state->curl.response.headset = NULL;
152
0
        nclistfreeall(state->curl.response.headers); state->curl.response.headers = NULL;
153
0
        ncbytesfree(state->curl.response.buf);
154
0
        nclistfreeall(state->curl.request.headers); state->curl.request.headers = NULL;
155
0
        break;
156
#ifdef ENABLE_S3
157
    case HTTPS3: {
158
  if(state->s3.s3client)
159
            NC_s3sdkclose(state->s3.s3client, state->s3.info, 0, NULL);
160
        NC_s3clear(state->s3.info);
161
  nullfree(state->s3.info);
162
  state->s3.s3client = NULL;
163
        } break;
164
#endif   
165
0
    default: stat = NCTHROW(NC_ENOTBUILT); goto done;
166
0
    }
167
0
    nullfree(state->path);
168
0
    ncurifree(state->url);
169
0
    nullfree(state);
170
0
done:
171
0
dbgflush();
172
0
    return NCTHROW(stat);
173
0
}
174
175
/* Reset after a request */
176
int
177
nc_http_reset(NC_HTTP_STATE* state)
178
0
{
179
0
    int stat = NC_NOERR;
180
0
    CURLcode cstat = CURLE_OK;
181
182
0
    switch (state->format) {
183
0
    case HTTPCURL:
184
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HTTPGET, 1L));
185
0
        if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
186
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_NOBODY, 0L));
187
0
        if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
188
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_UPLOAD, 0L));
189
0
        if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
190
0
        cstat = curl_easy_setopt(state->curl.curl, CURLOPT_CUSTOMREQUEST, NULL);
191
0
        if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
192
0
        cstat = curl_easy_setopt(state->curl.curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)-1);
193
0
        if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
194
0
        state->curl.request.method = HTTPGET;
195
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_WRITEFUNCTION, NULL));
196
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_WRITEDATA, NULL));
197
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_READFUNCTION, NULL));
198
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_READDATA, NULL));
199
0
        headersoff(state);
200
0
        break;
201
#ifdef ENABLE_S3
202
    case HTTPS3:
203
        break; /* Done automatically */
204
#endif
205
0
    default: stat = NCTHROW(NC_ENOTBUILT); goto done;
206
0
    }
207
0
done:
208
0
    return NCTHROW(stat);
209
0
}
210
211
/**************************************************/
212
/**************************************************/
213
/**
214
@param state state handle
215
@param objecturl to read
216
@param start starting offset
217
@param count number of bytes to read
218
@param buf store read data here -- caller must allocate and free
219
*/
220
221
int
222
nc_http_read(NC_HTTP_STATE* state, size64_t start, size64_t count, NCbytes* buf)
223
0
{
224
0
    int stat = NC_NOERR;
225
0
    char range[64];
226
0
    CURLcode cstat = CURLE_OK;
227
228
0
    Trace("read");
229
230
0
    if(count == 0)
231
0
        goto done; /* do not attempt to read */
232
233
0
    switch (state->format) {
234
0
    case HTTPCURL:
235
0
        if((stat = nc_http_set_response(state,buf))) goto fail;
236
0
        if((stat = setupconn(state,state->path)))
237
0
            goto fail;
238
    
239
        /* Set to read byte range */
240
0
        snprintf(range,sizeof(range),"%ld-%ld",(long)start,(long)((start+count)-1));
241
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_RANGE, range));
242
0
        if(cstat != CURLE_OK)
243
0
            {stat = NCTHROW(NC_ECURL); goto done;}
244
    
245
0
        if((stat = execute(state)))
246
0
            goto done;
247
0
  break;
248
#ifdef ENABLE_S3
249
    case HTTPS3: {
250
  /* Make sure buf has enough space allocated */
251
        ncbytessetalloc(buf,count);
252
        ncbytessetlength(buf,count);
253
        if((stat = NC_s3sdkread(state->s3.s3client,
254
                                state->s3.info->bucket,
255
                                state->s3.info->rootkey,
256
        start,
257
                                count,
258
                                ncbytescontents(buf),
259
                                &state->errmsg))) goto done;
260
        } break;
261
#endif
262
0
    default: stat = NCTHROW(NC_ENOTBUILT); goto done;
263
0
    }
264
0
done:
265
0
    nc_http_reset(state);
266
0
    if(state->format == HTTPCURL)
267
0
        state->curl.response.buf = NULL;
268
0
dbgflush();
269
0
    return NCTHROW(stat);
270
271
0
fail:
272
0
    stat = NCTHROW(NC_ECURL);
273
0
    goto done;
274
0
}
275
276
/**
277
@param state state handle
278
@param objectpath to write
279
@param payload send as body of a PUT
280
*/
281
282
int
283
nc_http_write(NC_HTTP_STATE* state, NCbytes* payload)
284
0
{
285
0
    int stat = NC_NOERR;
286
287
0
    Trace("write");
288
289
0
    if(payload == NULL || ncbyteslength(payload) == 0) goto done;    
290
291
0
    switch (state->format) {
292
0
    case HTTPCURL:
293
0
        if((stat = nc_http_set_payload(state,ncbyteslength(payload),ncbytescontents(payload)))) goto fail;
294
0
        if((stat = nc_http_set_method(state,HTTPPUT))) goto fail;
295
0
        if((stat = setupconn(state,state->path))) goto fail;
296
0
        if((stat = execute(state)))
297
0
            goto done;
298
0
  break;
299
#ifdef ENABLE_S3
300
    case HTTPS3:
301
        if((stat = NC_s3sdkwriteobject(state->s3.s3client,
302
                                state->s3.info->bucket,
303
                                state->s3.info->rootkey,
304
                                ncbyteslength(payload),
305
                                ncbytescontents(payload),
306
                                &state->errmsg))) goto done;
307
        break;
308
#endif
309
0
    default: stat = NCTHROW(NC_ENOTBUILT); goto done;
310
0
    }
311
0
done:
312
0
    nc_http_reset(state);
313
0
    return NCTHROW(stat);
314
315
0
fail:
316
0
    stat = NCTHROW(NC_ECURL);
317
0
    goto done;
318
0
}
319
320
/**
321
Return length of an object.
322
Assume URL etc has already been set.
323
@param curl curl handle
324
*/
325
326
int
327
nc_http_size(NC_HTTP_STATE* state, long long* sizep)
328
0
{
329
0
    int stat = NC_NOERR;
330
0
    const char* hdr = NULL;
331
332
0
    Trace("size");
333
0
    if(sizep == NULL)
334
0
        goto done; /* do not attempt to read */
335
336
0
    switch (state->format) {
337
0
    case HTTPCURL:
338
0
        if((stat = nc_http_set_method(state,HTTPHEAD))) goto done;
339
0
        if((stat = setupconn(state,state->path)))
340
0
            goto done;
341
        /* Make sure we get headers */
342
0
        if((stat = headerson(state,CONTENTLENGTH))) goto done;
343
    
344
0
        state->httpcode = 200;
345
0
        if((stat = execute(state)))
346
0
            goto done;
347
    
348
0
        if(nclistlength(state->curl.response.headers) == 0)
349
0
            {stat = NCTHROW(NC_EURL); goto done;}
350
    
351
        /* Get the content length header */
352
0
        if((stat = lookupheader(state,"content-length",&hdr))==NC_NOERR)
353
0
                sscanf(hdr,"%llu",sizep);
354
0
        break;
355
#ifdef ENABLE_S3
356
    case HTTPS3: {
357
  size64_t len = 0;
358
  if((stat = NC_s3sdkinfo(state->s3.s3client,state->s3.info->bucket,state->s3.info->rootkey,&len,&state->errmsg))) goto done;
359
  if(sizep) *sizep = len;
360
        } break;
361
#endif
362
0
    default: stat = NCTHROW(NC_ENOTBUILT); goto done;
363
0
    }
364
0
done:
365
0
    nc_http_reset(state);
366
0
    if(state->format == HTTPCURL)
367
0
        headersoff(state);
368
0
dbgflush();
369
0
    return NCTHROW(stat);
370
0
}
371
372
/**************************************************/
373
/* Set misc parameters */
374
375
static int
376
nc_http_set_method(NC_HTTP_STATE* state, HTTPMETHOD method)
377
0
{
378
0
    int stat = NC_NOERR;
379
0
    CURLcode cstat = CURLE_OK;
380
0
    switch (method) {
381
0
    case HTTPGET:
382
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HTTPGET, 1L));
383
0
        break;
384
0
    case HTTPHEAD:
385
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HTTPGET, 1L));
386
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_NOBODY, 1L));
387
0
        break;
388
0
    case HTTPPUT:
389
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_UPLOAD, 1L));
390
0
        break;
391
0
    case HTTPDELETE:
392
0
        cstat = curl_easy_setopt(state->curl.curl, CURLOPT_CUSTOMREQUEST, "DELETE");
393
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_NOBODY, 1L));
394
0
        break;
395
0
    default: stat = NCTHROW(NC_EINVAL); break;
396
0
    }
397
0
    if(cstat != CURLE_OK) {stat = NCTHROW(NC_ECURL); goto done;}
398
0
    state->curl.request.method = method;
399
0
done:
400
0
    return NCTHROW(stat);
401
0
}
402
403
static int
404
nc_http_set_payload(NC_HTTP_STATE* state, size_t size, void* payload)
405
0
{
406
0
    int stat = NC_NOERR;
407
0
    state->curl.request.payloadsize = size;
408
0
    state->curl.request.payload = payload;
409
0
    state->curl.request.payloadpos = 0;
410
0
    return NCTHROW(stat);
411
0
}
412
413
static int
414
nc_http_set_response(NC_HTTP_STATE* state, NCbytes* buf)
415
0
{
416
0
    int stat = NC_NOERR;
417
0
    state->curl.response.buf = buf;
418
0
    return NCTHROW(stat);
419
0
}
420
421
#if 0
422
static int
423
nc_http_response_headset(NC_HTTP_STATE* state, const NClist* keys)
424
{
425
    int i;
426
    if(keys == NULL) return NC_NOERR;
427
    if(state->curl.response.headset == NULL)
428
        state->curl.response.headset = nclistnew();
429
    for(i=0;i<nclistlength(keys);i++) {
430
        const char* key = (const char*)nclistget(keys,i);
431
        if(!nclistmatch(state->curl.response.headset,key,0)) /* remove duplicates */
432
            nclistpush(state->curl.response.headset,strdup(key));
433
    }
434
    return NC_NOERR;
435
}
436
437
static int
438
nc_http_response_headers(NC_HTTP_STATE* state, NClist** headersp)
439
{
440
    NClist* headers = NULL;
441
    if(headersp != NULL) {
442
        headers = nclistclone(state->curl.response.headers,1);
443
        *headersp = headers; headers = NULL;
444
    }
445
    return NC_NOERR;
446
}
447
448
static int
449
nc_http_request_setheaders(NC_HTTP_STATE* state, const NClist* headers)
450
{
451
    nclistfreeall(state->curl.request.headers);
452
    state->curl.request.headers = nclistclone(headers,1);
453
    return NC_NOERR;    
454
}
455
#endif
456
457
/**************************************************/
458
459
static size_t
460
ReadMemoryCallback(char* buffer, size_t size, size_t nmemb, void *data)
461
0
{
462
0
    NC_HTTP_STATE* state = data;
463
0
    size_t transfersize = size * nmemb;
464
0
    size_t avail = (state->curl.request.payloadsize - state->curl.request.payloadpos);
465
466
0
    Trace("ReadMemoryCallback");
467
0
    if(transfersize == 0)
468
0
        nclog(NCLOGWARN,"ReadMemoryCallback: zero sized buffer");
469
0
    if(transfersize > avail) transfersize = avail;
470
0
    memcpy(buffer,((char*)state->curl.request.payload)+state->curl.request.payloadpos,transfersize);
471
0
    state->curl.request.payloadpos += transfersize;
472
0
    return transfersize;
473
0
}
474
475
static size_t
476
WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
477
0
{
478
0
    NC_HTTP_STATE* state = data;
479
0
    size_t realsize = size * nmemb;
480
481
0
    Trace("WriteMemoryCallback");
482
0
    if(realsize == 0)
483
0
        nclog(NCLOGWARN,"WriteMemoryCallback: zero sized chunk");
484
0
    ncbytesappendn(state->curl.response.buf, ptr, realsize);
485
0
    return realsize;
486
0
}
487
488
static void
489
trim(char* s)
490
0
{
491
0
    size_t l = strlen(s);
492
0
    char* p = s;
493
0
    char* q = s + l;
494
0
    if(l == 0) return;
495
0
    q--; /* point to last char of string */
496
    /* Walk backward to first non-whitespace */
497
0
    for(;q > p;q--) {
498
0
        if(*q > ' ') break; /* found last non-whitespace */
499
0
    }
500
    /* invariant: p == q || *q > ' ' */
501
0
    if(p == q) /* string is all whitespace */
502
0
        {*p = '\0';}
503
0
    else {/* *q is last non-whitespace */
504
0
        q++; /* point to actual whitespace */
505
0
        *q = '\0';
506
0
    }
507
    /* Ok, skip past leading whitespace */
508
0
    for(p=s;*p;p++) {if(*p > ' ') break;}
509
    /* invariant: *p == '\0' || *p > ' ' */
510
0
    if(*p == 0) return; /* no leading whitespace */
511
    /* Ok, overwrite any leading whitespace */
512
0
    for(q=s;*p;) {*q++ = *p++;}
513
0
    *q = '\0';
514
0
    return;
515
0
}
516
517
static size_t
518
HeaderCallback(char *buffer, size_t size, size_t nitems, void *data)
519
0
{
520
0
    size_t realsize = size * nitems;
521
0
    char* name = NULL;
522
0
    char* value = NULL;
523
0
    char* p = NULL;
524
0
    size_t i;
525
0
    int havecolon;
526
0
    NC_HTTP_STATE* state = data;
527
0
    int match;
528
0
    const char* hdr;
529
530
0
    Trace("HeaderCallback");
531
0
    if(realsize == 0)
532
0
        nclog(NCLOGWARN,"HeaderCallback: zero sized chunk");
533
0
    i = 0;
534
    /* Look for colon separator */
535
0
    for(p=buffer;(i < realsize) && (*p != ':');p++,i++);
536
0
    havecolon = (i < realsize);
537
0
    if(i == 0)
538
0
        nclog(NCLOGWARN,"HeaderCallback: malformed header: %s",buffer);
539
0
    name = malloc(i+1);
540
0
    memcpy(name,buffer,i);
541
0
    name[i] = '\0';
542
0
    if(state->curl.response.headset != NULL) {
543
0
        for(match=0,i=0;i<nclistlength(state->curl.response.headset);i++) {
544
0
            hdr = (const char*)nclistget(state->curl.response.headset,i);
545
0
            if(strcasecmp(hdr,name)==0) {match = 1; break;}        
546
0
        }
547
0
        if(!match) goto done;
548
0
    }
549
    /* Capture this header */
550
0
    value = NULL;
551
0
    if(havecolon) {
552
0
        size_t vlen = (realsize - i);
553
0
        value = malloc(vlen+1);
554
0
        p++; /* skip colon */
555
0
        memcpy(value,p,vlen);
556
0
        value[vlen] = '\0';
557
0
        trim(value);
558
0
    }
559
0
    if(state->curl.response.headers == NULL)
560
0
        state->curl.response.headers = nclistnew();
561
0
    nclistpush(state->curl.response.headers,name);
562
0
    name = NULL;
563
0
    if(value == NULL) value = strdup("");
564
0
    nclistpush(state->curl.response.headers,value);
565
0
    value = NULL;
566
0
done:
567
0
    nullfree(name);
568
0
    return realsize;    
569
0
}
570
571
static int
572
setupconn(NC_HTTP_STATE* state, const char* objecturl)
573
0
{
574
0
    int stat = NC_NOERR;
575
0
    CURLcode cstat = CURLE_OK;
576
577
0
    if(objecturl != NULL) {
578
        /* Set the URL */
579
#ifdef TRACE
580
        fprintf(stderr,"curl.setup: url |%s|\n",objecturl);
581
#endif
582
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_URL, (void*)objecturl));
583
0
        if (cstat != CURLE_OK) goto fail;
584
0
    }
585
    /* Set options */
586
0
    cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_TIMEOUT, 100)); /* 30sec timeout*/
587
0
    if (cstat != CURLE_OK) goto fail;
588
0
    cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_CONNECTTIMEOUT, 100));
589
0
    if (cstat != CURLE_OK) goto fail;
590
0
    cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_NOPROGRESS, 1));
591
0
    if (cstat != CURLE_OK) goto fail;
592
0
    cstat = curl_easy_setopt(state->curl.curl, CURLOPT_FOLLOWLOCATION, 1); 
593
0
    if (cstat != CURLE_OK) goto fail;
594
595
    /* Pull some values from .rc tables */
596
0
    {
597
0
        NCURI* uri = NULL;
598
0
        char* hostport = NULL;
599
0
        char* value = NULL;
600
0
        ncuriparse(objecturl,&uri);
601
0
        if(uri == NULL) goto fail;
602
0
        hostport = NC_combinehostport(uri);
603
0
        ncurifree(uri); uri = NULL;
604
0
        value = NC_rclookup("HTTP.SSL.CAINFO",hostport,NULL);
605
0
        nullfree(hostport); hostport = NULL;    
606
0
        if(value == NULL)
607
0
            value = NC_rclookup("HTTP.SSL.CAINFO",NULL,NULL);
608
0
        if(value != NULL) {
609
0
            cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_CAINFO, value));
610
0
            if (cstat != CURLE_OK) goto fail;
611
0
        }
612
0
    }
613
614
    /* Set the method */
615
0
    if((stat = nc_http_set_method(state,state->curl.request.method))) goto done;
616
617
0
    if(state->curl.response.buf) {
618
        /* send all data to this function  */
619
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback));
620
0
        if (cstat != CURLE_OK) goto fail;
621
        /* Set argument for WriteMemoryCallback */
622
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_WRITEDATA, (void*)state));
623
0
        if (cstat != CURLE_OK) goto fail;
624
0
    } else {/* turn off data capture */
625
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_WRITEFUNCTION, NULL));
626
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_WRITEDATA, NULL));
627
0
    }
628
0
    if(state->curl.request.payloadsize > 0) {
629
0
        state->curl.request.payloadpos = 0; /* track reading */
630
        /* send all data to this function  */
631
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_READFUNCTION, ReadMemoryCallback));
632
0
        if (cstat != CURLE_OK) goto fail;
633
        /* Set argument for ReadMemoryCallback */
634
0
        cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_READDATA, (void*)state));
635
0
        if (cstat != CURLE_OK) goto fail;
636
0
    } else {/* turn off data capture */
637
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_READFUNCTION, NULL));
638
0
        (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_READDATA, NULL));
639
0
    }
640
641
    /* Do method specific actions */
642
0
    switch(state->curl.request.method) {
643
0
    case HTTPPUT:
644
0
        if(state->curl.request.payloadsize > 0)
645
0
            cstat = curl_easy_setopt(state->curl.curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)state->curl.request.payloadsize);
646
0
        break;
647
0
    default: break;
648
0
    }
649
    
650
0
done:
651
0
    return NCTHROW(stat);
652
0
fail:
653
    /* Turn off header capture */
654
0
    headersoff(state);
655
0
    stat = NCTHROW(NC_ECURL);
656
0
    goto done;
657
0
}
658
659
static int
660
execute(NC_HTTP_STATE* state)
661
0
{
662
0
    int stat = NC_NOERR;
663
0
    CURLcode cstat = CURLE_OK;
664
665
0
    cstat = CURLERR(curl_easy_perform(state->curl.curl));
666
0
    if(cstat != CURLE_OK) goto fail;
667
668
0
    cstat = CURLERR(curl_easy_getinfo(state->curl.curl,CURLINFO_RESPONSE_CODE,&state->httpcode));
669
0
    if(cstat != CURLE_OK) state->httpcode = 0;
670
671
0
done:
672
0
    return NCTHROW(stat);
673
0
fail:
674
0
    stat = NCTHROW(NC_ECURL);
675
0
    goto done;
676
0
}
677
678
static int
679
headerson(NC_HTTP_STATE* state, const char** headset)
680
0
{
681
0
    int stat = NC_NOERR;
682
0
    CURLcode cstat = CURLE_OK;
683
0
    const char** p;
684
685
0
    if(state->curl.response.headers != NULL)
686
0
        nclistfreeall(state->curl.response.headers);
687
0
    state->curl.response.headers = nclistnew();
688
0
    if(state->curl.response.headset != NULL)
689
0
        nclistfreeall(state->curl.response.headset);
690
0
    state->curl.response.headset = nclistnew();
691
0
    for(p=headset;*p;p++)
692
0
        nclistpush(state->curl.response.headset,strdup(*p));
693
694
0
    cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HEADERFUNCTION, HeaderCallback));
695
0
    if(cstat != CURLE_OK) goto fail;
696
0
    cstat = CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HEADERDATA, (void*)state));
697
0
    if (cstat != CURLE_OK) goto fail;
698
699
0
done:
700
0
    return NCTHROW(stat);
701
0
fail:
702
0
    stat = NCTHROW(NC_ECURL);
703
0
    goto done;
704
0
}
705
706
static void
707
headersoff(NC_HTTP_STATE* state)
708
0
{
709
0
    nclistfreeall(state->curl.response.headers);
710
0
    state->curl.response.headers = NULL;
711
0
    (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HEADERFUNCTION, NULL));
712
0
    (void)CURLERR(curl_easy_setopt(state->curl.curl, CURLOPT_HEADERDATA, NULL));
713
0
}
714
715
static int
716
lookupheader(NC_HTTP_STATE* state, const char* key, const char** valuep)
717
0
{
718
0
    int i;
719
0
    const char* value = NULL;
720
    /* Get the content length header */
721
0
    for(i=0;i<nclistlength(state->curl.response.headers);i+=2) {
722
0
        char* s = nclistget(state->curl.response.headers,i);
723
0
        if(strcasecmp(s,key)==0) {
724
0
            value = nclistget(state->curl.response.headers,i+1);
725
0
            break;
726
0
        }
727
0
    }
728
0
    if(value == NULL) return NCTHROW(NC_ENOOBJECT);
729
0
    if(valuep)
730
0
        *valuep = value;
731
0
    return NC_NOERR;
732
0
}
733
734
static void
735
showerrors(NC_HTTP_STATE* state)
736
0
{
737
0
    (void)curl_easy_setopt(state->curl.curl, CURLOPT_ERRORBUFFER, state->curl.errbuf);
738
0
}
739
    
740
static int
741
reporterror(NC_HTTP_STATE* state, CURLcode cstat)
742
0
{
743
0
    if(cstat != CURLE_OK) 
744
0
        fprintf(stderr,"curlcode: (%d)%s : %s\n",
745
0
                cstat,curl_easy_strerror(cstat),
746
0
                state->errmsg?state->errmsg:"?");
747
0
    return cstat;
748
0
}
749
750
static
751
void dump(const char *text, FILE *stream, unsigned char *ptr, size_t size)
752
0
{
753
0
  size_t i;
754
0
  size_t c;
755
0
  unsigned int width=0x10;
756
 
757
0
  fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n",
758
0
          text, (long)size, (long)size);
759
 
760
0
  for(i=0; i<size; i+= width) {
761
0
    fprintf(stream, "%4.4lx: ", (long)i);
762
 
763
    /* show hex to the left */
764
0
    for(c = 0; c < width; c++) {
765
0
      if(i+c < size)
766
0
        fprintf(stream, "%02x ", ptr[i+c]);
767
0
      else
768
0
        fputs("   ", stream);
769
0
    }
770
 
771
    /* show data on the right */
772
0
    for(c = 0; (c < width) && (i+c < size); c++) {
773
0
      char x = (ptr[i+c] >= 0x20 && ptr[i+c] < 0x80) ? ptr[i+c] : '.';
774
0
      fputc(x, stream);
775
0
    }
776
 
777
0
    fputc('\n', stream); /* newline */
778
0
  }
779
0
}
780
 
781
static int
782
my_trace(CURL *handle, curl_infotype type, char *data, size_t size,void *userp)
783
0
{
784
0
  const char *text;
785
0
  (void)handle; /* prevent compiler warning */
786
0
  (void)userp;
787
 
788
0
  switch (type) {
789
0
  case CURLINFO_TEXT:
790
0
    fprintf(stderr, "== Info: %s", data);
791
0
  default: /* in case a new one is introduced to shock us */
792
0
    return 0;
793
 
794
0
  case CURLINFO_HEADER_OUT:
795
0
    text = "=> Send header";
796
0
    break;
797
0
  case CURLINFO_DATA_OUT:
798
0
    text = "=> Send data";
799
0
    break;
800
0
  case CURLINFO_SSL_DATA_OUT:
801
0
    text = "=> Send SSL data";
802
0
    break;
803
0
  case CURLINFO_HEADER_IN:
804
0
    text = "<= Recv header";
805
0
    break;
806
0
  case CURLINFO_DATA_IN:
807
0
    text = "<= Recv data";
808
0
    break;
809
0
  case CURLINFO_SSL_DATA_IN:
810
0
    text = "<= Recv SSL data";
811
0
    break;
812
0
  }
813
 
814
0
  dump(text, stderr, (unsigned char *)data, size);
815
0
  return 0;
816
0
}
817
818
#if 0
819
static char*
820
urlify(NC_HTTP_STATE* state, const char* path)
821
{
822
    NCbytes* buf = ncbytesnew();
823
    char* tmp = NULL;
824
825
    tmp = ncuribuild(state->url,NULL,NULL,NCURIPWD);
826
    ncbytescat(buf,tmp);
827
    nullfree(tmp); tmp = NULL;
828
    ncbytescat(buf,"/");
829
    if(state->url->path != NULL) {
830
        if(state->url->path[0] == '/')
831
      ncbytescat(buf,state->url->path+1);
832
  else
833
      ncbytescat(buf,state->url->path);
834
        if(ncbytesget(buf,ncbyteslength(buf)-1) == '/')
835
            ncbytessetlength(buf,ncbyteslength(buf)-1);
836
    }     
837
    if(path != NULL) {
838
        if(path[0] != '/')
839
      ncbytescat(buf,"/");
840
  ncbytescat(buf,path);
841
    }
842
    tmp = ncbytesextract(buf);
843
    ncbytesfree(buf);
844
    return tmp;
845
}
846
847
int
848
nc_http_urisplit(const char* url, char** rootp, char** pathp)
849
{
850
    int stat = NC_NOERR;
851
    NCURI* uri = NULL;
852
853
    ncuriparse(url,&uri);
854
    if(uri == NULL) {stat = NCTHROW(NC_EURL); goto done;}
855
    if(rootp) {
856
        char* tmp = ncuribuild(uri,NULL,NULL,NCURIPWD);
857
        *rootp = tmp;
858
        nullfree(tmp);
859
  tmp = NULL;
860
    }
861
    if(pathp) {*pathp = strdup(uri->path);}
862
done:
863
    return NCTHROW(stat);
864
}
865
#endif