pycurl-7.19.3/0000755000470500047050000000000012263734634012525 5ustar piepie00000000000000pycurl-7.19.3/doc/0000755000470500047050000000000012263734634013272 5ustar piepie00000000000000pycurl-7.19.3/doc/callbacks.html0000644000470500047050000001303512256232314016067 0ustar piepie00000000000000 PyCurl: Callbacks

Callbacks

For more fine-grained control, libcurl allows a number of callbacks to be associated with each connection. In pycurl, callbacks are defined using the setopt() method for Curl objects with options WRITEFUNCTION, READFUNCTION, HEADERFUNCTION, PROGRESSFUNCTION, IOCTLFUNCTION, or DEBUGFUNCTION. These options correspond to the libcurl options with CURLOPT_* prefix removed. A callback in pycurl must be either a regular Python function, a class method or an extension type function.

There are some limitations to some of the options which can be used concurrently with the pycurl callbacks compared to the libcurl callbacks. This is to allow different callback functions to be associated with different Curl objects. More specifically, WRITEDATA cannot be used with WRITEFUNCTION, READDATA cannot be used with READFUNCTION, WRITEHEADER cannot be used with HEADERFUNCTION, PROGRESSDATA cannot be used with PROGRESSFUNCTION, IOCTLDATA cannot be used with IOCTLFUNCTION, and DEBUGDATA cannot be used with DEBUGFUNCTION. In practice, these limitations can be overcome by having a callback function be a class instance method and rather use the class instance attributes to store per object data such as files used in the callbacks.

The signature of each callback used in pycurl is as follows:

WRITEFUNCTION(string) -> number of characters written

READFUNCTION(number of characters to read)-> string

HEADERFUNCTION(string) -> number of characters written

PROGRESSFUNCTION(download total, downloaded, upload total, uploaded) -> status

DEBUGFUNCTION(debug message type, debug message string) -> None

IOCTLFUNCTION(ioctl cmd) -> status

In addition, READFUNCTION may return READFUNC_ABORT or READFUNC_PAUSE. See the libcurl documentation for an explanation of these values. The WRITEFUNCTION and HEADERFUNCTION callbacks may return None, which is an alternate way of indicating that the callback has consumed all of the string passed to it.


Example: Callbacks for document header and body

This example prints the header data to stderr and the body data to stdout. Also note that neither callback returns the number of bytes written. For WRITEFUNCTION and HEADERFUNCTION callbacks, returning None implies that all bytes where written.

    ## Callback function invoked when body data is ready
    def body(buf):
        # Print body data to stdout
        import sys
        sys.stdout.write(buf)
        # Returning None implies that all bytes were written

    ## Callback function invoked when header data is ready
    def header(buf):
        # Print header data to stderr
        import sys
        sys.stderr.write(buf)
        # Returning None implies that all bytes were written

    c = pycurl.Curl()
    c.setopt(pycurl.URL, "http://www.python.org/")
    c.setopt(pycurl.WRITEFUNCTION, body)
    c.setopt(pycurl.HEADERFUNCTION, header)
    c.perform()

Example: Download/upload progress callback

This example shows how to use the progress callback. When downloading a document, the arguments related to uploads are zero, and vice versa.

    ## Callback function invoked when download/upload has progress
    def progress(download_t, download_d, upload_t, upload_d):
        print "Total to download", download_t
        print "Total downloaded", download_d
        print "Total to upload", upload_t
        print "Total uploaded", upload_d

    c.setopt(c.URL, "http://slashdot.org/")
    c.setopt(c.NOPROGRESS, 0)
    c.setopt(c.PROGRESSFUNCTION, progress)
    c.perform()

Example: Debug callbacks

This example shows how to use the debug callback. The debug message type is an integer indicating the type of debug message. The VERBOSE option must be enabled for this callback to be invoked.

    def test(debug_type, debug_msg):
        print "debug(%d): %s" % (debug_type, debug_msg)

    c = pycurl.Curl()
    c.setopt(pycurl.URL, "http://curl.haxx.se/")
    c.setopt(pycurl.VERBOSE, 1)
    c.setopt(pycurl.DEBUGFUNCTION, test)
    c.perform()

Other examples

The pycurl distribution also contains a number of test scripts and examples which show how to use the various callbacks in libcurl. For instance, the file 'examples/file_upload.py' in the distribution contains example code for using READFUNCTION, 'tests/test_cb.py' shows WRITEFUNCTION and HEADERFUNCTION, 'tests/test_debug.py' shows DEBUGFUNCTION, and 'tests/test_getinfo.py' shows PROGRESSFUNCTION.


Valid XHTML 1.0!

pycurl-7.19.3/doc/curlmultiobject.html0000644000470500047050000001077212256232314017364 0ustar piepie00000000000000 PycURL: CurlMulti Objects

CurlMulti Object

CurlMulti objects have the following methods:

close() -> None

Corresponds to curl_multi_cleanup() in libcurl. This method is automatically called by pycurl when a CurlMulti object no longer has any references to it, but can also be called explicitly.

perform() -> tuple of status and the number of active Curl objects

Corresponds to curl_multi_perform() in libcurl.

add_handle(Curl object) -> None

Corresponds to curl_multi_add_handle() in libcurl. This method adds an existing and valid Curl object to the CurlMulti object.

IMPORTANT NOTE: add_handle does not implicitly add a Python reference to the Curl object (and thus does not increase the reference count on the Curl object).

remove_handle(Curl object) -> None

Corresponds to curl_multi_remove_handle() in libcurl. This method removes an existing and valid Curl object from the CurlMulti object.

IMPORTANT NOTE: remove_handle does not implicitly remove a Python reference from the Curl object (and thus does not decrease the reference count on the Curl object).

fdset() -> triple of lists with active file descriptors, readable, writeable, exceptions.

Corresponds to curl_multi_fdset() in libcurl. This method extracts the file descriptor information from a CurlMulti object. The returned lists can be used with the select module to poll for events.

Example usage:

import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://curl.haxx.se")
m = pycurl.CurlMulti()
m.add_handle(c)
while 1:
    ret, num_handles = m.perform()
    if ret != pycurl.E_CALL_MULTI_PERFORM: break
while num_handles:
    apply(select.select, m.fdset() + (1,))
    while 1:
        ret, num_handles = m.perform()
        if ret != pycurl.E_CALL_MULTI_PERFORM: break
select(timeout) -> number of ready file descriptors or -1 on timeout

This is a convenience function which simplifies the combined use of fdset() and the select module.

Example usage:

import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://curl.haxx.se")
m = pycurl.CurlMulti()
m.add_handle(c)
while 1:
    ret, num_handles = m.perform()
    if ret != pycurl.E_CALL_MULTI_PERFORM: break
while num_handles:
    ret = m.select(1.0)
    if ret == -1:  continue
    while 1:
        ret, num_handles = m.perform()
        if ret != pycurl.E_CALL_MULTI_PERFORM: break
info_read([max]) -> numberof queued messages, a list of successful objects, a list of failed objects

Corresponds to the curl_multi_info_read() function in libcurl. This method extracts at most max messages from the multi stack and returns them in two lists. The first list contains the handles which completed successfully and the second list contains a tuple <curl object, curl error number, curl error message> for each failed curl object. The number of queued messages after this method has been called is also returned.


Valid XHTML 1.0!

pycurl-7.19.3/doc/curlobject.html0000644000470500047050000001031312256232314016300 0ustar piepie00000000000000 PycURL: Curl Objects

Curl Object

Curl objects have the following methods:

close() -> None

Corresponds to curl_easy_cleanup in libcurl. This method is automatically called by pycurl when a Curl object no longer has any references to it, but can also be called explicitly.

perform() -> None

Corresponds to curl_easy_perform in libcurl.

reset() -> None

Corresponds to curl_easy_reset in libcurl.

setopt(option, value) -> None

Corresponds to curl_easy_setopt in libcurl, where option is specified with the CURLOPT_* constants in libcurl, except that the CURLOPT_ prefix has been removed. (See below for exceptions.) The type for value depends on the option, and can be either a string, integer, long integer, file object, list, or function.

Example usage:

import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://www.python.org/")
c.setopt(pycurl.HTTPHEADER, ["Accept:"])
import StringIO
b = StringIO.StringIO()
c.setopt(pycurl.WRITEFUNCTION, b.write)
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.setopt(pycurl.MAXREDIRS, 5)
c.perform()
print b.getvalue()
...
getinfo(option) -> Result

Corresponds to curl_easy_getinfo in libcurl, where option is the same as the CURLINFO_* constants in libcurl, except that the CURLINFO_ prefix has been removed. (See below for exceptions.) Result contains an integer, float or string, depending on which option is given. The getinfo method should not be called unless perform has been called and finished.

Example usage:

import pycurl
c = pycurl.Curl()
c.setopt(pycurl.URL, "http://sf.net")
c.setopt(pycurl.FOLLOWLOCATION, 1)
c.perform()
print c.getinfo(pycurl.HTTP_CODE), c.getinfo(pycurl.EFFECTIVE_URL)
...
--> 200 "http://sourceforge.net/"
pause(bitmask) -> None

Corresponds to curl_easy_pause in libcurl. The argument should be derived from the PAUSE_RECV, PAUSE_SEND, PAUSE_ALL and PAUSE_CONT constants.

errstr() -> String

Returns the internal libcurl error buffer of this handle as a string.

In order to distinguish between similarly-named CURLOPT and CURLINFO constants, some have OPT_ and INFO_ prefixes. These are INFO_FILETIME, OPT_FILETIME, INFO_COOKIELIST (but setopt uses COOKIELIST!), INFO_CERTINFO, and OPT_CERTINFO.

The value returned by getinfo(INFO_CERTINFO) is a list with one element per certificate in the chain, starting with the leaf; each element is a sequence of (key, value) tuples.


Valid XHTML 1.0!

pycurl-7.19.3/doc/curlshareobject.html0000644000470500047050000000276312256232314017335 0ustar piepie00000000000000 PycURL: CurlShare Objects

CurlShare Object

CurlShare objects have the following methods:

setopt(option, value) -> None

Corresponds to curl_share_setopt in libcurl, where option is specified with the CURLSHOPT_* constants in libcurl, except that the CURLSHOPT_ prefix has been changed to SH_. Currently, value must be either LOCK_DATA_COOKIE or LOCK_DATA_DNS.

Example usage:

import pycurl
curl = pycurl.Curl()
s = pycurl.CurlShare()
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE)
s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS)
curl.setopt(pycurl.URL, 'http://curl.haxx.se')
curl.setopt(pycurl.SHARE, s)
curl.perform()
curl.close()

Valid XHTML 1.0!

pycurl-7.19.3/doc/files.rst0000644000470500047050000000304112262776427015131 0ustar piepie00000000000000Files ===== In PycURL 7.19.0.3 and below, CURLOPT_READDATA, CURLOPT_WRITEDATA and CURLOPT_WRITEHEADER options accepted file objects and directly passed the underlying C library FILE pointers to libcurl. Python 3 no longer implements files as C library FILE objects. In PycURL 7.19.3 and above, when running on Python 3, these options are implemented as calls to CURLOPT_READFUNCTION, CURLOPT_WRITEFUNCTION and CURLOPT_HEADERFUNCTION, respectively, with the write method of the Python file object as the parameter. As a result, any Python file-like object implementing a write method can be passed to CURLOPT_READDATA, CURLOPT_WRITEDATA or CURLOPT_WRITEHEADER options. When running PycURL 7.19.3 and above on Python 2, the old behavior of passing FILE pointers to libcurl remains when a true file object is given to CURLOPT_READDATA, CURLOPT_WRITEDATA and CURLOPT_WRITEHEADER options. For consistency with Python 3 behavior these options also accept file-like objects implementing a write method as arguments, in which case the Python 3 code path is used converting these options to CURLOPT_*FUNCTION option calls. Files given to PycURL as arguments to CURLOPT_READDATA, CURLOPT_WRITEDATA or CURLOPT_WRITEHEADER must be opened for writing in binary mode. Files opened in text mode (without "b" flag to open()) expect string objects and writing to them from PycURL will fail. Similarly when passing f.write method of an open file to CURLOPT_WRITEFUNCTION or CURLOPT_HEADERFUNCTION, or f.read to CURLOPT_READFUNCTION, the file must have been be opened in binary mode. pycurl-7.19.3/doc/internals.rst0000644000470500047050000000050412256232314016010 0ustar piepie00000000000000Internals ========= Cleanup sequence: x=curl/multi/share x.close() -> do_x_close -> util_x_close del x -> do_x_dealloc -> util_x_close do_* functions are directly invoked by user code. They check pycurl object state. util_* functions are only invoked by other pycurl C functions. They do not check pycurl object state. pycurl-7.19.3/doc/pycurl.html0000644000470500047050000001126712262776427015512 0ustar piepie00000000000000 PycURL Documentation

PycURL — A Python Interface To The cURL library

The pycurl package is a Python interface to libcurl (http://curl.haxx.se/libcurl/). pycurl has been successfully built and tested with Python versions from 2.4 to 2.7.

libcurl is a client-side URL transfer library supporting FTP, FTPS, HTTP, HTTPS, GOPHER, TELNET, DICT, FILE and LDAP. libcurl also supports HTTPS certificates, HTTP POST, HTTP PUT, FTP uploads, proxies, cookies, basic authentication, file transfer resume of FTP sessions, HTTP proxy tunneling and more.

All the functionality provided by libcurl can used through the pycurl interface. The following subsections describe how to use the pycurl interface, and assume familiarity with how libcurl works. For information on how libcurl works, please consult the curl library web pages (http://curl.haxx.se/libcurl/c/).


Module Functionality

pycurl.global_init(option) ->None

option is one of the constants pycurl.GLOBAL_SSL, pycurl.GLOBAL_WIN32, pycurl.GLOBAL_ALL, pycurl.GLOBAL_NOTHING, pycurl.GLOBAL_DEFAULT. Corresponds to curl_global_init() in libcurl.

pycurl.global_cleanup() -> None

Corresponds to curl_global_cleanup() in libcurl.

pycurl.version

This is a string with version information on libcurl, corresponding to curl_version() in libcurl.

Example usage:

>>> import pycurl
>>> pycurl.version
'libcurl/7.12.3 OpenSSL/0.9.7e zlib/1.2.2.1 libidn/0.5.12'
pycurl.version_info() -> Tuple

Corresponds to curl_version_info() in libcurl. Returns a tuple of information which is similar to the curl_version_info_data struct returned by curl_version_info() in libcurl.

Example usage:

>>> import pycurl
>>> pycurl.version_info()
(2, '7.12.3', 461827, 'i586-pc-linux-gnu', 1565, 'OpenSSL/0.9.7e', 9465951,
'1.2.2.1', ('ftp', 'gopher', 'telnet', 'dict', 'ldap', 'http', 'file',
'https', 'ftps'), None, 0, '0.5.12')
pycurl.Curl() -> Curl object

This function creates a new Curl object which corresponds to a CURL handle in libcurl. Curl objects automatically set CURLOPT_VERBOSE to 0, CURLOPT_NOPROGRESS to 1, provide a default CURLOPT_USERAGENT and setup CURLOPT_ERRORBUFFER to point to a private error buffer.

pycurl.CurlMulti() -> CurlMulti object

This function creates a new CurlMulti object which corresponds to a CURLM handle in libcurl.

pycurl.CurlShare() -> CurlShare object

This function creates a new CurlShare object which corresponds to a CURLSH handle in libcurl. CurlShare objects is what you pass as an argument to the SHARE option on Curl objects.


Subsections

Documentation For Developers


Valid XHTML 1.0!

pycurl-7.19.3/doc/release-process.rst0000644000470500047050000000201612263710745017114 0ustar piepie00000000000000Release Process =============== 1. Ensure changelog is up to date with commits in master. 2. Run `python setup.py manifest`, check that none of the listed files should be in MANIFEST.in. 3. Make sure travis is green for master. 4. Update version numbers in: - Changelog - setup.py - winbuild.py - www/htdocs/index.php 5. Copy Changelog to www/htdocs. 6. Draft release notes. 7. `make docs`. 8. `python setup.py sdist`. 9. Manually test install the built package. 10. Build windows packages using winbuild.py. 11. Add windows packages to downloads repo on github. 12. Tag the new version. 13. Register new version with pypi - `python setup.py register`. 14. Upload source distribution to pypi - `python setup.py sdist upload`. This recreates the source distribution. 15. Add the source distribution to downloads repo on github. 16. Rsync downloads repo to sourceforge. 17. Rsync www/htdocs to sourceforge. 18. Push tag to github pycurl repo. 19. Announce release on mailing list. 20. Link to announcement from website. pycurl-7.19.3/doc/unicode.rst0000644000470500047050000001163312262015545015446 0ustar piepie00000000000000Unicode ======= Python 2.x ---------- Under Python 2, the string type can hold arbitrary encoded byte strings. PycURL will pass whatever byte strings it is given verbatim to libcurl. If your application works with encoded byte strings, you should be able to pass them to PycURL. If your application works with Unicode data, you need to encode the data to byte strings yourself. Which encoding to use depends on the protocol you are working with - HTTP headers should be encoded in latin1, HTTP request bodies are commonly encoded in utf-8 and their encoding is specified in the Content-Type header value. Prior to PycURL 7.19.3, PycURL did not accept Unicode data under Python 2. Even Unicode strings containing only ASCII code points had to be encoded to byte strings. As of PycURL 7.19.3, for compatibility with Python 3, PycURL will accept Unicode strings under Python 2 provided they contain ASCII code points only. In other words, PycURL will encode Unicode into ASCII for you. If you supply a Unicode string containing characters that are outside of ASCII, the call will fail with a UnicodeEncodeError. PycURL will return data from libcurl, like request bodies and header values, as byte strings. If the data is ASCII, you can treat it as string data. Otherwise you will need to decode the byte strings usisng the correct encoding. What encoding is correct depends on the protocol and potentially returned data itself - HTTP response headers are supposed to be latin1 encoded but encoding of response body is specified in the Content-Type header. Python 3.x (from PycURL 7.19.3 onward) -------------------------------------- Under Python 3, the rules are as follows: PycURL will accept bytes for any string data passed to libcurl (e.g. arguments to curl_easy_setopt). PycURL will accept (Unicode) strings for string arguments to curl_easy_setopt. libcurl generally expects the options to be already appropriately encoded and escaped (e.g. for CURLOPT_URL). Also, Python 2 code dealing with Unicode values for string options will typically manually encode such values. Therefore PycURL will attempt to encode Unicode strings with the ascii codec only, allowing the application to pass ASCII data in a straightforward manner but requiring Unicode data to be appropriately encoded. It may be helpful to remember that libcurl operates on byte arrays. It is a C library and does not do any Unicode encoding or decoding, offloading that task on the application using it. PycURL, being a thin wrapper around libcurl, passes the Unicode encoding and decoding responsibilities to you except for the trivial case of encoding Unicode data containing only ASCII characters into ASCII. Caution: when using CURLOPT_READFUNCTION in tandem with CURLOPT_POSTFIELDSIZE, as would be done for HTTP for example, take care to pass the length of encoded data to CURLOPT_POSTFIELDSIZE if you are doing the encoding from Unicode strings. If you pass the number of Unicode characters rather than encoded bytes to libcurl, the server will receive wrong Content-Length. Alternatively you can return Unicode strings from a CURLOPT_READFUNCTION function, if you are certain they will only contain ASCII code points. If encoding to ASCII fails, PycURL will return an error to libcurl, and libcurl in turn will fail the request with an exception like "read function error/data error". You may examine sys.last_value for information on exception that occurred during encoding in this case. PycURL will return all data read from the network as bytes. In particular, this means that BytesIO should be used rather than StringIO for writing the response to memory. Header function will also receive bytes. Because PycURL does not perform encoding or decoding, other than to ASCII, any file objects that PycURL is meant to interact with via CURLOPT_READDATA, CURLOPT_WRITEDATA, CURLOPT_WRITEHEADER, CURLOPT_READFUNCTION, CURLOPT_WRITEFUNCTION or CURLOPT_HEADERFUNCTION must be opened in binary mode ("b" flag to open() call). Python 3.x before PycURL 7.19.3 ------------------------------- PycURL did not have official Python 3 support prior to PycURL 7.19.3. There were two patches on SourceForge (original_, revised_) adding Python 3 support, but they did not handle Unicode strings correctly. Instead of using Python encoding functionality, these patches used C standard library unicode to multibyte conversion functions, and thus they can have the same behavior as Python encoding code or behave entirely differently. Python 3 support as implemented in PycURL 7.19.3 and documented here does not, as mentioned, actually perform any encoding other than to convert from Unicode strings containing ASCII-only bytes to ASCII byte strings. Linux distributions that offered Python 3 packages of PycURL prior to 7.19.3 used SourceForge patches and may behave in ways contradictory to what is described in this document. .. _original: http://sourceforge.net/p/pycurl/patches/5/ .. _revised: http://sourceforge.net/p/pycurl/patches/12/ pycurl-7.19.3/examples/0000755000470500047050000000000012263734634014343 5ustar piepie00000000000000pycurl-7.19.3/examples/tests/0000755000470500047050000000000012263734634015505 5ustar piepie00000000000000pycurl-7.19.3/examples/tests/test_gtk.py0000644000470500047050000000515412262015545017700 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import sys, threading import pycurl import pygtk pygtk.require('2.0') import gtk # We should ignore SIGPIPE when using pycurl.NOSIGNAL - see # the libcurl tutorial for more info. try: import signal from signal import SIGPIPE, SIG_IGN signal.signal(signal.SIGPIPE, signal.SIG_IGN) except ImportError: pass class ProgressBar: def __init__(self, uri): self.round = 0.0 win = gtk.Window(gtk.WINDOW_TOPLEVEL) win.set_title("PycURL progress") win.show() vbox = gtk.VBox(spacing=5) vbox.set_border_width(10) win.add(vbox) win.set_default_size(200, 20) vbox.show() label = gtk.Label("Downloading %s" % uri) label.set_alignment(0, 0.5) vbox.pack_start(label) label.show() pbar = gtk.ProgressBar() pbar.show() self.pbar = pbar vbox.pack_start(pbar) win.connect("destroy", self.close_app) def progress(self, download_t, download_d, upload_t, upload_d): if download_t == 0: self.round = self.round + 0.1 if self.round >= 1.0: self.round = 0.0 else: self.round = float(download_d) / float(download_t) gtk.threads_enter() self.pbar.set_fraction(self.round) gtk.threads_leave() def mainloop(self): gtk.threads_enter() gtk.main() gtk.threads_leave() def close_app(self, *args): args[0].destroy() gtk.main_quit() class Test(threading.Thread): def __init__(self, url, target_file, progress): threading.Thread.__init__(self) self.target_file = target_file self.progress = progress self.curl = pycurl.Curl() self.curl.setopt(pycurl.URL, url) self.curl.setopt(pycurl.WRITEDATA, self.target_file) self.curl.setopt(pycurl.FOLLOWLOCATION, 1) self.curl.setopt(pycurl.NOPROGRESS, 0) self.curl.setopt(pycurl.PROGRESSFUNCTION, self.progress) self.curl.setopt(pycurl.MAXREDIRS, 5) self.curl.setopt(pycurl.NOSIGNAL, 1) def run(self): self.curl.perform() self.curl.close() self.target_file.close() self.progress(1.0, 1.0, 0, 0) # Check command line args if len(sys.argv) < 3: print("Usage: %s " % sys.argv[0]) raise SystemExit # Make a progress bar window p = ProgressBar(sys.argv[1]) # Start thread for fetching url Test(sys.argv[1], open(sys.argv[2], 'wb'), p.progress).start() # Enter the GTK mainloop gtk.threads_init() try: p.mainloop() except KeyboardInterrupt: pass pycurl-7.19.3/examples/tests/test_xmlrpc.py0000644000470500047050000000135512262015545020417 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et ## XML-RPC lib included in python2.2 try: import xmlrpclib except ImportError: import xmlrpc.client as xmlrpclib import pycurl # Header fields passed in request xmlrpc_header = [ "User-Agent: PycURL XML-RPC Test", "Content-Type: text/xml" ] # XML-RPC request template xmlrpc_template = """ %s%s """ # Engage c = pycurl.Curl() c.setopt(c.URL, 'http://betty.userland.com/RPC2') c.setopt(c.POST, 1) c.setopt(c.HTTPHEADER, xmlrpc_header) c.setopt(c.POSTFIELDS, xmlrpc_template % ("examples.getStateName", xmlrpclib.dumps((5,)))) print('Response from http://betty.userland.com/') c.perform() c.close() pycurl-7.19.3/examples/basicfirst.py0000644000470500047050000000066612262015545017046 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import sys import pycurl class Test: def __init__(self): self.contents = '' def body_callback(self, buf): self.contents = self.contents + buf sys.stderr.write("Testing %s\n" % pycurl.version) t = Test() c = pycurl.Curl() c.setopt(c.URL, 'http://curl.haxx.se/dev/') c.setopt(c.WRITEFUNCTION, t.body_callback) c.perform() c.close() print(t.contents) pycurl-7.19.3/examples/file_upload.py0000644000470500047050000000223312262015545017170 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import os, sys import pycurl # Class which holds a file reference and the read callback class FileReader: def __init__(self, fp): self.fp = fp def read_callback(self, size): return self.fp.read(size) # Check commandline arguments if len(sys.argv) < 3: print("Usage: %s " % sys.argv[0]) raise SystemExit url = sys.argv[1] filename = sys.argv[2] if not os.path.exists(filename): print("Error: the file '%s' does not exist" % filename) raise SystemExit # Initialize pycurl c = pycurl.Curl() c.setopt(pycurl.URL, url) c.setopt(pycurl.UPLOAD, 1) # Two versions with the same semantics here, but the filereader version # is useful when you have to process the data which is read before returning if 1: c.setopt(pycurl.READFUNCTION, FileReader(open(filename, 'rb')).read_callback) else: c.setopt(pycurl.READFUNCTION, open(filename, 'rb').read) # Set size of file to be uploaded. filesize = os.path.getsize(filename) c.setopt(pycurl.INFILESIZE, filesize) # Start transfer print('Uploading file %s to url %s' % (filename, url)) c.perform() c.close() pycurl-7.19.3/examples/linksys.py0000755000470500047050000005237512262015545016420 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # # linksys.py -- program settings on a Linkys router # # This tool is designed to help you recover from the occasional episodes # of catatonia that afflict Linksys boxes. It allows you to batch-program # them rather than manually entering values to the Web interface. Commands # are taken from the command line first, then standard input. # # The somewhat spotty coverage of status queries is because I only did the # ones that were either (a) easy, or (b) necessary. If you want to know the # status of the box, look at the web interface. # # This code has been tested against the following hardware: # # Hardware Firmware # ---------- --------------------- # BEFW11S4v2 1.44.2.1, Dec 20 2002 # # The code is, of course, sensitive to changes in the names of CGI pages # and field names. # # Note: to make the no-arguments form work, you'll need to have the following # entry in your ~/.netrc file. If you have changed the router IP address or # name/password, modify accordingly. # # machine 192.168.1.1 # login "" # password admin # # By Eric S. Raymond, August April 2003. All rites reversed. import sys, re, copy, curl, exceptions def print_stderr(arg): sys.stderr.write(arg) sys.stderr.write("\n") class LinksysError(exceptions.Exception): def __init__(self, *args): self.args = args class LinksysSession: months = 'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec' WAN_CONNECT_AUTO = '1' WAN_CONNECT_STATIC = '2' WAN_CONNECT_PPOE = '3' WAN_CONNECT_RAS = '4' WAN_CONNECT_PPTP = '5' WAN_CONNECT_HEARTBEAT = '6' # Substrings to check for on each page load. # This may enable us to detect when a firmware change has hosed us. check_strings = { "": "basic setup functions", "Passwd.htm": "For security reasons,", "DHCP.html": "You can configure the router to act as a DHCP", "Log.html": "There are some log settings and lists in this page.", "Forward.htm":"Port forwarding can be used to set up public services", } def __init__(self): self.actions = [] self.host = "http://192.168.1.1" self.verbosity = False self.pagecache = {} def set_verbosity(self, flag): self.verbosity = flag # This is not a performance hack -- we need the page cache to do # sanity checks at configure time. def cache_load(self, page): if page not in self.pagecache: fetch = curl.Curl(self.host) fetch.set_verbosity(self.verbosity) fetch.get(page) self.pagecache[page] = fetch.body() if fetch.answered("401"): raise LinksysError("authorization failure.", True) elif not fetch.answered(LinksysSession.check_strings[page]): del self.pagecache[page] raise LinksysError("check string for page %s missing!" % os.path.join(self.host, page), False) fetch.close() def cache_flush(self): self.pagecache = {} # Primitives def screen_scrape(self, page, template): self.cache_load(page) match = re.compile(template).search(self.pagecache[page]) if match: result = match.group(1) else: result = None return result def get_MAC_address(self, page, prefix): return self.screen_scrape("", prefix+r":[^M]*\(MAC Address: *([^)]*)") def set_flag(page, flag, value): if value: self.actions.append(page, flag, "1") else: self.actions.append(page, flag, "0") def set_IP_address(self, page, cgi, role, ip): ind = 0 for octet in ip.split("."): self.actions.append(("", "F1", role + `ind+1`, octet)) ind += 1 # Scrape configuration data off the main page def get_firmware_version(self): # This is fragile. There is no distinguishing tag before the firmware # version, so we have to key off the pattern of the version number. # Our model is ">1.44.2.1, Dec 20 2002<" return self.screen_scrape("", ">([0-9.v]*, (" + \ LinksysSession.months + ")[^<]*)<", ) def get_LAN_MAC(self): return self.get_MAC_address("", r"LAN IP Address") def get_Wireless_MAC(self): return self.get_MAC_address("", r"Wireless") def get_WAN_MAC(self): return self.get_MAC_address("", r"WAN Connection Type") # Set configuration data on the main page def set_host_name(self, name): self.actions.append(("", "hostName", name)) def set_domain_name(self, name): self.actions.append(("", "DomainName", name)) def set_LAN_IP(self, ip): self.set_IP_address("", "ipAddr", ip) def set_LAN_netmask(self, ip): if not ip.startswith("255.255.255."): raise ValueError lastquad = ip.split(".")[-1] if lastquad not in ("0", "128", "192", "240", "252"): raise ValueError self.actions.append("", "netMask", lastquad) def set_wireless(self, flag): self.set_flag("", "wirelessStatus") def set_SSID(self, ssid): self.actions.append(("", "wirelessESSID", ssid)) def set_SSID_broadcast(self, flag): self.set_flag("", "broadcastSSID") def set_channel(self, channel): self.actions.append(("", "wirelessChannel", channel)) def set_WEP(self, flag): self.set_flag("", "WepType") # FIXME: Add support for setting WEP keys def set_connection_type(self, type): self.actions.append(("", "WANConnectionType", type)) def set_WAN_IP(self, ip): self.set_IP_address("", "aliasIP", ip) def set_WAN_netmask(self, ip): self.set_IP_address("", "aliasMaskIP", ip) def set_WAN_gateway_address(self, ip): self.set_IP_address("", "routerIP", ip) def set_DNS_server(self, index, ip): self.set_IP_address("", "dns" + "ABC"[index], ip) # Set configuration data on the password page def set_password(self, str): self.actions.append("Passwd.htm","sysPasswd", str) self.actions.append("Passwd.htm","sysPasswdConfirm", str) def set_UPnP(self, flag): self.set_flag("Passwd.htm", "UPnP_Work") def reset(self): self.actions.append("Passwd.htm", "FactoryDefaults") # DHCP features def set_DHCP(self, flag): if flag: self.actions.append("DHCP.htm","dhcpStatus","Enable") else: self.actions.append("DHCP.htm","dhcpStatus","Disable") def set_DHCP_starting_IP(self, val): self.actions.append("DHCP.htm","dhcpS4", str(val)) def set_DHCP_users(self, val): self.actions.append("DHCP.htm","dhcpLen", str(val)) def set_DHCP_lease_time(self, val): self.actions.append("DHCP.htm","leaseTime", str(val)) def set_DHCP_DNS_server(self, index, ip): self.set_IP_address("DHCP.htm", "dns" + "ABC"[index], ip) # FIXME: add support for setting WINS key # Logging features def set_logging(self, flag): if flag: self.actions.append("Log.htm", "rLog", "Enable") else: self.actions.append("Log.htm", "rLog", "Disable") def set_log_address(self, val): self.actions.append("DHCP.htm","trapAddr3", str(val)) # The AOL parental control flag is not supported by design. # FIXME: add Filters and other advanced features def configure(self): "Write configuration changes to the Linksys." if self.actions: fields = [] self.cache_flush() for (page, field, value) in self.actions: self.cache_load(page) if self.pagecache[page].find(field) == -1: print_stderr("linksys: field %s not found where expected in page %s!" % (field, os.path.join(self.host, page))) continue else: fields.append((field, value)) # Clearing the action list before fieldsping is deliberate. # Otherwise we could get permanently wedged by a 401. self.actions = [] transaction = curl.Curl(self.host) transaction.set_verbosity(self.verbosity) transaction.get("Gozila.cgi", tuple(fields)) transaction.close() if __name__ == "__main__": import os, cmd class LinksysInterpreter(cmd.Cmd): """Interpret commands to perform LinkSys programming actions.""" def __init__(self): cmd.Cmd.__init__(self) self.session = LinksysSession() if os.isatty(0): import readline print("Type ? or `help' for help.") self.prompt = self.session.host + ": " else: self.prompt = "" print("Bar1") def flag_command(self, func, line): if line.strip() in ("on", "enable", "yes"): func(True) elif line.strip() in ("off", "disable", "no"): func(False) else: print_stderr("linksys: unknown switch value") return 0 def do_connect(self, line): newhost = line.strip() if newhost: self.session.host = newhost self.session.cache_flush() self.prompt = self.session.host + ": " else: print(self.session.host) return 0 def help_connect(self): print("Usage: connect []") print("Connect to a Linksys by name or IP address.") print("If no argument is given, print the current host.") def do_status(self, line): self.session.cache_load("") if "" in self.session.pagecache: print("Firmware:", self.session.get_firmware_version()) print("LAN MAC:", self.session.get_LAN_MAC()) print("Wireless MAC:", self.session.get_Wireless_MAC()) print("WAN MAC:", self.session.get_WAN_MAC()) print(".") return 0 def help_status(self): print("Usage: status") print("The status command shows the status of the Linksys.") print("It is mainly useful as a sanity check to make sure") print("the box is responding correctly.") def do_verbose(self, line): self.flag_command(self.session.set_verbosity, line) def help_verbose(self): print("Usage: verbose {on|off|enable|disable|yes|no}") print("Enables display of HTTP requests.") def do_host(self, line): self.session.set_host_name(line) return 0 def help_host(self): print("Usage: host ") print("Sets the Host field to be queried by the ISP.") def do_domain(self, line): print("Usage: host ") self.session.set_domain_name(line) return 0 def help_domain(self): print("Sets the Domain field to be queried by the ISP.") def do_lan_address(self, line): self.session.set_LAN_IP(line) return 0 def help_lan_address(self): print("Usage: lan_address ") print("Sets the LAN IP address.") def do_lan_netmask(self, line): self.session.set_LAN_netmask(line) return 0 def help_lan_netmask(self): print("Usage: lan_netmask ") print("Sets the LAN subnetwork mask.") def do_wireless(self, line): self.flag_command(self.session.set_wireless, line) return 0 def help_wireless(self): print("Usage: wireless {on|off|enable|disable|yes|no}") print("Switch to enable or disable wireless features.") def do_ssid(self, line): self.session.set_SSID(line) return 0 def help_ssid(self): print("Usage: ssid ") print("Sets the SSID used to control wireless access.") def do_ssid_broadcast(self, line): self.flag_command(self.session.set_SSID_broadcast, line) return 0 def help_ssid_broadcast(self): print("Usage: ssid_broadcast {on|off|enable|disable|yes|no}") print("Switch to enable or disable SSID broadcast.") def do_channel(self, line): self.session.set_channel(line) return 0 def help_channel(self): print("Usage: channel ") print("Sets the wireless channel.") def do_wep(self, line): self.flag_command(self.session.set_WEP, line) return 0 def help_wep(self): print("Usage: wep {on|off|enable|disable|yes|no}") print("Switch to enable or disable WEP security.") def do_wan_type(self, line): try: type=eval("LinksysSession.WAN_CONNECT_"+line.strip().upper()) self.session.set_connection_type(type) except ValueError: print_stderr("linksys: unknown connection type.") return 0 def help_wan_type(self): print("Usage: wan_type {auto|static|ppoe|ras|pptp|heartbeat}") print("Set the WAN connection type.") def do_wan_address(self, line): self.session.set_WAN_IP(line) return 0 def help_wan_address(self): print("Usage: wan_address ") print("Sets the WAN IP address.") def do_wan_netmask(self, line): self.session.set_WAN_netmask(line) return 0 def help_wan_netmask(self): print("Usage: wan_netmask ") print("Sets the WAN subnetwork mask.") def do_wan_gateway(self, line): self.session.set_WAN_gateway(line) return 0 def help_wan_gateway(self): print("Usage: wan_gateway ") print("Sets the LAN subnetwork mask.") def do_dns(self, line): (index, address) = line.split() if index in ("1", "2", "3"): self.session.set_DNS_server(eval(index), address) else: print_stderr("linksys: server index out of bounds.") return 0 def help_dns(self): print("Usage: dns {1|2|3} ") print("Sets a primary, secondary, or tertiary DNS server address.") def do_password(self, line): self.session.set_password(line) return 0 def help_password(self): print("Usage: password ") print("Sets the router password.") def do_upnp(self, line): self.flag_command(self.session.set_UPnP, line) return 0 def help_upnp(self): print("Usage: upnp {on|off|enable|disable|yes|no}") print("Switch to enable or disable Universal Plug and Play.") def do_reset(self, line): self.session.reset() def help_reset(self): print("Usage: reset") print("Reset Linksys settings to factory defaults.") def do_dhcp(self, line): self.flag_command(self.session.set_DHCP, line) def help_dhcp(self): print("Usage: dhcp {on|off|enable|disable|yes|no}") print("Switch to enable or disable DHCP features.") def do_dhcp_start(self, line): self.session.set_DHCP_starting_IP(line) def help_dhcp_start(self): print("Usage: dhcp_start ") print("Set the start address of the DHCP pool.") def do_dhcp_users(self, line): self.session.set_DHCP_users(line) def help_dhcp_users(self): print("Usage: dhcp_users ") print("Set number of address slots to allocate in the DHCP pool.") def do_dhcp_lease(self, line): self.session.set_DHCP_lease(line) def help_dhcp_lease(self): print("Usage: dhcp_lease ") print("Set number of address slots to allocate in the DHCP pool.") def do_dhcp_dns(self, line): (index, address) = line.split() if index in ("1", "2", "3"): self.session.set_DHCP_DNS_server(eval(index), address) else: print_stderr("linksys: server index out of bounds.") return 0 def help_dhcp_dns(self): print("Usage: dhcp_dns {1|2|3} ") print("Sets primary, secondary, or tertiary DNS server address.") def do_logging(self, line): self.flag_command(self.session.set_logging, line) def help_logging(self): print("Usage: logging {on|off|enable|disable|yes|no}") print("Switch to enable or disable session logging.") def do_log_address(self, line): self.session.set_Log_address(line) def help_log_address(self): print("Usage: log_address ") print("Set the last quad of the address to which to log.") def do_configure(self, line): self.session.configure() return 0 def help_configure(self): print("Usage: configure") print("Writes the configuration to the Linksys.") def do_cache(self, line): print(self.session.pagecache) def help_cache(self): print("Usage: cache") print("Display the page cache.") def do_quit(self, line): return 1 def help_quit(self, line): print("The quit command ends your linksys session without") print("writing configuration changes to the Linksys.") def do_EOF(self, line): print("") self.session.configure() return 1 def help_EOF(self): print("The EOF command writes the configuration to the linksys") print("and ends your session.") def default(self, line): """Pass the command through to be executed by the shell.""" os.system(line) return 0 def help_help(self): print("On-line help is available through this command.") print("? is a convenience alias for help.") def help_introduction(self): print("""\ This program supports changing the settings on Linksys blue-box routers. This capability may come in handy when they freeze up and have to be reset. Though it can be used interactively (and will command-prompt when standard input is a terminal) it is really designed to be used in batch mode. Commands are taken from the command line first, then standard input. By default, it is assumed that the Linksys is at http://192.168.1.1, the default LAN address. You can connect to a different address or IP with the 'connect' command. Note that your .netrc must contain correct user/password credentials for the router. The entry corresponding to the defaults is: machine 192.168.1.1 login "" password admin Most commands queue up changes but don't actually send them to the Linksys. You can force pending changes to be written with 'configure'. Otherwise, they will be shipped to the Linksys at the end of session (e.g. when the program running in batch mode encounters end-of-file or you type a control-D). If you end the session with `quit', pending changes will be discarded. For more help, read the topics 'wan', 'lan', and 'wireless'.""") def help_lan(self): print("""\ The `lan_address' and `lan_netmask' commands let you set the IP location of the Linksys on your LAN, or inside. Normally you'll want to leave these untouched.""") def help_wan(self): print("""\ The WAN commands become significant if you are using the BEFSR41 or any of the other Linksys boxes designed as DSL or cable-modem gateways. You will need to use `wan_type' to declare how you expect to get your address. If your ISP has issued you a static address, you'll need to use the `wan_address', `wan_netmask', and `wan_gateway' commands to set the address of the router as seen from the WAN, the outside. In this case you will also need to use the `dns' command to declare which remote servers your DNS requests should be forwarded to. Some ISPs may require you to set host and domain for use with dynamic-address allocation.""") def help_wireless(self): print("""\ The channel, ssid, ssid_broadcast, wep, and wireless commands control wireless routing.""") def help_switches(self): print("Switches may be turned on with 'on', 'enable', or 'yes'.") print("Switches may be turned off with 'off', 'disable', or 'no'.") print("Switch commands include: wireless, ssid_broadcast.") def help_addresses(self): print("An address argument must be a valid IP address;") print("four decimal numbers separated by dots, each ") print("between 0 and 255.") def emptyline(self): pass interpreter = LinksysInterpreter() for arg in sys.argv[1:]: interpreter.onecmd(arg) fatal = False while not fatal: try: interpreter.cmdloop() fatal = True except LinksysError, (message, fatal): print "linksys:", message # The following sets edit modes for GNU EMACS # Local Variables: # mode:python # End: pycurl-7.19.3/examples/retriever-multi.py0000644000470500047050000000644612262015545020056 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # # Usage: python retriever-multi.py [<# of # concurrent connections>] # import sys import pycurl # We should ignore SIGPIPE when using pycurl.NOSIGNAL - see # the libcurl tutorial for more info. try: import signal from signal import SIGPIPE, SIG_IGN signal.signal(signal.SIGPIPE, signal.SIG_IGN) except ImportError: pass # Get args num_conn = 10 try: if sys.argv[1] == "-": urls = sys.stdin.readlines() else: urls = open(sys.argv[1]).readlines() if len(sys.argv) >= 3: num_conn = int(sys.argv[2]) except: print("Usage: %s [<# of concurrent connections>]" % sys.argv[0]) raise SystemExit # Make a queue with (url, filename) tuples queue = [] for url in urls: url = url.strip() if not url or url[0] == "#": continue filename = "doc_%03d.dat" % (len(queue) + 1) queue.append((url, filename)) # Check args assert queue, "no URLs given" num_urls = len(queue) num_conn = min(num_conn, num_urls) assert 1 <= num_conn <= 10000, "invalid number of concurrent connections" print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)) print("----- Getting", num_urls, "URLs using", num_conn, "connections -----") # Pre-allocate a list of curl objects m = pycurl.CurlMulti() m.handles = [] for i in range(num_conn): c = pycurl.Curl() c.fp = None c.setopt(pycurl.FOLLOWLOCATION, 1) c.setopt(pycurl.MAXREDIRS, 5) c.setopt(pycurl.CONNECTTIMEOUT, 30) c.setopt(pycurl.TIMEOUT, 300) c.setopt(pycurl.NOSIGNAL, 1) m.handles.append(c) # Main loop freelist = m.handles[:] num_processed = 0 while num_processed < num_urls: # If there is an url to process and a free curl object, add to multi stack while queue and freelist: url, filename = queue.pop(0) c = freelist.pop() c.fp = open(filename, "wb") c.setopt(pycurl.URL, url) c.setopt(pycurl.WRITEDATA, c.fp) m.add_handle(c) # store some info c.filename = filename c.url = url # Run the internal curl state machine for the multi stack while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Check for curl objects which have terminated, and add them to the freelist while 1: num_q, ok_list, err_list = m.info_read() for c in ok_list: c.fp.close() c.fp = None m.remove_handle(c) print("Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL)) freelist.append(c) for c, errno, errmsg in err_list: c.fp.close() c.fp = None m.remove_handle(c) print("Failed: ", c.filename, c.url, errno, errmsg) freelist.append(c) num_processed = num_processed + len(ok_list) + len(err_list) if num_q == 0: break # Currently no more I/O is pending, could do something in the meantime # (display a progress bar, etc.). # We just call select() to sleep until some more data is available. m.select(1.0) # Cleanup for c in m.handles: if c.fp is not None: c.fp.close() c.fp = None c.close() m.close() pycurl-7.19.3/examples/retriever.py0000644000470500047050000000506412262015545016721 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # # Usage: python retriever.py [<# of # concurrent connections>] # import sys, threading, Queue import pycurl # We should ignore SIGPIPE when using pycurl.NOSIGNAL - see # the libcurl tutorial for more info. try: import signal from signal import SIGPIPE, SIG_IGN signal.signal(signal.SIGPIPE, signal.SIG_IGN) except ImportError: pass # Get args num_conn = 10 try: if sys.argv[1] == "-": urls = sys.stdin.readlines() else: urls = open(sys.argv[1]).readlines() if len(sys.argv) >= 3: num_conn = int(sys.argv[2]) except: print("Usage: %s [<# of concurrent connections>]" % sys.argv[0]) raise SystemExit # Make a queue with (url, filename) tuples queue = Queue.Queue() for url in urls: url = url.strip() if not url or url[0] == "#": continue filename = "doc_%03d.dat" % (len(queue.queue) + 1) queue.put((url, filename)) # Check args assert queue.queue, "no URLs given" num_urls = len(queue.queue) num_conn = min(num_conn, num_urls) assert 1 <= num_conn <= 10000, "invalid number of concurrent connections" print("PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)) print("----- Getting", num_urls, "URLs using", num_conn, "connections -----") class WorkerThread(threading.Thread): def __init__(self, queue): threading.Thread.__init__(self) self.queue = queue def run(self): while 1: try: url, filename = self.queue.get_nowait() except Queue.Empty: raise SystemExit fp = open(filename, "wb") curl = pycurl.Curl() curl.setopt(pycurl.URL, url) curl.setopt(pycurl.FOLLOWLOCATION, 1) curl.setopt(pycurl.MAXREDIRS, 5) curl.setopt(pycurl.CONNECTTIMEOUT, 30) curl.setopt(pycurl.TIMEOUT, 300) curl.setopt(pycurl.NOSIGNAL, 1) curl.setopt(pycurl.WRITEDATA, fp) try: curl.perform() except: import traceback traceback.print_exc(file=sys.stderr) sys.stderr.flush() curl.close() fp.close() sys.stdout.write(".") sys.stdout.flush() # Start a bunch of threads threads = [] for dummy in range(num_conn): t = WorkerThread(queue) t.start() threads.append(t) # Wait for all threads to finish for thread in threads: thread.join() pycurl-7.19.3/examples/sfquery.py0000644000470500047050000000463512262015545016413 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # # sfquery -- Source Forge query script using the ClientCGI high-level interface # # Retrieves a SourceForge XML export object for a given project. # Specify the *numeric* project ID. the user name, and the password, # as arguments. If you have a valid ~/.netrc entry for sourceforge.net, # you can just give the project ID. # # By Eric S. Raymond, August 2002. All rites reversed. import os, sys, netrc import curl class SourceForgeUserSession(curl.Curl): # SourceForge-specific methods. Sensitive to changes in site design. def login(self, name, password): "Establish a login session." self.post("account/login.php", (("form_loginname", name), ("form_pw", password), ("return_to", ""), ("stay_in_ssl", "1"), ("login", "Login With SSL"))) def logout(self): "Log out of SourceForge." self.get("account/logout.php") def fetch_xml(self, numid): self.get("export/xml_export.php?group_id=%s" % numid) if __name__ == "__main__": if len(sys.argv) == 1: project_id = '28236' # PyCurl project ID else: project_id = sys.argv[1] # Try to grab authenticators out of your .netrc try: auth = netrc.netrc().authenticators("sourceforge.net") name, account, password = auth except: if len(sys.argv) < 4: print("Usage: %s " % sys.argv[0]) raise SystemExit name = sys.argv[2] password = sys.argv[3] session = SourceForgeUserSession("https://sourceforge.net/") session.set_verbosity(0) session.login(name, password) # Login could fail. if session.answered("Invalid Password or User Name"): sys.stderr.write("Login/password not accepted (%d bytes)\n" % len(session.body())) sys.exit(1) # We'll see this if we get the right thing. elif session.answered("Personal Page For: " + name): session.fetch_xml(project_id) sys.stdout.write(session.body()) session.logout() sys.exit(0) # Or maybe SourceForge has changed its site design so our check strings # are no longer valid. else: sys.stderr.write("Unexpected page (%d bytes)\n"%len(session.body())) sys.exit(1) pycurl-7.19.3/examples/xmlrpc_curl.py0000644000470500047050000000376712262015545017254 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # We should ignore SIGPIPE when using pycurl.NOSIGNAL - see # the libcurl tutorial for more info. try: import signal from signal import SIGPIPE, SIG_IGN signal.signal(signal.SIGPIPE, signal.SIG_IGN) except ImportError: pass try: from cStringIO import StringIO except ImportError: try: from StringIO import StringIO except ImportError: from io import StringIO try: import xmlrpclib except ImportError: import xmlrpc.client as xmlrpclib import pycurl class CURLTransport(xmlrpclib.Transport): """Handles a cURL HTTP transaction to an XML-RPC server.""" xmlrpc_h = [ "Content-Type: text/xml" ] def __init__(self, username=None, password=None): self.c = pycurl.Curl() self.c.setopt(pycurl.POST, 1) self.c.setopt(pycurl.NOSIGNAL, 1) self.c.setopt(pycurl.CONNECTTIMEOUT, 30) self.c.setopt(pycurl.HTTPHEADER, self.xmlrpc_h) if username != None and password != None: self.c.setopt(pycurl.USERPWD, '%s:%s' % (username, password)) self._use_datetime = False def request(self, host, handler, request_body, verbose=0): b = StringIO() self.c.setopt(pycurl.URL, 'http://%s%s' % (host, handler)) self.c.setopt(pycurl.POSTFIELDS, request_body) self.c.setopt(pycurl.WRITEFUNCTION, b.write) self.c.setopt(pycurl.VERBOSE, verbose) self.verbose = verbose try: self.c.perform() except pycurl.error, v: raise xmlrpclib.ProtocolError( host + handler, v[0], v[1], None ) b.seek(0) return self.parse_response(b) if __name__ == "__main__": ## Test server = xmlrpclib.ServerProxy("http://betty.userland.com", transport=CURLTransport()) print(server) try: print(server.examples.getStateName(41)) except xmlrpclib.Error, v: print("ERROR", v) pycurl-7.19.3/python/0000755000470500047050000000000012263734634014046 5ustar piepie00000000000000pycurl-7.19.3/python/curl/0000755000470500047050000000000012263734634015013 5ustar piepie00000000000000pycurl-7.19.3/python/curl/__init__.py0000644000470500047050000001563612256232314017125 0ustar piepie00000000000000# A high-level interface to the pycurl extension # # ** mfx NOTE: the CGI class uses "black magic" using COOKIEFILE in # combination with a non-existant file name. See the libcurl docs # for more info. # # By Eric S. Raymond, April 2003. import sys, pycurl py3 = sys.version_info[0] == 3 # python 2/3 compatibility if py3: import urllib.parse as urllib_parse from urllib.parse import urljoin from io import BytesIO else: import urllib as urllib_parse from urlparse import urljoin try: from cStringIO import StringIO as BytesIO except ImportError: from StringIO import StringIO as BytesIO try: import signal from signal import SIGPIPE, SIG_IGN signal.signal(signal.SIGPIPE, signal.SIG_IGN) except ImportError: pass class Curl: "High-level interface to pycurl functions." def __init__(self, base_url="", fakeheaders=[]): self.handle = pycurl.Curl() # These members might be set. self.set_url(base_url) self.verbosity = 0 self.fakeheaders = fakeheaders # Nothing past here should be modified by the caller. self.payload = None self.payload_io = BytesIO() self.hrd = "" # Verify that we've got the right site; harmless on a non-SSL connect. self.set_option(pycurl.SSL_VERIFYHOST, 2) # Follow redirects in case it wants to take us to a CGI... self.set_option(pycurl.FOLLOWLOCATION, 1) self.set_option(pycurl.MAXREDIRS, 5) self.set_option(pycurl.NOSIGNAL, 1) # Setting this option with even a nonexistent file makes libcurl # handle cookie capture and playback automatically. self.set_option(pycurl.COOKIEFILE, "/dev/null") # Set timeouts to avoid hanging too long self.set_timeout(30) # Use password identification from .netrc automatically self.set_option(pycurl.NETRC, 1) self.set_option(pycurl.WRITEFUNCTION, self.payload_io.write) def header_callback(x): self.hdr += x.decode('ascii') self.set_option(pycurl.HEADERFUNCTION, header_callback) def set_timeout(self, timeout): "Set timeout for a retrieving an object" self.set_option(pycurl.TIMEOUT, timeout) def set_url(self, url): "Set the base URL to be retrieved." self.base_url = url self.set_option(pycurl.URL, self.base_url) def set_option(self, *args): "Set an option on the retrieval." self.handle.setopt(*args) def set_verbosity(self, level): "Set verbosity to 1 to see transactions." self.set_option(pycurl.VERBOSE, level) def __request(self, relative_url=None): "Perform the pending request." if self.fakeheaders: self.set_option(pycurl.HTTPHEADER, self.fakeheaders) if relative_url: self.set_option(pycurl.URL, urljoin(self.base_url, relative_url)) self.payload = None self.hdr = "" self.handle.perform() self.payload = self.payload_io.getvalue() return self.payload def get(self, url="", params=None): "Ship a GET request for a specified URL, capture the response." if params: url += "?" + urllib_parse.urlencode(params) self.set_option(pycurl.HTTPGET, 1) return self.__request(url) def post(self, cgi, params): "Ship a POST request to a specified CGI, capture the response." self.set_option(pycurl.POST, 1) self.set_option(pycurl.POSTFIELDS, urllib_parse.urlencode(params)) return self.__request(cgi) def body(self): "Return the body from the last response." return self.payload def header(self): "Return the header from the last response." return self.hdr def get_info(self, *args): "Get information about retrieval." return self.handle.getinfo(*args) def info(self): "Return a dictionary with all info on the last response." m = {} m['effective-url'] = self.handle.getinfo(pycurl.EFFECTIVE_URL) m['http-code'] = self.handle.getinfo(pycurl.HTTP_CODE) m['total-time'] = self.handle.getinfo(pycurl.TOTAL_TIME) m['namelookup-time'] = self.handle.getinfo(pycurl.NAMELOOKUP_TIME) m['connect-time'] = self.handle.getinfo(pycurl.CONNECT_TIME) m['pretransfer-time'] = self.handle.getinfo(pycurl.PRETRANSFER_TIME) m['redirect-time'] = self.handle.getinfo(pycurl.REDIRECT_TIME) m['redirect-count'] = self.handle.getinfo(pycurl.REDIRECT_COUNT) m['size-upload'] = self.handle.getinfo(pycurl.SIZE_UPLOAD) m['size-download'] = self.handle.getinfo(pycurl.SIZE_DOWNLOAD) m['speed-upload'] = self.handle.getinfo(pycurl.SPEED_UPLOAD) m['header-size'] = self.handle.getinfo(pycurl.HEADER_SIZE) m['request-size'] = self.handle.getinfo(pycurl.REQUEST_SIZE) m['content-length-download'] = self.handle.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD) m['content-length-upload'] = self.handle.getinfo(pycurl.CONTENT_LENGTH_UPLOAD) m['content-type'] = self.handle.getinfo(pycurl.CONTENT_TYPE) m['response-code'] = self.handle.getinfo(pycurl.RESPONSE_CODE) m['speed-download'] = self.handle.getinfo(pycurl.SPEED_DOWNLOAD) m['ssl-verifyresult'] = self.handle.getinfo(pycurl.SSL_VERIFYRESULT) m['filetime'] = self.handle.getinfo(pycurl.INFO_FILETIME) m['starttransfer-time'] = self.handle.getinfo(pycurl.STARTTRANSFER_TIME) m['redirect-time'] = self.handle.getinfo(pycurl.REDIRECT_TIME) m['redirect-count'] = self.handle.getinfo(pycurl.REDIRECT_COUNT) m['http-connectcode'] = self.handle.getinfo(pycurl.HTTP_CONNECTCODE) m['httpauth-avail'] = self.handle.getinfo(pycurl.HTTPAUTH_AVAIL) m['proxyauth-avail'] = self.handle.getinfo(pycurl.PROXYAUTH_AVAIL) m['os-errno'] = self.handle.getinfo(pycurl.OS_ERRNO) m['num-connects'] = self.handle.getinfo(pycurl.NUM_CONNECTS) m['ssl-engines'] = self.handle.getinfo(pycurl.SSL_ENGINES) m['cookielist'] = self.handle.getinfo(pycurl.INFO_COOKIELIST) m['lastsocket'] = self.handle.getinfo(pycurl.LASTSOCKET) m['ftp-entry-path'] = self.handle.getinfo(pycurl.FTP_ENTRY_PATH) return m def answered(self, check): "Did a given check string occur in the last payload?" return self.payload.find(check) >= 0 def close(self): "Close a session, freeing resources." if self.handle: self.handle.close() self.handle = None self.hdr = "" self.payload = "" def __del__(self): self.close() if __name__ == "__main__": if len(sys.argv) < 2: url = 'http://curl.haxx.se' else: url = sys.argv[1] c = Curl() c.get(url) print(c.body()) print('='*74 + '\n') import pprint pprint.pprint(c.info()) print(c.get_info(pycurl.OS_ERRNO)) print(c.info()['os-errno']) c.close() pycurl-7.19.3/src/0000755000470500047050000000000012263734634013314 5ustar piepie00000000000000pycurl-7.19.3/src/Makefile0000644000470500047050000000064112255756661014762 0ustar piepie00000000000000CC=gcc RM=rm CP=cp PYINCLUDE=/usr/include/python2.2 CURLINCLUDE=/usr/include/curl INCLUDE=-I$(PYINCLUDE) -I$(CURLINCLUDE) LIBS=-L/usr/lib -lcurl LDOPTS=-shared CCOPTS=-g -O2 -Wall -Wstrict-prototypes -fPIC all: $(CC) $(INCLUDE) $(CCOPTS) -c pycurl.c -o pycurl.o $(CC) $(LIBS) $(LDOPTS) -lcurl pycurl.o -o pycurl.so install: all $(CP) pycurl.so /usr/lib/python2.2/site-packages clean: $(RM) -f *~ *.o *obj *.so pycurl-7.19.3/src/pycurl.c0000644000470500047050000050143512262777702015010 0ustar piepie00000000000000/* PycURL -- cURL Python module * * Authors: * Copyright (C) 2001-2008 by Kjetil Jacobsen * Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer * Copyright (C) 2013-2014 by Oleg Pudeyev * * All rights reserved. * * Contributions: * Tino Lange * Matt King * Conrad Steenberg * Amit Mongia * Eric S. Raymond * Martin Muenstermann * Domenico Andreoli * Dominique * Paul Pacheco * Victor Lascurain * K.S.Sreeram * Jayne * Bastian Kleineidam * Mark Eichin * Aaron Hill * Daniel Pena Arteaga * Jim Patterson * Yuhui H * Nick Pilon * Thomas Hunger * Wim Lewis * * See file README for license information. */ #if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32) # define WIN32 1 #endif #include #include #include #include #include #include #include #include #if !defined(WIN32) #include #include #include #endif #include #include #include #undef NDEBUG #include #if defined(WIN32) /* supposedly not present in errno.h provided with VC */ # if !defined(EAFNOSUPPORT) # define EAFNOSUPPORT 97 # endif #endif /* The inet_ntop() was added in ws2_32.dll on Windows Vista [1]. Hence the * Windows SDK targeting lesser OS'es doesn't provide that prototype. * Maybe we should use the local hidden inet_ntop() for all OS'es thus * making a pycurl.pyd work across OS'es w/o rebuilding? * * [1] http://msdn.microsoft.com/en-us/library/windows/desktop/cc805843(v=vs.85).aspx */ #if defined(WIN32) && ((_WIN32_WINNT < 0x0600) || (NTDDI_VERSION < NTDDI_VISTA)) static const char * pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size); #define inet_ntop(fam,addr,string,size) pycurl_inet_ntop(fam,addr,string,size) #endif /* Ensure we have updated versions */ #if !defined(PY_VERSION_HEX) || (PY_VERSION_HEX < 0x02040000) # error "Need Python version 2.4 or greater to compile pycurl." #endif #if !defined(LIBCURL_VERSION_NUM) || (LIBCURL_VERSION_NUM < 0x071300) # error "Need libcurl version 7.19.0 or greater to compile pycurl." #endif #if LIBCURL_VERSION_NUM >= 0x071301 /* check for 7.19.1 or greater */ #define HAVE_CURLOPT_USERNAME #define HAVE_CURLOPT_PROXYUSERNAME #define HAVE_CURLOPT_CERTINFO #define HAVE_CURLOPT_POSTREDIR #endif #if LIBCURL_VERSION_NUM >= 0x071303 /* check for 7.19.3 or greater */ #define HAVE_CURLAUTH_DIGEST_IE #endif #if LIBCURL_VERSION_NUM >= 0x071500 /* check for 7.21.0 or greater */ #define HAVE_CURLINFO_LOCAL_PORT #define HAVE_CURLINFO_PRIMARY_PORT #define HAVE_CURLINFO_LOCAL_IP #endif #if LIBCURL_VERSION_NUM >= 0x071304 /* check for 7.19.4 or greater */ #define HAVE_CURLOPT_NOPROXY #endif #if LIBCURL_VERSION_NUM >= 0x071503 /* check for 7.21.3 or greater */ #define HAVE_CURLOPT_RESOLVE #endif #if LIBCURL_VERSION_NUM >= 0x071800 /* check for 7.24.0 or greater */ #define HAVE_CURLOPT_DNS_SERVERS #endif #if LIBCURL_VERSION_NUM >= 0x071A00 /* check for 7.26.0 or greater */ #define HAVE_CURL_REDIR_POST_303 #endif /* Python < 2.5 compat for Py_ssize_t */ #if PY_VERSION_HEX < 0x02050000 typedef int Py_ssize_t; #endif /* Py_TYPE is defined by Python 2.6+ */ #if PY_VERSION_HEX < 0x02060000 && !defined(Py_TYPE) # define Py_TYPE(x) (x)->ob_type #endif #undef UNUSED #define UNUSED(var) ((void)&var) /* Cruft for thread safe SSL crypto locks, snapped from the PHP curl extension */ #if defined(HAVE_CURL_SSL) # if defined(HAVE_CURL_OPENSSL) # define PYCURL_NEED_SSL_TSL # define PYCURL_NEED_OPENSSL_TSL # include # define COMPILE_SSL_LIB "openssl" # elif defined(HAVE_CURL_GNUTLS) # include # if GNUTLS_VERSION_NUMBER <= 0x020b00 # define PYCURL_NEED_SSL_TSL # define PYCURL_NEED_GNUTLS_TSL # include # endif # define COMPILE_SSL_LIB "gnutls" # elif defined(HAVE_CURL_NSS) # define COMPILE_SSL_LIB "nss" # else # warning \ "libcurl was compiled with SSL support, but configure could not determine which " \ "library was used; thus no SSL crypto locking callbacks will be set, which may " \ "cause random crashes on SSL requests" /* since we have no crypto callbacks for other ssl backends, * no reason to require users match those */ # define COMPILE_SSL_LIB "none/other" # endif /* HAVE_CURL_OPENSSL || HAVE_CURL_GNUTLS || HAVE_CURL_NSS */ #else # define COMPILE_SSL_LIB "none/other" #endif /* HAVE_CURL_SSL */ #if defined(PYCURL_NEED_SSL_TSL) static void pycurl_ssl_init(void); static void pycurl_ssl_cleanup(void); #endif #ifdef WITH_THREAD # define PYCURL_DECLARE_THREAD_STATE PyThreadState *tmp_state # define PYCURL_ACQUIRE_THREAD() acquire_thread(self, &tmp_state) # define PYCURL_ACQUIRE_THREAD_MULTI() acquire_thread_multi(self, &tmp_state) # define PYCURL_RELEASE_THREAD() release_thread(tmp_state) /* Replacement for Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS when python callbacks are expected during blocking i/o operations: self->state will hold the handle to current thread to be used as context */ # define PYCURL_BEGIN_ALLOW_THREADS \ self->state = PyThreadState_Get(); \ assert(self->state != NULL); \ Py_BEGIN_ALLOW_THREADS # define PYCURL_END_ALLOW_THREADS \ Py_END_ALLOW_THREADS \ self->state = NULL; #else # define PYCURL_DECLARE_THREAD_STATE # define PYCURL_ACQUIRE_THREAD() (1) # define PYCURL_ACQUIRE_THREAD_MULTI() (1) # define PYCURL_RELEASE_THREAD() # define PYCURL_BEGIN_ALLOW_THREADS # define PYCURL_END_ALLOW_THREADS #endif #if PY_MAJOR_VERSION >= 3 #define PyInt_Type PyLong_Type #define PyInt_Check(op) PyLong_Check(op) #define PyInt_FromLong PyLong_FromLong #define PyInt_AsLong PyLong_AsLong #endif /* Calculate the number of OBJECTPOINT options we need to store */ #define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000) #define MOPTIONS_SIZE ((int)CURLMOPT_LASTENTRY % 10000) /* Memory groups */ /* Attributes dictionary */ #define PYCURL_MEMGROUP_ATTRDICT 1 /* multi_stack */ #define PYCURL_MEMGROUP_MULTI 2 /* Python callbacks */ #define PYCURL_MEMGROUP_CALLBACK 4 /* Python file objects */ #define PYCURL_MEMGROUP_FILE 8 /* Share objects */ #define PYCURL_MEMGROUP_SHARE 16 /* httppost buffer references */ #define PYCURL_MEMGROUP_HTTPPOST 32 /* Postfields object */ #define PYCURL_MEMGROUP_POSTFIELDS 64 #define PYCURL_MEMGROUP_EASY \ (PYCURL_MEMGROUP_CALLBACK | PYCURL_MEMGROUP_FILE | \ PYCURL_MEMGROUP_HTTPPOST | PYCURL_MEMGROUP_POSTFIELDS) #define PYCURL_MEMGROUP_ALL \ (PYCURL_MEMGROUP_ATTRDICT | PYCURL_MEMGROUP_EASY | \ PYCURL_MEMGROUP_MULTI | PYCURL_MEMGROUP_SHARE) #if defined(WIN32) # define PYCURL_STRINGIZE_IMP(x) #x # define PYCURL_STRINGIZE(x) PYCURL_STRINGIZE_IMP(x) # define PYCURL_VERSION_STRING PYCURL_STRINGIZE(PYCURL_VERSION) #else # define PYCURL_VERSION_STRING PYCURL_VERSION #endif #define PYCURL_VERSION_PREFIX "PycURL/" PYCURL_VERSION_STRING /* Initialized during module init */ static char *g_pycurl_useragent = NULL; /* Type objects */ static PyObject *ErrorObject = NULL; static PyTypeObject *p_Curl_Type = NULL; static PyTypeObject *p_CurlMulti_Type = NULL; static PyTypeObject *p_CurlShare_Type = NULL; typedef struct { PyThread_type_lock locks[CURL_LOCK_DATA_LAST]; } ShareLock; typedef struct { PyObject_HEAD PyObject *dict; /* Python attributes dictionary */ CURLSH *share_handle; #ifdef WITH_THREAD ShareLock *lock; /* lock object to implement CURLSHOPT_LOCKFUNC */ #endif } CurlShareObject; typedef struct { PyObject_HEAD PyObject *dict; /* Python attributes dictionary */ CURLM *multi_handle; #ifdef WITH_THREAD PyThreadState *state; #endif fd_set read_fd_set; fd_set write_fd_set; fd_set exc_fd_set; /* callbacks */ PyObject *t_cb; PyObject *s_cb; } CurlMultiObject; typedef struct { PyObject_HEAD PyObject *dict; /* Python attributes dictionary */ CURL *handle; #ifdef WITH_THREAD PyThreadState *state; #endif CurlMultiObject *multi_stack; CurlShareObject *share; struct curl_httppost *httppost; /* List of INC'ed references associated with httppost. */ PyObject *httppost_ref_list; struct curl_slist *httpheader; struct curl_slist *http200aliases; struct curl_slist *quote; struct curl_slist *postquote; struct curl_slist *prequote; #ifdef HAVE_CURLOPT_RESOLVE struct curl_slist *resolve; #endif /* callbacks */ PyObject *w_cb; PyObject *h_cb; PyObject *r_cb; PyObject *pro_cb; PyObject *debug_cb; PyObject *ioctl_cb; PyObject *opensocket_cb; PyObject *seek_cb; /* file objects */ PyObject *readdata_fp; PyObject *writedata_fp; PyObject *writeheader_fp; /* reference to the object used for CURLOPT_POSTFIELDS */ PyObject *postfields_obj; /* misc */ char error[CURL_ERROR_SIZE+1]; } CurlObject; /* Raise exception based on return value `res' and `self->error' */ #define CURLERROR_RETVAL() do {\ PyObject *v; \ self->error[sizeof(self->error) - 1] = 0; \ v = Py_BuildValue("(is)", (int) (res), self->error); \ if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ return NULL; \ } while (0) /* Raise exception based on return value `res' and custom message */ #define CURLERROR_MSG(msg) do {\ PyObject *v; const char *m = (msg); \ v = Py_BuildValue("(is)", (int) (res), (m)); \ if (v != NULL) { PyErr_SetObject(ErrorObject, v); Py_DECREF(v); } \ return NULL; \ } while (0) /************************************************************************* // python 2/3 compatibility **************************************************************************/ #if PY_MAJOR_VERSION >= 3 # define PyText_FromFormat(format, str) PyUnicode_FromFormat((format), (str)) # define PyText_FromString(str) PyUnicode_FromString(str) # define PyByteStr_Check(obj) PyBytes_Check(obj) # define PyByteStr_AsStringAndSize(obj, buffer, length) PyBytes_AsStringAndSize((obj), (buffer), (length)) #else # define PyText_FromFormat(format, str) PyString_FromFormat((format), (str)) # define PyText_FromString(str) PyString_FromString(str) # define PyByteStr_Check(obj) PyString_Check(obj) # define PyByteStr_AsStringAndSize(obj, buffer, length) PyString_AsStringAndSize((obj), (buffer), (length)) #endif #define PyText_EncodedDecref(encoded) Py_XDECREF(encoded) /************************************************************************* // python utility functions **************************************************************************/ int PyText_AsStringAndSize(PyObject *obj, char **buffer, Py_ssize_t *length, PyObject **encoded_obj) { if (PyByteStr_Check(obj)) { *encoded_obj = NULL; return PyByteStr_AsStringAndSize(obj, buffer, length); } else { int rv; assert(PyUnicode_Check(obj)); *encoded_obj = PyUnicode_AsEncodedString(obj, "ascii", "strict"); if (*encoded_obj == NULL) { return -1; } rv = PyByteStr_AsStringAndSize(*encoded_obj, buffer, length); if (rv != 0) { /* If we free the object, pointer must be reset to NULL */ Py_CLEAR(*encoded_obj); } return rv; } } /* Like PyString_AsString(), but set an exception if the string contains * embedded NULs. Actually PyString_AsStringAndSize() already does that for * us if the `len' parameter is NULL - see Objects/stringobject.c. */ static char *PyText_AsString_NoNUL(PyObject *obj, PyObject **encoded_obj) { char *s = NULL; Py_ssize_t r; r = PyText_AsStringAndSize(obj, &s, NULL, encoded_obj); if (r != 0) return NULL; /* exception already set */ assert(s != NULL); return s; } /* Returns true if the object is of a type that can be given to * curl_easy_setopt and such - either a byte string or a Unicode string * with ASCII code points only. */ #if PY_MAJOR_VERSION >= 3 static int PyText_Check(PyObject *o) { return PyUnicode_Check(o) || PyBytes_Check(o); } #else static int PyText_Check(PyObject *o) { return PyUnicode_Check(o) || PyString_Check(o); } #endif /* Convert a curl slist (a list of strings) to a Python list. * In case of error return NULL with an exception set. */ static PyObject *convert_slist(struct curl_slist *slist, int free_flags) { PyObject *ret = NULL; struct curl_slist *slist_start = slist; ret = PyList_New((Py_ssize_t)0); if (ret == NULL) goto error; for ( ; slist != NULL; slist = slist->next) { PyObject *v = NULL; if (slist->data == NULL) { v = Py_None; Py_INCREF(v); } else { v = PyText_FromString(slist->data); } if (v == NULL || PyList_Append(ret, v) != 0) { Py_XDECREF(v); goto error; } Py_DECREF(v); } if ((free_flags & 1) && slist_start) curl_slist_free_all(slist_start); return ret; error: Py_XDECREF(ret); if ((free_flags & 2) && slist_start) curl_slist_free_all(slist_start); return NULL; } #ifdef HAVE_CURLOPT_CERTINFO /* Convert a struct curl_certinfo into a Python data structure. * In case of error return NULL with an exception set. */ static PyObject *convert_certinfo(struct curl_certinfo *cinfo) { PyObject *certs; int cert_index; if (!cinfo) Py_RETURN_NONE; certs = PyList_New((Py_ssize_t)(cinfo->num_of_certs)); if (!certs) return NULL; for (cert_index = 0; cert_index < cinfo->num_of_certs; cert_index ++) { struct curl_slist *fields = cinfo->certinfo[cert_index]; struct curl_slist *field_cursor; int field_count, field_index; PyObject *cert; field_count = 0; field_cursor = fields; while (field_cursor != NULL) { field_cursor = field_cursor->next; field_count ++; } cert = PyTuple_New((Py_ssize_t)field_count); if (!cert) goto error; PyList_SetItem(certs, cert_index, cert); /* Eats the ref from New() */ for(field_index = 0, field_cursor = fields; field_cursor != NULL; field_index ++, field_cursor = field_cursor->next) { const char *field = field_cursor->data; PyObject *field_tuple; if (!field) { field_tuple = Py_None; Py_INCREF(field_tuple); } else { const char *sep = strchr(field, ':'); if (!sep) { field_tuple = PyText_FromString(field); } else { /* XXX check */ field_tuple = Py_BuildValue("s#s", field, (int)(sep - field), sep+1); } if (!field_tuple) goto error; } PyTuple_SET_ITEM(cert, field_index, field_tuple); /* Eats the ref */ } } return certs; error: Py_DECREF(certs); return NULL; } #endif #ifdef WITH_THREAD /************************************************************************* // static utility functions **************************************************************************/ static PyThreadState * get_thread_state(const CurlObject *self) { /* Get the thread state for callbacks to run in. * This is either `self->state' when running inside perform() or * `self->multi_stack->state' when running inside multi_perform(). * When the result is != NULL we also implicitly assert * a valid `self->handle'. */ if (self == NULL) return NULL; assert(Py_TYPE(self) == p_Curl_Type); if (self->state != NULL) { /* inside perform() */ assert(self->handle != NULL); if (self->multi_stack != NULL) { assert(self->multi_stack->state == NULL); } return self->state; } if (self->multi_stack != NULL && self->multi_stack->state != NULL) { /* inside multi_perform() */ assert(self->handle != NULL); assert(self->multi_stack->multi_handle != NULL); assert(self->state == NULL); return self->multi_stack->state; } return NULL; } static PyThreadState * get_thread_state_multi(const CurlMultiObject *self) { /* Get the thread state for callbacks to run in when given * multi handles instead of regular handles */ if (self == NULL) return NULL; assert(Py_TYPE(self) == p_CurlMulti_Type); if (self->state != NULL) { /* inside multi_perform() */ assert(self->multi_handle != NULL); return self->state; } return NULL; } static int acquire_thread(const CurlObject *self, PyThreadState **state) { *state = get_thread_state(self); if (*state == NULL) return 0; PyEval_AcquireThread(*state); return 1; } static int acquire_thread_multi(const CurlMultiObject *self, PyThreadState **state) { *state = get_thread_state_multi(self); if (*state == NULL) return 0; PyEval_AcquireThread(*state); return 1; } static void release_thread(PyThreadState *state) { PyEval_ReleaseThread(state); } #endif /* assert some CurlShareObject invariants */ static void assert_share_state(const CurlShareObject *self) { assert(self != NULL); assert(Py_TYPE(self) == p_CurlShare_Type); #ifdef WITH_THREAD assert(self->lock != NULL); #endif } /* assert some CurlObject invariants */ static void assert_curl_state(const CurlObject *self) { assert(self != NULL); assert(Py_TYPE(self) == p_Curl_Type); #ifdef WITH_THREAD (void) get_thread_state(self); #endif } /* assert some CurlMultiObject invariants */ static void assert_multi_state(const CurlMultiObject *self) { assert(self != NULL); assert(Py_TYPE(self) == p_CurlMulti_Type); #ifdef WITH_THREAD if (self->state != NULL) { assert(self->multi_handle != NULL); } #endif } /* check state for methods */ static int check_curl_state(const CurlObject *self, int flags, const char *name) { assert_curl_state(self); if ((flags & 1) && self->handle == NULL) { PyErr_Format(ErrorObject, "cannot invoke %s() - no curl handle", name); return -1; } #ifdef WITH_THREAD if ((flags & 2) && get_thread_state(self) != NULL) { PyErr_Format(ErrorObject, "cannot invoke %s() - perform() is currently running", name); return -1; } #endif return 0; } static int check_multi_state(const CurlMultiObject *self, int flags, const char *name) { assert_multi_state(self); if ((flags & 1) && self->multi_handle == NULL) { PyErr_Format(ErrorObject, "cannot invoke %s() - no multi handle", name); return -1; } #ifdef WITH_THREAD if ((flags & 2) && self->state != NULL) { PyErr_Format(ErrorObject, "cannot invoke %s() - multi_perform() is currently running", name); return -1; } #endif return 0; } static int check_share_state(const CurlShareObject *self, int flags, const char *name) { assert_share_state(self); return 0; } /************************************************************************* // SSL TSL **************************************************************************/ #ifdef WITH_THREAD #ifdef PYCURL_NEED_OPENSSL_TSL static PyThread_type_lock *pycurl_openssl_tsl = NULL; static void pycurl_ssl_lock(int mode, int n, const char * file, int line) { if (mode & CRYPTO_LOCK) { PyThread_acquire_lock(pycurl_openssl_tsl[n], 1); } else { PyThread_release_lock(pycurl_openssl_tsl[n]); } } static unsigned long pycurl_ssl_id(void) { return (unsigned long) PyThread_get_thread_ident(); } static void pycurl_ssl_init(void) { int i, c = CRYPTO_num_locks(); pycurl_openssl_tsl = PyMem_Malloc(c * sizeof(PyThread_type_lock)); for (i = 0; i < c; ++i) { pycurl_openssl_tsl[i] = PyThread_allocate_lock(); } CRYPTO_set_id_callback(pycurl_ssl_id); CRYPTO_set_locking_callback(pycurl_ssl_lock); } static void pycurl_ssl_cleanup(void) { if (pycurl_openssl_tsl) { int i, c = CRYPTO_num_locks(); CRYPTO_set_id_callback(NULL); CRYPTO_set_locking_callback(NULL); for (i = 0; i < c; ++i) { PyThread_free_lock(pycurl_openssl_tsl[i]); } PyMem_Free(pycurl_openssl_tsl); pycurl_openssl_tsl = NULL; } } #endif #ifdef PYCURL_NEED_GNUTLS_TSL static int pycurl_ssl_mutex_create(void **m) { if ((*((PyThread_type_lock *) m) = PyThread_allocate_lock()) == NULL) { return -1; } else { return 0; } } static int pycurl_ssl_mutex_destroy(void **m) { PyThread_free_lock(*((PyThread_type_lock *) m)); return 0; } static int pycurl_ssl_mutex_lock(void **m) { return !PyThread_acquire_lock(*((PyThread_type_lock *) m), 1); } static int pycurl_ssl_mutex_unlock(void **m) { PyThread_release_lock(*((PyThread_type_lock *) m)); return 0; } static struct gcry_thread_cbs pycurl_gnutls_tsl = { GCRY_THREAD_OPTION_USER, NULL, pycurl_ssl_mutex_create, pycurl_ssl_mutex_destroy, pycurl_ssl_mutex_lock, pycurl_ssl_mutex_unlock }; static void pycurl_ssl_init(void) { gcry_control(GCRYCTL_SET_THREAD_CBS, &pycurl_gnutls_tsl); } static void pycurl_ssl_cleanup(void) { return; } #endif /************************************************************************* // CurlShareObject **************************************************************************/ static void share_lock_lock(ShareLock *lock, curl_lock_data data) { PyThread_acquire_lock(lock->locks[data], 1); } static void share_lock_unlock(ShareLock *lock, curl_lock_data data) { PyThread_release_lock(lock->locks[data]); } static ShareLock *share_lock_new(void) { int i; ShareLock *lock = (ShareLock*)PyMem_Malloc(sizeof(ShareLock)); assert(lock); for (i = 0; i < CURL_LOCK_DATA_LAST; ++i) { lock->locks[i] = PyThread_allocate_lock(); if (lock->locks[i] == NULL) { goto error; } } return lock; error: for (--i; i >= 0; --i) { PyThread_free_lock(lock->locks[i]); lock->locks[i] = NULL; } PyMem_Free(lock); return NULL; } static void share_lock_destroy(ShareLock *lock) { int i; assert(lock); for (i = 0; i < CURL_LOCK_DATA_LAST; ++i){ assert(lock->locks[i] != NULL); PyThread_free_lock(lock->locks[i]); } PyMem_Free(lock); lock = NULL; } static void share_lock_callback(CURL *handle, curl_lock_data data, curl_lock_access locktype, void *userptr) { CurlShareObject *share = (CurlShareObject*)userptr; share_lock_lock(share->lock, data); } static void share_unlock_callback(CURL *handle, curl_lock_data data, void *userptr) { CurlShareObject *share = (CurlShareObject*)userptr; share_lock_unlock(share->lock, data); } #else /* WITH_THREAD */ #if defined(PYCURL_NEED_SSL_TSL) static void pycurl_ssl_init(void) { return; } static void pycurl_ssl_cleanup(void) { return; } #endif #endif /* WITH_THREAD */ /* constructor - this is a module-level function returning a new instance */ static CurlShareObject * do_share_new(PyObject *dummy) { int res; CurlShareObject *self; #ifdef WITH_THREAD const curl_lock_function lock_cb = share_lock_callback; const curl_unlock_function unlock_cb = share_unlock_callback; #endif UNUSED(dummy); /* Allocate python curl-share object */ self = (CurlShareObject *) PyObject_GC_New(CurlShareObject, p_CurlShare_Type); if (self) { PyObject_GC_Track(self); } else { return NULL; } /* Initialize object attributes */ self->dict = NULL; #ifdef WITH_THREAD self->lock = share_lock_new(); assert(self->lock != NULL); #endif /* Allocate libcurl share handle */ self->share_handle = curl_share_init(); if (self->share_handle == NULL) { Py_DECREF(self); PyErr_SetString(ErrorObject, "initializing curl-share failed"); return NULL; } #ifdef WITH_THREAD /* Set locking functions and data */ res = curl_share_setopt(self->share_handle, CURLSHOPT_LOCKFUNC, lock_cb); assert(res == CURLE_OK); res = curl_share_setopt(self->share_handle, CURLSHOPT_USERDATA, self); assert(res == CURLE_OK); res = curl_share_setopt(self->share_handle, CURLSHOPT_UNLOCKFUNC, unlock_cb); assert(res == CURLE_OK); #endif return self; } static int do_share_traverse(CurlShareObject *self, visitproc visit, void *arg) { int err; #undef VISIT #define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err VISIT(self->dict); return 0; #undef VISIT } /* Drop references that may have created reference cycles. */ static int do_share_clear(CurlShareObject *self) { Py_CLEAR(self->dict); return 0; } static void util_share_close(CurlShareObject *self){ if (self->share_handle != NULL) { CURLSH *share_handle = self->share_handle; self->share_handle = NULL; curl_share_cleanup(share_handle); } } static void do_share_dealloc(CurlShareObject *self){ PyObject_GC_UnTrack(self); Py_TRASHCAN_SAFE_BEGIN(self); Py_CLEAR(self->dict); util_share_close(self); #ifdef WITH_THREAD share_lock_destroy(self->lock); #endif PyObject_GC_Del(self); Py_TRASHCAN_SAFE_END(self) } static PyObject * do_share_close(CurlShareObject *self) { if (check_share_state(self, 2, "close") != 0) { return NULL; } util_share_close(self); Py_RETURN_NONE; } /* setopt, unsetopt*/ /* --------------- unsetopt/setopt/getinfo --------------- */ static PyObject * do_curlshare_setopt(CurlShareObject *self, PyObject *args) { int option; PyObject *obj; if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) return NULL; if (check_share_state(self, 1 | 2, "sharesetopt") != 0) return NULL; /* early checks of option value */ if (option <= 0) goto error; if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) goto error; if (option % 10000 >= OPTIONS_SIZE) goto error; #if 0 /* XXX - should we ??? */ /* Handle the case of None */ if (obj == Py_None) { return util_curl_unsetopt(self, option); } #endif /* Handle the case of integer arguments */ if (PyInt_Check(obj)) { long d = PyInt_AsLong(obj); if (d != CURL_LOCK_DATA_COOKIE && d != CURL_LOCK_DATA_DNS && d != CURL_LOCK_DATA_SSL_SESSION) { goto error; } switch(option) { case CURLSHOPT_SHARE: case CURLSHOPT_UNSHARE: curl_share_setopt(self->share_handle, option, d); break; default: PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); return NULL; } Py_RETURN_NONE; } /* Failed to match any of the function signatures -- return error */ error: PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); return NULL; } /************************************************************************* // CurlObject **************************************************************************/ /* --------------- construct/destruct (i.e. open/close) --------------- */ /* Allocate a new python curl object */ static CurlObject * util_curl_new(void) { CurlObject *self; self = (CurlObject *) PyObject_GC_New(CurlObject, p_Curl_Type); if (self == NULL) return NULL; PyObject_GC_Track(self); /* Set python curl object initial values */ self->dict = NULL; self->handle = NULL; #ifdef WITH_THREAD self->state = NULL; #endif self->share = NULL; self->multi_stack = NULL; self->httppost = NULL; self->httppost_ref_list = NULL; self->httpheader = NULL; self->http200aliases = NULL; self->quote = NULL; self->postquote = NULL; self->prequote = NULL; #ifdef HAVE_CURLOPT_RESOLVE self->resolve = NULL; #endif /* Set callback pointers to NULL by default */ self->w_cb = NULL; self->h_cb = NULL; self->r_cb = NULL; self->pro_cb = NULL; self->debug_cb = NULL; self->ioctl_cb = NULL; self->opensocket_cb = NULL; self->seek_cb = NULL; /* Set file object pointers to NULL by default */ self->readdata_fp = NULL; self->writedata_fp = NULL; self->writeheader_fp = NULL; /* Set postfields object pointer to NULL by default */ self->postfields_obj = NULL; /* Zero string pointer memory buffer used by setopt */ memset(self->error, 0, sizeof(self->error)); return self; } /* initializer - used to intialize curl easy handles for use with pycurl */ static int util_curl_init(CurlObject *self) { int res; /* Set curl error buffer and zero it */ res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error); if (res != CURLE_OK) { return (-1); } memset(self->error, 0, sizeof(self->error)); /* Set backreference */ res = curl_easy_setopt(self->handle, CURLOPT_PRIVATE, (char *) self); if (res != CURLE_OK) { return (-1); } /* Enable NOPROGRESS by default, i.e. no progress output */ res = curl_easy_setopt(self->handle, CURLOPT_NOPROGRESS, (long)1); if (res != CURLE_OK) { return (-1); } /* Disable VERBOSE by default, i.e. no verbose output */ res = curl_easy_setopt(self->handle, CURLOPT_VERBOSE, (long)0); if (res != CURLE_OK) { return (-1); } /* Set FTP_ACCOUNT to NULL by default */ res = curl_easy_setopt(self->handle, CURLOPT_FTP_ACCOUNT, NULL); if (res != CURLE_OK) { return (-1); } /* Set default USERAGENT */ assert(g_pycurl_useragent); res = curl_easy_setopt(self->handle, CURLOPT_USERAGENT, g_pycurl_useragent); if (res != CURLE_OK) { return (-1); } return (0); } /* constructor - this is a module-level function returning a new instance */ static CurlObject * do_curl_new(PyObject *dummy) { CurlObject *self = NULL; int res; UNUSED(dummy); /* Allocate python curl object */ self = util_curl_new(); if (self == NULL) return NULL; /* Initialize curl handle */ self->handle = curl_easy_init(); if (self->handle == NULL) goto error; res = util_curl_init(self); if (res < 0) goto error; /* Success - return new object */ return self; error: Py_DECREF(self); /* this also closes self->handle */ PyErr_SetString(ErrorObject, "initializing curl failed"); return NULL; } /* util function shared by close() and clear() */ static void util_curl_xdecref(CurlObject *self, int flags, CURL *handle) { if (flags & PYCURL_MEMGROUP_ATTRDICT) { /* Decrement refcount for attributes dictionary. */ Py_CLEAR(self->dict); } if (flags & PYCURL_MEMGROUP_MULTI) { /* Decrement refcount for multi_stack. */ if (self->multi_stack != NULL) { CurlMultiObject *multi_stack = self->multi_stack; self->multi_stack = NULL; if (multi_stack->multi_handle != NULL && handle != NULL) { (void) curl_multi_remove_handle(multi_stack->multi_handle, handle); } Py_DECREF(multi_stack); } } if (flags & PYCURL_MEMGROUP_CALLBACK) { /* Decrement refcount for python callbacks. */ Py_CLEAR(self->w_cb); Py_CLEAR(self->h_cb); Py_CLEAR(self->r_cb); Py_CLEAR(self->pro_cb); Py_CLEAR(self->debug_cb); Py_CLEAR(self->ioctl_cb); } if (flags & PYCURL_MEMGROUP_FILE) { /* Decrement refcount for python file objects. */ Py_CLEAR(self->readdata_fp); Py_CLEAR(self->writedata_fp); Py_CLEAR(self->writeheader_fp); } if (flags & PYCURL_MEMGROUP_POSTFIELDS) { /* Decrement refcount for postfields object */ Py_CLEAR(self->postfields_obj); } if (flags & PYCURL_MEMGROUP_SHARE) { /* Decrement refcount for share objects. */ if (self->share != NULL) { CurlShareObject *share = self->share; self->share = NULL; if (share->share_handle != NULL && handle != NULL) { curl_easy_setopt(handle, CURLOPT_SHARE, NULL); } Py_DECREF(share); } } if (flags & PYCURL_MEMGROUP_HTTPPOST) { /* Decrement refcounts for httppost related references. */ Py_CLEAR(self->httppost_ref_list); } } static void util_curl_close(CurlObject *self) { CURL *handle; /* Zero handle and thread-state to disallow any operations to be run * from now on */ assert(self != NULL); assert(Py_TYPE(self) == p_Curl_Type); handle = self->handle; self->handle = NULL; if (handle == NULL) { /* Some paranoia assertions just to make sure the object * deallocation problem is finally really fixed... */ #ifdef WITH_THREAD assert(self->state == NULL); #endif assert(self->multi_stack == NULL); assert(self->share == NULL); return; /* already closed */ } #ifdef WITH_THREAD self->state = NULL; #endif /* Decref multi stuff which uses this handle */ util_curl_xdecref(self, PYCURL_MEMGROUP_MULTI, handle); /* Decref share which uses this handle */ util_curl_xdecref(self, PYCURL_MEMGROUP_SHARE, handle); /* Cleanup curl handle - must be done without the gil */ Py_BEGIN_ALLOW_THREADS curl_easy_cleanup(handle); Py_END_ALLOW_THREADS handle = NULL; /* Decref easy related objects */ util_curl_xdecref(self, PYCURL_MEMGROUP_EASY, handle); /* Free all variables allocated by setopt */ #undef SFREE #define SFREE(v) if ((v) != NULL) (curl_formfree(v), (v) = NULL) SFREE(self->httppost); #undef SFREE #define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL) SFREE(self->httpheader); SFREE(self->http200aliases); SFREE(self->quote); SFREE(self->postquote); SFREE(self->prequote); #ifdef HAVE_CURLOPT_RESOLVE SFREE(self->resolve); #endif #undef SFREE } static void do_curl_dealloc(CurlObject *self) { PyObject_GC_UnTrack(self); Py_TRASHCAN_SAFE_BEGIN(self) Py_CLEAR(self->dict); util_curl_close(self); PyObject_GC_Del(self); Py_TRASHCAN_SAFE_END(self) } static PyObject * do_curl_close(CurlObject *self) { if (check_curl_state(self, 2, "close") != 0) { return NULL; } util_curl_close(self); Py_RETURN_NONE; } static PyObject * do_curl_errstr(CurlObject *self) { if (check_curl_state(self, 1 | 2, "errstr") != 0) { return NULL; } self->error[sizeof(self->error) - 1] = 0; return PyText_FromString(self->error); } /* --------------- GC support --------------- */ /* Drop references that may have created reference cycles. */ static int do_curl_clear(CurlObject *self) { #ifdef WITH_THREAD assert(get_thread_state(self) == NULL); #endif util_curl_xdecref(self, PYCURL_MEMGROUP_ALL, self->handle); return 0; } /* Traverse all refcounted objects. */ static int do_curl_traverse(CurlObject *self, visitproc visit, void *arg) { int err; #undef VISIT #define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err VISIT(self->dict); VISIT((PyObject *) self->multi_stack); VISIT((PyObject *) self->share); VISIT(self->w_cb); VISIT(self->h_cb); VISIT(self->r_cb); VISIT(self->pro_cb); VISIT(self->debug_cb); VISIT(self->ioctl_cb); VISIT(self->opensocket_cb); VISIT(self->seek_cb); VISIT(self->readdata_fp); VISIT(self->writedata_fp); VISIT(self->writeheader_fp); VISIT(self->postfields_obj); return 0; #undef VISIT } /* --------------- perform --------------- */ static PyObject * do_curl_perform(CurlObject *self) { int res; if (check_curl_state(self, 1 | 2, "perform") != 0) { return NULL; } PYCURL_BEGIN_ALLOW_THREADS res = curl_easy_perform(self->handle); PYCURL_END_ALLOW_THREADS if (res != CURLE_OK) { CURLERROR_RETVAL(); } Py_RETURN_NONE; } /* --------------- callback handlers --------------- */ /* IMPORTANT NOTE: due to threading issues, we cannot call _any_ Python * function without acquiring the thread state in the callback handlers. */ static size_t util_write_callback(int flags, char *ptr, size_t size, size_t nmemb, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; size_t ret = 0; /* assume error */ PyObject *cb; int total_size; PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ cb = flags ? self->h_cb : self->w_cb; if (cb == NULL) goto silent_error; if (size <= 0 || nmemb <= 0) goto done; total_size = (int)(size * nmemb); if (total_size < 0 || (size_t)total_size / size != nmemb) { PyErr_SetString(ErrorObject, "integer overflow in write callback"); goto verbose_error; } /* run callback */ #if PY_MAJOR_VERSION >= 3 arglist = Py_BuildValue("(y#)", ptr, total_size); #else arglist = Py_BuildValue("(s#)", ptr, total_size); #endif if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = total_size; /* None means success */ } else if (PyInt_Check(result) || PyLong_Check(result)) { /* if the cast to long fails, PyLong_AsLong() returns -1L */ ret = (size_t) PyLong_AsLong(result); } else { PyErr_SetString(ErrorObject, "write callback must return int or None"); goto verbose_error; } done: silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *stream) { return util_write_callback(0, ptr, size, nmemb, stream); } static size_t header_callback(char *ptr, size_t size, size_t nmemb, void *stream) { return util_write_callback(1, ptr, size, nmemb, stream); } /* convert protocol address from C to python, returns a tuple of protocol specific values */ static PyObject * convert_protocol_address(struct sockaddr* saddr, unsigned int saddrlen) { PyObject *res_obj = NULL; switch (saddr->sa_family) { case AF_INET: { struct sockaddr_in* sin = (struct sockaddr_in*)saddr; char *addr_str = (char *)PyMem_Malloc(INET_ADDRSTRLEN); if (addr_str == NULL) { PyErr_SetString(ErrorObject, "Out of memory"); goto error; } if (inet_ntop(saddr->sa_family, &sin->sin_addr, addr_str, INET_ADDRSTRLEN) == NULL) { PyErr_SetFromErrno(ErrorObject); PyMem_Free(addr_str); goto error; } res_obj = Py_BuildValue("(si)", addr_str, ntohs(sin->sin_port)); PyMem_Free(addr_str); } break; case AF_INET6: { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)saddr; char *addr_str = (char *)PyMem_Malloc(INET6_ADDRSTRLEN); if (addr_str == NULL) { PyErr_SetString(ErrorObject, "Out of memory"); goto error; } if (inet_ntop(saddr->sa_family, &sin6->sin6_addr, addr_str, INET6_ADDRSTRLEN) == NULL) { PyErr_SetFromErrno(ErrorObject); PyMem_Free(addr_str); goto error; } res_obj = Py_BuildValue("(si)", addr_str, ntohs(sin6->sin6_port)); PyMem_Free(addr_str); } break; default: /* We (currently) only support IPv4/6 addresses. Can curl even be used with anything else? */ PyErr_SetString(ErrorObject, "Unsupported address family."); } error: return res_obj; } #if defined(WIN32) static SOCKET dup_winsock(SOCKET sock, const struct curl_sockaddr *address) { int rv; WSAPROTOCOL_INFO pi; rv = WSADuplicateSocket(sock, GetCurrentProcessId(), &pi); if (rv) { return CURL_SOCKET_BAD; } /* not sure if WSA_FLAG_OVERLAPPED is needed, but it does not seem to hurt */ return WSASocket(address->family, address->socktype, address->protocol, &pi, 0, WSA_FLAG_OVERLAPPED); } #endif /* curl_socket_t is just an int on unix/windows (with limitations that * are not important here) */ static curl_socket_t opensocket_callback(void *clientp, curlsocktype purpose, struct curl_sockaddr *address) { PyObject *arglist; PyObject *result = NULL; PyObject *fileno_result = NULL; CurlObject *self; int ret = CURL_SOCKET_BAD; PYCURL_DECLARE_THREAD_STATE; self = (CurlObject *)clientp; PYCURL_ACQUIRE_THREAD(); arglist = Py_BuildValue("(iiiN)", address->family, address->socktype, address->protocol, convert_protocol_address(&address->addr, address->addrlen)); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->opensocket_cb, arglist); Py_DECREF(arglist); if (result == NULL) { goto verbose_error; } if (PyObject_HasAttrString(result, "fileno")) { fileno_result = PyObject_CallMethod(result, "fileno", NULL); if (fileno_result == NULL) { ret = CURL_SOCKET_BAD; goto verbose_error; } // normal operation: if (PyInt_Check(fileno_result)) { int sockfd = PyInt_AsLong(fileno_result); #if defined(WIN32) ret = dup_winsock(sockfd, address); #else ret = dup(sockfd); #endif goto done; } } else { PyErr_SetString(ErrorObject, "Return value must be a socket."); ret = CURL_SOCKET_BAD; goto verbose_error; } silent_error: done: Py_XDECREF(result); Py_XDECREF(fileno_result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } static int seek_callback(void *stream, curl_off_t offset, int origin) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 2; /* assume error 2 (can't seek, libcurl free to work around). */ PyObject *cb; int source = 0; /* assume beginning */ PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check arguments */ switch (origin) { case SEEK_SET: source = 0; break; case SEEK_CUR: source = 1; break; case SEEK_END: source = 2; break; default: source = origin; break; } /* run callback */ cb = self->seek_cb; if (cb == NULL) goto silent_error; arglist = Py_BuildValue("(i,i)", offset, source); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = 0; /* None means success */ } else if (PyInt_Check(result)) { int ret_code = PyInt_AsLong(result); if (ret_code < 0 || ret_code > 2) { PyErr_Format(ErrorObject, "invalid return value for seek callback %d not in (0, 1, 2)", ret_code); goto verbose_error; } ret = ret_code; /* pass the return code from the callback */ } else { PyErr_SetString(ErrorObject, "seek callback must return 0 (CURL_SEEKFUNC_OK), 1 (CURL_SEEKFUNC_FAIL), 2 (CURL_SEEKFUNC_CANTSEEK) or None"); goto verbose_error; } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; size_t ret = CURL_READFUNC_ABORT; /* assume error, this actually works */ int total_size; PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->r_cb == NULL) goto silent_error; if (size <= 0 || nmemb <= 0) goto done; total_size = (int)(size * nmemb); if (total_size < 0 || (size_t)total_size / size != nmemb) { PyErr_SetString(ErrorObject, "integer overflow in read callback"); goto verbose_error; } /* run callback */ arglist = Py_BuildValue("(i)", total_size); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->r_cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (PyByteStr_Check(result)) { char *buf = NULL; Py_ssize_t obj_size = -1; Py_ssize_t r; r = PyByteStr_AsStringAndSize(result, &buf, &obj_size); if (r != 0 || obj_size < 0 || obj_size > total_size) { PyErr_Format(ErrorObject, "invalid return value for read callback (%ld bytes returned when at most %ld bytes were wanted)", (long)obj_size, (long)total_size); goto verbose_error; } memcpy(ptr, buf, obj_size); ret = obj_size; /* success */ } else if (PyUnicode_Check(result)) { char *buf = NULL; Py_ssize_t obj_size = -1; Py_ssize_t r; /* Encode with ascii codec. HTTP requires sending content-length for request body to the server before the request body is sent, therefore typically content length is given via POSTFIELDSIZE before read function is invoked to provide the data. However, if we encode the string using any encoding other than ascii, the length of encoded string may not match the length of unicode string we are encoding. Therefore, if client code does a simple len(source_string) to determine the value to supply in content-length, the length of bytes read may be different. To avoid this situation, we only accept ascii bytes in the string here. Encode data yourself to bytes when dealing with non-ascii data. */ PyObject *encoded = PyUnicode_AsEncodedString(result, "ascii", "strict"); if (encoded == NULL) { goto verbose_error; } r = PyByteStr_AsStringAndSize(encoded, &buf, &obj_size); if (r != 0 || obj_size < 0 || obj_size > total_size) { Py_DECREF(encoded); PyErr_Format(ErrorObject, "invalid return value for read callback (%ld bytes returned after encoding to utf-8 when at most %ld bytes were wanted)", (long)obj_size, (long)total_size); goto verbose_error; } memcpy(ptr, buf, obj_size); Py_DECREF(encoded); ret = obj_size; /* success */ } #if PY_MAJOR_VERSION < 3 else if (PyInt_Check(result)) { long r = PyInt_AsLong(result); if (r != CURL_READFUNC_ABORT && r != CURL_READFUNC_PAUSE) goto type_error; ret = r; /* either CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE */ } #endif else if (PyLong_Check(result)) { long r = PyLong_AsLong(result); if (r != CURL_READFUNC_ABORT && r != CURL_READFUNC_PAUSE) goto type_error; ret = r; /* either CURL_READFUNC_ABORT or CURL_READFUNC_PAUSE */ } else { type_error: PyErr_SetString(ErrorObject, "read callback must return a byte string or Unicode string with ASCII code points only"); goto verbose_error; } done: silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } static int progress_callback(void *stream, double dltotal, double dlnow, double ultotal, double ulnow) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 1; /* assume error */ PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->pro_cb == NULL) goto silent_error; /* run callback */ arglist = Py_BuildValue("(dddd)", dltotal, dlnow, ultotal, ulnow); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->pro_cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = 0; /* None means success */ } else if (PyInt_Check(result)) { ret = (int) PyInt_AsLong(result); } else { ret = PyObject_IsTrue(result); /* FIXME ??? */ } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } static int debug_callback(CURL *curlobj, curl_infotype type, char *buffer, size_t total_size, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 0; /* always success */ PYCURL_DECLARE_THREAD_STATE; UNUSED(curlobj); /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return ret; /* check args */ if (self->debug_cb == NULL) goto silent_error; if ((int)total_size < 0 || (size_t)((int)total_size) != total_size) { PyErr_SetString(ErrorObject, "integer overflow in debug callback"); goto verbose_error; } /* run callback */ arglist = Py_BuildValue("(is#)", (int)type, buffer, (int)total_size); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->debug_cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* return values from debug callbacks should be ignored */ silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; } static curlioerr ioctl_callback(CURL *curlobj, int cmd, void *stream) { CurlObject *self; PyObject *arglist; PyObject *result = NULL; int ret = CURLIOE_FAILRESTART; /* assume error */ PYCURL_DECLARE_THREAD_STATE; UNUSED(curlobj); /* acquire thread */ self = (CurlObject *)stream; if (!PYCURL_ACQUIRE_THREAD()) return (curlioerr) ret; /* check args */ if (self->ioctl_cb == NULL) goto silent_error; /* run callback */ arglist = Py_BuildValue("(i)", cmd); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->ioctl_cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* handle result */ if (result == Py_None) { ret = CURLIOE_OK; /* None means success */ } else if (PyInt_Check(result)) { ret = (int) PyInt_AsLong(result); if (ret >= CURLIOE_LAST || ret < 0) { PyErr_SetString(ErrorObject, "ioctl callback returned invalid value"); goto verbose_error; } } silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return (curlioerr) ret; verbose_error: PyErr_Print(); goto silent_error; } /* ------------------------ reset ------------------------ */ static PyObject* do_curl_reset(CurlObject *self) { int res; curl_easy_reset(self->handle); /* Decref easy interface related objects */ util_curl_xdecref(self, PYCURL_MEMGROUP_EASY, self->handle); /* Free all variables allocated by setopt */ #undef SFREE #define SFREE(v) if ((v) != NULL) (curl_formfree(v), (v) = NULL) SFREE(self->httppost); #undef SFREE #define SFREE(v) if ((v) != NULL) (curl_slist_free_all(v), (v) = NULL) SFREE(self->httpheader); SFREE(self->http200aliases); SFREE(self->quote); SFREE(self->postquote); SFREE(self->prequote); #ifdef HAVE_CURLOPT_RESOLVE SFREE(self->resolve); #endif #undef SFREE res = util_curl_init(self); if (res < 0) { Py_DECREF(self); /* this also closes self->handle */ PyErr_SetString(ErrorObject, "resetting curl failed"); return NULL; } Py_RETURN_NONE; } /* --------------- unsetopt/setopt/getinfo --------------- */ static PyObject * util_curl_unsetopt(CurlObject *self, int option) { int res; #define SETOPT2(o,x) \ if ((res = curl_easy_setopt(self->handle, (o), (x))) != CURLE_OK) goto error #define SETOPT(x) SETOPT2((CURLoption)option, (x)) /* FIXME: implement more options. Have to carefully check lib/url.c in the * libcurl source code to see if it's actually safe to simply * unset the option. */ switch (option) { case CURLOPT_SHARE: SETOPT((CURLSH *) NULL); Py_XDECREF(self->share); self->share = NULL; break; case CURLOPT_HTTPPOST: SETOPT((void *) 0); curl_formfree(self->httppost); util_curl_xdecref(self, PYCURL_MEMGROUP_HTTPPOST, self->handle); self->httppost = NULL; /* FIXME: what about data->set.httpreq ?? */ break; case CURLOPT_INFILESIZE: SETOPT((long) -1); break; case CURLOPT_WRITEHEADER: SETOPT((void *) 0); Py_CLEAR(self->writeheader_fp); break; case CURLOPT_CAINFO: case CURLOPT_CAPATH: case CURLOPT_COOKIE: case CURLOPT_COOKIEJAR: case CURLOPT_CUSTOMREQUEST: case CURLOPT_EGDSOCKET: case CURLOPT_FTPPORT: case CURLOPT_PROXYUSERPWD: #ifdef HAVE_CURLOPT_PROXYUSERNAME case CURLOPT_PROXYUSERNAME: case CURLOPT_PROXYPASSWORD: #endif case CURLOPT_RANDOM_FILE: case CURLOPT_SSL_CIPHER_LIST: case CURLOPT_USERPWD: #ifdef HAVE_CURLOPT_USERNAME case CURLOPT_USERNAME: case CURLOPT_PASSWORD: #endif case CURLOPT_RANGE: SETOPT((char *) 0); break; #ifdef HAVE_CURLOPT_CERTINFO case CURLOPT_CERTINFO: SETOPT((long) 0); break; #endif /* info: we explicitly list unsupported options here */ case CURLOPT_COOKIEFILE: default: PyErr_SetString(PyExc_TypeError, "unsetopt() is not supported for this option"); return NULL; } Py_RETURN_NONE; error: CURLERROR_RETVAL(); #undef SETOPT #undef SETOPT2 } static PyObject * do_curl_unsetopt(CurlObject *self, PyObject *args) { int option; if (!PyArg_ParseTuple(args, "i:unsetopt", &option)) { return NULL; } if (check_curl_state(self, 1 | 2, "unsetopt") != 0) { return NULL; } /* early checks of option value */ if (option <= 0) goto error; if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) goto error; if (option % 10000 >= OPTIONS_SIZE) goto error; return util_curl_unsetopt(self, option); error: PyErr_SetString(PyExc_TypeError, "invalid arguments to unsetopt"); return NULL; } static PyObject * do_curl_setopt(CurlObject *self, PyObject *args) { int option; PyObject *obj; int res; PyObject *encoded_obj; if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) return NULL; if (check_curl_state(self, 1 | 2, "setopt") != 0) return NULL; /* early checks of option value */ if (option <= 0) goto error; if (option >= (int)CURLOPTTYPE_OFF_T + OPTIONS_SIZE) goto error; if (option % 10000 >= OPTIONS_SIZE) goto error; /* Handle the case of None as the call of unsetopt() */ if (obj == Py_None) { return util_curl_unsetopt(self, option); } /* Handle the case of string arguments */ if (PyText_Check(obj)) { char *str = NULL; Py_ssize_t len = -1; /* Check that the option specified a string as well as the input */ switch (option) { case CURLOPT_CAINFO: case CURLOPT_CAPATH: case CURLOPT_COOKIE: case CURLOPT_COOKIEFILE: case CURLOPT_COOKIELIST: case CURLOPT_COOKIEJAR: case CURLOPT_CUSTOMREQUEST: case CURLOPT_EGDSOCKET: case CURLOPT_ENCODING: case CURLOPT_FTPPORT: case CURLOPT_INTERFACE: case CURLOPT_KRB4LEVEL: case CURLOPT_NETRC_FILE: case CURLOPT_PROXY: case CURLOPT_PROXYUSERPWD: #ifdef HAVE_CURLOPT_PROXYUSERNAME case CURLOPT_PROXYUSERNAME: case CURLOPT_PROXYPASSWORD: #endif case CURLOPT_RANDOM_FILE: case CURLOPT_RANGE: case CURLOPT_REFERER: case CURLOPT_SSLCERT: case CURLOPT_SSLCERTTYPE: case CURLOPT_SSLENGINE: case CURLOPT_SSLKEY: case CURLOPT_SSLKEYPASSWD: case CURLOPT_SSLKEYTYPE: case CURLOPT_SSL_CIPHER_LIST: case CURLOPT_URL: case CURLOPT_USERAGENT: case CURLOPT_USERPWD: #ifdef HAVE_CURLOPT_USERNAME case CURLOPT_USERNAME: case CURLOPT_PASSWORD: #endif case CURLOPT_FTP_ALTERNATIVE_TO_USER: case CURLOPT_SSH_PUBLIC_KEYFILE: case CURLOPT_SSH_PRIVATE_KEYFILE: case CURLOPT_COPYPOSTFIELDS: case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5: case CURLOPT_CRLFILE: case CURLOPT_ISSUERCERT: #ifdef HAVE_CURLOPT_DNS_SERVERS case CURLOPT_DNS_SERVERS: #endif #ifdef HAVE_CURLOPT_NOPROXY case CURLOPT_NOPROXY: #endif /* FIXME: check if more of these options allow binary data */ str = PyText_AsString_NoNUL(obj, &encoded_obj); if (str == NULL) return NULL; break; case CURLOPT_POSTFIELDS: if (PyText_AsStringAndSize(obj, &str, &len, &encoded_obj) != 0) return NULL; /* automatically set POSTFIELDSIZE */ if (len <= INT_MAX) { res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE, (long)len); } else { res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)len); } if (res != CURLE_OK) { CURLERROR_RETVAL(); } break; default: PyErr_SetString(PyExc_TypeError, "strings are not supported for this option"); return NULL; } assert(str != NULL); /* Call setopt */ res = curl_easy_setopt(self->handle, (CURLoption)option, str); /* Check for errors */ if (res != CURLE_OK) { PyText_EncodedDecref(encoded_obj); CURLERROR_RETVAL(); } /* libcurl does not copy the value of CURLOPT_POSTFIELDS */ if (option == CURLOPT_POSTFIELDS) { PyObject *store_obj; #if PY_MAJOR_VERSION >= 3 /* if obj was bytes, it was not encoded, and we need to incref obj. * if obj was unicode, it was encoded, and we need to incref * encoded_obj - except we can simply transfer ownership. */ if (encoded_obj) { store_obj = encoded_obj; } else { store_obj = obj; Py_INCREF(store_obj); } #else /* no encoding is performed, incref the original object. */ store_obj = obj; Py_INCREF(store_obj); #endif util_curl_xdecref(self, PYCURL_MEMGROUP_POSTFIELDS, self->handle); self->postfields_obj = store_obj; } else { PyText_EncodedDecref(encoded_obj); } Py_RETURN_NONE; } #define IS_LONG_OPTION(o) (o < CURLOPTTYPE_OBJECTPOINT) #define IS_OFF_T_OPTION(o) (o >= CURLOPTTYPE_OFF_T) /* Handle the case of integer arguments */ if (PyInt_Check(obj)) { long d = PyInt_AsLong(obj); if (IS_LONG_OPTION(option)) res = curl_easy_setopt(self->handle, (CURLoption)option, (long)d); else if (IS_OFF_T_OPTION(option)) res = curl_easy_setopt(self->handle, (CURLoption)option, (curl_off_t)d); else { PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); return NULL; } if (res != CURLE_OK) { CURLERROR_RETVAL(); } Py_RETURN_NONE; } /* Handle the case of long arguments (used by *_LARGE options) */ if (PyLong_Check(obj)) { PY_LONG_LONG d = PyLong_AsLongLong(obj); if (d == -1 && PyErr_Occurred()) return NULL; if (IS_LONG_OPTION(option) && (long)d == d) res = curl_easy_setopt(self->handle, (CURLoption)option, (long)d); else if (IS_OFF_T_OPTION(option) && (curl_off_t)d == d) res = curl_easy_setopt(self->handle, (CURLoption)option, (curl_off_t)d); else { PyErr_SetString(PyExc_TypeError, "longs are not supported for this option"); return NULL; } if (res != CURLE_OK) { CURLERROR_RETVAL(); } Py_RETURN_NONE; } #undef IS_LONG_OPTION #undef IS_OFF_T_OPTION #if PY_MAJOR_VERSION < 3 /* Handle the case of file objects */ if (PyFile_Check(obj)) { FILE *fp; /* Ensure the option specified a file as well as the input */ switch (option) { case CURLOPT_READDATA: case CURLOPT_WRITEDATA: break; case CURLOPT_WRITEHEADER: if (self->w_cb != NULL) { PyErr_SetString(ErrorObject, "cannot combine WRITEHEADER with WRITEFUNCTION."); return NULL; } break; default: PyErr_SetString(PyExc_TypeError, "files are not supported for this option"); return NULL; } fp = PyFile_AsFile(obj); if (fp == NULL) { PyErr_SetString(PyExc_TypeError, "second argument must be open file"); return NULL; } res = curl_easy_setopt(self->handle, (CURLoption)option, fp); if (res != CURLE_OK) { CURLERROR_RETVAL(); } Py_INCREF(obj); switch (option) { case CURLOPT_READDATA: Py_CLEAR(self->readdata_fp); self->readdata_fp = obj; break; case CURLOPT_WRITEDATA: Py_CLEAR(self->writedata_fp); self->writedata_fp = obj; break; case CURLOPT_WRITEHEADER: Py_CLEAR(self->writeheader_fp); self->writeheader_fp = obj; break; default: assert(0); break; } /* Return success */ Py_RETURN_NONE; } #endif /* Handle the case of list objects */ if (PyList_Check(obj)) { struct curl_slist **old_slist = NULL; struct curl_slist *slist = NULL; Py_ssize_t i, len; switch (option) { case CURLOPT_HTTP200ALIASES: old_slist = &self->http200aliases; break; case CURLOPT_HTTPHEADER: old_slist = &self->httpheader; break; case CURLOPT_QUOTE: old_slist = &self->quote; break; case CURLOPT_POSTQUOTE: old_slist = &self->postquote; break; case CURLOPT_PREQUOTE: old_slist = &self->prequote; break; #ifdef HAVE_CURLOPT_RESOLVE case CURLOPT_RESOLVE: old_slist = &self->resolve; break; #endif case CURLOPT_HTTPPOST: break; default: /* None of the list options were recognized, raise exception */ PyErr_SetString(PyExc_TypeError, "lists are not supported for this option"); return NULL; } len = PyList_Size(obj); if (len == 0) Py_RETURN_NONE; /* Handle HTTPPOST different since we construct a HttpPost form struct */ if (option == CURLOPT_HTTPPOST) { struct curl_httppost *post = NULL; struct curl_httppost *last = NULL; /* List of all references that have been INCed as a result of * this operation */ PyObject *ref_params = NULL; PyObject *nencoded_obj, *cencoded_obj, *oencoded_obj; for (i = 0; i < len; i++) { char *nstr = NULL, *cstr = NULL; Py_ssize_t nlen = -1, clen = -1; PyObject *listitem = PyList_GetItem(obj, i); if (!PyTuple_Check(listitem)) { curl_formfree(post); Py_XDECREF(ref_params); PyErr_SetString(PyExc_TypeError, "list items must be tuple objects"); return NULL; } if (PyTuple_GET_SIZE(listitem) != 2) { curl_formfree(post); Py_XDECREF(ref_params); PyErr_SetString(PyExc_TypeError, "tuple must contain two elements (name, value)"); return NULL; } if (PyText_AsStringAndSize(PyTuple_GET_ITEM(listitem, 0), &nstr, &nlen, &nencoded_obj) != 0) { curl_formfree(post); Py_XDECREF(ref_params); PyErr_SetString(PyExc_TypeError, "tuple must contain a byte string or Unicode string with ASCII code points only as first element"); return NULL; } if (PyText_Check(PyTuple_GET_ITEM(listitem, 1))) { /* Handle strings as second argument for backwards compatibility */ if (PyText_AsStringAndSize(PyTuple_GET_ITEM(listitem, 1), &cstr, &clen, &cencoded_obj)) { curl_formfree(post); Py_XDECREF(ref_params); CURLERROR_RETVAL(); } /* INFO: curl_formadd() internally does memdup() the data, so * embedded NUL characters _are_ allowed here. */ res = curl_formadd(&post, &last, CURLFORM_COPYNAME, nstr, CURLFORM_NAMELENGTH, (long) nlen, CURLFORM_COPYCONTENTS, cstr, CURLFORM_CONTENTSLENGTH, (long) clen, CURLFORM_END); PyText_EncodedDecref(cencoded_obj); if (res != CURLE_OK) { curl_formfree(post); Py_XDECREF(ref_params); CURLERROR_RETVAL(); } } else if (PyTuple_Check(PyTuple_GET_ITEM(listitem, 1))) { /* Supports content, file and content-type */ PyObject *t = PyTuple_GET_ITEM(listitem, 1); Py_ssize_t tlen = PyTuple_Size(t); int j, k, l; struct curl_forms *forms = NULL; /* Sanity check that there are at least two tuple items */ if (tlen < 2) { curl_formfree(post); Py_XDECREF(ref_params); PyErr_SetString(PyExc_TypeError, "tuple must contain at least one option and one value"); return NULL; } /* Allocate enough space to accommodate length options for content or buffers, plus a terminator. */ forms = PyMem_Malloc(sizeof(struct curl_forms) * ((tlen*2) + 1)); if (forms == NULL) { curl_formfree(post); Py_XDECREF(ref_params); PyErr_NoMemory(); return NULL; } /* Iterate all the tuple members pairwise */ for (j = 0, k = 0, l = 0; j < tlen; j += 2, l++) { char *ostr; Py_ssize_t olen; int val; if (j == (tlen-1)) { PyErr_SetString(PyExc_TypeError, "expected value"); PyMem_Free(forms); curl_formfree(post); Py_XDECREF(ref_params); return NULL; } if (!PyInt_Check(PyTuple_GET_ITEM(t, j))) { PyErr_SetString(PyExc_TypeError, "option must be long"); PyMem_Free(forms); curl_formfree(post); Py_XDECREF(ref_params); return NULL; } if (!PyText_Check(PyTuple_GET_ITEM(t, j+1))) { PyErr_SetString(PyExc_TypeError, "value must be a byte string or a Unicode string with ASCII code points only"); PyMem_Free(forms); curl_formfree(post); Py_XDECREF(ref_params); return NULL; } val = PyLong_AsLong(PyTuple_GET_ITEM(t, j)); if (val != CURLFORM_COPYCONTENTS && val != CURLFORM_FILE && val != CURLFORM_FILENAME && val != CURLFORM_CONTENTTYPE && val != CURLFORM_BUFFER && val != CURLFORM_BUFFERPTR) { PyErr_SetString(PyExc_TypeError, "unsupported option"); PyMem_Free(forms); curl_formfree(post); Py_XDECREF(ref_params); return NULL; } if (PyText_AsStringAndSize(PyTuple_GET_ITEM(t, j+1), &ostr, &olen, &oencoded_obj)) { /* exception should be already set */ PyMem_Free(forms); curl_formfree(post); Py_XDECREF(ref_params); return NULL; } forms[k].option = val; forms[k].value = ostr; ++k; if (val == CURLFORM_COPYCONTENTS) { /* Contents can contain \0 bytes so we specify the length */ forms[k].option = CURLFORM_CONTENTSLENGTH; forms[k].value = (const char *)olen; ++k; } else if (val == CURLFORM_BUFFERPTR) { PyObject *obj = PyTuple_GET_ITEM(t, j+1); ref_params = PyList_New((Py_ssize_t)0); if (ref_params == NULL) { PyText_EncodedDecref(oencoded_obj); PyMem_Free(forms); curl_formfree(post); return NULL; } /* Ensure that the buffer remains alive until curl_easy_cleanup() */ if (PyList_Append(ref_params, obj) != 0) { PyText_EncodedDecref(oencoded_obj); PyMem_Free(forms); curl_formfree(post); Py_DECREF(ref_params); return NULL; } /* As with CURLFORM_COPYCONTENTS, specify the length. */ forms[k].option = CURLFORM_BUFFERLENGTH; forms[k].value = (const char *)olen; ++k; } } forms[k].option = CURLFORM_END; res = curl_formadd(&post, &last, CURLFORM_COPYNAME, nstr, CURLFORM_NAMELENGTH, (long) nlen, CURLFORM_ARRAY, forms, CURLFORM_END); PyText_EncodedDecref(oencoded_obj); PyMem_Free(forms); if (res != CURLE_OK) { curl_formfree(post); Py_XDECREF(ref_params); CURLERROR_RETVAL(); } } else { /* Some other type was given, ignore */ PyText_EncodedDecref(nencoded_obj); curl_formfree(post); Py_XDECREF(ref_params); PyErr_SetString(PyExc_TypeError, "unsupported second type in tuple"); return NULL; } PyText_EncodedDecref(nencoded_obj); } res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, post); /* Check for errors */ if (res != CURLE_OK) { curl_formfree(post); Py_XDECREF(ref_params); CURLERROR_RETVAL(); } /* Finally, free previously allocated httppost, ZAP any * buffer references, and update */ curl_formfree(self->httppost); util_curl_xdecref(self, PYCURL_MEMGROUP_HTTPPOST, self->handle); self->httppost = post; /* The previous list of INCed references was ZAPed above; save * the new one so that we can clean it up on the next * self->httppost free. */ self->httppost_ref_list = ref_params; Py_RETURN_NONE; } /* Just to be sure we do not bug off here */ assert(old_slist != NULL && slist == NULL); /* Handle regular list operations on the other options */ for (i = 0; i < len; i++) { PyObject *listitem = PyList_GetItem(obj, i); struct curl_slist *nlist; char *str; PyObject *sencoded_obj; if (!PyText_Check(listitem)) { curl_slist_free_all(slist); PyErr_SetString(PyExc_TypeError, "list items must be byte strings or Unicode strings with ASCII code points only"); return NULL; } /* INFO: curl_slist_append() internally does strdup() the data, so * no embedded NUL characters allowed here. */ str = PyText_AsString_NoNUL(listitem, &sencoded_obj); if (str == NULL) { curl_slist_free_all(slist); return NULL; } nlist = curl_slist_append(slist, str); PyText_EncodedDecref(sencoded_obj); if (nlist == NULL || nlist->data == NULL) { curl_slist_free_all(slist); return PyErr_NoMemory(); } slist = nlist; } res = curl_easy_setopt(self->handle, (CURLoption)option, slist); /* Check for errors */ if (res != CURLE_OK) { curl_slist_free_all(slist); CURLERROR_RETVAL(); } /* Finally, free previously allocated list and update */ curl_slist_free_all(*old_slist); *old_slist = slist; Py_RETURN_NONE; } /* Handle the case of function objects for callbacks */ if (PyFunction_Check(obj) || PyCFunction_Check(obj) || PyCallable_Check(obj) || PyMethod_Check(obj)) { /* We use function types here to make sure that our callback * definitions exactly match the interface. */ const curl_write_callback w_cb = write_callback; const curl_write_callback h_cb = header_callback; const curl_read_callback r_cb = read_callback; const curl_progress_callback pro_cb = progress_callback; const curl_debug_callback debug_cb = debug_callback; const curl_ioctl_callback ioctl_cb = ioctl_callback; const curl_opensocket_callback opensocket_cb = opensocket_callback; const curl_seek_callback seek_cb = seek_callback; switch(option) { case CURLOPT_WRITEFUNCTION: if (self->writeheader_fp != NULL) { PyErr_SetString(ErrorObject, "cannot combine WRITEFUNCTION with WRITEHEADER option."); return NULL; } Py_INCREF(obj); Py_CLEAR(self->writedata_fp); Py_CLEAR(self->w_cb); self->w_cb = obj; curl_easy_setopt(self->handle, CURLOPT_WRITEFUNCTION, w_cb); curl_easy_setopt(self->handle, CURLOPT_WRITEDATA, self); break; case CURLOPT_HEADERFUNCTION: Py_INCREF(obj); Py_CLEAR(self->h_cb); self->h_cb = obj; curl_easy_setopt(self->handle, CURLOPT_HEADERFUNCTION, h_cb); curl_easy_setopt(self->handle, CURLOPT_WRITEHEADER, self); break; case CURLOPT_READFUNCTION: Py_INCREF(obj); Py_CLEAR(self->readdata_fp); Py_CLEAR(self->r_cb); self->r_cb = obj; curl_easy_setopt(self->handle, CURLOPT_READFUNCTION, r_cb); curl_easy_setopt(self->handle, CURLOPT_READDATA, self); break; case CURLOPT_PROGRESSFUNCTION: Py_INCREF(obj); Py_CLEAR(self->pro_cb); self->pro_cb = obj; curl_easy_setopt(self->handle, CURLOPT_PROGRESSFUNCTION, pro_cb); curl_easy_setopt(self->handle, CURLOPT_PROGRESSDATA, self); break; case CURLOPT_DEBUGFUNCTION: Py_INCREF(obj); Py_CLEAR(self->debug_cb); self->debug_cb = obj; curl_easy_setopt(self->handle, CURLOPT_DEBUGFUNCTION, debug_cb); curl_easy_setopt(self->handle, CURLOPT_DEBUGDATA, self); break; case CURLOPT_IOCTLFUNCTION: Py_INCREF(obj); Py_CLEAR(self->ioctl_cb); self->ioctl_cb = obj; curl_easy_setopt(self->handle, CURLOPT_IOCTLFUNCTION, ioctl_cb); curl_easy_setopt(self->handle, CURLOPT_IOCTLDATA, self); break; case CURLOPT_OPENSOCKETFUNCTION: Py_INCREF(obj); Py_CLEAR(self->opensocket_cb); self->opensocket_cb = obj; curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETFUNCTION, opensocket_cb); curl_easy_setopt(self->handle, CURLOPT_OPENSOCKETDATA, self); break; case CURLOPT_SEEKFUNCTION: Py_INCREF(obj); Py_CLEAR(self->seek_cb); self->seek_cb = obj; curl_easy_setopt(self->handle, CURLOPT_SEEKFUNCTION, seek_cb); curl_easy_setopt(self->handle, CURLOPT_SEEKDATA, self); break; default: /* None of the function options were recognized, raise exception */ PyErr_SetString(PyExc_TypeError, "functions are not supported for this option"); return NULL; } Py_RETURN_NONE; } /* handle the SHARE case */ if (option == CURLOPT_SHARE) { CurlShareObject *share; if (self->share == NULL && (obj == NULL || obj == Py_None)) Py_RETURN_NONE; if (self->share) { if (obj != Py_None) { PyErr_SetString(ErrorObject, "Curl object already sharing. Unshare first."); return NULL; } else { share = self->share; res = curl_easy_setopt(self->handle, CURLOPT_SHARE, NULL); if (res != CURLE_OK) { CURLERROR_RETVAL(); } self->share = NULL; Py_DECREF(share); Py_RETURN_NONE; } } if (Py_TYPE(obj) != p_CurlShare_Type) { PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); return NULL; } share = (CurlShareObject*)obj; res = curl_easy_setopt(self->handle, CURLOPT_SHARE, share->share_handle); if (res != CURLE_OK) { CURLERROR_RETVAL(); } self->share = share; Py_INCREF(share); Py_RETURN_NONE; } /* Handle the case of file-like objects for Python 3. Given an object with a write method, we will call the write method from the appropriate callback. Files in Python 3 are no longer FILE * instances and therefore cannot be directly given to curl. For consistency, ability to use any file-like object is also available on Python 2. */ if (option == CURLOPT_READDATA || option == CURLOPT_WRITEDATA || option == CURLOPT_WRITEHEADER) { PyObject *write_method = PyObject_GetAttrString(obj, "write"); if (write_method) { PyObject *arglist; PyObject *rv; switch (option) { case CURLOPT_READDATA: option = CURLOPT_READFUNCTION; break; case CURLOPT_WRITEDATA: option = CURLOPT_WRITEFUNCTION; break; case CURLOPT_WRITEHEADER: if (self->w_cb != NULL) { PyErr_SetString(ErrorObject, "cannot combine WRITEHEADER with WRITEFUNCTION."); Py_DECREF(write_method); return NULL; } option = CURLOPT_HEADERFUNCTION; break; default: PyErr_SetString(PyExc_TypeError, "objects are not supported for this option"); Py_DECREF(write_method); return NULL; } arglist = Py_BuildValue("(iO)", option, write_method); /* reference is now in arglist */ Py_DECREF(write_method); if (arglist == NULL) { return NULL; } rv = do_curl_setopt(self, arglist); Py_DECREF(arglist); return rv; } else { PyErr_SetString(ErrorObject, "object given without a write method"); return NULL; } } /* Failed to match any of the function signatures -- return error */ error: PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); return NULL; } static PyObject * do_curl_getinfo(CurlObject *self, PyObject *args) { int option; int res; if (!PyArg_ParseTuple(args, "i:getinfo", &option)) { return NULL; } if (check_curl_state(self, 1 | 2, "getinfo") != 0) { return NULL; } switch (option) { case CURLINFO_FILETIME: case CURLINFO_HEADER_SIZE: case CURLINFO_HTTP_CODE: case CURLINFO_REDIRECT_COUNT: case CURLINFO_REQUEST_SIZE: case CURLINFO_SSL_VERIFYRESULT: case CURLINFO_HTTP_CONNECTCODE: case CURLINFO_HTTPAUTH_AVAIL: case CURLINFO_PROXYAUTH_AVAIL: case CURLINFO_OS_ERRNO: case CURLINFO_NUM_CONNECTS: case CURLINFO_LASTSOCKET: #ifdef HAVE_CURLINFO_LOCAL_PORT case CURLINFO_LOCAL_PORT: #endif #ifdef HAVE_CURLINFO_PRIMARY_PORT case CURLINFO_PRIMARY_PORT: #endif { /* Return PyInt as result */ long l_res = -1; res = curl_easy_getinfo(self->handle, (CURLINFO)option, &l_res); /* Check for errors and return result */ if (res != CURLE_OK) { CURLERROR_RETVAL(); } return PyInt_FromLong(l_res); } case CURLINFO_CONTENT_TYPE: case CURLINFO_EFFECTIVE_URL: case CURLINFO_FTP_ENTRY_PATH: case CURLINFO_REDIRECT_URL: case CURLINFO_PRIMARY_IP: #ifdef HAVE_CURLINFO_LOCAL_IP case CURLINFO_LOCAL_IP: #endif { /* Return PyString as result */ char *s_res = NULL; res = curl_easy_getinfo(self->handle, (CURLINFO)option, &s_res); if (res != CURLE_OK) { CURLERROR_RETVAL(); } /* If the resulting string is NULL, return None */ if (s_res == NULL) { Py_RETURN_NONE; } return PyText_FromString(s_res); } case CURLINFO_CONNECT_TIME: case CURLINFO_APPCONNECT_TIME: case CURLINFO_CONTENT_LENGTH_DOWNLOAD: case CURLINFO_CONTENT_LENGTH_UPLOAD: case CURLINFO_NAMELOOKUP_TIME: case CURLINFO_PRETRANSFER_TIME: case CURLINFO_REDIRECT_TIME: case CURLINFO_SIZE_DOWNLOAD: case CURLINFO_SIZE_UPLOAD: case CURLINFO_SPEED_DOWNLOAD: case CURLINFO_SPEED_UPLOAD: case CURLINFO_STARTTRANSFER_TIME: case CURLINFO_TOTAL_TIME: { /* Return PyFloat as result */ double d_res = 0.0; res = curl_easy_getinfo(self->handle, (CURLINFO)option, &d_res); if (res != CURLE_OK) { CURLERROR_RETVAL(); } return PyFloat_FromDouble(d_res); } case CURLINFO_SSL_ENGINES: case CURLINFO_COOKIELIST: { /* Return a list of strings */ struct curl_slist *slist = NULL; res = curl_easy_getinfo(self->handle, (CURLINFO)option, &slist); if (res != CURLE_OK) { CURLERROR_RETVAL(); } return convert_slist(slist, 1 | 2); } #ifdef HAVE_CURLOPT_CERTINFO case CURLINFO_CERTINFO: { /* Return a list of lists of 2-tuples */ struct curl_certinfo *clist = NULL; res = curl_easy_getinfo(self->handle, CURLINFO_CERTINFO, &clist); if (res != CURLE_OK) { CURLERROR_RETVAL(); } else { return convert_certinfo(clist); } } #endif } /* Got wrong option on the method call */ PyErr_SetString(PyExc_ValueError, "invalid argument to getinfo"); return NULL; } /* curl_easy_pause() can be called from inside a callback or outside */ static PyObject * do_curl_pause(CurlObject *self, PyObject *args) { int bitmask; CURLcode res; #ifdef WITH_THREAD PyThreadState *saved_state; #endif if (!PyArg_ParseTuple(args, "i:pause", &bitmask)) { return NULL; } if (check_curl_state(self, 1, "pause") != 0) { return NULL; } #ifdef WITH_THREAD /* Save handle to current thread (used as context for python callbacks) */ saved_state = self->state; PYCURL_BEGIN_ALLOW_THREADS /* We must allow threads here because unpausing a handle can cause some of its callbacks to be invoked immediately, from inside curl_easy_pause() */ #endif res = curl_easy_pause(self->handle, bitmask); #ifdef WITH_THREAD PYCURL_END_ALLOW_THREADS /* Restore the thread-state to whatever it was on entry */ self->state = saved_state; #endif if (res != CURLE_OK) { CURLERROR_MSG("pause/unpause failed"); } else { Py_INCREF(Py_None); return Py_None; } } static const char co_pause_doc [] = "pause(bitmask) -> None. " "Pauses or unpauses a curl handle. Bitmask should be a value such as PAUSE_RECV or PAUSE_CONT. " "Raises pycurl.error exception upon failure.\n"; /************************************************************************* // CurlMultiObject **************************************************************************/ /* --------------- construct/destruct (i.e. open/close) --------------- */ /* constructor - this is a module-level function returning a new instance */ static CurlMultiObject * do_multi_new(PyObject *dummy) { CurlMultiObject *self; UNUSED(dummy); /* Allocate python curl-multi object */ self = (CurlMultiObject *) PyObject_GC_New(CurlMultiObject, p_CurlMulti_Type); if (self) { PyObject_GC_Track(self); } else { return NULL; } /* Initialize object attributes */ self->dict = NULL; #ifdef WITH_THREAD self->state = NULL; #endif self->t_cb = NULL; self->s_cb = NULL; /* Allocate libcurl multi handle */ self->multi_handle = curl_multi_init(); if (self->multi_handle == NULL) { Py_DECREF(self); PyErr_SetString(ErrorObject, "initializing curl-multi failed"); return NULL; } return self; } static void util_multi_close(CurlMultiObject *self) { assert(self != NULL); #ifdef WITH_THREAD self->state = NULL; #endif if (self->multi_handle != NULL) { CURLM *multi_handle = self->multi_handle; self->multi_handle = NULL; curl_multi_cleanup(multi_handle); } } static void do_multi_dealloc(CurlMultiObject *self) { PyObject_GC_UnTrack(self); Py_TRASHCAN_SAFE_BEGIN(self) Py_CLEAR(self->dict); util_multi_close(self); PyObject_GC_Del(self); Py_TRASHCAN_SAFE_END(self) } static PyObject * do_multi_close(CurlMultiObject *self) { if (check_multi_state(self, 2, "close") != 0) { return NULL; } util_multi_close(self); Py_RETURN_NONE; } /* --------------- GC support --------------- */ /* Drop references that may have created reference cycles. */ static int do_multi_clear(CurlMultiObject *self) { Py_CLEAR(self->dict); return 0; } static int do_multi_traverse(CurlMultiObject *self, visitproc visit, void *arg) { int err; #undef VISIT #define VISIT(v) if ((v) != NULL && ((err = visit(v, arg)) != 0)) return err VISIT(self->dict); return 0; #undef VISIT } /* --------------- setopt --------------- */ static int multi_socket_callback(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp) { CurlMultiObject *self; PyObject *arglist; PyObject *result = NULL; PYCURL_DECLARE_THREAD_STATE; /* acquire thread */ self = (CurlMultiObject *)userp; if (!PYCURL_ACQUIRE_THREAD_MULTI()) return 0; /* check args */ if (self->s_cb == NULL) goto silent_error; if (socketp == NULL) { Py_INCREF(Py_None); socketp = Py_None; } /* run callback */ arglist = Py_BuildValue("(iiOO)", what, s, userp, (PyObject *)socketp); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->s_cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* return values from socket callbacks should be ignored */ silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return 0; verbose_error: PyErr_Print(); goto silent_error; return 0; } static int multi_timer_callback(CURLM *multi, long timeout_ms, void *userp) { CurlMultiObject *self; PyObject *arglist; PyObject *result = NULL; int ret = 0; /* always success */ PYCURL_DECLARE_THREAD_STATE; UNUSED(multi); /* acquire thread */ self = (CurlMultiObject *)userp; if (!PYCURL_ACQUIRE_THREAD_MULTI()) return ret; /* check args */ if (self->t_cb == NULL) goto silent_error; /* run callback */ arglist = Py_BuildValue("(i)", timeout_ms); if (arglist == NULL) goto verbose_error; result = PyEval_CallObject(self->t_cb, arglist); Py_DECREF(arglist); if (result == NULL) goto verbose_error; /* return values from timer callbacks should be ignored */ silent_error: Py_XDECREF(result); PYCURL_RELEASE_THREAD(); return ret; verbose_error: PyErr_Print(); goto silent_error; return 0; } static PyObject * do_multi_setopt(CurlMultiObject *self, PyObject *args) { int option; PyObject *obj; if (!PyArg_ParseTuple(args, "iO:setopt", &option, &obj)) return NULL; if (check_multi_state(self, 1 | 2, "setopt") != 0) return NULL; /* Early checks of option value */ if (option <= 0) goto error; if (option >= (int)CURLOPTTYPE_OFF_T + MOPTIONS_SIZE) goto error; if (option % 10000 >= MOPTIONS_SIZE) goto error; /* Handle the case of integer arguments */ if (PyInt_Check(obj)) { long d = PyInt_AsLong(obj); switch(option) { case CURLMOPT_PIPELINING: curl_multi_setopt(self->multi_handle, option, d); break; case CURLMOPT_MAXCONNECTS: curl_multi_setopt(self->multi_handle, option, d); break; default: PyErr_SetString(PyExc_TypeError, "integers are not supported for this option"); return NULL; } Py_RETURN_NONE; } if (PyFunction_Check(obj) || PyCFunction_Check(obj) || PyCallable_Check(obj) || PyMethod_Check(obj)) { /* We use function types here to make sure that our callback * definitions exactly match the interface. */ const curl_multi_timer_callback t_cb = multi_timer_callback; const curl_socket_callback s_cb = multi_socket_callback; switch(option) { case CURLMOPT_SOCKETFUNCTION: curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETFUNCTION, s_cb); curl_multi_setopt(self->multi_handle, CURLMOPT_SOCKETDATA, self); Py_INCREF(obj); self->s_cb = obj; break; case CURLMOPT_TIMERFUNCTION: curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERFUNCTION, t_cb); curl_multi_setopt(self->multi_handle, CURLMOPT_TIMERDATA, self); Py_INCREF(obj); self->t_cb = obj; break; default: PyErr_SetString(PyExc_TypeError, "callables are not supported for this option"); return NULL; } Py_RETURN_NONE; } /* Failed to match any of the function signatures -- return error */ error: PyErr_SetString(PyExc_TypeError, "invalid arguments to setopt"); return NULL; } /* --------------- timeout --------------- */ static PyObject * do_multi_timeout(CurlMultiObject *self) { CURLMcode res; long timeout; if (check_multi_state(self, 1 | 2, "timeout") != 0) { return NULL; } res = curl_multi_timeout(self->multi_handle, &timeout); if (res != CURLM_OK) { CURLERROR_MSG("timeout failed"); } /* Return number of millisecs until timeout */ return Py_BuildValue("l", timeout); } /* --------------- assign --------------- */ static PyObject * do_multi_assign(CurlMultiObject *self, PyObject *args) { CURLMcode res; curl_socket_t socket; PyObject *obj; if (!PyArg_ParseTuple(args, "iO:assign", &socket, &obj)) return NULL; if (check_multi_state(self, 1 | 2, "assign") != 0) { return NULL; } Py_INCREF(obj); res = curl_multi_assign(self->multi_handle, socket, obj); if (res != CURLM_OK) { CURLERROR_MSG("assign failed"); } Py_RETURN_NONE; } /* --------------- socket_action --------------- */ static PyObject * do_multi_socket_action(CurlMultiObject *self, PyObject *args) { CURLMcode res; curl_socket_t socket; int ev_bitmask; int running = -1; if (!PyArg_ParseTuple(args, "ii:socket_action", &socket, &ev_bitmask)) return NULL; if (check_multi_state(self, 1 | 2, "socket_action") != 0) { return NULL; } PYCURL_BEGIN_ALLOW_THREADS res = curl_multi_socket_action(self->multi_handle, socket, ev_bitmask, &running); PYCURL_END_ALLOW_THREADS if (res != CURLM_OK) { CURLERROR_MSG("multi_socket_action failed"); } /* Return a tuple with the result and the number of running handles */ return Py_BuildValue("(ii)", (int)res, running); } /* --------------- socket_all --------------- */ static PyObject * do_multi_socket_all(CurlMultiObject *self) { CURLMcode res; int running = -1; if (check_multi_state(self, 1 | 2, "socket_all") != 0) { return NULL; } PYCURL_BEGIN_ALLOW_THREADS res = curl_multi_socket_all(self->multi_handle, &running); PYCURL_END_ALLOW_THREADS /* We assume these errors are ok, otherwise raise exception */ if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { CURLERROR_MSG("perform failed"); } /* Return a tuple with the result and the number of running handles */ return Py_BuildValue("(ii)", (int)res, running); } /* --------------- perform --------------- */ static PyObject * do_multi_perform(CurlMultiObject *self) { CURLMcode res; int running = -1; if (check_multi_state(self, 1 | 2, "perform") != 0) { return NULL; } PYCURL_BEGIN_ALLOW_THREADS res = curl_multi_perform(self->multi_handle, &running); PYCURL_END_ALLOW_THREADS /* We assume these errors are ok, otherwise raise exception */ if (res != CURLM_OK && res != CURLM_CALL_MULTI_PERFORM) { CURLERROR_MSG("perform failed"); } /* Return a tuple with the result and the number of running handles */ return Py_BuildValue("(ii)", (int)res, running); } /* --------------- add_handle/remove_handle --------------- */ /* static utility function */ static int check_multi_add_remove(const CurlMultiObject *self, const CurlObject *obj) { /* check CurlMultiObject status */ assert_multi_state(self); if (self->multi_handle == NULL) { PyErr_SetString(ErrorObject, "cannot add/remove handle - multi-stack is closed"); return -1; } #ifdef WITH_THREAD if (self->state != NULL) { PyErr_SetString(ErrorObject, "cannot add/remove handle - multi_perform() already running"); return -1; } #endif /* check CurlObject status */ assert_curl_state(obj); #ifdef WITH_THREAD if (obj->state != NULL) { PyErr_SetString(ErrorObject, "cannot add/remove handle - perform() of curl object already running"); return -1; } #endif if (obj->multi_stack != NULL && obj->multi_stack != self) { PyErr_SetString(ErrorObject, "cannot add/remove handle - curl object already on another multi-stack"); return -1; } return 0; } static PyObject * do_multi_add_handle(CurlMultiObject *self, PyObject *args) { CurlObject *obj; CURLMcode res; if (!PyArg_ParseTuple(args, "O!:add_handle", p_Curl_Type, &obj)) { return NULL; } if (check_multi_add_remove(self, obj) != 0) { return NULL; } if (obj->handle == NULL) { PyErr_SetString(ErrorObject, "curl object already closed"); return NULL; } if (obj->multi_stack == self) { PyErr_SetString(ErrorObject, "curl object already on this multi-stack"); return NULL; } assert(obj->multi_stack == NULL); res = curl_multi_add_handle(self->multi_handle, obj->handle); if (res != CURLM_OK) { CURLERROR_MSG("curl_multi_add_handle() failed due to internal errors"); } obj->multi_stack = self; Py_INCREF(self); Py_RETURN_NONE; } static PyObject * do_multi_remove_handle(CurlMultiObject *self, PyObject *args) { CurlObject *obj; CURLMcode res; if (!PyArg_ParseTuple(args, "O!:remove_handle", p_Curl_Type, &obj)) { return NULL; } if (check_multi_add_remove(self, obj) != 0) { return NULL; } if (obj->handle == NULL) { /* CurlObject handle already closed -- ignore */ goto done; } if (obj->multi_stack != self) { PyErr_SetString(ErrorObject, "curl object not on this multi-stack"); return NULL; } res = curl_multi_remove_handle(self->multi_handle, obj->handle); if (res != CURLM_OK) { CURLERROR_MSG("curl_multi_remove_handle() failed due to internal errors"); } assert(obj->multi_stack == self); obj->multi_stack = NULL; Py_DECREF(self); done: Py_RETURN_NONE; } /* --------------- fdset ---------------------- */ static PyObject * do_multi_fdset(CurlMultiObject *self) { CURLMcode res; int max_fd = -1, fd; PyObject *ret = NULL; PyObject *read_list = NULL, *write_list = NULL, *except_list = NULL; PyObject *py_fd = NULL; if (check_multi_state(self, 1 | 2, "fdset") != 0) { return NULL; } /* Clear file descriptor sets */ FD_ZERO(&self->read_fd_set); FD_ZERO(&self->write_fd_set); FD_ZERO(&self->exc_fd_set); /* Don't bother releasing the gil as this is just a data structure operation */ res = curl_multi_fdset(self->multi_handle, &self->read_fd_set, &self->write_fd_set, &self->exc_fd_set, &max_fd); if (res != CURLM_OK) { CURLERROR_MSG("curl_multi_fdset() failed due to internal errors"); } /* Allocate lists. */ if ((read_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; if ((write_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; if ((except_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; /* Populate lists */ for (fd = 0; fd < max_fd + 1; fd++) { if (FD_ISSET(fd, &self->read_fd_set)) { if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; if (PyList_Append(read_list, py_fd) != 0) goto error; Py_DECREF(py_fd); py_fd = NULL; } if (FD_ISSET(fd, &self->write_fd_set)) { if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; if (PyList_Append(write_list, py_fd) != 0) goto error; Py_DECREF(py_fd); py_fd = NULL; } if (FD_ISSET(fd, &self->exc_fd_set)) { if ((py_fd = PyInt_FromLong((long)fd)) == NULL) goto error; if (PyList_Append(except_list, py_fd) != 0) goto error; Py_DECREF(py_fd); py_fd = NULL; } } /* Return a tuple with the 3 lists */ ret = Py_BuildValue("(OOO)", read_list, write_list, except_list); error: Py_XDECREF(py_fd); Py_XDECREF(except_list); Py_XDECREF(write_list); Py_XDECREF(read_list); return ret; } /* --------------- info_read --------------- */ static PyObject * do_multi_info_read(CurlMultiObject *self, PyObject *args) { PyObject *ret = NULL; PyObject *ok_list = NULL, *err_list = NULL; CURLMsg *msg; int in_queue = 0, num_results = INT_MAX; /* Sanity checks */ if (!PyArg_ParseTuple(args, "|i:info_read", &num_results)) { return NULL; } if (num_results <= 0) { PyErr_SetString(ErrorObject, "argument to info_read must be greater than zero"); return NULL; } if (check_multi_state(self, 1 | 2, "info_read") != 0) { return NULL; } if ((ok_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; if ((err_list = PyList_New((Py_ssize_t)0)) == NULL) goto error; /* Loop through all messages */ while ((msg = curl_multi_info_read(self->multi_handle, &in_queue)) != NULL) { CURLcode res; CurlObject *co = NULL; /* Check for termination as specified by the user */ if (num_results-- <= 0) { break; } /* Fetch the curl object that corresponds to the curl handle in the message */ res = curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &co); if (res != CURLE_OK || co == NULL) { Py_DECREF(err_list); Py_DECREF(ok_list); CURLERROR_MSG("Unable to fetch curl handle from curl object"); } assert(Py_TYPE(co) == p_Curl_Type); if (msg->msg != CURLMSG_DONE) { /* FIXME: what does this mean ??? */ } if (msg->data.result == CURLE_OK) { /* Append curl object to list of objects which succeeded */ if (PyList_Append(ok_list, (PyObject *)co) != 0) { goto error; } } else { /* Create a result tuple that will get added to err_list. */ PyObject *v = Py_BuildValue("(Ois)", (PyObject *)co, (int)msg->data.result, co->error); /* Append curl object to list of objects which failed */ if (v == NULL || PyList_Append(err_list, v) != 0) { Py_XDECREF(v); goto error; } Py_DECREF(v); } } /* Return (number of queued messages, [ok_objects], [error_objects]) */ ret = Py_BuildValue("(iOO)", in_queue, ok_list, err_list); error: Py_XDECREF(err_list); Py_XDECREF(ok_list); return ret; } /* --------------- select --------------- */ static PyObject * do_multi_select(CurlMultiObject *self, PyObject *args) { int max_fd = -1, n; double timeout = -1.0; struct timeval tv, *tvp; CURLMcode res; if (!PyArg_ParseTuple(args, "d:select", &timeout)) { return NULL; } if (check_multi_state(self, 1 | 2, "select") != 0) { return NULL; } if (timeout < 0 || timeout >= 365 * 24 * 60 * 60) { PyErr_SetString(PyExc_OverflowError, "invalid timeout period"); return NULL; } else { long seconds = (long)timeout; timeout = timeout - (double)seconds; assert(timeout >= 0.0); assert(timeout < 1.0); tv.tv_sec = seconds; tv.tv_usec = (long)(timeout*1000000.0); tvp = &tv; } FD_ZERO(&self->read_fd_set); FD_ZERO(&self->write_fd_set); FD_ZERO(&self->exc_fd_set); res = curl_multi_fdset(self->multi_handle, &self->read_fd_set, &self->write_fd_set, &self->exc_fd_set, &max_fd); if (res != CURLM_OK) { CURLERROR_MSG("multi_fdset failed"); } if (max_fd < 0) { n = 0; } else { Py_BEGIN_ALLOW_THREADS n = select(max_fd + 1, &self->read_fd_set, &self->write_fd_set, &self->exc_fd_set, tvp); Py_END_ALLOW_THREADS /* info: like Python's socketmodule.c we do not raise an exception * if select() fails - we'll leave it to the actual libcurl * socket code to report any errors. */ } return PyInt_FromLong(n); } /************************************************************************* // type definitions **************************************************************************/ /* --------------- methods --------------- */ static const char cso_close_doc [] = "close() -> None. " "Close shared handle.\n"; static const char cso_setopt_doc [] = "setopt(option, parameter) -> None. " "Set curl share option. Raises pycurl.error exception upon failure.\n"; static const char co_close_doc [] = "close() -> None. " "Close handle and end curl session.\n"; static const char co_errstr_doc [] = "errstr() -> String. " "Return the internal libcurl error buffer string.\n"; static const char co_getinfo_doc [] = "getinfo(info) -> Res. " "Extract and return information from a curl session. Raises pycurl.error exception upon failure.\n"; static const char co_perform_doc [] = "perform() -> None. " "Perform a file transfer. Raises pycurl.error exception upon failure.\n"; static const char co_setopt_doc [] = "setopt(option, parameter) -> None. " "Set curl session option. Raises pycurl.error exception upon failure.\n"; static const char co_unsetopt_doc [] = "unsetopt(option) -> None. " "Reset curl session option to default value. Raises pycurl.error exception upon failure.\n"; static const char co_reset_doc [] = "reset() -> None. " "Reset all options set on curl handle to default values, but preserves live connections, session ID cache, DNS cache, cookies, and shares.\n"; static const char co_multi_fdset_doc [] = "fdset() -> Tuple. " "Returns a tuple of three lists that can be passed to the select.select() method .\n"; static const char co_multi_info_read_doc [] = "info_read([max_objects]) -> Tuple. " "Returns a tuple (number of queued handles, [curl objects]).\n"; static const char co_multi_select_doc [] = "select([timeout]) -> Int. " "Returns result from doing a select() on the curl multi file descriptor with the given timeout.\n"; static const char co_multi_socket_action_doc [] = "socket_action(sockfd, ev_bitmask) -> Tuple. " "Returns result from doing a socket_action() on the curl multi file descriptor with the given timeout.\n"; static const char co_multi_socket_all_doc [] = "socket_all() -> Tuple. " "Returns result from doing a socket_all() on the curl multi file descriptor with the given timeout.\n"; static PyMethodDef curlshareobject_methods[] = { {"close", (PyCFunction)do_share_close, METH_NOARGS, cso_close_doc}, {"setopt", (PyCFunction)do_curlshare_setopt, METH_VARARGS, cso_setopt_doc}, {NULL, NULL, 0, 0} }; static PyMethodDef curlobject_methods[] = { {"close", (PyCFunction)do_curl_close, METH_NOARGS, co_close_doc}, {"errstr", (PyCFunction)do_curl_errstr, METH_NOARGS, co_errstr_doc}, {"getinfo", (PyCFunction)do_curl_getinfo, METH_VARARGS, co_getinfo_doc}, {"pause", (PyCFunction)do_curl_pause, METH_VARARGS, co_pause_doc}, {"perform", (PyCFunction)do_curl_perform, METH_NOARGS, co_perform_doc}, {"setopt", (PyCFunction)do_curl_setopt, METH_VARARGS, co_setopt_doc}, {"unsetopt", (PyCFunction)do_curl_unsetopt, METH_VARARGS, co_unsetopt_doc}, {"reset", (PyCFunction)do_curl_reset, METH_NOARGS, co_reset_doc}, {NULL, NULL, 0, NULL} }; static PyMethodDef curlmultiobject_methods[] = { {"add_handle", (PyCFunction)do_multi_add_handle, METH_VARARGS, NULL}, {"close", (PyCFunction)do_multi_close, METH_NOARGS, NULL}, {"fdset", (PyCFunction)do_multi_fdset, METH_NOARGS, co_multi_fdset_doc}, {"info_read", (PyCFunction)do_multi_info_read, METH_VARARGS, co_multi_info_read_doc}, {"perform", (PyCFunction)do_multi_perform, METH_NOARGS, NULL}, {"socket_action", (PyCFunction)do_multi_socket_action, METH_VARARGS, co_multi_socket_action_doc}, {"socket_all", (PyCFunction)do_multi_socket_all, METH_NOARGS, co_multi_socket_all_doc}, {"setopt", (PyCFunction)do_multi_setopt, METH_VARARGS, NULL}, {"timeout", (PyCFunction)do_multi_timeout, METH_NOARGS, NULL}, {"assign", (PyCFunction)do_multi_assign, METH_VARARGS, NULL}, {"remove_handle", (PyCFunction)do_multi_remove_handle, METH_VARARGS, NULL}, {"select", (PyCFunction)do_multi_select, METH_VARARGS, co_multi_select_doc}, {NULL, NULL, 0, NULL} }; /* --------------- setattr/getattr --------------- */ static PyObject *curlobject_constants = NULL; static PyObject *curlmultiobject_constants = NULL; static PyObject *curlshareobject_constants = NULL; #if PY_MAJOR_VERSION >= 3 static PyObject * my_getattro(PyObject *co, PyObject *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m) { PyObject *v = NULL; if( dict1 != NULL ) v = PyDict_GetItem(dict1, name); if( v == NULL && dict2 != NULL ) v = PyDict_GetItem(dict2, name); if( v != NULL ) { Py_INCREF(v); return v; } PyErr_SetString(PyExc_AttributeError, "trying to obtain a non-existing attribute"); return NULL; } static int my_setattro(PyObject **dict, PyObject *name, PyObject *v) { if( *dict == NULL ) { *dict = PyDict_New(); if( *dict == NULL ) return -1; } if (v != NULL) return PyDict_SetItem(*dict, name, v); else { int v = PyDict_DelItem(*dict, name); if (v != 0) { /* need to convert KeyError to AttributeError */ if (PyErr_ExceptionMatches(PyExc_KeyError)) { PyErr_SetString(PyExc_AttributeError, "trying to delete a non-existing attribute"); } } return v; } } PyObject *do_curl_getattro(PyObject *o, PyObject *n) { PyObject *v = PyObject_GenericGetAttr(o, n); if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) { PyErr_Clear(); v = my_getattro(o, n, ((CurlObject *)o)->dict, curlobject_constants, curlobject_methods); } return v; } static int do_curl_setattro(PyObject *o, PyObject *name, PyObject *v) { assert_curl_state((CurlObject *)o); return my_setattro(&((CurlObject *)o)->dict, name, v); } static PyObject * do_multi_getattro(PyObject *o, PyObject *n) { PyObject *v; assert_multi_state((CurlMultiObject *)o); v = PyObject_GenericGetAttr(o, n); if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) { PyErr_Clear(); v = my_getattro(o, n, ((CurlMultiObject *)o)->dict, curlmultiobject_constants, curlmultiobject_methods); } return v; } static int do_multi_setattro(PyObject *o, PyObject *n, PyObject *v) { assert_multi_state((CurlMultiObject *)o); return my_setattro(&((CurlMultiObject *)o)->dict, n, v); } static PyObject * do_share_getattro(PyObject *o, PyObject *n) { PyObject *v; assert_share_state((CurlShareObject *)o); v = PyObject_GenericGetAttr(o, n); if( !v && PyErr_ExceptionMatches(PyExc_AttributeError) ) { PyErr_Clear(); v = my_getattro(o, n, ((CurlShareObject *)o)->dict, curlshareobject_constants, curlshareobject_methods); } return v; } static int do_share_setattro(PyObject *o, PyObject *n, PyObject *v) { assert_share_state((CurlShareObject *)o); return my_setattro(&((CurlShareObject *)o)->dict, n, v); } #else static int my_setattr(PyObject **dict, char *name, PyObject *v) { if (v == NULL) { int rv = -1; if (*dict != NULL) rv = PyDict_DelItemString(*dict, name); if (rv < 0) PyErr_SetString(PyExc_AttributeError, "delete non-existing attribute"); return rv; } if (*dict == NULL) { *dict = PyDict_New(); if (*dict == NULL) return -1; } return PyDict_SetItemString(*dict, name, v); } static PyObject * my_getattr(PyObject *co, char *name, PyObject *dict1, PyObject *dict2, PyMethodDef *m) { PyObject *v = NULL; if (v == NULL && dict1 != NULL) v = PyDict_GetItemString(dict1, name); if (v == NULL && dict2 != NULL) v = PyDict_GetItemString(dict2, name); if (v != NULL) { Py_INCREF(v); return v; } return Py_FindMethod(m, co, name); } static int do_share_setattr(CurlShareObject *so, char *name, PyObject *v) { assert_share_state(so); return my_setattr(&so->dict, name, v); } static int do_curl_setattr(CurlObject *co, char *name, PyObject *v) { assert_curl_state(co); return my_setattr(&co->dict, name, v); } static int do_multi_setattr(CurlMultiObject *co, char *name, PyObject *v) { assert_multi_state(co); return my_setattr(&co->dict, name, v); } static PyObject * do_share_getattr(CurlShareObject *cso, char *name) { assert_share_state(cso); return my_getattr((PyObject *)cso, name, cso->dict, curlshareobject_constants, curlshareobject_methods); } static PyObject * do_curl_getattr(CurlObject *co, char *name) { assert_curl_state(co); return my_getattr((PyObject *)co, name, co->dict, curlobject_constants, curlobject_methods); } static PyObject * do_multi_getattr(CurlMultiObject *co, char *name) { assert_multi_state(co); return my_getattr((PyObject *)co, name, co->dict, curlmultiobject_constants, curlmultiobject_methods); } #endif /* --------------- actual type definitions --------------- */ #if PY_MAJOR_VERSION >= 3 static PyTypeObject CurlShare_Type = { PyVarObject_HEAD_INIT(NULL, 0) "pycurl.CurlShare", /* tp_name */ sizeof(CurlShareObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)do_share_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ (getattrofunc)do_share_getattro, /* tp_getattro */ (setattrofunc)do_share_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)do_share_traverse, /* tp_traverse */ (inquiry)do_share_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ curlshareobject_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; #else static PyTypeObject CurlShare_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pycurl.CurlShare", /* tp_name */ sizeof(CurlMultiObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* Methods */ (destructor)do_share_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)do_share_getattr, /* tp_getattr */ (setattrfunc)do_share_setattr, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)do_share_traverse, /* tp_traverse */ (inquiry)do_share_clear /* tp_clear */ /* More fields follow here, depending on your Python version. You can * safely ignore any compiler warnings about missing initializers. */ }; #endif #if PY_MAJOR_VERSION >= 3 static PyTypeObject Curl_Type = { PyVarObject_HEAD_INIT(NULL, 0) "pycurl.Curl", /* tp_name */ sizeof(CurlObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)do_curl_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ (getattrofunc)do_curl_getattro, /* tp_getattro */ (setattrofunc)do_curl_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)do_curl_traverse, /* tp_traverse */ (inquiry)do_curl_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ curlobject_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; #else static PyTypeObject Curl_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pycurl.Curl", /* tp_name */ sizeof(CurlObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* Methods */ (destructor)do_curl_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)do_curl_getattr, /* tp_getattr */ (setattrfunc)do_curl_setattr, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)do_curl_traverse, /* tp_traverse */ (inquiry)do_curl_clear /* tp_clear */ /* More fields follow here, depending on your Python version. You can * safely ignore any compiler warnings about missing initializers. */ }; #endif #if PY_MAJOR_VERSION >= 3 static PyTypeObject CurlMulti_Type = { PyVarObject_HEAD_INIT(NULL, 0) "pycurl.CurlMulti", /* tp_name */ sizeof(CurlMultiObject), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)do_multi_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, // (getattrfunc)do_curl_getattr, /* tp_getattr */ 0, //(setattrfunc)do_curl_setattr, /* tp_setattr */ 0, /* tp_reserved */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ (getattrofunc)do_multi_getattro, //0, /* tp_getattro */ (setattrofunc)do_multi_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)do_multi_traverse, /* tp_traverse */ (inquiry)do_multi_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ curlmultiobject_methods, /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ 0, /* tp_init */ 0, /* tp_alloc */ 0, /* tp_new */ }; #else static PyTypeObject CurlMulti_Type = { PyObject_HEAD_INIT(NULL) 0, /* ob_size */ "pycurl.CurlMulti", /* tp_name */ sizeof(CurlMultiObject), /* tp_basicsize */ 0, /* tp_itemsize */ /* Methods */ (destructor)do_multi_dealloc, /* tp_dealloc */ 0, /* tp_print */ (getattrfunc)do_multi_getattr, /* tp_getattr */ (setattrfunc)do_multi_setattr, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_HAVE_GC, /* tp_flags */ 0, /* tp_doc */ (traverseproc)do_multi_traverse, /* tp_traverse */ (inquiry)do_multi_clear /* tp_clear */ /* More fields follow here, depending on your Python version. You can * safely ignore any compiler warnings about missing initializers. */ }; #endif static int are_global_init_flags_valid(int flags) { #ifdef CURL_GLOBAL_ACK_EINTR /* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */ return !(flags & ~(CURL_GLOBAL_ALL | CURL_GLOBAL_ACK_EINTR)); #else return !(flags & ~(CURL_GLOBAL_ALL)); #endif } /************************************************************************* // module level // Note that the object constructors (do_curl_new, do_multi_new) // are module-level functions as well. **************************************************************************/ static PyObject * do_global_init(PyObject *dummy, PyObject *args) { int res, option; UNUSED(dummy); if (!PyArg_ParseTuple(args, "i:global_init", &option)) { return NULL; } if (!are_global_init_flags_valid(option)) { PyErr_SetString(PyExc_ValueError, "invalid option to global_init"); return NULL; } res = curl_global_init(option); if (res != CURLE_OK) { PyErr_SetString(ErrorObject, "unable to set global option"); return NULL; } Py_RETURN_NONE; } static PyObject * do_global_cleanup(PyObject *dummy) { UNUSED(dummy); curl_global_cleanup(); #ifdef PYCURL_NEED_SSL_TSL pycurl_ssl_cleanup(); #endif Py_RETURN_NONE; } static PyObject *vi_str(const char *s) { if (s == NULL) Py_RETURN_NONE; while (*s == ' ' || *s == '\t') s++; return PyText_FromString(s); } static PyObject * do_version_info(PyObject *dummy, PyObject *args) { const curl_version_info_data *vi; PyObject *ret = NULL; PyObject *protocols = NULL; PyObject *tmp; Py_ssize_t i; int stamp = CURLVERSION_NOW; UNUSED(dummy); if (!PyArg_ParseTuple(args, "|i:version_info", &stamp)) { return NULL; } vi = curl_version_info((CURLversion) stamp); if (vi == NULL) { PyErr_SetString(ErrorObject, "unable to get version info"); return NULL; } /* INFO: actually libcurl in lib/version.c does ignore * the "stamp" parameter, and so do we. */ for (i = 0; vi->protocols[i] != NULL; ) i++; protocols = PyTuple_New(i); if (protocols == NULL) goto error; for (i = 0; vi->protocols[i] != NULL; i++) { tmp = vi_str(vi->protocols[i]); if (tmp == NULL) goto error; PyTuple_SET_ITEM(protocols, i, tmp); } ret = PyTuple_New((Py_ssize_t)12); if (ret == NULL) goto error; #define SET(i, v) \ tmp = (v); if (tmp == NULL) goto error; PyTuple_SET_ITEM(ret, i, tmp) SET(0, PyInt_FromLong((long) vi->age)); SET(1, vi_str(vi->version)); SET(2, PyInt_FromLong(vi->version_num)); SET(3, vi_str(vi->host)); SET(4, PyInt_FromLong(vi->features)); SET(5, vi_str(vi->ssl_version)); SET(6, PyInt_FromLong(vi->ssl_version_num)); SET(7, vi_str(vi->libz_version)); SET(8, protocols); SET(9, vi_str(vi->ares)); SET(10, PyInt_FromLong(vi->ares_num)); SET(11, vi_str(vi->libidn)); #undef SET return ret; error: Py_XDECREF(ret); Py_XDECREF(protocols); return NULL; } /* Per function docstrings */ static const char pycurl_global_init_doc[] = "global_init(option) -> None. " "Initialize curl environment.\n"; static const char pycurl_global_cleanup_doc[] = "global_cleanup() -> None. " "Cleanup curl environment.\n"; static const char pycurl_version_info_doc[] = "version_info() -> tuple. " "Returns a 12-tuple with the version info.\n"; static const char pycurl_share_new_doc[] = "CurlShare() -> New CurlShare object."; static const char pycurl_curl_new_doc[] = "Curl() -> New curl object. " "Implicitly calls global_init() if not called.\n"; static const char pycurl_multi_new_doc[] = "CurlMulti() -> New curl multi-object.\n"; /* List of functions defined in this module */ static PyMethodDef curl_methods[] = { {"global_init", (PyCFunction)do_global_init, METH_VARARGS, pycurl_global_init_doc}, {"global_cleanup", (PyCFunction)do_global_cleanup, METH_NOARGS, pycurl_global_cleanup_doc}, {"version_info", (PyCFunction)do_version_info, METH_VARARGS, pycurl_version_info_doc}, {"Curl", (PyCFunction)do_curl_new, METH_NOARGS, pycurl_curl_new_doc}, {"CurlMulti", (PyCFunction)do_multi_new, METH_NOARGS, pycurl_multi_new_doc}, {"CurlShare", (PyCFunction)do_share_new, METH_NOARGS, pycurl_share_new_doc}, {NULL, NULL, 0, NULL} }; /* Module docstring */ static const char module_doc [] = "This module implements an interface to the cURL library.\n" "\n" "Types:\n" "\n" "Curl() -> New object. Create a new curl object.\n" "CurlMulti() -> New object. Create a new curl multi-object.\n" "\n" "Functions:\n" "\n" "global_init(option) -> None. Initialize curl environment.\n" "global_cleanup() -> None. Cleanup curl environment.\n" "version_info() -> tuple. Return version information.\n" ; /* Helper functions for inserting constants into the module namespace */ static void insobj2(PyObject *dict1, PyObject *dict2, char *name, PyObject *value) { /* Insert an object into one or two dicts. Eats a reference to value. * See also the implementation of PyDict_SetItemString(). */ PyObject *key = NULL; if (dict1 == NULL && dict2 == NULL) goto error; if (value == NULL) goto error; key = PyText_FromString(name); if (key == NULL) goto error; #if 0 PyString_InternInPlace(&key); /* XXX Should we really? */ #endif if (dict1 != NULL) { assert(PyDict_GetItem(dict1, key) == NULL); if (PyDict_SetItem(dict1, key, value) != 0) goto error; } if (dict2 != NULL && dict2 != dict1) { assert(PyDict_GetItem(dict2, key) == NULL); if (PyDict_SetItem(dict2, key, value) != 0) goto error; } Py_DECREF(key); Py_DECREF(value); return; error: Py_FatalError("pycurl: insobj2() failed"); assert(0); } static void insstr(PyObject *d, char *name, char *value) { PyObject *v = PyText_FromString(value); insobj2(d, NULL, name, v); } static void insint(PyObject *d, char *name, long value) { PyObject *v = PyInt_FromLong(value); insobj2(d, NULL, name, v); } static void insint_s(PyObject *d, char *name, long value) { PyObject *v = PyInt_FromLong(value); insobj2(d, curlshareobject_constants, name, v); } static void insint_c(PyObject *d, char *name, long value) { PyObject *v = PyInt_FromLong(value); insobj2(d, curlobject_constants, name, v); } static void insint_m(PyObject *d, char *name, long value) { PyObject *v = PyInt_FromLong(value); insobj2(d, curlmultiobject_constants, name, v); } /* Used in Python 3 only, and even then this function seems to never get * called. Python 2 has no module cleanup: * http://stackoverflow.com/questions/20741856/run-a-function-when-a-c-extension-module-is-freed-on-python-2 */ void do_curlmod_free(void *unused) { PyMem_Free(g_pycurl_useragent); g_pycurl_useragent = NULL; } #if PY_MAJOR_VERSION >= 3 static PyModuleDef curlmodule = { PyModuleDef_HEAD_INIT, "pycurl", /* m_name */ module_doc, /* m_doc */ -1, /* m_size */ curl_methods, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ do_curlmod_free /* m_free */ }; #endif #if PY_MAJOR_VERSION >= 3 #define PYCURL_MODINIT_RETURN_NULL return NULL PyMODINIT_FUNC PyInit_pycurl(void) #else #define PYCURL_MODINIT_RETURN_NULL return /* Initialization function for the module */ #if defined(PyMODINIT_FUNC) PyMODINIT_FUNC #else #if defined(__cplusplus) extern "C" #endif DL_EXPORT(void) #endif initpycurl(void) #endif { PyObject *m, *d; const curl_version_info_data *vi; const char *libcurl_version, *runtime_ssl_lib; int libcurl_version_len, pycurl_version_len; /* Check the version, as this has caused nasty problems in * some cases. */ vi = curl_version_info(CURLVERSION_NOW); if (vi == NULL) { PyErr_SetString(PyExc_ImportError, "pycurl: curl_version_info() failed"); PYCURL_MODINIT_RETURN_NULL; } if (vi->version_num < LIBCURL_VERSION_NUM) { PyErr_Format(PyExc_ImportError, "pycurl: libcurl link-time version (%s) is older than compile-time version (%s)", vi->version, LIBCURL_VERSION); PYCURL_MODINIT_RETURN_NULL; } /* Our compiled crypto locks should correspond to runtime ssl library. */ if (vi->ssl_version == NULL) { runtime_ssl_lib = "none/other"; } else if (!strncmp(vi->ssl_version, "OpenSSL/", 8)) { runtime_ssl_lib = "openssl"; } else if (!strncmp(vi->ssl_version, "GnuTLS/", 7)) { runtime_ssl_lib = "gnutls"; } else if (!strncmp(vi->ssl_version, "NSS/", 4)) { runtime_ssl_lib = "nss"; } else { runtime_ssl_lib = "none/other"; } if (strcmp(runtime_ssl_lib, COMPILE_SSL_LIB)) { PyErr_Format(PyExc_ImportError, "pycurl: libcurl link-time ssl backend (%s) is different from compile-time ssl backend (%s)", runtime_ssl_lib, COMPILE_SSL_LIB); PYCURL_MODINIT_RETURN_NULL; } /* Initialize the type of the new type objects here; doing it here * is required for portability to Windows without requiring C++. */ p_Curl_Type = &Curl_Type; p_CurlMulti_Type = &CurlMulti_Type; p_CurlShare_Type = &CurlShare_Type; Py_TYPE(&Curl_Type) = &PyType_Type; Py_TYPE(&CurlMulti_Type) = &PyType_Type; Py_TYPE(&CurlShare_Type) = &PyType_Type; /* Create the module and add the functions */ #if PY_MAJOR_VERSION >= 3 if (PyType_Ready(&Curl_Type) < 0) return NULL; if (PyType_Ready(&CurlMulti_Type) < 0) return NULL; m = PyModule_Create(&curlmodule); if (m == NULL) return NULL; Py_INCREF(&Curl_Type); #else m = Py_InitModule3("pycurl", curl_methods, module_doc); assert(m != NULL && PyModule_Check(m)); #endif /* Add error object to the module */ d = PyModule_GetDict(m); assert(d != NULL); ErrorObject = PyErr_NewException("pycurl.error", NULL, NULL); assert(ErrorObject != NULL); PyDict_SetItemString(d, "error", ErrorObject); curlobject_constants = PyDict_New(); assert(curlobject_constants != NULL); /* Add version strings to the module */ libcurl_version = curl_version(); libcurl_version_len = strlen(libcurl_version); #define PYCURL_VERSION_PREFIX_SIZE sizeof(PYCURL_VERSION_PREFIX) /* PYCURL_VERSION_PREFIX_SIZE includes terminating null which will be * replaced with the space; libcurl_version_len does not include * terminating null. */ pycurl_version_len = PYCURL_VERSION_PREFIX_SIZE + libcurl_version_len + 1; g_pycurl_useragent = PyMem_Malloc(pycurl_version_len); assert(g_pycurl_useragent != NULL); memcpy(g_pycurl_useragent, PYCURL_VERSION_PREFIX, PYCURL_VERSION_PREFIX_SIZE); g_pycurl_useragent[PYCURL_VERSION_PREFIX_SIZE-1] = ' '; memcpy(g_pycurl_useragent + PYCURL_VERSION_PREFIX_SIZE, libcurl_version, libcurl_version_len); g_pycurl_useragent[pycurl_version_len - 1] = 0; #undef PYCURL_VERSION_PREFIX_SIZE insobj2(d, NULL, "version", PyText_FromString(g_pycurl_useragent)); insstr(d, "COMPILE_DATE", __DATE__ " " __TIME__); insint(d, "COMPILE_PY_VERSION_HEX", PY_VERSION_HEX); insint(d, "COMPILE_LIBCURL_VERSION_NUM", LIBCURL_VERSION_NUM); /** ** the order of these constants mostly follows **/ /* Abort curl_read_callback(). */ insint_c(d, "READFUNC_ABORT", CURL_READFUNC_ABORT); insint_c(d, "READFUNC_PAUSE", CURL_READFUNC_PAUSE); /* Pause curl_write_callback(). */ insint_c(d, "WRITEFUNC_PAUSE", CURL_WRITEFUNC_PAUSE); /* constants for ioctl callback return values */ insint_c(d, "IOE_OK", CURLIOE_OK); insint_c(d, "IOE_UNKNOWNCMD", CURLIOE_UNKNOWNCMD); insint_c(d, "IOE_FAILRESTART", CURLIOE_FAILRESTART); /* constants for ioctl callback argument values */ insint_c(d, "IOCMD_NOP", CURLIOCMD_NOP); insint_c(d, "IOCMD_RESTARTREAD", CURLIOCMD_RESTARTREAD); /* curl_infotype: the kind of data that is passed to information_callback */ /* XXX do we actually need curl_infotype in pycurl ??? */ insint_c(d, "INFOTYPE_TEXT", CURLINFO_TEXT); insint_c(d, "INFOTYPE_HEADER_IN", CURLINFO_HEADER_IN); insint_c(d, "INFOTYPE_HEADER_OUT", CURLINFO_HEADER_OUT); insint_c(d, "INFOTYPE_DATA_IN", CURLINFO_DATA_IN); insint_c(d, "INFOTYPE_DATA_OUT", CURLINFO_DATA_OUT); insint_c(d, "INFOTYPE_SSL_DATA_IN", CURLINFO_SSL_DATA_IN); insint_c(d, "INFOTYPE_SSL_DATA_OUT", CURLINFO_SSL_DATA_OUT); /* CURLcode: error codes */ insint_c(d, "E_OK", CURLE_OK); insint_c(d, "E_UNSUPPORTED_PROTOCOL", CURLE_UNSUPPORTED_PROTOCOL); insint_c(d, "E_FAILED_INIT", CURLE_FAILED_INIT); insint_c(d, "E_URL_MALFORMAT", CURLE_URL_MALFORMAT); insint_c(d, "E_COULDNT_RESOLVE_PROXY", CURLE_COULDNT_RESOLVE_PROXY); insint_c(d, "E_COULDNT_RESOLVE_HOST", CURLE_COULDNT_RESOLVE_HOST); insint_c(d, "E_COULDNT_CONNECT", CURLE_COULDNT_CONNECT); insint_c(d, "E_FTP_WEIRD_SERVER_REPLY", CURLE_FTP_WEIRD_SERVER_REPLY); insint_c(d, "E_FTP_ACCESS_DENIED", CURLE_FTP_ACCESS_DENIED); insint_c(d, "E_FTP_WEIRD_PASS_REPLY", CURLE_FTP_WEIRD_PASS_REPLY); insint_c(d, "E_FTP_WEIRD_USER_REPLY", CURLE_FTP_WEIRD_USER_REPLY); insint_c(d, "E_FTP_WEIRD_PASV_REPLY", CURLE_FTP_WEIRD_PASV_REPLY); insint_c(d, "E_FTP_WEIRD_227_FORMAT", CURLE_FTP_WEIRD_227_FORMAT); insint_c(d, "E_FTP_CANT_GET_HOST", CURLE_FTP_CANT_GET_HOST); insint_c(d, "E_FTP_CANT_RECONNECT", CURLE_FTP_CANT_RECONNECT); insint_c(d, "E_FTP_COULDNT_SET_BINARY", CURLE_FTP_COULDNT_SET_BINARY); insint_c(d, "E_PARTIAL_FILE", CURLE_PARTIAL_FILE); insint_c(d, "E_FTP_COULDNT_RETR_FILE", CURLE_FTP_COULDNT_RETR_FILE); insint_c(d, "E_FTP_WRITE_ERROR", CURLE_FTP_WRITE_ERROR); insint_c(d, "E_FTP_QUOTE_ERROR", CURLE_FTP_QUOTE_ERROR); insint_c(d, "E_HTTP_RETURNED_ERROR", CURLE_HTTP_RETURNED_ERROR); insint_c(d, "E_WRITE_ERROR", CURLE_WRITE_ERROR); insint_c(d, "E_FTP_COULDNT_STOR_FILE", CURLE_FTP_COULDNT_STOR_FILE); insint_c(d, "E_READ_ERROR", CURLE_READ_ERROR); insint_c(d, "E_OUT_OF_MEMORY", CURLE_OUT_OF_MEMORY); insint_c(d, "E_OPERATION_TIMEOUTED", CURLE_OPERATION_TIMEOUTED); insint_c(d, "E_OPERATION_TIMEDOUT", CURLE_OPERATION_TIMEDOUT); insint_c(d, "E_FTP_COULDNT_SET_ASCII", CURLE_FTP_COULDNT_SET_ASCII); insint_c(d, "E_FTP_PORT_FAILED", CURLE_FTP_PORT_FAILED); insint_c(d, "E_FTP_COULDNT_USE_REST", CURLE_FTP_COULDNT_USE_REST); insint_c(d, "E_FTP_COULDNT_GET_SIZE", CURLE_FTP_COULDNT_GET_SIZE); insint_c(d, "E_HTTP_RANGE_ERROR", CURLE_HTTP_RANGE_ERROR); insint_c(d, "E_HTTP_POST_ERROR", CURLE_HTTP_POST_ERROR); insint_c(d, "E_SSL_CONNECT_ERROR", CURLE_SSL_CONNECT_ERROR); insint_c(d, "E_BAD_DOWNLOAD_RESUME", CURLE_BAD_DOWNLOAD_RESUME); insint_c(d, "E_FILE_COULDNT_READ_FILE", CURLE_FILE_COULDNT_READ_FILE); insint_c(d, "E_LDAP_CANNOT_BIND", CURLE_LDAP_CANNOT_BIND); insint_c(d, "E_LDAP_SEARCH_FAILED", CURLE_LDAP_SEARCH_FAILED); insint_c(d, "E_LIBRARY_NOT_FOUND", CURLE_LIBRARY_NOT_FOUND); insint_c(d, "E_FUNCTION_NOT_FOUND", CURLE_FUNCTION_NOT_FOUND); insint_c(d, "E_ABORTED_BY_CALLBACK", CURLE_ABORTED_BY_CALLBACK); insint_c(d, "E_BAD_FUNCTION_ARGUMENT", CURLE_BAD_FUNCTION_ARGUMENT); insint_c(d, "E_INTERFACE_FAILED", CURLE_INTERFACE_FAILED); insint_c(d, "E_TOO_MANY_REDIRECTS", CURLE_TOO_MANY_REDIRECTS); insint_c(d, "E_UNKNOWN_TELNET_OPTION", CURLE_UNKNOWN_TELNET_OPTION); insint_c(d, "E_TELNET_OPTION_SYNTAX", CURLE_TELNET_OPTION_SYNTAX); insint_c(d, "E_SSL_PEER_CERTIFICATE", CURLE_SSL_PEER_CERTIFICATE); insint_c(d, "E_GOT_NOTHING", CURLE_GOT_NOTHING); insint_c(d, "E_SSL_ENGINE_NOTFOUND", CURLE_SSL_ENGINE_NOTFOUND); insint_c(d, "E_SSL_ENGINE_SETFAILED", CURLE_SSL_ENGINE_SETFAILED); insint_c(d, "E_SEND_ERROR", CURLE_SEND_ERROR); insint_c(d, "E_RECV_ERROR", CURLE_RECV_ERROR); insint_c(d, "E_SHARE_IN_USE", CURLE_SHARE_IN_USE); insint_c(d, "E_SSL_CERTPROBLEM", CURLE_SSL_CERTPROBLEM); insint_c(d, "E_SSL_CIPHER", CURLE_SSL_CIPHER); insint_c(d, "E_SSL_CACERT", CURLE_SSL_CACERT); insint_c(d, "E_BAD_CONTENT_ENCODING", CURLE_BAD_CONTENT_ENCODING); insint_c(d, "E_LDAP_INVALID_URL", CURLE_LDAP_INVALID_URL); insint_c(d, "E_FILESIZE_EXCEEDED", CURLE_FILESIZE_EXCEEDED); insint_c(d, "E_FTP_SSL_FAILED", CURLE_FTP_SSL_FAILED); insint_c(d, "E_SEND_FAIL_REWIND", CURLE_SEND_FAIL_REWIND); insint_c(d, "E_SSL_ENGINE_INITFAILED", CURLE_SSL_ENGINE_INITFAILED); insint_c(d, "E_LOGIN_DENIED", CURLE_LOGIN_DENIED); insint_c(d, "E_TFTP_NOTFOUND", CURLE_TFTP_NOTFOUND); insint_c(d, "E_TFTP_PERM", CURLE_TFTP_PERM); insint_c(d, "E_TFTP_DISKFULL",CURLE_TFTP_DISKFULL ); insint_c(d, "E_TFTP_ILLEGAL",CURLE_TFTP_ILLEGAL ); insint_c(d, "E_TFTP_UNKNOWNID",CURLE_TFTP_UNKNOWNID ); insint_c(d, "E_TFTP_EXISTS", CURLE_TFTP_EXISTS); insint_c(d, "E_TFTP_NOSUCHUSER",CURLE_TFTP_NOSUCHUSER ); insint_c(d, "E_CONV_FAILED",CURLE_CONV_FAILED ); insint_c(d, "E_CONV_REQD",CURLE_CONV_REQD ); insint_c(d, "E_SSL_CACERT_BADFILE", CURLE_SSL_CACERT_BADFILE); insint_c(d, "E_REMOTE_FILE_NOT_FOUND",CURLE_REMOTE_FILE_NOT_FOUND ); insint_c(d, "E_SSH",CURLE_SSH ); insint_c(d, "E_SSL_SHUTDOWN_FAILED",CURLE_SSL_SHUTDOWN_FAILED ); /* curl_proxytype: constants for setopt(PROXYTYPE, x) */ insint_c(d, "PROXYTYPE_HTTP", CURLPROXY_HTTP); insint_c(d, "PROXYTYPE_SOCKS4", CURLPROXY_SOCKS4); insint_c(d, "PROXYTYPE_SOCKS5", CURLPROXY_SOCKS5); /* curl_httpauth: constants for setopt(HTTPAUTH, x) */ insint_c(d, "HTTPAUTH_NONE", CURLAUTH_NONE); insint_c(d, "HTTPAUTH_BASIC", CURLAUTH_BASIC); insint_c(d, "HTTPAUTH_DIGEST", CURLAUTH_DIGEST); #ifdef HAVE_CURLAUTH_DIGEST_IE insint_c(d, "HTTPAUTH_DIGEST_IE", CURLAUTH_DIGEST_IE); #endif insint_c(d, "HTTPAUTH_GSSNEGOTIATE", CURLAUTH_GSSNEGOTIATE); insint_c(d, "HTTPAUTH_NTLM", CURLAUTH_NTLM); insint_c(d, "HTTPAUTH_ANY", CURLAUTH_ANY); insint_c(d, "HTTPAUTH_ANYSAFE", CURLAUTH_ANYSAFE); /* curl_ftpssl: constants for setopt(FTP_SSL, x) */ insint_c(d, "FTPSSL_NONE", CURLFTPSSL_NONE); insint_c(d, "FTPSSL_TRY", CURLFTPSSL_TRY); insint_c(d, "FTPSSL_CONTROL", CURLFTPSSL_CONTROL); insint_c(d, "FTPSSL_ALL", CURLFTPSSL_ALL); /* curl_ftpauth: constants for setopt(FTPSSLAUTH, x) */ insint_c(d, "FTPAUTH_DEFAULT", CURLFTPAUTH_DEFAULT); insint_c(d, "FTPAUTH_SSL", CURLFTPAUTH_SSL); insint_c(d, "FTPAUTH_TLS", CURLFTPAUTH_TLS); /* curl_ftpauth: constants for setopt(FTPSSLAUTH, x) */ insint_c(d, "FORM_BUFFER", CURLFORM_BUFFER); insint_c(d, "FORM_BUFFERPTR", CURLFORM_BUFFERPTR); insint_c(d, "FORM_CONTENTS", CURLFORM_COPYCONTENTS); insint_c(d, "FORM_FILE", CURLFORM_FILE); insint_c(d, "FORM_CONTENTTYPE", CURLFORM_CONTENTTYPE); insint_c(d, "FORM_FILENAME", CURLFORM_FILENAME); /* FTP_FILEMETHOD options */ insint_c(d, "FTPMETHOD_DEFAULT", CURLFTPMETHOD_DEFAULT); insint_c(d, "FTPMETHOD_MULTICWD", CURLFTPMETHOD_MULTICWD); insint_c(d, "FTPMETHOD_NOCWD", CURLFTPMETHOD_NOCWD); insint_c(d, "FTPMETHOD_SINGLECWD", CURLFTPMETHOD_SINGLECWD); /* CURLoption: symbolic constants for setopt() */ /* FIXME: reorder these to match */ insint_c(d, "FILE", CURLOPT_WRITEDATA); insint_c(d, "URL", CURLOPT_URL); insint_c(d, "PORT", CURLOPT_PORT); insint_c(d, "PROXY", CURLOPT_PROXY); insint_c(d, "USERPWD", CURLOPT_USERPWD); #ifdef HAVE_CURLOPT_USERNAME insint_c(d, "USERNAME", CURLOPT_USERNAME); insint_c(d, "PASSWORD", CURLOPT_PASSWORD); #endif insint_c(d, "PROXYUSERPWD", CURLOPT_PROXYUSERPWD); #ifdef HAVE_CURLOPT_PROXYUSERNAME insint_c(d, "PROXYUSERNAME", CURLOPT_PROXYUSERNAME); insint_c(d, "PROXYPASSWORD", CURLOPT_PROXYPASSWORD); #endif insint_c(d, "RANGE", CURLOPT_RANGE); insint_c(d, "INFILE", CURLOPT_READDATA); /* ERRORBUFFER is not supported */ insint_c(d, "WRITEFUNCTION", CURLOPT_WRITEFUNCTION); insint_c(d, "READFUNCTION", CURLOPT_READFUNCTION); insint_c(d, "TIMEOUT", CURLOPT_TIMEOUT); insint_c(d, "INFILESIZE", CURLOPT_INFILESIZE_LARGE); /* _LARGE ! */ insint_c(d, "POSTFIELDS", CURLOPT_POSTFIELDS); insint_c(d, "REFERER", CURLOPT_REFERER); insint_c(d, "FTPPORT", CURLOPT_FTPPORT); insint_c(d, "USERAGENT", CURLOPT_USERAGENT); insint_c(d, "LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT); insint_c(d, "LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME); insint_c(d, "RESUME_FROM", CURLOPT_RESUME_FROM_LARGE); /* _LARGE ! */ insint_c(d, "WRITEDATA", CURLOPT_WRITEDATA); insint_c(d, "READDATA", CURLOPT_READDATA); insint_c(d, "PROXYPORT", CURLOPT_PROXYPORT); insint_c(d, "HTTPPROXYTUNNEL", CURLOPT_HTTPPROXYTUNNEL); insint_c(d, "VERBOSE", CURLOPT_VERBOSE); insint_c(d, "HEADER", CURLOPT_HEADER); insint_c(d, "NOPROGRESS", CURLOPT_NOPROGRESS); insint_c(d, "NOBODY", CURLOPT_NOBODY); insint_c(d, "FAILONERROR", CURLOPT_FAILONERROR); insint_c(d, "UPLOAD", CURLOPT_UPLOAD); insint_c(d, "POST", CURLOPT_POST); insint_c(d, "FTPLISTONLY", CURLOPT_FTPLISTONLY); insint_c(d, "FTPAPPEND", CURLOPT_FTPAPPEND); insint_c(d, "NETRC", CURLOPT_NETRC); insint_c(d, "FOLLOWLOCATION", CURLOPT_FOLLOWLOCATION); insint_c(d, "TRANSFERTEXT", CURLOPT_TRANSFERTEXT); insint_c(d, "PUT", CURLOPT_PUT); insint_c(d, "POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE_LARGE); /* _LARGE ! */ insint_c(d, "COOKIE", CURLOPT_COOKIE); insint_c(d, "HTTPHEADER", CURLOPT_HTTPHEADER); insint_c(d, "HTTPPOST", CURLOPT_HTTPPOST); insint_c(d, "SSLCERT", CURLOPT_SSLCERT); insint_c(d, "SSLCERTPASSWD", CURLOPT_SSLCERTPASSWD); insint_c(d, "CRLF", CURLOPT_CRLF); insint_c(d, "QUOTE", CURLOPT_QUOTE); insint_c(d, "POSTQUOTE", CURLOPT_POSTQUOTE); insint_c(d, "PREQUOTE", CURLOPT_PREQUOTE); insint_c(d, "WRITEHEADER", CURLOPT_WRITEHEADER); insint_c(d, "HEADERFUNCTION", CURLOPT_HEADERFUNCTION); insint_c(d, "SEEKFUNCTION", CURLOPT_SEEKFUNCTION); insint_c(d, "COOKIEFILE", CURLOPT_COOKIEFILE); insint_c(d, "SSLVERSION", CURLOPT_SSLVERSION); insint_c(d, "TIMECONDITION", CURLOPT_TIMECONDITION); insint_c(d, "TIMEVALUE", CURLOPT_TIMEVALUE); insint_c(d, "CUSTOMREQUEST", CURLOPT_CUSTOMREQUEST); insint_c(d, "STDERR", CURLOPT_STDERR); insint_c(d, "INTERFACE", CURLOPT_INTERFACE); insint_c(d, "KRB4LEVEL", CURLOPT_KRB4LEVEL); insint_c(d, "PROGRESSFUNCTION", CURLOPT_PROGRESSFUNCTION); insint_c(d, "SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER); insint_c(d, "CAPATH", CURLOPT_CAPATH); insint_c(d, "CAINFO", CURLOPT_CAINFO); insint_c(d, "OPT_FILETIME", CURLOPT_FILETIME); insint_c(d, "MAXREDIRS", CURLOPT_MAXREDIRS); insint_c(d, "MAXCONNECTS", CURLOPT_MAXCONNECTS); insint_c(d, "FRESH_CONNECT", CURLOPT_FRESH_CONNECT); insint_c(d, "FORBID_REUSE", CURLOPT_FORBID_REUSE); insint_c(d, "RANDOM_FILE", CURLOPT_RANDOM_FILE); insint_c(d, "EGDSOCKET", CURLOPT_EGDSOCKET); insint_c(d, "CONNECTTIMEOUT", CURLOPT_CONNECTTIMEOUT); insint_c(d, "HTTPGET", CURLOPT_HTTPGET); insint_c(d, "SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST); insint_c(d, "COOKIEJAR", CURLOPT_COOKIEJAR); insint_c(d, "SSL_CIPHER_LIST", CURLOPT_SSL_CIPHER_LIST); insint_c(d, "HTTP_VERSION", CURLOPT_HTTP_VERSION); insint_c(d, "FTP_USE_EPSV", CURLOPT_FTP_USE_EPSV); insint_c(d, "SSLCERTTYPE", CURLOPT_SSLCERTTYPE); insint_c(d, "SSLKEY", CURLOPT_SSLKEY); insint_c(d, "SSLKEYTYPE", CURLOPT_SSLKEYTYPE); insint_c(d, "SSLKEYPASSWD", CURLOPT_SSLKEYPASSWD); insint_c(d, "SSLENGINE", CURLOPT_SSLENGINE); insint_c(d, "SSLENGINE_DEFAULT", CURLOPT_SSLENGINE_DEFAULT); insint_c(d, "DNS_CACHE_TIMEOUT", CURLOPT_DNS_CACHE_TIMEOUT); insint_c(d, "DNS_USE_GLOBAL_CACHE", CURLOPT_DNS_USE_GLOBAL_CACHE); insint_c(d, "DEBUGFUNCTION", CURLOPT_DEBUGFUNCTION); insint_c(d, "BUFFERSIZE", CURLOPT_BUFFERSIZE); insint_c(d, "NOSIGNAL", CURLOPT_NOSIGNAL); insint_c(d, "SHARE", CURLOPT_SHARE); insint_c(d, "PROXYTYPE", CURLOPT_PROXYTYPE); insint_c(d, "ENCODING", CURLOPT_ENCODING); insint_c(d, "HTTP200ALIASES", CURLOPT_HTTP200ALIASES); insint_c(d, "UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH); insint_c(d, "FTP_USE_EPRT", CURLOPT_FTP_USE_EPRT); insint_c(d, "HTTPAUTH", CURLOPT_HTTPAUTH); insint_c(d, "FTP_CREATE_MISSING_DIRS", CURLOPT_FTP_CREATE_MISSING_DIRS); insint_c(d, "PROXYAUTH", CURLOPT_PROXYAUTH); insint_c(d, "FTP_RESPONSE_TIMEOUT", CURLOPT_FTP_RESPONSE_TIMEOUT); insint_c(d, "IPRESOLVE", CURLOPT_IPRESOLVE); insint_c(d, "MAXFILESIZE", CURLOPT_MAXFILESIZE_LARGE); /* _LARGE ! */ insint_c(d, "INFILESIZE_LARGE", CURLOPT_INFILESIZE_LARGE); insint_c(d, "RESUME_FROM_LARGE", CURLOPT_RESUME_FROM_LARGE); insint_c(d, "MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE); insint_c(d, "NETRC_FILE", CURLOPT_NETRC_FILE); insint_c(d, "FTP_SSL", CURLOPT_FTP_SSL); insint_c(d, "POSTFIELDSIZE_LARGE", CURLOPT_POSTFIELDSIZE_LARGE); insint_c(d, "TCP_NODELAY", CURLOPT_TCP_NODELAY); insint_c(d, "FTPSSLAUTH", CURLOPT_FTPSSLAUTH); insint_c(d, "IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION); insint_c(d, "IOCTLDATA", CURLOPT_IOCTLDATA); insint_c(d, "OPENSOCKETFUNCTION", CURLOPT_OPENSOCKETFUNCTION); insint_c(d, "FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT); insint_c(d, "IGNORE_CONTENT_LENGTH", CURLOPT_IGNORE_CONTENT_LENGTH); insint_c(d, "COOKIELIST", CURLOPT_COOKIELIST); insint_c(d, "FTP_SKIP_PASV_IP", CURLOPT_FTP_SKIP_PASV_IP); insint_c(d, "FTP_FILEMETHOD", CURLOPT_FTP_FILEMETHOD); insint_c(d, "CONNECT_ONLY", CURLOPT_CONNECT_ONLY); insint_c(d, "LOCALPORT", CURLOPT_LOCALPORT); insint_c(d, "LOCALPORTRANGE", CURLOPT_LOCALPORTRANGE); insint_c(d, "FTP_ALTERNATIVE_TO_USER", CURLOPT_FTP_ALTERNATIVE_TO_USER); insint_c(d, "MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE); insint_c(d, "MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE); insint_c(d, "SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE); insint_c(d, "SSH_AUTH_TYPES", CURLOPT_SSH_AUTH_TYPES); insint_c(d, "SSH_PUBLIC_KEYFILE", CURLOPT_SSH_PUBLIC_KEYFILE); insint_c(d, "SSH_PRIVATE_KEYFILE", CURLOPT_SSH_PRIVATE_KEYFILE); insint_c(d, "FTP_SSL_CCC", CURLOPT_FTP_SSL_CCC); insint_c(d, "TIMEOUT_MS", CURLOPT_TIMEOUT_MS); insint_c(d, "CONNECTTIMEOUT_MS", CURLOPT_CONNECTTIMEOUT_MS); insint_c(d, "HTTP_TRANSFER_DECODING", CURLOPT_HTTP_TRANSFER_DECODING); insint_c(d, "HTTP_CONTENT_DECODING", CURLOPT_HTTP_CONTENT_DECODING); insint_c(d, "NEW_FILE_PERMS", CURLOPT_NEW_FILE_PERMS); insint_c(d, "NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS); insint_c(d, "POST301", CURLOPT_POST301); insint_c(d, "PROXY_TRANSFER_MODE", CURLOPT_PROXY_TRANSFER_MODE); insint_c(d, "COPYPOSTFIELDS", CURLOPT_COPYPOSTFIELDS); insint_c(d, "SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5); insint_c(d, "AUTOREFERER", CURLOPT_AUTOREFERER); insint_c(d, "CRLFILE", CURLOPT_CRLFILE); insint_c(d, "ISSUERCERT", CURLOPT_ISSUERCERT); insint_c(d, "ADDRESS_SCOPE", CURLOPT_ADDRESS_SCOPE); #ifdef HAVE_CURLOPT_RESOLVE insint_c(d, "RESOLVE", CURLOPT_RESOLVE); #endif #ifdef HAVE_CURLOPT_CERTINFO insint_c(d, "OPT_CERTINFO", CURLOPT_CERTINFO); #endif #ifdef HAVE_CURLOPT_POSTREDIR insint_c(d, "POSTREDIR", CURLOPT_POSTREDIR); #endif #ifdef HAVE_CURLOPT_NOPROXY insint_c(d, "NOPROXY", CURLOPT_NOPROXY); #endif insint_c(d, "M_TIMERFUNCTION", CURLMOPT_TIMERFUNCTION); insint_c(d, "M_SOCKETFUNCTION", CURLMOPT_SOCKETFUNCTION); insint_c(d, "M_PIPELINING", CURLMOPT_PIPELINING); insint_c(d, "M_MAXCONNECTS", CURLMOPT_MAXCONNECTS); /* constants for setopt(IPRESOLVE, x) */ insint_c(d, "IPRESOLVE_WHATEVER", CURL_IPRESOLVE_WHATEVER); insint_c(d, "IPRESOLVE_V4", CURL_IPRESOLVE_V4); insint_c(d, "IPRESOLVE_V6", CURL_IPRESOLVE_V6); /* constants for setopt(HTTP_VERSION, x) */ insint_c(d, "CURL_HTTP_VERSION_NONE", CURL_HTTP_VERSION_NONE); insint_c(d, "CURL_HTTP_VERSION_1_0", CURL_HTTP_VERSION_1_0); insint_c(d, "CURL_HTTP_VERSION_1_1", CURL_HTTP_VERSION_1_1); insint_c(d, "CURL_HTTP_VERSION_LAST", CURL_HTTP_VERSION_LAST); /* CURL_NETRC_OPTION: constants for setopt(NETRC, x) */ insint_c(d, "NETRC_OPTIONAL", CURL_NETRC_OPTIONAL); insint_c(d, "NETRC_IGNORED", CURL_NETRC_IGNORED); insint_c(d, "NETRC_REQUIRED", CURL_NETRC_REQUIRED); /* constants for setopt(SSLVERSION, x) */ insint_c(d, "SSLVERSION_DEFAULT", CURL_SSLVERSION_DEFAULT); insint_c(d, "SSLVERSION_TLSv1", CURL_SSLVERSION_TLSv1); insint_c(d, "SSLVERSION_SSLv2", CURL_SSLVERSION_SSLv2); insint_c(d, "SSLVERSION_SSLv3", CURL_SSLVERSION_SSLv3); /* curl_TimeCond: constants for setopt(TIMECONDITION, x) */ insint_c(d, "TIMECONDITION_NONE", CURL_TIMECOND_NONE); insint_c(d, "TIMECONDITION_IFMODSINCE", CURL_TIMECOND_IFMODSINCE); insint_c(d, "TIMECONDITION_IFUNMODSINCE", CURL_TIMECOND_IFUNMODSINCE); insint_c(d, "TIMECONDITION_LASTMOD", CURL_TIMECOND_LASTMOD); /* constants for setopt(CURLOPT_SSH_AUTH_TYPES, x) */ insint_c(d, "SSH_AUTH_ANY", CURLSSH_AUTH_ANY); insint_c(d, "SSH_AUTH_NONE", CURLSSH_AUTH_NONE); insint_c(d, "SSH_AUTH_PUBLICKEY", CURLSSH_AUTH_PUBLICKEY); insint_c(d, "SSH_AUTH_PASSWORD", CURLSSH_AUTH_PASSWORD); insint_c(d, "SSH_AUTH_HOST", CURLSSH_AUTH_HOST); insint_c(d, "SSH_AUTH_KEYBOARD", CURLSSH_AUTH_KEYBOARD); insint_c(d, "SSH_AUTH_DEFAULT", CURLSSH_AUTH_DEFAULT); /* CURLINFO: symbolic constants for getinfo(x) */ insint_c(d, "EFFECTIVE_URL", CURLINFO_EFFECTIVE_URL); insint_c(d, "HTTP_CODE", CURLINFO_HTTP_CODE); insint_c(d, "RESPONSE_CODE", CURLINFO_HTTP_CODE); insint_c(d, "TOTAL_TIME", CURLINFO_TOTAL_TIME); insint_c(d, "NAMELOOKUP_TIME", CURLINFO_NAMELOOKUP_TIME); insint_c(d, "CONNECT_TIME", CURLINFO_CONNECT_TIME); insint_c(d, "APPCONNECT_TIME", CURLINFO_APPCONNECT_TIME); insint_c(d, "PRETRANSFER_TIME", CURLINFO_PRETRANSFER_TIME); insint_c(d, "SIZE_UPLOAD", CURLINFO_SIZE_UPLOAD); insint_c(d, "SIZE_DOWNLOAD", CURLINFO_SIZE_DOWNLOAD); insint_c(d, "SPEED_DOWNLOAD", CURLINFO_SPEED_DOWNLOAD); insint_c(d, "SPEED_UPLOAD", CURLINFO_SPEED_UPLOAD); insint_c(d, "HEADER_SIZE", CURLINFO_HEADER_SIZE); insint_c(d, "REQUEST_SIZE", CURLINFO_REQUEST_SIZE); insint_c(d, "SSL_VERIFYRESULT", CURLINFO_SSL_VERIFYRESULT); insint_c(d, "INFO_FILETIME", CURLINFO_FILETIME); insint_c(d, "CONTENT_LENGTH_DOWNLOAD", CURLINFO_CONTENT_LENGTH_DOWNLOAD); insint_c(d, "CONTENT_LENGTH_UPLOAD", CURLINFO_CONTENT_LENGTH_UPLOAD); insint_c(d, "STARTTRANSFER_TIME", CURLINFO_STARTTRANSFER_TIME); insint_c(d, "CONTENT_TYPE", CURLINFO_CONTENT_TYPE); insint_c(d, "REDIRECT_TIME", CURLINFO_REDIRECT_TIME); insint_c(d, "REDIRECT_COUNT", CURLINFO_REDIRECT_COUNT); insint_c(d, "REDIRECT_URL", CURLINFO_REDIRECT_URL); insint_c(d, "PRIMARY_IP", CURLINFO_PRIMARY_IP); #ifdef HAVE_CURLINFO_PRIMARY_PORT insint_c(d, "PRIMARY_PORT", CURLINFO_PRIMARY_PORT); #endif #ifdef HAVE_CURLINFO_LOCAL_IP insint_c(d, "LOCAL_IP", CURLINFO_LOCAL_IP); #endif #ifdef HAVE_CURLINFO_LOCAL_PORT insint_c(d, "LOCAL_PORT", CURLINFO_LOCAL_PORT); #endif insint_c(d, "HTTP_CONNECTCODE", CURLINFO_HTTP_CONNECTCODE); insint_c(d, "HTTPAUTH_AVAIL", CURLINFO_HTTPAUTH_AVAIL); insint_c(d, "PROXYAUTH_AVAIL", CURLINFO_PROXYAUTH_AVAIL); insint_c(d, "OS_ERRNO", CURLINFO_OS_ERRNO); insint_c(d, "NUM_CONNECTS", CURLINFO_NUM_CONNECTS); insint_c(d, "SSL_ENGINES", CURLINFO_SSL_ENGINES); insint_c(d, "INFO_COOKIELIST", CURLINFO_COOKIELIST); insint_c(d, "LASTSOCKET", CURLINFO_LASTSOCKET); insint_c(d, "FTP_ENTRY_PATH", CURLINFO_FTP_ENTRY_PATH); #ifdef HAVE_CURLOPT_CERTINFO insint_c(d, "INFO_CERTINFO", CURLINFO_CERTINFO); #endif /* CURLPAUSE: symbolic constants for pause(bitmask) */ insint_c(d, "PAUSE_RECV", CURLPAUSE_RECV); insint_c(d, "PAUSE_SEND", CURLPAUSE_SEND); insint_c(d, "PAUSE_ALL", CURLPAUSE_ALL); insint_c(d, "PAUSE_CONT", CURLPAUSE_CONT); #ifdef HAVE_CURLOPT_DNS_SERVERS insint_c(d, "DNS_SERVERS", CURLOPT_DNS_SERVERS); #endif #ifdef HAVE_CURLOPT_POSTREDIR insint_c(d, "REDIR_POST_301", CURL_REDIR_POST_301); insint_c(d, "REDIR_POST_302", CURL_REDIR_POST_302); # ifdef HAVE_CURL_REDIR_POST_303 insint_c(d, "REDIR_POST_303", CURL_REDIR_POST_303); # endif insint_c(d, "REDIR_POST_ALL", CURL_REDIR_POST_ALL); #endif /* options for global_init() */ insint(d, "GLOBAL_SSL", CURL_GLOBAL_SSL); insint(d, "GLOBAL_WIN32", CURL_GLOBAL_WIN32); insint(d, "GLOBAL_ALL", CURL_GLOBAL_ALL); insint(d, "GLOBAL_NOTHING", CURL_GLOBAL_NOTHING); insint(d, "GLOBAL_DEFAULT", CURL_GLOBAL_DEFAULT); #ifdef CURL_GLOBAL_ACK_EINTR /* CURL_GLOBAL_ACK_EINTR was introduced in libcurl-7.30.0 */ insint(d, "GLOBAL_ACK_EINTR", CURL_GLOBAL_ACK_EINTR); #endif /* constants for curl_multi_socket interface */ insint(d, "CSELECT_IN", CURL_CSELECT_IN); insint(d, "CSELECT_OUT", CURL_CSELECT_OUT); insint(d, "CSELECT_ERR", CURL_CSELECT_ERR); insint(d, "SOCKET_TIMEOUT", CURL_SOCKET_TIMEOUT); insint(d, "POLL_NONE", CURL_POLL_NONE); insint(d, "POLL_IN", CURL_POLL_IN); insint(d, "POLL_OUT", CURL_POLL_OUT); insint(d, "POLL_INOUT", CURL_POLL_INOUT); insint(d, "POLL_REMOVE", CURL_POLL_REMOVE); /* curl_lock_data: XXX do we need this in pycurl ??? */ /* curl_lock_access: XXX do we need this in pycurl ??? */ /* CURLSHcode: XXX do we need this in pycurl ??? */ /* CURLSHoption: XXX do we need this in pycurl ??? */ /* CURLversion: constants for curl_version_info(x) */ #if 0 /* XXX - do we need these ?? */ insint(d, "VERSION_FIRST", CURLVERSION_FIRST); insint(d, "VERSION_SECOND", CURLVERSION_SECOND); insint(d, "VERSION_THIRD", CURLVERSION_THIRD); insint(d, "VERSION_NOW", CURLVERSION_NOW); #endif /* version features - bitmasks for curl_version_info_data.features */ #if 0 /* XXX - do we need these ?? */ /* XXX - should we really rename these ?? */ insint(d, "VERSION_FEATURE_IPV6", CURL_VERSION_IPV6); insint(d, "VERSION_FEATURE_KERBEROS4", CURL_VERSION_KERBEROS4); insint(d, "VERSION_FEATURE_SSL", CURL_VERSION_SSL); insint(d, "VERSION_FEATURE_LIBZ", CURL_VERSION_LIBZ); insint(d, "VERSION_FEATURE_NTLM", CURL_VERSION_NTLM); insint(d, "VERSION_FEATURE_GSSNEGOTIATE", CURL_VERSION_GSSNEGOTIATE); insint(d, "VERSION_FEATURE_DEBUG", CURL_VERSION_DEBUG); insint(d, "VERSION_FEATURE_ASYNCHDNS", CURL_VERSION_ASYNCHDNS); insint(d, "VERSION_FEATURE_SPNEGO", CURL_VERSION_SPNEGO); insint(d, "VERSION_FEATURE_LARGEFILE", CURL_VERSION_LARGEFILE); insint(d, "VERSION_FEATURE_IDN", CURL_VERSION_IDN); #endif /** ** the order of these constants mostly follows **/ /* CURLMcode: multi error codes */ curlmultiobject_constants = PyDict_New(); assert(curlmultiobject_constants != NULL); insint_m(d, "E_CALL_MULTI_PERFORM", CURLM_CALL_MULTI_PERFORM); insint_m(d, "E_MULTI_OK", CURLM_OK); insint_m(d, "E_MULTI_BAD_HANDLE", CURLM_BAD_HANDLE); insint_m(d, "E_MULTI_BAD_EASY_HANDLE", CURLM_BAD_EASY_HANDLE); insint_m(d, "E_MULTI_OUT_OF_MEMORY", CURLM_OUT_OF_MEMORY); insint_m(d, "E_MULTI_INTERNAL_ERROR", CURLM_INTERNAL_ERROR); /* curl shared constants */ curlshareobject_constants = PyDict_New(); assert(curlshareobject_constants != NULL); insint_s(d, "SH_SHARE", CURLSHOPT_SHARE); insint_s(d, "SH_UNSHARE", CURLSHOPT_UNSHARE); insint_s(d, "LOCK_DATA_COOKIE", CURL_LOCK_DATA_COOKIE); insint_s(d, "LOCK_DATA_DNS", CURL_LOCK_DATA_DNS); insint_s(d, "LOCK_DATA_SSL_SESSION", CURL_LOCK_DATA_SSL_SESSION); /* Initialize callback locks if ssl is enabled */ #if defined(PYCURL_NEED_SSL_TSL) pycurl_ssl_init(); #endif #ifdef WITH_THREAD /* Finally initialize global interpreter lock */ PyEval_InitThreads(); #endif #if PY_MAJOR_VERSION >= 3 return m; #endif } #if defined(WIN32) && ((_WIN32_WINNT < 0x0600) || (NTDDI_VERSION < NTDDI_VISTA)) /* * Only Winsock on Vista+ has inet_ntop(). */ static const char * pycurl_inet_ntop (int family, void *addr, char *string, size_t string_size) { SOCKADDR *sa; int sa_len; if (family == AF_INET6) { struct sockaddr_in6 sa6; memset(&sa6, 0, sizeof(sa6)); sa6.sin6_family = AF_INET6; memcpy(&sa6.sin6_addr, addr, sizeof(sa6.sin6_addr)); sa = (SOCKADDR*) &sa6; sa_len = sizeof(sa6); } else if (family == AF_INET) { struct sockaddr_in sa4; memset(&sa4, 0, sizeof(sa4)); sa4.sin_family = AF_INET; memcpy(&sa4.sin_addr, addr, sizeof(sa4.sin_addr)); sa = (SOCKADDR*) &sa4; sa_len = sizeof(sa4); } else { errno = EAFNOSUPPORT; return NULL; } if (WSAAddressToString(sa, sa_len, NULL, string, &string_size)) return NULL; return string; } #endif /* vi:ts=4:et:nowrap */ pycurl-7.19.3/tests/0000755000470500047050000000000012263734634013667 5ustar piepie00000000000000pycurl-7.19.3/tests/certs/0000755000470500047050000000000012263734634015007 5ustar piepie00000000000000pycurl-7.19.3/tests/certs/server.crt0000644000470500047050000000142612256232314017020 0ustar piepie00000000000000-----BEGIN CERTIFICATE----- MIICGzCCAYQCCQCdeJzNRLLLvDANBgkqhkiG9w0BAQUFADBSMQswCQYDVQQGEwJB VTETMBEGA1UECBMKU29tZS1TdGF0ZTEaMBgGA1UEChMRUHljVVJMIHRlc3Qgc3Vp dGUxEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMzEyMjIxMzA0MTVaFw0xNzA5MTcx MzA0MTVaMFIxCzAJBgNVBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMRowGAYD VQQKExFQeWNVUkwgdGVzdCBzdWl0ZTESMBAGA1UEAxMJbG9jYWxob3N0MIGfMA0G CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7 FjhJYh+vvwV8JK12qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSx HgNvTXI8y0xT3EfODfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tH SJhRgs4iAwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAGhX0iIMCrRcI6T6S4YydkXf 7LrXZpjSJwVGSCd5ehx3Qvc1LQNJjpB68F0MhVTqbDP1o3CAHaTa2s/8NC9j3tV7 bUynoKJT3srpHisfdd/SV538mWvFDtGRctbmmqp8qT4On+kr76dKj+/d3HyfOKIK Aasa7ODxFKbbY542yYHu -----END CERTIFICATE----- pycurl-7.19.3/tests/certs/server.key0000644000470500047050000000156712255756661017045 0ustar piepie00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDETT7n0p/bP1xxnJQC4oZCmRBpe0QkLXd7FjhJYh+vvwV8JK12 qll1+abrp2MUQ2GZoBlgaw8cZesQgdCqNrbspiQejhxcvGSxHgNvTXI8y0xT3EfO Dfs5MhLaUZGYzEt24pzlcqzED3w9X8PWTiSyotwsMf/9h0tHSJhRgs4iAwIDAQAB AoGAMLNFTvgnJpqikaEZ61lNm8ojkze8oQkSolvR3QrV96D5eGIVEuKSTT2Blucx In7RAO8CPLRyzEXQuoiqPwBSAxY2Xswd+zcAgN2Zi8uqWTmPNsW6451BJRemgLjK OxLxCdVTOTxHfttj/CnwYQ6zn55oyZJGGmaVGykbvH/AgukCQQD3HfhOPExsI/6X Bp7CeZOhmM+LBOQGQGDjRnBdRp0s3HiUfaDxU2mbEafGPI2OyuzpYAqxHVTJLai6 CQlJGuQXAkEAy1upObz2bcN2dUCHNufk2qdfRSCRkmKemuqznwCW3fSoRKB+qOu3 xyTLEkTvLBNnAFjoyd6B75QzL/7//qvo9QJAE0xV3dY7qZ5N/YFY2Jsh+layroqd PBe++UDA+afQEnbNO9trvCzlbGS+k26bJ3GVeswzSY2e128nZA/cl8bv1QJAfTEO uybjpqtAj+qL03drYljLw+jK9Y2VCtYWgnqAZmAp/yW3FBMZbpXuFm8ttrqzHHmf xjcfUvivkoqv2n7GyQJBAKxbBVx/LQiSVpOTnXTEA1NJF8NS2NCF+3sm3kGhFKql Hi/cCAFrhBl9MoPJF/6noukfIkq0SzjkWrYIcoBDoVg= -----END RSA PRIVATE KEY----- pycurl-7.19.3/tests/ext/0000755000470500047050000000000012263734634014467 5ustar piepie00000000000000pycurl-7.19.3/tests/ext/test-lib.sh0000644000470500047050000000374712257207035016552 0ustar piepie00000000000000# shell test framework based on test framework in rpg: # https://github.com/rtomayko/rpg # # Copyright (c) 2010 Ryan Tomayko # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. : ${VERBOSE:=false} unset CDPATH #cd "$(dirname $0)" if test -z "$TESTDIR"; then TESTDIR=$(realpath $(pwd)) fi test_count=0 successes=0 failures=0 output="$TESTDIR/$(basename "$0" .sh).out" trap "rm -f $output" 0 succeeds () { test_count=$(( test_count + 1 )) echo "\$ ${2:-$1}" > "$output" eval "( ${2:-$1} )" 1>>"$output" 2>&1 ec=$? if test $ec -eq 0 then successes=$(( successes + 1 )) printf 'ok %d - %s\n' $test_count "$1" else failures=$(( failures + 1 )) printf 'not ok %d - %s [%d]\n' $test_count "$1" "$ec" fi $VERBOSE && dcat $output return 0 } fails () { if test $# -eq 1 then succeeds "! $1" else succeeds "$1" "! $2" fi } diag () { echo "$@" | sed 's/^/# /'; } dcat () { cat "$@" | sed 's/^/# /'; } desc () { diag "$@"; } setup () { rm -rf "$TESTDIR/trash" return 0 } pycurl-7.19.3/tests/ext/test-suite.sh0000755000470500047050000000136012257207035017125 0ustar piepie00000000000000# dir=$(dirname "$0") . "$dir"/test-lib.sh setup desc 'setup.py without arguments' fails 'python setup.py' succeeds 'python setup.py 2>&1 |grep "usage: setup.py"' desc 'setup.py --help' succeeds 'python setup.py --help' # .* = Unix|Windows succeeds 'python setup.py --help |grep "PycURL .* options:"' # distutils help succeeds 'python setup.py --help |grep "Common commands:"' desc 'setup.py --help with bogus --curl-config' succeeds 'python setup.py --help --curl-config=/dev/null' succeeds 'python setup.py --help --curl-config=/dev/null |grep "PycURL .* options:"' # this checks that --curl-config is consumed prior to # distutils processing --help fails 'python setup.py --help --curl-config=/dev/null 2>&1 |grep "option .* not recognized"' pycurl-7.19.3/tests/matrix/0000755000470500047050000000000012263734634015173 5ustar piepie00000000000000pycurl-7.19.3/tests/matrix/curl-7.19.0-sslv2-c66b0b32fba-modified.patch0000644000470500047050000000111112256232314024316 0ustar piepie00000000000000diff --git a/lib/ssluse.c b/lib/ssluse.c index cee78bb..967fc57 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -1327,8 +1327,13 @@ ossl_connect_step1(struct connectdata *conn, req_method = TLSv1_client_method(); break; case CURL_SSLVERSION_SSLv2: +#ifdef OPENSSL_NO_SSL2 + failf(data, "OpenSSL was built without SSLv2 support"); + return CURLE_UNSUPPORTED_PROTOCOL /* CURLE_NOT_BUILT_IN not defined in 7.19.0 */; +#else req_method = SSLv2_client_method(); break; +#endif case CURL_SSLVERSION_SSLv3: req_method = SSLv3_client_method(); break; pycurl-7.19.3/tests/matrix/nose-1.3.0-python24.patch0000644000470500047050000000434612255756661021316 0ustar piepie00000000000000diff --git a/nose/failure.py b/nose/failure.py index d24401c..c5fabfd 100644 --- a/nose/failure.py +++ b/nose/failure.py @@ -1,6 +1,7 @@ import logging import unittest from traceback import format_tb +from nose.pyversion import is_base_exception log = logging.getLogger(__name__) @@ -34,7 +35,7 @@ def address(self): def runTest(self): if self.tb is not None: - if isinstance(self.exc_val, BaseException): + if is_base_exception(self.exc_val): raise self.exc_val, None, self.tb raise self.exc_class, self.exc_val, self.tb else: diff --git a/nose/plugins/capture.py b/nose/plugins/capture.py index 224f0a5..d9041d8 100644 --- a/nose/plugins/capture.py +++ b/nose/plugins/capture.py @@ -13,6 +13,7 @@ import os import sys from nose.plugins.base import Plugin +from nose.pyversion import is_base_exception from nose.util import ln from StringIO import StringIO @@ -86,7 +87,7 @@ def formatFailure(self, test, err): return self.formatError(test, err) def addCaptureToErr(self, ev, output): - if isinstance(ev, BaseException): + if is_base_exception(ev): if hasattr(ev, '__unicode__'): # 2.6+ try: diff --git a/nose/pyversion.py b/nose/pyversion.py index a6ec3f7..14a133e 100644 --- a/nose/pyversion.py +++ b/nose/pyversion.py @@ -9,7 +9,7 @@ __all__ = ['make_instancemethod', 'cmp_to_key', 'sort_list', 'ClassType', 'TypeType', 'UNICODE_STRINGS', 'unbound_method', 'ismethod', - 'bytes_'] + 'bytes_', 'is_base_exception'] # In Python 3.x, all strings are unicode (the call to 'unicode()' in the 2.x # source will be replaced with 'str()' when running 2to3, so this test will @@ -147,3 +147,12 @@ def isgenerator(func): return func.func_code.co_flags & CO_GENERATOR != 0 except AttributeError: return False + +# Make a function to help check if an exception is derived from BaseException. +# In Python 2.4, we just use Exception instead. +if sys.version_info[:2] < (2, 5): + def is_base_exception(exc): + return isinstance(exc, Exception) +else: + def is_base_exception(exc): + return isinstance(exc, BaseException) pycurl-7.19.3/tests/matrix/python25.patch0000644000470500047050000000405112255756661017711 0ustar piepie00000000000000 # HG changeset patch # User Benjamin Peterson # Date 1243106677 0 # Node ID 150986ab3db284cfd38d67a1ace2f04c2780bae4 # Parent 54bbfd41be668dfc23c083510211accda7e54133 support building with subversion 1.7 #6094 diff --git a/Makefile.pre.in b/Makefile.pre.in --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -505,7 +505,7 @@ Modules/getbuildinfo.o: $(PARSER_OBJS) \ $(SIGNAL_OBJS) \ $(MODOBJS) \ $(srcdir)/Modules/getbuildinfo.c - $(CC) -c $(PY_CFLAGS) -DSVNVERSION=\"`LC_ALL=C $(SVNVERSION)`\" -o $@ $(srcdir)/Modules/getbuildinfo.c + $(CC) -c $(PY_CFLAGS) -DSVNVERSION="\"`LC_ALL=C $(SVNVERSION)`\"" -o $@ $(srcdir)/Modules/getbuildinfo.c Modules/getpath.o: $(srcdir)/Modules/getpath.c Makefile $(CC) -c $(PY_CFLAGS) -DPYTHONPATH='"$(PYTHONPATH)"' \ diff --git a/Modules/getbuildinfo.c b/Modules/getbuildinfo.c --- a/Modules/getbuildinfo.c +++ b/Modules/getbuildinfo.c @@ -48,5 +48,5 @@ const char * static const char svnversion[] = SVNVERSION; if (svnversion[0] != '$') return svnversion; /* it was interpolated, or passed on command line */ - return "exported"; + return "Unversioned directory"; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1158,7 +1158,7 @@ svnversion_init(void) svnversion = _Py_svnversion(); - if (strcmp(svnversion, "exported") != 0) + if (strcmp(svnversion, "Unversioned directory") != 0 && strcmp(svnversion, "exported") != 0) svn_revision = svnversion; else if (istag) { len = strlen(_patchlevel_revision); diff --git a/configure b/configure --- a/configure +++ b/configure @@ -4385,7 +4385,7 @@ if test $SVNVERSION = found then SVNVERSION="svnversion \$(srcdir)" else - SVNVERSION="echo exported" + SVNVERSION="echo Unversioned directory" fi case $MACHDEP in diff --git a/configure.in b/configure.in --- a/configure.in +++ b/configure.in @@ -802,7 +802,7 @@ if test $SVNVERSION = found then SVNVERSION="svnversion \$(srcdir)" else - SVNVERSION="echo exported" + SVNVERSION="echo Unversioned directory" fi case $MACHDEP in pycurl-7.19.3/tests/matrix/python30.patch0000644000470500047050000000123712256232314017671 0ustar piepie00000000000000Python 3.0's version.py uses cmp which is not defined by the interpreter. The only difference between version.py in 3.0 and 3.1 is this fix. --- Python-3.0.1/Lib/distutils/version.py 2013-12-14 21:06:23.000000000 -0500 +++ Python-3.1.5/Lib/distutils/version.py 2013-12-14 21:01:10.000000000 -0500 @@ -338,7 +338,12 @@ if isinstance(other, str): other = LooseVersion(other) - return cmp(self.version, other.version) + if self.version == other.version: + return 0 + if self.version < other.version: + return -1 + if self.version > other.version: + return 1 # end class LooseVersion pycurl-7.19.3/tests/__init__.py0000644000470500047050000000044512260666417016003 0ustar piepie00000000000000def setup_package(): # import here, not globally, so that running # python -m tests.appmanager # to launch the app manager is possible without having pycurl installed # (as the test app does not depend on pycurl) import pycurl print('Testing %s' % pycurl.version) pycurl-7.19.3/tests/app.py0000644000470500047050000000524412262015545015016 0ustar piepie00000000000000import time as _time, sys import bottle try: import json except ImportError: import simplejson as json py3 = sys.version_info[0] == 3 app = bottle.Bottle() app.debug = True @app.route('/success') def ok(): return 'success' @app.route('/short_wait') def ok(): _time.sleep(0.1) return 'success' @app.route('/status/403') def forbidden(): return bottle.HTTPResponse('forbidden', 403) @app.route('/status/404') def not_found(): return bottle.HTTPResponse('not found', 404) @app.route('/postfields', method='post') def postfields(): return json.dumps(dict(bottle.request.forms)) @app.route('/raw_utf8', method='post') def raw_utf8(): data = bottle.request.body.getvalue().decode('utf8') return json.dumps(data) # XXX file is not a bottle FileUpload instance, but FieldStorage? def convert_file(key, file): return { 'key': key, 'name': file.name, 'raw_filename': file.raw_filename, 'headers': file.headers, 'content_type': file.content_type, 'content_length': file.content_length, 'data': file.read(), } def convert_file(key, file): return { 'name': file.name, 'filename': file.filename, 'data': file.file.read().decode(), } @app.route('/files', method='post') def files(): files = [convert_file(key, bottle.request.files[key]) for key in bottle.request.files] return json.dumps(files) @app.route('/header') def header(): return bottle.request.headers[bottle.request.query['h']] # This is a hacky endpoint to test non-ascii text being given to libcurl # via headers. # HTTP RFC requires headers to be latin1-encoded. # Any string can be decoded as latin1; here we encode the header value # back into latin1 to obtain original bytestring, then decode it in utf-8. # Thanks to bdarnell for the idea: https://github.com/pycurl/pycurl/issues/124 @app.route('/header_utf8') def header(): header_value = bottle.request.headers[bottle.request.query['h']] if py3: # header_value is a string, headers are decoded in latin1 header_value = header_value.encode('latin1').decode('utf8') else: # header_value is a binary string, decode in utf-8 directly header_value = header_value.decode('utf8') return header_value @app.route('/param_utf8_hack', method='post') def param_utf8_hack(): param = bottle.request.forms['p'] if py3: # python 3 decodes bytes as latin1 perhaps? # apply the latin1-utf8 hack param = param.encode('latin').decode('utf8') return param def pause_writer(): yield 'part1' _time.sleep(0.5) yield 'part2' @app.route('/pause') def pause(): return pause_writer() pycurl-7.19.3/tests/appmanager.py0000644000470500047050000000252112260666417016354 0ustar piepie00000000000000import sys, time, os def noop(*args): pass def setup(*specs): if os.environ.get('PYCURL_STANDALONE_APP') and os.environ['PYCURL_STANDALONE_APP'].lower() in ['1', 'yes', 'true']: return (noop, noop) else: return perform_setup(*specs) def perform_setup(*specs): from . import runwsgi app_specs = [] for spec in specs: app_module = __import__(spec[0], globals(), locals(), ['app'], 1) app = getattr(app_module, 'app') app_specs.append([app] + list(spec[1:])) return runwsgi.app_runner_setup(*app_specs) quit = False def sigterm_handler(*args): global quit quit = True def run_standalone(): import signal funcs = [] signal.signal(signal.SIGTERM, sigterm_handler) funcs.append(setup(('app', 8380))) funcs.append(setup(('app', 8381))) funcs.append(setup(('app', 8382))) funcs.append(setup(('app', 8383, dict(ssl=True)))) for setup_func, teardown_func in funcs: setup_func(sys.modules[__name__]) sys.stdout.write("Running, use SIGTERM or SIGINT to stop\n") try: while not quit: time.sleep(1) except KeyboardInterrupt: pass for setup_func, teardown_func in funcs: teardown_func(sys.modules[__name__]) if __name__ == '__main__': run_standalone() pycurl-7.19.3/tests/certinfo_test.py0000644000470500047050000000440312262015545017102 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import nose.plugins.skip from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8383, dict(ssl=True))) class CertinfoTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() # CURLOPT_CERTINFO was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) def test_certinfo_option(self): assert hasattr(pycurl, 'OPT_CERTINFO') # CURLOPT_CERTINFO was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) @util.only_ssl def test_request_without_certinfo(self): self.curl.setopt(pycurl.URL, 'https://localhost:8383/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) # self signed certificate self.curl.setopt(pycurl.SSL_VERIFYPEER, 0) self.curl.perform() assert sio.getvalue().decode() == 'success' certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO) self.assertEqual([], certinfo) # CURLOPT_CERTINFO was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) @util.only_ssl def test_request_with_certinfo(self): # CURLOPT_CERTINFO only works with OpenSSL if 'openssl' not in pycurl.version.lower(): raise nose.plugins.skip.SkipTest('libcurl does not use openssl') self.curl.setopt(pycurl.URL, 'https://localhost:8383/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.setopt(pycurl.OPT_CERTINFO, 1) # self signed certificate self.curl.setopt(pycurl.SSL_VERIFYPEER, 0) self.curl.perform() assert sio.getvalue().decode() == 'success' certinfo = self.curl.getinfo(pycurl.INFO_CERTINFO) # self signed certificate, one certificate in chain assert len(certinfo) == 1 certinfo = certinfo[0] # convert to a dictionary certinfo_dict = {} for entry in certinfo: certinfo_dict[entry[0]] = entry[1] assert 'Subject' in certinfo_dict assert 'PycURL test suite' in certinfo_dict['Subject'] pycurl-7.19.3/tests/debug_test.py0000644000470500047050000000311112262015545016352 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class DebugTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() self.debug_entries = [] def tearDown(self): self.curl.close() def debug_function(self, t, b): self.debug_entries.append((t, b)) def test_perform_get_with_debug_function(self): self.curl.setopt(pycurl.VERBOSE, 1) self.curl.setopt(pycurl.DEBUGFUNCTION, self.debug_function) self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() # Some checks with no particular intent self.check(0, 'Trying') if util.pycurl_version_less_than(7, 24): self.check(0, 'connected') else: self.check(0, 'Connected to localhost') self.check(0, 'port 8380') # request self.check(2, 'GET /success HTTP/1.1') # response self.check(1, 'HTTP/1.0 200 OK') self.check(1, 'Content-Length: 7') # result self.check(3, 'success') def check(self, wanted_t, wanted_b): for t, b in self.debug_entries: if t == wanted_t and wanted_b in b: return assert False, "%d: %s not found in debug entries\nEntries are:\n%s" % \ (wanted_t, wanted_b, repr(self.debug_entries)) pycurl-7.19.3/tests/default_write_function_test.py0000644000470500047050000000573012262015545022040 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import unittest import pycurl import sys import tempfile import os from . import appmanager setup_module, teardown_module = appmanager.setup(('app', 8380)) STDOUT_FD_NUM = 1 def try_fsync(fd): try: os.fsync(fd) except OSError: # On travis: # OSError: [Errno 22] Invalid argument # ignore pass class DefaultWriteFunctionTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_perform_get(self): # This test performs a GET request without doing anything else. # Unfortunately, the default curl behavior is to print response # body to standard output, which spams test output. # As a result this test is commented out. Uncomment for debugging. # test_perform_get_with_default_write_function is the test # which exercises default curl write handler. self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') self.curl.perform() # If this flush is not done, stdout output bleeds into the next test # that is executed (without nose output capture) sys.stdout.flush() try_fsync(STDOUT_FD_NUM) # I have a really hard time getting this to work with nose output capture def skip_perform_get_with_default_write_function(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') f = tempfile.NamedTemporaryFile() try: #with open('w', 'w+') as f: # nose output capture plugin replaces sys.stdout with a StringIO # instance. We want to redirect the underlying file descriptor # anyway because underlying C code uses it. # Therefore: # 1. Use file descriptor 1 rather than sys.stdout.fileno() to # reference the standard output file descriptor. # 2. We do not touch sys.stdout. This means anything written to # sys.stdout will be captured by nose, and not make it to our code. # But the output we care about happens at libcurl level, below # nose, therefore this is fine. saved_stdout_fd = os.dup(STDOUT_FD_NUM) os.dup2(f.fileno(), STDOUT_FD_NUM) #os.dup2(1, 100) #os.dup2(2, 1) # We also need to flush the output that libcurl wrote to stdout. # Since sys.stdout might be nose's StringIO instance, open the # stdout file descriptor manually. try: self.curl.perform() sys.stdout.flush() finally: try_fsync(STDOUT_FD_NUM) os.dup2(saved_stdout_fd, STDOUT_FD_NUM) os.close(saved_stdout_fd) #os.dup2(100, 1) f.seek(0) body = f.read() finally: f.close() self.assertEqual('success', body) pycurl-7.19.3/tests/easy_test.py0000644000470500047050000000046212262015545016233 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest class EasyTest(unittest.TestCase): def test_easy_close(self): c = pycurl.Curl() c.close() def test_easy_close_twice(self): c = pycurl.Curl() c.close() c.close() pycurl-7.19.3/tests/error_test.py0000644000470500047050000000545212262015545016427 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import sys import unittest class ErrorTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() # error originating in libcurl def test_pycurl_error_libcurl(self): try: # perform without a url self.curl.perform() except pycurl.error: exc_type, exc = sys.exc_info()[:2] assert exc_type == pycurl.error # pycurl.error's arguments are libcurl errno and message self.assertEqual(2, len(exc.args)) self.assertEqual(int, type(exc.args[0])) self.assertEqual(str, type(exc.args[1])) # unpack err, msg = exc.args self.assertEqual(pycurl.E_URL_MALFORMAT, err) # possibly fragile self.assertEqual('No URL set!', msg) else: self.fail('Expected pycurl.error to be raised') def test_pycurl_errstr_initially_empty(self): self.assertEqual('', self.curl.errstr()) def test_pycurl_errstr_type(self): self.assertEqual('', self.curl.errstr()) try: # perform without a url self.curl.perform() except pycurl.error: # might be fragile self.assertEqual('No URL set!', self.curl.errstr()) # repeated checks do not clear value self.assertEqual('No URL set!', self.curl.errstr()) # check the type - on all python versions self.assertEqual(str, type(self.curl.errstr())) else: self.fail('no exception') # pycurl raises standard library exceptions in some cases def test_pycurl_error_stdlib(self): try: # set an option of the wrong type self.curl.setopt(pycurl.WRITEFUNCTION, True) except TypeError: exc_type, exc = sys.exc_info()[:2] else: self.fail('Expected TypeError to be raised') # error originating in pycurl def test_pycurl_error_pycurl(self): try: # invalid option combination self.curl.setopt(pycurl.WRITEFUNCTION, lambda x: x) f = open(__file__) try: self.curl.setopt(pycurl.WRITEHEADER, f) finally: f.close() except pycurl.error: exc_type, exc = sys.exc_info()[:2] assert exc_type == pycurl.error # for non-libcurl errors, arguments are just the error string self.assertEqual(1, len(exc.args)) self.assertEqual(str, type(exc.args[0])) self.assertEqual('cannot combine WRITEHEADER with WRITEFUNCTION.', exc.args[0]) else: self.fail('Expected pycurl.error to be raised') pycurl-7.19.3/tests/ftp_test.py0000644000470500047050000000300212262015545016054 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # Note: this test is meant to be run from pycurl project root. import pycurl import unittest from . import util from . import procmgr setup_module, teardown_module = procmgr.vsftpd_setup() class FtpTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_get_ftp(self): self.curl.setopt(pycurl.URL, 'ftp://localhost:8321') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() result = sio.getvalue().decode() assert 'README.rst' in result assert 'INSTALL' in result # XXX this test needs to be fixed def test_quote(self): self.curl.setopt(pycurl.URL, 'ftp://localhost:8321') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.setopt(pycurl.QUOTE, ['CWD tests']) self.curl.perform() result = sio.getvalue().decode() assert 'README.rst' not in result assert 'ftp_test.py' in result def test_epsv(self): self.curl.setopt(pycurl.URL, 'ftp://localhost:8321') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.setopt(pycurl.FTP_USE_EPSV, 1) self.curl.perform() result = sio.getvalue().decode() assert 'README.rst' in result assert 'INSTALL' in result pycurl-7.19.3/tests/functools_backport.py0000644000470500047050000000456512260666420020146 0ustar piepie00000000000000# partial implementation from # http://stackoverflow.com/questions/12274814/functools-wraps-for-python-2-4 def partial(func, *args, **kwds): """Emulate Python2.6's functools.partial""" return lambda *fargs, **fkwds: func(*(args+fargs), **dict(kwds, **fkwds)) # functools from python 2.5 """functools.py - Tools for working with functions and callable objects """ # Python module wrapper for _functools C module # to allow utilities written in Python to be added # to the functools module. # Written by Nick Coghlan # Copyright (C) 2006 Python Software Foundation. # See C source code for _functools credits/copyright # update_wrapper() and wraps() are tools to help write # wrapper functions that can handle naive introspection WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Update a wrapper function to look like the wrapped function wrapper is the function to be updated wrapped is the original function assigned is a tuple naming the attributes assigned directly from the wrapped function to the wrapper function (defaults to functools.WRAPPER_ASSIGNMENTS) updated is a tuple naming the attributes off the wrapper that are updated with the corresponding attribute from the wrapped function (defaults to functools.WRAPPER_UPDATES) """ for attr in assigned: setattr(wrapper, attr, getattr(wrapped, attr)) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Return the wrapper so this can be used as a decorator via partial() return wrapper def wraps(wrapped, assigned = WRAPPER_ASSIGNMENTS, updated = WRAPPER_UPDATES): """Decorator factory to apply update_wrapper() to a wrapper function Returns a decorator that invokes update_wrapper() with the decorated function as the wrapper argument and the arguments to wraps() as the remaining arguments. Default arguments are as for update_wrapper(). This is a convenience function to simplify applying partial() to update_wrapper(). """ return partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated) pycurl-7.19.3/tests/getinfo_test.py0000644000470500047050000000403212262540746016730 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class GetinfoTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_getinfo(self): self.make_request() self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE)) assert type(self.curl.getinfo(pycurl.TOTAL_TIME)) is float assert self.curl.getinfo(pycurl.TOTAL_TIME) > 0 assert self.curl.getinfo(pycurl.TOTAL_TIME) < 1 assert type(self.curl.getinfo(pycurl.SPEED_DOWNLOAD)) is float assert self.curl.getinfo(pycurl.SPEED_DOWNLOAD) > 0 self.assertEqual(7, self.curl.getinfo(pycurl.SIZE_DOWNLOAD)) self.assertEqual('http://localhost:8380/success', self.curl.getinfo(pycurl.EFFECTIVE_URL)) self.assertEqual('text/html; charset=utf-8', self.curl.getinfo(pycurl.CONTENT_TYPE).lower()) assert type(self.curl.getinfo(pycurl.NAMELOOKUP_TIME)) is float assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) > 0 assert self.curl.getinfo(pycurl.NAMELOOKUP_TIME) < 1 self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_TIME)) self.assertEqual(0, self.curl.getinfo(pycurl.REDIRECT_COUNT)) # time not requested self.assertEqual(-1, self.curl.getinfo(pycurl.INFO_FILETIME)) @util.min_libcurl(7, 21, 0) def test_primary_port_etc(self): self.make_request() assert type(self.curl.getinfo(pycurl.PRIMARY_PORT)) is int assert type(self.curl.getinfo(pycurl.LOCAL_IP)) is str assert type(self.curl.getinfo(pycurl.LOCAL_PORT)) is int def make_request(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() self.assertEqual('success', sio.getvalue().decode()) pycurl-7.19.3/tests/global_init_test.py0000644000470500047050000000204712262015545017556 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import nose.tools import nose.plugins.skip from . import util class GlobalInitTest(unittest.TestCase): def test_global_init_default(self): # initialize libcurl with DEFAULT flags pycurl.global_init(pycurl.GLOBAL_DEFAULT) pycurl.global_cleanup() def test_global_init_ack_eintr(self): # the GLOBAL_ACK_EINTR flag was introduced in libcurl-7.30, but can also # be backported for older versions of libcurl at the distribution level if util.pycurl_version_less_than(7, 30) and not hasattr(pycurl, 'GLOBAL_ACK_EINTR'): raise nose.plugins.skip.SkipTest('libcurl < 7.30.0 or no GLOBAL_ACK_EINTR') # initialize libcurl with the GLOBAL_ACK_EINTR flag pycurl.global_init(pycurl.GLOBAL_ACK_EINTR) pycurl.global_cleanup() @nose.tools.raises(ValueError) def test_global_init_bogus(self): # initialize libcurl with bogus flags pycurl.global_init(0xffff) pycurl-7.19.3/tests/header_function_test.py0000644000470500047050000000302412262015545020424 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import time as _time from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class HeaderFunctionTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() self.header_lines = [] def tearDown(self): self.curl.close() def header_function(self, line): self.header_lines.append(line.decode()) def test_get(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.setopt(pycurl.HEADERFUNCTION, self.header_function) self.curl.perform() self.assertEqual('success', sio.getvalue().decode()) assert len(self.header_lines) > 0 self.assertEqual("HTTP/1.0 200 OK\r\n", self.header_lines[0]) # day of week # important: must be in utc todays_day = _time.strftime('%a', _time.gmtime()) # Date: Sun, 03 Mar 2013 05:38:12 GMT\r\n self.check('Date: %s' % todays_day) # Server: WSGIServer/0.1 Python/2.7.3\r\n self.check('Server: WSGIServer') self.check('Content-Length: 7') self.check('Content-Type: text/html') def check(self, wanted_text): for line in self.header_lines: if wanted_text in line: return assert False, "%s not found in header lines" % wanted_text pycurl-7.19.3/tests/header_test.py0000644000470500047050000000315412262015545016523 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import nose.tools from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) # NB: HTTP RFC requires headers to be latin1 encoded, which we violate. # See the comments under /header_utf8 route in app.py. class HeaderTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_ascii_string_header(self): self.check('x-test-header: ascii', 'ascii') def test_ascii_unicode_header(self): self.check(util.u('x-test-header: ascii'), 'ascii') # on python 2 unicode is accepted in strings because strings are byte strings @util.only_python3 @nose.tools.raises(UnicodeEncodeError) def test_unicode_string_header(self): self.check('x-test-header: Москва', 'Москва') @nose.tools.raises(UnicodeEncodeError) def test_unicode_unicode_header(self): self.check(util.u('x-test-header: Москва'), util.u('Москва')) def test_encoded_unicode_header(self): self.check(util.u('x-test-header: Москва').encode('utf-8'), util.u('Москва')) def check(self, send, expected): self.curl.setopt(pycurl.URL, 'http://localhost:8380/header_utf8?h=x-test-header') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.setopt(pycurl.HTTPHEADER, [send]) self.curl.perform() self.assertEqual(expected, sio.getvalue().decode('utf-8')) pycurl-7.19.3/tests/internals_test.py0000644000470500047050000001141612262015545017272 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest try: import cPickle except ImportError: cPickle = None import pickle import copy from . import util class InternalsTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() del self.curl # /*********************************************************************** # // test misc # ************************************************************************/ def test_constant_aliasing(self): assert self.curl.URL is pycurl.URL # /*********************************************************************** # // test handles # ************************************************************************/ def test_remove_invalid_handle(self): m = pycurl.CurlMulti() try: m.remove_handle(self.curl) except pycurl.error: pass else: assert False, "No exception when trying to remove a handle that is not in CurlMulti" del m def test_remove_invalid_closed_handle(self): m = pycurl.CurlMulti() c = pycurl.Curl() c.close() m.remove_handle(c) del m, c def test_add_closed_handle(self): m = pycurl.CurlMulti() c = pycurl.Curl() c.close() try: m.add_handle(c) except pycurl.error: pass else: assert 0, "No exception when trying to add a close handle to CurlMulti" m.close() del m, c def test_add_handle_twice(self): m = pycurl.CurlMulti() m.add_handle(self.curl) try: m.add_handle(self.curl) except pycurl.error: pass else: assert 0, "No exception when trying to add the same handle twice" del m def test_add_handle_on_multiple_stacks(self): m1 = pycurl.CurlMulti() m2 = pycurl.CurlMulti() m1.add_handle(self.curl) try: m2.add_handle(self.curl) except pycurl.error: pass else: assert 0, "No exception when trying to add the same handle on multiple stacks" del m1, m2 def test_move_handle(self): m1 = pycurl.CurlMulti() m2 = pycurl.CurlMulti() m1.add_handle(self.curl) m1.remove_handle(self.curl) m2.add_handle(self.curl) del m1, m2 # /*********************************************************************** # // test copying and pickling - copying and pickling of # // instances of Curl and CurlMulti is not allowed # ************************************************************************/ def test_copy_curl(self): try: copy.copy(self.curl) # python 2 raises copy.Error, python 3 raises TypeError except (copy.Error, TypeError): pass else: assert False, "No exception when trying to copy a Curl handle" def test_copy_multi(self): m = pycurl.CurlMulti() try: copy.copy(m) except (copy.Error, TypeError): pass else: assert False, "No exception when trying to copy a CurlMulti handle" def test_pickle_curl(self): fp = util.StringIO() p = pickle.Pickler(fp, 1) try: p.dump(self.curl) # python 2 raises pickle.PicklingError, python 3 raises TypeError except (pickle.PicklingError, TypeError): pass else: assert 0, "No exception when trying to pickle a Curl handle" del fp, p def test_pickle_multi(self): m = pycurl.CurlMulti() fp = util.StringIO() p = pickle.Pickler(fp, 1) try: p.dump(m) except (pickle.PicklingError, TypeError): pass else: assert 0, "No exception when trying to pickle a CurlMulti handle" del m, fp, p if cPickle is not None: def test_cpickle_curl(self): fp = util.StringIO() p = cPickle.Pickler(fp, 1) try: p.dump(self.curl) except cPickle.PicklingError: pass else: assert 0, "No exception when trying to pickle a Curl handle via cPickle" del fp, p def test_cpickle_multi(self): m = pycurl.CurlMulti() fp = util.StringIO() p = cPickle.Pickler(fp, 1) try: p.dump(m) except cPickle.PicklingError: pass else: assert 0, "No exception when trying to pickle a CurlMulti handle via cPickle" del m, fp, p pycurl-7.19.3/tests/matrix.py0000644000470500047050000001745312260666420015551 0ustar piepie00000000000000import os, os.path, subprocess, shutil, re try: from urllib.request import urlopen except ImportError: from urllib import urlopen python_versions = ['2.4.6', '2.5.6', '2.6.8', '2.7.5', '3.0.1', '3.1.5', '3.2.5', '3.3.3'] libcurl_versions = ['7.19.0', '7.33.0'] python_meta = { '2.5.6': { 'patches': ['python25.patch'], }, '3.0.1': { 'patches': ['python25.patch', 'python30.patch'], }, } root = os.path.abspath(os.path.dirname(__file__)) class in_dir: def __init__(self, dir): self.dir = dir def __enter__(self): self.oldwd = os.getcwd() os.chdir(self.dir) def __exit__(self, type, value, traceback): os.chdir(self.oldwd) def fetch(url, archive=None): if archive is None: archive = os.path.basename(url) if not os.path.exists(archive): sys.stdout.write("Fetching %s\n" % url) io = urlopen(url) with open('.tmp.%s' % archive, 'wb') as f: while True: chunk = io.read(65536) if len(chunk) == 0: break f.write(chunk) os.rename('.tmp.%s' % archive, archive) def build(archive, dir, prefix, meta=None): if not os.path.exists(dir): sys.stdout.write("Building %s\n" % archive) subprocess.check_call(['tar', 'xf', archive]) with in_dir(dir): if meta and 'patches' in meta: for patch in meta['patches']: patch_path = os.path.join(root, 'matrix', patch) subprocess.check_call(['patch', '-p1', '-i', patch_path]) subprocess.check_call(['./configure', '--prefix=%s' % prefix]) subprocess.check_call(['make']) subprocess.check_call(['make', 'install']) def patch_pycurl_for_24(): # change relative imports to old syntax as python 2.4 does not # support relative imports for root, dirs, files in os.walk('tests'): for file in files: if file.endswith('.py'): path = os.path.join(root, file) with open(path, 'r') as f: contents = f.read() contents = re.compile(r'^(\s*)from \. import', re.M).sub(r'\1import', contents) contents = re.compile(r'^(\s*)from \.(\w+) import', re.M).sub(r'\1from \2 import', contents) with open(path, 'w') as f: f.write(contents) def run_matrix(python_versions, libcurl_versions): for python_version in python_versions: url = 'http://www.python.org/ftp/python/%s/Python-%s.tgz' % (python_version, python_version) archive = os.path.basename(url) fetch(url, archive) dir = archive.replace('.tgz', '') prefix = os.path.abspath('i/%s' % dir) build(archive, dir, prefix, meta=python_meta.get(python_version)) for libcurl_version in libcurl_versions: url = 'http://curl.haxx.se/download/curl-%s.tar.gz' % libcurl_version archive = os.path.basename(url) fetch(url, archive) dir = archive.replace('.tar.gz', '') prefix = os.path.abspath('i/%s' % dir) build(archive, dir, prefix) fetch('https://raw.github.com/pypa/virtualenv/1.7/virtualenv.py', 'virtualenv-1.7.py') fetch('https://raw.github.com/pypa/virtualenv/1.9.1/virtualenv.py', 'virtualenv-1.9.1.py') if not os.path.exists('venv'): os.mkdir('venv') for python_version in python_versions: python_version_pieces = [int(piece) for piece in python_version.split('.')[:2]] for libcurl_version in libcurl_versions: python_prefix = os.path.abspath('i/Python-%s' % python_version) libcurl_prefix = os.path.abspath('i/curl-%s' % libcurl_version) venv = os.path.abspath('venv/Python-%s-curl-%s' % (python_version, libcurl_version)) if os.path.exists(venv): shutil.rmtree(venv) if python_version_pieces >= [2, 5]: fetch('https://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c11-py2.5.egg') fetch('https://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg') fetch('https://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg') # I had virtualenv 1.8.2 installed systemwide which # did not work with python 3.0: # http://stackoverflow.com/questions/14224361/why-am-i-getting-this-error-related-to-pip-and-easy-install-when-trying-to-set # so, use known versions everywhere # md5=89e68df89faf1966bcbd99a0033fbf8e fetch('https://pypi.python.org/packages/source/d/distribute/distribute-0.6.49.tar.gz') subprocess.check_call(['python', 'virtualenv-1.9.1.py', venv, '-p', '%s/bin/python%d.%d' % (python_prefix, python_version_pieces[0], python_version_pieces[1]), '--no-site-packages', '--never-download']) else: # md5=bd639f9b0eac4c42497034dec2ec0c2b fetch('https://pypi.python.org/packages/2.4/s/setuptools/setuptools-0.6c11-py2.4.egg') # md5=6afbb46aeb48abac658d4df742bff714 fetch('https://pypi.python.org/packages/source/p/pip/pip-1.4.1.tar.gz') subprocess.check_call(['python', 'virtualenv-1.7.py', venv, '-p', '%s/bin/python' % python_prefix, '--no-site-packages', '--never-download']) curl_config_path = os.path.join(libcurl_prefix, 'bin/curl-config') curl_lib_path = os.path.join(libcurl_prefix, 'lib') with in_dir('pycurl'): extra_patches = [] extra_env = [] if python_version_pieces >= [2, 6]: deps_cmd = 'pip install -r requirements-dev.txt' elif python_version_pieces >= [2, 5]: deps_cmd = 'pip install -r requirements-dev-2.5.txt' else: deps_cmd = 'easy_install nose simplejson==2.1.0' patch_pycurl_for_24() extra_patches.append('(cd %s/lib/python2.4/site-packages/nose-* && patch -p1) 1 and sys.argv[1] == 'patch-24': patch_pycurl_for_24() else: main() pycurl-7.19.3/tests/memory_mgmt_test.py0000644000470500047050000001267012262015545017632 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import gc debug = False class MemoryMgmtTest(unittest.TestCase): def maybe_enable_debug(self): if debug: flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE # python 3 has no DEBUG_OBJECTS if hasattr(gc, 'DEBUG_OBJECTS'): flags |= gc.DEBUG_OBJECTS flags |= gc.DEBUG_STATS gc.set_debug(flags) gc.collect() print("Tracked objects:", len(gc.get_objects())) def maybe_print_objects(self): if debug: print("Tracked objects:", len(gc.get_objects())) def tearDown(self): gc.set_debug(0) def test_multi_collection(self): gc.collect() self.maybe_enable_debug() multi = pycurl.CurlMulti() t = [] searches = [] for a in range(100): curl = pycurl.Curl() multi.add_handle(curl) t.append(curl) c_id = id(curl) searches.append(c_id) m_id = id(multi) searches.append(m_id) self.maybe_print_objects() for curl in t: curl.close() multi.remove_handle(curl) self.maybe_print_objects() del curl del t del multi self.maybe_print_objects() gc.collect() self.maybe_print_objects() objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) def test_multi_cycle(self): gc.collect() self.maybe_enable_debug() multi = pycurl.CurlMulti() t = [] searches = [] for a in range(100): curl = pycurl.Curl() multi.add_handle(curl) t.append(curl) c_id = id(curl) searches.append(c_id) m_id = id(multi) searches.append(m_id) self.maybe_print_objects() del curl del t del multi self.maybe_print_objects() gc.collect() self.maybe_print_objects() objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) def test_share_collection(self): gc.collect() self.maybe_enable_debug() share = pycurl.CurlShare() t = [] searches = [] for a in range(100): curl = pycurl.Curl() curl.setopt(curl.SHARE, share) t.append(curl) c_id = id(curl) searches.append(c_id) m_id = id(share) searches.append(m_id) self.maybe_print_objects() for curl in t: curl.unsetopt(curl.SHARE) curl.close() self.maybe_print_objects() del curl del t del share self.maybe_print_objects() gc.collect() self.maybe_print_objects() objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) def test_share_cycle(self): gc.collect() self.maybe_enable_debug() share = pycurl.CurlShare() t = [] searches = [] for a in range(100): curl = pycurl.Curl() curl.setopt(curl.SHARE, share) t.append(curl) c_id = id(curl) searches.append(c_id) m_id = id(share) searches.append(m_id) self.maybe_print_objects() del curl del t del share self.maybe_print_objects() gc.collect() self.maybe_print_objects() objects = gc.get_objects() for search in searches: for object in objects: assert search != id(object) # basic check of reference counting (use a memory checker like valgrind) def test_reference_counting(self): c = pycurl.Curl() m = pycurl.CurlMulti() m.add_handle(c) del m m = pycurl.CurlMulti() c.close() del m, c def test_cyclic_gc(self): gc.collect() c = pycurl.Curl() c.m = pycurl.CurlMulti() c.m.add_handle(c) # create some nasty cyclic references c.c = c c.c.c1 = c c.c.c2 = c c.c.c3 = c.c c.c.c4 = c.m c.m.c = c c.m.m = c.m c.m.c = c # delete gc.collect() self.maybe_enable_debug() ##print gc.get_referrers(c) ##print gc.get_objects() #if opts.verbose >= 1: #print("Tracked objects:", len(gc.get_objects())) c_id = id(c) # The `del' below should delete these 4 objects: # Curl + internal dict, CurlMulti + internal dict del c gc.collect() objects = gc.get_objects() for object in objects: assert id(object) != c_id #if opts.verbose >= 1: #print("Tracked objects:", len(gc.get_objects())) def test_refcounting_bug_in_reset(self): try: range_generator = xrange except NameError: range_generator = range # Ensure that the refcounting error in "reset" is fixed: for i in range_generator(100000): c = pycurl.Curl() c.reset() pycurl-7.19.3/tests/multi_socket_select_test.py0000644000470500047050000000736012262015545021337 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import select from . import appmanager from . import util setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380)) setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381)) setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382)) def setup_module(mod): setup_module_1(mod) setup_module_2(mod) setup_module_3(mod) def teardown_module(mod): teardown_module_3(mod) teardown_module_2(mod) teardown_module_1(mod) class MultiSocketSelectTest(unittest.TestCase): def test_multi_socket_select(self): sockets = set() timeout = 0 urls = [ # we need libcurl to actually wait on the handles, # and initiate polling. # thus use urls that sleep for a bit. 'http://localhost:8380/short_wait', 'http://localhost:8381/short_wait', 'http://localhost:8382/short_wait', ] socket_events = [] # socket callback def socket(event, socket, multi, data): if event == pycurl.POLL_REMOVE: #print("Remove Socket %d"%socket) sockets.remove(socket) else: if socket not in sockets: #print("Add socket %d"%socket) sockets.add(socket) socket_events.append((event, multi)) # init m = pycurl.CurlMulti() m.setopt(pycurl.M_SOCKETFUNCTION, socket) m.handles = [] for url in urls: c = pycurl.Curl() # save info in standard Python attributes c.url = url c.body = util.BytesIO() c.http_code = -1 m.handles.append(c) # pycurl API calls c.setopt(c.URL, c.url) c.setopt(c.WRITEFUNCTION, c.body.write) m.add_handle(c) # get data num_handles = len(m.handles) while (pycurl.E_CALL_MULTI_PERFORM==m.socket_all()[0]): pass timeout = m.timeout() # timeout might be -1, indicating that all work is done # XXX make sure there is always work to be done here? while timeout >= 0: (rr, wr, er) = select.select(sockets,sockets,sockets,timeout/1000.0) socketSet = set(rr+wr+er) if socketSet: for s in socketSet: while True: (ret,running) = m.socket_action(s,0) if ret!=pycurl.E_CALL_MULTI_PERFORM: break else: (ret,running) = m.socket_action(pycurl.SOCKET_TIMEOUT,0) if running==0: break for c in m.handles: # save info in standard Python attributes c.http_code = c.getinfo(c.HTTP_CODE) # at least in and remove events per socket assert len(socket_events) >= 6, 'Less than 6 socket events: %s' % repr(socket_events) # print result for c in m.handles: self.assertEqual('success', c.body.getvalue().decode()) self.assertEqual(200, c.http_code) # multi, not curl handle self.check(pycurl.POLL_IN, m, socket_events) self.check(pycurl.POLL_REMOVE, m, socket_events) # close handles for c in m.handles: # pycurl API calls m.remove_handle(c) c.close() m.close() def check(self, event, multi, socket_events): for event_, multi_ in socket_events: if event == event_ and multi == multi_: return assert False, '%d %s not found in socket events' % (event, multi) pycurl-7.19.3/tests/multi_socket_test.py0000644000470500047050000000572512262015545020003 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest from . import appmanager from . import util setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380)) setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381)) setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382)) def setup_module(mod): setup_module_1(mod) setup_module_2(mod) setup_module_3(mod) def teardown_module(mod): teardown_module_3(mod) teardown_module_2(mod) teardown_module_1(mod) class MultiSocketTest(unittest.TestCase): def test_multi_socket(self): urls = [ # not sure why requesting /success produces no events. # see multi_socket_select_test.py for a longer explanation # why short wait is used there. 'http://localhost:8380/short_wait', 'http://localhost:8381/short_wait', 'http://localhost:8382/short_wait', ] socket_events = [] # socket callback def socket(event, socket, multi, data): #print(event, socket, multi, data) socket_events.append((event, multi)) # init m = pycurl.CurlMulti() m.setopt(pycurl.M_SOCKETFUNCTION, socket) m.handles = [] for url in urls: c = pycurl.Curl() # save info in standard Python attributes c.url = url c.body = util.BytesIO() c.http_code = -1 m.handles.append(c) # pycurl API calls c.setopt(c.URL, c.url) c.setopt(c.WRITEFUNCTION, c.body.write) m.add_handle(c) # get data num_handles = len(m.handles) while num_handles: while 1: ret, num_handles = m.socket_all() if ret != pycurl.E_CALL_MULTI_PERFORM: break # currently no more I/O is pending, could do something in the meantime # (display a progress bar, etc.) m.select(0.1) for c in m.handles: # save info in standard Python attributes c.http_code = c.getinfo(c.HTTP_CODE) # at least in and remove events per socket assert len(socket_events) >= 6 # print result for c in m.handles: self.assertEqual('success', c.body.getvalue().decode()) self.assertEqual(200, c.http_code) # multi, not curl handle self.check(pycurl.POLL_IN, m, socket_events) self.check(pycurl.POLL_REMOVE, m, socket_events) # close handles for c in m.handles: # pycurl API calls m.remove_handle(c) c.close() m.close() def check(self, event, multi, socket_events): for event_, multi_ in socket_events: if event == event_ and multi == multi_: return assert False, '%d %s not found in socket events' % (event, multi) pycurl-7.19.3/tests/multi_test.py0000644000470500047050000002724112262015545016430 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import select from . import appmanager from . import util setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380)) setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381)) setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382)) def setup_module(mod): setup_module_1(mod) setup_module_2(mod) setup_module_3(mod) def teardown_module(mod): teardown_module_3(mod) teardown_module_2(mod) teardown_module_1(mod) class MultiTest(unittest.TestCase): def test_multi(self): io1 = util.BytesIO() io2 = util.BytesIO() m = pycurl.CurlMulti() handles = [] c1 = pycurl.Curl() c2 = pycurl.Curl() c1.setopt(c1.URL, 'http://localhost:8380/success') c1.setopt(c1.WRITEFUNCTION, io1.write) c2.setopt(c2.URL, 'http://localhost:8381/success') c2.setopt(c1.WRITEFUNCTION, io2.write) m.add_handle(c1) m.add_handle(c2) handles.append(c1) handles.append(c2) num_handles = len(handles) while num_handles: while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break m.select(1.0) m.remove_handle(c2) m.remove_handle(c1) m.close() c1.close() c2.close() self.assertEqual('success', io1.getvalue().decode()) self.assertEqual('success', io2.getvalue().decode()) def test_multi_select_fdset(self): c1 = pycurl.Curl() c2 = pycurl.Curl() c3 = pycurl.Curl() c1.setopt(c1.URL, "http://localhost:8380/success") c2.setopt(c2.URL, "http://localhost:8381/success") c3.setopt(c3.URL, "http://localhost:8382/success") c1.body = util.BytesIO() c2.body = util.BytesIO() c3.body = util.BytesIO() c1.setopt(c1.WRITEFUNCTION, c1.body.write) c2.setopt(c2.WRITEFUNCTION, c2.body.write) c3.setopt(c3.WRITEFUNCTION, c3.body.write) m = pycurl.CurlMulti() m.add_handle(c1) m.add_handle(c2) m.add_handle(c3) # Number of seconds to wait for a timeout to happen SELECT_TIMEOUT = 0.1 # Stir the state machine into action while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Keep going until all the connections have terminated while num_handles: select.select(*m.fdset() + (SELECT_TIMEOUT,)) while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Cleanup m.remove_handle(c3) m.remove_handle(c2) m.remove_handle(c1) m.close() c1.close() c2.close() c3.close() self.assertEqual('success', c1.body.getvalue().decode()) self.assertEqual('success', c2.body.getvalue().decode()) self.assertEqual('success', c3.body.getvalue().decode()) def test_multi_status_codes(self): # init m = pycurl.CurlMulti() m.handles = [] urls = [ 'http://localhost:8380/success', 'http://localhost:8381/status/403', 'http://localhost:8382/status/404', ] for url in urls: c = pycurl.Curl() # save info in standard Python attributes c.url = url.rstrip() c.body = util.BytesIO() c.http_code = -1 m.handles.append(c) # pycurl API calls c.setopt(c.URL, c.url) c.setopt(c.WRITEFUNCTION, c.body.write) m.add_handle(c) # get data num_handles = len(m.handles) while num_handles: while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # currently no more I/O is pending, could do something in the meantime # (display a progress bar, etc.) m.select(0.1) # close handles for c in m.handles: # save info in standard Python attributes c.http_code = c.getinfo(c.HTTP_CODE) # pycurl API calls m.remove_handle(c) c.close() m.close() # check result self.assertEqual('success', m.handles[0].body.getvalue().decode()) self.assertEqual(200, m.handles[0].http_code) # bottle generated response body self.assertEqual('forbidden', m.handles[1].body.getvalue().decode()) self.assertEqual(403, m.handles[1].http_code) # bottle generated response body self.assertEqual('not found', m.handles[2].body.getvalue().decode()) self.assertEqual(404, m.handles[2].http_code) def check_adding_closed_handle(self, close_fn): # init m = pycurl.CurlMulti() m.handles = [] urls = [ 'http://localhost:8380/success', 'http://localhost:8381/status/403', 'http://localhost:8382/status/404', ] for url in urls: c = pycurl.Curl() # save info in standard Python attributes c.url = url c.body = util.BytesIO() c.http_code = -1 c.debug = 0 m.handles.append(c) # pycurl API calls c.setopt(c.URL, c.url) c.setopt(c.WRITEFUNCTION, c.body.write) m.add_handle(c) # debug - close a handle c = m.handles[2] c.debug = 1 c.close() # get data num_handles = len(m.handles) while num_handles: while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # currently no more I/O is pending, could do something in the meantime # (display a progress bar, etc.) m.select(0.1) # close handles for c in m.handles: # save info in standard Python attributes try: c.http_code = c.getinfo(c.HTTP_CODE) except pycurl.error: # handle already closed - see debug above assert c.debug c.http_code = -1 # pycurl API calls close_fn(m, c) m.close() # check result self.assertEqual('success', m.handles[0].body.getvalue().decode()) self.assertEqual(200, m.handles[0].http_code) # bottle generated response body self.assertEqual('forbidden', m.handles[1].body.getvalue().decode()) self.assertEqual(403, m.handles[1].http_code) # bottle generated response body self.assertEqual('', m.handles[2].body.getvalue().decode()) self.assertEqual(-1, m.handles[2].http_code) def _remove_then_close(self, m, c): m.remove_handle(c) c.close() def _close_then_remove(self, m, c): # in the C API this is the wrong calling order, but pycurl # handles this automatically c.close() m.remove_handle(c) def _close_without_removing(self, m, c): # actually, remove_handle is called automatically on close c.close def test_adding_closed_handle_remove_then_close(self): self.check_adding_closed_handle(self._remove_then_close) def test_adding_closed_handle_close_then_remove(self): self.check_adding_closed_handle(self._close_then_remove) def test_adding_closed_handle_close_without_removing(self): self.check_adding_closed_handle(self._close_without_removing) def test_multi_select(self): c1 = pycurl.Curl() c2 = pycurl.Curl() c3 = pycurl.Curl() c1.setopt(c1.URL, "http://localhost:8380/success") c2.setopt(c2.URL, "http://localhost:8381/success") c3.setopt(c3.URL, "http://localhost:8382/success") c1.body = util.BytesIO() c2.body = util.BytesIO() c3.body = util.BytesIO() c1.setopt(c1.WRITEFUNCTION, c1.body.write) c2.setopt(c2.WRITEFUNCTION, c2.body.write) c3.setopt(c3.WRITEFUNCTION, c3.body.write) m = pycurl.CurlMulti() m.add_handle(c1) m.add_handle(c2) m.add_handle(c3) # Number of seconds to wait for a timeout to happen SELECT_TIMEOUT = 1.0 # Stir the state machine into action while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Keep going until all the connections have terminated while num_handles: # The select method uses fdset internally to determine which file descriptors # to check. m.select(SELECT_TIMEOUT) while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Cleanup m.remove_handle(c3) m.remove_handle(c2) m.remove_handle(c1) m.close() c1.close() c2.close() c3.close() self.assertEqual('success', c1.body.getvalue().decode()) self.assertEqual('success', c2.body.getvalue().decode()) self.assertEqual('success', c3.body.getvalue().decode()) def test_multi_info_read(self): c1 = pycurl.Curl() c2 = pycurl.Curl() c3 = pycurl.Curl() c1.setopt(c1.URL, "http://localhost:8380/short_wait") c2.setopt(c2.URL, "http://localhost:8381/short_wait") c3.setopt(c3.URL, "http://localhost:8382/short_wait") c1.body = util.BytesIO() c2.body = util.BytesIO() c3.body = util.BytesIO() c1.setopt(c1.WRITEFUNCTION, c1.body.write) c2.setopt(c2.WRITEFUNCTION, c2.body.write) c3.setopt(c3.WRITEFUNCTION, c3.body.write) m = pycurl.CurlMulti() m.add_handle(c1) m.add_handle(c2) m.add_handle(c3) # Number of seconds to wait for a timeout to happen SELECT_TIMEOUT = 1.0 # Stir the state machine into action while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break infos = [] # Keep going until all the connections have terminated while num_handles: # The select method uses fdset internally to determine which file descriptors # to check. m.select(SELECT_TIMEOUT) while 1: ret, num_handles = m.perform() info = m.info_read() infos.append(info) if ret != pycurl.E_CALL_MULTI_PERFORM: break all_handles = [] for info in infos: handles = info[1] # last info is an empty array if handles: all_handles.extend(handles) self.assertEqual(3, len(all_handles)) assert c1 in all_handles assert c2 in all_handles assert c3 in all_handles # Cleanup m.remove_handle(c3) m.remove_handle(c2) m.remove_handle(c1) m.close() c1.close() c2.close() c3.close() self.assertEqual('success', c1.body.getvalue().decode()) self.assertEqual('success', c2.body.getvalue().decode()) self.assertEqual('success', c3.body.getvalue().decode()) def test_multi_close(self): m = pycurl.CurlMulti() m.close() def test_multi_close_twice(self): m = pycurl.CurlMulti() m.close() m.close() pycurl-7.19.3/tests/multi_timer_test.py0000644000470500047050000000515312262015545017626 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest from . import appmanager from . import util setup_module_1, teardown_module_1 = appmanager.setup(('app', 8380)) setup_module_2, teardown_module_2 = appmanager.setup(('app', 8381)) setup_module_3, teardown_module_3 = appmanager.setup(('app', 8382)) def setup_module(mod): setup_module_1(mod) setup_module_2(mod) setup_module_3(mod) def teardown_module(mod): teardown_module_3(mod) teardown_module_2(mod) teardown_module_1(mod) class MultiSocketTest(unittest.TestCase): def test_multi_timer(self): urls = [ 'http://localhost:8380/success', 'http://localhost:8381/success', 'http://localhost:8382/success', ] timers = [] # timer callback def timer(msecs): #print('Timer callback msecs:', msecs) timers.append(msecs) # init m = pycurl.CurlMulti() m.setopt(pycurl.M_TIMERFUNCTION, timer) m.handles = [] for url in urls: c = pycurl.Curl() # save info in standard Python attributes c.url = url c.body = util.BytesIO() c.http_code = -1 m.handles.append(c) # pycurl API calls c.setopt(c.URL, c.url) c.setopt(c.WRITEFUNCTION, c.body.write) m.add_handle(c) # get data num_handles = len(m.handles) while num_handles: while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # currently no more I/O is pending, could do something in the meantime # (display a progress bar, etc.) m.select(1.0) for c in m.handles: # save info in standard Python attributes c.http_code = c.getinfo(c.HTTP_CODE) # print result for c in m.handles: self.assertEqual('success', c.body.getvalue().decode()) self.assertEqual(200, c.http_code) assert len(timers) > 0 # libcurl 7.23.0 produces a 0 timer assert timers[0] >= 0 # this assertion does not appear to hold on older libcurls # or apparently on any linuxes, see # https://github.com/p/pycurl/issues/19 #if not util.pycurl_version_less_than(7, 24): # self.assertEqual(-1, timers[-1]) # close handles for c in m.handles: # pycurl API calls m.remove_handle(c) c.close() m.close() pycurl-7.19.3/tests/option_constants_test.py0000644000470500047050000000525612262560234020704 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import nose.plugins.skip from . import util class OptionConstantsTest(unittest.TestCase): # CURLOPT_USERNAME was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) def test_username(self): assert hasattr(pycurl, 'USERNAME') assert hasattr(pycurl, 'PASSWORD') assert hasattr(pycurl, 'PROXYUSERNAME') assert hasattr(pycurl, 'PROXYPASSWORD') # CURLOPT_DNS_SERVERS was introduced in libcurl-7.24.0 @util.min_libcurl(7, 24, 0) def test_dns_servers(self): assert hasattr(pycurl, 'DNS_SERVERS') # Does not work unless libcurl was built against c-ares #c = pycurl.Curl() #c.setopt(c.DNS_SERVERS, '1.2.3.4') #c.close() # CURLOPT_POSTREDIR was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) def test_postredir(self): assert hasattr(pycurl, 'POSTREDIR') assert hasattr(pycurl, 'REDIR_POST_301') assert hasattr(pycurl, 'REDIR_POST_302') assert hasattr(pycurl, 'REDIR_POST_ALL') # CURLOPT_POSTREDIR was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) def test_postredir_setopt(self): curl = pycurl.Curl() curl.setopt(curl.POSTREDIR, curl.REDIR_POST_301) curl.close() # CURL_REDIR_POST_303 was introduced in libcurl-7.26.0 @util.min_libcurl(7, 26, 0) def test_redir_post_303(self): assert hasattr(pycurl, 'REDIR_POST_303') # CURLOPT_POSTREDIR was introduced in libcurl-7.19.1 @util.min_libcurl(7, 19, 1) def test_postredir_flags(self): self.assertEqual(pycurl.REDIR_POST_301, pycurl.REDIR_POST_ALL & pycurl.REDIR_POST_301) self.assertEqual(pycurl.REDIR_POST_302, pycurl.REDIR_POST_ALL & pycurl.REDIR_POST_302) # CURL_REDIR_POST_303 was introduced in libcurl-7.26.0 @util.min_libcurl(7, 26, 0) def test_postredir_flags(self): self.assertEqual(pycurl.REDIR_POST_303, pycurl.REDIR_POST_ALL & pycurl.REDIR_POST_303) # HTTPAUTH_DIGEST_IE was introduced in libcurl-7.19.3 @util.min_libcurl(7, 19, 3) def test_httpauth_digest_ie(self): assert hasattr(pycurl, 'HTTPAUTH_DIGEST_IE') # CURLE_OPERATION_TIMEDOUT was introduced in libcurl-7.10.2 # to replace CURLE_OPERATION_TIMEOUTED def test_operation_timedout_constant(self): self.assertEqual(pycurl.E_OPERATION_TIMEDOUT, pycurl.E_OPERATION_TIMEOUTED) # CURLOPT_NOPROXY was introduced in libcurl-7.19.4 @util.min_libcurl(7, 19, 4) def test_noproxy_setopt(self): curl = pycurl.Curl() curl.setopt(curl.NOPROXY, 'localhost') curl.close() pycurl-7.19.3/tests/pause_test.py0000644000470500047050000000562412262015545016414 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest, signal import time as _time from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class PauseTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_pause_via_call(self): self.check_pause(True) def test_pause_via_return(self): self.check_pause(False) def check_pause(self, call): # the app sleeps for 0.5 seconds self.curl.setopt(pycurl.URL, 'http://localhost:8380/pause') sio = util.BytesIO() state = dict(paused=False, resumed=False) if call: def writefunc(data): rv = sio.write(data) if not state['paused']: self.curl.pause(pycurl.PAUSE_ALL) state['paused'] = True return rv else: def writefunc(data): if not state['paused']: # cannot write to sio here, because # curl takes pause return value to mean that # nothing was written state['paused'] = True return pycurl.READFUNC_PAUSE else: return sio.write(data) def resume(*args): state['resumed'] = True self.curl.pause(pycurl.PAUSE_CONT) signal.signal(signal.SIGALRM, resume) # alarm for 1 second which is 0.5 seconds more than the server side # should sleep for signal.alarm(1) start = _time.time() self.curl.setopt(pycurl.WRITEFUNCTION, writefunc) m = pycurl.CurlMulti() m.add_handle(self.curl) # Number of seconds to wait for a timeout to happen SELECT_TIMEOUT = 1.0 # Stir the state machine into action while 1: ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Keep going until all the connections have terminated while num_handles: # The select method uses fdset internally to determine which file descriptors # to check. m.select(SELECT_TIMEOUT) while 1: if _time.time() - start > 2: # test is taking too long, fail assert False, 'Test is taking too long' ret, num_handles = m.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break # Cleanup m.remove_handle(self.curl) m.close() self.assertEqual('part1part2', sio.getvalue().decode()) end = _time.time() # check that client side waited self.assertTrue(end-start > 1) assert state['resumed'] pycurl-7.19.3/tests/post_test.py0000644000470500047050000001101312262015545016251 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import os.path import pycurl import unittest try: import json except ImportError: import simplejson as json try: import urllib.parse as urllib_parse except ImportError: import urllib as urllib_parse from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class PostTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_post_single_field(self): pf = {'field1': 'value1'} self.urlencode_and_check(pf) def test_post_multiple_fields(self): pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'} self.urlencode_and_check(pf) def test_post_fields_with_ampersand(self): pf = {'field1':'value1', 'field2':'value2 with blanks and & chars', 'field3':'value3'} self.urlencode_and_check(pf) def urlencode_and_check(self, pf): self.curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') postfields = urllib_parse.urlencode(pf) self.curl.setopt(pycurl.POSTFIELDS, postfields) # But directly passing urlencode result into setopt call: #self.curl.setopt(pycurl.POSTFIELDS, urllib_parse.urlencode(pf)) # produces: # {'\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00': ''} # Traceback (most recent call last): # File "/usr/local/bin/bottle.py", line 744, in _handle # return route.call(**args) # File "/usr/local/bin/bottle.py", line 1479, in wrapper # rv = callback(*a, **ka) # File "/home/pie/apps/pycurl/tests/app.py", line 21, in postfields # return json.dumps(dict(bottle.request.forms)) # File "/usr/local/lib/python2.7/json/__init__.py", line 231, in dumps # return _default_encoder.encode(obj) # File "/usr/local/lib/python2.7/json/encoder.py", line 201, in encode # chunks = self.iterencode(o, _one_shot=True) # File "/usr/local/lib/python2.7/json/encoder.py", line 264, in iterencode # return _iterencode(o, 0) # UnicodeDecodeError: 'utf8' codec can't decode byte 0x80 in position 4: invalid start byte #self.curl.setopt(pycurl.VERBOSE, 1) sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE)) body = sio.getvalue().decode() returned_fields = json.loads(body) self.assertEqual(pf, returned_fields) def test_post_with_null_byte(self): send = [ ('field3', (pycurl.FORM_CONTENTS, 'this is wei\000rd, but null-bytes are okay')) ] expect = { 'field3': 'this is wei\000rd, but null-bytes are okay', } self.check_post(send, expect, 'http://localhost:8380/postfields') def test_post_file(self): path = os.path.join(os.path.dirname(__file__), '..', 'README.rst') f = open(path) try: contents = f.read() finally: f.close() send = [ #('field2', (pycurl.FORM_FILE, 'test_post.py', pycurl.FORM_FILE, 'test_post2.py')), ('field2', (pycurl.FORM_FILE, path)), ] expect = [{ 'name': 'field2', 'filename': 'README.rst', 'data': contents, }] self.check_post(send, expect, 'http://localhost:8380/files') def test_post_buffer(self): contents = 'hello, world!' send = [ ('field2', (pycurl.FORM_BUFFER, 'uploaded.file', pycurl.FORM_BUFFERPTR, contents)), ] expect = [{ 'name': 'field2', 'filename': 'uploaded.file', 'data': contents, }] self.check_post(send, expect, 'http://localhost:8380/files') # XXX this test takes about a second to run, check keep-alives? def check_post(self, send, expect, endpoint): self.curl.setopt(pycurl.URL, endpoint) self.curl.setopt(pycurl.HTTPPOST, send) #self.curl.setopt(pycurl.VERBOSE, 1) sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() self.assertEqual(200, self.curl.getinfo(pycurl.HTTP_CODE)) body = sio.getvalue().decode() returned_fields = json.loads(body) self.assertEqual(expect, returned_fields) pycurl-7.19.3/tests/procmgr.py0000644000470500047050000000507412260666420015712 0ustar piepie00000000000000import threading import subprocess import os import sys import signal import nose.plugins.skip from . import util class ProcessManager(object): def __init__(self, cmd): self.cmd = cmd self.running = False def start(self): self.process = subprocess.Popen(self.cmd) self.running = True self.thread = threading.Thread(target=self.run) self.thread.daemon = True self.thread.start() def run(self): self.process.communicate() def stop(self): try: os.kill(self.process.pid, signal.SIGTERM) except OSError: pass self.running = False managers = {} def start(cmd): if str(cmd) in managers and managers[str(cmd)].running: # already started return manager = ProcessManager(cmd) managers[str(cmd)] = manager manager.start() def start_setup(cmd): def do_start(): start(cmd) return do_start # Example on FreeBSD: # PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd nosetests if 'PYCURL_VSFTPD_PATH' in os.environ: vsftpd_path = os.environ['PYCURL_VSFTPD_PATH'] else: vsftpd_path = None try: # python 2 exception_base = StandardError except NameError: # python 3 exception_base = Exception class VsftpdNotConfigured(exception_base): pass def vsftpd_setup(): config_file_path = os.path.join(os.path.dirname(__file__), 'vsftpd.conf') root_path = os.path.join(os.path.dirname(__file__), '..') cmd = [ vsftpd_path, config_file_path, '-oanon_root=%s' % root_path, ] setup_module = start_setup(cmd) def do_setup_module(): if vsftpd_path is None: raise nose.plugins.skip.SkipTest('PYCURL_VSFTPD_PATH environment variable not set') try: setup_module() except OSError: import errno e = sys.exc_info()[1] if e.errno == errno.ENOENT: msg = "Tried to execute `%s`\nTry specifying path to vsftpd via PYCURL_VSFTPD_PATH environment variable\n" % vsftpd_path raise OSError(e.errno, e.strerror + "\n" + msg) else: raise ok = util.wait_for_network_service(('127.0.0.1', 8321), 0.1, 10) if not ok: import warnings warnings.warn('vsftpd did not start after 1 second') def teardown_module(): try: manager = managers[str(cmd)] except KeyError: pass else: manager.stop() return do_setup_module, teardown_module pycurl-7.19.3/tests/pycurl_object_test.py0000644000470500047050000001057112262015545020140 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import sys class PycurlObjectTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_set_attribute_curl(self): self.instantiate_and_check(self.check_set_attribute, 'Curl') def test_get_attribute_curl(self): self.instantiate_and_check(self.check_get_attribute, 'Curl') def test_get_missing_attribute_curl(self): self.instantiate_and_check(self.check_get_missing_attribute, 'Curl') def test_delete_attribute_curl(self): self.instantiate_and_check(self.check_delete_attribute, 'Curl') def test_delete_missing_attribute_curl(self): self.instantiate_and_check(self.check_delete_missing_attribute, 'Curl') def test_set_attribute_multi(self): self.instantiate_and_check(self.check_set_attribute, 'CurlMulti') def test_get_attribute_multi(self): self.instantiate_and_check(self.check_get_attribute, 'CurlMulti') def test_get_missing_attribute_curl(self): self.instantiate_and_check(self.check_get_missing_attribute, 'CurlMulti') def test_delete_attribute_multi(self): self.instantiate_and_check(self.check_delete_attribute, 'CurlMulti') def test_delete_missing_attribute_curl(self): self.instantiate_and_check(self.check_delete_missing_attribute, 'CurlMulti') def test_set_attribute_share(self): self.instantiate_and_check(self.check_set_attribute, 'CurlShare') def test_get_attribute_share(self): self.instantiate_and_check(self.check_get_attribute, 'CurlShare') def test_get_missing_attribute_curl(self): self.instantiate_and_check(self.check_get_missing_attribute, 'CurlShare') def test_delete_attribute_share(self): self.instantiate_and_check(self.check_delete_attribute, 'CurlShare') def test_delete_missing_attribute_curl(self): self.instantiate_and_check(self.check_delete_missing_attribute, 'CurlShare') def instantiate_and_check(self, fn, cls_name): cls = getattr(pycurl, cls_name) instance = cls() try: fn(instance) finally: instance.close() def check_set_attribute(self, pycurl_obj): assert not hasattr(pycurl_obj, 'attr') pycurl_obj.attr = 1 assert hasattr(pycurl_obj, 'attr') def check_get_attribute(self, pycurl_obj): assert not hasattr(pycurl_obj, 'attr') pycurl_obj.attr = 1 self.assertEqual(1, pycurl_obj.attr) def check_get_missing_attribute(self, pycurl_obj): try: getattr(pycurl_obj, 'doesnotexist') self.fail('Expected an AttributeError exception to be raised') except AttributeError: pass def check_delete_attribute(self, pycurl_obj): assert not hasattr(pycurl_obj, 'attr') pycurl_obj.attr = 1 self.assertEqual(1, pycurl_obj.attr) assert hasattr(pycurl_obj, 'attr') del pycurl_obj.attr assert not hasattr(pycurl_obj, 'attr') def check_delete_missing_attribute(self, pycurl_obj): try: del pycurl_obj.doesnotexist self.fail('Expected an AttributeError exception to be raised') except AttributeError: pass def test_modify_attribute_curl(self): self.check_modify_attribute(pycurl.Curl, 'READFUNC_PAUSE') def test_modify_attribute_multi(self): self.check_modify_attribute(pycurl.CurlMulti, 'E_MULTI_OK') def test_modify_attribute_share(self): self.check_modify_attribute(pycurl.CurlShare, 'SH_SHARE') def check_modify_attribute(self, cls, name): obj1 = cls() obj2 = cls() old_value = getattr(obj1, name) self.assertNotEqual('helloworld', old_value) # value should be identical to pycurl global self.assertEqual(old_value, getattr(pycurl, name)) setattr(obj1, name, 'helloworld') self.assertEqual('helloworld', getattr(obj1, name)) # change does not affect other existing objects self.assertEqual(old_value, getattr(obj2, name)) # change does not affect objects created later obj3 = cls() self.assertEqual(old_value, getattr(obj3, name)) pycurl-7.19.3/tests/read_callback_test.py0000644000470500047050000001043612262015545020023 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import sys try: import json except ImportError: import simplejson as json try: import urllib.parse as urllib_parse except ImportError: import urllib as urllib_parse from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) POSTFIELDS = { 'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3', } POSTSTRING = urllib_parse.urlencode(POSTFIELDS) class DataProvider(object): def __init__(self, data): self.data = data self.finished = False def read_cb(self, size): assert len(self.data) <= size if not self.finished: self.finished = True return self.data else: # Nothing more to read return "" class ReadCallbackTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_post_with_read_callback(self): d = DataProvider(POSTSTRING) self.curl.setopt(self.curl.URL, 'http://localhost:8380/postfields') self.curl.setopt(self.curl.POST, 1) self.curl.setopt(self.curl.POSTFIELDSIZE, len(POSTSTRING)) self.curl.setopt(self.curl.READFUNCTION, d.read_cb) #self.curl.setopt(self.curl.VERBOSE, 1) sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() actual = json.loads(sio.getvalue().decode()) self.assertEqual(POSTFIELDS, actual) def test_post_with_read_callback_returning_bytes(self): self.check_bytes('world') def test_post_with_read_callback_returning_bytes_with_nulls(self): self.check_bytes("wor\0ld") def test_post_with_read_callback_returning_bytes_with_multibyte(self): self.check_bytes(util.u("Пушкин")) def check_bytes(self, poststring): data = poststring.encode('utf8') assert type(data) == util.binary_type d = DataProvider(data) self.curl.setopt(self.curl.URL, 'http://localhost:8380/raw_utf8') self.curl.setopt(self.curl.POST, 1) self.curl.setopt(self.curl.HTTPHEADER, ['Content-Type: application/octet-stream']) # length of bytes self.curl.setopt(self.curl.POSTFIELDSIZE, len(data)) self.curl.setopt(self.curl.READFUNCTION, d.read_cb) #self.curl.setopt(self.curl.VERBOSE, 1) sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() # json should be ascii actual = json.loads(sio.getvalue().decode('ascii')) self.assertEqual(poststring, actual) def test_post_with_read_callback_returning_unicode(self): self.check_unicode(util.u('world')) def test_post_with_read_callback_returning_unicode_with_nulls(self): self.check_unicode(util.u("wor\0ld")) def test_post_with_read_callback_returning_unicode_with_multibyte(self): try: self.check_unicode(util.u("Пушкин")) # prints: # UnicodeEncodeError: 'ascii' codec can't encode characters in position 6-11: ordinal not in range(128) except pycurl.error: err, msg = sys.exc_info()[1].args # we expect pycurl.E_WRITE_ERROR as the response self.assertEqual(pycurl.E_ABORTED_BY_CALLBACK, err) self.assertEqual('operation aborted by callback', msg) def check_unicode(self, poststring): assert type(poststring) == util.text_type d = DataProvider(poststring) self.curl.setopt(self.curl.URL, 'http://localhost:8380/raw_utf8') self.curl.setopt(self.curl.POST, 1) self.curl.setopt(self.curl.HTTPHEADER, ['Content-Type: application/octet-stream']) self.curl.setopt(self.curl.POSTFIELDSIZE, len(poststring)) self.curl.setopt(self.curl.READFUNCTION, d.read_cb) #self.curl.setopt(self.curl.VERBOSE, 1) sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() # json should be ascii actual = json.loads(sio.getvalue().decode('ascii')) self.assertEqual(poststring, actual) pycurl-7.19.3/tests/relative_url_test.py0000644000470500047050000000101212262015545017757 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # uses the high level interface import curl import unittest from . import appmanager setup_module, teardown_module = appmanager.setup(('app', 8380)) class RelativeUrlTest(unittest.TestCase): def setUp(self): self.curl = curl.Curl('http://localhost:8380/') def tearDown(self): self.curl.close() def test_get_relative(self): self.curl.get('/success') self.assertEqual('success', self.curl.body().decode()) pycurl-7.19.3/tests/reset_test.py0000644000470500047050000000372212262015545016416 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest try: import urllib.parse as urllib_parse except ImportError: import urllib as urllib_parse from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class ResetTest(unittest.TestCase): # XXX this test was broken when it was test_reset.py def skip_reset(self): outf = util.BytesIO() cm = pycurl.CurlMulti() eh = pycurl.Curl() for x in range(1, 20): eh.setopt(pycurl.WRITEFUNCTION, outf.write) eh.setopt(pycurl.URL, 'http://localhost:8380/success') cm.add_handle(eh) while 1: ret, active_handles = cm.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break while active_handles: ret = cm.select(1.0) if ret == -1: continue while 1: ret, active_handles = cm.perform() if ret != pycurl.E_CALL_MULTI_PERFORM: break count, good, bad = cm.info_read() for h, en, em in bad: print("Transfer to %s failed with %d, %s\n" % \ (h.getinfo(pycurl.EFFECTIVE_URL), en, em)) raise RuntimeError for h in good: httpcode = h.getinfo(pycurl.RESPONSE_CODE) if httpcode != 200: print("Transfer to %s failed with code %d\n" %\ (h.getinfo(pycurl.EFFECTIVE_URL), httpcode)) raise RuntimeError else: print("Recd %d bytes from %s" % \ (h.getinfo(pycurl.SIZE_DOWNLOAD), h.getinfo(pycurl.EFFECTIVE_URL))) cm.remove_handle(eh) eh.reset() eh.close() cm.close() outf.close() pycurl-7.19.3/tests/resolve_test.py0000644000470500047050000000142712262015545016753 0ustar piepie00000000000000# -*- coding: utf-8 -*- import pycurl import unittest import nose.plugins.skip from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class ResolveTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_resolve(self): if util.pycurl_version_less_than(7, 21, 3) and not hasattr(pycurl, 'RESOLVE'): raise nose.plugins.skip.SkipTest('libcurl < 7.21.3 or no RESOLVE') self.curl.setopt(pycurl.URL, 'http://p.localhost:8380/success') self.curl.setopt(pycurl.RESOLVE, ['p.localhost:8380:127.0.0.1']) self.curl.perform() self.assertEqual(200, self.curl.getinfo(pycurl.RESPONSE_CODE)) pycurl-7.19.3/tests/runwsgi.py0000644000470500047050000001003312260666420015726 0ustar piepie00000000000000# Run a WSGI application in a daemon thread import sys import bottle import threading import os.path from . import util global_stop = False class Server(bottle.WSGIRefServer): def run(self, handler): # pragma: no cover from wsgiref.simple_server import make_server, WSGIRequestHandler if self.quiet: base = self.options.get('handler_class', WSGIRequestHandler) class QuietHandler(base): def log_request(*args, **kw): pass self.options['handler_class'] = QuietHandler self.srv = make_server(self.host, self.port, handler, **self.options) if sys.version_info[0] == 2 and sys.version_info[1] < 6: # python 2.5 has no poll_interval # and thus no way to stop the server while not global_stop: self.srv.handle_request() else: self.srv.serve_forever(poll_interval=0.1) class SslServer(bottle.CherryPyServer): def run(self, handler): import cherrypy.wsgiserver, cherrypy.wsgiserver.ssl_builtin server = cherrypy.wsgiserver.CherryPyWSGIServer((self.host, self.port), handler) cert_dir = os.path.join(os.path.dirname(__file__), 'certs') ssl_adapter = cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter( os.path.join(cert_dir, 'server.crt'), os.path.join(cert_dir, 'server.key'), ) server.ssl_adapter = ssl_adapter try: server.start() finally: server.stop() def start_bottle_server(app, port, server, **kwargs): server_thread = ServerThread(app, port, server, kwargs) server_thread.daemon = True server_thread.start() ok = util.wait_for_network_service(('127.0.0.1', port), 0.1, 10) if not ok: import warnings warnings.warn('Server did not start after 1 second') return server_thread.server class ServerThread(threading.Thread): def __init__(self, app, port, server, server_kwargs): threading.Thread.__init__(self) self.app = app self.port = port self.server_kwargs = server_kwargs self.server = server(host='127.0.0.1', port=self.port, **self.server_kwargs) def run(self): bottle.run(self.app, server=self.server, quiet=True) started_servers = {} def app_runner_setup(*specs): '''Returns setup and teardown methods for running a list of WSGI applications in a daemon thread. Each argument is an (app, port) pair. Return value is a (setup, teardown) function pair. The setup and teardown functions expect to be called with an argument on which server state will be stored. Example usage with nose: >>> setup_module, teardown_module = \ runwsgi.app_runner_setup((app_module.app, 8050)) ''' def setup(self): self.servers = [] for spec in specs: if len(spec) == 2: app, port = spec kwargs = {} else: app, port, kwargs = spec if port in started_servers: assert started_servers[port] == (app, kwargs) else: server = Server if 'server' in kwargs: server = kwargs['server'] del kwargs['server'] elif 'ssl' in kwargs: if kwargs['ssl']: server = SslServer del kwargs['ssl'] self.servers.append(start_bottle_server(app, port, server, **kwargs)) started_servers[port] = (app, kwargs) def teardown(self): return for server in self.servers: # if no tests from module were run, there is no server to shut down if hasattr(server, 'srv'): if hasattr(server.srv, 'shutdown'): server.srv.shutdown() else: # python 2.5 global global_stop global_stop = True return [setup, teardown] pycurl-7.19.3/tests/seek_function_test.py0000644000470500047050000000431512262015545020127 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et # Note: this test is meant to be run from pycurl project root. import pycurl import unittest import os.path from . import procmgr setup_module, teardown_module = procmgr.vsftpd_setup() class PartialFileSource: def __init__(self): self.__buf = '1234567890.1234567890' self.__maxread = None self.__bufptr = 0 def read(self, size): p = self.__bufptr end = p+size if self.__maxread: end = min(self.__maxread, end) ret = self.__buf[p:end] self.__bufptr+= len(ret) #print 20*">>>", "read(%s) ==> %s" % (size, len(ret)) return ret def seek(self, offset, origin): #print 20*">>>", "seek(%s, %s)" % (offset, origin) self.__bufptr = offset def set_maxread(self, maxread): self.__maxread = maxread class SeekFunctionTest(unittest.TestCase): def test_seek_function(self): c = pycurl.Curl() c.setopt(pycurl.UPLOAD, 1) c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt") c.setopt(pycurl.RESUME_FROM, 0) #c.setopt(pycurl.VERBOSE, 1) upload_file = PartialFileSource() c.setopt(pycurl.READFUNCTION, upload_file.read) upload_file.set_maxread(10) c.perform() f = open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt')) try: content = f.read() finally: f.close() self.assertEqual('1234567890', content) c.close() del c del upload_file c = pycurl.Curl() c.setopt(pycurl.URL, "ftp://localhost:8321/tests/tmp/upload.txt") c.setopt(pycurl.RESUME_FROM, -1) c.setopt(pycurl.UPLOAD, 1) #c.setopt(pycurl.VERBOSE, 1) upload_file = PartialFileSource() c.setopt(pycurl.READFUNCTION, upload_file.read) c.setopt(pycurl.SEEKFUNCTION, upload_file.seek) c.perform() c.close() f = open(os.path.join(os.path.dirname(__file__), 'tmp', 'upload.txt')) try: content = f.read() finally: f.close() self.assertEqual('1234567890.1234567890', content) pycurl-7.19.3/tests/setopt_lifecycle_test.py0000644000470500047050000000340612262015545020630 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import gc import os.path import pycurl import unittest try: import json except ImportError: import simplejson as json from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class TestString(str): def __del__(self): self.replace('1', '2') #print self #print 'd' class SetoptLifecycleTest(unittest.TestCase): # separate method to permit pf to go out of scope and be # garbage collected before perform call def do_setopt(self, curl, index): pf = TestString('&'.join(50*['field=value%d' % (index,)])) curl.setopt(pycurl.URL, 'http://localhost:8380/postfields') curl.setopt(pycurl.POSTFIELDS, pf) # This test takes 6+ seconds to run. # It seems to pass with broken pycurl code when run by itself, # but fails when run as part of the entire test suite. def test_postfields_lifecycle(self): requests = [] for i in range(1000): curl = pycurl.Curl() self.do_setopt(curl, i) gc.collect() requests.append(curl) # send requests here to permit maximum garbage recycling for i in range(100): curl = requests[i] #self.curl.setopt(pycurl.VERBOSE, 1) sio = util.BytesIO() curl.setopt(pycurl.WRITEFUNCTION, sio.write) curl.perform() self.assertEqual(200, curl.getinfo(pycurl.HTTP_CODE)) body = sio.getvalue().decode() returned_fields = json.loads(body) self.assertEqual(dict(field='value%d' % i), returned_fields) for i in range(100): curl = requests[i] curl.close() pycurl-7.19.3/tests/setopt_unicode_test.py0000644000470500047050000000217512262015545020321 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import nose.tools try: import json except ImportError: import simplejson as json from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class SetoptUnicodeTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_ascii_string(self): self.check('p=test', 'test') @nose.tools.raises(UnicodeEncodeError) def test_unicode_string(self): self.check(util.u('p=Москва'), util.u('Москва')) def test_unicode_encoded(self): self.check(util.u('p=Москва').encode('utf8'), util.u('Москва')) def check(self, send, expected): self.curl.setopt(pycurl.URL, 'http://localhost:8380/param_utf8_hack') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.setopt(pycurl.POSTFIELDS, send) self.curl.perform() self.assertEqual(expected, sio.getvalue().decode('utf-8')) pycurl-7.19.3/tests/share_test.py0000644000470500047050000000276112262015545016400 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import threading import pycurl import unittest try: import urllib.parse as urllib_parse except ImportError: import urllib as urllib_parse from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class WorkerThread(threading.Thread): def __init__(self, share): threading.Thread.__init__(self) self.curl = pycurl.Curl() self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') self.curl.setopt(pycurl.SHARE, share) self.sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, self.sio.write) def run(self): self.curl.perform() self.curl.close() class ShareTest(unittest.TestCase): def test_share(self): s = pycurl.CurlShare() s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_COOKIE) s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_DNS) s.setopt(pycurl.SH_SHARE, pycurl.LOCK_DATA_SSL_SESSION) t1 = WorkerThread(s) t2 = WorkerThread(s) t1.start() t2.start() t1.join() t2.join() del s self.assertEqual('success', t1.sio.getvalue().decode()) self.assertEqual('success', t2.sio.getvalue().decode()) def test_share_close(self): s = pycurl.CurlShare() s.close() def test_share_close_twice(self): s = pycurl.CurlShare() s.close() s.close() pycurl-7.19.3/tests/socket_open_test.py0000644000470500047050000000247212262015545017606 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import socket import pycurl import unittest try: import urllib.parse as urllib_parse except ImportError: import urllib as urllib_parse from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) socket_open_called = False socket_open_address = None def socket_open(family, socktype, protocol, address): global socket_open_called global socket_open_address socket_open_called = True socket_open_address = address #print(family, socktype, protocol, address) s = socket.socket(family, socktype, protocol) s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) return s class SocketOpenTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_socket_open(self): self.curl.setopt(pycurl.OPENSOCKETFUNCTION, socket_open) self.curl.setopt(self.curl.URL, 'http://localhost:8380/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() assert socket_open_called self.assertEqual(("127.0.0.1", 8380), socket_open_address) self.assertEqual('success', sio.getvalue().decode()) pycurl-7.19.3/tests/unset_range_test.py0000644000470500047050000000270512262015545017606 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import os.path import pycurl import sys import unittest class UnsetRangeTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_unset_range(self): def write_cb(data): self.read += len(data) return None # download bytes 0-9 of the script itself through the file:// protocol self.read = 0 self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) self.curl.setopt(pycurl.RANGE, '0-9') self.curl.perform() assert 10 == self.read # the RANGE setting should be preserved from the previous transfer self.read = 0 self.curl.perform() assert 10 == self.read # drop the RANGE setting using unsetopt() and download entire script self.read = 0 self.curl.unsetopt(pycurl.RANGE) self.curl.perform() assert 10 < self.read # now set the RANGE again and check that pycurl takes it into account self.read = 0 self.curl.setopt(pycurl.RANGE, '0-9') self.curl.perform() assert 10 == self.read # now drop the RANGE setting using setopt(..., None) self.read = 0 self.curl.setopt(pycurl.RANGE, None) self.curl.perform() assert 10 < self.read pycurl-7.19.3/tests/user_agent_string_test.py0000644000470500047050000000143212262015545021012 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import unittest import pycurl from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class UserAgentStringTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_pycurl_user_agent_string(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/header?h=user-agent') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() user_agent = sio.getvalue().decode() assert user_agent.startswith('PycURL/') assert 'libcurl/' in user_agent, 'User agent did not include libcurl/: %s' % user_agent pycurl-7.19.3/tests/util.py0000644000470500047050000001024012262015545015203 0ustar piepie00000000000000# -*- coding: utf-8 -*- # vi:ts=4:et import os, sys, socket import time as _time try: import functools except ImportError: import functools_backport as functools py3 = sys.version_info[0] == 3 # python 2/3 compatibility if py3: from io import StringIO, BytesIO # borrowed from six def b(s): '''Byte literal''' return s.encode("latin-1") def u(s): '''Text literal''' return s text_type = str binary_type = bytes else: try: from cStringIO import StringIO except ImportError: from StringIO import StringIO BytesIO = StringIO # borrowed from six def b(s): '''Byte literal''' return s # Workaround for standalone backslash def u(s): '''Text literal''' return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") text_type = unicode binary_type = str def version_less_than_spec(version_tuple, spec_tuple): # spec_tuple may have 2 elements, expect version_tuple to have 3 elements assert len(version_tuple) >= len(spec_tuple) for i in range(len(spec_tuple)): if version_tuple[i] < spec_tuple[i]: return True if version_tuple[i] > spec_tuple[i]: return False return False def pycurl_version_less_than(*spec): import pycurl version = [int(part) for part in pycurl.version_info()[1].split('.')] return version_less_than_spec(version, spec) def only_python3(fn): import nose.plugins.skip @functools.wraps(fn) def decorated(*args, **kwargs): if sys.version_info[0] < 3: raise nose.plugins.skip.SkipTest('python < 3') return fn(*args, **kwargs) return decorated def min_libcurl(major, minor, patch): import nose.plugins.skip def decorator(fn): @functools.wraps(fn) def decorated(*args, **kwargs): if pycurl_version_less_than(major, minor, patch): raise nose.plugins.skip.SkipTest('libcurl < %d.%d.%d' % (major, minor, patch)) return fn(*args, **kwargs) return decorated return decorator def only_ssl(fn): import nose.plugins.skip import pycurl @functools.wraps(fn) def decorated(*args, **kwargs): # easier to check that pycurl supports https, although # theoretically it is not the same test. # pycurl.version_info()[8] is a tuple of protocols supported by libcurl if 'https' not in pycurl.version_info()[8]: raise nose.plugins.skip.SkipTest('libcurl does not support ssl') return fn(*args, **kwargs) return decorated try: create_connection = socket.create_connection except AttributeError: # python 2.5 def create_connection(netloc, timeout=None): # XXX ipv4 only s = socket.socket() if timeout is not None: s.settimeout(timeout) s.connect(netloc) return s def wait_for_network_service(netloc, check_interval, num_attempts): ok = False for i in range(num_attempts): try: conn = create_connection(netloc, check_interval) except socket.error: #e = sys.exc_info()[1] _time.sleep(check_interval) else: conn.close() ok = True break return ok # # prepare sys.path in case we are still in the build directory # see also: distutils/command/build.py (build_platlib) # def get_sys_path(p=None): if p is None: p = sys.path p = p[:] try: from distutils.util import get_platform except ImportError: return p p0 = "" if p: p0 = p[0] # plat = get_platform() plat_specifier = "%s-%s" % (plat, sys.version[:3]) ##print plat, plat_specifier # for prefix in (p0, os.curdir, os.pardir,): if not prefix: continue d = os.path.join(prefix, "build") for subdir in ("lib", "lib." + plat_specifier, "lib." + plat): dir = os.path.normpath(os.path.join(d, subdir)) if os.path.isdir(dir): if dir not in p: p.insert(1, dir) # return p pycurl-7.19.3/tests/version_comparison_test.py0000644000470500047050000000100212262015545021200 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import unittest from . import util class VersionComparisonTest(unittest.TestCase): def test_comparison(self): assert util.version_less_than_spec((7, 22, 0), (7, 23, 0)) assert util.version_less_than_spec((7, 22, 0), (7, 23)) assert util.version_less_than_spec((7, 22, 0), (7, 22, 1)) assert not util.version_less_than_spec((7, 22, 0), (7, 22, 0)) assert not util.version_less_than_spec((7, 22, 0), (7, 22)) pycurl-7.19.3/tests/version_test.py0000644000470500047050000000047112262015545016757 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import unittest import pycurl class VersionTest(unittest.TestCase): def test_pycurl_presence_and_case(self): assert pycurl.version.startswith('PycURL/') def test_libcurl_presence(self): assert 'libcurl/' in pycurl.version pycurl-7.19.3/tests/vsftpd.conf0000644000470500047050000000044612260666420016042 0ustar piepie00000000000000anon_world_readable_only=yes anonymous_enable=yes background=no # currently we only list files download_enable=no listen=yes run_as_launching_user=yes write_enable=yes anon_upload_enable=yes anon_other_write_enable=yes listen_port=8321 # should be supplied on command line anon_root=/var/empty pycurl-7.19.3/tests/write_abort_test.py0000644000470500047050000000223212262015545017610 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import os.path import pycurl import sys import unittest class WriteAbortTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_write_abort(self): def write_cb(_): # this should cause pycurl.WRITEFUNCTION (without any range errors) return -1 try: # set when running full test suite if any earlier tests # failed in Python code called from C del sys.last_value except AttributeError: pass # download the script itself through the file:// protocol into write_cb self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) try: self.curl.perform() except pycurl.error: err, msg = sys.exc_info()[1].args # we expect pycurl.E_WRITE_ERROR as the response assert pycurl.E_WRITE_ERROR == err # no additional errors should be reported assert not hasattr(sys, 'last_value') pycurl-7.19.3/tests/write_cb_bogus_test.py0000644000470500047050000000260312262015545020266 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import os.path import pycurl import sys import unittest class WriteAbortTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def write_cb_returning_string(self, data): return 'foo' def write_cb_returning_float(self, data): return 0.5 def test_write_cb_returning_string(self): self.check(self.write_cb_returning_string) def test_write_cb_returning_float(self): self.check(self.write_cb_returning_float) def check(self, write_cb): # download the script itself through the file:// protocol into write_cb c = pycurl.Curl() self.curl.setopt(pycurl.URL, 'file://' + os.path.abspath(sys.argv[0])) self.curl.setopt(pycurl.WRITEFUNCTION, write_cb) try: self.curl.perform() self.fail('Should not get here') except pycurl.error: err, msg = sys.exc_info()[1].args # we expect pycurl.E_WRITE_ERROR as the response assert pycurl.E_WRITE_ERROR == err # actual error assert hasattr(sys, 'last_type') self.assertEqual(pycurl.error, sys.last_type) assert hasattr(sys, 'last_value') self.assertEqual('write callback must return int or None', str(sys.last_value)) pycurl-7.19.3/tests/write_to_file_test.py0000644000470500047050000000535312262015545020131 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import unittest import pycurl import tempfile import shutil import os.path from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class Acceptor(object): def __init__(self): self.buffer = '' def write(self, chunk): self.buffer += chunk.decode() class WriteToFileTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_write_to_tempfile_via_function(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') f = tempfile.NamedTemporaryFile() try: self.curl.setopt(pycurl.WRITEFUNCTION, f.write) self.curl.perform() f.seek(0) body = f.read() finally: f.close() self.assertEqual('success', body.decode()) @util.only_python3 def test_write_to_tempfile_via_object(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') f = tempfile.NamedTemporaryFile() try: self.curl.setopt(pycurl.WRITEDATA, f) self.curl.perform() f.seek(0) body = f.read() finally: f.close() self.assertEqual('success', body.decode()) def test_write_to_file_via_function(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') dir = tempfile.mkdtemp() try: path = os.path.join(dir, 'pycurltest') f = open(path, 'wb+') try: self.curl.setopt(pycurl.WRITEFUNCTION, f.write) self.curl.perform() f.seek(0) body = f.read() finally: f.close() finally: shutil.rmtree(dir) self.assertEqual('success', body.decode()) def test_write_to_file_via_object(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') dir = tempfile.mkdtemp() try: path = os.path.join(dir, 'pycurltest') f = open(path, 'wb+') try: self.curl.setopt(pycurl.WRITEDATA, f) self.curl.perform() f.seek(0) body = f.read() finally: f.close() finally: shutil.rmtree(dir) self.assertEqual('success', body.decode()) def test_write_to_file_like(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') acceptor = Acceptor() self.curl.setopt(pycurl.WRITEDATA, acceptor) self.curl.perform() self.assertEqual('success', acceptor.buffer) pycurl-7.19.3/tests/write_to_stringio_test.py0000644000470500047050000000227512262015545021050 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et import pycurl import unittest import sys from . import appmanager from . import util setup_module, teardown_module = appmanager.setup(('app', 8380)) class WriteToStringioTest(unittest.TestCase): def setUp(self): self.curl = pycurl.Curl() def tearDown(self): self.curl.close() def test_write_to_bytesio(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') sio = util.BytesIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) self.curl.perform() self.assertEqual('success', sio.getvalue().decode()) @util.only_python3 def test_write_to_stringio(self): self.curl.setopt(pycurl.URL, 'http://localhost:8380/success') # stringio in python 3 sio = util.StringIO() self.curl.setopt(pycurl.WRITEFUNCTION, sio.write) try: self.curl.perform() self.fail('Should have received a write error') except pycurl.error: err, msg = sys.exc_info()[1].args # we expect pycurl.E_WRITE_ERROR as the response assert pycurl.E_WRITE_ERROR == err pycurl-7.19.3/COPYING-LGPL0000644000470500047050000006363612256232314014320 0ustar piepie00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! pycurl-7.19.3/COPYING-MIT0000644000470500047050000000224512262777656014224 0ustar piepie00000000000000COPYRIGHT AND PERMISSION NOTICE Copyright (C) 2001-2008 by Kjetil Jacobsen Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer Copyright (C) 2013-2014 by Oleg Pudeyev All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. pycurl-7.19.3/ChangeLog0000644000470500047050000010536512263706003014276 0ustar piepie00000000000000Version 7.19.3 [requires libcurl-7.19.0 or better] - 2014-01-09 --------------------------------------------------------------- * Added CURLOPT_NOPROXY. * Added CURLINFO_LOCAL_PORT, CURLINFO_PRIMARY_PORT and CURLINFO_LOCAL_IP (patch by Adam Jacob Muller). * When running on Python 2.x, for compatibility with Python 3.x, Unicode strings containing ASCII code points only are now accepted in setopt() calls. * PycURL now requires that compile time SSL backend used by libcurl is the same as the one used at runtime. setup.py supports --with-ssl, --with-gnutls and --with-nss options like libcurl does, to specify which backend libcurl uses. On some systems PycURL can automatically figure out libcurl's backend. If the backend is not one for which PycURL provides crypto locks (i.e., any of the other backends supported by libcurl), no runtime SSL backend check is performed. * Default PycURL user agent string is now built at runtime, and will include the user agent string of libcurl loaded at runtime rather than the one present at compile time. * PycURL will now use WSAduplicateSocket rather than dup on Windows to duplicate sockets obtained from OPENSOCKETFUNCTION. Using dup may have caused crashes, OPENSOCKETFUNCTION should now be usable on Windows. * A new script, winbuild.py, was added to build PycURL on Windows against Python 2.6, 2.7, 3.2 and 3.3. * Added CURL_LOCK_DATA_SSL_SESSION (patch by Tom Pierce). * Added E_OPERATION_TIMEDOUT (patch by Romuald Brunet). * setup.py now handles --help argument and will print PycURL-specific configuration options in addition to distutils help. * Windows build configuration has been redone: PYCURL_USE_LIBCURL_DLL #define is gone, use --use-libcurl-dll argument to setup.py to build against a libcurl DLL. CURL_STATICLIB is now #defined only when --use-libcurl-dll is not given to setup.py, and PycURL is built against libcurl statically. --libcurl-lib-name option can be used to override libcurl import library name. * Added CURLAUTH_DIGEST_IE as pycurl.HTTPAUTH_DIGEST_IE. * Added CURLOPT_POSTREDIR option and CURL_REDIR_POST_301, CURL_REDIR_POST_302, CURL_REDIR_POST_303 and CURL_REDIR_POST_ALL constants. CURL_REDIR_POST_303 requires libcurl 7.26.0 or higher, all others require libcurl 7.19.1 or higher. * PycURL now supports Python 3.1 through 3.3. Python 3.0 might work but it appears to ship with broken distutils, making virtualenv not function on it. * PycURL multi objects now have the multi constants defined on them. Previously the constants were only available on pycurl module. The new behavior matches that of curl and share objects. * PycURL share objects can now be closed via the close() method. * PycURL will no longer call `curl-config --static-libs` if `curl-config --libs` succeeds and returns output. Systems on which neither `curl-config --libs` nor `curl-config --static-libs` do the right thing should provide a `curl-config` wrapper that is sane. * Added CURLFORM_BUFFER and CURLFORM_BUFFERPTR. * pycurl.version and user agent string now include both PycURL version and libcurl version as separate items. * Added CURLOPT_DNS_SERVERS. * PycURL can now be dynamically linked against libcurl on Windows if PYCURL_USE_LIBCURL_DLL is #defined during compilation. * Breaking change: opensocket callback now takes an additional (address, port) tuple argument. Existing callbacks will need to be modified to accept this new argument. https://github.com/pycurl/pycurl/pull/18 Version 7.19.0.3 [requires libcurl-7.19.0 or better] - 2013-12-24 ----------------------------------------------------------------- * Re-release of 7.19.0.2 with minor changes to build Windows packages due to botched 7.19.0.2 files on PyPi. http://curl.haxx.se/mail/curlpython-2013-12/0021.html Version 7.19.0.2 [requires libcurl-7.19.0 or better] - 2013-10-08 ----------------------------------------------------------------- * Fixed a bug in a commit made in 2008 but not released until 7.19.0.1 which caused CURLOPT_POSTFIELDS to not correctly increment reference count of the object being given as its argument, despite libcurl not copying the data provided by said object. * Added support for libcurl pause/unpause functionality, via curl_easy_pause call and returning READFUNC_PAUSE from read callback function. Version 7.19.0.1 [requires libcurl-7.19.0 or better] - 2013-09-23 ----------------------------------------------------------------- * Test matrix tool added to test against all supported Python and libcurl versions. * Python 2.4 is now the minimum required version. * Source code, bugs and patches are now kept on GitHub. * Added CURLINFO_CERTINFO and CURLOPT_CERTINFO. * Added CURLOPT_RESOLVE. * PycURL can now be used with Python binaries without thread support. * gcrypt is no longer initialized when a newer version of gnutls is used. * Marked NSS as supported. * Fixed relative URL request logic. * Fixed a memory leak in util_curl_init. * Added CURLOPT_USERNAME and CURLOPT_PASSWORD. * Fixed handling of big timeout values. * Added GLOBAL_ACK_EINTR. * setopt(..., None) can be used as unsetopt(). * CURLOPT_RANGE can now be unset. * Write callback can return -1 to signal user abort. * Reorganized tests into an automated test suite. * Added CURLOPT_SEEKFUNCTION and CURLOPT_SEEKDATA. * Cleaned up website. * Fix pycurl.reset() (patch by ). * Fix install routine in setup.py where certain platforms (Solaris, Mac OSX, etc) would search for a static copy of libcurl (dbp). * Fixed build on OpenSolaris 0906 and other platforms on which curl-config does not have a --static-libs option. * No longer keep string options copies in the Curl Python objects, since string options are now managed by libcurl. Version 7.19.0 [requires libcurl-7.19.0 or better] -------------------------------------------------- * Added CURLFILE, ADDRESS_SCOPE and ISSUERCERT options, as well as the APPCONNECT_TIME info. * Added PRIMARY_IP info (patch by Yuhui H ). * Added support for curl_easy_reset through a new 'reset' method on curl objects (patch by Nick Pilon ). * Added support for OPENSOCKET callbacks. See 'tests/test_opensocket.py' for example usage (patch by Thomas Hunger ). Version 7.18.2 -------------- * Added REDIRECT_URL info and M_MAXCONNECTS option (patch by Yuhui H ). * Added socket_action() method to CurlMulti objects. See 'tests/test_multi_socket_select.py' for example usage (patch by Yuhui H ). * Added AUTOREFERER option. * Allow resetting some list operations (HTTPHEADER, QUOTE, POSTQUOTE, PREQUOTE) by passing an empty list to setopt (patch by Jim Patterson). Version 7.18.1 -------------- * Added POST301, SSH_HOST_PUBLIC_KEY_MD5, COPYPOSTFIELDS and PROXY_TRANSFER_MODE options. * Check for static libs in setup.py to better detect whether libcurl was linked with OpenSSL or GNUTLS. * PycURL is now dual licensed under the LGPL and a license similar to the cURL license (an MIT/X derivative). Version 7.16.4 -------------- * Allow any callable object as the callback function. This change comes handy when you would like to use objects which are callable but are not functions or methods, for example those objects created by the functions in the functools module (patch by Daniel Pena Arteaga ). * Added NEW_DIRECTORY_PERMS and NEW_FILE_PERMS options. Version 7.16.2.1 ---------------- * Added IOCMD_NOP and IOCMD_RESTARTREAD for ioctl callback handling (patch by Mark Eichin). * Use Py_ssize_t where appropriate for Python 2.5 and 64-bit compatibility. This fixes the problem reported by Aaron Hill, where the exception "pycurl.error: (2, '')" is thrown when calling setopt(pycurl.POSTFIELDS,...) on 64-bit platforms. Version 7.16.2 -------------- * Added options HTTP_TRANSFER_DECODING, HTTP_CONTENT_DECODING, TIMEOUT_MS, CONNECTTIMEOUT_MS from libcurl 7.16.2. * Right-strip URLs read from files in the test scripts to avoid sending requests with '\n' at the end. Version 7.16.1 -------------- * Added constants for all libcurl (error) return codes. They are named the same as the macro constants in curl.h but prefixed with E_ instead of CURLE. Return codes for the multi API are prefixed with M_ instead of CURLM. * Added CURLOPT_FTP_SSL_CCC, CURLOPT_SSH_PUBLIC_KEYFILE, CURLOPT_SSH_PRIVATE_KEYFILE, CURLOPT_SSH_AUTH_TYPES. * Removed CLOSEPOLICY and friends since this option is now deprecated in libcurl. * Set the _use_datetime attribute on the CURLTransport class to unbreak xmlrpc_curl.py on Python 2.5. Version 7.16.0 [no public release] -------------- * Added CURLOPT_SSL_SESSIONID_CACHE. * Removed SOURCE_* options since they are no longer supported by libcurl. Version 7.15.5.1 ---------------- * Added test for basic ftp usage (tests/test_ftp.py). * Fix broken ssl mutex lock function when using GNU TLS (Debian bug #380156, fix by Bastian Kleineidam) Version 7.15.5 -------------- * Added CURLOPT_FTP_ALTERNATIVE_TO_USER, CURLOPT_MAX_SEND_SPEED_LARGE, and CURLOPT_MAX_RECV_SPEED_LARGE. Version 7.15.4.2 ---------------- * Use SSL locking callbacks, fixes random crashes for multithreaded SSL connections (patch by Jayne ). Version 7.15.4.1 ---------------- * Fixed compilation problem with C compilers not allowing declarations in the middle of code blocks (patch by K.S.Sreeram ). * Fixed bug in curl_multi_fdset wrapping, max_fd < 0 is not an error (patch by K.S.Sreeram ). Version 7.15.4 -------------- * Added support for libcurl shares, patch from Victor Lascurain . See the file tests/test_share.py for example usage. * Added support for CURLINFO_FTP_ENTRY_PATH. Version 7.15.2 -------------- * Added CURLOPT_CONNECT_ONLY, CURLINFO_LASTSOCKET, CURLOPT_LOCALPORT and CURLOPT_LOCALPORTRANGE. Version 7.15.1 -------------- 2006-01-31 Kjetil Jacobsen * Fixed memory leak for getinfo calls that return a list as result. Patch by Paul Pacheco. Version 7.15.0 -------------- 2005-10-18 Kjetil Jacobsen * Added CURLOPT_FTP_SKIP_PASV_IP. Version 7.14.1 -------------- 2005-09-05 Kjetil Jacobsen * Added CURLOPT_IGNORE_CONTENT_LENGTH, CURLOPT_COOKIELIST as COOKIELIST and CURLINFO_COOKIELIST as INFO_COOKIELIST. Version 7.14.0 -------------- 2005-05-18 Kjetil Jacobsen * Added missing information returned from the info() method in the high-level interface. * Added the FORM_FILENAME option to the CURLFORM API with HTTPPOST. Version 7.13.2 -------------- 2005-03-30 Kjetil Jacobsen * Unbreak tests/test_gtk.py and require pygtk >= 2.0. 2005-03-15 Kjetil Jacobsen * Cleaned up several of the examples. 2005-03-11 Kjetil Jacobsen * WARNING: multi.select() now requires the previously optional timeout parameter. Updated the tests and examples to reflect this change. If the timeout is not set, select could block infinitely and cause problems for the internal timeout handling in the multi stack. The problem was identified by . Version 7.13.1 -------------- 2005-03-04 Kjetil Jacobsen * Use METH_NOARGS where appropriate. 2005-03-03 Kjetil Jacobsen * Added support for CURLFORM API with HTTPPOST: Supports a a tuple with pairs of options and values instead of just supporting string contents. See tests/test_post2.py for example usage. Options are FORM_CONTENTS, FORM_FILE and FORM_CONTENTTYPE, corresponding to the CURLFORM_* options, and values are strings. 2005-02-13 Markus F.X.J. Oberhumer * Read callbacks (pycurl.READFUNCTION) can now return pycurl.READFUNC_ABORT to immediately abort the current transfer. * The INFILESIZE, MAXFILESIZE, POSTFIELDSIZE and RESUME_FROM options now automatically use the largefile version to handle files > 2GB. * Added missing pycurl.PORT constant. Version 7.13.0 -------------- 2005-02-10 Kjetil Jacobsen * Added file_upload.py to examples, shows how to upload a file. * Added CURLOPT_IOCTLFUNCTION/DATA. * Added options from libcurl 7.13.0: FTP_ACCOUNT, SOURCE_URL, SOURCE_QUOTE. * Obsoleted options: SOURCE_HOST, SOURCE_PATH, SOURCE_PORT, PASV_HOST. Version 7.12.3 -------------- 2004-12-22 Markus F.X.J. Oberhumer * Added CURLINFO_NUM_CONNECTS and CURLINFO_SSL_ENGINES. * Added some other missing constants. * Updated pycurl.version_info() to return a 12-tuple instead of a 9-tuple. Version 7.12.2 -------------- 2004-10-15 Kjetil Jacobsen * Added CURLOPT_FTPSSLAUTH (and CURLFTPAUTH_*). * Added CURLINFO_OS_ERRNO. 2004-08-17 Kjetil Jacobsen * Use LONG_LONG instead of PY_LONG_LONG to make pycurl compile on Python versions < 2.3 (fix from Domenico Andreoli ). Version 7.12.1 -------------- 2004-08-02 Kjetil Jacobsen * Added INFOTYPE_SSL_DATA_IN/OUT. 2004-07-16 Markus F.X.J. Oberhumer * WARNING: removed deprecated PROXY_, TIMECOND_ and non-prefixed INFOTYPE constant names. See ChangeLog entry 2003-06-10. 2004-06-21 Kjetil Jacobsen * Added test program for HTTP post using the read callback (see tests/test_post3.py for details). * Use the new CURL_READFUNC_ABORT return code where appropriate to avoid hanging in perform() when read callbacks are used. * Added support for libcurl 7.12.1 CURLOPT features: SOURCE_HOST, SOURCE_USERPWD, SOURCE_PATH, SOURCE_PORT, PASV_HOST, SOURCE_PREQUOTE, SOURCE_POSTQUOTE. 2004-06-08 Markus F.X.J. Oberhumer * Setting CURLOPT_POSTFIELDS now allows binary data and automatically sets CURLOPT_POSTFIELDSIZE for you. If you really want a different size you have to manually set POSTFIELDSIZE after setting POSTFIELDS. (Based on a patch by Martin Muenstermann). 2004-06-05 Markus F.X.J. Oberhumer * Added stricter checks within the callback handlers. * Unify the behaviour of int and long parameters where appropriate. Version 7.12 ------------ 2004-05-18 Kjetil Jacobsen * WARNING: To simplify code maintenance pycurl now requires libcurl 7.11.2 and Python 2.2 or newer to work. * GC support is now always enabled. Version 7.11.3 -------------- 2004-04-30 Kjetil Jacobsen * Do not use the deprecated curl_formparse function. API CHANGE: HTTPPOST now takes a list of tuples where each tuple contains a form name and a form value, both strings (see test/test_post2.py for example usage). * Found a possible reference count bug in the multithreading code which may have contributed to the long-standing GC segfault which has haunted pycurl. Fingers crossed. Version 7.11.2 -------------- 2004-04-21 Kjetil Jacobsen * Added support for libcurl 7.11.2 CURLOPT features: CURLOPT_TCP_NODELAY. 2004-03-25 Kjetil Jacobsen * Store Python longs in off_t with PyLong_AsLongLong instead of PyLong_AsLong. Should make the options which deal with large files behave a little better. Note that this requires the long long support in Python 2.2 or newer to work properly. Version 7.11.1 -------------- 2004-03-16 Kjetil Jacobsen * WARNING: Removed support for the PASSWDFUNCTION callback, which is no longer supported by libcurl. 2004-03-15 Kjetil Jacobsen * Added support for libcurl 7.11.1 CURLOPT features: CURLOPT_POSTFIELDSIZE_LARGE. Version 7.11.0 -------------- 2004-02-11 Kjetil Jacobsen * Added support for libcurl 7.11.0 CURLOPT features: INFILESIZE_LARGE, RESUME_FROM_LARGE, MAXFILESIZE_LARGE and FTP_SSL. * Circular garbage collection support can now be enabled or disabled by passing the '--use-gc=[1|0]' parameter to setup.py when building pycurl. * HTTP_VERSION options are known as CURL_HTTP_VERSION_NONE, CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_1 and CURL_HTTP_VERSION_LAST. 2003-11-16 Markus F.X.J. Oberhumer * Added support for these new libcurl 7.11.0 features: CURLOPT_NETRC_FILE. Version 7.10.8 -------------- 2003-11-04 Markus F.X.J. Oberhumer * Added support for these new libcurl 7.10.8 features: CURLOPT_FTP_RESPONSE_TIMEOUT, CURLOPT_IPRESOLVE, CURLOPT_MAXFILESIZE, CURLINFO_HTTPAUTH_AVAIL, CURLINFO_PROXYAUTH_AVAIL, CURL_IPRESOLVE_* constants. * Added support for these new libcurl 7.10.7 features: CURLOPT_FTP_CREATE_MISSING_DIRS, CURLOPT_PROXYAUTH, CURLINFO_HTTP_CONNECTCODE. 2003-10-28 Kjetil Jacobsen * Added missing CURLOPT_ENCODING option (patch by Martijn Boerwinkel ) Version 7.10.6 -------------- 2003-07-29 Markus F.X.J. Oberhumer * Started working on support for CURLOPT_SSL_CTX_FUNCTION and CURLOPT_SSL_CTX_DATA (libcurl-7.10.6) - not yet finished. 2003-06-10 Markus F.X.J. Oberhumer * Added support for CURLOPT_HTTPAUTH (libcurl-7.10.6), including the new HTTPAUTH_BASIC, HTTPAUTH_DIGEST, HTTPAUTH_GSSNEGOTIATE and HTTPAUTH_NTML constants. * Some constants were renamed for consistency: All curl_infotype constants are now prefixed with "INFOTYPE_", all curl_proxytype constants are prefixed with "PROXYTYPE_" instead of "PROXY_", and all curl_TimeCond constants are now prefixed with "TIMECONDITION_" instead of "TIMECOND_". (The old names are still available but will get removed in a future release.) * WARNING: Removed the deprecated pycurl.init() and pycurl.multi_init() names - use pycurl.Curl() and pycurl.CurlMulti() instead. * WARNING: Removed the deprecated Curl.cleanup() and CurlMulti.cleanup() methods - use Curl.close() and CurlMulti.close() instead. Version 7.10.5 -------------- 2003-05-15 Markus F.X.J. Oberhumer * Added support for CURLOPT_FTP_USE_EPRT (libcurl-7.10.5). * Documentation updates. 2003-05-07 Eric S. Raymond * Lifted all HTML docs to clean XHTML, verified by tidy. 2003-05-02 Markus F.X.J. Oberhumer * Fixed some `int' vs. `long' mismatches that affected 64-bit systems. * Fixed wrong pycurl.CAPATH constant. 2003-05-01 Markus F.X.J. Oberhumer * Added new method Curl.errstr() which returns the internal libcurl error buffer string of the handle. Version 7.10.4.2 ---------------- 2003-04-15 Markus F.X.J. Oberhumer * Allow compilation against the libcurl-7.10.3 release - some recent Linux distributions (e.g. Mandrake 9.1) ship with 7.10.3, and apart from the new CURLOPT_UNRESTRICTED_AUTH option there is no need that we require libcurl-7.10.4. Version 7.10.4 -------------- 2003-04-01 Kjetil Jacobsen * Markus added CURLOPT_UNRESTRICTED_AUTH (libcurl-7.10.4). 2003-02-25 Kjetil Jacobsen * Fixed some broken test code and removed the fileupload test since it didn't work properly. 2003-01-28 Kjetil Jacobsen * Some documentation updates by Markus and me. 2003-01-22 Kjetil Jacobsen * API CHANGE: the CurlMulti.info_read() method now returns a separate array with handles that failed. Each entry in this array is a tuple with (curl object, error number, error message). This addition makes it simpler to do error checking of individual curl objects when using the multi interface. Version 7.10.3 -------------- 2003-01-13 Kjetil Jacobsen * PycURL memory usage has been reduced. 2003-01-10 Kjetil Jacobsen * Added 'examples/retriever-multi.py' which shows how to retrieve a set of URLs concurrently using the multi interface. 2003-01-09 Kjetil Jacobsen * Added support for CURLOPT_HTTP200ALIASES. 2002-11-22 Kjetil Jacobsen * Updated pycurl documentation in the 'doc' directory. 2002-11-21 Kjetil Jacobsen * Updated and improved 'examples/curl.py'. * Added 'tests/test_multi6.py' which shows how to use the info_read method with CurlMulti. 2002-11-19 Kjetil Jacobsen * Added new method CurlMulti.info_read(). Version 7.10.2 -------------- 2002-11-14 Kjetil Jacobsen * Free options set with setopt after cleanup is called, as cleanup assumes that options are still valid when invoked. This fixes the bug with COOKIEJAR reported by Bastiaan Naber . 2002-11-06 Markus F.X.J. Oberhumer * Install documentation under /usr/share/doc instead of /usr/doc. Also, start shipping the (unfinished) HTML docs and some basic test scripts. 2002-10-30 Markus F.X.J. Oberhumer * API CHANGE: For integral values, Curl.getinfo() now returns a Python-int instead of a Python-long. Version 7.10.1 -------------- 2002-10-03 Markus F.X.J. Oberhumer * Added new module-level function version_info() from libcurl-7.10. Version 7.10 ------------ 2002-09-13 Kjetil Jacobsen * Added commandline options to setup.py for specifying the path to 'curl-config' (non-windows) and the curl installation directory (windows). See the 'INSTALL' file for details. * Added CURLOPT_ENCODING, CURLOPT_NOSIGNAL and CURLOPT_BUFFERSIZE from libcurl-7.10 (by Markus Oberhumer). Version 7.9.8.4 --------------- 2002-08-28 Kjetil Jacobsen * Added a simple web-browser example based on gtkhtml and pycurl. See the file 'examples/gtkhtml_demo.py' for details. The example requires a working installation of gnome-python with gtkhtml bindings enabled (pass --with-gtkhtml to gnome-python configure). 2002-08-14 Kjetil Jacobsen * Added new method 'select' on CurlMulti objects. Example usage in 'tests/test_multi5.py'. This method is just an optimization of the combined use of fdset and select. 2002-08-12 Kjetil Jacobsen * Added support for curl_multi_fdset. See the file 'tests/test_multi4.py' for example usage. Contributed by Conrad Steenberg . * perform() on multi objects now returns a tuple (result, number of handles) like the libcurl interface does. 2002-08-08 Kjetil Jacobsen * Added the 'sfquery' script which retrieves a SourceForge XML export object for a given project. See the file 'examples/sfquery.py' for details and usage. 'sfquery' was contributed by Eric S. Raymond . 2002-07-20 Markus F.X.J. Oberhumer * API enhancements: added Curl() and CurlMulti() as aliases for init() and multi_init(), and added close() methods as aliases for the cleanup() methods. The new names much better match the actual intended use of the objects, and they also nicely correspond to Python's file object. * Also, all constants for Curl.setopt() and Curl.getinfo() are now visible from within Curl objects. All changes are fully backward-compatible. Version 7.9.8.3 --------------- 2002-07-16 Markus F.X.J. Oberhumer * Under Python 2.2 or better, Curl and CurlMulti objects now automatically participate in cyclic garbarge collection (using the gc module). Version 7.9.8.2 --------------- 2002-07-05 Markus F.X.J. Oberhumer * Curl and CurlMulti objects now support standard Python attributes. See tests/test_multi2.py for an example. 2002-07-02 Kjetil Jacobsen * Added support for the multi-interface. Version 7.9.8.1 --------------- 2002-06-25 Markus F.X.J. Oberhumer * Fixed a couple of `int' vs. `size_t' mismatches in callbacks and Py_BuildValue() calls. 2002-06-25 Kjetil Jacobsen * Use 'double' type instead of 'size_t' for progress callbacks (by Conrad Steenberg ). Also cleaned up some other type mismatches in the callback interfaces. 2002-06-24 Kjetil Jacobsen * Added example code on how to upload a file using HTTPPOST in pycurl (code by Amit Mongia ). See the file 'test_fileupload.py' for details. Version 7.9.8 ------------- 2002-06-24 Kjetil Jacobsen * Resolved some build problems on Windows (by Markus Oberhumer). 2002-06-19 Kjetil Jacobsen * Added CURLOPT_CAPATH. * Added option constants for CURLOPT_NETRC: CURL_NETRC_OPTIONAL, CURL_NETRC_IGNORED and CURL_NETRC_REQUIRED. * Added option constants for CURLOPT_TIMECONDITION: TIMECOND_IFMODSINCE and TIMECOND_IFUNMODSINCE. * Added an simple example crawler, which downloads documents listed in a file with a configurable number of worker threads. See the file 'crawler.py' in the 'tests' directory for details. * Removed the redundant 'test_xmlrpc2.py' test script. * Disallow recursive callback invocations (by Markus Oberhumer). 2002-06-18 Kjetil Jacobsen * Made some changes to setup.py which should fix the build problems on RedHat 7.3 (suggested by Benji ). * Use CURLOPT_READDATA instead of CURLOPT_INFILE, and CURLOPT_WRITEDATA instead of CURLOPT_FILE. Also fixed some reference counting bugs with file objects. * CURLOPT_FILETIME and CURLINFO_FILETIME had a namespace clash which caused them not to work. Use OPT_FILETIME for setopt() and INFO_FILETIME for getinfo(). See example usage in 'test_getinfo.py' for details. Version 7.9.7 ------------- 2002-05-20 Kjetil Jacobsen * New versioning scheme. Pycurl now has the same version number as the libcurl version it was built with. The pycurl version number thus indicates which version of libcurl is required to run. 2002-05-17 Kjetil Jacobsen * Added CURLINFO_REDIRECT_TIME and CURLINFO_REDIRECT_COUNT. 2002-04-27 Kjetil Jacobsen * Fixed potential memory leak and thread race (by Markus Oberhumer). Version 0.4.9 ------------- 2002-04-15 Kjetil Jacobsen * Added CURLOPT_DEBUGFUNCTION to allow debug callbacks to be specified (see the file 'test_debug.py' for details on how to use debug callbacks). * Added CURLOPT_DNS_USE_GLOBAL_CACHE and CURLOPT_DNS_CACHE_TIMEOUT. * Fixed a segfault when finalizing curl objects in Python 1.5.2. * Now requires libcurl 7.9.6 or greater. 2002-04-12 Kjetil Jacobsen * Added 'test_post2.py' file which is another example on how to issue POST requests. 2002-04-11 Markus F.X.J. Oberhumer * Added the 'test_post.py' file which demonstrates the use of POST requests. Version 0.4.8 ------------- 2002-03-07 Kjetil Jacobsen * Added CURLOPT_PREQUOTE. * Now requires libcurl 7.9.5 or greater. * Other minor code cleanups and bugfixes. 2002-03-05 Kjetil Jacobsen * Do not allow WRITEFUNCTION and WRITEHEADER on the same handle. Version 0.4.7 ------------- 2002-02-27 Kjetil Jacobsen * Abort callback if the thread state of the calling thread cannot be determined. * Check that the installed version of libcurl matches the requirements of pycurl. 2002-02-26 Kjetil Jacobsen * Clarence Garnder found a bug where string arguments to setopt sometimes were prematurely deallocated, this should now be fixed. 2002-02-21 Kjetil Jacobsen * Added the 'xmlrpc_curl.py' file which implements a transport for xmlrpclib (xmlrpclib is part of Python 2.2). * Added CURLINFO_CONTENT_TYPE. * Added CURLOPT_SSLCERTTYPE, CURLOPT_SSLKEY, CURLOPT_SSLKEYTYPE, CURLOPT_SSLKEYPASSWD, CURLOPT_SSLENGINE and CURLOPT_SSLENGINE_DEFAULT. * When thrown, the pycurl.error exception is now a tuple consisting of the curl error code and the error message. * Now requires libcurl 7.9.4 or greater. 2002-02-19 Kjetil Jacobsen * Fixed docstring for getopt() function. 2001-12-18 Kjetil Jacobsen * Updated the INSTALL information for Win32. 2001-12-12 Kjetil Jacobsen * Added missing link flag to make pycurl build on MacOS X (by Matt King ). 2001-12-06 Kjetil Jacobsen * Added CURLINFO_STARTTRANSFER_TIME and CURLOPT_FTP_USE_EPSV from libcurl 7.9.2. 2001-12-01 Markus F.X.J. Oberhumer * Added the 'test_stringio.py' file which demonstrates the use of StringIO objects as callback. 2001-12-01 Markus F.X.J. Oberhumer * setup.py: Do not remove entries from a list while iterating over it. 2001-11-29 Kjetil Jacobsen * Added code in setup.py to install on Windows. Requires some manual configuration (by Tino Lange ). 2001-11-27 Kjetil Jacobsen * Improved detection of where libcurl is installed in setup.py. Should make it easier to install pycurl when libcurl is not located in regular lib/include paths. 2001-11-05 Kjetil Jacobsen * Some of the newer options to setopt were missing, this should now be fixed. 2001-11-04 Kjetil Jacobsen * Exception handling has been improved and should no longer throw spurious exceptions (by Markus F.X.J. Oberhumer ). 2001-10-15 Kjetil Jacobsen * Refactored the test_gtk.py script to avoid global variables. 2001-10-12 Kjetil Jacobsen * Added module docstrings, terse perhaps, but better than nothing. * Added the 'basicfirst.py' file which is a Python version of the corresponding Perl script by Daniel. * PycURL now works properly under Python 1.5 and 1.6 (by Markus F.X.J. Oberhumer ). * Allow C-functions and Python methods as callbacks (by Markus F.X.J. Oberhumer ). * Allow None as success result of write, header and progress callback invocations (by Markus F.X.J. Oberhumer ). * Added the 'basicfirst2.py' file which demonstrates the use of a class method as callback instead of just a function. 2001-08-21 Kjetil Jacobsen * Cleaned up the script with GNOME/PycURL integration. 2001-08-20 Kjetil Jacobsen * Added another test script for shipping XML-RPC requests which uses py-xmlrpc to encode the arguments (tests/test_xmlrpc2.py). 2001-08-20 Kjetil Jacobsen * Added test script for using PycURL and GNOME (tests/test_gtk.py). 2001-08-20 Kjetil Jacobsen * Added test script for using XML-RPC (tests/test_xmlrpc.py). * Added more comments to the test sources. 2001-08-06 Kjetil Jacobsen * Renamed module namespace to pycurl instead of curl. 2001-08-06 Kjetil Jacobsen * Set CURLOPT_VERBOSE to 0 by default. 2001-06-29 Kjetil Jacobsen * Updated INSTALL, curl version 7.8 or greater is now mandatory to use pycurl. 2001-06-13 Kjetil Jacobsen * Set NOPROGRESS to 1 by default. 2001-06-07 Kjetil Jacobsen * Added global_init/cleanup. 2001-06-06 Kjetil Jacobsen * Added HEADER/PROGRESSFUNCTION callbacks (see files in tests/). * Added PASSWDFUNCTION callback (untested). * Added READFUNCTION callback (untested). 2001-06-05 Kjetil Jacobsen * WRITEFUNCTION callbacks now work (see tests/test_cb.py for details). * Preliminary distutils installation. * Added CLOSEPOLICY constants to module namespace. 2001-06-04 Kjetil Jacobsen * Return -1 on error from Python callback in WRITEFUNCTION callback. 2001-06-01 Kjetil Jacobsen * Moved source to src and tests to tests directory. 2001-05-31 Kjetil Jacobsen * Added better type checking for setopt. 2001-05-30 Kjetil Jacobsen * Moved code to sourceforge. * Added getinfo support. # vi:ts=8:et pycurl-7.19.3/INSTALL0000644000470500047050000000654512257354374013572 0ustar piepie00000000000000NOTE: You need Python and libcurl installed on your system to use or build pycurl. Some RPM distributions of curl/libcurl do not include everything necessary to build pycurl, in which case you need to install the developer specific RPM which is usually called curl-dev. Distutils --------- Build and install pycurl with the following commands: (if necessary, become root) tar -zxvf pycurl-$VER.tar.gz cd pycurl-$VER python setup.py install $VER should be substituted with the pycurl version number, e.g. 7.10.5. Note that the installation script assumes that 'curl-config' can be located in your path setting. If curl-config is installed outside your path or you want to force installation to use a particular version of curl-config, use the '--curl-config' commandline option to specify the location of curl-config. Example: python setup.py install --curl-config=/usr/local/bin/curl-config If libcurl is linked dynamically with pycurl, you may have to alter the LD_LIBRARY_PATH environment variable accordingly. This normally applies only if there is more than one version of libcurl installed, e.g. one in /usr/lib and one in /usr/local/lib. easy_install / pip ---------------- easy_install pycurl pip install pycurl If you need to specify an alternate curl-config, it can be done via an environment variable: export PYCURL_CURL_CONFIG=/usr/local/bin/curl-config easy_install pycurl The same applies to the SSL backend, if you need to specify it (see the SSL section below): export PYCURL_SSL_LIBRARY=openssl easy_install pycurl SSL --- PycURL has locks around crypto functions. In order to compile correct locking code, it has to know which SSL library is going to be used by libcurl at runtime. setup.py will attempt to automatically detect the SSL library that libcurl uses, but this does not always work. In the cases when setup.py cannot figure out the SSL library, it must be provided via --with-ssl/--with-gnutls/ --with-nss arguments, just like libcurl's configure script uses, or via PYCURL_SSL_LIBRARY=openssl|gnutls|nss environment variable. Please note the difference in spelling that concerns OpenSSL: the command-line argument is --with-ssl, to match libcurl, but the environment variable value is "openssl". Windows ------- First, you will need to obtain dependencies. These can be precompiled binaries or source packages that you are going to compile yourself. For a minimum build you will just need libcurl source. Follow its Windows build instructions to build either a static or a DLL version of the library, then configure PycURL as follows to use it: python setup.py --curl-dir=c:\dev\curl-7.33.0\builds\libcurl-vc-x86-release-dll-ipv6-sspi-spnego-winssl --use-libcurl-dll Note that --curl-dir does not point to libcurl source but rather to headers and compiled libraries. Additional Windows setup.py options: --use-libcurl-dll - build against libcurl DLL, if not given PycURL will be built against libcurl statically. --libcurl-lib-name=libcurl_imp.lib - specify a different name for libcurl import library. The default is libcurl.lib which is appropriate for static linking and is sometimes the correct choice for dynamic linking as well. The other possibility for dynamic linking is libcurl_imp.lib. A good setup.py target to use is bdist_wininst which produces an executable installer that you can run to install PycURL. pycurl-7.19.3/MANIFEST.in0000644000470500047050000000110712263706154014256 0ustar piepie00000000000000# # MANIFEST.in # Manifest template for creating the source distribution. # include COPYING-LGPL include COPYING-MIT include ChangeLog include INSTALL include MANIFEST.in include Makefile include README.rst include RELEASE-NOTES.rst include doc/*.html include doc/*.rst include examples/*.py include examples/tests/*.py include src/Makefile include src/pycurl.c include python/curl/*.py include requirements*.txt include tests/*.py include tests/certs/*.crt include tests/certs/*.key include tests/ext/*.sh include tests/matrix/*.patch include tests/vsftpd.conf include winbuild.py pycurl-7.19.3/Makefile0000644000470500047050000000320312263707127014160 0ustar piepie00000000000000# # to use a specific python version call # `make PYTHON=python2.2' # SHELL = /bin/sh PYTHON = python NOSETESTS = nosetests all build: $(PYTHON) setup.py build build-7.10.8: $(PYTHON) setup.py build --curl-config=/home/hosts/localhost/packages/curl-7.10.8/bin/curl-config test: build mkdir -p tests/tmp PYTHONSUFFIX=$$(python -V 2>&1 |awk '{print $$2}' |awk -F. '{print $$1 "." $$2}') && \ PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \ $(PYTHON) -c 'import pycurl; print(pycurl.version)' PYTHONPATH=$$(ls -d build/lib.*$$PYTHONSUFFIX):$$PYTHONPATH \ $(NOSETESTS) ./tests/ext/test-suite.sh # (needs GNU binutils) strip: build strip -p --strip-unneeded build/lib*/*.so chmod -x build/lib*/*.so install install_lib: $(PYTHON) setup.py $@ clean: -rm -rf build dist -rm -f *.pyc *.pyo */*.pyc */*.pyo */*/*.pyc */*/*.pyo -rm -f MANIFEST cd src && $(MAKE) clean distclean: clean maintainer-clean: distclean dist sdist: distclean $(PYTHON) setup.py sdist # target for maintainer windist: distclean rm -rf build python2.2 setup.py bdist_wininst rm -rf build python2.3 setup.py bdist_wininst rm -rf build python2.4 setup.py bdist_wininst rm -rf build python2.2 setup_win32_ssl.py bdist_wininst rm -rf build python2.3 setup_win32_ssl.py bdist_wininst rm -rf build python2.4 setup_win32_ssl.py bdist_wininst rm -rf build docs: cd doc && for file in *.rst; do rst2html "$$file" ../www/htdocs/doc/`echo "$$file" |sed -e 's/.rst$$/.html/'`; done rst2html RELEASE-NOTES.rst www/htdocs/release-notes.html .PHONY: all build test strip install install_lib clean distclean maintainer-clean dist sdist windist .NOEXPORT: pycurl-7.19.3/README.rst0000644000470500047050000002041712262777671014227 0ustar piepie00000000000000PycURL: Python interface to libcurl ==================================== .. image:: https://api.travis-ci.org/pycurl/pycurl.png :target: https://travis-ci.org/pycurl/pycurl PycURL is a Python interface to `libcurl`_. PycURL can be used to fetch objects identified by a URL from a Python program, similar to the `urllib`_ Python module. PycURL is mature, very fast, and supports a lot of features. Overview -------- - libcurl is a free and easy-to-use client-side URL transfer library, supporting FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS, FILE, IMAP, SMTP, POP3 and RTSP. libcurl supports SSL certificates, HTTP POST, HTTP PUT, FTP uploading, HTTP form based upload, proxies, cookies, user+password authentication (Basic, Digest, NTLM, Negotiate, Kerberos4), file transfer resume, http proxy tunneling and more! - libcurl is highly portable, it builds and works identically on numerous platforms, including Solaris, NetBSD, FreeBSD, OpenBSD, Darwin, HPUX, IRIX, AIX, Tru64, Linux, UnixWare, HURD, Windows, Amiga, OS/2, BeOs, Mac OS X, Ultrix, QNX, OpenVMS, RISC OS, Novell NetWare, DOS and more... - libcurl is `free`_, `thread-safe`_, `IPv6 compatible`_, `feature rich`_, `well supported`_, `fast`_, `thoroughly documented`_ and is already used by many known, big and successful `companies`_ and numerous `applications`_. .. _free: http://curl.haxx.se/docs/copyright.html .. _thread-safe: http://curl.haxx.se/libcurl/features.html#thread .. _`IPv6 compatible`: http://curl.haxx.se/libcurl/features.html#ipv6 .. _`feature rich`: http://curl.haxx.se/libcurl/features.html#features .. _`well supported`: http://curl.haxx.se/libcurl/features.html#support .. _`fast`: http://curl.haxx.se/libcurl/features.html#fast .. _`thoroughly documented`: http://curl.haxx.se/libcurl/features.html#docs .. _companies: http://curl.haxx.se/docs/companies.html .. _applications: http://curl.haxx.se/libcurl/using/apps.html Requirements ------------ - Python 2.4 through 2.7 or 3.1 through 3.3. - libcurl 7.19.0 or better. Installation ------------ You can install the most recent PycURL version using `easy_install`_:: easy_install pycurl or `pip`_:: pip install pycurl Installing from source is performed via ``setup.py``:: python setup.py install You will need libcurl headers and libraries installed to install PycURL from source. PycURL uses ``curl-config`` to determine correct flags/libraries to use during compilation; you can override the location of ``curl-config`` if it is not in PATH or you want to use a custom libcurl installation:: python setup.py --curl-config=/path/to/curl-config install Sometimes it is more convenient to use an environment variable, if you are not directly invoking ``setup.py``:: PYCURL_CURL_CONFIG=/path/to/curl-config python setup.py install ``curl-config`` is expected to support the following options: - ``--version`` - ``--cflags`` - ``--libs`` - ``--static-libs`` (if ``--libs`` does not work) PycURL requires that the SSL library that it is built against is the same one libcurl, and therefore PycURL, uses at runtime. PycURL's ``setup.py`` uses ``curl-config`` to attempt to figure out which SSL library libcurl was compiled against, however this does not always work. If PycURL is unable to determine the SSL library in use it will print a warning similar to the following:: src/pycurl.c:137:4: warning: #warning "libcurl was compiled with SSL support, but configure could not determine which " "library was used; thus no SSL crypto locking callbacks will be set, which may " "cause random crashes on SSL requests" [-Wcpp] It will then fail at runtime as follows:: ImportError: pycurl: libcurl link-time ssl backend (openssl) is different from compile-time ssl backend (none/other) To fix this, you need to tell ``setup.py`` what SSL backend is used:: python setup.py --with-[ssl|gnutls|nss] install Or use an environment variable:: PYCURL_SSL_LIBRARY=openssl|gnutls|nss python setup.py installl Note the difference between ``--with-ssl`` (for compatibility with libcurl) and ``PYCURL_SSL_LIBRARY=openssl``. .. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall .. _pip: http://pypi.python.org/pypi/pip Support ------- For support questions, please use `curl-and-python mailing list`_. `Mailing list archives`_ are available for your perusal as well. Bugs can be reported `via GitHub`_. Please only use GitHub issues when you are certain you have found a bug in PycURL. If you do not have a patch to fix the bug, or at least a specific code fragment in PycURL that you believe is the cause, you should instead post you inquiry to the mailing list. .. _curl-and-python mailing list: http://cool.haxx.se/mailman/listinfo/curl-and-python .. _Mailing list archives: http://curl.haxx.se/mail/list.cgi?list=curl-and-python .. _via GitHub: https://github.com/pycurl/pycurl/issues Automated Tests --------------- PycURL comes with an automated test suite. To run the tests, execute:: make test The suite depends on packages `nose`_, `bottle`_ and `cherrypy`_. Some tests use vsftpd configured to accept anonymous uploads. These tests are not run by default. As configured, vsftpd will allow reads and writes to anything the user running the tests has read and write access. To run vsftpd tests you must explicitly set PYCURL_VSFTPD_PATH variable like so:: # use vsftpd in PATH export PYCURL_VSFTPD_PATH=vsftpd # specify full path to vsftpd export PYCURL_VSFTPD_PATH=/usr/local/libexec/vsftpd These instructions work for Python 2.5 through 2.7 and 3.1 through 3.3. .. _nose: https://nose.readthedocs.org/ .. _bottle: http://bottlepy.org/ .. _cherrypy: http://www.cherrypy.org/ Test Matrix ----------- The test matrix is a separate framework that runs tests on more esoteric configurations. It supports: - Testing against Python 2.4, which bottle does not support. - Testing against Python compiled without threads, which requires an out of process test server. - Testing against locally compiled libcurl with arbitrary options. To use the test matrix, first you need to start the test server from Python 2.5+ by running::: python -m tests.appmanager Then in a different shell, and preferably in a separate user account, run the test matrix::: # run ftp tests, etc. export PYCURL_VSFTPD_PATH=vsftpd # create a new work directory, preferably not under pycurl tree mkdir testmatrix cd testmatrix # run the matrix specifying absolute path python /path/to/pycurl/tests/matrix.py The test matrix will download, build and install supported Python versions and supported libcurl versions, then run pycurl tests against each combination. To see what the combinations are, look in `tests/matrix.py `_. Contribute ---------- For smaller changes: #. Fork `the repository`_ on Github. #. Create a branch off **master**. #. Make your changes. #. Write a test which shows that the bug was fixed or that the feature works as expected. #. Send a pull request. #. Check back after 10-15 minutes to see if tests passed on Travis CI. PycURL supports old Python and libcurl releases and their support is tested on Travis. For larger changes: #. Join the `mailing list`_. #. Discuss your proposal on the mailing list. #. When consensus is reached, implement it as described above. Please contribute binary distributions for your system to the `downloads repository`_. License ------- :: Copyright (C) 2001-2008 by Kjetil Jacobsen Copyright (C) 2001-2008 by Markus F.X.J. Oberhumer Copyright (C) 2013-2014 by Oleg Pudeyev All rights reserved. PycURL is dual licensed under the LGPL and an MIT/X derivative license based on the cURL license. A full copy of the LGPL license is included in the file COPYING-LGPL. A full copy of the MIT/X derivative license is included in the file COPYING-MIT. You can redistribute and/or modify PycURL according to the terms of either license. .. _PycURL: http://pycurl.sourceforge.net/ .. _libcurl: http://curl.haxx.se/libcurl/ .. _urllib: http://docs.python.org/library/urllib.html .. _`the repository`: https://github.com/pycurl/pycurl .. _`mailing list`: http://cool.haxx.se/mailman/listinfo/curl-and-python .. _`downloads repository`: https://github.com/pycurl/downloads pycurl-7.19.3/RELEASE-NOTES.rst0000644000470500047050000000247412263710604015223 0ustar piepie00000000000000Release Notes ============= PycURL 7.19.3 - 2014-01-09 -------------------------- This release brings official Python 3 support to PycURL. Several GNU/Linux distributions provided Python 3 packages of PycURL previously; these packages were based on patches that were incomplete and in some places incorrect. Behavior of PycURL 7.19.3 and later may therefore differ from behavior of unofficial Python 3 packages of previous PycURL versions. To summarize the behavior under Python 3, PycURL will accept ``bytes`` where it accepted strings under Python 2, and will also accept Unicode strings containing ASCII codepoints only for convenience. Please refer to `Unicode`_ and `file`_ documentation for further details. In the interests of compatibility, PycURL will also accept Unicode data on Python 2 given the same constraints as under Python 3. While Unicode and file handling rules are expected to be sensible for all use cases, and retain backwards compatibility with previous PycURL versions, please treat behavior of this versions under Python 3 as experimental and subject to change. Another potentially disruptive change in PycURL is the requirement for compile time and runtime SSL backends to match. Please see the readme for how to indicate the SSL backend to setup.py. .. _Unicode: doc/unicode.html .. _file: doc/files.html pycurl-7.19.3/requirements-dev-2.4.txt0000644000470500047050000000002712255756661017072 0ustar piepie00000000000000nose simplejson==2.1.0 pycurl-7.19.3/requirements-dev-2.5.txt0000644000470500047050000000004312255756661017071 0ustar piepie00000000000000-r requirements-dev.txt simplejson pycurl-7.19.3/requirements-dev.txt0000644000470500047050000000002512255756661016567 0ustar piepie00000000000000bottle nose cherrypy pycurl-7.19.3/setup.py0000644000470500047050000004276512263706570014253 0ustar piepie00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- # vi:ts=4:et """Setup script for the PycURL module distribution.""" PACKAGE = "pycurl" PY_PACKAGE = "curl" VERSION = "7.19.3" import glob, os, re, sys, string, subprocess import distutils from distutils.core import setup from distutils.extension import Extension from distutils.util import split_quoted from distutils.version import LooseVersion try: # python 2 exception_base = StandardError except NameError: # python 3 exception_base = Exception class ConfigurationError(exception_base): pass include_dirs = [] define_macros = [("PYCURL_VERSION", '"%s"' % VERSION)] library_dirs = [] libraries = [] runtime_library_dirs = [] extra_objects = [] extra_compile_args = [] extra_link_args = [] def fail(msg): sys.stderr.write(msg + "\n") exit(10) def scan_argv(s, default=None): p = default i = 1 while i < len(sys.argv): arg = sys.argv[i] if str.find(arg, s) == 0: if s.endswith('='): # --option=value p = arg[len(s):] assert p, arg else: # --option # set value to True p = True del sys.argv[i] else: i = i + 1 ##print sys.argv return p # append contents of an environment variable to library_dirs[] def add_libdirs(envvar, sep, fatal=False): v = os.environ.get(envvar) if not v: return for dir in str.split(v, sep): dir = str.strip(dir) if not dir: continue dir = os.path.normpath(dir) if os.path.isdir(dir): if not dir in library_dirs: library_dirs.append(dir) elif fatal: fail("FATAL: bad directory %s in environment variable %s" % (dir, envvar)) def configure_windows(): # Windows users have to pass --curl-dir parameter to specify path # to libcurl, because there is no curl-config on windows at all. curl_dir = scan_argv("--curl-dir=") if curl_dir is None: fail("Please specify --curl-dir=/path/to/built/libcurl") if not os.path.exists(curl_dir): fail("Curl directory does not exist: %s" % curl_dir) if not os.path.isdir(curl_dir): fail("Curl directory is not a directory: %s" % curl_dir) print("Using curl directory: %s" % curl_dir) include_dirs.append(os.path.join(curl_dir, "include")) # libcurl windows documentation states that for linking against libcurl # dll, the import library name is libcurl_imp.lib. # in practice, the library name sometimes is libcurl.lib. # override with: --libcurl-lib-name=libcurl_imp.lib curl_lib_name = scan_argv('--libcurl-lib-name=', 'libcurl.lib') if scan_argv("--use-libcurl-dll") is not None: libcurl_lib_path = os.path.join(curl_dir, "lib", curl_lib_name) extra_link_args.extend(["ws2_32.lib"]) if str.find(sys.version, "MSC") >= 0: # build a dll extra_compile_args.append("-MD") else: extra_compile_args.append("-DCURL_STATICLIB") libcurl_lib_path = os.path.join(curl_dir, "lib", curl_lib_name) extra_link_args.extend(["gdi32.lib", "wldap32.lib", "winmm.lib", "ws2_32.lib",]) if not os.path.exists(libcurl_lib_path): fail("libcurl.lib does not exist at %s.\nCurl directory must point to compiled libcurl (bin/include/lib subdirectories): %s" %(libcurl_lib_path, curl_dir)) extra_objects.append(libcurl_lib_path) # make pycurl binary work on windows xp. # we use inet_ntop which was added in vista and implement a fallback. # our implementation will not be compiled with _WIN32_WINNT targeting # vista or above, thus said binary won't work on xp. # http://curl.haxx.se/mail/curlpython-2013-12/0007.html extra_compile_args.append("-D_WIN32_WINNT=0x0501") if str.find(sys.version, "MSC") >= 0: extra_compile_args.append("-O2") extra_compile_args.append("-GF") # enable read-only string pooling extra_compile_args.append("-WX") # treat warnings as errors p = subprocess.Popen(['cl.exe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() match = re.search(r'Version (\d+)', err.decode().split("\n")[0]) if match and int(match.group(1)) < 16: # option removed in vs 2010: # connect.microsoft.com/VisualStudio/feedback/details/475896/link-fatal-error-lnk1117-syntax-error-in-option-opt-nowin98/ extra_link_args.append("/opt:nowin98") # use small section alignment def get_bdist_msi_version_hack(): # workaround for distutils/msi version requirement per # epydoc.sourceforge.net/stdlib/distutils.version.StrictVersion-class.html - # only x.y.z version numbers are supported, whereas our versions might be x.y.z.p. # bugs.python.org/issue6040#msg133094 from distutils.command.bdist_msi import bdist_msi import inspect import types import re class bdist_msi_version_hack(bdist_msi): """ MSI builder requires version to be in the x.x.x format """ def run(self): def monkey_get_version(self): """ monkey patch replacement for metadata.get_version() that returns MSI compatible version string for bdist_msi """ # get filename of the calling function if inspect.stack()[1][1].endswith('bdist_msi.py'): # strip revision from version (if any), e.g. 11.0.0-r31546 match = re.match(r'(\d+\.\d+\.\d+)', self.version) assert match return match.group(1) else: return self.version # monkeypatching get_version() call for DistributionMetadata self.distribution.metadata.get_version = \ types.MethodType(monkey_get_version, self.distribution.metadata) bdist_msi.run(self) return bdist_msi_version_hack def configure_unix(): # Find out the rest the hard way OPENSSL_DIR = scan_argv("--openssl-dir=") if OPENSSL_DIR is not None: include_dirs.append(os.path.join(OPENSSL_DIR, "include")) CURL_CONFIG = os.environ.get('PYCURL_CURL_CONFIG', "curl-config") CURL_CONFIG = scan_argv("--curl-config=", CURL_CONFIG) try: p = subprocess.Popen((CURL_CONFIG, '--version'), stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: exc = sys.exc_info()[1] msg = 'Could not run curl-config: %s' % str(exc) raise ConfigurationError(msg) stdout, stderr = p.communicate() if p.wait() != 0: msg = "`%s' not found -- please install the libcurl development files or specify --curl-config=/path/to/curl-config" % CURL_CONFIG if stderr: msg += ":\n" + stderr.decode() raise ConfigurationError(msg) libcurl_version = stdout.decode().strip() print("Using %s (%s)" % (CURL_CONFIG, libcurl_version)) p = subprocess.Popen((CURL_CONFIG, '--cflags'), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.wait() != 0: msg = "Problem running `%s' --cflags" % CURL_CONFIG if stderr: msg += ":\n" + stderr.decode() raise ConfigurationError(msg) for arg in split_quoted(stdout.decode()): if arg[:2] == "-I": # do not add /usr/include if not re.search(r"^\/+usr\/+include\/*$", arg[2:]): include_dirs.append(arg[2:]) else: extra_compile_args.append(arg) # Obtain linker flags/libraries to link against. # In theory, all we should need is `curl-config --libs`. # Apparently on some platforms --libs fails and --static-libs works, # so try that. # If --libs succeeds do not try --static libs; see # https://github.com/pycurl/pycurl/issues/52 for more details. # If neither --libs nor --static-libs work, fail. optbuf = "" errtext = '' for option in ["--libs", "--static-libs"]: p = subprocess.Popen((CURL_CONFIG, option), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.wait() == 0: optbuf = stdout.decode() break else: errtext += stderr.decode() if optbuf == "": msg = "Neither curl-config --libs nor curl-config --static-libs" +\ " succeeded and produced output" if errtext: msg += ":\n" + errtext raise ConfigurationError(msg) libs = split_quoted(optbuf) ssl_lib_detected = False if 'PYCURL_SSL_LIBRARY' in os.environ: ssl_lib = os.environ['PYCURL_SSL_LIBRARY'] if ssl_lib in ['openssl', 'gnutls', 'nss']: ssl_lib_detected = True define_macros.append(('HAVE_CURL_%s' % ssl_lib.upper(), 1)) else: raise ConfigurationError('Invalid value "%s" for PYCURL_SSL_LIBRARY' % ssl_lib) ssl_options = { '--with-ssl': 'HAVE_CURL_OPENSSL', '--with-gnutls': 'HAVE_CURL_GNUTLS', '--with-nss': 'HAVE_CURL_NSS', } for option in ssl_options: if scan_argv(option) is not None: for other_option in ssl_options: if option != other_option: if scan_argv(other_option) is not None: raise ConfigurationError('Cannot give both %s and %s' % (option, other_option)) ssl_lib_detected = True define_macros.append((ssl_options[option], 1)) for arg in libs: if arg[:2] == "-l": libraries.append(arg[2:]) if not ssl_lib_detected and arg[2:] == 'ssl': define_macros.append(('HAVE_CURL_OPENSSL', 1)) ssl_lib_detected = True if not ssl_lib_detected and arg[2:] == 'gnutls': define_macros.append(('HAVE_CURL_GNUTLS', 1)) ssl_lib_detected = True if not ssl_lib_detected and arg[2:] == 'ssl3': define_macros.append(('HAVE_CURL_NSS', 1)) ssl_lib_detected = True elif arg[:2] == "-L": library_dirs.append(arg[2:]) else: extra_link_args.append(arg) if not ssl_lib_detected: p = subprocess.Popen((CURL_CONFIG, '--features'), stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() if p.wait() != 0: msg = "Problem running `%s' --features" % CURL_CONFIG if stderr: msg += ":\n" + stderr.decode() raise ConfigurationError(msg) for feature in split_quoted(stdout.decode()): if feature == 'SSL': # this means any ssl library, not just openssl define_macros.append(('HAVE_CURL_SSL', 1)) else: # if we are configuring for a particular ssl library, # we can assume that ssl is being used define_macros.append(('HAVE_CURL_SSL', 1)) if not libraries: libraries.append("curl") # Add extra compile flag for MacOS X if sys.platform[:-1] == "darwin": extra_link_args.append("-flat_namespace") def configure(): if sys.platform == "win32": configure_windows() else: configure_unix() def strip_pycurl_options(): if sys.platform == 'win32': options = ['--curl-dir=', '--curl-lib-name=', '--use-libcurl-dll'] else: options = ['--openssl-dir', '--curl-config'] for option in options: scan_argv(option) ############################################################################### def get_extension(): ext = Extension( name=PACKAGE, sources=[ os.path.join("src", "pycurl.c"), ], include_dirs=include_dirs, define_macros=define_macros, library_dirs=library_dirs, libraries=libraries, runtime_library_dirs=runtime_library_dirs, extra_objects=extra_objects, extra_compile_args=extra_compile_args, extra_link_args=extra_link_args, ) ##print(ext.__dict__); sys.exit(1) return ext ############################################################################### # prepare data_files def get_data_files(): # a list of tuples with (path to install to, a list of local files) data_files = [] if sys.platform == "win32": datadir = os.path.join("doc", PACKAGE) else: datadir = os.path.join("share", "doc", PACKAGE) # files = ["ChangeLog", "COPYING-LGPL", "COPYING-MIT", "INSTALL", "README.rst"] if files: data_files.append((os.path.join(datadir), files)) files = glob.glob(os.path.join("doc", "*.html")) if files: data_files.append((os.path.join(datadir, "html"), files)) files = glob.glob(os.path.join("examples", "*.py")) if files: data_files.append((os.path.join(datadir, "examples"), files)) files = glob.glob(os.path.join("tests", "*.py")) if files: data_files.append((os.path.join(datadir, "tests"), files)) # assert data_files for install_dir, files in data_files: assert files for f in files: assert os.path.isfile(f), (f, install_dir) return data_files ##print get_data_files(); sys.exit(1) ############################################################################### def check_manifest(): import fnmatch f = open('MANIFEST.in') globs = [] try: for line in f.readlines(): stripped = line.strip() if stripped == '' or stripped.startswith('#'): continue assert stripped.startswith('include ') glob = stripped[8:] globs.append(glob) finally: f.close() paths = [] start = os.path.abspath(os.path.dirname(__file__)) for root, dirs, files in os.walk(start): if '.git' in dirs: dirs.remove('.git') for file in files: if file.endswith('.pyc'): continue rel = os.path.join(root, file)[len(start)+1:] paths.append(rel) for path in paths: included = False for glob in globs: if fnmatch.fnmatch(path, glob): included = True break if not included: print(path) ############################################################################### setup_args = dict( name=PACKAGE, version=VERSION, description="PycURL -- cURL library module for Python", author="Kjetil Jacobsen, Markus F.X.J. Oberhumer, Oleg Pudeyev", author_email="kjetilja at gmail.com, markus at oberhumer.com, oleg at bsdpower.com", maintainer="Oleg Pudeyev", maintainer_email="oleg@bsdpower.com", url="http://pycurl.sourceforge.net/", download_url="http://pycurl.sourceforge.net/download/", license="LGPL/MIT", keywords=['curl', 'libcurl', 'urllib', 'wget', 'download', 'file transfer', 'http', 'www'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'License :: OSI Approved :: MIT License', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 3', 'Topic :: Internet :: File Transfer Protocol (FTP)', 'Topic :: Internet :: WWW/HTTP', ], packages=[PY_PACKAGE], package_dir={ PY_PACKAGE: os.path.join('python', 'curl') }, long_description=""" This module provides Python bindings for the cURL library.""", ) if sys.platform == "win32": setup_args['cmdclass'] = {'bdist_msi': get_bdist_msi_version_hack()} ##print distutils.__version__ if LooseVersion(distutils.__version__) > LooseVersion("1.0.1"): setup_args["platforms"] = "All" if LooseVersion(distutils.__version__) < LooseVersion("1.0.3"): setup_args["licence"] = setup_args["license"] unix_help = '''\ PycURL Unix options: --curl-config=/path/to/curl-config use specified curl-config binary --openssl-dir=/path/to/openssl/dir path to OpenSSL headers and libraries --with-ssl libcurl is linked against OpenSSL --with-gnutls libcurl is linked against GnuTLS --with-nss libcurl is linked against NSS ''' windows_help = '''\ PycURL Windows options: --curl-dir=/path/to/compiled/libcurl path to libcurl headers and libraries --use-libcurl-dll link against libcurl DLL, if not given link against libcurl statically --libcurl-lib-name=libcurl_imp.lib override libcurl import library name ''' if __name__ == "__main__": if '--help' in sys.argv: # unfortunately this help precedes distutils help if sys.platform == "win32": print(windows_help) else: print(unix_help) # invoke setup without configuring pycurl because # configuration might fail, and we want to display help anyway. # we need to remove our options because distutils complains about them strip_pycurl_options() setup(**setup_args) elif len(sys.argv) > 1 and sys.argv[1] == 'manifest': check_manifest() else: configure() setup_args['data_files'] = get_data_files() ext = get_extension() setup_args['ext_modules'] = [ext] for o in ext.extra_objects: assert os.path.isfile(o), o setup(**setup_args) pycurl-7.19.3/winbuild.py0000644000470500047050000001554712263706602014722 0ustar piepie00000000000000# work directory for downloading dependencies and building everything root = 'c:/dev/build-pycurl' # where msysgit is installed git_root = 'c:/program files/git' # which versions of python to build against python_versions = ['2.6', '2.7', '3.2', '3.3'] # where pythons are installed python_path_template = 'c:/python%s/python' vc_paths = { # where msvc 9 is installed, for python 2.6 through 3.2 'vc9': 'c:/program files/microsoft visual studio 9.0', # where msvc 10 is installed, for python 3.3 'vc10': 'c:/program files/microsoft visual studio 10.0', } # whether to link libcurl against zlib use_zlib = False # which version of zlib to use, will be downloaded from internet zlib_version = '1.2.8' # which version of libcurl to use, will be downloaded from the internet libcurl_version = '7.34.0' # pycurl version to build, we should know this ourselves pycurl_version = '7.19.3' import os, os.path, sys, subprocess, shutil, contextlib archives_path = os.path.join(root, 'archives') state_path = os.path.join(root, 'state') git_bin_path = os.path.join(git_root, 'bin') git_path = os.path.join(git_bin_path, 'git') rm_path = os.path.join(git_bin_path, 'rm') tar_path = os.path.join(git_bin_path, 'tar') for key in vc_paths: vc_paths[key] = { 'root': vc_paths[key], 'vsvars': os.path.join(vc_paths[key], 'common7/tools/vsvars32.bat'), } python_vc_versions = { '2.6': 'vc9', '2.7': 'vc9', '3.2': 'vc9', '3.3': 'vc10', } vc_versions = vc_paths.keys() try: from urllib.request import urlopen except ImportError: from urllib import urlopen def fetch(url, archive=None): if archive is None: archive = os.path.basename(url) if not os.path.exists(archive): sys.stdout.write("Fetching %s\n" % url) io = urlopen(url) with open('.tmp.%s' % archive, 'wb') as f: while True: chunk = io.read(65536) if len(chunk) == 0: break f.write(chunk) os.rename('.tmp.%s' % archive, archive) @contextlib.contextmanager def in_dir(dir): old_cwd = os.getcwd() try: os.chdir(dir) yield finally: os.chdir(old_cwd) @contextlib.contextmanager def step(step_fn, *args): step = step_fn.__name__ if args: step += '-' + '-'.join(args) if not os.path.exists(state_path): os.makedirs(state_path) state_file_path = os.path.join(state_path, step) if not os.path.exists(state_file_path): step_fn(*args) with open(state_file_path, 'w') as f: pass def untar(basename): if os.path.exists(basename): shutil.rmtree(basename) subprocess.check_call([tar_path, 'xf', '%s.tar.gz' % basename]) def rename_for_vc(basename, vc_version): suffixed_dir = '%s-%s' % (basename, vc_version) if os.path.exists(suffixed_dir): shutil.rmtree(suffixed_dir) os.rename(basename, suffixed_dir) return suffixed_dir def work(): os.environ['PATH'] += ";%s" % git_bin_path if not os.path.exists(archives_path): os.makedirs(archives_path) with in_dir(archives_path): def build_zlib(vc_version): fetch('http://downloads.sourceforge.net/project/libpng/zlib/%s/zlib-%s.tar.gz' % (zlib_version, zlib_version)) untar('zlib-%s' % zlib_version) zlib_dir = rename_for_vc('zlib-%s' % zlib_version, vc_version) with in_dir(zlib_dir): with open('doit.bat', 'w') as f: f.write("call \"%s\"\n" % vc_paths[vc_version]['vsvars']) f.write("nmake /f win32/Makefile.msc\n") subprocess.check_call(['doit.bat']) def build_curl(vc_version): fetch('http://curl.haxx.se/download/curl-%s.tar.gz' % libcurl_version) untar('curl-%s' % libcurl_version) curl_dir = rename_for_vc('curl-%s' % libcurl_version, vc_version) with in_dir(os.path.join(curl_dir, 'winbuild')): with open('doit.bat', 'w') as f: f.write("call \"%s\"\n" % vc_paths[vc_version]['vsvars']) f.write("set include=%%include%%;%s\n" % os.path.join(archives_path, 'zlib-%s-%s' % (zlib_version, vc_version))) f.write("set lib=%%lib%%;%s\n" % os.path.join(archives_path, 'zlib-%s-%s' % (zlib_version, vc_version))) if use_zlib: extra_options = ' WITH_ZLIB=dll' else: extra_options = '' f.write("nmake /f Makefile.vc mode=dll ENABLE_IDN=no\n") subprocess.check_call(['doit.bat']) for vc_version in vc_versions: if use_zlib: step(build_zlib, vc_version) step(build_curl, vc_version) def prepare_pycurl(): #fetch('http://pycurl.sourceforge.net/download/pycurl-%s.tar.gz' % pycurl_version) if os.path.exists('pycurl-%s' % pycurl_version): #shutil.rmtree('pycurl-%s' % pycurl_version) subprocess.check_call([rm_path, '-rf', 'pycurl-%s' % pycurl_version]) #subprocess.check_call([tar_path, 'xf', 'pycurl-%s.tar.gz' % pycurl_version]) shutil.copytree('c:/dev/pycurl', 'pycurl-%s' % pycurl_version) def build_pycurl(python_version, target): python_path = python_path_template % python_version.replace('.', '') vc_version = python_vc_versions[python_version] with in_dir(os.path.join('pycurl-%s' % pycurl_version)): if use_zlib: libcurl_build_name = 'libcurl-vc-x86-release-dll-zlib-dll-ipv6-sspi-spnego-winssl' else: libcurl_build_name = 'libcurl-vc-x86-release-dll-ipv6-sspi-spnego-winssl' curl_dir = '../curl-%s-%s/builds/%s' % (libcurl_version, vc_version, libcurl_build_name) if not os.path.exists('build/lib.win32-%s' % python_version): # exists for building additional targets for the same python version os.makedirs('build/lib.win32-%s' % python_version) shutil.copy(os.path.join(curl_dir, 'bin', 'libcurl.dll'), 'build/lib.win32-%s' % python_version) with open('doit.bat', 'w') as f: f.write("call \"%s\"\n" % vc_paths[vc_version]['vsvars']) f.write("%s setup.py %s --curl-dir=%s --use-libcurl-dll\n" % (python_path, target, curl_dir)) subprocess.check_call(['doit.bat']) if target == 'bdist': os.rename('dist/pycurl-%s.win32.zip' % pycurl_version, 'dist/pycurl-%s.win32-py%s.zip' % (pycurl_version, python_version)) prepare_pycurl() for python_version in python_versions: for target in ['bdist', 'bdist_wininst', 'bdist_msi']: build_pycurl(python_version, target) work() pycurl-7.19.3/PKG-INFO0000644000470500047050000000175612263734634013633 0ustar piepie00000000000000Metadata-Version: 1.1 Name: pycurl Version: 7.19.3 Summary: PycURL -- cURL library module for Python Home-page: http://pycurl.sourceforge.net/ Author: Oleg Pudeyev Author-email: oleg@bsdpower.com License: LGPL/MIT Download-URL: http://pycurl.sourceforge.net/download/ Description: This module provides Python bindings for the cURL library. Keywords: curl,libcurl,urllib,wget,download,file transfer,http,www Platform: All Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL) Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: Internet :: File Transfer Protocol (FTP) Classifier: Topic :: Internet :: WWW/HTTP