summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Changes246
-rw-r--r--Curl.xs1190
-rw-r--r--LICENSE4
-rw-r--r--MANIFEST55
-rw-r--r--META.yml22
-rw-r--r--Makefile.PL302
-rw-r--r--README59
-rw-r--r--README.Win3212
-rw-r--r--SIGNATURE77
-rw-r--r--inc/Module/Install.pm353
-rw-r--r--inc/Module/Install/Base.pm70
-rw-r--r--inc/Module/Install/Can.pm82
-rw-r--r--inc/Module/Install/External.pm66
-rw-r--r--inc/Module/Install/MakeMaker.pm50
-rw-r--r--inc/Module/Install/Makefile.pm245
-rw-r--r--inc/Module/Install/Metadata.pm407
-rw-r--r--lib/WWW/Curl.pm334
-rw-r--r--lib/WWW/Curl/Easy.pm245
-rw-r--r--lib/WWW/Curl/Form.pm12
-rw-r--r--lib/WWW/Curl/Multi.pm14
-rw-r--r--lib/WWW/Curl/Share.pm49
-rw-r--r--packaging/perl-WWW-Curl.spec131
-rw-r--r--t/00constants.t8
-rw-r--r--t/01basic.t48
-rw-r--r--t/02callbacks.t41
-rw-r--r--t/04abort-test.t36
-rw-r--r--t/05progress.t54
-rw-r--r--t/06http-post.t58
-rw-r--r--t/07ftp-upload.t86
-rw-r--r--t/08ssl.t92
-rw-r--r--t/09times.t58
-rw-r--r--t/10errbuf.t40
-rw-r--r--t/13slowleak.t43
-rw-r--r--t/14duphandle.t71
-rw-r--r--t/15duphandle-callback.t94
-rw-r--r--t/16formpost.t84
-rw-r--r--t/17slist.t91
-rw-r--r--t/18twinhandles.t52
-rw-r--r--t/meta.t4
-rw-r--r--t/new/00constants.t5
-rw-r--r--t/new/01basic.t13
-rw-r--r--t/new/02header-callback.t20
-rw-r--r--t/new/03body-callback.t20
-rw-r--r--t/new/04abort.t17
-rw-r--r--t/new/05progress.t25
-rw-r--r--t/new/06http-post.t26
-rw-r--r--t/new/07errbuf.t9
-rw-r--r--t/new/08duphandle.t14
-rw-r--r--t/new/09duphandle-callback.t21
-rw-r--r--t/new/10multi-callback.t54
-rw-r--r--t/new/README8
-rw-r--r--t/pod-coverage.t7
-rw-r--r--t/pod.t6
-rw-r--r--template/Easy.pm.tmpl46
-rw-r--r--template/Share.pm.tmpl36
-rw-r--r--typemap5
56 files changed, 5317 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..280d525
--- /dev/null
+++ b/Changes
@@ -0,0 +1,246 @@
+Revision history for Perl extension WWW::Curl.
+4.05 Sat Jul 06 2008: - Balint Szilakszi <szbalint at cpan.org>
+
+ - Corrected the duphandle mechanism.
+ - Fixed a typo (fixes RT #37253).
+ - Added better Windows compatibility detection and separate
+ installation instructions (thanks to Shiyu Tang).
+
+4.04 Sat Jun 21 2008: - Balint Szilakszi <szbalint at cpan.org>
+
+ - Test::More and use_ok at begin time doesn't mesh with no plan.
+
+4.03 Sat Jun 21 2008: - Balint Szilakszi <szbalint at cpan.org>
+
+ - Removing option to pass destination files as a typeglob as
+ it is a backwards pointing magic and caused bugs.
+ Use a filehandle or reference instead.
+ - Fixed tests for 5.6, raised minimum Perl version to 5.6.1 due to tests.
+ - Fixed a duphandle bug.
+
+4.02 Thu Jun 19 2008: - Balint Szilakszi <szbalint at cpan.org>
+
+ - Adding forgotten backwards compatibility with older Perl versions
+
+4.01 Wed Jun 18 2008: - Balint Szilakszi <szbalint at cpan.org>
+
+ - Reimplemented callback handling to fix several I/O issues (fixes RT #35491)
+ - Documentation showing example WWW::Curl::Multi usage is corrected (fixes RT #35520)
+ - Should now compile on HP-UX and OSX (fixes RT #36058 and #35349)
+ - POD validity fix (fixes RT #15850)
+ - Added several new tests
+ - Upgraded to Module::Install 0.75 as it now exits correctly on build time
+ with NA if the required libcurl isn't available.
+
+4.00 Wed Mar 05 2008: - Balint Szilakszi <szbalint at cpan.org>
+ - Rewritten documentation.
+ - New test suite.
+ - New build system: using Module::Install instead of ExtUtils::Makemaker.
+ - New minimal requirements, Perl 5.6 and libcurl 7.10.8.
+ - New WWW::Curl::Multi interface.
+ - Removed WWW::Curl::easy.
+ - Removed compatibility code for pre 7.10.8 libcurl.
+ - Added lots of small improvements to setopt and friends.
+ - Bugfixes, mainly for memory leaks.
+ - SIGNATURE.
+
+3.12 Wed Feb 27 2008: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - New Share.pm.in was missed from the MANIFEST, so was not in the distribution. Fixed.
+
+3.11 Mon Feb 25 2008: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Merge recent contributions.
+ - Updates from various small bug/build reports
+
+3.10 Fri Feb 22 2008: - Anton Fedorov <datacompboy at mail.ru>
+ - Fix documentation for multi interface
+ - Added WWW::Curl::Share
+ - Added strerror into Easy and Multi interfaces
+ - Added support for CURLOPT_DEBUGFUNCTION in Easy interface
+
+3.02_01 Tue Jan 29 2008: - Mark Hindley
+ - Contributed CURLOPT_*_LARGE support
+
+3.02_00 Fri Sep 16 2005: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Workaround case-sensitive filename issues by making equivalent Easy
+ names in easy namespace
+ - Test scripts - Fixup ssl tests and remove leftover MUTE option
+ - NOTE: That due to namespace changes (easy->Easy) you must change any existing
+ code to 'use WWW::Curl::Easy' instead of 'use WWW::Curl::easy', but
+ you can still use the WWW::Curl::easy function names until they
+ are removed in a future release.
+ - Tested on Redhat EL3 (curl 7.10.6) and Mandrake 10.1 (curl 7.13.1)
+
+3.01 Thu Apr 20 2004: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Merged Sebastians changes into CVS
+ - Updated remaining tests to Test::Simple
+ - Added backwards compatability to V2 names (WWW::Curl::easy etc)
+ - Dropped backwards compatability to V1.x names
+
+3.00 Thu Feb 12 2004: - Sebastian Riedel <sri at oook.de>
+ - Added multi support
+ - New module names
+ - New tests
+ - New documentation
+ - New examples
+ - Big cleanup!!!
+ - Too much to tell, it's worth an upgrade
+
+2.00 Tue Apr 22 2003: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - New top level package name of WWW::Curl in preparation for
+ entry to CPAN
+ - Rename "Curl::easy" to "WWW::Curl::easy"
+ - Add backwards compatability namespace module for existing scripts
+ - Implement initial curl_easy_duphandle support
+ - Started on curl_easy_form support (WWW:Curl::form) - NOT FUNCTIONAL YET
+ - Fixup use of env vars in t/07ftp-upload.t (jellyfish at pisem.net)
+ - Adjust IP addresses for t/08ssl.t tests due to moved https servers
+
+1.35 Sun Sep 22 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Fixed progress function callback prototype [ curl-Bugs-612432 ],
+ reflecting the fix made in curl-7.9.5. Tested in t/05progress.t to
+ now return sensible values!
+
+1.34 Wed Aug 7 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Fix off-by-one error in setting up of curl_slists from perl arrays,
+ which was causing the last item of slists to be dropped. Added regression
+ test case.
+
+1.33 Mon Aug 5 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Fix serious bug in read callback support (used for POST and upload
+ requests), introduced in 1.30, which uploaded random data (due to a
+ reversed src/dest in a memory copy).
+
+1.32 Thu Aug 1 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Minor Makefile.PL fixes to build cleanly with curl 7.8 as found
+ on redhat 7.2.
+
+1.31 Tue Jul 16 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Generate better switch() statement syntax in C code, to fix build
+ issues on some systems with strict compilers. Reported by Ignasi Roca.
+
+1.30 Mon Jul 15 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Testing release after complete code overhaul. Now supports
+ cleaner object interface, supports multiple handles per process,
+ uses PerlIO for portable I/O (which should be perl 5.8 ready) and
+ maybe even supports ithreads. Should be fully backwards compatible,
+ but please read the man page for change details and report any issues.
+ - Fixed warning caused by slist functions accessing past the end of the
+ perl array.
+ - Fixed leak caused by consuming slist arguments without freeing.
+ - Updates test scripts to OO style, cleaned up output.
+ - Deprecated USE_INTERNAL_VARS.
+
+1.21 Thu Jul 11 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Minor fixes to assist windows builds from Shawn Poulson
+ - Allow passing curl include location on the command line when
+ running perl Makefile.PL
+
+1.20 Sat Feb 16 2002: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Use standard perl module numbering syntax (valid decimal)
+ - Skipped 1.10 in case anyone confuses it with 1.1.0
+ - Made every build a rebuild and removed 'pre-built' files - no point
+ worrying about not finding curl.h - if we can't find it, we can't
+ compile anyway. Obviates bug in 1.1.9 preventing rebuilds.
+ - Add support for redefining CURLOPT_STDERR (file handle globs only!)
+
+1.1.9 Sat Dec 8 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Enhance Makefile.PL to re-build easy.pm and 'constants' xs
+ function from local installed curl.h. CURLOPT_ and CURLINFO_
+ Constants up-to-date for libcurl-7.9.2, but can be re-built
+ for almost any libcurl version by removing easy.pm and
+ curlopt-constants.c and re-running 'perl Makefile.PL'
+ - Use curl-config to find include and library compile options
+ - Updated test scripts to work better under 'make test' (You need
+ to set the environment variable 'CURL_TEST_URL' though!)
+ - Added test script to display transfer times using new time options
+ - Merge changes in Curl_easy 1.1.2.1 by Georg Horn
+
+1.1.8 Thu Sep 20 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Re-generate CURLOPT_ constants from curl.h and enhance makefile
+ to allow this to be repeated in future or for older versions of
+ libcurl. Constants up-to-date for libcurl-7.9(pre)
+ - Split tests into t/*.t to simplify each case
+ - Add test cases for new SSL switches. This needs ca-bundle.crt
+ (from mod_ssl) for verifying test cases.
+
+1.1.7 Thu Sep 13 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Documentation Update only - Explicitly state that Curl_easy
+ is released under the MIT-X/MPL dual licence. No code changes.
+
+1.1.6 Mon Sep 10 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Fix segfault due to changes in header callback behaviour
+ since curl-7.8.1-pre3
+
+1.1.5 Fri Apr 20 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Add latest CURLOPT_ and CURLINFO_ constants to the constants list
+
+1.1.4 Fri Apr 20 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Fix case where curl_slists such as 'HTTPHEADERS' need to
+ be re-set over persistant requests
+
+1.1.3 Wed Apr 18 2001: - Cris Bailiff <c.bailiff+curl at devsecure.com>
+ - Change/shorten module function names: Curl::easy::curl_easy_setopt
+ becomes Curl::easy::setopt etc. This requires minor changes to existing
+ scripts....
+ - Added callback function support to pass arbitrary SV * (including
+ FILE globs) from perl through libcurl to the perl callback.
+ - Make callbacks still work with existing scripts which use STDIO
+ - Initial support for libcurl 7.7.2 HEADERFUNCTION callback feature
+ - Minor API cleanups/changes in the callback function signatures
+ - Added Curl::easy::version function to return curl version string
+ - Callback documentation added in easy.pm
+ - More tests in test.pl
+
+1.1.2 Mon Apr 16 2001: - Georg Horn <horn at koblenz-net.de>
+ - Added support for callback functions. This is for the curl_easy_setopt()
+ options WRITEFUNCTION, READFUNCTION, PROGRESSFUNCTION and PASSWDFUNCTION.
+ Still missing, but not really neccessary: Passing a FILE * pointer,
+ that is passed in from libcurl, on to the perl callback function.
+ - Various cleanups, fixes and enhancements to easy.xs and test.pl.
+
+1.1.1 Thu Apr 12 2001:
+ - Made more options of curl_easy_setopt() work: Options that require
+ a list of curl_slist structs to be passed in, like CURLOPT_HTTPHEADER,
+ are now working by passing a perl array containing the list elements.
+ As always, look at the test script test.pl for an example.
+
+1.1.0 Wed Apr 11 2001:
+ - tested against libcurl 7.7
+ - Added new function Curl::easy::internal_setopt(). By calling
+ Curl::easy::internal_setopt(Curl::easy::USE_INTERNAL_VARS, 1);
+ the headers and content of the fetched page are no longer stored
+ into files (or written to stdout) but are stored into internal
+ Variables $Curl::easy::headers and $Curl::easy::content.
+
+1.0.2 Tue Oct 10 2000:
+ - runs with libcurl 7.4
+ - modified curl_easy_getinfo(). It now calls curl_getinfo() that has
+ been added to libcurl in version 7.4.
+
+1.0.1 Tue Oct 10 2000:
+ - Added some missing features of curl_easy_setopt():
+ - CURLOPT_ERRORBUFFER now works by passing the name of a perl
+ variable that shall be crated and the errormessage (if any)
+ be stored to.
+ - Passing filehandles (Options FILE, INFILE and WRITEHEADER) now works.
+ Have a look at test.pl to see how it works...
+
+ - Added a new function, curl_easy_getinfo(), that for now always
+ returns the number of bytes that where written to disk during the last
+ download. If the curl_easy_getinfo() function is included in libcurl,
+ (as promised by Daniel ;-)) i will turn this into just a call to this
+ function.
+
+1.0 Thu Oct 5 2000:
+ - first released version
+ - runs with libcurl 7.3
+ - some features of curl_easy_setopt() are still missing:
+ - passing function pointers doesn't work (options WRITEFUNCTION,
+ READFUNCTION and PROGRESSFUNCTION).
+ - passing FILE * pointers doesn't work (options FILE, INFILE and
+ WRITEHEADER).
+ - passing linked lists doesn't work (options HTTPHEADER and
+ HTTPPOST).
+ - setting the buffer where to store error messages in doesn't work
+ (option ERRORBUFFER).
+
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
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6dcf455
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,4 @@
+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.
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..1fb8309
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,55 @@
+Changes
+Curl.xs
+inc/Module/Install.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/External.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/MakeMaker.pm
+inc/Module/Install/Metadata.pm
+lib/WWW/Curl.pm
+lib/WWW/Curl/Easy.pm
+lib/WWW/Curl/Form.pm
+lib/WWW/Curl/Multi.pm
+lib/WWW/Curl/Share.pm
+LICENSE
+Makefile.PL
+MANIFEST
+META.yml
+README
+README.Win32
+SIGNATURE
+t/00constants.t
+t/01basic.t
+t/02callbacks.t
+t/04abort-test.t
+t/05progress.t
+t/06http-post.t
+t/07ftp-upload.t
+t/08ssl.t
+t/09times.t
+t/10errbuf.t
+t/13slowleak.t
+t/14duphandle.t
+t/15duphandle-callback.t
+t/16formpost.t
+t/17slist.t
+t/18twinhandles.t
+t/meta.t
+t/pod-coverage.t
+t/pod.t
+t/new/00constants.t
+t/new/01basic.t
+t/new/02header-callback.t
+t/new/03body-callback.t
+t/new/04abort.t
+t/new/05progress.t
+t/new/06http-post.t
+t/new/07errbuf.t
+t/new/08duphandle.t
+t/new/09duphandle-callback.t
+t/new/10multi-callback.t
+t/new/README
+template/Easy.pm.tmpl
+template/Share.pm.tmpl
+typemap
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..8e51613
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,22 @@
+---
+abstract: 'Perl extension interface for libcurl'
+author:
+ - 'Cris Bailiff <c.bailiff+curl at devsecure.com>'
+configure_requires:
+ ExtUtils::MakeMaker: 6.42
+distribution_type: module
+generated_by: 'Module::Install version 0.75'
+license: 'MPL or MIT/X-derivate'
+meta-spec:
+ url: http://module-build.sourceforge.net/META-spec-v1.3.html
+ version: 1.3
+module_name: WWW::Curl
+name: WWW-Curl
+no_index:
+ directory:
+ - template
+ - inc
+ - t
+requires:
+ perl: 5.6.1
+version: 4.05
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..92c3ba3
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,302 @@
+# Makefile.PL for Perl module WWW::Curl
+# Check out the README file for more information.
+
+use inc::Module::Install;
+
+name 'WWW-Curl';
+abstract 'Perl extension interface for libcurl';
+author 'Cris Bailiff <c.bailiff+curl at devsecure.com>';
+license 'MPL or MIT/X-derivate';
+perl_version '5.006001';
+no_index directory => 'template';
+# This is a hack. If you have libcurl installed, just specify curl.h below
+# and comment out this line.
+if ($^O ne 'MSWin32') {
+ requires_external_bin 'curl-config';
+} else {
+ print "Sorry, no automated install is available on Windows,\n".
+ "please see the README.Win32 file on instructions for a manual install.\n";
+ exit(0);
+}
+
+my @includes = qw();
+my ($cflags,$lflags, $ldflags) = ('','','');
+
+# This utility helper generates the constants function from curl.h
+# It is normally only used by the maintainer, but if you're curl is older
+# or missing some constants, you can delete curlopt-constants.c and re-run 'perl Makefile.PL'
+# You may need to specify where to find curl.h on your platform
+# These are guesses only, in case curl-config is not telling us.
+
+if ($^O ne 'MSWin32') {
+ push @includes, qw(
+ /usr/include
+ /usr/local/curl/include
+ /usr/local/include/curl
+ ../../include
+ ../curl/include
+ );
+}
+#
+# Get curl to tell us where it is, if we can.
+#
+
+
+if ($^O ne 'MSWin32') {
+ $cflags = `curl-config --cflags`;
+ $lflags = `curl-config --libs`;
+}
+
+# can't find link flags, make some guesses
+if (!defined($lflags)) {
+ $lflags="-lcurl";
+ print "Guessing your linker flags as: $lflags\n";
+}
+
+my ($flag) = ($cflags =~ m/-I(\S+)/);
+
+if (defined $flag) {
+ unshift @includes, $flag; # first guess
+}
+
+# try the path given on the command line, if any
+if (defined($ARGV[0])) {
+ unshift @includes, $ARGV[0];
+};
+
+my $curl_d = "";
+my $curl_h;
+
+# otherwise try a list of common locations
+foreach my $try (@includes) {
+ if (-f $try . "/curl/curl.h") {
+ $curl_d = $try;
+ $curl_h = $try . "/curl/curl.h";
+ last;
+ }
+}
+
+if (!defined($curl_h)) {
+ die "Cannot find curl.h - cannot build constants files - see Makefile.PL";
+} else {
+ $curl_d = "-I" . $curl_d;
+ print "Found curl.h in $curl_h\n";
+ open(CURL_H, "<" . $curl_h) or die "Can't open curl.h\n";
+ my %cinit_types;
+ my %cinit_codes;
+ my @curlinfo;
+ my @curlshopt;
+ my @curllockopt;
+ while (<CURL_H>) {
+ if ($_ =~ m/CINIT\(/ and $_ !~ m/#/) {
+ my ($option, $type, $code) =
+ m/.*CINIT\((\w*)\s*,\s*(\w+)\s*,\s*(\d+).*/;
+ $cinit_types{$option} = $option;
+ $cinit_codes{$option} = $code;
+ } elsif ($_ =~ m/^#define CURLOPT_\w+ CURLOPT_\w+/) {
+ my ($option, $value) =
+ m/^#define CURLOPT_(\w+) CURLOPT_(\w+)/;
+ $cinit_types{$option} = $value;
+ } elsif ($_ =~ m/^\s*(CURLINFO_\w+)/) {
+ push @curlinfo, $1;
+ } elsif ($_ =~ m/^\s*(CURLSHOPT_\w+)/) {
+ push @curlshopt, $1;
+ } elsif ($_ =~ m/^\s*(CURL_LOCK_DATA_\w+)/) {
+ push @curllockopt, $1;
+ }
+
+ }
+ close(CURL_H);
+
+ # some things are ifdefed out...
+ foreach my $ifdef0 (qw(FLAGS PROGRESSMODE MOREDOCS)) {
+ delete $cinit_types{$ifdef0};
+ delete $cinit_codes{$ifdef0};
+ }
+
+ print "Building curlopt-constants.c for your libcurl version\n";
+
+ open(CURL_XS, ">curlopt-constants.c")
+ or die "Can't write curlopt-constants.c\n";
+
+ # boilerplate xs constant function here
+ print CURL_XS <<HERE
+static int
+constant(char *name, int arg)
+{
+ errno = 0;
+ if (strncmp(name, "CURLINFO_", 9) == 0) {
+ name += 9;
+ switch (*name) {
+HERE
+ ;
+ foreach my $next_initial ('A' .. 'Z') {
+ print CURL_XS " case '$next_initial':\n";
+ my $count = 0;
+ foreach my $curlinfo_name (sort @curlinfo) {
+ my $initial = substr($curlinfo_name, 9, 1);
+ my $option = substr($curlinfo_name, 9);
+ if ($next_initial eq $initial) {
+
+ print CURL_XS
+" if (strEQ(name, \"$option\")) return CURLINFO_$option;\n";
+
+ $count++;
+ }
+ }
+ if ($count or $next_initial eq 'Z') {
+ print CURL_XS " break;\n";
+ }
+ }
+
+ print CURL_XS " };\n";
+ print CURL_XS " }\n";
+
+ # CURLOPT_
+ print CURL_XS <<HERE2
+
+ if (strncmp(name, "CURLOPT_", 8) == 0) {
+ name += 8;
+ switch (*name) {
+HERE2
+ ;
+
+ foreach my $next_initial ('A' .. 'Z') {
+ print CURL_XS " case '$next_initial':\n";
+ my $count = 0;
+ foreach my $option (sort keys %cinit_types) {
+ my $initial = substr($option, 0, 1);
+ if ($next_initial eq $initial) {
+
+ print CURL_XS
+" if (strEQ(name, \"$option\")) return CURLOPT_$cinit_types{$option};\n";
+
+ $count++;
+ }
+ }
+ if ($count or $next_initial eq 'Z') {
+ print CURL_XS " break;\n";
+ }
+ }
+
+ print CURL_XS " };\n";
+ print CURL_XS " }\n";
+
+ # CURLSHOPT_
+ print CURL_XS <<HERE2
+
+ if (strncmp(name, "CURLSHOPT_", 10) == 0) {
+ name += 10;
+ switch (*name) {
+HERE2
+ ;
+
+ foreach my $next_initial ('A' .. 'Z') {
+ print CURL_XS " case '$next_initial':\n";
+ my $count = 0;
+ foreach my $option (sort @curlshopt) {
+ my $initial = substr($option, 10, 1);
+ if ($next_initial eq $initial) {
+ my $optsuff = substr($option, 10);
+ print CURL_XS
+" if (strEQ(name, \"$optsuff\")) return $option;\n";
+
+ $count++;
+ }
+ }
+ if ($count or $next_initial eq 'Z') {
+ print CURL_XS " break;\n";
+ }
+ }
+
+ print CURL_XS " };\n";
+ print CURL_XS " }\n";
+
+ # CURL_LOCK_DATA_
+ print CURL_XS <<HERE2
+ if (strncmp(name, "CURL_LOCK_DATA_", 15) == 0) {
+ name += 15;
+ switch (*name) {
+HERE2
+ ;
+
+ foreach my $next_initial ('A' .. 'Z') {
+ print CURL_XS " case '$next_initial':\n";
+ my $count = 0;
+ foreach my $option (sort @curllockopt) {
+ my $initial = substr($option, 15, 1);
+ if ($next_initial eq $initial) {
+ my $optsuff = substr($option, 15);
+ print CURL_XS
+" if (strEQ(name, \"$optsuff\")) return $option;\n";
+
+ $count++;
+ }
+ }
+ if ($count or $next_initial eq 'Z') {
+ print CURL_XS " break;\n";
+ }
+ }
+
+ print CURL_XS " };\n";
+ print CURL_XS " }\n";
+
+ print CURL_XS <<HERE
+
+ errno = EINVAL;
+ return 0;
+}
+HERE
+ ;
+
+ close(CURL_XS);
+
+ print "Building Easy.pm constants for your libcurl version\n";
+
+ open(EASY_PM, ">lib/WWW/Curl/Easy.pm") or die "Can't create lib/WWW/Curl/Easy.pm\n";
+ open(EASY_PM_IN, "template/Easy.pm.tmpl") or die "Can't read template/Easy.pm.tmpl\n";
+ while (my $line = <EASY_PM_IN>) {
+ if ($line !~ m/^\@CURLOPT_INCLUDE\@/) {
+ print EASY_PM $line;
+ } else {
+ foreach my $option (sort keys %cinit_types) {
+ next unless $option; # an empty CURLOPT_
+ print EASY_PM "CURLOPT_$option\n";
+ }
+ foreach my $option (sort @curlinfo) {
+ print EASY_PM $option . "\n";
+ }
+ }
+ }
+ close(EASY_PM);
+ close(EASY_PM_IN);
+
+ print "Building Share.pm constants for your libcurl version\n";
+
+ open(SHARE_PM, ">lib/WWW/Curl/Share.pm") or die "Can't create lib/WWW/Curl/Share.pm\n";
+ open(SHARE_PM_IN, "template/Share.pm.tmpl") or die "Can't read template/Share.pm.tmpl\n";
+ while (my $line = <SHARE_PM_IN>) {
+ if ($line !~ m/^(.*?)\@CURLSHOPT_INCLUDE\@/) {
+ print SHARE_PM $line;
+ } else {
+ foreach my $option (sort @curlshopt) {
+ print SHARE_PM $1 . $option . "\n";
+ }
+ foreach my $option (sort @curllockopt) {
+ print SHARE_PM $1 . $option . "\n";
+ }
+ }
+ }
+ close(SHARE_PM);
+ close(SHARE_PM_IN);
+}
+
+# Let Module::Install generate META.yml and other necessary files.
+WriteMakefile(
+ 'NAME' => 'WWW::Curl',
+ 'VERSION_FROM' => 'lib/WWW/Curl.pm', # finds $VERSION
+ 'LIBS' => "$ldflags $lflags", # e.g., '-lm'
+ 'INC' => $curl_d, # e.g., '-I/usr/include/other'
+ 'clean' => { FILES => "curlopt-constants.c head.out body.out" }
+);
+
diff --git a/README b/README
new file mode 100644
index 0000000..4de2b6b
--- /dev/null
+++ b/README
@@ -0,0 +1,59 @@
+README for Perl extension WWW::Curl
+
+The perl module WWW::Curl provides an interface to the cURL library "libcurl".
+See http://curl.haxx.se/ for more information on cURL and libcurl.
+
+This module requires libcurl and the corresponding headerfiles to be
+installed. You then may install this module via the usual way
+(for installation on Windows please see README.Win32):
+
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+The Makefile.PL script will try to use the 'curl-config' command to find
+the correct headers and libcurl path. If curl.h can't be found,
+either through curl-config or the internal list of guesses, you can specify
+an explicit path when building the Makefile:
+
+ perl Makefile.PL /home/foo/curl/include
+
+Minimum version requirements:
+
+This module, starting from version 4 requires at least Perl 5.6 and libcurl 7.10.8.
+These releases are more than 5 years old and they have multiple security vulnerabilities,
+it is advised that you upgrade as soon as possible to a more recent, secure version.
+Anything older than these versions of Perl and libcurl respectively are not supported.
+
+Windows specific fixes and patches are welcome as testing is mainly focused on linux.
+
+The module provides the same functionality, except as noted in the documentation,
+as libcurl provides to C programs. Please refer to the documentation of libcurl for the
+general interface description and the WWW::Curl POD for the differences.
+
+This software is distributed AS IS, WITHOUT WARRANTY OF ANY KIND, either
+express or implied.
+
+History:
+
+The author of the original relase of this software is Georg Horn <horn at koblenz-net.de>
+
+Parts of the callback support were added Forrest Cahoon
+<forrest.cahoon at merrillcorp.com>
+
+More callback support, many tests additional documentation and Makefile
+features have been added by Cris Bailiff <c.bailiff+curl at devsecure.com>
+
+Curl multi support has been added by Sebastian Riedel <sri at oook.de>
+
+The current maintainer is Cris Bailiff <c.bailiff+curl at devsecure.com>
+
+The latest version can be downloaded from http://curl.haxx.se/libcurl/perl/ or
+found on CPAN as the module WWW::Curl
+
+Copyright (C) 2000-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.
diff --git a/README.Win32 b/README.Win32
new file mode 100644
index 0000000..124b3bf
--- /dev/null
+++ b/README.Win32
@@ -0,0 +1,12 @@
+Installation on Windows need to be done manually, by editing Makefile.PL.
+
+1. Specify your curl include directory on the line "my @include = qw()".
+2. Specify the following parameters on the line below, where <DIR> is your curl directory like this:
+
+my ($cflags,$ldflags,$lflags) = ('-I"<DIR>\\include"', '-L"<DIR>\\lib"','-lcurl -lcurldll');
+
+<DIR> can be for example: "E:\\Perldev\\downloader\\curl-7.18.2-devel-mingw32" (without quotes);
+3. Save Makefile.PL.
+4. Execute "perl Makefile.PL";
+5. Execute "nmake" ( you may need nmake from Mircosoft, which can be downloaded from http://support.microsoft.com/default.aspx?scid=kb;en-us;Q132084 );
+6. Execute "nmake install".
diff --git a/SIGNATURE b/SIGNATURE
new file mode 100644
index 0000000..47e54c3
--- /dev/null
+++ b/SIGNATURE
@@ -0,0 +1,77 @@
+This file contains message digests of all files listed in MANIFEST,
+signed via the Module::Signature module, version 0.55.
+
+To verify the content in this distribution, first make sure you have
+Module::Signature installed, then type:
+
+ % cpansign -v
+
+It will check each file's integrity, as well as the signature's
+validity. If "==> Signature verified OK! <==" is not displayed,
+the distribution may already have been compromised, and you should
+not run its Makefile.PL or Build.PL.
+
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+SHA1 5e0b0a409e7a96524bfa071ca7f6e69b479191bd Changes
+SHA1 94dc4a32bc5f04deb4784b7b43f0bdf75ace8ed8 Curl.xs
+SHA1 94cbea5b3fb940e25cd4535d1c81bfd7d51dac3c LICENSE
+SHA1 7b182375c7584c6edb02989e822acaf8eac0a2e6 MANIFEST
+SHA1 667f7ccff205b983166e237ec8c30aacd3730684 META.yml
+SHA1 09323cfc717a3723d07bdcef329725bdd5f53624 Makefile.PL
+SHA1 322619607d1c95a9e29a13f74f0b1f1c92d0eed5 README
+SHA1 ed6f9f399075307a33bd02902ebbadbbbfbd8bab README.Win32
+SHA1 f0577f8f88a6703e9bca2d10196cfd293f3d6f2f inc/Module/Install.pm
+SHA1 5d574849849860b8cf06035cc6f1a306c66cb326 inc/Module/Install/Base.pm
+SHA1 c30f50bc2a1542dff17f84a54d9316f3c3c6299e inc/Module/Install/Can.pm
+SHA1 dd345661caea9a4f18c6f39cd50f22739cf2459d inc/Module/Install/External.pm
+SHA1 c8b486eb7a38823afb92e4d5b00dbb57b43351d5 inc/Module/Install/MakeMaker.pm
+SHA1 cd14c8de2a67b0f14df9bc20c27f3edabd359d1b inc/Module/Install/Makefile.pm
+SHA1 a2162aec678d21988fce47ab805cf01773196292 inc/Module/Install/Metadata.pm
+SHA1 5cd962de0a085f0ec7625c288df655d6d89dd2e7 lib/WWW/Curl.pm
+SHA1 e536065e27b7621f04619a71c8fff63b28d0d3e2 lib/WWW/Curl/Easy.pm
+SHA1 5909923d44832d435213746d562bf6b6075f5ef3 lib/WWW/Curl/Form.pm
+SHA1 46e9ebfbe386bbf319f26e6d3eec7e17772026b5 lib/WWW/Curl/Multi.pm
+SHA1 ab003199fd68eef6d58d382aded93c01a6efd9ae lib/WWW/Curl/Share.pm
+SHA1 259c953ed6189b23fddd5d4afd2c6df0fbb24930 t/00constants.t
+SHA1 553836d2318a3a2ff6982616b3237eb86c372194 t/01basic.t
+SHA1 07b63b1baca142a0e34e79633d0eb57684524bed t/02callbacks.t
+SHA1 905c848deb6492d539c5bdf89c49632a412af15a t/04abort-test.t
+SHA1 f9c842503835908a0687ab41655042a16b8b5112 t/05progress.t
+SHA1 6406f237b74c8b847d233e5570c2769d7445c307 t/06http-post.t
+SHA1 a252ca35aaf428e566e8f24bb59f3b56e4c8511d t/07ftp-upload.t
+SHA1 4811cc6d6af52f7adaf5e0a3332df980ea377d27 t/08ssl.t
+SHA1 ec62c062c627e7f52df512eae496223bd49ad2f3 t/09times.t
+SHA1 9499f362a6d06a19aa6bc41d3647dbd5dc5aca63 t/10errbuf.t
+SHA1 0390b52a5cbebb2896f38aecc8602acb20199790 t/13slowleak.t
+SHA1 f911c90eaf0fdd2ee60f913262ee6ca3236037f0 t/14duphandle.t
+SHA1 62551de2e5da5df233b296b716093cea8d791d70 t/15duphandle-callback.t
+SHA1 785507b3fa6f414298cdcf7ceaba1f9274aa07d2 t/16formpost.t
+SHA1 e784a874eb36fd5b16a12fc58365cce697ecbbab t/17slist.t
+SHA1 4246e503682d25acf461e539104d10b66e74889c t/18twinhandles.t
+SHA1 2924361d0713031b92c6b888f11e860d357837f7 t/meta.t
+SHA1 3cd20c1711b43058550922404f53a844fb2695e6 t/new/00constants.t
+SHA1 d9863d2e71f618a58d419534867cda8bd97dcfbf t/new/01basic.t
+SHA1 f4a3ea4777e0905d2cf0f6cdb18d04a39892c69d t/new/02header-callback.t
+SHA1 4aa1ab99c68aa4069a37419f439520b639fdb1e6 t/new/03body-callback.t
+SHA1 1470b63fda40ae09ce5faa891b6b30d804709c93 t/new/04abort.t
+SHA1 0d80cbde8d56c32ada4284f4738357cce1346b2b t/new/05progress.t
+SHA1 fda1cb27df8d45a6e00fb0f1ca664b815a5d9b47 t/new/06http-post.t
+SHA1 66c9b543a13c328a5b49b79d29aaa6345702ec71 t/new/07errbuf.t
+SHA1 63aecf6590d0d4268be8f23ba406d5b957710cde t/new/08duphandle.t
+SHA1 a20c28c3e06ebf836e837da4c5d1afb2998d5cc7 t/new/09duphandle-callback.t
+SHA1 eee30cfb57132e7c8bdd4a5dd2f32273ab222115 t/new/10multi-callback.t
+SHA1 20ec0bd03ff2600505d38623153a6eb3087b5814 t/new/README
+SHA1 ac25bfa56d36f19cbee72a968b06372e88602a61 t/pod-coverage.t
+SHA1 0190346d7072d458c8a10a45c19f86db641dcc48 t/pod.t
+SHA1 a049ec8232b28a1aedb130a67d992a63b8361a8d template/Easy.pm.tmpl
+SHA1 b4841adcad866b70d9d72f171d3d0f8f9d5b3c79 template/Share.pm.tmpl
+SHA1 468b011caaf4d54609b421027d7c6262a9260e89 typemap
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iEYEARECAAYFAkhv9rgACgkQ+22TSbOiiI+IdwCfeIufWW3r0CeAaS4UX2DQO7J8
+AHQAoJLDA/poQi2NCqHF1Mnpprc/qafN
+=w48a
+-----END PGP SIGNATURE-----
diff --git a/inc/Module/Install.pm b/inc/Module/Install.pm
new file mode 100644
index 0000000..8fb6b20
--- /dev/null
+++ b/inc/Module/Install.pm
@@ -0,0 +1,353 @@
+#line 1
+package Module::Install;
+
+# For any maintainers:
+# The load order for Module::Install is a bit magic.
+# It goes something like this...
+#
+# IF ( host has Module::Install installed, creating author mode ) {
+# 1. Makefile.PL calls "use inc::Module::Install"
+# 2. $INC{inc/Module/Install.pm} set to installed version of inc::Module::Install
+# 3. The installed version of inc::Module::Install loads
+# 4. inc::Module::Install calls "require Module::Install"
+# 5. The ./inc/ version of Module::Install loads
+# } ELSE {
+# 1. Makefile.PL calls "use inc::Module::Install"
+# 2. $INC{inc/Module/Install.pm} set to ./inc/ version of Module::Install
+# 3. The ./inc/ version of Module::Install loads
+# }
+
+BEGIN {
+ require 5.004;
+}
+use strict 'vars';
+
+use vars qw{$VERSION};
+BEGIN {
+ # All Module::Install core packages now require synchronised versions.
+ # This will be used to ensure we don't accidentally load old or
+ # different versions of modules.
+ # This is not enforced yet, but will be some time in the next few
+ # releases once we can make sure it won't clash with custom
+ # Module::Install extensions.
+ $VERSION = '0.75';
+
+ *inc::Module::Install::VERSION = *VERSION;
+ @inc::Module::Install::ISA = __PACKAGE__;
+
+}
+
+
+
+
+
+# Whether or not inc::Module::Install is actually loaded, the
+# $INC{inc/Module/Install.pm} is what will still get set as long as
+# the caller loaded module this in the documented manner.
+# If not set, the caller may NOT have loaded the bundled version, and thus
+# they may not have a MI version that works with the Makefile.PL. This would
+# result in false errors or unexpected behaviour. And we don't want that.
+my $file = join( '/', 'inc', split /::/, __PACKAGE__ ) . '.pm';
+unless ( $INC{$file} ) { die <<"END_DIE" }
+
+Please invoke ${\__PACKAGE__} with:
+
+ use inc::${\__PACKAGE__};
+
+not:
+
+ use ${\__PACKAGE__};
+
+END_DIE
+
+
+
+
+
+# If the script that is loading Module::Install is from the future,
+# then make will detect this and cause it to re-run over and over
+# again. This is bad. Rather than taking action to touch it (which
+# is unreliable on some platforms and requires write permissions)
+# for now we should catch this and refuse to run.
+if ( -f $0 and (stat($0))[9] > time ) { die <<"END_DIE" }
+
+Your installer $0 has a modification time in the future.
+
+This is known to create infinite loops in make.
+
+Please correct this, then run $0 again.
+
+END_DIE
+
+
+
+
+
+# Build.PL was formerly supported, but no longer is due to excessive
+# difficulty in implementing every single feature twice.
+if ( $0 =~ /Build.PL$/i ) { die <<"END_DIE" }
+
+Module::Install no longer supports Build.PL.
+
+It was impossible to maintain duel backends, and has been deprecated.
+
+Please remove all Build.PL files and only use the Makefile.PL installer.
+
+END_DIE
+
+
+
+
+
+# To save some more typing in Module::Install installers, every...
+# use inc::Module::Install
+# ...also acts as an implicit use strict.
+$^H |= strict::bits(qw(refs subs vars));
+
+
+
+
+
+use Cwd ();
+use File::Find ();
+use File::Path ();
+use FindBin;
+
+sub autoload {
+ my $self = shift;
+ my $who = $self->_caller;
+ my $cwd = Cwd::cwd();
+ my $sym = "${who}::AUTOLOAD";
+ $sym->{$cwd} = sub {
+ my $pwd = Cwd::cwd();
+ if ( my $code = $sym->{$pwd} ) {
+ # delegate back to parent dirs
+ goto &$code unless $cwd eq $pwd;
+ }
+ $$sym =~ /([^:]+)$/ or die "Cannot autoload $who - $sym";
+ unshift @_, ( $self, $1 );
+ goto &{$self->can('call')} unless uc($1) eq $1;
+ };
+}
+
+sub import {
+ my $class = shift;
+ my $self = $class->new(@_);
+ my $who = $self->_caller;
+
+ unless ( -f $self->{file} ) {
+ require "$self->{path}/$self->{dispatch}.pm";
+ File::Path::mkpath("$self->{prefix}/$self->{author}");
+ $self->{admin} = "$self->{name}::$self->{dispatch}"->new( _top => $self );
+ $self->{admin}->init;
+ @_ = ($class, _self => $self);
+ goto &{"$self->{name}::import"};
+ }
+
+ *{"${who}::AUTOLOAD"} = $self->autoload;
+ $self->preload;
+
+ # Unregister loader and worker packages so subdirs can use them again
+ delete $INC{"$self->{file}"};
+ delete $INC{"$self->{path}.pm"};
+
+ return 1;
+}
+
+sub preload {
+ my $self = shift;
+ unless ( $self->{extensions} ) {
+ $self->load_extensions(
+ "$self->{prefix}/$self->{path}", $self
+ );
+ }
+
+ my @exts = @{$self->{extensions}};
+ unless ( @exts ) {
+ my $admin = $self->{admin};
+ @exts = $admin->load_all_extensions;
+ }
+
+ my %seen;
+ foreach my $obj ( @exts ) {
+ while (my ($method, $glob) = each %{ref($obj) . '::'}) {
+ next unless $obj->can($method);
+ next if $method =~ /^_/;
+ next if $method eq uc($method);
+ $seen{$method}++;
+ }
+ }
+
+ my $who = $self->_caller;
+ foreach my $name ( sort keys %seen ) {
+ *{"${who}::$name"} = sub {
+ ${"${who}::AUTOLOAD"} = "${who}::$name";
+ goto &{"${who}::AUTOLOAD"};
+ };
+ }
+}
+
+sub new {
+ my ($class, %args) = @_;
+
+ # ignore the prefix on extension modules built from top level.
+ my $base_path = Cwd::abs_path($FindBin::Bin);
+ unless ( Cwd::abs_path(Cwd::cwd()) eq $base_path ) {
+ delete $args{prefix};
+ }
+
+ return $args{_self} if $args{_self};
+
+ $args{dispatch} ||= 'Admin';
+ $args{prefix} ||= 'inc';
+ $args{author} ||= ($^O eq 'VMS' ? '_author' : '.author');
+ $args{bundle} ||= 'inc/BUNDLES';
+ $args{base} ||= $base_path;
+ $class =~ s/^\Q$args{prefix}\E:://;
+ $args{name} ||= $class;
+ $args{version} ||= $class->VERSION;
+ unless ( $args{path} ) {
+ $args{path} = $args{name};
+ $args{path} =~ s!::!/!g;
+ }
+ $args{file} ||= "$args{base}/$args{prefix}/$args{path}.pm";
+ $args{wrote} = 0;
+
+ bless( \%args, $class );
+}
+
+sub call {
+ my ($self, $method) = @_;
+ my $obj = $self->load($method) or return;
+ splice(@_, 0, 2, $obj);
+ goto &{$obj->can($method)};
+}
+
+sub load {
+ my ($self, $method) = @_;
+
+ $self->load_extensions(
+ "$self->{prefix}/$self->{path}", $self
+ ) unless $self->{extensions};
+
+ foreach my $obj (@{$self->{extensions}}) {
+ return $obj if $obj->can($method);
+ }
+
+ my $admin = $self->{admin} or die <<"END_DIE";
+The '$method' method does not exist in the '$self->{prefix}' path!
+Please remove the '$self->{prefix}' directory and run $0 again to load it.
+END_DIE
+
+ my $obj = $admin->load($method, 1);
+ push @{$self->{extensions}}, $obj;
+
+ $obj;
+}
+
+sub load_extensions {
+ my ($self, $path, $top) = @_;
+
+ unless ( grep { lc $_ eq lc $self->{prefix} } @INC ) {
+ unshift @INC, $self->{prefix};
+ }
+
+ foreach my $rv ( $self->find_extensions($path) ) {
+ my ($file, $pkg) = @{$rv};
+ next if $self->{pathnames}{$pkg};
+
+ local $@;
+ my $new = eval { require $file; $pkg->can('new') };
+ unless ( $new ) {
+ warn $@ if $@;
+ next;
+ }
+ $self->{pathnames}{$pkg} = delete $INC{$file};
+ push @{$self->{extensions}}, &{$new}($pkg, _top => $top );
+ }
+
+ $self->{extensions} ||= [];
+}
+
+sub find_extensions {
+ my ($self, $path) = @_;
+
+ my @found;
+ File::Find::find( sub {
+ my $file = $File::Find::name;
+ return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is;
+ my $subpath = $1;
+ return if lc($subpath) eq lc($self->{dispatch});
+
+ $file = "$self->{path}/$subpath.pm";
+ my $pkg = "$self->{name}::$subpath";
+ $pkg =~ s!/!::!g;
+
+ # If we have a mixed-case package name, assume case has been preserved
+ # correctly. Otherwise, root through the file to locate the case-preserved
+ # version of the package name.
+ if ( $subpath eq lc($subpath) || $subpath eq uc($subpath) ) {
+ my $content = Module::Install::_read($subpath . '.pm');
+ my $in_pod = 0;
+ foreach ( split //, $content ) {
+ $in_pod = 1 if /^=\w/;
+ $in_pod = 0 if /^=cut/;
+ next if ($in_pod || /^=cut/); # skip pod text
+ next if /^\s*#/; # and comments
+ if ( m/^\s*package\s+($pkg)\s*;/i ) {
+ $pkg = $1;
+ last;
+ }
+ }
+ }
+
+ push @found, [ $file, $pkg ];
+ }, $path ) if -d $path;
+
+ @found;
+}
+
+
+
+
+
+#####################################################################
+# Utility Functions
+
+sub _caller {
+ my $depth = 0;
+ my $call = caller($depth);
+ while ( $call eq __PACKAGE__ ) {
+ $depth++;
+ $call = caller($depth);
+ }
+ return $call;
+}
+
+sub _read {
+ local *FH;
+ open FH, "< $_[0]" or die "open($_[0]): $!";
+ my $str = do { local $/; <FH> };
+ close FH or die "close($_[0]): $!";
+ return $str;
+}
+
+sub _write {
+ local *FH;
+ open FH, "> $_[0]" or die "open($_[0]): $!";
+ foreach ( 1 .. $#_ ) { print FH $_[$_] or die "print($_[0]): $!" }
+ close FH or die "close($_[0]): $!";
+}
+
+sub _version {
+ my $s = shift || 0;
+ $s =~ s/^(\d+)\.?//;
+ my $l = $1 || 0;
+ my @v = map { $_ . '0' x (3 - length $_) } $s =~ /(\d{1,3})\D?/g;
+ $l = $l . '.' . join '', @v if @v;
+ return $l + 0;
+}
+
+1;
+
+# Copyright 2008 Adam Kennedy.
diff --git a/inc/Module/Install/Base.pm b/inc/Module/Install/Base.pm
new file mode 100644
index 0000000..bd12f2b
--- /dev/null
+++ b/inc/Module/Install/Base.pm
@@ -0,0 +1,70 @@
+#line 1
+package Module::Install::Base;
+
+$VERSION = '0.75';
+
+# Suspend handler for "redefined" warnings
+BEGIN {
+ my $w = $SIG{__WARN__};
+ $SIG{__WARN__} = sub { $w };
+}
+
+### This is the ONLY module that shouldn't have strict on
+# use strict;
+
+#line 41
+
+sub new {
+ my ($class, %args) = @_;
+
+ foreach my $method ( qw(call load) ) {
+ *{"$class\::$method"} = sub {
+ shift()->_top->$method(@_);
+ } unless defined &{"$class\::$method"};
+ }
+
+ bless( \%args, $class );
+}
+
+#line 61
+
+sub AUTOLOAD {
+ my $self = shift;
+ local $@;
+ my $autoload = eval { $self->_top->autoload } or return;
+ goto &$autoload;
+}
+
+#line 76
+
+sub _top { $_[0]->{_top} }
+
+#line 89
+
+sub admin {
+ $_[0]->_top->{admin} or Module::Install::Base::FakeAdmin->new;
+}
+
+sub is_admin {
+ $_[0]->admin->VERSION;
+}
+
+sub DESTROY {}
+
+package Module::Install::Base::FakeAdmin;
+
+my $Fake;
+sub new { $Fake ||= bless(\@_, $_[0]) }
+
+sub AUTOLOAD {}
+
+sub DESTROY {}
+
+# Restore warning handler
+BEGIN {
+ $SIG{__WARN__} = $SIG{__WARN__}->();
+}
+
+1;
+
+#line 138
diff --git a/inc/Module/Install/Can.pm b/inc/Module/Install/Can.pm
new file mode 100644
index 0000000..3f436c7
--- /dev/null
+++ b/inc/Module/Install/Can.pm
@@ -0,0 +1,82 @@
+#line 1
+package Module::Install::Can;
+
+use strict;
+use Module::Install::Base;
+use Config ();
+### This adds a 5.005 Perl version dependency.
+### This is a bug and will be fixed.
+use File::Spec ();
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+ $VERSION = '0.75';
+ $ISCORE = 1;
+ @ISA = qw{Module::Install::Base};
+}
+
+# check if we can load some module
+### Upgrade this to not have to load the module if possible
+sub can_use {
+ my ($self, $mod, $ver) = @_;
+ $mod =~ s{::|\\}{/}g;
+ $mod .= '.pm' unless $mod =~ /\.pm$/i;
+
+ my $pkg = $mod;
+ $pkg =~ s{/}{::}g;
+ $pkg =~ s{\.pm$}{}i;
+
+ local $@;
+ eval { require $mod; $pkg->VERSION($ver || 0); 1 };
+}
+
+# check if we can run some command
+sub can_run {
+ my ($self, $cmd) = @_;
+
+ my $_cmd = $cmd;
+ return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd));
+
+ for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') {
+ my $abs = File::Spec->catfile($dir, $_[1]);
+ return $abs if (-x $abs or $abs = MM->maybe_command($abs));
+ }
+
+ return;
+}
+
+# can we locate a (the) C compiler
+sub can_cc {
+ my $self = shift;
+ my @chunks = split(/ /, $Config::Config{cc}) or return;
+
+ # $Config{cc} may contain args; try to find out the program part
+ while (@chunks) {
+ return $self->can_run("@chunks") || (pop(@chunks), next);
+ }
+
+ return;
+}
+
+# Fix Cygwin bug on maybe_command();
+if ( $^O eq 'cygwin' ) {
+ require ExtUtils::MM_Cygwin;
+ require ExtUtils::MM_Win32;
+ if ( ! defined(&ExtUtils::MM_Cygwin::maybe_command) ) {
+ *ExtUtils::MM_Cygwin::maybe_command = sub {
+ my ($self, $file) = @_;
+ if ($file =~ m{^/cygdrive/}i and ExtUtils::MM_Win32->can('maybe_command')) {
+ ExtUtils::MM_Win32->maybe_command($file);
+ } else {
+ ExtUtils::MM_Unix->maybe_command($file);
+ }
+ }
+ }
+}
+
+1;
+
+__END__
+
+#line 157
diff --git a/inc/Module/Install/External.pm b/inc/Module/Install/External.pm
new file mode 100644
index 0000000..cd12a0c
--- /dev/null
+++ b/inc/Module/Install/External.pm
@@ -0,0 +1,66 @@
+#line 1
+package Module::Install::External;
+
+# Provides dependency declarations for external non-Perl things
+
+use strict;
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+ $VERSION = '0.75';
+ $ISCORE = 1;
+ @ISA = qw{Module::Install::Base};
+}
+
+sub requires_external_cc {
+ my $self = shift;
+
+ # We need a C compiler, use the can_cc method for this
+ unless ( $self->can_cc ) {
+ print "Unresolvable missing external dependency.\n";
+ print "This package requires a C compiler.\n";
+ print STDERR "NA: Unable to build distribution on this platform.\n";
+ exit(0);
+ }
+
+ # Unlike some of the other modules, while we need to specify a
+ # C compiler as a dep, it needs to be a build-time dependency.
+
+ 1;
+}
+
+sub requires_external_bin {
+ my ($self, $bin, $version) = @_;
+ if ( $version ) {
+ die "requires_external_bin does not support versions yet";
+ }
+
+ # Load the package containing can_run early,
+ # to avoid breaking the message below.
+ $self->load('can_run');
+
+ # Locate the bin
+ print "Locating required external dependency bin:$bin...";
+ my $found_bin = $self->can_run( $bin );
+ if ( $found_bin ) {
+ print " found at $found_bin.\n";
+ } else {
+ print " missing.\n";
+ print "Unresolvable missing external dependency.\n";
+ print "Please install '$bin' seperately and try again.\n";
+ print STDERR "NA: Unable to build distribution on this platform.\n";
+ exit(0);
+ }
+
+ # Once we have some way to specify external deps, do it here.
+ # In the mean time, continue as normal.
+
+ 1;
+}
+
+1;
+
+__END__
+
+#line 138
diff --git a/inc/Module/Install/MakeMaker.pm b/inc/Module/Install/MakeMaker.pm
new file mode 100644
index 0000000..5ea325a
--- /dev/null
+++ b/inc/Module/Install/MakeMaker.pm
@@ -0,0 +1,50 @@
+#line 1
+package Module::Install::MakeMaker;
+
+use strict;
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+ $VERSION = '0.75';
+ $ISCORE = 1;
+ @ISA = qw{Module::Install::Base};
+}
+
+my $makefile;
+
+sub WriteMakefile {
+ my ($self, %args) = @_;
+ $makefile = $self->load('Makefile');
+
+ # mapping between MakeMaker and META.yml keys
+ $args{MODULE_NAME} = $args{NAME};
+ unless ( $args{NAME} = $args{DISTNAME} or ! $args{MODULE_NAME} ) {
+ $args{NAME} = $args{MODULE_NAME};
+ $args{NAME} =~ s/::/-/g;
+ }
+
+ foreach my $key ( qw{name module_name version version_from abstract author installdirs} ) {
+ my $value = delete($args{uc($key)}) or next;
+ $self->$key($value);
+ }
+
+ if (my $prereq = delete($args{PREREQ_PM})) {
+ while (my($k,$v) = each %$prereq) {
+ $self->requires($k,$v);
+ }
+ }
+
+ # put the remaining args to makemaker_args
+ $self->makemaker_args(%args);
+}
+
+END {
+ if ( $makefile ) {
+ $makefile->write;
+ $makefile->Meta->write;
+ }
+}
+
+1;
diff --git a/inc/Module/Install/Makefile.pm b/inc/Module/Install/Makefile.pm
new file mode 100644
index 0000000..b7c2ba9
--- /dev/null
+++ b/inc/Module/Install/Makefile.pm
@@ -0,0 +1,245 @@
+#line 1
+package Module::Install::Makefile;
+
+use strict 'vars';
+use Module::Install::Base;
+use ExtUtils::MakeMaker ();
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+ $VERSION = '0.75';
+ $ISCORE = 1;
+ @ISA = qw{Module::Install::Base};
+}
+
+sub Makefile { $_[0] }
+
+my %seen = ();
+
+sub prompt {
+ shift;
+
+ # Infinite loop protection
+ my @c = caller();
+ if ( ++$seen{"$c[1]|$c[2]|$_[0]"} > 3 ) {
+ die "Caught an potential prompt infinite loop ($c[1]|$c[2]|$_[0])";
+ }
+
+ # In automated testing, always use defaults
+ if ( $ENV{AUTOMATED_TESTING} and ! $ENV{PERL_MM_USE_DEFAULT} ) {
+ local $ENV{PERL_MM_USE_DEFAULT} = 1;
+ goto &ExtUtils::MakeMaker::prompt;
+ } else {
+ goto &ExtUtils::MakeMaker::prompt;
+ }
+}
+
+sub makemaker_args {
+ my $self = shift;
+ my $args = ($self->{makemaker_args} ||= {});
+ %$args = ( %$args, @_ ) if @_;
+ $args;
+}
+
+# For mm args that take multiple space-seperated args,
+# append an argument to the current list.
+sub makemaker_append {
+ my $self = sShift;
+ my $name = shift;
+ my $args = $self->makemaker_args;
+ $args->{name} = defined $args->{$name}
+ ? join( ' ', $args->{name}, @_ )
+ : join( ' ', @_ );
+}
+
+sub build_subdirs {
+ my $self = shift;
+ my $subdirs = $self->makemaker_args->{DIR} ||= [];
+ for my $subdir (@_) {
+ push @$subdirs, $subdir;
+ }
+}
+
+sub clean_files {
+ my $self = shift;
+ my $clean = $self->makemaker_args->{clean} ||= {};
+ %$clean = (
+ %$clean,
+ FILES => join ' ', grep { length $_ } ($clean->{FILES} || (), @_),
+ );
+}
+
+sub realclean_files {
+ my $self = shift;
+ my $realclean = $self->makemaker_args->{realclean} ||= {};
+ %$realclean = (
+ %$realclean,
+ FILES => join ' ', grep { length $_ } ($realclean->{FILES} || (), @_),
+ );
+}
+
+sub libs {
+ my $self = shift;
+ my $libs = ref $_[0] ? shift : [ shift ];
+ $self->makemaker_args( LIBS => $libs );
+}
+
+sub inc {
+ my $self = shift;
+ $self->makemaker_args( INC => shift );
+}
+
+my %test_dir = ();
+
+sub _wanted_t {
+ /\.t$/ and -f $_ and $test_dir{$File::Find::dir} = 1;
+}
+
+sub tests_recursive {
+ my $self = shift;
+ if ( $self->tests ) {
+ die "tests_recursive will not work if tests are already defined";
+ }
+ my $dir = shift || 't';
+ unless ( -d $dir ) {
+ die "tests_recursive dir '$dir' does not exist";
+ }
+ %test_dir = ();
+ require File::Find;
+ File::Find::find( \&_wanted_t, $dir );
+ $self->tests( join ' ', map { "$_/*.t" } sort keys %test_dir );
+}
+
+sub write {
+ my $self = shift;
+ die "&Makefile->write() takes no arguments\n" if @_;
+
+ # Make sure we have a new enough
+ require ExtUtils::MakeMaker;
+ $self->configure_requires( 'ExtUtils::MakeMaker' => $ExtUtils::MakeMaker::VERSION );
+
+ # Generate the
+ my $args = $self->makemaker_args;
+ $args->{DISTNAME} = $self->name;
+ $args->{NAME} = $self->module_name || $self->name;
+ $args->{VERSION} = $self->version;
+ $args->{NAME} =~ s/-/::/g;
+ if ( $self->tests ) {
+ $args->{test} = { TESTS => $self->tests };
+ }
+ if ($] >= 5.005) {
+ $args->{ABSTRACT} = $self->abstract;
+ $args->{AUTHOR} = $self->author;
+ }
+ if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) {
+ $args->{NO_META} = 1;
+ }
+ if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 and $self->sign ) {
+ $args->{SIGN} = 1;
+ }
+ unless ( $self->is_admin ) {
+ delete $args->{SIGN};
+ }
+
+ # merge both kinds of requires into prereq_pm
+ my $prereq = ($args->{PREREQ_PM} ||= {});
+ %$prereq = ( %$prereq,
+ map { @$_ }
+ map { @$_ }
+ grep $_,
+ ($self->configure_requires, $self->build_requires, $self->requires)
+ );
+
+ # Remove any reference to perl, PREREQ_PM doesn't support it
+ delete $args->{PREREQ_PM}->{perl};
+
+ # merge both kinds of requires into prereq_pm
+ my $subdirs = ($args->{DIR} ||= []);
+ if ($self->bundles) {
+ foreach my $bundle (@{ $self->bundles }) {
+ my ($file, $dir) = @$bundle;
+ push @$subdirs, $dir if -d $dir;
+ delete $prereq->{$file};
+ }
+ }
+
+ if ( my $perl_version = $self->perl_version ) {
+ eval "use $perl_version; 1"
+ or die "ERROR: perl: Version $] is installed, "
+ . "but we need version >= $perl_version";
+ }
+
+ $args->{INSTALLDIRS} = $self->installdirs;
+
+ my %args = map { ( $_ => $args->{$_} ) } grep {defined($args->{$_})} keys %$args;
+
+ my $user_preop = delete $args{dist}->{PREOP};
+ if (my $preop = $self->admin->preop($user_preop)) {
+ $args{dist} = $preop;
+ }
+
+ my $mm = ExtUtils::MakeMaker::WriteMakefile(%args);
+ $self->fix_up_makefile($mm->{FIRST_MAKEFILE} || 'Makefile');
+}
+
+sub fix_up_makefile {
+ my $self = shift;
+ my $makefile_name = shift;
+ my $top_class = ref($self->_top) || '';
+ my $top_version = $self->_top->VERSION || '';
+
+ my $preamble = $self->preamble
+ ? "# Preamble by $top_class $top_version\n"
+ . $self->preamble
+ : '';
+ my $postamble = "# Postamble by $top_class $top_version\n"
+ . ($self->postamble || '');
+
+ local *MAKEFILE;
+ open MAKEFILE, "< $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+ my $makefile = do { local $/; <MAKEFILE> };
+ close MAKEFILE or die $!;
+
+ $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /;
+ $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g;
+ $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g;
+ $makefile =~ s/^(FULLPERL = .*)/$1 "-Iinc"/m;
+ $makefile =~ s/^(PERL = .*)/$1 "-Iinc"/m;
+
+ # Module::Install will never be used to build the Core Perl
+ # Sometimes PERL_LIB and PERL_ARCHLIB get written anyway, which breaks
+ # PREFIX/PERL5LIB, and thus, install_share. Blank them if they exist
+ $makefile =~ s/^PERL_LIB = .+/PERL_LIB =/m;
+ #$makefile =~ s/^PERL_ARCHLIB = .+/PERL_ARCHLIB =/m;
+
+ # Perl 5.005 mentions PERL_LIB explicitly, so we have to remove that as well.
+ $makefile =~ s/(\"?)-I\$\(PERL_LIB\)\1//g;
+
+ # XXX - This is currently unused; not sure if it breaks other MM-users
+ # $makefile =~ s/^pm_to_blib\s+:\s+/pm_to_blib :: /mg;
+
+ open MAKEFILE, "> $makefile_name" or die "fix_up_makefile: Couldn't open $makefile_name: $!";
+ print MAKEFILE "$preamble$makefile$postamble" or die $!;
+ close MAKEFILE or die $!;
+
+ 1;
+}
+
+sub preamble {
+ my ($self, $text) = @_;
+ $self->{preamble} = $text . $self->{preamble} if defined $text;
+ $self->{preamble};
+}
+
+sub postamble {
+ my ($self, $text) = @_;
+ $self->{postamble} ||= $self->admin->postamble;
+ $self->{postamble} .= $text if defined $text;
+ $self->{postamble}
+}
+
+1;
+
+__END__
+
+#line 371
diff --git a/inc/Module/Install/Metadata.pm b/inc/Module/Install/Metadata.pm
new file mode 100644
index 0000000..ce26bf6
--- /dev/null
+++ b/inc/Module/Install/Metadata.pm
@@ -0,0 +1,407 @@
+#line 1
+package Module::Install::Metadata;
+
+use strict 'vars';
+use Module::Install::Base;
+
+use vars qw{$VERSION $ISCORE @ISA};
+BEGIN {
+ $VERSION = '0.75';
+ $ISCORE = 1;
+ @ISA = qw{Module::Install::Base};
+}
+
+my @scalar_keys = qw{
+ name
+ module_name
+ abstract
+ author
+ version
+ license
+ distribution_type
+ perl_version
+ tests
+ installdirs
+};
+
+my @tuple_keys = qw{
+ configure_requires
+ build_requires
+ requires
+ recommends
+ bundles
+ resources
+};
+
+sub Meta { shift }
+sub Meta_ScalarKeys { @scalar_keys }
+sub Meta_TupleKeys { @tuple_keys }
+
+foreach my $key (@scalar_keys) {
+ *$key = sub {
+ my $self = shift;
+ return $self->{values}{$key} if defined wantarray and !@_;
+ $self->{values}{$key} = shift;
+ return $self;
+ };
+}
+
+sub requires {
+ my $self = shift;
+ while ( @_ ) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ push @{ $self->{values}->{requires} }, [ $module, $version ];
+ }
+ $self->{values}{requires};
+}
+
+sub build_requires {
+ my $self = shift;
+ while ( @_ ) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ push @{ $self->{values}->{build_requires} }, [ $module, $version ];
+ }
+ $self->{values}{build_requires};
+}
+
+sub configure_requires {
+ my $self = shift;
+ while ( @_ ) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ push @{ $self->{values}->{configure_requires} }, [ $module, $version ];
+ }
+ $self->{values}->{configure_requires};
+}
+
+sub recommends {
+ my $self = shift;
+ while ( @_ ) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ push @{ $self->{values}->{recommends} }, [ $module, $version ];
+ }
+ $self->{values}->{recommends};
+}
+
+sub bundles {
+ my $self = shift;
+ while ( @_ ) {
+ my $module = shift or last;
+ my $version = shift || 0;
+ push @{ $self->{values}->{bundles} }, [ $module, $version ];
+ }
+ $self->{values}->{bundles};
+}
+
+# Resource handling
+sub resources {
+ my $self = shift;
+ while ( @_ ) {
+ my $resource = shift or last;
+ my $value = shift or next;
+ push @{ $self->{values}->{resources} }, [ $resource, $value ];
+ }
+ $self->{values}->{resources};
+}
+
+sub repository {
+ my $self = shift;
+ $self->resources( repository => shift );
+ return 1;
+}
+
+# Aliases for build_requires that will have alternative
+# meanings in some future version of META.yml.
+sub test_requires { shift->build_requires(@_) }
+sub install_requires { shift->build_requires(@_) }
+
+# Aliases for installdirs options
+sub install_as_core { $_[0]->installdirs('perl') }
+sub install_as_cpan { $_[0]->installdirs('site') }
+sub install_as_site { $_[0]->installdirs('site') }
+sub install_as_vendor { $_[0]->installdirs('vendor') }
+
+sub sign {
+ my $self = shift;
+ return $self->{'values'}{'sign'} if defined wantarray and ! @_;
+ $self->{'values'}{'sign'} = ( @_ ? $_[0] : 1 );
+ return $self;
+}
+
+sub dynamic_config {
+ my $self = shift;
+ unless ( @_ ) {
+ warn "You MUST provide an explicit true/false value to dynamic_config, skipping\n";
+ return $self;
+ }
+ $self->{values}{dynamic_config} = $_[0] ? 1 : 0;
+ return $self;
+}
+
+sub all_from {
+ my ( $self, $file ) = @_;
+
+ unless ( defined($file) ) {
+ my $name = $self->name
+ or die "all_from called with no args without setting name() first";
+ $file = join('/', 'lib', split(/-/, $name)) . '.pm';
+ $file =~ s{.*/}{} unless -e $file;
+ die "all_from: cannot find $file from $name" unless -e $file;
+ }
+
+ # Some methods pull from POD instead of code.
+ # If there is a matching .pod, use that instead
+ my $pod = $file;
+ $pod =~ s/\.pm$/.pod/i;
+ $pod = $file unless -e $pod;
+
+ # Pull the different values
+ $self->name_from($file) unless $self->name;
+ $self->version_from($file) unless $self->version;
+ $self->perl_version_from($file) unless $self->perl_version;
+ $self->author_from($pod) unless $self->author;
+ $self->license_from($pod) unless $self->license;
+ $self->abstract_from($pod) unless $self->abstract;
+
+ return 1;
+}
+
+sub provides {
+ my $self = shift;
+ my $provides = ( $self->{values}{provides} ||= {} );
+ %$provides = (%$provides, @_) if @_;
+ return $provides;
+}
+
+sub auto_provides {
+ my $self = shift;
+ return $self unless $self->is_admin;
+ unless (-e 'MANIFEST') {
+ warn "Cannot deduce auto_provides without a MANIFEST, skipping\n";
+ return $self;
+ }
+ # Avoid spurious warnings as we are not checking manifest here.
+ local $SIG{__WARN__} = sub {1};
+ require ExtUtils::Manifest;
+ local *ExtUtils::Manifest::manicheck = sub { return };
+
+ require Module::Build;
+ my $build = Module::Build->new(
+ dist_name => $self->name,
+ dist_version => $self->version,
+ license => $self->license,
+ );
+ $self->provides( %{ $build->find_dist_packages || {} } );
+}
+
+sub feature {
+ my $self = shift;
+ my $name = shift;
+ my $features = ( $self->{values}{features} ||= [] );
+ my $mods;
+
+ if ( @_ == 1 and ref( $_[0] ) ) {
+ # The user used ->feature like ->features by passing in the second
+ # argument as a reference. Accomodate for that.
+ $mods = $_[0];
+ } else {
+ $mods = \@_;
+ }
+
+ my $count = 0;
+ push @$features, (
+ $name => [
+ map {
+ ref($_) ? ( ref($_) eq 'HASH' ) ? %$_ : @$_ : $_
+ } @$mods
+ ]
+ );
+
+ return @$features;
+}
+
+sub features {
+ my $self = shift;
+ while ( my ( $name, $mods ) = splice( @_, 0, 2 ) ) {
+ $self->feature( $name, @$mods );
+ }
+ return $self->{values}->{features}
+ ? @{ $self->{values}->{features} }
+ : ();
+}
+
+sub no_index {
+ my $self = shift;
+ my $type = shift;
+ push @{ $self->{values}{no_index}{$type} }, @_ if $type;
+ return $self->{values}{no_index};
+}
+
+sub read {
+ my $self = shift;
+ $self->include_deps( 'YAML::Tiny', 0 );
+
+ require YAML::Tiny;
+ my $data = YAML::Tiny::LoadFile('META.yml');
+
+ # Call methods explicitly in case user has already set some values.
+ while ( my ( $key, $value ) = each %$data ) {
+ next unless $self->can($key);
+ if ( ref $value eq 'HASH' ) {
+ while ( my ( $module, $version ) = each %$value ) {
+ $self->can($key)->($self, $module => $version );
+ }
+ } else {
+ $self->can($key)->($self, $value);
+ }
+ }
+ return $self;
+}
+
+sub write {
+ my $self = shift;
+ return $self unless $self->is_admin;
+ $self->admin->write_meta;
+ return $self;
+}
+
+sub version_from {
+ require ExtUtils::MM_Unix;
+ my ( $self, $file ) = @_;
+ $self->version( ExtUtils::MM_Unix->parse_version($file) );
+}
+
+sub abstract_from {
+ require ExtUtils::MM_Unix;
+ my ( $self, $file ) = @_;
+ $self->abstract(
+ bless(
+ { DISTNAME => $self->name },
+ 'ExtUtils::MM_Unix'
+ )->parse_abstract($file)
+ );
+}
+
+# Add both distribution and module name
+sub name_from {
+ my ($self, $file) = @_;
+ if (
+ Module::Install::_read($file) =~ m/
+ ^ \s*
+ package \s*
+ ([\w:]+)
+ \s* ;
+ /ixms
+ ) {
+ my ($name, $module_name) = ($1, $1);
+ $name =~ s{::}{-}g;
+ $self->name($name);
+ unless ( $self->module_name ) {
+ $self->module_name($module_name);
+ }
+ } else {
+ die "Cannot determine name from $file\n";
+ }
+}
+
+sub perl_version_from {
+ my $self = shift;
+ if (
+ Module::Install::_read($_[0]) =~ m/
+ ^
+ (?:use|require) \s*
+ v?
+ ([\d_\.]+)
+ \s* ;
+ /ixms
+ ) {
+ my $perl_version = $1;
+ $perl_version =~ s{_}{}g;
+ $self->perl_version($perl_version);
+ } else {
+ warn "Cannot determine perl version info from $_[0]\n";
+ return;
+ }
+}
+
+sub author_from {
+ my $self = shift;
+ my $content = Module::Install::_read($_[0]);
+ if ($content =~ m/
+ =head \d \s+ (?:authors?)\b \s*
+ ([^\n]*)
+ |
+ =head \d \s+ (?:licen[cs]e|licensing|copyright|legal)\b \s*
+ .*? copyright .*? \d\d\d[\d.]+ \s* (?:\bby\b)? \s*
+ ([^\n]*)
+ /ixms) {
+ my $author = $1 || $2;
+ $author =~ s{E<lt>}{<}g;
+ $author =~ s{E<gt>}{>}g;
+ $self->author($author);
+ } else {
+ warn "Cannot determine author info from $_[0]\n";
+ }
+}
+
+sub license_from {
+ my $self = shift;
+ if (
+ Module::Install::_read($_[0]) =~ m/
+ (
+ =head \d \s+
+ (?:licen[cs]e|licensing|copyright|legal)\b
+ .*?
+ )
+ (=head\\d.*|=cut.*|)
+ \z
+ /ixms ) {
+ my $license_text = $1;
+ my @phrases = (
+ 'under the same (?:terms|license) as perl itself' => 'perl', 1,
+ 'GNU public license' => 'gpl', 1,
+ 'GNU lesser public license' => 'lgpl', 1,
+ 'BSD license' => 'bsd', 1,
+ 'Artistic license' => 'artistic', 1,
+ 'GPL' => 'gpl', 1,
+ 'LGPL' => 'lgpl', 1,
+ 'BSD' => 'bsd', 1,
+ 'Artistic' => 'artistic', 1,
+ 'MIT' => 'mit', 1,
+ 'proprietary' => 'proprietary', 0,
+ );
+ while ( my ($pattern, $license, $osi) = splice(@phrases, 0, 3) ) {
+ $pattern =~ s{\s+}{\\s+}g;
+ if ( $license_text =~ /\b$pattern\b/i ) {
+ if ( $osi and $license_text =~ /All rights reserved/i ) {
+ print "WARNING: 'All rights reserved' in copyright may invalidate Open Source license.\n";
+ }
+ $self->license($license);
+ return 1;
+ }
+ }
+ }
+
+ warn "Cannot determine license info from $_[0]\n";
+ return 'unknown';
+}
+
+sub install_script {
+ my $self = shift;
+ my $args = $self->makemaker_args;
+ my $exe = $args->{EXE_FILES} ||= [];
+ foreach ( @_ ) {
+ if ( -f $_ ) {
+ push @$exe, $_;
+ } elsif ( -d 'script' and -f "script/$_" ) {
+ push @$exe, "script/$_";
+ } else {
+ die "Cannot find script '$_'";
+ }
+ }
+}
+
+1;
diff --git a/lib/WWW/Curl.pm b/lib/WWW/Curl.pm
new file mode 100644
index 0000000..cdb52c9
--- /dev/null
+++ b/lib/WWW/Curl.pm
@@ -0,0 +1,334 @@
+package WWW::Curl;
+
+use strict;
+use warnings;
+use vars qw(@ISA $VERSION);
+use DynaLoader;
+
+BEGIN {
+ $VERSION = '4.05';
+ @ISA = qw(DynaLoader);
+ __PACKAGE__->bootstrap;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+WWW::Curl - Perl extension interface for libcurl
+
+=head1 SYNOPSIS
+
+ use WWW::Curl;
+ print $WWW::Curl::VERSION;
+
+
+=head1 DESCRIPTION
+
+WWW::Curl is a Perl extension interface for libcurl.
+
+=head1 DOCUMENTATION
+
+This module provides a Perl interface to libcurl. It is not intended to be a standalone module
+and because of this, the main libcurl documentation should be consulted for API details at
+L<http://curl.haxx.se>. The documentation you're reading right now only contains the Perl specific
+details, some sample code and the differences between the C API and the Perl one.
+
+=head1 WWW::Curl::Easy
+
+The name might be confusing, it originates from libcurl. This is not an ::Easy module
+in the sense normally used on CPAN.
+
+Here is a small snippet of making a request with WWW::Curl::Easy.
+
+ use strict;
+ use warnings;
+ use WWW::Curl::Easy;
+
+ # Setting the options
+ my $curl = new WWW::Curl::Easy;
+
+ $curl->setopt(CURLOPT_HEADER,1);
+ $curl->setopt(CURLOPT_URL, 'http://example.com');
+ my $response_body;
+
+ # NOTE - do not use a typeglob here. A reference to a typeglob is okay though.
+ open (my $fileb, ">", \$response_body);
+ $curl->setopt(CURLOPT_WRITEDATA,$fileb);
+
+ # Starts the actual request
+ my $retcode = $curl->perform;
+
+ # Looking at the results...
+ if ($retcode == 0) {
+ print("Transfer went ok\n");
+ my $response_code = $curl->getinfo(CURLINFO_HTTP_CODE);
+ # judge result and next action based on $response_code
+ print("Received response: $response_body\n");
+ } else {
+ print("An error happened: ".$curl->strerror($retcode)." ($retcode)\n");
+ }
+
+
+=head1 WWW::Curl::Multi
+
+ use strict;
+ use warnings;
+ use WWW::Curl::Easy;
+ use WWW::Curl::Multi;
+
+ my %easy;
+ my $curl = WWW::Curl::Easy->new;
+ my $curl_id = '13'; # This should be a handle unique id.
+ $easy{$curl_id} = $curl;
+ my $active_handles = 0;
+
+ $curl->setopt(CURLOPT_PRIVATE,$curl_id);
+ # do the usual configuration on the handle
+ ...
+
+ my $curlm = WWW::Curl::Multi->new;
+
+ # Add some easy handles
+ $curlm->add_handle($curl);
+ $active_handles++;
+
+ while ($active_handles) {
+ my $active_transfers = $curlm->perform;
+ if ($active_transfers != $active_handles) {
+ while (my ($id,$return_value) = $curlm->info_read) {
+ if ($id) {
+ $active_handles--;
+ my $actual_easy_handle = $easy{$id};
+ # do the usual result/error checking routine here
+ ...
+ # letting the curl handle get garbage collected, or we leak memory.
+ delete $easy{$id};
+ }
+ }
+ }
+ }
+
+This interface is different than what the C API does. $curlm->perform is non-blocking and performs
+requests in parallel. The method does a little work and then returns control, therefor it has to be called
+periodically to get the job done. It's return value is the number of unfinished requests.
+
+When the number of unfinished requests changes compared to the number of active handles, $curlm->info_read
+should be checked for finished requests. It returns one handle and it's return value at a time, or an empty list
+if there are no more finished requests. $curlm->info_read calls remove_handle on the given easy handle automatically,
+internally. The easy handle will still remain available until it goes out of scope, this action just detaches it from
+multi.
+
+Please make sure that the easy handle does not get garbage collected until after the multi handle finishes processing it,
+or bad things happen.
+
+The multi handle does not need to be cleaned up, when it goes out of scope it calls the required cleanup methods
+automatically.
+
+It is possible to use $curlm->add_handle to add further requests to be processed after $curlm->perform has been called.
+WWW::Curl::Multi doesn't care about the order. It is possible to process all requests for a multi handle and then add
+a new batch of easy handles for processing.
+
+=head1 WWW::Curl::Share
+
+ use WWW::CURL::Share;
+ my $curlsh = new WWW::Curl::Share;
+ $curlsh->setopt(CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+ $curlsh->setopt(CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
+ $curl->setopt(CURLOPT_SHARE, $curlsh);
+ $curlsh->setopt(CURLSHOPT_UNSHARE, CURL_LOCK_DATA_COOKIE);
+ $curlsh->setopt(CURLSHOPT_UNSHARE, CURL_LOCK_DATA_DNS);
+
+WWW::Curl::Share is an extension to WWW::Curl::Easy which makes it possible
+to use a single cookies/dns cache for several Easy handles.
+
+It's usable methods are:
+
+ $curlsh = new WWW::Curl::Share
+ This method constructs a new WWW::Curl::Share object.
+
+ $curlsh->setopt(CURLSHOPT_SHARE, $value );
+ Enables share for:
+ CURL_LOCK_DATA_COOKIE use single cookies database
+ CURL_LOCK_DATA_DNS use single DNS cache
+ $curlsh->setopt(CURLSHOPT_UNSHARE, $value );
+ Disable share for given $value (see CURLSHOPT_SHARE)
+
+ $curlsh->strerror( ErrNo )
+ This method returns a string describing the CURLSHcode error
+ code passed in the argument errornum.
+
+This is how you enable sharing for a specific WWW::Curl::Easy handle:
+
+ $curl->setopt(CURLOPT_SHARE, $curlsh)
+ Attach share object to WWW::Curl::Easy instance
+
+
+=head1 COMPATIBILITY
+
+=over
+
+=item curl_easy_setopt
+
+Most of the options should work, however some might not. Please send reports, tests and patches to fix
+those.
+
+=item curl_easy_escape
+
+Not implemented. Since equivalent Perl code is easily produced, this method will only made
+available for interface completeness, if ever.
+
+=item curl_easy_init
+
+Used only internally. The standard Perl way of initializing an object should be used,
+ C<< my $curl = WWW::Curl::Easy->new; >>.
+
+=item curl_easy_cleanup
+
+Used only internally. Curl object cleanup happens when the handle goes out of scope.
+
+=item curl_easy_duphandle
+
+Should be working for most cases, however do not change the value of options which accept
+a list/arrayref value on a duped handle, otherwise memory leaks or crashes will happen.
+This behaviour will be fixed in the future.
+
+=item curl_easy_pause
+
+Not implemented.
+
+=item curl_easy_reset
+
+Not implemented.
+
+=item curl_easy_unescape
+
+Not implemented. Trivial Perl replacements are available.
+
+=item curl_escape
+
+Not implemented and won't be as this method is considered deprecated.
+
+=item curl_formadd
+
+Not yet implemented.
+
+=item curl_formfree
+
+When WWW::Curl::Form support is added, this function will be used internally,
+but won't be accessible from the public API.
+
+=item curl_free
+
+Used internally. Not exposed through the public API, as this call has no relevance
+to Perl code.
+
+=item curl_getdate
+
+Not implemented. This function is easily replaced by Perl code and as such, most likely
+it won't be implemented.
+
+=item curl_global_cleanup
+
+Only used internally, not exposed through the public API.
+
+=item curl_global_init
+
+Only used internally, not exposed through the public API.
+
+=item curl_global_init_mem
+
+Not implemented.
+
+=item curl_slist_append
+
+Only used internally, not exposed through the public API.
+
+=item curl_slist_free_all
+
+Only used internally, not exposed through the public API.
+
+=item curl_unescape
+
+Not implemented and won't be, as this method is considered deprecated.
+
+=item curl_version_info
+
+Not yet implemented.
+
+=item curl_multi_*
+
+Most methods are either not exposed through the WWW::Curl::Multi API or they behave differently
+than it's C counterpart. Please see the section about WWW::Curl::Multi above.
+
+=back
+
+=head1 USAGE CASES
+
+The standard Perl WWW module, LWP should be used in most cases to work with
+the HTTP or FTP protocol from Perl. However, there are some cases where LWP doesn't
+perform well. One is speed and the other is paralellism. WWW::Curl is much faster,
+uses much less CPU cycles and it's capable of non-blocking parallel requests.
+
+In some cases, for example when building a web crawler, cpu usage and parallel downloads are
+important considerations. It can be desirable to use WWW::Curl to do the heavy-lifting of
+a large number of downloads and wrap the resulting data into a Perl-friendly structure by
+HTTP::Response.
+
+=head1 CHANGES
+
+Version 4.01 adds several bugfixes. See Changes file.
+
+Version 4.00 added new documentation, the build system changed to Module::Install,
+the test suite was rewritten to use Test::More, a new calling syntax for WWW::Curl::Multi
+was added, memory leak and other bugfixes added, Perl 5.6 and libcurl 7.10.8 as minimum
+requirements for this module were set.
+
+Version 3.12 is a bugfix for a missing Share.pm.in file in the release.
+
+Version 3.11 added WWW::Curl::Share.
+
+Version 3.10 adds the WWW::Curl::Share interface by Anton Federov
+and large file options after a contribution from Mark Hindley.
+
+Version 3.02 adds some backwards compatibility for scripts still using
+'WWW::Curl::easy' names.
+
+Version 3.01 added some support for pre-multi versions of libcurl.
+
+Version 3.00 adds WWW::Curl::Multi interface, and a new module names
+following perl conventions (WWW::Curl::Easy rather than WWW::Curl::easy),
+by Sebastian Riedel <sri at cpan.org>.
+
+Version 2.00 of WWW::Curl::easy is a renaming of the previous version
+(named Curl::easy), to follow CPAN naming guidelines, by Cris Bailiff.
+
+Versions 1.30, a (hopefully) threadable, object-oriented,
+multiple-callback compatible version of Curl::easy was substantially
+reworked from the previous Curl::easy release (1.21) by Cris Bailiff.
+
+=head1 AUTHORS
+
+Currently maintained by Cris Bailiff <c.bailiff+curl at devsecure.com>
+
+Original Author Georg Horn <horn@koblenz-net.de>, with additional callback,
+pod and test work by Cris Bailiff <c.bailiff+curl@devsecure.com> and
+Forrest Cahoon <forrest.cahoon@merrillcorp.com>. Sebastian Riedel added ::Multi
+and Anton Fedorov (datacompboy <at> mail.ru) added ::Share. Balint Szilakszi
+repackaged the module into a more modern form.
+
+=head1 COPYRIGHT
+
+Copyright (C) 2000-2005,2008 Daniel Stenberg, Cris Bailiff,
+Sebastian Riedel, Balint Szilakszi 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.
+
+=head1 SEE ALSO
+
+http://curl.haxx.se
diff --git a/lib/WWW/Curl/Easy.pm b/lib/WWW/Curl/Easy.pm
new file mode 100644
index 0000000..a636534
--- /dev/null
+++ b/lib/WWW/Curl/Easy.pm
@@ -0,0 +1,245 @@
+package WWW::Curl::Easy;
+
+use strict;
+use warnings;
+use Carp;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
+
+$VERSION = '4.05';
+
+require WWW::Curl;
+require Exporter;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+@EXPORT = qw(
+CURLOPT_APPEND
+CURLOPT_AUTOREFERER
+CURLOPT_BUFFERSIZE
+CURLOPT_CAINFO
+CURLOPT_CAPATH
+CURLOPT_CLOSEPOLICY
+CURLOPT_CONNECTTIMEOUT
+CURLOPT_CONNECTTIMEOUT_MS
+CURLOPT_CONNECT_ONLY
+CURLOPT_CONV_FROM_NETWORK_FUNCTION
+CURLOPT_CONV_FROM_UTF8_FUNCTION
+CURLOPT_CONV_TO_NETWORK_FUNCTION
+CURLOPT_COOKIE
+CURLOPT_COOKIEFILE
+CURLOPT_COOKIEJAR
+CURLOPT_COOKIELIST
+CURLOPT_COOKIESESSION
+CURLOPT_COPYPOSTFIELDS
+CURLOPT_CRLF
+CURLOPT_CUSTOMREQUEST
+CURLOPT_DEBUGDATA
+CURLOPT_DEBUGFUNCTION
+CURLOPT_DIRLISTONLY
+CURLOPT_DNS_CACHE_TIMEOUT
+CURLOPT_DNS_USE_GLOBAL_CACHE
+CURLOPT_EGDSOCKET
+CURLOPT_ENCODING
+CURLOPT_ERRORBUFFER
+CURLOPT_FAILONERROR
+CURLOPT_FILE
+CURLOPT_FILETIME
+CURLOPT_FOLLOWLOCATION
+CURLOPT_FORBID_REUSE
+CURLOPT_FRESH_CONNECT
+CURLOPT_FTPAPPEND
+CURLOPT_FTPLISTONLY
+CURLOPT_FTPPORT
+CURLOPT_FTPSSLAUTH
+CURLOPT_FTP_ACCOUNT
+CURLOPT_FTP_ALTERNATIVE_TO_USER
+CURLOPT_FTP_CREATE_MISSING_DIRS
+CURLOPT_FTP_FILEMETHOD
+CURLOPT_FTP_RESPONSE_TIMEOUT
+CURLOPT_FTP_SKIP_PASV_IP
+CURLOPT_FTP_SSL
+CURLOPT_FTP_SSL_CCC
+CURLOPT_FTP_USE_EPRT
+CURLOPT_FTP_USE_EPSV
+CURLOPT_HEADER
+CURLOPT_HEADERDATA
+CURLOPT_HEADERFUNCTION
+CURLOPT_HTTP200ALIASES
+CURLOPT_HTTPAUTH
+CURLOPT_HTTPGET
+CURLOPT_HTTPHEADER
+CURLOPT_HTTPPOST
+CURLOPT_HTTPPROXYTUNNEL
+CURLOPT_HTTP_CONTENT_DECODING
+CURLOPT_HTTP_TRANSFER_DECODING
+CURLOPT_HTTP_VERSION
+CURLOPT_IGNORE_CONTENT_LENGTH
+CURLOPT_INFILE
+CURLOPT_INFILESIZE
+CURLOPT_INFILESIZE_LARGE
+CURLOPT_INTERFACE
+CURLOPT_IOCTLDATA
+CURLOPT_IOCTLFUNCTION
+CURLOPT_IPRESOLVE
+CURLOPT_KEYPASSWD
+CURLOPT_KRB4LEVEL
+CURLOPT_KRBLEVEL
+CURLOPT_LOCALPORT
+CURLOPT_LOCALPORTRANGE
+CURLOPT_LOW_SPEED_LIMIT
+CURLOPT_LOW_SPEED_TIME
+CURLOPT_MAXCONNECTS
+CURLOPT_MAXFILESIZE
+CURLOPT_MAXFILESIZE_LARGE
+CURLOPT_MAXREDIRS
+CURLOPT_MAX_RECV_SPEED_LARGE
+CURLOPT_MAX_SEND_SPEED_LARGE
+CURLOPT_NETRC
+CURLOPT_NETRC_FILE
+CURLOPT_NEW_DIRECTORY_PERMS
+CURLOPT_NEW_FILE_PERMS
+CURLOPT_NOBODY
+CURLOPT_NOPROGRESS
+CURLOPT_NOSIGNAL
+CURLOPT_OPENSOCKETDATA
+CURLOPT_OPENSOCKETFUNCTION
+CURLOPT_PORT
+CURLOPT_POST
+CURLOPT_POST301
+CURLOPT_POSTFIELDS
+CURLOPT_POSTFIELDSIZE
+CURLOPT_POSTFIELDSIZE_LARGE
+CURLOPT_POSTQUOTE
+CURLOPT_PREQUOTE
+CURLOPT_PRIVATE
+CURLOPT_PROGRESSDATA
+CURLOPT_PROGRESSFUNCTION
+CURLOPT_PROXY
+CURLOPT_PROXYAUTH
+CURLOPT_PROXYPORT
+CURLOPT_PROXYTYPE
+CURLOPT_PROXYUSERPWD
+CURLOPT_PROXY_TRANSFER_MODE
+CURLOPT_PUT
+CURLOPT_QUOTE
+CURLOPT_RANDOM_FILE
+CURLOPT_RANGE
+CURLOPT_READFUNCTION
+CURLOPT_REFERER
+CURLOPT_RESUME_FROM
+CURLOPT_RESUME_FROM_LARGE
+CURLOPT_SEEKDATA
+CURLOPT_SEEKFUNCTION
+CURLOPT_SHARE
+CURLOPT_SOCKOPTDATA
+CURLOPT_SOCKOPTFUNCTION
+CURLOPT_SSH_AUTH_TYPES
+CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
+CURLOPT_SSH_PRIVATE_KEYFILE
+CURLOPT_SSH_PUBLIC_KEYFILE
+CURLOPT_SSLCERT
+CURLOPT_SSLCERTPASSWD
+CURLOPT_SSLCERTTYPE
+CURLOPT_SSLENGINE
+CURLOPT_SSLENGINE_DEFAULT
+CURLOPT_SSLKEY
+CURLOPT_SSLKEYPASSWD
+CURLOPT_SSLKEYTYPE
+CURLOPT_SSLVERSION
+CURLOPT_SSL_CIPHER_LIST
+CURLOPT_SSL_CTX_DATA
+CURLOPT_SSL_CTX_FUNCTION
+CURLOPT_SSL_SESSIONID_CACHE
+CURLOPT_SSL_VERIFYHOST
+CURLOPT_SSL_VERIFYPEER
+CURLOPT_STDERR
+CURLOPT_TCP_NODELAY
+CURLOPT_TELNETOPTIONS
+CURLOPT_TIMECONDITION
+CURLOPT_TIMEOUT
+CURLOPT_TIMEOUT_MS
+CURLOPT_TIMEVALUE
+CURLOPT_TRANSFERTEXT
+CURLOPT_UNRESTRICTED_AUTH
+CURLOPT_UPLOAD
+CURLOPT_URL
+CURLOPT_USERAGENT
+CURLOPT_USERPWD
+CURLOPT_USE_SSL
+CURLOPT_VERBOSE
+CURLOPT_WRITEDATA
+CURLOPT_WRITEFUNCTION
+CURLOPT_WRITEHEADER
+CURLOPT_WRITEINFO
+CURLINFO_CONNECT_TIME
+CURLINFO_CONTENT_LENGTH_DOWNLOAD
+CURLINFO_CONTENT_LENGTH_UPLOAD
+CURLINFO_CONTENT_TYPE
+CURLINFO_COOKIELIST
+CURLINFO_DATA_IN
+CURLINFO_DATA_OUT
+CURLINFO_EFFECTIVE_URL
+CURLINFO_END
+CURLINFO_FILETIME
+CURLINFO_FTP_ENTRY_PATH
+CURLINFO_HEADER_IN
+CURLINFO_HEADER_OUT
+CURLINFO_HEADER_SIZE
+CURLINFO_HTTPAUTH_AVAIL
+CURLINFO_HTTP_CODE
+CURLINFO_HTTP_CONNECTCODE
+CURLINFO_LASTONE
+CURLINFO_LASTSOCKET
+CURLINFO_NAMELOOKUP_TIME
+CURLINFO_NONE
+CURLINFO_NUM_CONNECTS
+CURLINFO_OS_ERRNO
+CURLINFO_PRETRANSFER_TIME
+CURLINFO_PRIVATE
+CURLINFO_PROXYAUTH_AVAIL
+CURLINFO_REDIRECT_COUNT
+CURLINFO_REDIRECT_TIME
+CURLINFO_REDIRECT_URL
+CURLINFO_REQUEST_SIZE
+CURLINFO_RESPONSE_CODE
+CURLINFO_SIZE_DOWNLOAD
+CURLINFO_SIZE_UPLOAD
+CURLINFO_SPEED_DOWNLOAD
+CURLINFO_SPEED_UPLOAD
+CURLINFO_SSL_DATA_IN
+CURLINFO_SSL_DATA_OUT
+CURLINFO_SSL_ENGINES
+CURLINFO_SSL_VERIFYRESULT
+CURLINFO_STARTTRANSFER_TIME
+CURLINFO_TEXT
+CURLINFO_TOTAL_TIME
+);
+
+$WWW::Curl::Easy::headers = "";
+$WWW::Curl::Easy::content = "";
+
+sub AUTOLOAD {
+
+ # This AUTOLOAD is used to 'autoload' constants from the constant()
+ # XS function.
+
+ ( my $constname = $AUTOLOAD ) =~ s/.*:://;
+ return constant( $constname, 0 );
+}
+
+1;
+
+__END__
+
+Copyright (C) 2000-2005,2008 Daniel Stenberg, Cris Bailiff,
+Sebastian Riedel, 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.
diff --git a/lib/WWW/Curl/Form.pm b/lib/WWW/Curl/Form.pm
new file mode 100644
index 0000000..dd69c68
--- /dev/null
+++ b/lib/WWW/Curl/Form.pm
@@ -0,0 +1,12 @@
+package WWW::Curl::Form;
+use strict;
+
+# In development!
+#
+#require WWW::Curl;
+#use vars qw(@ISA @EXPORT_OK);
+#require Exporter;
+#require AutoLoader;
+# @ISA = qw(Exporter DynaLoader);
+
+1;
diff --git a/lib/WWW/Curl/Multi.pm b/lib/WWW/Curl/Multi.pm
new file mode 100644
index 0000000..cc64628
--- /dev/null
+++ b/lib/WWW/Curl/Multi.pm
@@ -0,0 +1,14 @@
+package WWW::Curl::Multi;
+
+use strict;
+use WWW::Curl;
+
+1;
+__END__
+
+Copyright (C) 2004 Sebastian Riedel, 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.
diff --git a/lib/WWW/Curl/Share.pm b/lib/WWW/Curl/Share.pm
new file mode 100644
index 0000000..4da2183
--- /dev/null
+++ b/lib/WWW/Curl/Share.pm
@@ -0,0 +1,49 @@
+package WWW::Curl::Share;
+
+use strict;
+use warnings;
+use Carp;
+use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD);
+
+use WWW::Curl;
+require Exporter;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+
+@EXPORT = qw(
+CURLSHOPT_LAST
+CURLSHOPT_LOCKFUNC
+CURLSHOPT_NONE
+CURLSHOPT_SHARE
+CURLSHOPT_UNLOCKFUNC
+CURLSHOPT_UNSHARE
+CURLSHOPT_USERDATA
+CURL_LOCK_DATA_CONNECT
+CURL_LOCK_DATA_COOKIE
+CURL_LOCK_DATA_DNS
+CURL_LOCK_DATA_LAST
+CURL_LOCK_DATA_NONE
+CURL_LOCK_DATA_SHARE
+CURL_LOCK_DATA_SSL_SESSION
+);
+
+sub AUTOLOAD {
+
+ # This AUTOLOAD is used to 'autoload' constants from the constant()
+ # XS function.
+
+ ( my $constname = $AUTOLOAD ) =~ s/.*:://;
+ return constant( $constname, 0 );
+}
+
+1;
+__END__
+
+
+Copyright (C) 2008, Anton Fedorov (datacompboy <at> mail.ru)
+
+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.
diff --git a/packaging/perl-WWW-Curl.spec b/packaging/perl-WWW-Curl.spec
new file mode 100644
index 0000000..f49c5a4
--- /dev/null
+++ b/packaging/perl-WWW-Curl.spec
@@ -0,0 +1,131 @@
+#
+# - WWW::Curl -
+# This spec file was automatically generated by cpan2rpm [ver: 2.028]
+# The following arguments were used:
+# --version 4.05 WWW-Curl
+# For more information on cpan2rpm please visit: http://perl.arix.com/
+#
+
+%define pkgname WWW-Curl
+%define filelist %{pkgname}-%{version}-filelist
+%define NVR %{pkgname}-%{version}-%{release}
+%define maketest 0
+
+name: perl-WWW-Curl
+summary: WWW::Curl is a Perl extension interface for libcurl
+version: 4.05
+release: 4.1
+vendor: szbalint@cpan.org
+packager: Arix International <cpan2rpm@arix.com>
+license: Artistic
+group: Applications/CPAN
+url: http://www.cpan.org
+buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n)
+BuildRequires: pkgconfig(libcurl)
+BuildRequires: pkgconfig(libcrypto)
+BuildRequires: pkgconfig(libcares)
+Requires: libcurl
+BuildRequires: perl-Test-Pod-Coverage
+prefix: %(echo %{_prefix})
+source: %{name}-%{version}.tar.bz2
+
+%description
+This module provides a Perl interface to libcurl. It is not intended to be a standalone module
+and because of this, the main libcurl documentation should be consulted for API details at
+L<http://curl.haxx.se>. The documentation you're reading right now only contains the Perl specific
+details, some sample code and the differences between the C API and the Perl one.
+
+#
+# This package was generated automatically with the cpan2rpm
+# utility. To get this software or for more information
+# please visit: http://perl.arix.com/
+#
+
+%prep
+%setup
+chmod -R u+w %{_builddir}/%{name}-%{version}
+
+%build
+grep -rsl '^#!.*perl' . |
+grep -v '.bak$' |xargs --no-run-if-empty \
+%__perl -MExtUtils::MakeMaker -e 'MY->fixin(@ARGV)'
+CFLAGS="$RPM_OPT_FLAGS"
+%{__perl} Makefile.PL `%{__perl} -MExtUtils::MakeMaker -e ' print qq|PREFIX=%{buildroot}%{_prefix}| if \$ExtUtils::MakeMaker::VERSION =~ /5\.9[1-6]|6\.0[0-5]/ '`
+%{__make}
+%if %maketest
+%{__make} test
+%endif
+
+%install
+[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
+
+%{makeinstall} `%{__perl} -MExtUtils::MakeMaker -e ' print \$ExtUtils::MakeMaker::VERSION <= 6.05 ? qq|PREFIX=%{buildroot}%{_prefix}| : qq|DESTDIR=%{buildroot}| '`
+
+cmd=/usr/share/spec-helper/compress_files
+[ -x $cmd ] || cmd=/usr/lib/rpm/brp-compress
+[ -x $cmd ] && $cmd
+
+# SuSE Linux
+if [ -e /etc/SuSE-release -o -e /etc/UnitedLinux-release ]
+then
+ %{__mkdir_p} %{buildroot}/var/adm/perl-modules
+ %{__cat} `find %{buildroot} -name "perllocal.pod"` \
+ | %{__sed} -e s+%{buildroot}++g \
+ > %{buildroot}/var/adm/perl-modules/%{name}
+fi
+
+# remove special files
+find %{buildroot} -name "perllocal.pod" \
+ -o -name ".packlist" \
+ -o -name "*.bs" \
+ |xargs -i rm -f {}
+
+# no empty directories
+find %{buildroot}%{_prefix} \
+ -type d -depth \
+ -exec rmdir {} \; 2>/dev/null
+
+%{__perl} -MFile::Find -le '
+ find({ wanted => \&wanted, no_chdir => 1}, "%{buildroot}");
+ print "%doc README.Win32 template inc Changes README LICENSE";
+ for my $x (sort @dirs, @files) {
+ push @ret, $x unless indirs($x);
+ }
+ print join "\n", sort @ret;
+
+ sub wanted {
+ return if /auto$/;
+
+ local $_ = $File::Find::name;
+ my $f = $_; s|^\Q%{buildroot}\E||;
+ return unless length;
+ return $files[@files] = $_ if -f $f;
+
+ $d = $_;
+ /\Q$d\E/ && return for reverse sort @INC;
+ $d =~ /\Q$_\E/ && return
+ for qw|/etc %_prefix/man %_prefix/bin %_prefix/share|;
+
+ $dirs[@dirs] = $_;
+ }
+
+ sub indirs {
+ my $x = shift;
+ $x =~ /^\Q$_\E\// && $x ne $_ && return 1 for @dirs;
+ }
+ ' > %filelist
+
+[ -z %filelist ] && {
+ echo "ERROR: empty %files listing"
+ exit -1
+ }
+
+%clean
+[ "%{buildroot}" != "/" ] && rm -rf %{buildroot}
+
+%files -f %filelist
+%defattr(-,root,root)
+
+%changelog
+* Thu Dec 4 2008 root@vsltsus103
+- Initial build.
diff --git a/t/00constants.t b/t/00constants.t
new file mode 100644
index 0000000..fba9aba
--- /dev/null
+++ b/t/00constants.t
@@ -0,0 +1,8 @@
+#!perl
+use strict;
+use warnings;
+use Test::More tests => 2;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+ok (CURLOPT_URL == 10000+2, "Constant loaded ok");
diff --git a/t/01basic.t b/t/01basic.t
new file mode 100644
index 0000000..5b4d175
--- /dev/null
+++ b/t/01basic.t
@@ -0,0 +1,48 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 14;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+my $memfile = '';
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS");
+ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION");
+ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT");
+$curl->setopt(CURLOPT_HEADER, 1);
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEDATA,$body), "Setting CURLOPT_WRITEDATA");
+
+ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+my @myheaders;
+$myheaders[0] = "Server: www";
+$myheaders[1] = "User-Agent: Perl interface for libcURL";
+ok(! $curl->setopt(CURLOPT_HTTPHEADER, \@myheaders), "Setting CURLOPT_HTTPHEADER");
+
+my $retcode = $curl->perform();
+
+ok(! $retcode, "Curl return code ok");
+
+my $bytes = $curl->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ok( $bytes, "getinfo returns non-zero number of bytes");
+my $realurl = $curl->getinfo(CURLINFO_EFFECTIVE_URL);
+ok( $realurl, "getinfo returns CURLINFO_EFFECTIVE_URL");
+my $httpcode = $curl->getinfo(CURLINFO_HTTP_CODE);
+ok( $httpcode, "getinfo returns CURLINFO_HTTP_CODE");
+#diag ("Bytes: $bytes");
+#diag ("realurl: $realurl");
+#diag ("httpcode: $httpcode");
diff --git a/t/02callbacks.t b/t/02callbacks.t
new file mode 100644
index 0000000..76aff4f
--- /dev/null
+++ b/t/02callbacks.t
@@ -0,0 +1,41 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 7;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+$curl->setopt(CURLOPT_NOPROGRESS, 1);
+$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
+$curl->setopt(CURLOPT_TIMEOUT, 30);
+
+my $head = tempfile();
+$curl->setopt(CURLOPT_WRITEHEADER, $head);
+
+my $body = tempfile();
+$curl->setopt(CURLOPT_FILE,$body);
+
+$curl->setopt(CURLOPT_URL, $url);
+
+my $header_called = 0;
+sub header_callback { $header_called = 1; return length($_[0]) };
+my $body_called = 0;
+sub body_callback { $body_called++;return length($_[0]) };
+
+
+
+ok (! $curl->setopt(CURLOPT_HEADERFUNCTION, \&header_callback), "CURLOPT_HEADERFUNCTION set");
+ok (! $curl->setopt(CURLOPT_WRITEFUNCTION, \&body_callback), "CURLOPT_WRITEFUNCTION set");
+
+$curl->perform();
+ok($header_called, "CURLOPT_HEADERFUNCTION callback was used");
+ok($body_called, "CURLOPT_WRITEFUNCTION callback was used");
diff --git a/t/04abort-test.t b/t/04abort-test.t
new file mode 100644
index 0000000..c653c49
--- /dev/null
+++ b/t/04abort-test.t
@@ -0,0 +1,36 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 8;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+$curl->setopt(CURLOPT_NOPROGRESS, 1);
+$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
+$curl->setopt(CURLOPT_TIMEOUT, 30);
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_FILE,$body), "Setting CURLOPT_FILE");
+
+ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+my $body_abort_called = 0;
+sub body_abort_callback { $body_abort_called++; return -1 };
+
+$curl->setopt(CURLOPT_WRITEFUNCTION, \&body_abort_callback);
+
+ok( $curl->perform(), "Request fails, Abort succeeds");
+
+ok( $body_abort_called, "Abort function was invoked");
diff --git a/t/05progress.t b/t/05progress.t
new file mode 100644
index 0000000..52129ea
--- /dev/null
+++ b/t/05progress.t
@@ -0,0 +1,54 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 16;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+ok(! $curl->setopt(CURLOPT_NOPROGRESS, 0), "Setting CURLOPT_NOPROGRESS");
+ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION");
+ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT");
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_FILE,$body), "Setting CURLOPT_FILE");
+
+ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+my @myheaders;
+$myheaders[0] = "Server: www";
+$myheaders[1] = "User-Agent: Perl interface for libcURL";
+ok(! $curl->setopt(CURLOPT_HTTPHEADER, \@myheaders), "Setting CURLOPT_HTTPHEADER");
+
+ok(! $curl->setopt(CURLOPT_PROGRESSDATA,"making progress!"), "Setting CURLOPT_PROGRESSDATA");
+
+my $progress_called = 0;
+my $last_dlnow = 0;
+sub prog_callb
+{
+ my ($clientp,$dltotal,$dlnow,$ultotal,$ulnow)=@_;
+ $last_dlnow=$dlnow;
+ $progress_called++;
+ return 0;
+}
+
+ok (! $curl->setopt(CURLOPT_PROGRESSFUNCTION, \&prog_callb), "Setting CURLOPT_PROGRESSFUNCTION");
+
+ok (! $curl->setopt(CURLOPT_NOPROGRESS, 0), "Turning progress meter back on");
+
+ok (! $curl->perform(), "Performing perform");
+
+ok ($progress_called, "Progress callback called");
+
+ok ($last_dlnow, "Last downloaded chunk non-zero");
diff --git a/t/06http-post.t b/t/06http-post.t
new file mode 100644
index 0000000..fa1f1e7
--- /dev/null
+++ b/t/06http-post.t
@@ -0,0 +1,58 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More skip_all => "Not performing http POST/upload tests";
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS");
+ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION");
+ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT");
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_FILE, $body), "Setting CURLOPT_FILE");
+
+ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+my @myheaders;
+$myheaders[0] = "Server: www";
+$myheaders[1] = "User-Agent: Perl interface for libcURL";
+ok(! $curl->setopt(CURLOPT_HTTPHEADER, \@myheaders), "Setting CURLOPT_HTTPHEADER");
+
+my $read_max=1000;
+
+sub read_callb
+{
+ my ($maxlen,$sv)=@_;
+ if ($read_max > 0) {
+ my $len=int($read_max/3)+1;
+ my $data = chr(ord('A')+rand(26))x$len;
+ $read_max=$read_max-length($data);
+ return $data;
+ } else {
+ return "";
+ }
+}
+
+#
+# XXX - Outdated POST mechanism!
+#
+
+ok(! $curl->setopt(CURLOPT_READFUNCTION,\&read_callb), "Setting CURLOPT_READFUNCTION");
+ok(! $curl->setopt(CURLOPT_INFILESIZE,$read_max ), "Setting CURLOPT_INFILESIZE");
+ok(! $curl->setopt(CURLOPT_UPLOAD,1 ), "Setting CURLOPT_UPLOAD");
+ok(! $curl->setopt(CURLOPT_CUSTOMREQUEST,"POST" ), "Setting CURLOPT_CUSTOMREQUEST");
+
+ok(! $curl->perform(), "Performing perform");
diff --git a/t/07ftp-upload.t b/t/07ftp-upload.t
new file mode 100644
index 0000000..27a57e2
--- /dev/null
+++ b/t/07ftp-upload.t
@@ -0,0 +1,86 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More skip_all => "Not performing ftp upload tests";
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $count=1;
+
+
+# Read URL to get, defaulting to environment variable if supplied
+my $url=$ENV{CURL_TEST_URL_FTP} || "";
+if (!$url) {
+ print "1..0 # No test ftp URL supplied - skipping test\n";
+ exit;
+}
+
+print "1..8\n";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+if ($curl == 0) {
+ print "not ";
+}
+print "ok ".++$count."\n";
+
+$curl->setopt(CURLOPT_NOPROGRESS, 1);
+$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
+$curl->setopt(CURLOPT_TIMEOUT, 30);
+
+open HEAD, ">head.out";
+$curl->setopt(CURLOPT_WRITEHEADER, \*HEAD);
+print "ok ".++$count."\n";
+
+open BODY, ">body.out";
+$curl->setopt(CURLOPT_FILE, \*body);
+print "ok ".++$count."\n";
+
+$curl->setopt(CURLOPT_URL, $url);
+
+print "ok ".++$count."\n";
+
+# Now do an ftp upload:
+
+$curl->setopt(CURLOPT_UPLOAD, 1);
+
+
+my $read_max=1000;
+$curl->setopt(CURLOPT_INFILESIZE,$read_max );
+print "ok ".++$count."\n";
+
+sub read_callb
+{
+ my ($maxlen,$sv)=@_;
+ print "# perl read_callback has been called!\n";
+ print "# max data size: $maxlen - $read_max bytes needed\n";
+
+ if ($read_max > 0) {
+ my $len=int($read_max/3)+1;
+ my $data = chr(ord('A')+rand(26))x$len;
+ print "# generated max/3=", int($read_max/3)+1, " characters to be uploaded - $data.\n";
+ $read_max=$read_max-length($data);
+ return $data;
+ } else {
+ return "";
+ }
+}
+
+# Use perl read callback to read data to be uploaded
+$curl->setopt(CURLOPT_READFUNCTION, \&read_callb);
+
+# Use perl passwd callback to read password for login to ftp server
+$curl->setopt(CURLOPT_USERPWD, "ftp\@");
+
+print "ok ".++$count."\n";
+
+# Go get it
+my $code;
+if (($code=$curl->perform()) == 0) {
+ my $bytes=$curl->getinfo(CURLINFO_SIZE_UPLOAD);
+ print "ok ".++$count." $bytes bytes transferred\n";
+} else {
+ # We can acces the error message in $errbuf here
+ print "not ok ".++$count." ftpcode= $code, errbuf=".$curl->errbuf."\n";
+}
diff --git a/t/08ssl.t b/t/08ssl.t
new file mode 100644
index 0000000..28759e5
--- /dev/null
+++ b/t/08ssl.t
@@ -0,0 +1,92 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More;
+use File::Temp qw/tempfile/;
+use WWW::Curl::Easy;
+
+# list of tests
+# site-url, verifypeer(0,1), verifyhost(0,2), result(0=ok, 1=fail), result-openssl0.9.5
+my $url_list=[
+
+ [ 'https://65.205.248.243/', 0, 0, 0 , 0 ], # www.thawte.com
+# [ 'https://65.205.248.243/', 0, 2, 1 , 1 ], # www.thawte.com
+ [ 'https://65.205.249.60/', 0, 0, 0 , 0 ], # www.verisign.com
+# [ 'https://65.205.249.60/', 0, 2, 1 , 1 ], # www.verisign.com
+ [ 'https://www.microsoft.com/', 0, 0, 0 , 0 ],
+ [ 'https://www.microsoft.com/', 0, 0, 0 , 0 ],
+ [ 'https://www.verisign.com/', 1, 2, 0 , 0 ], # verisign have had broken ssl - do this first
+ [ 'https://www.verisign.com/', 0, 0, 0 , 0 ],
+ [ 'https://www.verisign.com/', 0, 0, 0 , 0 ],
+ [ 'https://www.verisign.com/', 0, 2, 0 , 0 ],
+ [ 'https://www.thawte.com/', 0, 0, 0 , 0 ],
+ [ 'https://www.thawte.com/', 0, 2, 0 , 0 ],
+
+# libcurl < 7.9.3 crashes with more than 5 ssl hosts per handle.
+
+ [ 'https://www.rapidssl.com/', 0, 0, 0 , 0],
+ [ 'https://www.rapidssl.com/', 0, 2, 0 , 0],
+ [ 'https://www.rapidssl.com/', 1, 0, 1 , 0],
+ [ 'https://www.rapidssl.com/', 1, 2, 1 , 0],
+];
+
+
+if (&WWW::Curl::Easy::version() !~ /ssl/i) {
+ plan skip_all => 'libcurl was compiled without ssl support, skipping ssl tests';
+} else {
+ plan tests => scalar(@{$url_list})+7;
+}
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something'); #1
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module'); #2
+
+ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS"); #3
+ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION"); #4
+ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT"); #5
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER"); #6
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_FILE, $body), "Setting CURLOPT_FILE"); #7
+
+my @myheaders;
+$myheaders[0] = "User-Agent: Verifying SSL functions in WWW::Curl perl interface for libcURL";
+$curl->setopt(CURLOPT_HTTPHEADER, \@myheaders);
+
+$curl->setopt(CURLOPT_FORBID_REUSE, 1);
+$curl->setopt(CURLOPT_FRESH_CONNECT, 1);
+#$curl->setopt(CURLOPT_SSL_CIPHER_LIST, "HIGH:MEDIUM");
+
+$curl->setopt(CURLOPT_CAINFO,"ca-bundle.crt");
+$curl->setopt(CURLOPT_DEBUGFUNCTION, \&silence);
+
+sub silence { return 0 }
+
+my $count = 1;
+
+my $sslversion95 = 0;
+$sslversion95++ if (&WWW::Curl::Easy::version() =~ m/SSL 0.9.5/); # 0.9.5 has buggy connect with some ssl sites
+
+my $haveca = 0;
+if (-f "ca-bundle.crt") { $haveca = 1; }
+
+for my $test_list (@$url_list) {
+ my ($url,$verifypeer,$verifyhost,$result,$result95)=@{$test_list};
+ if ($verifypeer && !$haveca) { $result = 1 } # expect to fail if no ca-bundle file
+ if ($sslversion95) { $result=$result95 }; # change expectation
+
+
+ $curl->setopt(CURLOPT_SSL_VERIFYPEER,$verifypeer); # do verify
+ $curl->setopt(CURLOPT_SSL_VERIFYHOST,$verifyhost); # check name
+ my $retcode;
+
+ $curl->setopt(CURLOPT_URL, $url);
+
+ $retcode = $curl->perform();
+ ok(($retcode != 0) == $result, "$url ssl test succeeds");
+}
+
diff --git a/t/09times.t b/t/09times.t
new file mode 100644
index 0000000..6b3c43a
--- /dev/null
+++ b/t/09times.t
@@ -0,0 +1,58 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 19;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS");
+ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION");
+ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT");
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_FILE, $body), "Setting CURLOPT_FILE");
+
+ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+my @myheaders;
+$myheaders[0] = "Server: www";
+$myheaders[1] = "User-Agent: Perl interface for libcURL";
+ok(! $curl->setopt(CURLOPT_HTTPHEADER, \@myheaders), "Setting CURLOPT_HTTPHEADER");
+
+my $retcode;
+$retcode = $curl->perform();
+ok(! $retcode,"Checking perform return code");
+
+if ($retcode == 0) {
+ my $bytes = $curl->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ ok($bytes, "Non-zero bytesize check");
+ my $realurl = $curl->getinfo(CURLINFO_EFFECTIVE_URL);
+ ok($realurl, "URL definedness check");
+ my $httpcode = $curl->getinfo(CURLINFO_HTTP_CODE);
+ ok($httpcode, "HTTP status code check");
+}
+
+my $start = $curl->getinfo(CURLINFO_STARTTRANSFER_TIME);
+ok ($start, "Valid transfer start time");
+my $total = $curl->getinfo(CURLINFO_TOTAL_TIME);
+ok ($total, "defined total transfer time");
+my $dns = $curl->getinfo(CURLINFO_NAMELOOKUP_TIME);
+ok ($dns, "NSLOOKUP time is defined");
+my $conn = $curl->getinfo(CURLINFO_CONNECT_TIME);
+ok ($conn, "Connect time defined");
+my $pre = $curl->getinfo(CURLINFO_PRETRANSFER_TIME);
+ok ($pre, "Pre-transfer time nonzero, defined");
+
+exit;
diff --git a/t/10errbuf.t b/t/10errbuf.t
new file mode 100644
index 0000000..8349b1b
--- /dev/null
+++ b/t/10errbuf.t
@@ -0,0 +1,40 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 12;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+ok($curl, 'Curl session initialize returns something');
+ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS");
+ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION");
+ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT");
+
+my $head = tempfile();
+ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+my $body = tempfile();
+ok(! $curl->setopt(CURLOPT_FILE, $body), "Setting CURLOPT_FILE");
+
+ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+my $new_error = tempfile();
+ok(! $curl->setopt(CURLOPT_STDERR, $new_error), "Setting CURLOPT_STDERR");
+
+# create a (hopefully) bad URL, so we get an error
+
+ok(! $curl->setopt(CURLOPT_URL, "badprotocol://127.0.0.1:2"), "Setting CURLOPT_URL succeeds, even with a bad protocol");
+
+my $retcode = $curl->perform();
+
+ok($retcode, "Non-zero return code indicates the expected failure");
+
+exit;
diff --git a/t/13slowleak.t b/t/13slowleak.t
new file mode 100644
index 0000000..1bfbb21
--- /dev/null
+++ b/t/13slowleak.t
@@ -0,0 +1,43 @@
+#!perl
+
+use strict;
+use warnings;
+#use Test::More tests => 214;
+use Test::More skip_all => "Not performing slow leakage regression test";
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+# There was a slow leak per curl handle init/cleanup. Hopefully fixed.
+
+foreach my $j (1..200) {
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new() or die "cannot curl";
+
+$curl->setopt(CURLOPT_NOPROGRESS, 1);
+$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
+$curl->setopt(CURLOPT_TIMEOUT, 30);
+
+open (HEAD, "+>",undef);
+WWW::Curl::Easy::setopt($curl, CURLOPT_WRITEHEADER, \*HEAD);
+open (BODY, "+>, undef);
+WWW::Curl::Easy::setopt($curl, CURLOPT_FILE, \*BODY);
+
+$curl->setopt(CURLOPT_URL, $url);
+
+my $httpcode = 0;
+
+my $retcode=$curl->perform();
+if ($retcode == 0) {
+ my bytes=$curl->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ my $realurl=$curl->getinfo(CURLINFO_EFFECTIVE_URL);
+ my $httpcode=$curl->getinfo(CURLINFO_HTTP_CODE);
+} else {
+ print "not ok $retcode / ".$curl->errbuf."\n";
+}
+
+}
+
+WWW::Curl::Easy::global_cleanup; #noop
diff --git a/t/14duphandle.t b/t/14duphandle.t
new file mode 100644
index 0000000..f56ac57
--- /dev/null
+++ b/t/14duphandle.t
@@ -0,0 +1,71 @@
+#!perl
+
+use strict;
+use warnings;
+use lib 'inc';
+use lib 'blib/lib';
+use lib 'blib/arch';
+use Test::More tests => 17;
+use File::Temp qw/tempfile/;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+{
+my $other_handle;
+my $head = tempfile();
+my $body = tempfile();
+
+ {
+ # Init the curl session
+ my $curl = WWW::Curl::Easy->new();
+ ok($curl, 'Curl session initialize returns something');
+ ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module');
+
+ ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS");
+ ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION");
+ ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT");
+
+ ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER");
+
+ ok(! $curl->setopt(CURLOPT_FILE, $body), "Setting CURLOPT_FILE");
+
+ ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+
+ my @myheaders;
+ $myheaders[0] = "Server: www";
+ $myheaders[1] = "User-Agent: Perl interface for libcURL";
+ ok(! $curl->setopt(CURLOPT_HTTPHEADER, \@myheaders), "Setting CURLOPT_HTTPHEADER");
+
+ # duplicate the handle
+ $other_handle = $curl->duphandle();
+ ok ($other_handle && ref($other_handle) eq 'WWW::Curl::Easy', "Duplicated handle seems to be an object in the right namespace");
+
+ foreach my $x ($other_handle,$curl) {
+ my $retcode = $x->perform();
+ ok(!$retcode, "Perform returns without an error");
+ if ($retcode == 0) {
+ my $bytes = $x->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ my $realurl = $x->getinfo(CURLINFO_EFFECTIVE_URL);
+ my $httpcode = $x->getinfo(CURLINFO_HTTP_CODE);
+ }
+ }
+ }
+
+ok(1, "Survived original curl handle DESTROY");
+
+ok(! $other_handle->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL");
+my $retcode = $other_handle->perform();
+ok(!$retcode, "Perform returns without an error");
+if ($retcode == 0) {
+ my $bytes=$other_handle->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ my $realurl=$other_handle->getinfo(CURLINFO_EFFECTIVE_URL);
+ my $httpcode=$other_handle->getinfo(CURLINFO_HTTP_CODE);
+}
+
+
+
+}
+ok(1, "Survived dup curl handle DESTROY");
+exit;
diff --git a/t/15duphandle-callback.t b/t/15duphandle-callback.t
new file mode 100644
index 0000000..3c5ecd0
--- /dev/null
+++ b/t/15duphandle-callback.t
@@ -0,0 +1,94 @@
+#!perl
+
+use strict;
+use warnings;
+use lib 'inc';
+use lib 'blib/lib';
+use lib 'blib/arch';
+use Test::More tests => 25;
+use File::Temp qw/tempfile/;
+use WWW::Curl::Easy;
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+my $other_handle;
+my $head = tempfile();
+my $hcall;
+my $body_called = 0;
+my $head_called = 0;
+{
+ # Init the curl session
+ my $curl = WWW::Curl::Easy->new();
+ ok($curl, 'Curl session initialize returns something'); #1
+ ok(ref($curl) eq 'WWW::Curl::Easy', 'Curl session looks like an object from the WWW::Curl::Easy module'); #2
+
+ ok(! $curl->setopt(CURLOPT_NOPROGRESS, 1), "Setting CURLOPT_NOPROGRESS"); #3
+ ok(! $curl->setopt(CURLOPT_FOLLOWLOCATION, 1), "Setting CURLOPT_FOLLOWLOCATION"); #4
+ ok(! $curl->setopt(CURLOPT_TIMEOUT, 30), "Setting CURLOPT_TIMEOUT"); #5
+
+ ok(! $curl->setopt(CURLOPT_WRITEHEADER, $head), "Setting CURLOPT_WRITEHEADER"); #6
+
+ my $body = tempfile();
+ ok(! $curl->setopt(CURLOPT_FILE, $body), "Setting CURLOPT_FILE"); #7
+
+
+ my @myheaders;
+ $myheaders[0] = "Server: www";
+ $myheaders[1] = "User-Agent: Perl interface for libcURL";
+ ok(! $curl->setopt(CURLOPT_HTTPHEADER, \@myheaders), "Setting CURLOPT_HTTPHEADER"); #8
+
+
+ sub body_callback {
+ my ($chunk,$handle)=@_;
+ $body_called++;
+ return length($chunk); # OK
+ }
+
+ sub head_callback {
+ my ($chunk,$handle)=@_;
+ $head_called++;
+ return length($chunk); # OK
+ }
+
+ $hcall = \&head_callback;
+ ok(! $curl->setopt(CURLOPT_WRITEFUNCTION, \&body_callback), "Setting CURLOPT_WRITEFUNCTION callback"); #9
+ ok(! $curl->setopt(CURLOPT_HEADERFUNCTION, $hcall), "Setting CURLOPT_HEADERFUNCTION callback"); #10
+
+ ok(! $curl->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL"); #11
+
+ # duplicate the handle
+ $other_handle = $curl->duphandle();
+ ok($other_handle, 'duphandle seems to return something'); #12
+ ok(ref($other_handle) eq 'WWW::Curl::Easy', 'Dup handle looks like an object from the WWW::Curl::Easy module'); #13
+
+ foreach my $x ($curl,$other_handle) {
+ my $retcode=$x->perform();
+ ok(!$retcode, "Handle return code check"); #14-15
+ if ($retcode == 0) {
+ my $bytes = $x->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ my $realurl = $x->getinfo(CURLINFO_EFFECTIVE_URL);
+ my $httpcode = $x->getinfo(CURLINFO_HTTP_CODE);
+ }
+ }
+ ok( $head_called >= 2, "Header callback seems to have worked"); #16
+ ok( $body_called >= 2, "Body callback seems to have worked"); #17
+}
+
+ok(! $other_handle->setopt(CURLOPT_URL, $url), "Setting CURLOPT_URL"); #18
+
+my $retcode=$other_handle->perform();
+ok(!$retcode, "Handle return code check");
+ok( 1, "We survive DESTROY time for the original handle");
+ok( head_callback('1',undef), "We can still access the callbacks");
+my $third = $other_handle->duphandle();
+ok($third, 'duphandle seems to return something again');
+ok(ref($third) eq 'WWW::Curl::Easy', 'Dup handle looks like an object from the WWW::Curl::Easy module');
+
+foreach my $x ($other_handle,$third) {
+ my $retcode=$x->perform();
+ ok(!$retcode, "Handle return code check");
+ if ($retcode == 0) {
+ my $bytes = $x->getinfo(CURLINFO_SIZE_DOWNLOAD);
+ my $realurl = $x->getinfo(CURLINFO_EFFECTIVE_URL);
+ my $httpcode = $x->getinfo(CURLINFO_HTTP_CODE);
+ }
+}
diff --git a/t/16formpost.t b/t/16formpost.t
new file mode 100644
index 0000000..a5bcd45
--- /dev/null
+++ b/t/16formpost.t
@@ -0,0 +1,84 @@
+#!perl
+
+use Test::More skip_all => "Not performing POST";
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+use strict;
+
+END {print "not ok 1\n" unless $::loaded;}
+use WWW::Curl::Easy;
+
+$::loaded = 1;
+
+######################### End of black magic.
+
+my $count=0;
+
+use ExtUtils::MakeMaker qw(prompt);
+
+# Read URL to get, defaulting to environment variable if supplied
+my $defurl=$ENV{CURL_TEST_URL} || "http://www.google.com/";
+my $url = prompt("# Please enter an URL to fetch",$defurl);
+if (!$url) {
+ print "1..0 # No test URL supplied - skipping test\n";
+ exit;
+}
+print "1..6\n";
+print "ok ".++$count."\n";
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+if ($curl == 0) {
+ print "not ";
+}
+print "ok ".++$count."\n";
+
+$curl->setopt(CURLOPT_NOPROGRESS, 1);
+$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
+$curl->setopt(CURLOPT_TIMEOUT, 30);
+
+open HEAD, ">head.out";
+$curl->setopt(CURLOPT_WRITEHEADER, *HEAD);
+print "ok ".++$count."\n";
+
+open BODY, ">body.out";
+$curl->setopt(CURLOPT_FILE,*BODY);
+print "ok ".++$count."\n";
+
+$curl->setopt(CURLOPT_URL, $url);
+
+print "ok ".++$count."\n";
+
+my $read_max=1000;
+
+sub read_callb
+{
+ my ($maxlen,$sv)=@_;
+# print STDERR "\nperl read_callback has been called!\n";
+# print STDERR "max data size: $maxlen - need $read_max bytes\n";
+ if ($read_max > 0) {
+ my $len=int($read_max/3)+1;
+ my $data = chr(ord('A')+rand(26))x$len;
+# print STDERR "generated max/3=", int($read_max/3)+1, " characters to be uploaded - $data.\n";
+ $read_max=$read_max-length($data);
+ return $data;
+ } else {
+ return "";
+ }
+}
+
+#
+# test post/read callback functions - requires a url which accepts posts, or it fails!
+#
+
+$curl->setopt(CURLOPT_READFUNCTION,\&read_callb);
+$curl->setopt(CURLOPT_INFILESIZE,$read_max );
+$curl->setopt(CURLOPT_UPLOAD,1 );
+$curl->setopt(CURLOPT_CUSTOMREQUEST,"POST" );
+
+if ($curl->perform() != 0) {
+ print "not ";
+};
+print "ok ".++$count."\n";
diff --git a/t/17slist.t b/t/17slist.t
new file mode 100644
index 0000000..e1da320
--- /dev/null
+++ b/t/17slist.t
@@ -0,0 +1,91 @@
+#!perl
+
+use Test::More skip_all => "Not performing printenv cgi tests";
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+use strict;
+
+use WWW::Curl::Easy;
+
+######################### End of black magic.
+
+my $count=0;
+
+use ExtUtils::MakeMaker qw(prompt);
+
+# Read URL to get, defaulting to environment variable if supplied
+my $defurl=$ENV{CURL_TEST_URL} || "http://www.google.com/";
+my $url = prompt("# Please enter an URL to fetch",$defurl);
+if (!$url) {
+ print "1..0 # No test URL supplied - skipping test\n";
+ exit;
+}
+
+# we need the real printenv cgi for these tests, so skip if
+# our test URL is not a printenv variant (or test.cgi from
+# mdk apache2). We basically need something which will echo
+# back sent headers in the output
+#
+
+
+if ($url !~ m/printenv|test.cgi/) {
+ print "1..0 # need a real 'printenv' cgi script for this test";
+ exit;
+}
+print "1..5\n";
+
+
+# Init the curl session
+my $curl = WWW::Curl::Easy->new();
+if ($curl == 0) {
+ print "not ";
+}
+print "ok ".++$count."\n";
+
+$curl->setopt(CURLOPT_NOPROGRESS, 1);
+$curl->setopt(CURLOPT_FOLLOWLOCATION, 1);
+$curl->setopt(CURLOPT_TIMEOUT, 30);
+
+open HEAD, ">head.out";
+$curl->setopt(CURLOPT_WRITEHEADER, \*HEAD);
+print "ok ".++$count."\n";
+
+
+sub body_callback {
+ my ($chunk,$handle)=@_;
+ ${$handle}.=$chunk;
+ return length($chunk); # OK
+}
+$curl->setopt(CURLOPT_WRITEFUNCTION, \&body_callback);
+
+my $body="";
+$curl->setopt(CURLOPT_FILE,\$body);
+print "ok ".++$count."\n";
+
+$curl->setopt(CURLOPT_URL, $url);
+
+print "ok ".++$count."\n";
+# Add some additional headers to the http-request:
+# Check that the printenv script sends back FOO=bar somewhere
+# This checks that all headers were sent.
+my @myheaders;
+$myheaders[0] = "Baz: xyzzy";
+$myheaders[1] = "Foo: bar";
+$curl->setopt(CURLOPT_HTTPHEADER, \@myheaders);
+
+# Go get it
+my $retcode=$curl->perform();
+if ($retcode == 0) {
+ if ($body !~ m/FOO\s*=\s*"?bar"?/) {
+ print "not ";
+ }
+} else {
+ # We can acces the error message in $errbuf here
+# print STDERR "$retcode / ".$curl->errbuf."\n";
+ print "not ";
+}
+print "ok ".++$count."\n";
+
+exit;
diff --git a/t/18twinhandles.t b/t/18twinhandles.t
new file mode 100644
index 0000000..10423d3
--- /dev/null
+++ b/t/18twinhandles.t
@@ -0,0 +1,52 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 11;
+
+BEGIN { use_ok( 'WWW::Curl::Easy' ); }
+
+my $url = $ENV{CURL_TEST_URL} || "http://www.google.com";
+
+my $header_called = 0;
+sub header_callback { $header_called++; return length($_[0]) };
+
+my $body_called = 0;
+sub body_callback {
+ my ($chunk,$handle)=@_;
+ $body_called++;
+ return length($chunk); # OK
+}
+
+
+# Init the curl session
+my $curl1 = WWW::Curl::Easy->new();
+ok($curl1, 'Curl1 session initialize returns something');
+ok(ref($curl1) eq 'WWW::Curl::Easy', 'Curl1 session looks like an object from the WWW::Curl::Easy module');
+
+my $curl2 = WWW::Curl::Easy->new();
+ok($curl2, 'Curl2 session initialize returns something');
+ok(ref($curl2) eq 'WWW::Curl::Easy', 'Curl2 session looks like an object from the WWW::Curl::Easy module');
+
+for my $handle ($curl1,$curl2) {
+ $handle->setopt(CURLOPT_NOPROGRESS, 1);
+ $handle->setopt(CURLOPT_FOLLOWLOCATION, 1);
+ $handle->setopt(CURLOPT_TIMEOUT, 30);
+
+ my $body_ref=\&body_callback;
+ $handle->setopt(CURLOPT_WRITEFUNCTION, $body_ref);
+ $handle->setopt(CURLOPT_HEADERFUNCTION, \&header_callback);
+}
+
+
+ok(! $curl1->setopt(CURLOPT_URL, "error:bad-url"), "Setting deliberately bad url succeeds - should return error on perform"); # deliberate error
+ok(! $curl2->setopt(CURLOPT_URL, $url), "Setting OK url");
+
+my $code1=$curl1->perform();
+ok($code1 != 0, "Curl1 handle fails as expected");
+
+my $code2=$curl2->perform();
+ok($code2 == 0, "Curl2 handle succeeds");
+
+ok($header_called, "Header callback works");
+ok($body_called, "Body callback works");
diff --git a/t/meta.t b/t/meta.t
new file mode 100644
index 0000000..924aea2
--- /dev/null
+++ b/t/meta.t
@@ -0,0 +1,4 @@
+use Test::More;
+eval "use Test::CPAN::Meta";
+plan skip_all => "Test::CPAN::Meta required for testing META.yml" if $@;
+meta_yaml_ok();
diff --git a/t/new/00constants.t b/t/new/00constants.t
new file mode 100644
index 0000000..2b41d1e
--- /dev/null
+++ b/t/new/00constants.t
@@ -0,0 +1,5 @@
+use strict;
+use Test::Simple tests => 1;
+use WWW::Curl::Easy;
+
+ok( CURLOPT_URL == 10000 + 2 );
diff --git a/t/new/01basic.t b/t/new/01basic.t
new file mode 100644
index 0000000..9a96d54
--- /dev/null
+++ b/t/new/01basic.t
@@ -0,0 +1,13 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ my @headers = ( 'Server: cURL', 'User-Agent: WWW::Curl/3.00' );
+ $curl->setopt( CURLOPT_HTTPHEADER, \@headers );
+ my $code = $curl->perform;
+ ok( $code == 0 );
+}
diff --git a/t/new/02header-callback.t b/t/new/02header-callback.t
new file mode 100644
index 0000000..497e2dd
--- /dev/null
+++ b/t/new/02header-callback.t
@@ -0,0 +1,20 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+my $header;
+
+sub header_callback {
+ my $chunk = shift;
+ $header .= $chunk;
+ return length $chunk;
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_HEADERFUNCTION, \&header_callback );
+ $curl->perform;
+ ok($header);
+}
diff --git a/t/new/03body-callback.t b/t/new/03body-callback.t
new file mode 100644
index 0000000..c91d585
--- /dev/null
+++ b/t/new/03body-callback.t
@@ -0,0 +1,20 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+my $body;
+
+sub body_callback {
+ my ( $chunk, $handle ) = @_;
+ $body .= $chunk;
+ return length $chunk;
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_WRITEFUNCTION, \&body_callback );
+ $curl->perform;
+ ok($body);
+}
diff --git a/t/new/04abort.t b/t/new/04abort.t
new file mode 100644
index 0000000..77b21c1
--- /dev/null
+++ b/t/new/04abort.t
@@ -0,0 +1,17 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+sub body_callback {
+ my ( $chunk, $handle ) = @_;
+ return -1;
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_WRITEFUNCTION, \&body_callback );
+ my $code = $curl->perform;
+ ok($code);
+}
diff --git a/t/new/05progress.t b/t/new/05progress.t
new file mode 100644
index 0000000..a8f2ceb
--- /dev/null
+++ b/t/new/05progress.t
@@ -0,0 +1,25 @@
+use strict;
+use Test::More tests => 3;
+use WWW::Curl::Easy;
+
+my ( $progress, $last );
+
+sub progress_callback {
+ my ( $clientp, $dltotal, $dlnow, $ultotal, $ulnow ) = @_;
+ $last = $dlnow;
+ $progress++;
+ return 0;
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 3 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_NOPROGRESS, 1 );
+ $curl->setopt( CURLOPT_NOPROGRESS, 0 );
+ $curl->setopt( CURLOPT_PROGRESSFUNCTION, \&progress_callback );
+ my $code = $curl->perform;
+ ok( $code == 0 );
+ ok($progress);
+ ok($last);
+}
diff --git a/t/new/06http-post.t b/t/new/06http-post.t
new file mode 100644
index 0000000..55990f2
--- /dev/null
+++ b/t/new/06http-post.t
@@ -0,0 +1,26 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+my $max = 1000;
+
+sub read_callback {
+ my ( $maxlen, $sv ) = @_;
+
+ # Create some random data
+ my $data = chr( ord('A') + rand(26) ) x ( int( $max / 3 ) + 1 );
+ $max = $max - length $data;
+ return $data;
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_READFUNCTION, \&read_callback );
+ $curl->setopt( CURLOPT_INFILESIZE, $max );
+ $curl->setopt( CURLOPT_UPLOAD, 1 );
+ $curl->setopt( CURLOPT_CUSTOMREQUEST, 'POST' );
+ my $code = $curl->perform;
+ ok( $code == 0 );
+}
diff --git a/t/new/07errbuf.t b/t/new/07errbuf.t
new file mode 100644
index 0000000..9ecd4f4
--- /dev/null
+++ b/t/new/07errbuf.t
@@ -0,0 +1,9 @@
+use strict;
+use Test::Simple tests => 1;
+use WWW::Curl::Easy;
+
+my $curl = new WWW::Curl::Easy;
+$curl->setopt( CURLOPT_URL, 'badprotocol://127.0.0.1:2' );
+$curl->perform;
+my $err = $curl->errbuf;
+ok($err);
diff --git a/t/new/08duphandle.t b/t/new/08duphandle.t
new file mode 100644
index 0000000..39381e8
--- /dev/null
+++ b/t/new/08duphandle.t
@@ -0,0 +1,14 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ my @headers = ( 'Server: cURL', 'User-Agent: WWW::Curl/3.00' );
+ $curl->setopt( CURLOPT_HTTPHEADER, \@headers );
+ my $curl2 = $curl->duphandle;
+ my $code = $curl2->perform;
+ ok( $code == 0 );
+}
diff --git a/t/new/09duphandle-callback.t b/t/new/09duphandle-callback.t
new file mode 100644
index 0000000..db7d368
--- /dev/null
+++ b/t/new/09duphandle-callback.t
@@ -0,0 +1,21 @@
+use strict;
+use Test::More tests => 1;
+use WWW::Curl::Easy;
+
+my $body;
+
+sub body_callback {
+ my ( $chunk, $handle ) = @_;
+ $body .= $chunk;
+ return length $chunk;
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 1 unless $ENV{CURL_TEST_URL};
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_WRITEFUNCTION, \&body_callback );
+ my $curl2 = $curl->duphandle;
+ $curl2->perform;
+ ok($body);
+}
diff --git a/t/new/10multi-callback.t b/t/new/10multi-callback.t
new file mode 100644
index 0000000..ee887ad
--- /dev/null
+++ b/t/new/10multi-callback.t
@@ -0,0 +1,54 @@
+use strict;
+use Test::More tests => 4;
+use WWW::Curl::Easy;
+use WWW::Curl::Multi;
+
+my ( $header, $body, $header2, $body2 );
+
+sub header_callback {
+ my $chunk = shift;
+ $header .= $chunk;
+ return length($chunk);
+}
+
+sub body_callback {
+ my ( $chunk, $handle ) = @_;
+ $body .= $chunk;
+ return length($chunk);
+}
+
+sub header_callback2 {
+ my $chunk = shift;
+ $header2 .= $chunk;
+ return length($chunk);
+}
+
+sub body_callback2 {
+ my ( $chunk, $handle ) = @_;
+ $body2 .= $chunk;
+ return length($chunk);
+}
+
+SKIP: {
+ skip 'You need to set CURL_TEST_URL', 4 unless $ENV{CURL_TEST_URL};
+
+ my $curl = new WWW::Curl::Easy;
+ $curl->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl->setopt( CURLOPT_HEADERFUNCTION, \&header_callback );
+ $curl->setopt( CURLOPT_WRITEFUNCTION, \&body_callback );
+
+ my $curl2 = new WWW::Curl::Easy;
+ $curl2->setopt( CURLOPT_URL, $ENV{CURL_TEST_URL} );
+ $curl2->setopt( CURLOPT_HEADERFUNCTION, \&header_callback2 );
+ $curl2->setopt( CURLOPT_WRITEFUNCTION, \&body_callback2 );
+
+ my $curlm = new WWW::Curl::Multi;
+ $curlm->add_handle($curl);
+ $curlm->add_handle($curl2);
+ $curlm->perform;
+
+ ok($header);
+ ok($body);
+ ok($header2);
+ ok($body2);
+}
diff --git a/t/new/README b/t/new/README
new file mode 100644
index 0000000..7879eb1
--- /dev/null
+++ b/t/new/README
@@ -0,0 +1,8 @@
+These test scripts have been updated by Sebastian Riedel to use modern
+features of the Test::Harness suite, such as Test::Simple and Test::More,
+which makes them cleaner and more maintainable, but which are unfortunately
+not natively supported by older perl versions. They are placed here reference,
+and will become the standard test scripts once we drop support for perl5.005.
+
+
+
diff --git a/t/pod-coverage.t b/t/pod-coverage.t
new file mode 100644
index 0000000..d1e1f34
--- /dev/null
+++ b/t/pod-coverage.t
@@ -0,0 +1,7 @@
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage" if $@;
+plan tests => 1;
+pod_coverage_ok('WWW::Curl','WWW::Curl has proper POD coverage');
diff --git a/t/pod.t b/t/pod.t
new file mode 100644
index 0000000..976d7cd
--- /dev/null
+++ b/t/pod.t
@@ -0,0 +1,6 @@
+#!perl -T
+
+use Test::More;
+eval "use Test::Pod 1.14";
+plan skip_all => "Test::Pod 1.14 required for testing POD" if $@;
+all_pod_files_ok();
diff --git a/template/Easy.pm.tmpl b/template/Easy.pm.tmpl
new file mode 100644
index 0000000..04a0b3f
--- /dev/null
+++ b/template/Easy.pm.tmpl
@@ -0,0 +1,46 @@
+package WWW::Curl::Easy;
+
+use strict;
+use warnings;
+use Carp;
+use vars qw($VERSION @ISA @EXPORT @EXPORT_OK $AUTOLOAD);
+
+$VERSION = '4.05';
+
+require WWW::Curl;
+require Exporter;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+
+@EXPORT = qw(
+@CURLOPT_INCLUDE@
+);
+
+$WWW::Curl::Easy::headers = "";
+$WWW::Curl::Easy::content = "";
+
+sub AUTOLOAD {
+
+ # This AUTOLOAD is used to 'autoload' constants from the constant()
+ # XS function.
+
+ ( my $constname = $AUTOLOAD ) =~ s/.*:://;
+ return constant( $constname, 0 );
+}
+
+1;
+
+__END__
+
+Copyright (C) 2000-2005,2008 Daniel Stenberg, Cris Bailiff,
+Sebastian Riedel, 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.
diff --git a/template/Share.pm.tmpl b/template/Share.pm.tmpl
new file mode 100644
index 0000000..9149217
--- /dev/null
+++ b/template/Share.pm.tmpl
@@ -0,0 +1,36 @@
+package WWW::Curl::Share;
+
+use strict;
+use warnings;
+use Carp;
+use vars qw(@ISA @EXPORT @EXPORT_OK $AUTOLOAD);
+
+use WWW::Curl;
+require Exporter;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+
+@EXPORT = qw(
+@CURLSHOPT_INCLUDE@
+);
+
+sub AUTOLOAD {
+
+ # This AUTOLOAD is used to 'autoload' constants from the constant()
+ # XS function.
+
+ ( my $constname = $AUTOLOAD ) =~ s/.*:://;
+ return constant( $constname, 0 );
+}
+
+1;
+__END__
+
+
+Copyright (C) 2008, Anton Fedorov (datacompboy <at> mail.ru)
+
+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.
diff --git a/typemap b/typemap
new file mode 100644
index 0000000..2b510b1
--- /dev/null
+++ b/typemap
@@ -0,0 +1,5 @@
+TYPEMAP
+WWW::Curl::Easy T_PTROBJ
+WWW::Curl::Form T_PTROBJ
+WWW::Curl::Multi T_PTROBJ
+WWW::Curl::Share T_PTROBJ