[Python-modules-commits] [pycurl] 01/140: Import pycurl_7.13.0.orig.tar.gz
Barry Warsaw
barry at moszumanska.debian.org
Wed Oct 1 21:44:59 UTC 2014
This is an automated email from the git hooks/post-receive script.
barry pushed a commit to branch master
in repository pycurl.
commit 5cdfa85e9eb0e7c8d73ce4e8a9a1ae8839a39b66
Author: Barry Warsaw <barry at python.org>
Date: Wed Oct 1 16:43:19 2014 -0400
Import pycurl_7.13.0.orig.tar.gz
---
COPYING | 504 ++++++++
ChangeLog | 731 +++++++++++
INSTALL | 44 +
MANIFEST.in | 22 +
Makefile | 60 +
PKG-INFO | 11 +
README | 12 +
TODO | 32 +
doc/callbacks.html | 140 +++
doc/curlmultiobject.html | 136 ++
doc/curlobject.html | 102 ++
doc/pycurl.html | 119 ++
examples/basicfirst.py | 27 +
examples/file_upload.py | 54 +
examples/linksys.py | 563 +++++++++
examples/retriever-multi.py | 125 ++
examples/retriever.py | 79 ++
examples/sfquery.py | 64 +
examples/xmlrpc_curl.py | 61 +
python/curl/__init__.py | 146 +++
setup.py | 199 +++
setup_win32_ssl.py | 34 +
src/Makefile | 19 +
src/pycurl.c | 2732 +++++++++++++++++++++++++++++++++++++++++
tests/test.py | 74 ++
tests/test_cb.py | 28 +
tests/test_debug.py | 16 +
tests/test_getinfo.py | 49 +
tests/test_gtk.py | 93 ++
tests/test_internals.py | 253 ++++
tests/test_memleak.py | 53 +
tests/test_multi.py | 33 +
tests/test_multi2.py | 72 ++
tests/test_multi3.py | 87 ++
tests/test_multi4.py | 57 +
tests/test_multi5.py | 60 +
tests/test_multi6.py | 62 +
tests/test_multi_vs_thread.py | 262 ++++
tests/test_post.py | 24 +
tests/test_post2.py | 16 +
tests/test_post3.py | 32 +
tests/test_stringio.py | 25 +
tests/test_xmlrpc.py | 29 +
tests/util.py | 38 +
44 files changed, 7379 insertions(+)
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..99dce33
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,504 @@
+ 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.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..f02bd3c
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,731 @@
+Version 7.13.0 [requires libcurl-7.13.0 or better]
+--------------
+
+2005-02-10 Kjetil Jacobsen <kjetilja>
+
+ * 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 <mfx>
+
+ * 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 <kjetilja>
+
+ * Added CURLOPT_FTPSSLAUTH (and CURLFTPAUTH_*).
+
+ * Added CURLINFO_OS_ERRNO.
+
+2004-08-17 Kjetil Jacobsen <kjetilja>
+
+ * Use LONG_LONG instead of PY_LONG_LONG to make pycurl compile
+ on Python versions < 2.3 (fix from Domenico Andreoli
+ <cavok at libero.it>).
+
+
+Version 7.12.1
+--------------
+
+2004-08-02 Kjetil Jacobsen <kjetilja>
+
+ * Added INFOTYPE_SSL_DATA_IN/OUT.
+
+2004-07-16 Markus F.X.J. Oberhumer <mfx>
+
+ * WARNING: removed deprecated PROXY_, TIMECOND_ and non-prefixed
+ INFOTYPE constant names. See ChangeLog entry 2003-06-10.
+
+2004-06-21 Kjetil Jacobsen <kjetilja>
+
+ * 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 <mfx>
+
+ * 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 <mfx>
+
+ * 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 <kjetilja>
+
+ * 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 <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Added support for libcurl 7.11.2 CURLOPT features:
+ CURLOPT_TCP_NODELAY.
+
+2004-03-25 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * WARNING: Removed support for the PASSWDFUNCTION callback, which
+ is no longer supported by libcurl.
+
+2004-03-15 Kjetil Jacobsen <kjetilja>
+
+ * Added support for libcurl 7.11.1 CURLOPT features:
+ CURLOPT_POSTFIELDSIZE_LARGE.
+
+
+Version 7.11.0
+--------------
+
+2004-02-11 Kjetil Jacobsen <kjetilja>
+
+ * 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 <mfx>
+
+ * 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 <mfx>
+
+ * 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 <kjetilja>
+
+ * Added missing CURLOPT_ENCODING option (patch by Martijn
+ Boerwinkel <xim at xs4all.nl>)
+
+
+Version 7.10.6
+--------------
+
+2003-07-29 Markus F.X.J. Oberhumer <mfx>
+
+ * 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 <mfx>
+
+ * 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 <mfx>
+
+ * Added support for CURLOPT_FTP_USE_EPRT (libcurl-7.10.5).
+
+ * Documentation updates.
+
+2003-05-07 Eric S. Raymond <esr at snark.thyrsus.com>
+
+ * Lifted all HTML docs to clean XHTML, verified by tidy.
+
+2003-05-02 Markus F.X.J. Oberhumer <mfx>
+
+ * Fixed some `int' vs. `long' mismatches that affected 64-bit systems.
+
+ * Fixed wrong pycurl.CAPATH constant.
+
+2003-05-01 Markus F.X.J. Oberhumer <mfx>
+
+ * 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 <mfx>
+
+ * 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 <kjetilja>
+
+ * Markus added CURLOPT_UNRESTRICTED_AUTH (libcurl-7.10.4).
+
+2003-02-25 Kjetil Jacobsen <kjetilja>
+
+ * Fixed some broken test code and removed the fileupload test
+ since it didn't work properly.
+
+2003-01-28 Kjetil Jacobsen <kjetilja>
+
+ * Some documentation updates by Markus and me.
+
+2003-01-22 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * PycURL memory usage has been reduced.
+
+2003-01-10 Kjetil Jacobsen <kjetilja>
+
+ * Added 'examples/retriever-multi.py' which shows how to retrieve
+ a set of URLs concurrently using the multi interface.
+
+2003-01-09 Kjetil Jacobsen <kjetilja>
+
+ * Added support for CURLOPT_HTTP200ALIASES.
+
+2002-11-22 Kjetil Jacobsen <kjetilja>
+
+ * Updated pycurl documentation in the 'doc' directory.
+
+2002-11-21 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Added new method CurlMulti.info_read().
+
+
+Version 7.10.2
+--------------
+
+2002-11-14 Kjetil Jacobsen <kjetilja>
+
+ * 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
+ <bastiaan at ricardis.tudelft.nl>.
+
+2002-11-06 Markus F.X.J. Oberhumer <mfx>
+
+ * 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 <mfx>
+
+ * 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 <mfx>
+
+ * Added new module-level function version_info() from
+ libcurl-7.10.
+
+
+Version 7.10
+------------
+
+2002-09-13 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * 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 <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Added support for curl_multi_fdset. See the file
+ 'tests/test_multi4.py' for example usage. Contributed by Conrad
+ Steenberg <conrad at hep.caltech.edu>.
+
+ * perform() on multi objects now returns a tuple (result, number
+ of handles) like the libcurl interface does.
+
+2002-08-08 Kjetil Jacobsen <kjetilja>
+
+ * 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 <esr at thyrsus.com>.
+
+2002-07-20 Markus F.X.J. Oberhumer <mfx>
+
+ * 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 <mfx>
+
+ * 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 <mfx>
+
+ * Curl and CurlMulti objects now support standard Python attributes.
+ See tests/test_multi2.py for an example.
+
+2002-07-02 Kjetil Jacobsen <kjetilja>
+
+ * Added support for the multi-interface.
+
+
+Version 7.9.8.1
+---------------
+
+2002-06-25 Markus F.X.J. Oberhumer <mfx>
+
+ * Fixed a couple of `int' vs. `size_t' mismatches in callbacks
+ and Py_BuildValue() calls.
+
+2002-06-25 Kjetil Jacobsen <kjetilja>
+
+ * Use 'double' type instead of 'size_t' for progress callbacks
+ (by Conrad Steenberg <conrad at hep.caltech.edu>). Also cleaned up
+ some other type mismatches in the callback interfaces.
+
+2002-06-24 Kjetil Jacobsen <kjetilja>
+
+ * Added example code on how to upload a file using HTTPPOST in
+ pycurl (code by Amit Mongia <amit_mongia at hotmail.com>). See the
+ file 'test_fileupload.py' for details.
+
+
+Version 7.9.8
+-------------
+
+2002-06-24 Kjetil Jacobsen <kjetilja>
+
+ * Resolved some build problems on Windows (by Markus Oberhumer).
+
+2002-06-19 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Made some changes to setup.py which should fix the build
+ problems on RedHat 7.3 (suggested by Benji <benji at kioza.net>).
+
+ * 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 <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Added CURLINFO_REDIRECT_TIME and CURLINFO_REDIRECT_COUNT.
+
+2002-04-27 Kjetil Jacobsen <kjetilja>
+
+ * Fixed potential memory leak and thread race (by Markus
+ Oberhumer).
+
+
+Version 0.4.9
+-------------
+
+2002-04-15 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Added 'test_post2.py' file which is another example on how to
+ issue POST requests.
+
+2002-04-11 Markus F.X.J. Oberhumer <mfx>
+
+ * Added the 'test_post.py' file which demonstrates the use of
+ POST requests.
+
+
+Version 0.4.8
+-------------
+
+2002-03-07 Kjetil Jacobsen <kjetilja>
+
+ * Added CURLOPT_PREQUOTE.
+
+ * Now requires libcurl 7.9.5 or greater.
+
+ * Other minor code cleanups and bugfixes.
+
+2002-03-05 Kjetil Jacobsen <kjetilja>
+
+ * Do not allow WRITEFUNCTION and WRITEHEADER on the same handle.
+
+
+Version 0.4.7
+-------------
+
+2002-02-27 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Clarence Garnder <clarence at silcom.com> found a bug where string
+ arguments to setopt sometimes were prematurely deallocated, this
+ should now be fixed.
+
+2002-02-21 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Fixed docstring for getopt() function.
+
+2001-12-18 Kjetil Jacobsen <kjetilja>
+
+ * Updated the INSTALL information for Win32.
+
+2001-12-12 Kjetil Jacobsen <kjetilja>
+
+ * Added missing link flag to make pycurl build on MacOS X (by Matt
+ King <matt at gnik.com>).
+
+2001-12-06 Kjetil Jacobsen <kjetilja>
+
+ * Added CURLINFO_STARTTRANSFER_TIME and CURLOPT_FTP_USE_EPSV from
+ libcurl 7.9.2.
+
+2001-12-01 Markus F.X.J. Oberhumer <mfx>
+
+ * Added the 'test_stringio.py' file which demonstrates the use of
+ StringIO objects as callback.
+
+2001-12-01 Markus F.X.J. Oberhumer <mfx>
+
+ * setup.py: Do not remove entries from a list while iterating
+ over it.
+
+2001-11-29 Kjetil Jacobsen <kjetilja>
+
+ * Added code in setup.py to install on Windows. Requires some
+ manual configuration (by Tino Lange <Tino.Lange at gmx.de>).
+
+2001-11-27 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Some of the newer options to setopt were missing, this should
+ now be fixed.
+
+2001-11-04 Kjetil Jacobsen <kjetilja>
+
+ * Exception handling has been improved and should no longer throw
+ spurious exceptions (by Markus F.X.J. Oberhumer
+ <markus at oberhumer.com>).
+
+2001-10-15 Kjetil Jacobsen <kjetilja>
+
+ * Refactored the test_gtk.py script to avoid global variables.
+
+2001-10-12 Kjetil Jacobsen <kjetilja>
+
+ * 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 <markus at oberhumer.com>).
+
+ * Allow C-functions and Python methods as callbacks (by Markus
+ F.X.J. Oberhumer <markus at oberhumer.com>).
+
+ * Allow None as success result of write, header and progress
+ callback invocations (by Markus F.X.J. Oberhumer
+ <markus at oberhumer.com>).
+
+ * 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 <kjetilja>
+
+ * Cleaned up the script with GNOME/PycURL integration.
+
+2001-08-20 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Added test script for using PycURL and GNOME (tests/test_gtk.py).
+
+2001-08-20 Kjetil Jacobsen <kjetilja>
+
+ * Added test script for using XML-RPC (tests/test_xmlrpc.py).
+
+ * Added more comments to the test sources.
+
+2001-08-06 Kjetil Jacobsen <kjetilja>
+
+ * Renamed module namespace to pycurl instead of curl.
+
+2001-08-06 Kjetil Jacobsen <kjetilja>
+
+ * Set CURLOPT_VERBOSE to 0 by default.
+
+2001-06-29 Kjetil Jacobsen <kjetilja>
+
+ * Updated INSTALL, curl version 7.8 or greater is now mandatory to
+ use pycurl.
+
+2001-06-13 Kjetil Jacobsen <kjetilja>
+
+ * Set NOPROGRESS to 1 by default.
+
+2001-06-07 Kjetil Jacobsen <kjetilja>
+
+ * Added global_init/cleanup.
+
+2001-06-06 Kjetil Jacobsen <kjetilja>
+
+ * Added HEADER/PROGRESSFUNCTION callbacks (see files in tests/).
+
+ * Added PASSWDFUNCTION callback (untested).
+
+ * Added READFUNCTION callback (untested).
+
+2001-06-05 Kjetil Jacobsen <kjetilja>
+
+ * 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 <kjetilja>
+
+ * Return -1 on error from Python callback in WRITEFUNCTION callback.
+
+2001-06-01 Kjetil Jacobsen <kjetilja>
+
+ * Moved source to src and tests to tests directory.
+
+2001-05-31 Kjetil Jacobsen <kjetilja>
+
+ * Added better type checking for setopt.
+
+2001-05-30 Kjetil Jacobsen <kjetilja>
+
+ * Moved code to sourceforge.
+
+ * Added getinfo support.
+
+
+# vi:ts=8:et
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..ef42cf6
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,44 @@
+NOTE: 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
+---------
+
+Assuming that distutils is installed (which it is by default on Python
+versions greater than 1.5.2) 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 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.
+
+
+Windows
+-------
+
+When installing on Windows, you need to manually configure the path to
+the curl source tree, specified with the CURL_DIR variable in the file
+'setup.py'. The CURL_DIR variable can also be set using the
+commandline option '--curl-dir' when invoking setup.py:
+
+ python setup.py install --curl-dir=c:\curl-7.10.5
+
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..f4e3837
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,22 @@
+#
+# MANIFEST.in
+# Manifest template for creating the source distribution.
+#
+
+include ChangeLog
+include COPYING
+include INSTALL
+include Makefile
+include README
+include TODO
+include MANIFEST.in
+include src/Makefile
+include src/pycurl.c
+include python/curl/*.py
+include examples/*.py
+include tests/*.py
+include doc/*.html
+include setup_win32_ssl.py
+
+# exclude unfinished test scripts
+#exclude tests/test_multi_vs_thread.py
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9b2369d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+#
+# to use a specific python version call
+# `make PYTHON=python2.2'
+#
+
+SHELL = /bin/sh
+
+PYTHON = python2.3
+PYTHON = python
+
+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
+ $(PYTHON) tests/test_internals.py -q
+
+# (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
+
+
+.PHONY: all build test strip install install_lib clean distclean maintainer-clean dist sdist windist
+
+.NOEXPORT:
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..7012c4e
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,11 @@
+Metadata-Version: 1.0
+Name: pycurl
+Version: 7.13.0
+Summary: PycURL -- cURL library module for Python
+Home-page: http://pycurl.sourceforge.net/
+Author: Kjetil Jacobsen, Markus F.X.J. Oberhumer
+Author-email: kjetilja at cs.uit.no, markus at oberhumer.com
+License: GNU Lesser General Public License (LGPL)
+Description:
+ This module provides Python bindings for the cURL library.
+Platform: All
diff --git a/README b/README
new file mode 100644
index 0000000..bd04ab6
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+LICENSE
+-------
+
+Copyright (C) 2001-2005 by Kjetil Jacobsen <kjetilja at cs.uit.no>
+Copyright (C) 2001-2005 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
+
+PycURL 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.
+
+A full copy of the LGPL license is included in the file COPYING.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..8696e58
--- /dev/null
+++ b/TODO
@@ -0,0 +1,32 @@
+# $Id: TODO,v 1.92 2005/02/08 11:46:05 kjetilja Exp $
+# vi:ts=4:et
+
+If you want to hack on pycurl, here's our list of unresolved issues:
+
+
+NEW FEATURES/IMPROVEMENTS:
+
+ * Support more of the CURLFORM_* API in HTTPPOST?
+
+ * Add docs to the high-level interface.
+
+ * Add more options to the undocumented and currently mostly useless
+ Curl.unsetopt() method. Have to carefully check the libcurl source
+ code for each option we want to support.
+
+ * curl_easy_reset() should probably be supported. But we have to be
+ careful since curl_easy_reset() e.g. modifies callbacks and other
+ pointers which could leave pycurl and libcurl out of sync.
+
+ * Use METH_O and METH_NOARGS where appropriate instead of METH_VARAGS.
+
+
+DEFICIENICES:
+
+ * Using certain invalid options, it may be possible to cause a crash.
+ This is un-Pythonic behaviour, but you somewhere have to draw a line
+ between efficiency (and feature completeness) and safety.
+ There _are_ quite a number of internal error checks, but tracking and
+ catching all possible (deliberate) misuses is not a goal (and probably
+ impossible anyway, due to the complexity of libcurl).
+
diff --git a/doc/callbacks.html b/doc/callbacks.html
new file mode 100644
index 0000000..e300c4d
--- /dev/null
+++ b/doc/callbacks.html
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>PyCurl: Callbacks</title>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+ <meta name="revisit-after" content="30 days" />
+ <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1>Callbacks</h1>
+
+<p>For more fine-grained control, libcurl allows a
+number of callbacks to be associated with each connection. In
+pycurl, callbacks are defined using the <code>setopt()</code> 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.</p>
+
+<p>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.
+</p>
+
+The signature of each callback used in pycurl is as follows:<br/>
+<br/>
+<code>WRITEFUNCTION(</code><em>string</em><code>) </code><em>-> number of characters written<br/>
+</em>
+<br/>
+<code>READFUNCTION(</code><em>number of characters to read</em><code>)</code><em>->
+string</em><br/>
+<br/>
+<code>HEADERFUNCTION(</code><em>string</em><code>)</code><em> -> number of characters written<br/>
+</em><br/>
+<code>PROGRESSFUNCTION(</code><em>download total, downloaded, upload total, uploaded</em><code>) </code><em>-> status</em><br/>
+<br/>
+<code>DEBUGFUNCTION(</code><em>debug message type, debug message string</em><code>)</code>
+<em>-> None<br/></em>
+<br/>
+<code>IOCTLFUNCTION(</code><em>ioctl cmd</em><code>)</code>
+<em>-> status<br/></em>
+<br/>
+<hr/>
+
+<h2>Example: Callbacks for document header and body</h2>
+
+<p>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.</p>
+
+<pre>
+ ## 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()
+</pre>
+
+<h2>Example: Download/upload progress callback</h2>
+
+<p>This example shows how to use the progress callback. When downloading
+a document, the arguments related to uploads are zero, and vice versa.</p>
+
+<pre>
+ ## 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()
+</pre>
+
+<h2>Example: Debug callbacks</h2>
+
+<p>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.</p>
+
+<pre>
+ 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()
+</pre>
+
+<h2>Other examples</h2>
+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.</p>
+
+
+<hr />
+<p>
+ <a href="http://validator.w3.org/check/referer"><img align="right"
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+ $Id: callbacks.html,v 1.15 2005/02/10 11:35:23 kjetilja Exp $
+</p>
+
+</body>
+</html>
diff --git a/doc/curlmultiobject.html b/doc/curlmultiobject.html
new file mode 100644
index 0000000..812111d
--- /dev/null
+++ b/doc/curlmultiobject.html
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>PycURL: CurlMulti Objects</title>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+ <meta name="revisit-after" content="30 days" />
+ <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1>CurlMulti Object</h1>
+
+<p>CurlMulti objects have the following methods: </p>
+
+<dl>
+<dt><code>close()</code> -> <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_cleanup.html"><code>curl_multi_cleanup()</code></a> 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.</p>
+</dd>
+
+<dt><code>perform()</code> -> <em>tuple of status and the number of active Curl objects</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_perform.html"><code>curl_multi_perform()</code></a> in libcurl.</p>
+</dd>
+
+<dt><code> add_handle(</code><em>Curl object</em><code>) </code>-> <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_add_handle.html"><code>curl_multi_add_handle()</code></a> in libcurl.
+This method adds an existing and valid Curl object to the CurlMulti
+object.</p>
+
+<p>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).</p>
+</dd>
+
+<dt><code>remove_handle(</code><em>Curl object</em><code>)</code> -> <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_remove_handle.html"><code>curl_multi_remove_handle()</code></a> in libcurl.
+This method removes an existing and valid Curl object from the CurlMulti
+object.</p>
+
+<p>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).</p>
+</dd>
+
+<dt><code>fdset()</code> ->
+<em>triple of lists with active file descriptors,
+readable, writeable, exceptions.</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_fdset.html"><code>curl_multi_fdset()</code></a> in libcurl.
+This method extracts the file descriptor information from a CurlMulti object.
+The returned lists can be used with the <code>select</code> module to
+poll for events.</p>
+
+<p>Example usage:</p>
+
+<pre>
+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
+</pre>
+</dd>
+
+<dt><code>select(</code><em>[timeout]</em><code>)</code> ->
+<em>number of ready file descriptors or -1 on timeout</em></dt>
+<dd>
+<p>This is a convenience function which simplifies the combined
+use of <code>fdset()</code> and the <code>select</code> module.</p>
+
+<p>Example usage:</p>
+
+<pre>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()
+ if ret == -1: continue
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM: break
+</pre>
+</dd>
+
+<dt><code>info_read(</code><em>[max]</em><code>)</code> ->
+<em>numberof queued messages, a list of successful objects, a list of
+failed objects</em></dt>
+<dd>
+<p>Corresponds to the
+<a href="http://curl.haxx.se/libcurl/c/curl_multi_info_read.html"><code>curl_multi_info_read()</code></a> function in libcurl.
+This method extracts at most <em>max</em> 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 <em><curl object, curl error number, curl
+error message></em> for each failed curl object. The number
+of queued messages after this method has been called is also
+returned.</p>
+</dd>
+</dl>
+
+<hr />
+<p>
+ <a href="http://validator.w3.org/check/referer"><img align="right"
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+ $Id: curlmultiobject.html,v 1.4 2004/06/05 17:59:01 mfx Exp $
+</p>
+
+</body>
+</html>
diff --git a/doc/curlobject.html b/doc/curlobject.html
new file mode 100644
index 0000000..281ba2e
--- /dev/null
+++ b/doc/curlobject.html
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>PycURL: Curl Objects</title>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+ <meta name="revisit-after" content="30 days" />
+ <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1>Curl Object</h1>
+
+<p>Curl objects have the following methods:</p>
+
+<dl>
+<dt><code>close()</code> -> <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_cleanup.html"><code>curl_easy_cleanup</code></a> 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.</p>
+</dd>
+
+<dt><code>perform()</code> -> <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_perform.html"><code>curl_easy_perform</code></a> in libcurl.</p>
+</dd>
+
+<dt><code>setopt(</code><em>option, value</em><code>)</code> -> <em>None</em></dt>
+<dd>
+
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html"><code>curl_easy_setopt</code></a> in libcurl, where
+<em>option</em> is specified with the CURLOPT_* constants in libcurl,
+except that the CURLOPT_ prefix has been removed. The type for
+<em>value</em> depends on the option, and can be either a string,
+integer, long integer, file objects, lists, or functions.</p>
+
+<p>Example usage:</p>
+
+<pre>
+import pycurl
+c = pycurl.Curl()
+c.setopt(pycurl.URL, "http://www.python.org/")
+c.setopt(pycurl.HTTPHEADER, ["User-Agent: PycURL test", "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()
+...
+</pre>
+</dd>
+
+<dt><code>getinfo(</code><em>option</em><code>) </code>-> <em>Result</em></dt>
+<dd>
+
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_easy_getinfo.html"><code>curl_easy_getinfo</code></a> in libcurl, where
+<em>option</em> is the same as the CURLINFO_* constants in libcurl,
+except that the CURLINFO_ prefix has been removed.
+<em>Result</em> contains an integer, float or string, depending on
+which option is given. The <code>getinfo</code> method should
+not be called unless <code>perform</code> has been called and
+finished.</p>
+
+<p>Example usage:</p>
+
+<pre>
+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/"
+</pre>
+</dd>
+
+<dt><code>errstr()</code> -> <em>String</em></dt>
+<dd>
+<p>Returns the internal libcurl error buffer of this handle as a string.</p>
+</dd>
+</dl>
+
+
+<hr />
+<p>
+ <a href="http://validator.w3.org/check/referer"><img align="right"
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+ $Id: curlobject.html,v 1.13 2004/03/15 10:57:04 kjetilja Exp $
+</p>
+
+</body>
+</html>
diff --git a/doc/pycurl.html b/doc/pycurl.html
new file mode 100644
index 0000000..bdbcc10
--- /dev/null
+++ b/doc/pycurl.html
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>PycURL Documentation</title>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+ <meta name="revisit-after" content="30 days" />
+ <meta name="robots" content="noarchive, index, follow" />
+</head>
+<body>
+
+<h1><tt>pycurl</tt> — A Python interface to the cURL library</h1>
+
+<p>The pycurl package is a Python interface to libcurl (<a
+href="http://curl.haxx.se/libcurl/">http://curl.haxx.se/libcurl/</a>). pycurl
+has been successfully built and tested with Python versions from
+2.2 to the current 2.4.x releases.</p>
+
+<p>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.</p>
+
+<p>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
+(<a href="http://curl.haxx.se/libcurl/c/">http://curl.haxx.se/libcurl/c/</a>).</p>
+
+<hr/>
+
+<h1>Module Functionality</h1>
+
+<dl>
+<dt><code>pycurl.global_init(</code><em>option</em><code>)</code> -><em>None</em></dt>
+
+<dd><p><em>option</em> is one of the constants
+pycurl.GLOBAL_SSL, pycurl.GLOBAL_WIN32, pycurl.GLOBAL_ALL,
+pycurl.GLOBAL_NOTHING, pycurl.GLOBAL_DEFAULT. Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_global_init.html"><code>curl_global_init()</code></a> in libcurl.</p>
+</dd>
+
+<dt><code>pycurl.global_cleanup()</code> -> <em>None</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_global_cleanup.html"><code>curl_global_cleanup()</code></a> in libcurl.</p>
+</dd>
+
+<dt><code>pycurl.version</code></dt>
+
+<dd><p>This is a string with version information on libcurl,
+corresponding to
+<a href="http://curl.haxx.se/libcurl/c/curl_version.html"><code>curl_version()</code></a> in libcurl.</p>
+
+<p>Example usage:</p>
+<pre>
+>>> import pycurl
+>>> pycurl.version
+'libcurl/7.12.3 OpenSSL/0.9.7e zlib/1.2.2.1 libidn/0.5.12'
+</pre>
+</dd>
+
+<dt><code>pycurl.version_info()</code> -> <em>Tuple</em></dt>
+<dd>
+<p>Corresponds to
+<a href="http://curl.haxx.se/libcurl/c/curl_version_info.html"><code>curl_version_info()</code></a> in libcurl.
+Returns a tuple of information which is similar to the
+<code>curl_version_info_data</code> struct returned by
+<code>curl_version_info()</code> in libcurl.</p>
+
+<p>Example usage:</p>
+<pre>
+>>> 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')
+</pre>
+</dd>
+
+<dt><code>pycurl.Curl()</code> -> <em>Curl object</em></dt>
+<dd>
+<p>This function creates a new
+<a href="curlobject.html">Curl object</a> which corresponds to a
+<code>CURL</code> handle in libcurl. Curl objects automatically
+set CURLOPT_VERBOSE to 0, CURLOPT_NOPROGRESS to 1 and
+CURLOPT_ERRORBUFFER to point to a private error buffer.</p>
+</dd>
+
+<dt><code>pycurl.CurlMulti()</code> -> <em>CurlMulti object</em></dt>
+<dd>
+<p>This function creates a new
+<a href="curlmultiobject.html">CurlMulti object</a> which corresponds to
+a <code>CURLM</code> handle in libcurl.</p>
+</dd>
+</dl>
+
+<hr/>
+
+<h1>Subsections</h1>
+
+<ul>
+ <li><a href="curlobject.html">Curl objects</a></li>
+ <li><a href="curlmultiobject.html">CurlMulti objects</a></li>
+ <li><a href="callbacks.html">Callbacks</a></li>
+</ul>
+
+<hr />
+<p>
+ <a href="http://validator.w3.org/check/referer"><img align="right"
+ src="http://www.w3.org/Icons/valid-xhtml10"
+ alt="Valid XHTML 1.0!" height="31" width="88" border="0" /></a>
+ $Id: pycurl.html,v 1.27 2004/12/22 14:27:16 mfx Exp $
+</p>
+
+</body>
+</html>
diff --git a/examples/basicfirst.py b/examples/basicfirst.py
new file mode 100644
index 0000000..a63c4be
--- /dev/null
+++ b/examples/basicfirst.py
@@ -0,0 +1,27 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: basicfirst.py,v 1.4 2003/05/07 17:46:58 esr Exp $
+
+import sys
+import pycurl
+
+class Test:
+ def __init__(self):
+ self.contents = ''
+
+ def body_callback(self, buf):
+ self.contents = self.contents + buf
+
+print >>sys.stderr, 'Testing', pycurl.version
+
+t = Test()
+c = pycurl.Curl()
+c.setopt(c.URL, 'http://curl.haxx.se/dev/')
+c.setopt(c.WRITEFUNCTION, t.body_callback)
+c.setopt(c.HTTPHEADER, ["I-am-a-silly-programmer: yes indeed you are",
+ "User-Agent: Python interface for libcURL"])
+c.perform()
+c.close()
+
+print t.contents
diff --git a/examples/file_upload.py b/examples/file_upload.py
new file mode 100644
index 0000000..ad690cb
--- /dev/null
+++ b/examples/file_upload.py
@@ -0,0 +1,54 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: file_upload.py,v 1.3 2005/02/10 11:26:23 kjetilja Exp $
+
+import pycurl
+import sys
+import os.path
+
+# Class which holds a file reference and the read callback
+class filereader:
+
+ def __init__(self, f):
+ self.f = f
+
+ def read_callback(self, size):
+ return self.f.read(size)
+
+# Check commandline arguments
+if len(sys.argv) < 3:
+ print "Usage: %s <url> <file to upload>" % sys.argv[0]
+ raise SystemExit
+else:
+ 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, use LARGE option if file size is
+# greater than 2GB
+filesize = os.path.getsize(filename)
+if filesize > 2**31:
+ c.setopt(pycurl.INFILESIZE_LARGE, filesize)
+else:
+ c.setopt(pycurl.INFILESIZE, filesize)
+
+# Start transfer
+print 'Uploading file %s to url %s' % (filename, url)
+c.perform()
+c.close()
diff --git a/examples/linksys.py b/examples/linksys.py
new file mode 100755
index 0000000..a60eba1
--- /dev/null
+++ b/examples/linksys.py
@@ -0,0 +1,563 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# 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
+
+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 >>sys.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):
+ 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):
+ if line.strip() in ("on", "enable", "yes"):
+ func(True)
+ elif line.strip() in ("off", "disable", "no"):
+ func(False)
+ else:
+ print >>sys.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 [<hostname-or-IP>]"
+ 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)
+ 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 <hostname>"
+ print "Sets the Host field to be queried by the ISP."
+
+ def do_domain(self, line):
+ print "Usage: host <domainname>"
+ 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 <ip-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 <ip-mask>"
+ print "Sets the LAN subnetwork mask."
+
+ def do_wireless(self, line):
+ self.flag_command(self.session.set_wireless)
+ 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 <string>"
+ print "Sets the SSID used to control wireless access."
+
+ def do_ssid_broadcast(self, line):
+ self.flag_command(self.session.set_SSID_broadcast)
+ 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 <number>"
+ print "Sets the wireless channel."
+
+ def do_wep(self, line):
+ self.flag_command(self.session.set_WEP)
+ 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 >>sys.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 <ip-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 <ip-mask>"
+ 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 <ip-address>"
+ 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 >>sys.stderr, "linksys: server index out of bounds."
+ return 0
+ def help_dns(self):
+ print "Usage: dns {1|2|3} <ip-mask>"
+ 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 <string>"
+ print "Sets the router password."
+
+ def do_upnp(self, line):
+ self.flag_command(self.session.set_UPnP)
+ 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)
+ 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 <number>"
+ 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 <number>"
+ 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 <number>"
+ 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 >>sys.stderr, "linksys: server index out of bounds."
+ return 0
+ def help_dhcp_dns(self):
+ print "Usage: dhcp_dns {1|2|3} <ip-mask>"
+ print "Sets primary, secondary, or tertiary DNS server address."
+
+ def do_logging(self, line):
+ self.flag_command(self.session.set_logging)
+ 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 <number>"
+ 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:
diff --git a/examples/retriever-multi.py b/examples/retriever-multi.py
new file mode 100644
index 0000000..549810e
--- /dev/null
+++ b/examples/retriever-multi.py
@@ -0,0 +1,125 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: retriever-multi.py,v 1.23 2005/01/08 19:15:42 mfx Exp $
+
+#
+# Usage: python retriever-multi.py <file with URLs to fetch> [<# of
+# concurrent connections>]
+#
+
+import string, 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:
+ urls = open(sys.argv[1]).readlines()
+ if len(sys.argv) >= 3:
+ num_conn = int(sys.argv[2])
+except:
+ print "Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0]
+ raise SystemExit
+
+
+# Make a queue with (url, filename) tuples
+queue = []
+fileno = 1
+for url in urls:
+ url = string.strip(url)
+ if not url or url[0] == "#":
+ continue
+ filename = "doc_%d" % (fileno)
+ queue.append((url, filename))
+ fileno = fileno + 1
+del fileno, url, urls
+
+
+# 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 -----"
+
+
+# Preallocate 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.HTTPHEADER, ["User-Agent: PycURL"])
+ 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()
+
+
+# Cleanup
+for c in m.handles:
+ if c.fp is not None:
+ c.fp.close()
+ c.fp = None
+ c.close()
+m.close()
+
+# Delete objects (just for testing the refcounts)
+del c, m, freelist, queue
+
diff --git a/examples/retriever.py b/examples/retriever.py
new file mode 100644
index 0000000..3d6990a
--- /dev/null
+++ b/examples/retriever.py
@@ -0,0 +1,79 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: retriever.py,v 1.15 2004/12/26 17:31:53 mfx Exp $
+
+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
+
+
+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
+ f = open(filename, "wb")
+ curl = pycurl.Curl()
+ curl.setopt(pycurl.HTTPHEADER, ["User-Agent: PycURL"])
+ curl.setopt(pycurl.FOLLOWLOCATION, 1)
+ curl.setopt(pycurl.MAXREDIRS, 5)
+ curl.setopt(pycurl.URL, url)
+ curl.setopt(pycurl.WRITEDATA, f)
+ curl.setopt(pycurl.NOSIGNAL, 1)
+ curl.setopt(pycurl.CONNECTTIMEOUT, 30)
+ curl.setopt(pycurl.TIMEOUT, 300)
+ try:
+ curl.perform()
+ except:
+ import traceback
+ traceback.print_exc(file=sys.stderr)
+ sys.stderr.flush()
+ curl.close()
+ f.close()
+ sys.stdout.write(".")
+ sys.stdout.flush()
+
+# Read list of URLs from file specified on commandline
+try:
+ urls = open(sys.argv[1]).readlines()
+ num_workers = int(sys.argv[2])
+except:
+ # File or number of workers was not specified, show usage string
+ print "Usage: %s <file with URLs to fetch> <number of worker threads>" % sys.argv[0]
+ raise SystemExit
+
+# Initialize thread array and the file number used to store documents
+threads = []
+fileno = 0
+queue = Queue.Queue()
+
+# Fill the work input queue with URLs
+for url in urls:
+ fileno = fileno + 1
+ filename = "doc_%d" % (fileno,)
+ queue.put((url, filename))
+
+# Start a bunch of threads
+for num_threads in range(num_workers):
+ t = WorkerThread(queue)
+ t.start()
+ threads.append(t)
+
+# Wait for all threads to finish
+for thread in threads:
+ thread.join()
diff --git a/examples/sfquery.py b/examples/sfquery.py
new file mode 100644
index 0000000..0c63f61
--- /dev/null
+++ b/examples/sfquery.py
@@ -0,0 +1,64 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# 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
+
+assert sys.version[:3] >= "2.2", "requires Python 2.2 or better"
+
+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:
+ 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)
+
diff --git a/examples/xmlrpc_curl.py b/examples/xmlrpc_curl.py
new file mode 100644
index 0000000..640db0e
--- /dev/null
+++ b/examples/xmlrpc_curl.py
@@ -0,0 +1,61 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: xmlrpc_curl.py,v 1.9 2004/12/26 17:31:53 mfx Exp $
+
+# 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:
+ from StringIO import StringIO
+import xmlrpclib, pycurl
+
+
+class CURLTransport(xmlrpclib.Transport):
+ """Handles a cURL HTTP transaction to an XML-RPC server."""
+
+ xmlrpc_h = [ "User-Agent: PycURL XML-RPC", "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))
+
+ 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
diff --git a/python/curl/__init__.py b/python/curl/__init__.py
new file mode 100644
index 0000000..8fecb4d
--- /dev/null
+++ b/python/curl/__init__.py
@@ -0,0 +1,146 @@
+# 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.
+#
+# If you want thread-safe operation, you'll have to set the NOSIGNAL option
+# yourself.
+#
+# By Eric S. Raymond, April 2003.
+
+import os, sys, urllib, exceptions, mimetools, pycurl
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+
+class Curl:
+ "High-level interface to cURL 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 = ""
+ self.header = StringIO()
+ # 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)
+ # 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)
+ # Set up a callback to capture the payload
+ def payload_callback(x):
+ self.payload += x
+ self.set_option(pycurl.WRITEFUNCTION, payload_callback)
+ def header_callback(x):
+ self.header.write(x)
+ self.set_option(pycurl.HEADERFUNCTION, header_callback)
+
+ def set_timeout(self, timeout):
+ "Set timeout for connect and object retrieval (applies for both)"
+ self.set_option(pycurl.CONNECTTIMEOUT, timeout)
+ 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,"
+ apply(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,os.path.join(self.base_url,relative_url))
+ self.header.seek(0,0)
+ self.payload = ""
+ self.handle.perform()
+ return self.payload
+
+ def get(self, url="", params=None):
+ "Ship a GET request for a specified URL, capture the response."
+ if params:
+ url += "?" + urllib.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.urlencode(params))
+ return self.__request(cgi)
+
+ def body(self):
+ "Return the body from the last response."
+ return self.payload
+
+ def info(self):
+ "Return an RFC822 object with info on the page."
+ self.header.seek(0,0)
+ url = self.handle.getinfo(pycurl.EFFECTIVE_URL)
+ if url[:5] == 'http:':
+ self.header.readline()
+ m = mimetools.Message(self.header)
+ else:
+ m = mimetools.Message(StringIO())
+ m['effective-url'] = url
+ m['http-code'] = str(self.handle.getinfo(pycurl.HTTP_CODE))
+ m['total-time'] = str(self.handle.getinfo(pycurl.TOTAL_TIME))
+ m['namelookup-time'] = str(self.handle.getinfo(pycurl.NAMELOOKUP_TIME))
+ m['connect-time'] = str(self.handle.getinfo(pycurl.CONNECT_TIME))
+ m['pretransfer-time'] = str(self.handle.getinfo(pycurl.PRETRANSFER_TIME))
+ m['redirect-time'] = str(self.handle.getinfo(pycurl.REDIRECT_TIME))
+ m['redirect-count'] = str(self.handle.getinfo(pycurl.REDIRECT_COUNT))
+ m['size-upload'] = str(self.handle.getinfo(pycurl.SIZE_UPLOAD))
+ m['size-download'] = str(self.handle.getinfo(pycurl.SIZE_DOWNLOAD))
+ m['speed-upload'] = str(self.handle.getinfo(pycurl.SPEED_UPLOAD))
+ m['header-size'] = str(self.handle.getinfo(pycurl.HEADER_SIZE))
+ m['request-size'] = str(self.handle.getinfo(pycurl.REQUEST_SIZE))
+ m['content-length-download'] = str(self.handle.getinfo(pycurl.CONTENT_LENGTH_DOWNLOAD))
+ m['content-length-upload'] = str(self.handle.getinfo(pycurl.CONTENT_LENGTH_UPLOAD))
+ m['content-type'] = (self.handle.getinfo(pycurl.CONTENT_TYPE) or '').strip(';')
+ 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."
+ self.handle.close()
+ self.header.close()
+
+ 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'
+ print c.info()
+ c.close()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..9fe4054
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,199 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: setup.py,v 1.121 2005/02/04 01:38:20 mfx Exp $
+
+"""Setup script for the PycURL module distribution."""
+
+PACKAGE = "pycurl"
+PY_PACKAGE = "curl"
+VERSION = "7.13.0"
+
+import glob, os, re, sys, string
+import distutils
+from distutils.core import setup
+from distutils.extension import Extension
+from distutils.util import split_quoted
+from distutils.version import LooseVersion
+
+include_dirs = []
+define_macros = []
+library_dirs = []
+libraries = []
+runtime_library_dirs = []
+extra_objects = []
+extra_compile_args = []
+extra_link_args = []
+
+
+def scan_argv(s, default):
+ p = default
+ i = 1
+ while i < len(sys.argv):
+ arg = sys.argv[i]
+ if string.find(arg, s) == 0:
+ p = arg[len(s):]
+ assert p, arg
+ 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=0):
+ v = os.environ.get(envvar)
+ if not v:
+ return
+ for dir in string.split(v, sep):
+ dir = string.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:
+ print "FATAL: bad directory %s in environment variable %s" % (dir, envvar)
+ sys.exit(1)
+
+
+if sys.platform == "win32":
+ # Windows users have to configure the CURL_DIR path parameter to match
+ # their cURL source installation. The path set here is just an example
+ # and thus unlikely to match your installation.
+ CURL_DIR = r"c:\src\build\pycurl\curl-7.12.3"
+ CURL_DIR = scan_argv("--curl-dir=", CURL_DIR)
+ print "Using curl directory:", CURL_DIR
+ assert os.path.isdir(CURL_DIR), "please check CURL_DIR in setup.py"
+ include_dirs.append(os.path.join(CURL_DIR, "include"))
+ extra_objects.append(os.path.join(CURL_DIR, "lib", "libcurl.lib"))
+ extra_link_args.extend(["gdi32.lib", "winmm.lib", "ws2_32.lib",])
+ add_libdirs("LIB", ";")
+ if string.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
+ extra_link_args.append("/opt:nowin98") # use small section alignment
+else:
+ # Find out the rest the hard way
+ CURL_CONFIG = "curl-config"
+ CURL_CONFIG = scan_argv("--curl-config=", CURL_CONFIG)
+ d = os.popen("'%s' --version" % CURL_CONFIG).read()
+ if d:
+ d = string.strip(d)
+ if not d:
+ raise Exception, ("`%s' not found -- please install the libcurl development files" % CURL_CONFIG)
+ print "Using %s (%s)" % (CURL_CONFIG, d)
+ for e in split_quoted(os.popen("'%s' --cflags" % CURL_CONFIG).read()):
+ if e[:2] == "-I":
+ # do not add /usr/include
+ if not re.search(r"^\/+usr\/+include\/*$", e[2:]):
+ include_dirs.append(e[2:])
+ else:
+ extra_compile_args.append(e)
+ for e in split_quoted(os.popen("'%s' --libs" % CURL_CONFIG).read()):
+ if e[:2] == "-l":
+ libraries.append(e[2:])
+ elif e[:2] == "-L":
+ library_dirs.append(e[2:])
+ else:
+ extra_link_args.append(e)
+ 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 get_kw(**kw): return kw
+
+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)
+
+
+###############################################################################
+
+# 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", "INSTALL", "README", "TODO",]
+ 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)
+
+
+###############################################################################
+
+setup_args = get_kw(
+ name=PACKAGE,
+ version=VERSION,
+ description="PycURL -- cURL library module for Python",
+ author="Kjetil Jacobsen, Markus F.X.J. Oberhumer",
+ author_email="kjetilja at cs.uit.no, markus at oberhumer.com",
+ maintainer="Kjetil Jacobsen, Markus F.X.J. Oberhumer",
+ maintainer_email="kjetilja at cs.uit.no, markus at oberhumer.com",
+ url="http://pycurl.sourceforge.net/",
+ license="GNU Lesser General Public License (LGPL)",
+ data_files=get_data_files(),
+ ext_modules=[ext],
+ long_description="""
+This module provides Python bindings for the cURL library.""",
+)
+
+if sys.version >= "2.2":
+ setup_args["packages"] = [PY_PACKAGE]
+ setup_args["package_dir"] = { PY_PACKAGE: os.path.join('python', 'curl') }
+
+
+##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"]
+
+if __name__ == "__main__":
+ for o in ext.extra_objects:
+ assert os.path.isfile(o), o
+ # We can live with the deprecationwarning for a while
+ apply(setup, (), setup_args)
diff --git a/setup_win32_ssl.py b/setup_win32_ssl.py
new file mode 100644
index 0000000..18e7e3a
--- /dev/null
+++ b/setup_win32_ssl.py
@@ -0,0 +1,34 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: setup_win32_ssl.py,v 1.26 2005/02/04 01:38:20 mfx Exp $
+
+import os, sys, string
+assert sys.platform == "win32", "Only for building on Win32 with SSL and zlib"
+
+
+CURL_DIR = r"c:\src\build\pycurl\curl-7.13.0-ssl"
+OPENSSL_DIR = r"c:\src\build\pycurl\openssl-0.9.7e"
+sys.argv.insert(1, "--curl-dir=" + CURL_DIR)
+
+from setup import *
+
+setup_args["name"] = "pycurl-ssl"
+
+
+for l in ("libeay32.lib", "ssleay32.lib",):
+ ext.extra_objects.append(os.path.join(OPENSSL_DIR, "out32", l))
+
+pool = "\\" + r"pool\win32\vc6" + "\\"
+if string.find(sys.version, "MSC v.1310") >= 0:
+ pool = "\\" + r"pool\win32\vc71" + "\\"
+ext.extra_objects.append(r"c:\src\pool\zlib-1.2.2" + pool + "zlib.lib")
+ext.extra_objects.append(r"c:\src\pool\c-ares-20041212" + pool + "ares.lib")
+ext.extra_objects.append(r"c:\src\pool\libidn-0.5.13" + pool + "idn.lib")
+
+
+if __name__ == "__main__":
+ for o in ext.extra_objects:
+ assert os.path.isfile(o), o
+ apply(setup, (), setup_args)
+
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..a4828d3
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,19 @@
+CC=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
diff --git a/src/pycurl.c b/src/pycurl.c
new file mode 100644
index 0000000..504e0d0
--- /dev/null
+++ b/src/pycurl.c
@@ -0,0 +1,2732 @@
+/* $Id: pycurl.c,v 1.74 2005/02/10 10:17:15 kjetilja Exp $ */
+
+/* PycURL -- cURL Python module
+ *
+ * Authors:
+ * Copyright (C) 2001-2005 by Kjetil Jacobsen <kjetilja at cs.uit.no>
+ * Copyright (C) 2001-2005 by Markus F.X.J. Oberhumer <markus at oberhumer.com>
+ *
+ * Contributions:
+ * Tino Lange <Tino.Lange at gmx.de>
+ * Matt King <matt at gnik.com>
+ * Conrad Steenberg <conrad at hep.caltech.edu>
+ * Amit Mongia <amit_mongia at hotmail.com>
+ * Eric S. Raymond <esr at thyrsus.com>
+ * Martin Muenstermann <mamuema at sourceforge.net>
+ * Domenico Andreoli <cavok at libero.it>
+ *
+ * See file COPYING for license information.
+ *
+ * Some quick info on Python's refcount:
+ * Py_BuildValue does incref the item(s)
+ * PyArg_ParseTuple does NOT incref the item
+ * PyList_Append does incref the item
+ * PyTuple_SET_ITEM does NOT incref the item
+ * PyTuple_SetItem does NOT incref the item
+ * PyXXX_GetItem returns a borrowed reference
+ */
+
+#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
+# define WIN32 1
+#endif
+#if defined(WIN32)
+# define CURL_STATICLIB 1
+#endif
+#include <Python.h>
+#include <sys/types.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <curl/curl.h>
+#include <curl/multi.h>
+#undef NDEBUG
+#include <assert.h>
+
+/* Ensure we have updated versions */
+#if !defined(PY_VERSION_HEX) || (PY_VERSION_HEX < 0x02020000)
+# error "Need Python version 2.2 or greater to compile pycurl."
+#endif
+#if !defined(LIBCURL_VERSION_NUM) || (LIBCURL_VERSION_NUM < 0x070d00)
+# error "Need libcurl version 7.13.0 or greater to compile pycurl."
+#endif
+
+#undef UNUSED
+#define UNUSED(var) ((void)&var)
+
+#undef COMPILE_TIME_ASSERT
+#define COMPILE_TIME_ASSERT(expr) \
+ { typedef int compile_time_assert_fail__[1 - 2 * !(expr)]; }
+
+
+/* Calculate the number of OBJECTPOINT options we need to store */
+#define OPTIONS_SIZE ((int)CURLOPT_LASTENTRY % 10000)
+static int OPT_INDEX(int o)
+{
+ assert(o >= CURLOPTTYPE_OBJECTPOINT);
+ assert(o < CURLOPTTYPE_OBJECTPOINT + OPTIONS_SIZE);
+ return o - CURLOPTTYPE_OBJECTPOINT;
+}
+
+
+static PyObject *ErrorObject = NULL;
+static PyTypeObject *p_Curl_Type = NULL;
+static PyTypeObject *p_CurlMulti_Type = NULL;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *dict; /* Python attributes dictionary */
+ CURLM *multi_handle;
+ PyThreadState *state;
+ fd_set read_fd_set;
+ fd_set write_fd_set;
+ fd_set exc_fd_set;
+} CurlMultiObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *dict; /* Python attributes dictionary */
+ CURL *handle;
+ PyThreadState *state;
+ CurlMultiObject *multi_stack;
+ struct curl_httppost *httppost;
+ struct curl_slist *httpheader;
+ struct curl_slist *http200aliases;
+ struct curl_slist *quote;
+ struct curl_slist *postquote;
+ struct curl_slist *prequote;
+ struct curl_slist *source_prequote;
+ struct curl_slist *source_postquote;
+ /* callbacks */
+ PyObject *w_cb;
+ PyObject *h_cb;
+ PyObject *r_cb;
+ PyObject *pro_cb;
+ PyObject *debug_cb;
+ PyObject *ioctl_cb;
+ /* file objects */
+ PyObject *readdata_fp;
+ PyObject *writedata_fp;
+ PyObject *writeheader_fp;
+ /* misc */
+ void *options[OPTIONS_SIZE]; /* for OBJECTPOINT options */
+ char error[CURL_ERROR_SIZE+1];
+} CurlObject;
+
+/* Throw 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)
+
+/* Throw 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)
+
+
+/* Safe XDECREF for object states that handles nested deallocations */
+#define ZAP(v) do {\
+ PyObject *tmp = (PyObject *)(v); \
+ (v) = NULL; \
+ Py_XDECREF(tmp); \
+} while (0)
+
+
+/*************************************************************************
+// python utility functions
+**************************************************************************/
+
+#if (PY_VERSION_HEX < 0x02030000) && !defined(PY_LONG_LONG)
+# define PY_LONG_LONG LONG_LONG
+#endif
+
+/* 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 *PyString_AsString_NoNUL(PyObject *obj)
+{
+ char *s = NULL;
+ int r;
+ r = PyString_AsStringAndSize(obj, &s, NULL);
+ if (r != 0)
+ return NULL; /* exception already set */
+ assert(s != NULL);
+ return s;
+}
+
+
+/* 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;
+
+ ret = PyList_New(0);
+ if (ret == NULL) goto error;
+
+ for ( ; slist != NULL; slist = slist->next) {
+ PyObject *v = NULL;
+
+ if (slist->data != NULL) {
+ v = PyString_FromString(slist->data);
+ if (v == NULL || PyList_Append(ret, v) != 0) {
+ Py_XDECREF(v);
+ goto error;
+ }
+ Py_DECREF(v);
+ }
+ }
+
+ if ((free_flags & 1) && slist)
+ curl_slist_free_all(slist);
+ return ret;
+
+error:
+ Py_XDECREF(ret);
+ if ((free_flags & 2) && slist)
+ curl_slist_free_all(slist);
+ return NULL;
+}
+
+
+/*************************************************************************
+// 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(self->ob_type == 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;
+}
+
+
+/* assert some CurlObject invariants */
+static void
+assert_curl_state(const CurlObject *self)
+{
+ assert(self != NULL);
+ assert(self->ob_type == p_Curl_Type);
+ (void) get_thread_state(self);
+}
+
+
+/* assert some CurlMultiObject invariants */
+static void
+assert_multi_state(const CurlMultiObject *self)
+{
+ assert(self != NULL);
+ assert(self->ob_type == p_CurlMulti_Type);
+ if (self->state != NULL) {
+ assert(self->multi_handle != NULL);
+ }
+}
+
+
+/* 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;
+ }
+ if ((flags & 2) && get_thread_state(self) != NULL) {
+ PyErr_Format(ErrorObject, "cannot invoke %s() - perform() is currently running", name);
+ return -1;
+ }
+ 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;
+ }
+ if ((flags & 2) && self->state != NULL) {
+ PyErr_Format(ErrorObject, "cannot invoke %s() - multi_perform() is currently running", name);
+ return -1;
+ }
+ return 0;
+}
+
+
+/*************************************************************************
+// 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;
+ self->state = NULL;
+ self->multi_stack = NULL;
+ self->httppost = NULL;
+ self->httpheader = NULL;
+ self->http200aliases = NULL;
+ self->quote = NULL;
+ self->postquote = NULL;
+ self->prequote = NULL;
+ self->source_postquote = NULL;
+ self->source_prequote = NULL;
+
+ /* 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;
+
+ /* Set file object pointers to NULL by default */
+ self->readdata_fp = NULL;
+ self->writedata_fp = NULL;
+ self->writeheader_fp = NULL;
+
+ /* Zero string pointer memory buffer used by setopt */
+ memset(self->options, 0, sizeof(self->options));
+ memset(self->error, 0, sizeof(self->error));
+
+ return self;
+}
+
+
+/* constructor - this is a module-level function returning a new instance */
+static CurlObject *
+do_curl_new(PyObject *dummy, PyObject *args)
+{
+ CurlObject *self;
+ int res;
+
+ UNUSED(dummy);
+ if (!PyArg_ParseTuple(args, ":Curl")) {
+ return NULL;
+ }
+
+ /* 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;
+
+ /* Set curl error buffer and zero it */
+ res = curl_easy_setopt(self->handle, CURLOPT_ERRORBUFFER, self->error);
+ if (res != CURLE_OK)
+ goto error;
+ memset(self->error, 0, sizeof(self->error));
+
+ /* Enable NOPROGRESS by default, i.e. no progress output */
+ res = curl_easy_setopt(self->handle, CURLOPT_NOPROGRESS, (long)1);
+ if (res != CURLE_OK)
+ goto error;
+
+ /* Disable VERBOSE by default, i.e. no verbose output */
+ res = curl_easy_setopt(self->handle, CURLOPT_VERBOSE, (long)0);
+ if (res != CURLE_OK)
+ goto error;
+
+ /* Set backreference */
+ res = curl_easy_setopt(self->handle, CURLOPT_PRIVATE, (char *) self);
+ if (res != CURLE_OK)
+ goto error;
+
+ /* Set FTP_ACCOUNT to NULL by default */
+ res = curl_easy_setopt(self->handle, CURLOPT_FTP_ACCOUNT, NULL);
+ if (res != CURLE_OK)
+ 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 & 1) {
+ /* Decrement refcount for attributes dictionary. */
+ ZAP(self->dict);
+ }
+
+ if (flags & 2) {
+ /* 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 & 4) {
+ /* Decrement refcount for python callbacks. */
+ ZAP(self->w_cb);
+ ZAP(self->h_cb);
+ ZAP(self->r_cb);
+ ZAP(self->pro_cb);
+ ZAP(self->debug_cb);
+ ZAP(self->ioctl_cb);
+ }
+
+ if (flags & 8) {
+ /* Decrement refcount for python file objects. */
+ ZAP(self->readdata_fp);
+ ZAP(self->writedata_fp);
+ ZAP(self->writeheader_fp);
+ }
+}
+
+
+static void
+util_curl_close(CurlObject *self)
+{
+ CURL *handle;
+ int i;
+
+ /* Zero handle and thread-state to disallow any operations to be run
+ * from now on */
+ assert(self != NULL);
+ assert(self->ob_type == 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... */
+ assert(self->state == NULL);
+ assert(self->multi_stack == NULL);
+ return; /* already closed */
+ }
+ self->state = NULL;
+
+ /* Decref multi stuff which uses this handle */
+ util_curl_xdecref(self, 2, 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 callbacks and file handles */
+ util_curl_xdecref(self, 4 | 8, 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);
+ SFREE(self->source_postquote);
+ SFREE(self->source_prequote);
+#undef SFREE
+
+ /* Last, free the options. This must be done after the curl handle
+ * is closed since libcurl assumes that some options are valid when
+ * invoking curl_easy_cleanup(). */
+ for (i = 0; i < OPTIONS_SIZE; i++) {
+ if (self->options[i] != NULL) {
+ free(self->options[i]);
+ self->options[i] = NULL;
+ }
+ }
+}
+
+
+static void
+do_curl_dealloc(CurlObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_TRASHCAN_SAFE_BEGIN(self)
+
+ ZAP(self->dict);
+ util_curl_close(self);
+
+ PyObject_GC_Del(self);
+ Py_TRASHCAN_SAFE_END(self)
+}
+
+
+static PyObject *
+do_curl_close(CurlObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":close")) {
+ return NULL;
+ }
+ if (check_curl_state(self, 2, "close") != 0) {
+ return NULL;
+ }
+ util_curl_close(self);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+do_curl_errstr(CurlObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":errstr")) {
+ return NULL;
+ }
+ if (check_curl_state(self, 1 | 2, "errstr") != 0) {
+ return NULL;
+ }
+ self->error[sizeof(self->error) - 1] = 0;
+ return PyString_FromString(self->error);
+}
+
+
+/* --------------- GC support --------------- */
+
+/* Drop references that may have created reference cycles. */
+static int
+do_curl_clear(CurlObject *self)
+{
+ assert(get_thread_state(self) == NULL);
+ util_curl_xdecref(self, 1 | 2 | 4 | 8, 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(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->readdata_fp);
+ VISIT(self->writedata_fp);
+ VISIT(self->writeheader_fp);
+
+ return 0;
+#undef VISIT
+}
+
+
+/* --------------- perform --------------- */
+
+static PyObject *
+do_curl_perform(CurlObject *self, PyObject *args)
+{
+ int res;
+
+ if (!PyArg_ParseTuple(args, ":perform")) {
+ return NULL;
+ }
+ if (check_curl_state(self, 1 | 2, "perform") != 0) {
+ return NULL;
+ }
+
+ /* Save handle to current thread (used as context for python callbacks) */
+ self->state = PyThreadState_Get();
+ assert(self->state != NULL);
+
+ /* Release global lock and start */
+ Py_BEGIN_ALLOW_THREADS
+ res = curl_easy_perform(self->handle);
+ Py_END_ALLOW_THREADS
+
+ /* Zero thread-state to disallow callbacks to be run from now on */
+ self->state = NULL;
+
+ if (res != CURLE_OK) {
+ CURLERROR_RETVAL();
+ }
+ Py_INCREF(Py_None);
+ return Py_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;
+ PyThreadState *tmp_state;
+ PyObject *arglist;
+ PyObject *result = NULL;
+ size_t ret = 0; /* assume error */
+ PyObject *cb;
+ int total_size;
+
+ /* acquire thread */
+ self = (CurlObject *)stream;
+ tmp_state = get_thread_state(self);
+ if (tmp_state == NULL)
+ return ret;
+ PyEval_AcquireThread(tmp_state);
+
+ /* 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 */
+ arglist = Py_BuildValue("(s#)", ptr, total_size);
+ 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)) {
+ long obj_size = PyInt_AsLong(result);
+ if (obj_size < 0 || obj_size > total_size) {
+ PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size);
+ goto verbose_error;
+ }
+ ret = (size_t) obj_size; /* success */
+ }
+ else if (PyLong_Check(result)) {
+ long obj_size = PyLong_AsLong(result);
+ if (obj_size < 0 || obj_size > total_size) {
+ PyErr_Format(ErrorObject, "invalid return value for write callback %ld %ld", (long)obj_size, (long)total_size);
+ goto verbose_error;
+ }
+ ret = (size_t) obj_size; /* success */
+ }
+ else {
+ PyErr_SetString(ErrorObject, "write callback must return int or None");
+ goto verbose_error;
+ }
+
+done:
+silent_error:
+ Py_XDECREF(result);
+ PyEval_ReleaseThread(tmp_state);
+ 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);
+}
+
+
+static size_t
+read_callback(char *ptr, size_t size, size_t nmemb, void *stream)
+{
+ CurlObject *self;
+ PyThreadState *tmp_state;
+ PyObject *arglist;
+ PyObject *result = NULL;
+
+ size_t ret = CURL_READFUNC_ABORT; /* assume error, this actually works */
+ int total_size;
+
+ /* acquire thread */
+ self = (CurlObject *)stream;
+ tmp_state = get_thread_state(self);
+ if (tmp_state == NULL)
+ return ret;
+ PyEval_AcquireThread(tmp_state);
+
+ /* 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 (PyString_Check(result)) {
+ char *buf = NULL;
+ int obj_size = -1;
+ int r;
+ r = PyString_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 %ld", (long)obj_size, (long)total_size);
+ goto verbose_error;
+ }
+ memcpy(ptr, buf, obj_size);
+ ret = obj_size; /* success */
+ }
+ else {
+ PyErr_SetString(ErrorObject, "read callback must return string");
+ goto verbose_error;
+ }
+
+done:
+silent_error:
+ Py_XDECREF(result);
+ PyEval_ReleaseThread(tmp_state);
+ 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;
+ PyThreadState *tmp_state;
+ PyObject *arglist;
+ PyObject *result = NULL;
+ int ret = 1; /* assume error */
+
+ /* acquire thread */
+ self = (CurlObject *)stream;
+ tmp_state = get_thread_state(self);
+ if (tmp_state == NULL)
+ return ret;
+ PyEval_AcquireThread(tmp_state);
+
+ /* 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);
+ PyEval_ReleaseThread(tmp_state);
+ 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;
+ PyThreadState *tmp_state;
+ PyObject *arglist;
+ PyObject *result = NULL;
+ int ret = 0; /* always success */
+
+ UNUSED(curlobj);
+
+ /* acquire thread */
+ self = (CurlObject *)stream;
+ tmp_state = get_thread_state(self);
+ if (tmp_state == NULL)
+ return ret;
+ PyEval_AcquireThread(tmp_state);
+
+ /* 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);
+ PyEval_ReleaseThread(tmp_state);
+ return ret;
+verbose_error:
+ PyErr_Print();
+ goto silent_error;
+}
+
+
+static curlioerr
+ioctl_callback(CURL *curlobj, int cmd, void *stream)
+{
+ CurlObject *self;
+ PyThreadState *tmp_state;
+ PyObject *arglist;
+ PyObject *result = NULL;
+ int ret = CURLIOE_FAILRESTART; /* assume error */
+
+ UNUSED(curlobj);
+
+ /* acquire thread */
+ self = (CurlObject *)stream;
+ tmp_state = get_thread_state(self);
+ if (tmp_state == NULL)
+ return ret;
+ PyEval_AcquireThread(tmp_state);
+
+ /* check args */
+ if (self->ioctl_cb == NULL)
+ goto silent_error;
+
+ /* run callback */
+ arglist = Py_BuildValue("(i)", (int)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);
+ PyEval_ReleaseThread(tmp_state);
+ return ret;
+verbose_error:
+ PyErr_Print();
+ goto silent_error;
+}
+
+
+/* --------------- unsetopt/setopt/getinfo --------------- */
+
+static PyObject *
+util_curl_unsetopt(CurlObject *self, int option)
+{
+ int res;
+ int opt_index = -1;
+
+#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_HTTPPOST:
+ SETOPT((void *) 0);
+ curl_formfree(self->httppost);
+ self->httppost = NULL;
+ /* FIXME: what about data->set.httpreq ?? */
+ break;
+ case CURLOPT_INFILESIZE:
+ SETOPT((long) -1);
+ break;
+ case CURLOPT_WRITEHEADER:
+ SETOPT((void *) 0);
+ ZAP(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:
+ case CURLOPT_RANDOM_FILE:
+ case CURLOPT_SSL_CIPHER_LIST:
+ case CURLOPT_USERPWD:
+ SETOPT((char *) 0);
+ opt_index = OPT_INDEX(option);
+ break;
+
+ /* info: we explicitly list unsupported options here */
+ case CURLOPT_COOKIEFILE:
+ default:
+ PyErr_SetString(PyExc_TypeError, "unsetopt() is not supported for this option");
+ return NULL;
+ }
+
+ if (opt_index >= 0 && self->options[opt_index] != NULL) {
+ free(self->options[opt_index]);
+ self->options[opt_index] = NULL;
+ }
+
+ Py_INCREF(Py_None);
+ return Py_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;
+
+ 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;
+
+#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 string arguments */
+ if (PyString_Check(obj)) {
+ char *str = NULL;
+ int len = -1;
+ char *buf;
+ int opt_index;
+
+ /* 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_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:
+ 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:
+ case CURLOPT_SOURCE_HOST:
+ case CURLOPT_SOURCE_USERPWD:
+ case CURLOPT_SOURCE_PATH:
+/* FIXME: check if more of these options allow binary data */
+ str = PyString_AsString_NoNUL(obj);
+ if (str == NULL)
+ return NULL;
+ break;
+ case CURLOPT_POSTFIELDS:
+ if (PyString_AsStringAndSize(obj, &str, &len) != 0)
+ return NULL;
+ /* automatically set POSTFIELDSIZE */
+ res = curl_easy_setopt(self->handle, CURLOPT_POSTFIELDSIZE, (long)len);
+ if (res != CURLE_OK) {
+ CURLERROR_RETVAL();
+ }
+ break;
+ default:
+ PyErr_SetString(PyExc_TypeError, "strings are not supported for this option");
+ return NULL;
+ }
+ /* Allocate memory to hold the string */
+ assert(str != NULL);
+ if (len <= 0)
+ buf = strdup(str);
+ else {
+ buf = (char *) malloc(len);
+ if (buf) memcpy(buf, str, len);
+ }
+ if (buf == NULL)
+ return PyErr_NoMemory();
+ /* Call setopt */
+ res = curl_easy_setopt(self->handle, (CURLoption)option, buf);
+ /* Check for errors */
+ if (res != CURLE_OK) {
+ free(buf);
+ CURLERROR_RETVAL();
+ }
+ /* Save allocated option buffer */
+ opt_index = OPT_INDEX(option);
+ if (self->options[opt_index] != NULL) {
+ free(self->options[opt_index]);
+ self->options[opt_index] = NULL;
+ }
+ self->options[opt_index] = buf;
+ Py_INCREF(Py_None);
+ return Py_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_INCREF(Py_None);
+ return Py_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_INCREF(Py_None);
+ return Py_None;
+ }
+
+#undef IS_LONG_OPTION
+#undef IS_OFF_T_OPTION
+
+ /* 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:
+ ZAP(self->readdata_fp);
+ self->readdata_fp = obj;
+ break;
+ case CURLOPT_WRITEDATA:
+ ZAP(self->writedata_fp);
+ self->writedata_fp = obj;
+ break;
+ case CURLOPT_WRITEHEADER:
+ ZAP(self->writeheader_fp);
+ self->writeheader_fp = obj;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ /* Return success */
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ /* Handle the case of list objects */
+ if (PyList_Check(obj)) {
+ struct curl_slist **old_slist = NULL;
+ struct curl_slist *slist = NULL;
+ int 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;
+ case CURLOPT_SOURCE_PREQUOTE:
+ old_slist = &self->source_prequote;
+ break;
+ case CURLOPT_SOURCE_POSTQUOTE:
+ old_slist = &self->source_postquote;
+ break;
+ case CURLOPT_HTTPPOST:
+ break;
+ default:
+ /* None of the list options were recognized, throw exception */
+ PyErr_SetString(PyExc_TypeError, "lists are not supported for this option");
+ return NULL;
+ }
+
+ len = PyList_Size(obj);
+ if (len == 0) {
+ /* Empty list - do nothing */
+ Py_INCREF(Py_None);
+ return Py_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;
+
+ for (i = 0; i < len; i++) {
+ char *nstr = NULL, *cstr = NULL;
+ int nlen = -1, clen = -1;
+ PyObject *listitem = PyList_GetItem(obj, i);
+
+ if (!PyTuple_Check(listitem)) {
+ curl_formfree(post);
+ PyErr_SetString(PyExc_TypeError, "list items must be tuple objects");
+ return NULL;
+ }
+ if (PyTuple_GET_SIZE(listitem) != 2) {
+ curl_formfree(post);
+ PyErr_SetString(PyExc_TypeError, "tuple must contain two items (name and value)");
+ return NULL;
+ }
+ /* FIXME: Only support strings as names and values for now */
+ if (PyString_AsStringAndSize(PyTuple_GET_ITEM(listitem, 0), &nstr, &nlen) != 0 ||
+ PyString_AsStringAndSize(PyTuple_GET_ITEM(listitem, 1), &cstr, &clen) != 0) {
+ curl_formfree(post);
+ PyErr_SetString(PyExc_TypeError, "tuple items must be strings");
+ return NULL;
+ }
+ /* 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);
+ if (res != CURLE_OK) {
+ curl_formfree(post);
+ CURLERROR_RETVAL();
+ }
+ }
+ res = curl_easy_setopt(self->handle, CURLOPT_HTTPPOST, post);
+ /* Check for errors */
+ if (res != CURLE_OK) {
+ curl_formfree(post);
+ CURLERROR_RETVAL();
+ }
+ /* Finally, free previously allocated httppost and update */
+ curl_formfree(self->httppost);
+ self->httppost = post;
+
+ Py_INCREF(Py_None);
+ return Py_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;
+
+ if (!PyString_Check(listitem)) {
+ curl_slist_free_all(slist);
+ PyErr_SetString(PyExc_TypeError, "list items must be string objects");
+ return NULL;
+ }
+ /* INFO: curl_slist_append() internally does strdup() the data, so
+ * no embedded NUL characters allowed here. */
+ str = PyString_AsString_NoNUL(listitem);
+ if (str == NULL) {
+ curl_slist_free_all(slist);
+ return NULL;
+ }
+ nlist = curl_slist_append(slist, str);
+ 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_INCREF(Py_None);
+ return Py_None;
+ }
+
+ /* Handle the case of function objects for callbacks */
+ if (PyFunction_Check(obj) || PyCFunction_Check(obj) || PyMethod_Check(obj)) {
+ /* We use function types here to make sure that our callback
+ * definitions exactly match the <curl/curl.h> 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;
+
+ switch(option) {
+ case CURLOPT_WRITEFUNCTION:
+ if (self->writeheader_fp != NULL) {
+ PyErr_SetString(ErrorObject, "cannot combine WRITEFUNCTION with WRITEHEADER option.");
+ return NULL;
+ }
+ Py_INCREF(obj);
+ ZAP(self->writedata_fp);
+ ZAP(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);
+ ZAP(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);
+ ZAP(self->readdata_fp);
+ ZAP(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);
+ ZAP(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);
+ ZAP(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);
+ ZAP(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;
+
+ default:
+ /* None of the function options were recognized, throw exception */
+ PyErr_SetString(PyExc_TypeError, "functions are not supported for this option");
+ return NULL;
+ }
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ /* 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:
+ {
+ /* 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:
+ {
+ /* 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_INCREF(Py_None);
+ return Py_None;
+ }
+ return PyString_FromString(s_res);
+ }
+
+ case CURLINFO_CONNECT_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:
+ {
+ /* 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);
+ }
+ }
+
+ /* Got wrong option on the method call */
+ PyErr_SetString(PyExc_ValueError, "invalid argument to getinfo");
+ return NULL;
+}
+
+
+/*************************************************************************
+// 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, PyObject *args)
+{
+ CurlMultiObject *self;
+
+ UNUSED(dummy);
+ if (!PyArg_ParseTuple(args, ":CurlMulti")) {
+ return NULL;
+ }
+
+ /* 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;
+ self->state = 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);
+ self->state = NULL;
+ 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)
+
+ ZAP(self->dict);
+ util_multi_close(self);
+
+ PyObject_GC_Del(self);
+ Py_TRASHCAN_SAFE_END(self)
+}
+
+
+static PyObject *
+do_multi_close(CurlMultiObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":close")) {
+ return NULL;
+ }
+ if (check_multi_state(self, 2, "close") != 0) {
+ return NULL;
+ }
+ util_multi_close(self);
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/* --------------- GC support --------------- */
+
+/* Drop references that may have created reference cycles. */
+static int
+do_multi_clear(CurlMultiObject *self)
+{
+ ZAP(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
+}
+
+/* --------------- perform --------------- */
+
+
+static PyObject *
+do_multi_perform(CurlMultiObject *self, PyObject *args)
+{
+ CURLMcode res;
+ int running = -1;
+
+ if (!PyArg_ParseTuple(args, ":perform")) {
+ return NULL;
+ }
+ if (check_multi_state(self, 1 | 2, "perform") != 0) {
+ return NULL;
+ }
+
+ /* Release global lock and start */
+ self->state = PyThreadState_Get();
+ assert(self->state != NULL);
+ Py_BEGIN_ALLOW_THREADS
+ res = curl_multi_perform(self->multi_handle, &running);
+ Py_END_ALLOW_THREADS
+ self->state = NULL;
+
+ /* We assume these errors are ok, otherwise throw 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;
+ }
+ if (self->state != NULL) {
+ PyErr_SetString(ErrorObject, "cannot add/remove handle - multi_perform() already running");
+ return -1;
+ }
+ /* check CurlObject status */
+ assert_curl_state(obj);
+ if (obj->state != NULL) {
+ PyErr_SetString(ErrorObject, "cannot add/remove handle - perform() of curl object already running");
+ return -1;
+ }
+ 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_INCREF(Py_None);
+ return Py_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_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/* --------------- fdset ---------------------- */
+
+static PyObject *
+do_multi_fdset(CurlMultiObject *self, PyObject *args)
+{
+ 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 (!PyArg_ParseTuple(args, ":fdset")) {
+ return 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 || max_fd < 0) {
+ CURLERROR_MSG("curl_multi_fdset() failed due to internal errors");
+ }
+
+ /* Allocate lists. */
+ if ((read_list = PyList_New(0)) == NULL) goto error;
+ if ((write_list = PyList_New(0)) == NULL) goto error;
+ if ((except_list = PyList_New(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(0)) == NULL) goto error;
+ if ((err_list = PyList_New(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(co->ob_type == 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 == -1.0) {
+ /* no timeout given - wait forever */
+ tvp = NULL;
+ } else 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 char co_close_doc [] = "close() -> None. Close handle and end curl session.\n";
+static char co_errstr_doc [] = "errstr() -> String. Return the internal libcurl error buffer string.\n";
+static char co_getinfo_doc [] = "getinfo(info) -> Res. Extract and return information from a curl session. Throws pycurl.error exception upon failure.\n";
+static char co_perform_doc [] = "perform() -> None. Perform a file transfer. Throws pycurl.error exception upon failure.\n";
+static char co_setopt_doc [] = "setopt(option, parameter) -> None. Set curl session option. Throws pycurl.error exception upon failure.\n";
+static char co_unsetopt_doc [] = "unsetopt(option) -> None. Reset curl session option to default value. Throws pycurl.error exception upon failure.\n";
+
+static char co_multi_fdset_doc [] = "fdset() -> Tuple. Returns a tuple of three lists that can be passed to the select.select() method .\n";
+static char co_multi_info_read_doc [] = "info_read([max_objects]) -> Tuple. Returns a tuple (number of queued handles, [curl objects]).\n";
+static 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 PyMethodDef curlobject_methods[] = {
+ {"close", (PyCFunction)do_curl_close, METH_VARARGS, co_close_doc},
+ {"errstr", (PyCFunction)do_curl_errstr, METH_VARARGS, co_errstr_doc},
+ {"getinfo", (PyCFunction)do_curl_getinfo, METH_VARARGS, co_getinfo_doc},
+ {"perform", (PyCFunction)do_curl_perform, METH_VARARGS, co_perform_doc},
+ {"setopt", (PyCFunction)do_curl_setopt, METH_VARARGS, co_setopt_doc},
+ {"unsetopt", (PyCFunction)do_curl_unsetopt, METH_VARARGS, co_unsetopt_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_VARARGS, NULL},
+ {"fdset", (PyCFunction)do_multi_fdset, METH_VARARGS, co_multi_fdset_doc},
+ {"info_read", (PyCFunction)do_multi_info_read, METH_VARARGS, co_multi_info_read_doc},
+ {"perform", (PyCFunction)do_multi_perform, 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 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_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_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);
+}
+
+
+/* --------------- actual type definitions --------------- */
+
+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.
+ */
+};
+
+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.
+ */
+};
+
+
+/*************************************************************************
+// 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 (!(option == CURL_GLOBAL_SSL ||
+ option == CURL_GLOBAL_WIN32 ||
+ option == CURL_GLOBAL_ALL ||
+ option == CURL_GLOBAL_NOTHING)) {
+ 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_INCREF(Py_None);
+ return Py_None;
+}
+
+
+static PyObject *
+do_global_cleanup(PyObject *dummy, PyObject *args)
+{
+ UNUSED(dummy);
+ if (!PyArg_ParseTuple(args, ":global_cleanup")) {
+ return NULL;
+ }
+
+ curl_global_cleanup();
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+
+static PyObject *vi_str(const char *s)
+{
+ if (s == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ while (*s == ' ' || *s == '\t')
+ s++;
+ return PyString_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;
+ int 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;
+ }
+
+ /* Note: actually libcurl in lib/version.c does ignore
+ * the "stamp" parm, 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(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 char pycurl_global_init_doc [] =
+"global_init(option) -> None. Initialize curl environment.\n";
+
+static char pycurl_global_cleanup_doc [] =
+"global_cleanup() -> None. Cleanup curl environment.\n";
+
+static char pycurl_version_info_doc [] =
+"version_info() -> tuple. Returns a 12-tuple with the version info.\n";
+
+static char pycurl_curl_new_doc [] =
+"Curl() -> New curl object. Implicitly calls global_init() if not called.\n";
+
+static 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_VARARGS, pycurl_global_cleanup_doc},
+ {"version_info", (PyCFunction)do_version_info, METH_VARARGS, pycurl_version_info_doc},
+ {"Curl", (PyCFunction)do_curl_new, METH_VARARGS, pycurl_curl_new_doc},
+ {"CurlMulti", (PyCFunction)do_multi_new, METH_VARARGS, pycurl_multi_new_doc},
+ {NULL, NULL, 0, NULL}
+};
+
+
+/* Module docstring */
+static 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 = PyString_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: FATAL: insobj2() failed");
+ assert(0);
+}
+
+static void
+insstr(PyObject *d, char *name, char *value)
+{
+ PyObject *v = PyString_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_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);
+}
+
+
+/* Initialization function for the module */
+#if defined(PyMODINIT_FUNC)
+PyMODINIT_FUNC
+#else
+#if defined(__cplusplus)
+extern "C"
+#endif
+DL_EXPORT(void)
+#endif
+initpycurl(void)
+{
+ PyObject *m, *d;
+ const curl_version_info_data *vi;
+
+ /* 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;
+ Curl_Type.ob_type = &PyType_Type;
+ CurlMulti_Type.ob_type = &PyType_Type;
+
+ /* Create the module and add the functions */
+ m = Py_InitModule3("pycurl", curl_methods, module_doc);
+ assert(m != NULL && PyModule_Check(m));
+
+ /* 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 */
+ insstr(d, "version", curl_version());
+ 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 <curl/curl.h>
+ **/
+
+ /* 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 */
+/* FIXME: lots of error codes are missing */
+ insint_c(d, "E_OK", CURLE_OK);
+ insint_c(d, "E_UNSUPPORTED_PROTOCOL", CURLE_UNSUPPORTED_PROTOCOL);
+
+ /* 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);
+ 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);
+
+ /* CURLoption: symbolic constants for setopt() */
+/* FIXME: reorder these to match <curl/curl.h> */
+ insint_c(d, "FILE", CURLOPT_WRITEDATA);
+ insint_c(d, "INFILE", CURLOPT_READDATA);
+ insint_c(d, "WRITEDATA", CURLOPT_WRITEDATA);
+ insint_c(d, "WRITEFUNCTION", CURLOPT_WRITEFUNCTION);
+ insint_c(d, "READDATA", CURLOPT_READDATA);
+ insint_c(d, "READFUNCTION", CURLOPT_READFUNCTION);
+ insint_c(d, "INFILESIZE", CURLOPT_INFILESIZE);
+ insint_c(d, "URL", CURLOPT_URL);
+ insint_c(d, "PROXY", CURLOPT_PROXY);
+ 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, "USERPWD", CURLOPT_USERPWD);
+ insint_c(d, "PROXYUSERPWD", CURLOPT_PROXYUSERPWD);
+ insint_c(d, "RANGE", CURLOPT_RANGE);
+ insint_c(d, "TIMEOUT", CURLOPT_TIMEOUT);
+ insint_c(d, "POSTFIELDS", CURLOPT_POSTFIELDS);
+ insint_c(d, "POSTFIELDSIZE", CURLOPT_POSTFIELDSIZE);
+ insint_c(d, "REFERER", CURLOPT_REFERER);
+ insint_c(d, "USERAGENT", CURLOPT_USERAGENT);
+ insint_c(d, "FTPPORT", CURLOPT_FTPPORT);
+ insint_c(d, "LOW_SPEED_LIMIT", CURLOPT_LOW_SPEED_LIMIT);
+ insint_c(d, "LOW_SPEED_TIME", CURLOPT_LOW_SPEED_TIME);
+ insint_c(d, "CURLOPT_RESUME_FROM", CURLOPT_RESUME_FROM);
+ 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, "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, "CLOSEPOLICY", CURLOPT_CLOSEPOLICY);
+ 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);
+ 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, "SOURCE_USERPWD", CURLOPT_SOURCE_USERPWD);
+ insint_c(d, "SOURCE_PREQUOTE", CURLOPT_SOURCE_PREQUOTE);
+ insint_c(d, "SOURCE_POSTQUOTE", CURLOPT_SOURCE_POSTQUOTE);
+ insint_c(d, "FTPSSLAUTH", CURLOPT_FTPSSLAUTH);
+ insint_c(d, "FTP_ACCOUNT", CURLOPT_FTP_ACCOUNT);
+ insint_c(d, "SOURCE_URL", CURLOPT_SOURCE_URL);
+ insint_c(d, "SOURCE_QUOTE", CURLOPT_SOURCE_QUOTE);
+ insint_c(d, "IOCTLFUNCTION", CURLOPT_IOCTLFUNCTION);
+ insint_c(d, "IOCTLDATA", CURLOPT_IOCTLDATA);
+
+ /* 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 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);
+
+ /* 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, "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, "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);
+
+ /* curl_closepolicy: constants for setopt(CLOSEPOLICY, x) */
+ insint_c(d, "CLOSEPOLICY_OLDEST", CURLCLOSEPOLICY_OLDEST);
+ insint_c(d, "CLOSEPOLICY_LEAST_RECENTLY_USED", CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
+ insint_c(d, "CLOSEPOLICY_LEAST_TRAFFIC", CURLCLOSEPOLICY_LEAST_TRAFFIC);
+ insint_c(d, "CLOSEPOLICY_SLOWEST", CURLCLOSEPOLICY_SLOWEST);
+ insint_c(d, "CLOSEPOLICY_CALLBACK", CURLCLOSEPOLICY_CALLBACK);
+
+ /* 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);
+
+ /* 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 <curl/multi.h>
+ **/
+
+ /* CURLMcode: multi error codes */
+ 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);
+
+ /* Check the version, as this has caused nasty problems in
+ * some cases. */
+ vi = curl_version_info(CURLVERSION_NOW);
+ if (vi == NULL) {
+ Py_FatalError("pycurl: FATAL: curl_version_info() failed");
+ assert(0);
+ }
+ if (vi->version_num < LIBCURL_VERSION_NUM) {
+ Py_FatalError("pycurl: FATAL: libcurl link-time version is older than compile-time version");
+ assert(0);
+ }
+
+ /* Finally initialize global interpreter lock */
+ PyEval_InitThreads();
+}
+
+/* vi:ts=4:et:nowrap
+ */
diff --git a/tests/test.py b/tests/test.py
new file mode 100644
index 0000000..f5afada
--- /dev/null
+++ b/tests/test.py
@@ -0,0 +1,74 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test.py,v 1.16 2004/12/26 17:31:53 mfx Exp $
+
+import sys, threading, time
+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
+
+
+class Test(threading.Thread):
+ def __init__(self, url, ofile):
+ threading.Thread.__init__(self)
+ self.curl = pycurl.Curl()
+ self.curl.setopt(pycurl.URL, url)
+ self.curl.setopt(pycurl.WRITEDATA, ofile)
+ self.curl.setopt(pycurl.FOLLOWLOCATION, 1)
+ self.curl.setopt(pycurl.MAXREDIRS, 5)
+ self.curl.setopt(pycurl.NOSIGNAL, 1)
+
+ def run(self):
+ self.curl.perform()
+ self.curl.close()
+ sys.stdout.write(".")
+ sys.stdout.flush()
+
+
+# Read list of URIs from file specified on commandline
+try:
+ urls = open(sys.argv[1]).readlines()
+except IndexError:
+ # No file was specified, show usage string
+ print "Usage: %s <file with uris to fetch>" % sys.argv[0]
+ raise SystemExit
+
+# Initialize thread array and the file number
+threads = []
+fileno = 0
+
+# Start one thread per URI in parallel
+t1 = time.time()
+for url in urls:
+ f = open(str(fileno), "wb")
+ t = Test(url, f)
+ t.start()
+ threads.append((t, f))
+ fileno = fileno + 1
+# Wait for all threads to finish
+for thread, file in threads:
+ thread.join()
+ file.close()
+t2 = time.time()
+print "\n** Multithreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls))
+
+# Start one thread per URI in sequence
+fileno = 0
+t1 = time.time()
+for url in urls:
+ f = open(str(fileno), "wb")
+ t = Test(url, f)
+ t.start()
+ fileno = fileno + 1
+ t.join()
+ f.close()
+t2 = time.time()
+print "\n** Singlethreading, %d seconds elapsed for %d uris" % (int(t2-t1), len(urls))
diff --git a/tests/test_cb.py b/tests/test_cb.py
new file mode 100644
index 0000000..509f49e
--- /dev/null
+++ b/tests/test_cb.py
@@ -0,0 +1,28 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_cb.py,v 1.14 2003/04/21 18:46:10 mfx Exp $
+
+import sys
+import pycurl
+
+## Callback function invoked when body data is ready
+def body(buf):
+ # Print body data to stdout
+ sys.stdout.write(buf)
+
+## Callback function invoked when header data is ready
+def header(buf):
+ # Print header data to stderr
+ sys.stderr.write(buf)
+
+c = pycurl.Curl()
+c.setopt(pycurl.URL, 'http://www.python.org/')
+c.setopt(pycurl.WRITEFUNCTION, body)
+c.setopt(pycurl.HEADERFUNCTION, header)
+c.setopt(pycurl.FOLLOWLOCATION, 1)
+c.setopt(pycurl.MAXREDIRS, 5)
+c.perform()
+c.setopt(pycurl.URL, 'http://curl.haxx.se/')
+c.perform()
+c.close()
diff --git a/tests/test_debug.py b/tests/test_debug.py
new file mode 100644
index 0000000..d3a6356
--- /dev/null
+++ b/tests/test_debug.py
@@ -0,0 +1,16 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_debug.py,v 1.6 2003/04/21 18:46:10 mfx Exp $
+
+import pycurl
+
+def test(t, b):
+ print "debug(%d): %s" % (t, b)
+
+c = pycurl.Curl()
+c.setopt(pycurl.URL, 'http://curl.haxx.se/')
+c.setopt(pycurl.VERBOSE, 1)
+c.setopt(pycurl.DEBUGFUNCTION, test)
+c.perform()
+c.close()
diff --git a/tests/test_getinfo.py b/tests/test_getinfo.py
new file mode 100644
index 0000000..dd13570
--- /dev/null
+++ b/tests/test_getinfo.py
@@ -0,0 +1,49 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_getinfo.py,v 1.18 2003/05/01 19:35:01 mfx Exp $
+
+import time
+import pycurl
+
+
+## Callback function invoked when progress information is updated
+def progress(download_t, download_d, upload_t, upload_d):
+ print "Total to download %d bytes, have %d bytes so far" % \
+ (download_t, download_d)
+
+url = "http://www.cnn.com"
+
+print "Starting downloading", url
+print
+f = open("body", "wb")
+h = open("header", "wb")
+c = pycurl.Curl()
+c.setopt(c.URL, url)
+c.setopt(c.WRITEDATA, f)
+c.setopt(c.NOPROGRESS, 0)
+c.setopt(c.PROGRESSFUNCTION, progress)
+c.setopt(c.FOLLOWLOCATION, 1)
+c.setopt(c.MAXREDIRS, 5)
+c.setopt(c.WRITEHEADER, h)
+c.setopt(c.OPT_FILETIME, 1)
+c.perform()
+
+print
+print "HTTP-code:", c.getinfo(c.HTTP_CODE)
+print "Total-time:", c.getinfo(c.TOTAL_TIME)
+print "Download speed: %.2f bytes/second" % c.getinfo(c.SPEED_DOWNLOAD)
+print "Document size: %d bytes" % c.getinfo(c.SIZE_DOWNLOAD)
+print "Effective URL:", c.getinfo(c.EFFECTIVE_URL)
+print "Content-type:", c.getinfo(c.CONTENT_TYPE)
+print "Namelookup-time:", c.getinfo(c.NAMELOOKUP_TIME)
+print "Redirect-time:", c.getinfo(c.REDIRECT_TIME)
+print "Redirect-count:", c.getinfo(c.REDIRECT_COUNT)
+epoch = c.getinfo(c.INFO_FILETIME)
+print "Filetime: %d (%s)" % (epoch, time.ctime(epoch))
+print
+print "Header is in file 'header', body is in file 'body'"
+
+c.close()
+f.close()
+h.close()
diff --git a/tests/test_gtk.py b/tests/test_gtk.py
new file mode 100644
index 0000000..b3c28c6
--- /dev/null
+++ b/tests/test_gtk.py
@@ -0,0 +1,93 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_gtk.py,v 1.23 2004/12/26 17:31:53 mfx Exp $
+
+import sys, threading
+from gtk import *
+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
+
+
+class ProgressBar:
+ def __init__(self, uri):
+ self.round = 0.0
+ win = GtkDialog()
+ win.set_title("PycURL progress")
+ win.show()
+ vbox = GtkVBox(spacing=5)
+ vbox.set_border_width(10)
+ win.vbox.pack_start(vbox)
+ win.set_default_size(200, 20)
+ vbox.show()
+ label = GtkLabel("Downloading %s" % uri)
+ label.set_alignment(0, 0.5)
+ vbox.pack_start(label, expand=FALSE)
+ label.show()
+ pbar = GtkProgressBar()
+ pbar.show()
+ self.pbar = pbar
+ vbox.pack_start(pbar)
+ win.connect("destroy", self.close_app)
+ win.connect("delete_event", self.close_app)
+
+ def progress(self, download_t, download_d, upload_t, upload_d):
+ threads_enter()
+ 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)
+ self.pbar.update(self.round)
+ threads_leave()
+
+ def mainloop(self):
+ threads_enter()
+ mainloop()
+ threads_leave()
+
+ def close_app(self, *args):
+ args[0].destroy()
+ mainquit()
+
+
+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 <URL> <filename>" % 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
+p.mainloop()
diff --git a/tests/test_internals.py b/tests/test_internals.py
new file mode 100644
index 0000000..afcc53d
--- /dev/null
+++ b/tests/test_internals.py
@@ -0,0 +1,253 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_internals.py,v 1.17 2003/05/01 16:48:54 mfx Exp $
+
+#
+# a simple self-test
+#
+
+try:
+ # need Python 2.2 or better for garbage collection
+ from gc import get_objects
+ import gc
+ del get_objects
+ gc.enable()
+except ImportError:
+ gc = None
+import copy, os, sys
+from StringIO import StringIO
+try:
+ import cPickle
+except ImportError:
+ cPickle = None
+try:
+ import pickle
+except ImportError:
+ pickle = None
+
+# update sys.path when running in the build directory
+from util import get_sys_path
+sys.path = get_sys_path()
+
+import pycurl
+from pycurl import Curl, CurlMulti
+
+
+class opts:
+ verbose = 1
+
+if "-q" in sys.argv:
+ opts.verbose = opts.verbose - 1
+
+
+print "Python", sys.version
+print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
+print "PycURL version info", pycurl.version_info()
+print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE)
+
+
+# /***********************************************************************
+# // test misc
+# ************************************************************************/
+
+if 1:
+ c = Curl()
+ assert c.URL is pycurl.URL
+ del c
+
+
+# /***********************************************************************
+# // test handles
+# ************************************************************************/
+
+# remove an invalid handle: this should fail
+if 1:
+ m = CurlMulti()
+ c = Curl()
+ try:
+ m.remove_handle(c)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "internal error"
+ del m, c
+
+
+# remove an invalid but closed handle
+if 1:
+ m = CurlMulti()
+ c = Curl()
+ c.close()
+ m.remove_handle(c)
+ del m, c
+
+
+# add a closed handle: this should fail
+if 1:
+ m = CurlMulti()
+ c = Curl()
+ c.close()
+ try:
+ m.add_handle(c)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "internal error"
+ m.close()
+ del m, c
+
+
+# add a handle twice: this should fail
+if 1:
+ m = CurlMulti()
+ c = Curl()
+ m.add_handle(c)
+ try:
+ m.add_handle(c)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "internal error"
+ del m, c
+
+
+# add a handle on multiple stacks: this should fail
+if 1:
+ m1 = CurlMulti()
+ m2 = CurlMulti()
+ c = Curl()
+ m1.add_handle(c)
+ try:
+ m2.add_handle(c)
+ except pycurl.error:
+ pass
+ else:
+ assert 0, "internal error"
+ del m1, m2, c
+
+
+# move a handle
+if 1:
+ m1 = CurlMulti()
+ m2 = CurlMulti()
+ c = Curl()
+ m1.add_handle(c)
+ m1.remove_handle(c)
+ m2.add_handle(c)
+ del m1, m2, c
+
+
+# /***********************************************************************
+# // test copying and pickling - copying and pickling of
+# // instances of Curl and CurlMulti is not allowed
+# ************************************************************************/
+
+if 1 and copy:
+ c = Curl()
+ m = CurlMulti()
+ try:
+ copy.copy(c)
+ except copy.Error:
+ pass
+ else:
+ assert 0, "internal error - copying should fail"
+ try:
+ copy.copy(m)
+ except copy.Error:
+ pass
+ else:
+ assert 0, "internal error - copying should fail"
+
+if 1 and pickle:
+ c = Curl()
+ m = CurlMulti()
+ fp = StringIO()
+ p = pickle.Pickler(fp, 1)
+ try:
+ p.dump(c)
+ except pickle.PicklingError:
+ pass
+ else:
+ assert 0, "internal error - pickling should fail"
+ try:
+ p.dump(m)
+ except pickle.PicklingError:
+ pass
+ else:
+ assert 0, "internal error - pickling should fail"
+ del c, m, fp, p
+
+if 1 and cPickle:
+ c = Curl()
+ m = CurlMulti()
+ fp = StringIO()
+ p = cPickle.Pickler(fp, 1)
+ try:
+ p.dump(c)
+ except cPickle.PicklingError:
+ pass
+ else:
+ assert 0, "internal error - pickling should fail"
+ try:
+ p.dump(m)
+ except cPickle.PicklingError:
+ pass
+ else:
+ assert 0, "internal error - pickling should fail"
+ del c, m, fp, p
+
+
+# /***********************************************************************
+# // test refcounts
+# ************************************************************************/
+
+# basic check of reference counting (use a memory checker like valgrind)
+if 1:
+ c = Curl()
+ m = CurlMulti()
+ m.add_handle(c)
+ del m
+ m = CurlMulti()
+ c.close()
+ del m, c
+
+# basic check of cyclic garbage collection
+if 1 and gc:
+ gc.collect()
+ c = Curl()
+ c.m = 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()
+ flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS
+ if opts.verbose >= 1:
+ flags = flags | gc.DEBUG_STATS
+ gc.set_debug(flags)
+ gc.collect()
+ ##print gc.get_referrers(c)
+ ##print gc.get_objects()
+ if opts.verbose >= 1:
+ print "Tracked objects:", len(gc.get_objects())
+ # The `del' below should delete these 4 objects:
+ # Curl + internal dict, CurlMulti + internal dict
+ del c
+ gc.collect()
+ if opts.verbose >= 1:
+ print "Tracked objects:", len(gc.get_objects())
+
+
+# /***********************************************************************
+# // done
+# ************************************************************************/
+
+print "All tests passed."
diff --git a/tests/test_memleak.py b/tests/test_memleak.py
new file mode 100644
index 0000000..284df62
--- /dev/null
+++ b/tests/test_memleak.py
@@ -0,0 +1,53 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_memleak.py,v 1.4 2003/05/01 16:48:54 mfx Exp $
+
+#
+# just a simple self-test
+# need Python 2.2 or better for garbage collection
+#
+
+import gc, pycurl, sys
+gc.enable()
+
+
+print "Python", sys.version
+print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
+##print "PycURL version info", pycurl.version_info()
+print " %s, compiled %s" % (pycurl.__file__, pycurl.COMPILE_DATE)
+
+
+gc.collect()
+flags = gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_OBJECTS
+if 1:
+ flags = flags | gc.DEBUG_STATS
+gc.set_debug(flags)
+gc.collect()
+
+print "Tracked objects:", len(gc.get_objects())
+
+multi = pycurl.CurlMulti()
+t = []
+for a in range(100):
+ curl = pycurl.Curl()
+ multi.add_handle(curl)
+ t.append(curl)
+
+print "Tracked objects:", len(gc.get_objects())
+
+for curl in t:
+ curl.close()
+ multi.remove_handle(curl)
+
+print "Tracked objects:", len(gc.get_objects())
+
+del curl
+del t
+del multi
+
+print "Tracked objects:", len(gc.get_objects())
+gc.collect()
+print "Tracked objects:", len(gc.get_objects())
+
+
diff --git a/tests/test_multi.py b/tests/test_multi.py
new file mode 100644
index 0000000..5be86f0
--- /dev/null
+++ b/tests/test_multi.py
@@ -0,0 +1,33 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi.py,v 1.9 2003/04/21 18:46:10 mfx Exp $
+
+import pycurl
+
+m = pycurl.CurlMulti()
+m.handles = []
+c1 = pycurl.Curl()
+c2 = pycurl.Curl()
+c1.setopt(c1.URL, 'http://curl.haxx.se')
+c2.setopt(c2.URL, 'http://cnn.com')
+c2.setopt(c2.FOLLOWLOCATION, 1)
+m.add_handle(c1)
+m.add_handle(c2)
+m.handles.append(c1)
+m.handles.append(c2)
+
+num_handles = len(m.handles)
+while num_handles:
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+ m.select()
+
+m.remove_handle(c2)
+m.remove_handle(c1)
+del m.handles
+m.close()
+c1.close()
+c2.close()
diff --git a/tests/test_multi2.py b/tests/test_multi2.py
new file mode 100644
index 0000000..3d8ab1b
--- /dev/null
+++ b/tests/test_multi2.py
@@ -0,0 +1,72 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi2.py,v 1.13 2003/04/21 18:46:10 mfx Exp $
+
+import os, sys
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+import pycurl
+
+
+urls = (
+ "http://curl.haxx.se",
+ "http://www.python.org",
+ "http://pycurl.sourceforge.net",
+ "http://pycurl.sourceforge.net/tests/403_FORBIDDEN", # that actually exists ;-)
+ "http://pycurl.sourceforge.net/tests/404_NOT_FOUND",
+)
+
+# Read list of URIs from file specified on commandline
+try:
+ urls = open(sys.argv[1], "rb").readlines()
+except IndexError:
+ # No file was specified
+ pass
+
+# init
+m = pycurl.CurlMulti()
+m.handles = []
+for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url
+ c.body = StringIO()
+ 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()
+
+# 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()
+
+# print result
+for c in m.handles:
+ data = c.body.getvalue()
+ if 0:
+ print "**********", c.url, "**********"
+ print data
+ else:
+ print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
+
diff --git a/tests/test_multi3.py b/tests/test_multi3.py
new file mode 100644
index 0000000..9b52159
--- /dev/null
+++ b/tests/test_multi3.py
@@ -0,0 +1,87 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi3.py,v 1.12 2003/04/21 18:46:10 mfx Exp $
+
+# same as test_multi2.py, but enforce some debugging and strange API-calls
+
+import os, sys
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+import pycurl
+
+
+urls = (
+ "http://curl.haxx.se",
+ "http://www.python.org",
+ "http://pycurl.sourceforge.net",
+ "http://pycurl.sourceforge.net/THIS_HANDLE_IS_CLOSED",
+)
+
+# init
+m = pycurl.CurlMulti()
+m.handles = []
+for url in urls:
+ c = pycurl.Curl()
+ # save info in standard Python attributes
+ c.url = url
+ c.body = StringIO()
+ 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
+if 1:
+ c = m.handles[3]
+ 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()
+
+# 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
+ if 0:
+ m.remove_handle(c)
+ c.close()
+ elif 0:
+ # in the C API this is the wrong calling order, but pycurl
+ # handles this automatically
+ c.close()
+ m.remove_handle(c)
+ else:
+ # actually, remove_handle is called automatically on close
+ c.close()
+m.close()
+
+# print result
+for c in m.handles:
+ data = c.body.getvalue()
+ if 0:
+ print "**********", c.url, "**********"
+ print data
+ else:
+ print "%-53s http_code %3d, %6d bytes" % (c.url, c.http_code, len(data))
+
diff --git a/tests/test_multi4.py b/tests/test_multi4.py
new file mode 100644
index 0000000..6b143aa
--- /dev/null
+++ b/tests/test_multi4.py
@@ -0,0 +1,57 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi4.py,v 1.13 2003/04/21 18:46:10 mfx Exp $
+
+import sys, select, time
+import pycurl
+
+c1 = pycurl.Curl()
+c2 = pycurl.Curl()
+c3 = pycurl.Curl()
+c1.setopt(c1.URL, "http://www.python.org")
+c2.setopt(c2.URL, "http://curl.haxx.se")
+c3.setopt(c3.URL, "http://slashdot.org")
+c1.body = open("doc1", "wb")
+c2.body = open("doc2", "wb")
+c3.body = open("doc3", "wb")
+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 = 10
+
+# 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:
+ apply(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.body.close()
+c2.body.close()
+c3.body.close()
+c1.close()
+c2.close()
+c3.close()
+print "http://www.python.org is in file doc1"
+print "http://curl.haxx.se is in file doc2"
+print "http://slashdot.org is in file doc3"
diff --git a/tests/test_multi5.py b/tests/test_multi5.py
new file mode 100644
index 0000000..10f799e
--- /dev/null
+++ b/tests/test_multi5.py
@@ -0,0 +1,60 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi5.py,v 1.11 2003/04/21 18:46:10 mfx Exp $
+
+import sys, select, time
+import pycurl
+
+c1 = pycurl.Curl()
+c2 = pycurl.Curl()
+c3 = pycurl.Curl()
+c1.setopt(c1.URL, "http://www.python.org")
+c2.setopt(c2.URL, "http://curl.haxx.se")
+c3.setopt(c3.URL, "http://slashdot.org")
+c1.body = open("doc1", "wb")
+c2.body = open("doc2", "wb")
+c3.body = open("doc3", "wb")
+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 = 10
+
+# 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.body.close()
+c2.body.close()
+c3.body.close()
+c1.close()
+c2.close()
+c3.close()
+print "http://www.python.org is in file doc1"
+print "http://curl.haxx.se is in file doc2"
+print "http://slashdot.org is in file doc3"
+
diff --git a/tests/test_multi6.py b/tests/test_multi6.py
new file mode 100644
index 0000000..77585e5
--- /dev/null
+++ b/tests/test_multi6.py
@@ -0,0 +1,62 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi6.py,v 1.5 2003/04/21 18:46:10 mfx Exp $
+
+import sys, select, time
+import pycurl
+
+c1 = pycurl.Curl()
+c2 = pycurl.Curl()
+c3 = pycurl.Curl()
+c1.setopt(c1.URL, "http://www.python.org")
+c2.setopt(c2.URL, "http://curl.haxx.se")
+c3.setopt(c3.URL, "http://slashdot.org")
+c1.body = open("doc1", "wb")
+c2.body = open("doc2", "wb")
+c3.body = open("doc3", "wb")
+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 = 10
+
+# 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()
+ # Print the message, if any
+ print m.info_read(1)
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+# Cleanup
+m.remove_handle(c3)
+m.remove_handle(c2)
+m.remove_handle(c1)
+m.close()
+c1.body.close()
+c2.body.close()
+c3.body.close()
+c1.close()
+c2.close()
+c3.close()
+print "http://www.python.org is in file doc1"
+print "http://curl.haxx.se is in file doc2"
+print "http://slashdot.org is in file doc3"
+
diff --git a/tests/test_multi_vs_thread.py b/tests/test_multi_vs_thread.py
new file mode 100644
index 0000000..a6030cc
--- /dev/null
+++ b/tests/test_multi_vs_thread.py
@@ -0,0 +1,262 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_multi_vs_thread.py,v 1.15 2004/12/26 17:31:53 mfx Exp $
+
+import os, sys, time
+from threading import Thread, RLock
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+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
+
+# The conclusion is: the multi interface is fastest!
+
+NUM_PAGES = 30
+NUM_THREADS = 10
+assert NUM_PAGES % NUM_THREADS == 0
+
+##URL = "http://pycurl.sourceforge.net/tests/testgetvars.php?%d"
+URL = "http://pycurl.sourceforge.net/tests/teststaticpage.html?%d"
+
+
+#
+# util
+#
+
+class Curl:
+ def __init__(self, url):
+ self.url = url
+ self.body = StringIO()
+ self.http_code = -1
+ # pycurl API calls
+ self._curl = pycurl.Curl()
+ self._curl.setopt(pycurl.URL, self.url)
+ self._curl.setopt(pycurl.WRITEFUNCTION, self.body.write)
+ self._curl.setopt(pycurl.NOSIGNAL, 1)
+
+ def perform(self):
+ self._curl.perform()
+
+ def close(self):
+ self.http_code = self._curl.getinfo(pycurl.HTTP_CODE)
+ self._curl.close()
+
+
+def print_result(items):
+ return # DO NOTHING
+ #
+ for c in items:
+ data = c.body.getvalue()
+ if 0:
+ print "**********", c.url, "**********"
+ print data
+ elif 1:
+ print "%-60s %3d %6d" % (c.url, c.http_code, len(data))
+
+
+###
+### 1) multi
+###
+
+def test_multi():
+ clock1 = time.time()
+
+ # init
+ handles = []
+ m = pycurl.CurlMulti()
+ for i in range(NUM_PAGES):
+ c = Curl(URL %i)
+ m.add_handle(c._curl)
+ handles.append(c)
+
+ clock2 = time.time()
+
+ # stir state machine into action
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ # get data
+ while num_handles:
+ m.select()
+ while 1:
+ ret, num_handles = m.perform()
+ if ret != pycurl.E_CALL_MULTI_PERFORM:
+ break
+
+ clock3 = time.time()
+
+ # close handles
+ for c in handles:
+ c.close()
+ m.close()
+
+ clock4 = time.time()
+ print "multi interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
+
+ # print result
+ print_result(handles)
+
+
+
+###
+### 2) thread
+###
+
+class Test(Thread):
+ def __init__(self, lock=None):
+ Thread.__init__(self)
+ self.lock = lock
+ self.items = []
+
+ def run(self):
+ if self.lock:
+ self.lock.acquire()
+ self.lock.release()
+ for c in self.items:
+ c.perform()
+
+
+def test_threads(lock=None):
+ clock1 = time.time()
+
+ # create and start threads, but block them
+ if lock:
+ lock.acquire()
+
+ # init (FIXME - this is ugly)
+ threads = []
+ handles = []
+ t = None
+ for i in range(NUM_PAGES):
+ if i % (NUM_PAGES / NUM_THREADS) == 0:
+ t = Test(lock)
+ if lock:
+ t.start()
+ threads.append(t)
+ c = Curl(URL % i)
+ t.items.append(c)
+ handles.append(c)
+ assert len(handles) == NUM_PAGES
+ assert len(threads) == NUM_THREADS
+
+ clock2 = time.time()
+
+ #
+ if lock:
+ # release lock to let the blocked threads run
+ lock.release()
+ else:
+ # start threads
+ for t in threads:
+ t.start()
+ # wait for threads to finish
+ for t in threads:
+ t.join()
+
+ clock3 = time.time()
+
+ # close handles
+ for c in handles:
+ c.close()
+
+ clock4 = time.time()
+ if lock:
+ print "thread interface [lock]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
+ else:
+ print "thread interface: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
+
+ # print result
+ print_result(handles)
+
+
+
+###
+### 3) thread - threads grab curl objects on demand from a shared pool
+###
+
+class TestPool(Thread):
+ def __init__(self, lock, pool):
+ Thread.__init__(self)
+ self.lock = lock
+ self.pool = pool
+
+ def run(self):
+ while 1:
+ self.lock.acquire()
+ c = None
+ if self.pool:
+ c = self.pool.pop()
+ self.lock.release()
+ if c is None:
+ break
+ c.perform()
+
+
+def test_thread_pool(lock):
+ clock1 = time.time()
+
+ # init
+ handles = []
+ for i in range(NUM_PAGES):
+ c = Curl(URL %i)
+ handles.append(c)
+
+ # create and start threads, but block them
+ lock.acquire()
+ threads = []
+ pool = handles[:] # shallow copy of the list, shared for pop()
+ for i in range(NUM_THREADS):
+ t = TestPool(lock, pool)
+ t.start()
+ threads.append(t)
+ assert len(pool) == NUM_PAGES
+ assert len(threads) == NUM_THREADS
+
+ clock2 = time.time()
+
+ # release lock to let the blocked threads run
+ lock.release()
+
+ # wait for threads to finish
+ for t in threads:
+ t.join()
+
+ clock3 = time.time()
+
+ # close handles
+ for c in handles:
+ c.close()
+
+ clock4 = time.time()
+ print "thread interface [pool]: %d pages: perform %5.2f secs, total %5.2f secs" % (NUM_PAGES, clock3 - clock2, clock4 - clock1)
+
+ # print result
+ print_result(handles)
+
+
+
+lock = RLock()
+if 1:
+ test_multi()
+ test_threads()
+ test_threads(lock)
+ test_thread_pool(lock)
+else:
+ test_thread_pool(lock)
+ test_threads(lock)
+ test_threads()
+ test_multi()
+
diff --git a/tests/test_post.py b/tests/test_post.py
new file mode 100644
index 0000000..574bbda
--- /dev/null
+++ b/tests/test_post.py
@@ -0,0 +1,24 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_post.py,v 1.9 2003/04/21 18:46:11 mfx Exp $
+
+import urllib
+import pycurl
+
+# simple
+pf = {'field1': 'value1'}
+
+# multiple fields
+pf = {'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'}
+
+# multiple fields with & in field
+pf = {'field1':'value1', 'field2':'value2 with blanks and & chars',
+ 'field3':'value3'}
+
+c = pycurl.Curl()
+c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php')
+c.setopt(c.POSTFIELDS, urllib.urlencode(pf))
+c.setopt(c.VERBOSE, 1)
+c.perform()
+c.close()
diff --git a/tests/test_post2.py b/tests/test_post2.py
new file mode 100644
index 0000000..b4fd9b0
--- /dev/null
+++ b/tests/test_post2.py
@@ -0,0 +1,16 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_post2.py,v 1.12 2004/04/30 15:12:28 kjetilja Exp $
+
+import pycurl
+
+
+pf = [('field1', 'this is a test using httppost & stuff'), ('field2', 'value2')]
+
+c = pycurl.Curl()
+c.setopt(c.URL, 'http://www.contactor.se/~dast/postit.cgi')
+c.setopt(c.HTTPPOST, pf)
+c.setopt(c.VERBOSE, 1)
+c.perform()
+c.close()
diff --git a/tests/test_post3.py b/tests/test_post3.py
new file mode 100644
index 0000000..3ebdd55
--- /dev/null
+++ b/tests/test_post3.py
@@ -0,0 +1,32 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_post3.py,v 1.1 2004/06/21 11:24:18 kjetilja Exp $
+
+import urllib
+POSTSTRING = urllib.urlencode({'field1':'value1', 'field2':'value2 with blanks', 'field3':'value3'})
+
+class test:
+
+ def __init__(self):
+ self.finished = False
+
+ def read_cb(self, size):
+ assert len(POSTSTRING) <= size
+ if not self.finished:
+ self.finished = True
+ return POSTSTRING
+ else:
+ # Nothing more to read
+ return ""
+
+import pycurl
+c = pycurl.Curl()
+t = test()
+c.setopt(c.URL, 'http://pycurl.sourceforge.net/tests/testpostvars.php')
+c.setopt(c.POST, 1)
+c.setopt(c.POSTFIELDSIZE, len(POSTSTRING))
+c.setopt(c.READFUNCTION, t.read_cb)
+c.setopt(c.VERBOSE, 1)
+c.perform()
+c.close()
diff --git a/tests/test_stringio.py b/tests/test_stringio.py
new file mode 100644
index 0000000..7fdf153
--- /dev/null
+++ b/tests/test_stringio.py
@@ -0,0 +1,25 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_stringio.py,v 1.6 2003/04/21 18:46:11 mfx Exp $
+
+import sys
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+import pycurl
+
+url = "http://curl.haxx.se/dev/"
+
+print "Testing", pycurl.version
+
+body = StringIO()
+c = pycurl.Curl()
+c.setopt(c.URL, url)
+c.setopt(c.WRITEFUNCTION, body.write)
+c.perform()
+c.close()
+
+contents = body.getvalue()
+print contents
diff --git a/tests/test_xmlrpc.py b/tests/test_xmlrpc.py
new file mode 100644
index 0000000..c2ff86d
--- /dev/null
+++ b/tests/test_xmlrpc.py
@@ -0,0 +1,29 @@
+#! /usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: test_xmlrpc.py,v 1.7 2003/04/21 18:46:11 mfx Exp $
+
+## XML-RPC lib included in python2.2
+import 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 = """
+<?xml version='1.0'?><methodCall><methodName>%s</methodName>%s</methodCall>
+"""
+
+# 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()
diff --git a/tests/util.py b/tests/util.py
new file mode 100644
index 0000000..945ad0d
--- /dev/null
+++ b/tests/util.py
@@ -0,0 +1,38 @@
+# -*- coding: iso-8859-1 -*-
+# vi:ts=4:et
+# $Id: util.py,v 1.4 2003/04/21 18:46:11 mfx Exp $
+
+import os, sys
+
+#
+# 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
+
+
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/pycurl.git
More information about the Python-modules-commits
mailing list