1/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
2 See the COPYRIGHT file for more information. */
3
4#include "config.h"
5#include <sys/stat.h>
6#ifdef HAVE_UNISTD_H
7#include <unistd.h>
8#endif
9#include <fcntl.h>
10#include "ocinternal.h"
11#include "ocdebug.h"
12#include "ochttp.h"
13
14static size_t WriteFileCallback(void*, size_t, size_t, void*);
15static size_t WriteMemoryCallback(void*, size_t, size_t, void*);
16
17struct Fetchdata {
18 FILEstream;
19 size_t size;
20};
21
22long
23ocfetchhttpcode(CURLcurl)
24{
25    long httpcode = 200;
26    CURLcode cstat = CURLE_OK;
27    /* Extract the http code */
28#ifdef HAVE_CURLINFO_RESPONSE_CODE
29    cstat = CURLERR(curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&httpcode));
30#else
31    cstat = curl_easy_getinfo(curl,CURLINFO_HTTP_CODE,&httpcode);
32#endif
33    if(cstat != CURLE_OKhttpcode = 0;
34    return httpcode;
35}
36
37OCerror
38ocfetchurl_file(CURLcurl, const char* urlFILEstream,
39 off_tsizep, long* filetime)
40{
41 int stat = OC_NOERR;
42 CURLcode cstat = CURLE_OK;
43 struct Fetchdata fetchdata;
44
45 /* Set the URL */
46 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_URL, (void*)url));
47 if (cstat != CURLE_OK)
48 goto fail;
49
50 /* send all data to this function  */
51 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_WRITEFUNCTIONWriteFileCallback));
52 if (cstat != CURLE_OK)
53 goto fail;
54
55 /* we pass our file to the callback function */
56 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_WRITEDATA, (void *)&fetchdata));
57 if (cstat != CURLE_OK)
58 goto fail;
59
60        /* One last thing; always try to get the last modified time */
61        cstat = CURLERR(curl_easy_setopt(curlCURLOPT_FILETIME, (long)1));
62 if (cstat != CURLE_OK)
63 goto fail;
64
65 fetchdata.stream = stream;
66 fetchdata.size = 0;
67 cstat = CURLERR(curl_easy_perform(curl));
68
69 if (cstat != CURLE_OK)
70     goto fail;
71
72 if (stat == OC_NOERR) {
73     /* return the file size*/
74#ifdef OCDEBUG
75     oclog(OCLOGNOTE,"filesize: %lu bytes",fetchdata.size);
76#endif
77     if (sizep != NULL)
78 *sizep = fetchdata.size;
79     /* Get the last modified time */
80     if(filetime != NULL)
81                cstat = curl_easy_getinfo(curl,CURLINFO_FILETIME,filetime);
82            if(cstat != CURLE_OK) goto fail;
83 }
84 return OCTHROW(stat);
85
86fail:
87 oclog(OCLOGERR, "curl error: %s", curl_easy_strerror(cstat));
88 return OCTHROW(OC_ECURL);
89}
90
91OCerror
92ocfetchurl(CURLcurl, const char* urlOCbytesbuf, long* filetime,
93           struct OCcredentialscreds)
94{
95 OCerror stat = OC_NOERR;
96 CURLcode cstat = CURLE_OK;
97 size_t len;
98        long httpcode = 0;
99
100 /* Set the URL */
101 cstat = CURLERR(CURLERR(curl_easy_setopt(curlCURLOPT_URL, (void*)url)));
102 if (cstat != CURLE_OK)
103 goto fail;
104
105#if 0
106 if(creds != NULL && creds->password != NULL  && creds->username != NULL) {
107     /* Set user and password */
108#if defined (HAVE_CURLOPT_USERNAME) && defined (HAVE_CURLOPT_PASSWORD)
109     cstat = CURLERR(curl_easy_setopt(curlCURLOPT_USERNAMEcreds->username));
110     if (cstat != CURLE_OK)
111 goto fail;
112     cstat = CURLERR(curl_easy_setopt(curlCURLOPT_PASSWORDcreds->password));
113     if (cstat != CURLE_OK)
114 goto fail;
115#else
116 snprintf(tbuf,1023,"%s:%s",creds->username,creds->password);
117 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_USERPWDtbuf));
118 if (cstat != CURLE_OK)
119 goto fail;
120#endif
121 }
122#endif
123
124 /* send all data to this function  */
125 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_WRITEFUNCTIONWriteMemoryCallback));
126 if (cstat != CURLE_OK)
127 goto fail;
128
129 /* we pass our file to the callback function */
130 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_WRITEDATA, (void*)buf));
131 if (cstat != CURLE_OK)
132 goto fail;
133
134        /* One last thing; always try to get the last modified time */
135 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_FILETIME, (long)1));
136
137 cstat = CURLERR(curl_easy_perform(curl));
138
139 if(cstat == CURLE_PARTIAL_FILE) {
140     /* Log it but otherwise ignore */
141     oclog(OCLOGWARN, "curl error: %s; ignored",
142    curl_easy_strerror(cstat));
143     cstat = CURLE_OK;
144 }
145        httpcode = ocfetchhttpcode(curl);
146
147 if(cstat != CURLE_OK) goto fail;
148
149        /* Get the last modified time */
150 if(filetime != NULL)
151            cstat = CURLERR(curl_easy_getinfo(curl,CURLINFO_FILETIME,filetime));
152        if(cstat != CURLE_OK) goto fail;
153
154 /* Null terminate the buffer*/
155 len = ocbyteslength(buf);
156 ocbytesappend(buf, '\0');
157 ocbytessetlength(buflen); /* dont count null in buffer size*/
158#ifdef OCDEBUG
159 oclog(OCLOGNOTE,"buffersize: %lu bytes",(off_t)ocbyteslength(buf));
160#endif
161
162 return OCTHROW(stat);
163
164fail:
165 oclog(OCLOGERR, "curl error: %s", curl_easy_strerror(cstat));
166 switch (httpcode) {
167 case 401: stat = OC_EAUTH; break;
168 case 404: stat = OC_ENOFILE; break;
169 case 500: stat = OC_EDAPSVC; break;
170 case 200: break;
171 default: stat = OC_ECURL; break;
172 }
173 return OCTHROW(stat);
174}
175
176static size_t
177WriteFileCallback(void* ptr, size_t size, size_t nmemb, void* data)
178{
179 size_t realsize = size * nmemb;
180 size_t count;
181 struct Fetchdatafetchdata;
182 fetchdata = (struct Fetchdata*) data;
183        if(realsize == 0)
184     oclog(OCLOGWARN,"WriteFileCallback: zero sized chunk");
185 count = fwrite(ptrsizenmembfetchdata->stream);
186 if (count > 0) {
187 fetchdata->size += (count * size);
188 } else {
189     oclog(OCLOGWARN,"WriteFileCallback: zero sized write");
190 }
191#ifdef OCPROGRESS
192        oclog(OCLOGNOTE,"callback: %lu bytes",(off_t)realsize);
193#endif
194 return count;
195}
196
197static size_t
198WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
199{
200 size_t realsize = size * nmemb;
201 OCbytesbuf = (OCbytes*) data;
202        if(realsize == 0)
203     oclog(OCLOGWARN,"WriteMemoryCallback: zero sized chunk");
204 /* Optimize for reading potentially large dods datasets */
205 if(!ocbytesavail(buf,realsize)) {
206     /* double the size of the packet */
207     ocbytessetalloc(buf,2*ocbytesalloc(buf));
208 }
209 ocbytesappendn(bufptrrealsize);
210#ifdef OCPROGRESS
211        oclog(OCLOGNOTE,"callback: %lu bytes",(off_t)realsize);
212#endif
213 return realsize;
214}
215
216#if 0
217static void
218assembleurl(DAPURLdurlOCbytesbuf, int what)
219{
220 encodeurltext(durl->url,buf);
221 if(what & WITHPROJ) {
222 ocbytescat(buf,"?");
223 encodeurltext(durl->projection,buf);
224 }
225 if(what & WITHSELencodeurltext(durl->selection,buf);
226
227}
228
229static char mustencode="";
230static char hexchars[16] = {
231 '0', '1', '2', '3',
232 '4', '5', '6', '7',
233 '8', '9', 'a', 'b',
234 'c', 'd', 'e', 'f',
235};
236
237static void
238encodeurltext(char* textOCbytesbuf)
239{
240 /* Encode the URL to handle illegal characters */
241 len = strlen(url);
242 encoded = ocmalloc(len*4+1); /* should never be larger than this*/
243 if(encoded==NULL) return;
244 p = urlq = encoded;
245 while((c=*p++)) {
246 if(strchr(mustencode,c) != NULL) {
247 char tmp[8];
248 int hex1hex2;
249 hex1 = (c & 0x0F);
250 hex2 = (c & 0xF0) >> 4;
251 tmp[0] = '0'; tmp[1] = 'x';
252 tmp[2] = hexchars[hex2]; tmp[3] = hexchars[hex1];
253 tmp[4] = '\0';
254 ocbytescat(buf,tmp);
255 } else *q++ = (char)c;
256 }
257
258}
259
260#endif
261
262OCerror
263occurlopen(CURL** curlp)
264{
265 int stat = OC_NOERR;
266 CURLcode cstat = CURLE_OK;
267 CURLcurl;
268 /* initialize curl*/
269 curl = curl_easy_init();
270 if (curl == NULL)
271 stat = OC_ECURL;
272 else {
273 cstat = CURLERR(curl_easy_setopt(curlCURLOPT_NOPROGRESS, 1));
274 if (cstat != CURLE_OK)
275 stat = OC_ECURL;
276 }
277 if (curlp)
278 *curlp = curl;
279 return OCTHROW(stat);
280}
281
282void
283occurlclose(CURLcurl)
284{
285 if (curl != NULL)
286 curl_easy_cleanup(curl);
287}
288
289OCerror
290ocfetchlastmodified(CURLcurl, char* url, long* filetime)
291{
292    int stat = OC_NOERR;
293    CURLcode cstat = CURLE_OK;
294
295    /* Set the URL */
296    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_URL, (void*)url));
297    if (cstat != CURLE_OK)
298        goto fail;
299
300    /* Ask for head */
301    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_TIMEOUT, 30)); /* 30sec timeout*/
302    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_CONNECTTIMEOUT, 2));
303    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_HEADER, 1));
304    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_NOBODY, 1));
305    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_NOPROGRESS, 1));
306    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_FILETIME, (long)1));
307
308    cstat = CURLERR(curl_easy_perform(curl));
309    if(cstat != CURLE_OK) goto fail;
310    if(filetime != NULL)
311        cstat = CURLERR(curl_easy_getinfo(curl,CURLINFO_FILETIME,filetime));
312    if(cstat != CURLE_OK) goto fail;
313
314    return OCTHROW(stat);
315
316fail:
317    oclog(OCLOGERR, "curl error: %s", curl_easy_strerror(cstat));
318    return OCTHROW(OC_ECURL);
319}
320
321OCerror
322ocping(const char* url)
323{
324    int stat = OC_NOERR;
325    CURLcode cstat = CURLE_OK;
326    CURLcurl = NULL;
327    OCbytesbuf = NULL;
328
329    /* Create a CURL instance */
330    stat = occurlopen(&curl);
331    if(stat != OC_NOERR) return stat;
332
333    /* Use redirects */
334    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_MAXREDIRS, 10L));
335    if (cstat != CURLE_OK)
336        goto done;
337    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_FOLLOWLOCATION, 1L));
338    if (cstat != CURLE_OK)
339        goto done;
340
341    /* use a very short timeout: 10 seconds */
342    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_TIMEOUT, (long)10));
343    if (cstat != CURLE_OK)
344        goto done;
345
346    /* fail on HTTP 400 code errors */
347    cstat = CURLERR(curl_easy_setopt(curlCURLOPT_FAILONERROR, (long)1));
348    if (cstat != CURLE_OK)
349        goto done;
350
351    /* Try to get the file */
352    buf = ocbytesnew();
353    stat = ocfetchurl(curl,url,buf,NULL,NULL);
354    if(stat == OC_NOERR) {
355 /* Don't trust curl to return an error when request gets 404 */
356 long http_code = 0;
357 cstat = CURLERR(curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE, &http_code));
358        if (cstat != CURLE_OK)
359            goto done;
360 if(http_code >= 400) {
361     cstat = CURLE_HTTP_RETURNED_ERROR;
362     goto done;
363 }
364    } else
365        goto done;
366
367done:
368    ocbytesfree(buf);
369    occurlclose(curl);
370    if(cstat != CURLE_OK) {
371        oclog(OCLOGERR, "curl error: %s", curl_easy_strerror(cstat));
372        stat = OC_EDAPSVC;
373    }
374    return OCTHROW(stat);
375}


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