/**********************************************************************/ /* wrap some common curl operations for use with OpenCOBOL 1.1 */ /* Author: Brian Tiffin */ /* Date: 21-July-2008, 24-Nov-2008 */ /* Version: 0.5 */ /* Purpose: Provide some net access to OpenCOBOL */ /* Tectonics: gcc -c occurl.c */ /* with libcurl dev installed */ /**********************************************************************/ #include #include #include /* A release bump changed/merged some error codes, and this is a hack */ #ifndef CURLE_REMOTE_FILE_NOT_FOUND #define CURLE_REMOTE_FILE_NOT_FOUND 78 #endif /* Support structure for the file callbacks */ struct LocalFileStruc { const char *filename; FILE *stream; }; /* Progress tracking */ double *Bar; /* libcurl call back for file write */ static size_t wrap_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) { struct LocalFileStruc *out=(struct LocalFileStruc *)stream; if(out && !out->stream) { /* open file for writing */ out->stream=fopen(out->filename, "wb"); if(!out->stream) return 0; /* failure, can't open file to write */ } return fwrite(buffer, size, nmemb, out->stream); } /* Progress */ int progress_callback(char *Bar, double t, double d, double ultotal, double ulnow) { int oe; double val; if (t == 0) t = 1.0; val = d / t * 100; oe = (int)val; if (oe & 1) { putc('\\', stdout); } else { putc('/', stdout); } printf("%03.0f%%\b\b\b\b\b\b\b\b", val); fflush(stdout); return 0; } /* Routines to get and release CURL handles from OpenCOBOL */ /* Usage: */ /* DATA DIVISION. */ /* 01 handle usage is pointer. */ /* PROCEDURE DIVISION. */ /* CALL "occurl_init" RETURNING handle. */ CURL* CBL_OC_CURL_INIT() { return curl_easy_init(); } /* Usage: */ /* CALL "occurl_cleanup" USING BY VALUE handle. */ void CBL_OC_CURL_CLEANUP(CURL *curl) { curl_easy_cleanup(curl); } /* Set verbosity */ /* Usage: vflag being 0 or 1 */ /* CALL "CBL_OC_CURL_VERBOSE" USING BY VALUE vflag. */ void CBL_OC_CURL_VERBOSE(CURL *curl, int vflag) { /* Switch on or off full protocol/debug output */ if (curl) { curl_easy_setopt(curl, CURLOPT_VERBOSE, (long)vflag); } } /* Set progress display on/off */ /* Usage: pflag being 0 or 1 */ /* CALL "CBL_OC_CURL_PROGRESS" USING BY VALUE pflag. */ void CBL_OC_CURL_PROGRESS(CURL *curl, int pflag, int (*pcallback)(char *, double, double, double, double)) { /* Switch on or off progress display */ if (curl) { if (pflag) { curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, pcallback); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &Bar); } else { curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, NULL); curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, NULL); } } } /* Ease of use; check mod-times, if since then read url, write to file */ /* Retrieve URL and save to local file after checking timestamps */ /* Usage: */ /* DATA DIVISION. */ /* 01 handle USAGE IS POINTER. */ /* 01 url. */ /* 02 urlname PIC x(21) VALUE "http://opencobol.org/". */ /* 02 filler PIC x VALUE X"00". */ /* 01 filename */ /* 02 PIC x(). */ /* 02 filler pic x value low-value. */ /* 01 modtime USAGE IS BINARY-C-LONG */ /* PROCEDURE DIVISION. */ /* CALL "CBL_OC_CURL_RETRIEVE_FILE" USING BY VALUE handle */ /* BY REFERENCE url */ /* BY REFERENCE filename */ /* BY REFERENCE modtime */ /* RETURNING result. */ /* Pass modtime of 0 to get local mtime field, */ /* modtime is modified with value from url if available */ int CBL_OC_CURL_RETRIEVE_FILE(CURL *curl, char *url, char *file, long *modtime) { CURLcode res; long urlstamp; struct stat st; struct LocalFileStruc localfile={ "occurl.default", /* default filename */ NULL /* stream */ }; /* point to the COBOL passed filename */ if (file) { localfile.filename = file; } /* if modtime is zero, get local file modtime */ if (*modtime == 0) { if (stat (file, &st) == 0) { *modtime = st.st_mtime; } } /* let libcurl do all the real work */ if (curl) { curl_easy_setopt(curl, CURLOPT_URL, url); /* Only fetch new */ curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE); curl_easy_setopt(curl, CURLOPT_TIMEVALUE, *modtime); /* Define our callback to get called when there's data to be written */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wrap_fwrite); /* Set a pointer to our struct to pass to the callback */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &localfile); /* After all the setopts, perform the operation */ res = curl_easy_perform(curl); /* close off any file pointers */ if (localfile.stream) { fclose(localfile.stream); /* close the local file */ } /* return error results */ if (res != 0) { return (int)res; } /* retrieve response code */ res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &urlstamp); if (res != 0) { return (int)res; } else { if (urlstamp == 404) { return CURLE_REMOTE_FILE_NOT_FOUND; } } /* retrieve filetime for return value */ res = curl_easy_getinfo(curl, CURLINFO_FILETIME, &urlstamp); if (res != 0) { return (int)res; } else { *modtime = urlstamp; return 0; } } else { return CURLE_FAILED_INIT; } return 0; } /* Fetch a url to a local file */ int CBL_OC_CURL_GET_URL(CURL *curl, char *theurl, char *thefile) { CURLcode res; long urlstamp; struct LocalFileStruc localfile = { "occurl.default", /* default filename */ NULL /* stream */ }; /* point to the COBOL field */ localfile.filename = thefile; if (curl) { curl_easy_setopt(curl, CURLOPT_URL, theurl); curl_easy_setopt(curl, CURLOPT_FILETIME, 1L); /* Define our callback to get called when there's data to be written */ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wrap_fwrite); /* Set a pointer to our struct to pass to the callback */ curl_easy_setopt(curl, CURLOPT_WRITEDATA, &localfile); res = curl_easy_perform(curl); /* close the local file */ if (localfile.stream) { fclose(localfile.stream); } /* return error results */ if (res != 0) { return (int)res; } /* retrieve response code */ res = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &urlstamp); if (res != 0) { return (int)res; } else { if (urlstamp == 404) { return CURLE_REMOTE_FILE_NOT_FOUND; } } } else { return CURLE_FAILED_INIT; } return 0; } /* The plan is to return a structure with all curl INFO fields filled */ int CBL_OC_CURL_GETINFO(CURL *curl, char *theurl, char *thedata) { CURLcode res; long urlstamp; if (curl) { curl_easy_setopt(curl, CURLOPT_URL, theurl); res = curl_easy_getinfo(curl, CURLINFO_FILETIME, &urlstamp); } else { return -1; } return (int)res; }