summaryrefslogtreecommitdiff
path: root/Curl.xs
diff options
context:
space:
mode:
Diffstat (limited to 'Curl.xs')
-rw-r--r--Curl.xs1190
1 files changed, 1190 insertions, 0 deletions
diff --git a/Curl.xs b/Curl.xs
new file mode 100644
index 0000000..3a33686
--- /dev/null
+++ b/Curl.xs
@@ -0,0 +1,1190 @@
+
+/*
+ * Perl interface for libcurl. Check out the file README for more info.
+ */
+
+/*
+ * Copyright (C) 2000, 2001, 2002, 2005, 2008 Daniel Stenberg, Cris Bailiff, et al.
+ * You may opt to use, copy, modify, merge, publish, distribute and/or
+ * sell copies of the Software, and permit persons to whom the
+ * Software is furnished to do so, under the terms of the MPL or
+ * the MIT/X-derivate licenses. You may pick one of these licenses.
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#include <curl/curl.h>
+#include <curl/easy.h>
+#include <curl/multi.h>
+
+#define header_callback_func writeheader_callback_func
+
+/* Do a favor for older perl versions */
+#ifndef Newxz
+# define Newxz(v,n,t) Newz(0,v,n,t)
+#endif
+
+typedef enum {
+ CALLBACK_WRITE = 0,
+ CALLBACK_READ,
+ CALLBACK_HEADER,
+ CALLBACK_PROGRESS,
+ CALLBACK_DEBUG,
+ CALLBACK_LAST
+} perl_curl_easy_callback_code;
+
+typedef enum {
+ SLIST_HTTPHEADER = 0,
+ SLIST_QUOTE,
+ SLIST_POSTQUOTE,
+ SLIST_LAST
+} perl_curl_easy_slist_code;
+
+
+typedef struct {
+ /* The main curl handle */
+ struct CURL *curl;
+ I32 *y;
+ /* Lists that can be set via curl_easy_setopt() */
+ struct curl_slist *slist[SLIST_LAST];
+ SV *callback[CALLBACK_LAST];
+ SV *callback_ctx[CALLBACK_LAST];
+
+ /* copy of error buffer var for caller*/
+ char errbuf[CURL_ERROR_SIZE+1];
+ char *errbufvarname;
+
+} perl_curl_easy;
+
+
+typedef struct {
+ struct HttpPost * post;
+ struct HttpPost * last;
+} perl_curl_form;
+
+
+typedef struct {
+#ifdef __CURL_MULTI_H
+ struct CURLM *curlm;
+#else
+ struct void *curlm;
+#endif
+} perl_curl_multi;
+
+typedef struct {
+ struct CURLSH *curlsh;
+} perl_curl_share;
+
+
+/* switch from curl option codes to the relevant callback index */
+static perl_curl_easy_callback_code callback_index(int option)
+{
+ switch(option) {
+ case CURLOPT_WRITEFUNCTION:
+ case CURLOPT_FILE:
+ return CALLBACK_WRITE;
+ break;
+
+ case CURLOPT_READFUNCTION:
+ case CURLOPT_INFILE:
+ return CALLBACK_READ;
+ break;
+
+ case CURLOPT_HEADERFUNCTION:
+ case CURLOPT_WRITEHEADER:
+ return CALLBACK_HEADER;
+ break;
+
+ case CURLOPT_PROGRESSFUNCTION:
+ case CURLOPT_PROGRESSDATA:
+ return CALLBACK_PROGRESS;
+ break;
+ case CURLOPT_DEBUGFUNCTION:
+ case CURLOPT_DEBUGDATA:
+ return CALLBACK_DEBUG;
+ break;
+ }
+ croak("Bad callback index requested\n");
+ return CALLBACK_LAST;
+}
+
+/* switch from curl slist names to an slist index */
+static perl_curl_easy_slist_code slist_index(int option)
+{
+ switch(option) {
+ case CURLOPT_HTTPHEADER:
+ return SLIST_HTTPHEADER;
+ break;
+ case CURLOPT_QUOTE:
+ return SLIST_QUOTE;
+ break;
+ case CURLOPT_POSTQUOTE:
+ return SLIST_POSTQUOTE;
+ break;
+ }
+ croak("Bad slist index requested\n");
+ return SLIST_LAST;
+}
+
+static perl_curl_easy * perl_curl_easy_new()
+{
+ perl_curl_easy *self;
+ Newz(1, self, 1, perl_curl_easy);
+ if (!self)
+ croak("out of memory");
+ self->curl=curl_easy_init();
+ return self;
+}
+
+static perl_curl_easy * perl_curl_easy_duphandle(perl_curl_easy *orig)
+{
+ perl_curl_easy *self;
+ Newz(1, self, 1, perl_curl_easy);
+ if (!self)
+ croak("out of memory");
+ self->curl=curl_easy_duphandle(orig->curl);
+ return self;
+}
+
+static void perl_curl_easy_delete(perl_curl_easy *self)
+{
+ perl_curl_easy_slist_code index;
+ perl_curl_easy_callback_code i;
+
+ if (self->curl)
+ curl_easy_cleanup(self->curl);
+
+ *self->y = *self->y - 1;
+ if (*self->y <= 0) {
+ for (index=0;index<SLIST_LAST;index++) {
+ if (self->slist[index]) curl_slist_free_all(self->slist[index]);
+ };
+ Safefree(self->y);
+ }
+ for(i=0;i<CALLBACK_LAST;i++) {
+ sv_2mortal(self->callback[i]);
+ }
+ for(i=0;i<CALLBACK_LAST;i++) {
+ sv_2mortal(self->callback_ctx[i]);
+ }
+
+
+ if (self->errbufvarname)
+ free(self->errbufvarname);
+
+ Safefree(self);
+
+}
+
+/* Register a callback function */
+
+static void perl_curl_easy_register_callback(perl_curl_easy *self, SV **callback, SV *function)
+{
+ if (function && SvOK(function)) {
+ /* FIXME: need to check the ref-counts here */
+ if (*callback == NULL) {
+ *callback = newSVsv(function);
+ } else {
+ SvSetSV(*callback, function);
+ }
+ } else {
+ if (*callback != NULL) {
+ sv_2mortal(*callback);
+ *callback = NULL;
+ }
+ }
+}
+
+/* start of form functions - very un-finished! */
+static perl_curl_form * perl_curl_form_new()
+{
+ perl_curl_form *self;
+ Newz(1, self, 1, perl_curl_form);
+ if (!self)
+ croak("out of memory");
+ self->post=NULL;
+ self->last=NULL;
+ return self;
+}
+
+static void perl_curl_form_delete(perl_curl_form *self)
+{
+#if 0
+ if (self->post) {
+ curl_formfree(self->post);
+ }
+#endif
+ Safefree(self);
+}
+
+/* make a new multi */
+static perl_curl_multi * perl_curl_multi_new()
+{
+ perl_curl_multi *self;
+ Newz(1, self, 1, perl_curl_multi);
+ if (!self)
+ croak("out of memory");
+#ifdef __CURL_MULTI_H
+ self->curlm=curl_multi_init();
+#else
+ croak("curl version too old to support curl_multi_init()");
+#endif
+ return self;
+}
+
+/* delete the multi */
+static void perl_curl_multi_delete(perl_curl_multi *self)
+{
+#ifdef __CURL_MULTI_H
+ if (self->curlm)
+ curl_multi_cleanup(self->curlm);
+ Safefree(self);
+#endif
+
+}
+
+/* make a new share */
+static perl_curl_share * perl_curl_share_new()
+{
+ perl_curl_share *self;
+ Newz(1, self, 1, perl_curl_share);
+ if (!self)
+ croak("out of memory");
+ self->curlsh=curl_share_init();
+ return self;
+}
+
+/* delete the share */
+static void perl_curl_share_delete(perl_curl_share *self)
+{
+ if (self->curlsh)
+ curl_share_cleanup(self->curlsh);
+ Safefree(self);
+}
+
+
+/* generic fwrite callback, which decides which callback to call */
+static size_t
+fwrite_wrapper (
+ const void *ptr,
+ size_t size,
+ size_t nmemb,
+ perl_curl_easy *self,
+ void *call_function,
+ void *call_ctx)
+{
+ dSP;
+
+ if (call_function) { /* We are doing a callback to perl */
+ int count, status;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+
+ if (ptr) {
+ XPUSHs(sv_2mortal(newSVpvn((char *)ptr, (STRLEN)(size * nmemb))));
+ } else { /* just in case */
+ XPUSHs(&PL_sv_undef);
+ }
+ if (call_ctx) {
+ XPUSHs(sv_2mortal(newSVsv(call_ctx)));
+ } else { /* should be a stdio glob ? */
+ XPUSHs(&PL_sv_undef);
+ }
+
+ PUTBACK;
+ count = perl_call_sv((SV *) call_function, G_SCALAR);
+ SPAGAIN;
+
+ if (count != 1)
+ croak("callback for CURLOPT_WRITEFUNCTION didn't return a status\n");
+
+ status = POPi;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return status;
+
+ } else {
+ /* perform write directly, via PerlIO */
+
+ PerlIO *handle;
+ if (call_ctx) { /* Assume the context is a GLOB */
+ handle = IoOFP(sv_2io(call_ctx));
+
+ } else { /* punt to stdout */
+ handle = PerlIO_stdout();
+ }
+ return PerlIO_write(handle,ptr,size*nmemb);
+ }
+}
+
+/* debug fwrite callback */
+static size_t
+fwrite_wrapper2 (
+ const void *ptr,
+ size_t size,
+ perl_curl_easy *self,
+ void *call_function,
+ void *call_ctx,
+ int curl_infotype)
+{
+ dSP;
+
+ if (call_function) { /* We are doing a callback to perl */
+ int count, status;
+ SV *sv;
+
+ ENTER;
+ SAVETMPS;
+
+ PUSHMARK(SP);
+
+ if (ptr) {
+ XPUSHs(sv_2mortal(newSVpvn((char *)ptr, (STRLEN)(size * sizeof(char)))));
+ } else { /* just in case */
+ XPUSHs(&PL_sv_undef);
+ }
+
+ if (call_ctx) {
+ XPUSHs(sv_2mortal(newSVsv(call_ctx)));
+ } else { /* should be a stdio glob ? */
+ XPUSHs(&PL_sv_undef);
+ }
+
+ XPUSHs(sv_2mortal(newSViv(curl_infotype)));
+
+ PUTBACK;
+ count = perl_call_sv((SV *) call_function, G_SCALAR);
+ SPAGAIN;
+
+ if (count != 1)
+ croak("callback for CURLOPT_*FUNCTION didn't return a status\n");
+
+ status = POPi;
+
+ PUTBACK;
+ FREETMPS;
+ LEAVE;
+ return status;
+
+ } else {
+ /* perform write directly, via PerlIO */
+
+ PerlIO *handle;
+ if (call_ctx) { /* Assume the context is a GLOB */
+ handle = IoOFP(sv_2io(call_ctx));
+
+ } else { /* punt to stdout */
+ handle = PerlIO_stdout();
+ }
+ return PerlIO_write(handle,ptr,size*sizeof(char));
+ }
+}
+
+/* Write callback for calling a perl callback */
+size_t
+write_callback_func(const void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ perl_curl_easy *self;
+ self=(perl_curl_easy *)stream;
+ return fwrite_wrapper(ptr,size,nmemb,self,
+ self->callback[CALLBACK_WRITE],self->callback_ctx[CALLBACK_WRITE]);
+}
+
+/* header callback for calling a perl callback */
+size_t
+writeheader_callback_func(const void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ perl_curl_easy *self;
+ self=(perl_curl_easy *)stream;
+
+ return fwrite_wrapper(ptr,size,nmemb,self,
+ self->callback[CALLBACK_HEADER],self->callback_ctx[CALLBACK_HEADER]);
+}
+
+/* debug callback for calling a perl callback */
+size_t
+debug_callback_func(CURL* handle, int curl_infotype, const void *ptr, size_t size, void *stream)
+{
+ perl_curl_easy *self;
+ self=(perl_curl_easy *)stream;
+
+ return fwrite_wrapper2(ptr,size,self,
+ self->callback[CALLBACK_DEBUG],self->callback_ctx[CALLBACK_DEBUG],curl_infotype);
+}
+
+/* read callback for calling a perl callback */
+size_t
+read_callback_func( void *ptr, size_t size, size_t nmemb, void *stream)
+{
+ dSP ;
+
+ size_t maxlen;
+ perl_curl_easy *self;
+ self=(perl_curl_easy *)stream;
+
+ maxlen = size*nmemb;
+
+ if (self->callback[CALLBACK_READ]) { /* We are doing a callback to perl */
+ char *data;
+ int count;
+ SV *sv;
+ STRLEN len;
+
+ ENTER ;
+ SAVETMPS ;
+
+ PUSHMARK(SP) ;
+
+ if (self->callback_ctx[CALLBACK_READ]) {
+ sv = self->callback_ctx[CALLBACK_READ];
+ } else {
+ sv = &PL_sv_undef;
+ }
+
+ XPUSHs(sv_2mortal(newSViv(maxlen)));
+ XPUSHs(sv_2mortal(newSVsv(sv)));
+
+ PUTBACK ;
+ count = perl_call_sv(self->callback[CALLBACK_READ], G_SCALAR);
+ SPAGAIN;
+
+ if (count != 1)
+ croak("callback for CURLOPT_READFUNCTION didn't return any data\n");
+
+ sv = POPs;
+ data = SvPV(sv,len);
+
+ /* only allowed to return the number of bytes asked for */
+ len = (len<maxlen ? len : maxlen);
+ /* memcpy(ptr,data,(size_t)len); */
+ Copy(data,ptr,len,char);
+
+ PUTBACK ;
+ FREETMPS ;
+ LEAVE ;
+ return (size_t) (len/size);
+
+ } else {
+ /* read input directly */
+ PerlIO *f;
+ if (self->callback_ctx[CALLBACK_READ]) { /* hope its a GLOB! */
+ f = IoIFP(sv_2io(self->callback_ctx[CALLBACK_READ]));
+ } else { /* punt to stdin */
+ f = PerlIO_stdin();
+ }
+ return PerlIO_read(f,ptr,maxlen);
+ }
+}
+
+/* Progress callback for calling a perl callback */
+
+static int progress_callback_func(void *clientp, double dltotal, double dlnow,
+ double ultotal, double ulnow)
+{
+ dSP;
+
+ int count;
+ perl_curl_easy *self;
+ self=(perl_curl_easy *)clientp;
+
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(sp);
+ if (self->callback_ctx[CALLBACK_PROGRESS]) {
+ XPUSHs(sv_2mortal(newSVsv(self->callback_ctx[CALLBACK_PROGRESS])));
+ } else {
+ XPUSHs(&PL_sv_undef);
+ }
+ XPUSHs(sv_2mortal(newSVnv(dltotal)));
+ XPUSHs(sv_2mortal(newSVnv(dlnow)));
+ XPUSHs(sv_2mortal(newSVnv(ultotal)));
+ XPUSHs(sv_2mortal(newSVnv(ulnow)));
+
+ PUTBACK;
+ count = perl_call_sv(self->callback[CALLBACK_PROGRESS], G_SCALAR);
+ SPAGAIN;
+
+ if (count != 1)
+ croak("callback for CURLOPT_PROGRESSFUNCTION didn't return 1\n");
+
+ count = POPi;
+
+ 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 = perl_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
+
+#include "curlopt-constants.c"
+
+typedef perl_curl_easy * WWW__Curl__Easy;
+
+typedef perl_curl_form * WWW__Curl__Form;
+
+typedef perl_curl_multi * WWW__Curl__Multi;
+
+typedef perl_curl_share * WWW__Curl__Share;
+
+MODULE = WWW::Curl PACKAGE = WWW::Curl::Easy PREFIX = curl_easy_
+
+BOOT:
+ curl_global_init(CURL_GLOBAL_ALL); /* FIXME: does this need a mutex for ithreads? */
+
+
+PROTOTYPES: ENABLE
+
+int
+constant(name,arg)
+ char * name
+ int arg
+
+
+void
+curl_easy_init(...)
+ ALIAS:
+ new = 1
+ PREINIT:
+ perl_curl_easy *self;
+ char *sclass = "WWW::Curl::Easy";
+
+ PPCODE:
+ if (items>0 && !SvROK(ST(0))) {
+ STRLEN dummy;
+ sclass = SvPV(ST(0),dummy);
+ }
+
+ self=perl_curl_easy_new(); /* curl handle created by this point */
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), sclass, (void*)self);
+ SvREADONLY_on(SvRV(ST(0)));
+
+ Newxz(self->y,1,I32);
+ if (!self->y) { croak ("out of memory"); }
+ (*self->y)++;
+ /* configure curl to always callback to the XS interface layer */
+ curl_easy_setopt(self->curl, CURLOPT_WRITEFUNCTION, write_callback_func);
+ curl_easy_setopt(self->curl, CURLOPT_READFUNCTION, read_callback_func);
+
+ /* set our own object as the context for all curl callbacks */
+ curl_easy_setopt(self->curl, CURLOPT_FILE, self);
+ curl_easy_setopt(self->curl, CURLOPT_INFILE, self);
+
+ /* we always collect this, in case it's wanted */
+ curl_easy_setopt(self->curl, CURLOPT_ERRORBUFFER, self->errbuf);
+
+ XSRETURN(1);
+
+void
+curl_easy_duphandle(self)
+ WWW::Curl::Easy self
+ PREINIT:
+ perl_curl_easy *clone;
+ char *sclass = "WWW::Curl::Easy";
+ perl_curl_easy_callback_code i;
+
+ PPCODE:
+ clone=perl_curl_easy_duphandle(self);
+ clone->y = self->y;
+ (*self->y)++;
+
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), sclass, (void*)clone);
+ SvREADONLY_on(SvRV(ST(0)));
+
+ /* configure curl to always callback to the XS interface layer */
+
+ curl_easy_setopt(clone->curl, CURLOPT_WRITEFUNCTION, write_callback_func);
+ curl_easy_setopt(clone->curl, CURLOPT_READFUNCTION, read_callback_func);
+ if (self->callback[callback_index(CURLOPT_HEADERFUNCTION)] || self->callback_ctx[callback_index(CURLOPT_WRITEHEADER)]) {
+ curl_easy_setopt(clone->curl, CURLOPT_HEADERFUNCTION, header_callback_func);
+ curl_easy_setopt(clone->curl, CURLOPT_WRITEHEADER, clone);
+ }
+
+ if (self->callback[callback_index(CURLOPT_PROGRESSFUNCTION)] || self->callback_ctx[callback_index(CURLOPT_PROGRESSDATA)]) {
+ curl_easy_setopt(clone->curl, CURLOPT_PROGRESSFUNCTION, progress_callback_func);
+ curl_easy_setopt(clone->curl, CURLOPT_PROGRESSDATA, clone);
+ }
+
+ if (self->callback[callback_index(CURLOPT_DEBUGFUNCTION)] || self->callback_ctx[callback_index(CURLOPT_DEBUGDATA)]) {
+ curl_easy_setopt(clone->curl, CURLOPT_DEBUGFUNCTION, debug_callback_func);
+ curl_easy_setopt(clone->curl, CURLOPT_DEBUGDATA, clone);
+ }
+
+ /* set our own object as the context for all curl callbacks */
+ curl_easy_setopt(clone->curl, CURLOPT_FILE, clone);
+ curl_easy_setopt(clone->curl, CURLOPT_INFILE, clone);
+ curl_easy_setopt(clone->curl, CURLOPT_ERRORBUFFER, clone->errbuf);
+
+ for(i=0;i<CALLBACK_LAST;i++) {
+ perl_curl_easy_register_callback(clone,&(clone->callback[i]), self->callback[i]);
+ perl_curl_easy_register_callback(clone,&(clone->callback_ctx[i]), self->callback_ctx[i]);
+ };
+
+ XSRETURN(1);
+
+char *
+curl_easy_version(...)
+ CODE:
+ RETVAL=curl_version();
+ OUTPUT:
+ RETVAL
+
+int
+curl_easy_setopt(self, option, value)
+ WWW::Curl::Easy self
+ int option
+ SV * value
+ CODE:
+ RETVAL=CURLE_OK;
+ switch(option) {
+ /* SV * to user contexts for callbacks - any SV (glob,scalar,ref) */
+ case CURLOPT_FILE:
+ case CURLOPT_INFILE:
+ perl_curl_easy_register_callback(self,
+ &(self->callback_ctx[callback_index(option)]), value);
+ break;
+ case CURLOPT_WRITEHEADER:
+ curl_easy_setopt(self->curl, CURLOPT_HEADERFUNCTION, SvOK(value) ? header_callback_func : NULL);
+ curl_easy_setopt(self->curl, option, SvOK(value) ? self : NULL);
+ perl_curl_easy_register_callback(self,&(self->callback_ctx[callback_index(option)]),value);
+ break;
+ case CURLOPT_PROGRESSDATA:
+ curl_easy_setopt(self->curl, CURLOPT_PROGRESSFUNCTION, SvOK(value) ? progress_callback_func : NULL);
+ curl_easy_setopt(self->curl, option, SvOK(value) ? self : NULL);
+ perl_curl_easy_register_callback(self,&(self->callback_ctx[callback_index(option)]), value);
+ break;
+ case CURLOPT_DEBUGDATA:
+ curl_easy_setopt(self->curl, CURLOPT_DEBUGFUNCTION, SvOK(value) ? debug_callback_func : NULL);
+ curl_easy_setopt(self->curl, option, SvOK(value) ? self : NULL);
+ perl_curl_easy_register_callback(self,&(self->callback_ctx[callback_index(option)]), value);
+ break;
+
+ /* SV * to a subroutine ref */
+ case CURLOPT_WRITEFUNCTION:
+ case CURLOPT_READFUNCTION:
+ perl_curl_easy_register_callback(self,&(self->callback[callback_index(option)]), value);
+ break;
+ case CURLOPT_HEADERFUNCTION:
+ curl_easy_setopt(self->curl, option, SvOK(value) ? header_callback_func : NULL);
+ curl_easy_setopt(self->curl, CURLOPT_WRITEHEADER, SvOK(value) ? self : NULL);
+ perl_curl_easy_register_callback(self,&(self->callback[callback_index(option)]), value);
+ break;
+ case CURLOPT_PROGRESSFUNCTION:
+ curl_easy_setopt(self->curl, option, SvOK(value) ? progress_callback_func : NULL);
+ curl_easy_setopt(self->curl, CURLOPT_PROGRESSDATA, SvOK(value) ? self : NULL);
+ perl_curl_easy_register_callback(self,&(self->callback[callback_index(option)]), value);
+ break;
+ case CURLOPT_DEBUGFUNCTION:
+ curl_easy_setopt(self->curl, option, SvOK(value) ? debug_callback_func : NULL);
+ curl_easy_setopt(self->curl, CURLOPT_DEBUGDATA, SvOK(value) ? self : NULL);
+ perl_curl_easy_register_callback(self,&(self->callback[callback_index(option)]), value);
+ break;
+
+ /* slist cases */
+ case CURLOPT_HTTPHEADER:
+ case CURLOPT_QUOTE:
+ case CURLOPT_POSTQUOTE:
+ {
+ /* This is an option specifying a list, which we put in a curl_slist struct */
+ AV *array = (AV *)SvRV(value);
+ struct curl_slist **slist = NULL;
+ int last = av_len(array);
+ int i;
+
+ /* We have to find out which list to use... */
+ slist = &(self->slist[slist_index(option)]);
+
+ /* free any previous list */
+ if (*slist) {
+ curl_slist_free_all(*slist);
+ *slist=NULL;
+ }
+ /* copy perl values into this slist */
+ for (i=0;i<=last;i++) {
+ SV **sv = av_fetch(array,i,0);
+ STRLEN len = 0;
+ char *string = SvPV(*sv, len);
+ if (len == 0) /* FIXME: is this correct? */
+ break;
+ *slist = curl_slist_append(*slist, string);
+ }
+ /* pass the list into curl_easy_setopt() */
+ RETVAL = curl_easy_setopt(self->curl, option, *slist);
+ };
+ break;
+
+ /* Pass in variable name for storing error messages. Yuck. */
+ case CURLOPT_ERRORBUFFER:
+ {
+ STRLEN dummy;
+ if (self->errbufvarname)
+ free(self->errbufvarname);
+ self->errbufvarname = strdup((char *)SvPV(value, dummy));
+ };
+ break;
+
+ /* tell curl to redirect STDERR - value should be a glob */
+ case CURLOPT_STDERR:
+ RETVAL = curl_easy_setopt(self->curl, option, IoOFP(sv_2io(value)) );
+ break;
+
+ /* not working yet...
+ case CURLOPT_HTTPPOST:
+ if (sv_derived_from(value, "WWW::Curl::Form")) {
+ WWW__Curl__form wrapper;
+ IV tmp = SvIV((SV*)SvRV(value));
+ wrapper = INT2PTR(WWW__Curl__form,tmp);
+ RETVAL = curl_easy_setopt(self->curl, option, wrapper->post);
+ } else
+ croak("value is not of type WWW::Curl::Form");
+ break;
+ */
+
+ /* Curl share support from Anton Fedorov */
+#if (LIBCURL_VERSION_NUM>=0x070a03)
+ case CURLOPT_SHARE:
+ if (sv_derived_from(value, "WWW::Curl::Share")) {
+ WWW__Curl__Share wrapper;
+ IV tmp = SvIV((SV*)SvRV(value));
+ wrapper = INT2PTR(WWW__Curl__Share,tmp);
+ RETVAL = curl_easy_setopt(self->curl, option, wrapper->curlsh);
+ } else
+ croak("value is not of type WWW::Curl::Share");
+ break;
+#endif
+ case CURLOPT_PRIVATE:
+ RETVAL = curl_easy_setopt(self->curl, option, (long)SvIV(value));
+ break;
+
+ /* default cases */
+ default:
+ if (option < CURLOPTTYPE_OBJECTPOINT) { /* A long (integer) value */
+ RETVAL = curl_easy_setopt(self->curl, option, (long)SvIV(value));
+ }
+ else if (option < CURLOPTTYPE_FUNCTIONPOINT) { /* An objectpoint - string */
+ /* FIXME: Does curl really want NULL for empty strings? */
+ STRLEN dummy;
+ char *pv = SvPV(value, dummy);
+ RETVAL = curl_easy_setopt(self->curl, option, SvOK(value) ? pv : NULL);
+ }
+#ifdef CURLOPTTYPE_OFF_T
+ else if (option < CURLOPTTYPE_OFF_T) { /* A function - notreached? */
+ croak("Unknown curl option of type function");
+ }
+ else { /* A LARGE file option using curl_off_t */
+ RETVAL = curl_easy_setopt(self->curl, option, (curl_off_t)SvIV(value));
+ }
+#endif
+ ;
+ break;
+ };
+ OUTPUT:
+ RETVAL
+
+int
+internal_setopt(self, option, value)
+ WWW::Curl::Easy self
+ int option
+ int value
+ CODE:
+ croak("internal_setopt no longer supported - use a callback\n");
+ RETVAL = 0;
+ OUTPUT:
+ RETVAL
+
+int
+curl_easy_perform(self)
+ WWW::Curl::Easy self
+ CODE:
+ /* perform the actual curl fetch */
+ RETVAL = curl_easy_perform(self->curl);
+
+ if (RETVAL && self->errbufvarname) {
+ /* If an error occurred and a varname for error messages has been
+ specified, store the error message. */
+ SV *sv = perl_get_sv(self->errbufvarname, TRUE | GV_ADDMULTI);
+ sv_setpv(sv, self->errbuf);
+ }
+ OUTPUT:
+ RETVAL
+
+
+SV *
+curl_easy_getinfo(self, option, ... )
+ WWW::Curl::Easy self
+ int option
+ CODE:
+ switch (option & CURLINFO_TYPEMASK) {
+ case CURLINFO_STRING:
+ {
+ char * vchar;
+ curl_easy_getinfo(self->curl, option, &vchar);
+ RETVAL = newSVpv(vchar,0);
+ break;
+ }
+ case CURLINFO_LONG:
+ {
+ long vlong;
+ curl_easy_getinfo(self->curl, option, &vlong);
+ RETVAL = newSViv(vlong);
+ break;
+ }
+ case CURLINFO_DOUBLE:
+ {
+ double vdouble;
+ curl_easy_getinfo(self->curl, option, &vdouble);
+ RETVAL = newSVnv(vdouble);
+ break;
+ }
+ default: {
+ RETVAL = newSViv(CURLE_BAD_FUNCTION_ARGUMENT);
+ break;
+ }
+ }
+ if (items > 2)
+ sv_setsv(ST(2),RETVAL);
+ OUTPUT:
+ RETVAL
+
+char *
+curl_easy_errbuf(self)
+ WWW::Curl::Easy self
+ CODE:
+ RETVAL = self->errbuf;
+ OUTPUT:
+ RETVAL
+
+int
+curl_easy_cleanup(self)
+ WWW::Curl::Easy self
+ CODE:
+ /* does nothing anymore - cleanup is automatic when a curl handle goes out of scope */
+ RETVAL = 0;
+ OUTPUT:
+ RETVAL
+
+void
+curl_easy_DESTROY(self)
+ WWW::Curl::Easy self
+ CODE:
+ perl_curl_easy_delete(self);
+
+void
+curl_easy_global_cleanup()
+ CODE:
+ curl_global_cleanup();
+
+SV *
+curl_easy_strerror(self, errornum)
+ WWW::Curl::Easy self
+ int errornum
+ CODE:
+ {
+#if (LIBCURL_VERSION_NUM>=0x070C00)
+ const char * vchar = curl_easy_strerror(errornum);
+#else
+ const char * vchar = "Unknown because curl_easy_strerror function not available}";
+#endif
+ RETVAL = newSVpv(vchar,0);
+ }
+ OUTPUT:
+ RETVAL
+
+MODULE = WWW::Curl PACKAGE = WWW::Curl::Form PREFIX = curl_form_
+
+void
+curl_form_new(...)
+ PREINIT:
+ perl_curl_form *self;
+ char *sclass = "WWW::Curl::Form";
+ PPCODE:
+ if (items>0 && !SvROK(ST(0))) {
+ STRLEN dummy;
+ sclass = SvPV(ST(0),dummy);
+ }
+
+ self=perl_curl_form_new();
+
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), sclass, (void*)self);
+ SvREADONLY_on(SvRV(ST(0)));
+
+ XSRETURN(1);
+
+void
+curl_form_add(self,name,value)
+ WWW::Curl::Form self
+ char *name
+ char *value
+ CODE:
+#if 0
+ curl_formadd(&(self->post),&(self->last),
+ CURLFORM_COPYNAME,name,
+ CURLFORM_COPYCONTENTS,value,
+ CURLFORM_END);
+#endif
+
+void
+curl_form_addfile(self,filename,description,type)
+ WWW::Curl::Form self
+ char *filename
+ char *description
+ char *type
+ CODE:
+#if 0
+ curl_formadd(&(self->post),&(self->last),
+ CURLFORM_FILE,filename,
+ CURLFORM_COPYNAME,description,
+ CURLFORM_CONTENTTYPE,type,
+ CURLFORM_END);
+#endif
+
+void
+curl_form_DESTROY(self)
+ WWW::Curl::Form self
+ CODE:
+ perl_curl_form_delete(self);
+
+MODULE = WWW::Curl PACKAGE = WWW::Curl::Multi PREFIX = curl_multi_
+
+void
+curl_multi_new(...)
+ PREINIT:
+ perl_curl_multi *self;
+ char *sclass = "WWW::Curl::Multi";
+ PPCODE:
+ if (items>0 && !SvROK(ST(0))) {
+ STRLEN dummy;
+ sclass = SvPV(ST(0),dummy);
+ }
+
+ self=perl_curl_multi_new();
+
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), sclass, (void*)self);
+ SvREADONLY_on(SvRV(ST(0)));
+
+ XSRETURN(1);
+
+void
+curl_multi_add_handle(curlm, curl)
+ WWW::Curl::Multi curlm
+ WWW::Curl::Easy curl
+ CODE:
+#ifdef __CURL_MULTI_H
+ curl_multi_add_handle(curlm->curlm, curl->curl);
+#endif
+
+void
+curl_multi_remove_handle(curlm, curl)
+ WWW::Curl::Multi curlm
+ WWW::Curl::Easy curl
+ CODE:
+#ifdef __CURL_MULTI_H
+ curl_multi_remove_handle(curlm->curlm, curl->curl);
+#endif
+
+void
+curl_multi_info_read(self)
+ WWW::Curl::Multi self
+ PREINIT:
+ CURL *easy = NULL;
+ CURLcode res;
+ long stashid;
+ int queue;
+ CURLMsg *msg;
+ PPCODE:
+ while ((msg = curl_multi_info_read(self->curlm, &queue))) {
+ if (msg->msg == CURLMSG_DONE) {
+ easy=msg->easy_handle;
+ res=msg->data.result;
+ break;
+ }
+ };
+ if (easy) {
+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, &stashid);
+ curl_easy_setopt(easy, CURLINFO_PRIVATE, NULL);
+ curl_multi_remove_handle(self->curlm, easy);
+ XPUSHs(sv_2mortal(newSViv(stashid)));
+ XPUSHs(sv_2mortal(newSViv(res)));
+ } else {
+ XSRETURN_EMPTY;
+ }
+
+int
+curl_multi_perform(self)
+ WWW::Curl::Multi self
+ PREINIT:
+ int remaining;
+ CODE:
+#ifdef __CURL_MULTI_H
+ while(CURLM_CALL_MULTI_PERFORM ==
+ curl_multi_perform(self->curlm, &remaining));
+ RETVAL = remaining;
+ /* while(remaining) {
+ struct timeval timeout;
+ int rc;
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ int maxfd;
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ curl_multi_fdset(self->curlm, &fdread, &fdwrite, &fdexcep, &maxfd);
+ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+ switch(rc) {
+ case -1:
+ break;
+ default:
+ while(CURLM_CALL_MULTI_PERFORM ==
+ curl_multi_perform(self->curlm, &remaining));
+ break;
+ }
+ } */
+#endif
+ OUTPUT:
+ RETVAL
+
+void
+curl_multi_DESTROY(self)
+ WWW::Curl::Multi self
+ CODE:
+ perl_curl_multi_delete(self);
+
+SV *
+curl_multi_strerror(self, errornum)
+ WWW::Curl::Multi self
+ int errornum
+ CODE:
+ {
+#if (LIBCURL_VERSION_NUM>=0x070C00)
+ const char * vchar = curl_multi_strerror(errornum);
+#else
+ const char * vchar = "Unknown because curl_multi_strerror function not available}";
+#endif
+ RETVAL = newSVpv(vchar,0);
+ }
+ OUTPUT:
+ RETVAL
+
+MODULE = WWW::Curl PACKAGE = WWW::Curl::Share PREFIX = curl_share_
+
+PROTOTYPES: ENABLE
+
+int
+constant(name,arg)
+ char * name
+ int arg
+
+void
+curl_share_new(...)
+ PREINIT:
+ perl_curl_share *self;
+ char *sclass = "WWW::Curl::Share";
+ PPCODE:
+ if (items>0 && !SvROK(ST(0))) {
+ STRLEN dummy;
+ sclass = SvPV(ST(0),dummy);
+ }
+
+ self=perl_curl_share_new();
+
+ ST(0) = sv_newmortal();
+ sv_setref_pv(ST(0), sclass, (void*)self);
+ SvREADONLY_on(SvRV(ST(0)));
+
+ XSRETURN(1);
+
+void
+curl_share_DESTROY(self)
+ WWW::Curl::Share self
+ CODE:
+ perl_curl_share_delete(self);
+
+int
+curl_share_setopt(self, option, value)
+ WWW::Curl::Share self
+ int option
+ SV * value
+ CODE:
+ RETVAL=CURLE_OK;
+#if (LIBCURL_VERSION_NUM>=0x070a03)
+ switch(option) {
+ /* slist cases */
+ case CURLSHOPT_SHARE:
+ case CURLSHOPT_UNSHARE:
+ if (option < CURLOPTTYPE_OBJECTPOINT) { /* An integer value: */
+ RETVAL = curl_share_setopt(self->curlsh, option, (long)SvIV(value));
+ } else { /* A char * value: */
+ /* FIXME: Does curl really want NULL for empty strings? */
+ STRLEN dummy;
+ char *pv = SvPV(value, dummy);
+ RETVAL = curl_share_setopt(self->curlsh, option, *pv ? pv : NULL);
+ };
+ break;
+ };
+#else
+ croak("curl_share_setopt not supported in your libcurl version");
+#endif
+ OUTPUT:
+ RETVAL
+
+SV *
+curl_share_strerror(self, errornum)
+ WWW::Curl::Share self
+ int errornum
+ CODE:
+ {
+#if (LIBCURL_VERSION_NUM>=0x070C00)
+ const char * vchar = curl_share_strerror(errornum);
+#else
+ const char * vchar = "Unknown because curl_share_strerror function not available}";
+#endif
+ RETVAL = newSVpv(vchar,0);
+ }
+ OUTPUT:
+ RETVAL