netcdf-c/oc/http.c
2010-06-03 13:24:43 +00:00

320 lines
7.9 KiB
C

/* Copyright 2009, UCAR/Unidata and OPeNDAP, Inc.
See the COPYRIGHT file for more information. */
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include "ocinternal.h"
#include "ocdebug.h"
#include "http.h"
#include "rc.h"
static size_t WriteFileCallback(void*, size_t, size_t, void*);
static size_t WriteMemoryCallback(void*, size_t, size_t, void*);
static int ocsetcurlproperties(CURL* curl, const char*);
struct Fetchdata {
FILE* stream;
size_t size;
};
long
ocfetchhttpcode(CURL* curl)
{
long httpcode;
CURLcode cstat = CURLE_OK;
/* Extract the http code */
cstat = curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&httpcode);
if(cstat != CURLE_OK) httpcode = 0;
return httpcode;
}
int
ocfetchurl_file(CURL* curl, char* url, FILE* stream,
unsigned long* sizep, long* filetime)
{
int stat = OC_NOERR;
CURLcode cstat = CURLE_OK;
struct Fetchdata fetchdata;
if((stat = ocsetcurlproperties(curl,url)) != OC_NOERR) goto fail;
/* Set the URL */
cstat = curl_easy_setopt(curl, CURLOPT_URL, (void*)url);
if (cstat != CURLE_OK)
goto fail;
/* send all data to this function */
cstat = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteFileCallback);
if (cstat != CURLE_OK)
goto fail;
/* we pass our file to the callback function */
cstat = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&fetchdata);
if (cstat != CURLE_OK)
goto fail;
/* One last thing; always try to get the last modified time */
cstat = curl_easy_setopt(curl, CURLOPT_FILETIME, (long)1);
fetchdata.stream = stream;
fetchdata.size = 0;
cstat = curl_easy_perform(curl);
if (cstat != CURLE_OK) {
goto fail;
}
if (stat == OC_NOERR) {
/* return the file size*/
if (sizep != NULL)
*sizep = fetchdata.size;
/* Get the last modified time */
if(filetime != NULL)
cstat = curl_easy_getinfo(curl,CURLINFO_FILETIME,filetime);
if(cstat != CURLE_OK) goto fail;
}
return THROW(stat);
fail: oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
return THROW(OC_ECURL);
}
int
ocfetchurl(CURL* curl, char* url, OCbytes* buf, long* filetime)
{
int stat = OC_NOERR;
CURLcode cstat = CURLE_OK;
size_t len;
if((stat = ocsetcurlproperties(curl,url)) != OC_NOERR) goto fail;
/* Set the URL */
cstat = curl_easy_setopt(curl, CURLOPT_URL, (void*)url);
if (cstat != CURLE_OK)
goto fail;
/* send all data to this function */
cstat = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
if (cstat != CURLE_OK)
goto fail;
/* we pass our file to the callback function */
cstat = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)buf);
if (cstat != CURLE_OK)
goto fail;
/* One last thing; always try to get the last modified time */
cstat = curl_easy_setopt(curl, CURLOPT_FILETIME, (long)1);
cstat = curl_easy_perform(curl);
if(cstat == CURLE_PARTIAL_FILE) {
/* Log it but otherwise ignore */
oc_log(LOGWARN, "curl error: %s; ignored",
curl_easy_strerror(cstat));
cstat = CURLE_OK;
}
if(cstat != CURLE_OK) goto fail;
/* Get the last modified time */
if(filetime != NULL)
cstat = curl_easy_getinfo(curl,CURLINFO_FILETIME,filetime);
if(cstat != CURLE_OK) goto fail;
/* Null terminate the buffer*/
len = ocbyteslength(buf);
ocbytesappend(buf, '\0');
ocbytessetlength(buf, len); /* dont count null in buffer size*/
return THROW(stat);
fail:
oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
return THROW(OC_ECURL);
}
static size_t
WriteFileCallback(void* ptr, size_t size, size_t nmemb, void* data)
{
size_t count;
struct Fetchdata* fetchdata;
fetchdata = (struct Fetchdata*) data;
count = fwrite(ptr, size, nmemb, fetchdata->stream);
if (count > 0) {
fetchdata->size += (count * size);
}
#ifdef OCPROGRESS
oc_log(LOGNOTE,"callback: %lu bytes",(unsigned long)(size*nmemb));
#endif
return count;
}
static size_t
WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
{
size_t realsize = size * nmemb;
OCbytes* buf = (OCbytes*) data;
if(realsize == 0)
oc_log(LOGWARN,"WriteMemoryCallback: zero sized chunk");
/* Optimize for reading potentially large dods datasets */
if(!ocbytesavail(buf,realsize)) {
/* double the size of the packet */
ocbytessetalloc(buf,2*ocbytesalloc(buf));
}
ocbytesappendn(buf, ptr, realsize);
return realsize;
}
#if 0
static void
assembleurl(DAPURL* durl, OCbytes* buf, int what)
{
encodeurltext(durl->url,buf);
if(what & WITHPROJ) {
ocbytescat(buf,"?");
encodeurltext(durl->projection,buf);
}
if(what & WITHSEL) encodeurltext(durl->selection,buf);
}
static char mustencode="";
static char hexchars[16] = {
'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'a', 'b',
'c', 'd', 'e', 'f',
};
static void
encodeurltext(char* text, OCbytes* buf)
{
/* Encode the URL to handle illegal characters */
len = strlen(url);
encoded = ocmalloc(len*4+1); /* should never be larger than this*/
if(encoded==NULL) return;
p = url; q = encoded;
while((c=*p++)) {
if(strchr(mustencode,c) != NULL) {
char tmp[8];
int hex1, hex2;
hex1 = (c & 0x0F);
hex2 = (c & 0xF0) >> 4;
tmp[0] = '0'; tmp[1] = 'x';
tmp[2] = hexchars[hex2]; tmp[3] = hexchars[hex1];
tmp[4] = '\0';
ocbytescat(buf,tmp);
} else *q++ = (char)c;
}
}
#endif
int
occurlopen(CURL** curlp)
{
int stat = OC_NOERR;
CURLcode cstat;
CURL* curl;
/* initialize curl*/
curl = curl_easy_init();
if (curl == NULL)
stat = OC_ECURL;
else {
cstat = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
if (cstat != CURLE_OK)
stat = OC_ECURL;
/* some servers don't like requests that are made without a user-agent */
cstat = curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
if (cstat != CURLE_OK)
stat = OC_ECURL;
}
if (curlp)
*curlp = curl;
return THROW(stat);
}
void
occurlclose(CURL* curl)
{
if (curl != NULL)
curl_easy_cleanup(curl);
}
static int
ocsetcurlproperties(CURL* curl, const char* url)
{
CURLcode cstat = CURLE_OK;
/* These conditionals look for value in four globals set when the
* .dodsrc file was read.
*/
if (dods_verify) {
if (set_verify(curl) != OC_NOERR)
goto fail;
}
if (dods_compress) {
if (set_compression(curl) != OC_NOERR)
goto fail;
}
if (pstructProxy) {
if (set_proxy(curl, pstructProxy) != OC_NOERR)
goto fail;
}
if (cook) {
if (set_cookies(curl, cook) != OC_NOERR)
goto fail;
}
if (credentials_in_url(url)) {
char *result_url = NULL;
if (extract_credentials(url, &userName, &password, &result_url) != OC_NOERR)
goto fail;
url = result_url;
}
if (userName && password) {
if (set_user_password(curl, userName, password) != OC_NOERR)
goto fail;
}
return OC_NOERR;
fail:
oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
return THROW(OC_ECURL);
}
int
ocfetchlastmodified(CURL* curl, char* url, long* filetime)
{
int stat = OC_NOERR;
CURLcode cstat = CURLE_OK;
if((stat = ocsetcurlproperties(curl,url)) != OC_NOERR) goto fail;
/* Set the URL */
cstat = curl_easy_setopt(curl, CURLOPT_URL, (void*)url);
if (cstat != CURLE_OK)
goto fail;
/* Ask for head */
cstat = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); /* 30sec timeout*/
cstat = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 2);
cstat = curl_easy_setopt(curl, CURLOPT_HEADER, 1);
cstat = curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
cstat = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
cstat = curl_easy_setopt(curl, CURLOPT_FILETIME, (long)1);
cstat = curl_easy_perform(curl);
if(cstat != CURLE_OK) goto fail;
if(filetime != NULL)
cstat = curl_easy_getinfo(curl,CURLINFO_FILETIME,filetime);
if(cstat != CURLE_OK) goto fail;
return THROW(stat);
fail:
oc_log(LOGERR, "curl error: %s", curl_easy_strerror(cstat));
return THROW(OC_ECURL);
}