aboutsummaryrefslogtreecommitdiff
path: root/perl/Curl_easy/easy.xs
diff options
context:
space:
mode:
Diffstat (limited to 'perl/Curl_easy/easy.xs')
-rw-r--r--perl/Curl_easy/easy.xs502
1 files changed, 483 insertions, 19 deletions
diff --git a/perl/Curl_easy/easy.xs b/perl/Curl_easy/easy.xs
index c7f19b026..4fff2b332 100644
--- a/perl/Curl_easy/easy.xs
+++ b/perl/Curl_easy/easy.xs
@@ -7,6 +7,17 @@
#include <curl/curl.h>
#include <curl/easy.h>
+#if (LIBCURL_VERSION_NUM<0x070702)
+#define CURLOPT_HEADERFUNCTION 79
+#define header_callback_func write_callback_func
+#else
+#define header_callback_func writeheader_callback_func
+#endif
+
+/* Lists that can be set via curl_easy_setopt() */
+
+static struct curl_slist *httpheader = NULL, *quote = NULL, *postquote = NULL;
+
/* Buffer and varname for option CURLOPT_ERRORBUFFER */
@@ -14,6 +25,341 @@ static char errbuf[CURL_ERROR_SIZE];
static char *errbufvarname = NULL;
+/* Callback functions */
+
+static SV *read_callback = NULL, *write_callback = NULL,
+ *progress_callback = NULL, *passwd_callback = NULL,
+ *header_callback = NULL;
+ /* *closepolicy_callback = NULL; */
+
+
+/* For storing the content */
+
+static char *contbuf = NULL, *bufptr = NULL;
+static int bufsize = 32768, contlen = 0;
+
+
+/* Internal options for this perl module */
+
+#define USE_INTERNAL_VARS 0x01
+
+static int internal_options = 0;
+
+
+/* Setup these global vars */
+
+static void init_globals(void)
+{
+ if (httpheader) curl_slist_free_all(httpheader);
+ if (quote) curl_slist_free_all(quote);
+ if (postquote) curl_slist_free_all(postquote);
+ httpheader = quote = postquote = NULL;
+ if (errbufvarname) free(errbufvarname);
+ errbufvarname = NULL;
+ if (contbuf == NULL) {
+ contbuf = malloc(bufsize + 1);
+ }
+ bufptr = contbuf;
+ *bufptr = '\0';
+ contlen = 0;
+ internal_options = 0;
+}
+
+
+/* Register a callback function */
+
+static void register_callback(SV **callback, SV *function)
+{
+ if (*callback == NULL) {
+ /* First time, create new SV */
+ *callback = newSVsv(function);
+ } else {
+ /* Been there, done that. Just overwrite the SV */
+ SvSetSV(*callback, function);
+ }
+}
+
+/* generic fwrite callback, which decides which callback to call */
+static size_t
+fwrite_wrapper (const void *ptr,
+ size_t size,
+ size_t nmemb,
+ void *stream,
+ void *call_function)
+{
+ dSP ;
+ int count,status;
+ SV *sv;
+
+ if (call_function) {
+ /* then we are doing a callback to perl */
+
+ ENTER ;
+ SAVETMPS ;
+
+ PUSHMARK(SP) ;
+
+ if (stream == stdout) {
+ sv = newSViv(0); /* FIXME: should cast stdout to GLOB somehow? */
+ } else { /* its already an SV */
+ sv = stream;
+ }
+
+ if (ptr != NULL) {
+ XPUSHs(sv_2mortal(newSVpvn(ptr, size * nmemb)));
+ } else {
+ XPUSHs(sv_2mortal(newSVpv("",0)));
+ }
+ XPUSHs(sv_2mortal(newSVsv(sv))); /* CURLOPT_FILE SV* */
+ PUTBACK ;
+
+ count = call_sv((SV *)call_function, G_SCALAR);
+
+ SPAGAIN;
+ if (count != 1)
+ croak("Big trouble, perl_call_sv(write_callback) didn't return status\n");
+
+ status = POPi;
+
+ PUTBACK ;
+
+ FREETMPS ;
+ LEAVE ;
+ return status;
+
+ } else {
+ /* default to a normal 'fwrite' */
+ /* stream could be a FILE * or an SV * */
+ FILE *f;
+
+ if (stream == stdout) { /* the only possible FILE ? Think so*/
+ f = stream;
+ } else { /* its a GLOB */
+ f = IoIFP(sv_2io(stream)); /* may barf if not a GLOB */
+ }
+
+ return fwrite(ptr,size,nmemb,f);
+ }
+}
+
+/* Write callback for calling a perl callback */
+size_t
+write_callback_func( const void *ptr, size_t size,
+ size_t nmemb, void *stream)
+{
+ return fwrite_wrapper(ptr,size,nmemb,stream,
+ write_callback);
+}
+
+/* header callback for calling a perl callback */
+size_t
+writeheader_callback_func( const void *ptr, size_t size,
+ size_t nmemb, void *stream)
+{
+ return fwrite_wrapper(ptr,size,nmemb,stream,
+ header_callback);
+}
+
+size_t
+read_callback_func( void *ptr, size_t size,
+ size_t nmemb, void *stream)
+{
+ dSP ;
+
+ int count;
+ SV *sv;
+ STRLEN len;
+ size_t maxlen,mylen;
+ char *p;
+
+ maxlen = size*nmemb;
+
+ if (read_callback) {
+ /* we are doing a callback to perl */
+
+ ENTER ;
+ SAVETMPS ;
+
+ PUSHMARK(SP) ;
+
+ if (stream == stdin) {
+ sv = newSViv(0); /* should cast stdin to GLOB somehow? */
+ } else { /* its an SV */
+ sv = stream;
+ }
+
+ XPUSHs(sv_2mortal(newSViv(maxlen))); /* send how many bytes please */
+ XPUSHs(sv_2mortal(newSVsv(sv))); /* CURLOPT_INFILE SV* */
+ PUTBACK ;
+
+ count = call_sv(read_callback, G_SCALAR);
+
+ SPAGAIN;
+ if (count != 1)
+ croak("Big trouble, perl_call_sv(read_callback) didn't return data\n");
+
+ sv = POPs;
+ p = SvPV(sv,len);
+
+ /* only allowed to return the number of bytes asked for */
+ mylen = len<maxlen ? len : maxlen;
+ memcpy(ptr,p,(size_t)mylen);
+ PUTBACK ;
+
+ FREETMPS ;
+ LEAVE ;
+ return (size_t) (mylen/size);
+
+ } else {
+ /* default to a normal 'fread' */
+ /* stream could be a FILE * or an SV * */
+ FILE *f;
+
+ if (stream == stdin) { /* the only possible FILE ? Think so*/
+ f = stream;
+ } else { /* its a GLOB */
+ f = IoIFP(sv_2io(stream)); /* may barf if not a GLOB */
+ }
+
+ return fread(ptr,size,nmemb,f);
+ }
+}
+
+/* Porgress callback for calling a perl callback */
+
+static int progress_callback_func(void *clientp, size_t dltotal, size_t dlnow,
+ size_t ultotal, size_t ulnow)
+{
+ dSP;
+ int count;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(sp);
+ if (clientp != NULL) {
+ XPUSHs(sv_2mortal(newSVpv(clientp, 0)));
+ } else {
+ XPUSHs(sv_2mortal(newSVpv("", 0)));
+ }
+ XPUSHs(sv_2mortal(newSViv(dltotal)));
+ XPUSHs(sv_2mortal(newSViv(dlnow)));
+ XPUSHs(sv_2mortal(newSViv(ultotal)));
+ XPUSHs(sv_2mortal(newSViv(ulnow)));
+ PUTBACK;
+ count = perl_call_sv(progress_callback, G_SCALAR);
+ SPAGAIN;
+ if (count != 1)
+ croak("Big trouble, perl_call_sv(progress_callback) didn't return 1\n");
+ count = POPi;
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return count;
+}
+
+
+/* Password callback for calling a perl callback */
+
+static int passwd_callback_func(void *clientp, char *prompt, char *buffer,
+ int buflen)
+{
+ dSP;
+ int count;
+ SV *sv;
+ STRLEN len;
+ size_t mylen;
+ char *p;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(sp);
+ if (clientp != NULL) {
+ XPUSHs(sv_2mortal(newSVsv(clientp)));
+ } else {
+ XPUSHs(sv_2mortal(newSVpv("", 0)));
+ }
+ XPUSHs(sv_2mortal(newSVpv(prompt, 0)));
+ XPUSHs(sv_2mortal(newSViv(buflen)));
+ PUTBACK;
+ count = perl_call_sv(passwd_callback, G_ARRAY);
+ SPAGAIN;
+ if (count != 2)
+ croak("Big trouble, perl_call_sv(passwd_callback) didn't return status + data\n");
+
+ sv = POPs;
+ count = POPi;
+
+ p = SvPV(sv,len);
+
+ /* only allowed to return the number of bytes asked for */
+ mylen = len<(buflen-1) ? len : (buflen-1);
+ memcpy(buffer,p,mylen);
+ buffer[buflen]=0; /* ensure C string terminates */
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return count;
+}
+
+
+#if 0
+/* awaiting closepolicy prototype */
+int
+closepolicy_callback_func(void *clientp)
+{
+ dSP;
+ int argc, status;
+ SV *pl_status;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+ PUTBACK;
+
+ argc = call_sv(closepolicy_callback, G_SCALAR);
+ SPAGAIN;
+
+ if (argc != 1) {
+ croak
+ ("Unexpected number of arguments returned from closefunction callback\n");
+ }
+ pl_status = POPs;
+ status = SvTRUE(pl_status) ? 0 : 1;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+
+ return status;
+}
+#endif
+
+
+
+/* Internal write callback. Only used if USE_INTERNAL_VARS was specified */
+
+static size_t internal_write_callback(char *data, size_t size, size_t num,
+ FILE *fp)
+{
+ int i;
+
+ size *= num;
+ if ((contlen + size) >= bufsize) {
+ bufsize *= 2;
+ contbuf = realloc(contbuf, bufsize + 1);
+ bufptr = contbuf + contlen;
+ }
+ contlen += size;
+ for (i = 0; i < size; i++) {
+ *bufptr++ = *data++;
+ }
+ *bufptr = '\0';
+ return size;
+}
+
+
static int
constant(char *name, int arg)
{
@@ -97,6 +443,7 @@ constant(char *name, int arg)
case 'G':
case 'H':
if (strEQ(name, "HEADER")) return CURLOPT_HEADER;
+ if (strEQ(name, "HEADERFUNCTION")) return CURLOPT_HEADERFUNCTION;
if (strEQ(name, "HTTPHEADER")) return CURLOPT_HTTPHEADER;
if (strEQ(name, "HTTPPOST")) return CURLOPT_HTTPPOST;
if (strEQ(name, "HTTPPROXYTUNNEL")) return CURLOPT_HTTPPROXYTUNNEL;
@@ -124,6 +471,8 @@ constant(char *name, int arg)
break;
case 'O':
case 'P':
+ if (strEQ(name, "PASSWDDATA")) return CURLOPT_PASSWDDATA;
+ if (strEQ(name, "PASSWDFUNCTION")) return CURLOPT_PASSWDFUNCTION;
if (strEQ(name, "PORT")) return CURLOPT_PORT;
if (strEQ(name, "POST")) return CURLOPT_POST;
if (strEQ(name, "POSTFIELDS")) return CURLOPT_POSTFIELDS;
@@ -173,12 +522,13 @@ constant(char *name, int arg)
break;
}
}
+ if (strEQ(name, "USE_INTERNAL_VARS")) return USE_INTERNAL_VARS;
errno = EINVAL;
return 0;
}
-MODULE = Curl::easy PACKAGE = Curl::easy
+MODULE = Curl::easy PACKAGE = Curl::easy PREFIX = curl_easy_
int
constant(name,arg)
@@ -189,56 +539,167 @@ constant(name,arg)
void *
curl_easy_init()
CODE:
- if (errbufvarname) free(errbufvarname);
- errbufvarname = NULL;
+ init_globals();
RETVAL = curl_easy_init();
+ curl_easy_setopt(RETVAL, CURLOPT_HEADERFUNCTION, header_callback_func);
+ curl_easy_setopt(RETVAL, CURLOPT_WRITEFUNCTION, write_callback_func);
OUTPUT:
RETVAL
+char *
+curl_easy_version()
+CODE:
+ RETVAL=curl_version();
+OUTPUT:
+ RETVAL
int
curl_easy_setopt(curl, option, value)
void * curl
int option
-char * value
+SV * value
CODE:
if (option < CURLOPTTYPE_OBJECTPOINT) {
+
/* This is an option specifying an integer value: */
- long value = (long)SvIV(ST(2));
- RETVAL = curl_easy_setopt(curl, option, value);
+ RETVAL = curl_easy_setopt(curl, option, (long)SvIV(value));
+
} else if (option == CURLOPT_FILE || option == CURLOPT_INFILE ||
- option == CURLOPT_WRITEHEADER) {
- /* This is an option specifying a FILE * value: */
- FILE * value = IoIFP(sv_2io(ST(2)));
- RETVAL = curl_easy_setopt(curl, option, value);
+ option == CURLOPT_WRITEHEADER || option == CURLOPT_PROGRESSDATA ||
+ option == CURLOPT_PASSWDDATA) {
+ /* This is an option specifying an SV * value: */
+ RETVAL = curl_easy_setopt(curl, option, newSVsv(ST(2)));
+
} else if (option == CURLOPT_ERRORBUFFER) {
- SV *sv;
+ /* Pass in variable name for storing error messages... */
RETVAL = curl_easy_setopt(curl, option, errbuf);
if (errbufvarname) free(errbufvarname);
- errbufvarname = strdup(value);
- sv = perl_get_sv(errbufvarname, TRUE | GV_ADDMULTI);
+ errbufvarname = strdup((char *)SvPV(value, PL_na));
+
} else if (option == CURLOPT_WRITEFUNCTION || option ==
- CURLOPT_READFUNCTION || option == CURLOPT_PROGRESSFUNCTION) {
+ CURLOPT_READFUNCTION || option == CURLOPT_PROGRESSFUNCTION ||
+ option == CURLOPT_PASSWDFUNCTION || option == CURLOPT_HEADERFUNCTION) {
/* This is an option specifying a callback function */
- /* not yet implemented */
+ switch (option) {
+ case CURLOPT_WRITEFUNCTION:
+ register_callback(&write_callback, value);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback_func);
+ break;
+ case CURLOPT_READFUNCTION:
+ register_callback(&read_callback, value);
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback_func);
+ break;
+ case CURLOPT_HEADERFUNCTION:
+ register_callback(&header_callback, value);
+ curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback_func);
+ case CURLOPT_PROGRESSFUNCTION:
+ register_callback(&progress_callback, value);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_callback_func);
+ break;
+ case CURLOPT_PASSWDFUNCTION:
+ register_callback(&passwd_callback, value);
+ curl_easy_setopt(curl, CURLOPT_PASSWDFUNCTION, passwd_callback_func);
+ break;
+ /* awaiting a prototype for the closepolicy function callback
+ case CURLOPT_CLOSEFUNCTION:
+ register_callback(&closepolicy_callback, value);
+ curl_easy_setopt(curl, CURLOPT_CLOSEFUNCTION, closepolicy_callback_func);
+ break;
+ */
+ }
RETVAL = -1;
+
+ } else if (option == CURLOPT_HTTPHEADER || option == CURLOPT_QUOTE ||
+ option == CURLOPT_POSTQUOTE) {
+ /* This is an option specifying a list of curl_slist structs: */
+ AV *array = (AV *)SvRV(value);
+ struct curl_slist **slist = NULL;
+ /* We have to find out which list to use... */
+ switch (option) {
+ case CURLOPT_HTTPHEADER:
+ slist = &httpheader; break;
+ case CURLOPT_QUOTE:
+ slist = &quote; break;
+ case CURLOPT_POSTQUOTE:
+ slist = &postquote; break;
+ }
+ /* ...store the values into it... */
+ for (;;) {
+ SV *sv = av_shift(array);
+ int len = 0;
+ char *str = SvPV(sv, len);
+ if (len == 0) break;
+ *slist = curl_slist_append(*slist, str);
+ }
+ /* ...and pass the list into curl_easy_setopt() */
+ RETVAL = curl_easy_setopt(curl, option, *slist);
} else {
- /* default, option specifying a char * value: */
- RETVAL = curl_easy_setopt(curl, option, value);
+ /* This is an option specifying a char * value: */
+ RETVAL = curl_easy_setopt(curl, option, SvPV(value, PL_na));
}
OUTPUT:
RETVAL
int
+internal_setopt(option, value)
+int option
+int value
+CODE:
+ if (value == 1) {
+ internal_options |= option;
+ } else {
+ internal_options &= !option;
+ }
+ RETVAL = 0;
+OUTPUT:
+ RETVAL
+
+
+int
curl_easy_perform(curl)
void * curl
CODE:
+ if (internal_options & USE_INTERNAL_VARS) {
+ /* Use internal callback which just stores the content into a buffer. */
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, internal_write_callback);
+ curl_easy_setopt(curl, CURLOPT_HEADER, 1);
+ }
RETVAL = curl_easy_perform(curl);
if (RETVAL && errbufvarname) {
+ /* If an error occurred and a varname for error messages has been
+ specified, store the error message. */
SV *sv = perl_get_sv(errbufvarname, TRUE | GV_ADDMULTI);
sv_setpv(sv, errbuf);
}
+ if (!RETVAL && (internal_options & USE_INTERNAL_VARS)) {
+ /* No error and internal variable for the content are to be used:
+ Split the data into headers and content and store them into
+ perl variables. */
+ SV *head_sv = perl_get_sv("Curl::easy::headers", TRUE | GV_ADDMULTI);
+ SV *cont_sv = perl_get_sv("Curl::easy::content", TRUE | GV_ADDMULTI);
+ char *p = contbuf;
+ int nl = 0, found = 0;
+ while (p < bufptr) {
+ if (nl && (*p == '\n' || *p == '\r')) {
+ /* found empty line, end of headers */
+ *p++ = '\0';
+ sv_setpv(head_sv, contbuf);
+ while (*p == '\n' || *p == '\r') {
+ p++;
+ }
+ sv_setpv(cont_sv, p);
+ found = 1;
+ break;
+ }
+ nl = (*p == '\n');
+ p++;
+ }
+ if (!found) {
+ sv_setpv(head_sv, "");
+ sv_setpv(cont_sv, contbuf);
+ }
+ }
OUTPUT:
RETVAL
@@ -249,6 +710,10 @@ void * curl
int option
double value
CODE:
+#ifdef __GNUC__
+ /* a(void) warnig about unnused variable */
+ (void) value;
+#endif
switch (option & CURLINFO_TYPEMASK) {
case CURLINFO_STRING: {
char * value = (char *)SvPV(ST(2), PL_na);
@@ -282,8 +747,7 @@ curl_easy_cleanup(curl)
void * curl
CODE:
curl_easy_cleanup(curl);
- if (errbufvarname) free(errbufvarname);
- errbufvarname = NULL;
+ init_globals();
RETVAL = 0;
OUTPUT:
RETVAL