diff --git a/lib/libcurlemu/LICENSE b/lib/libcurlemu/LICENSE new file mode 100644 index 00000000000..3912109b5cd --- /dev/null +++ b/lib/libcurlemu/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, 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 or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +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 give any other recipients of the Program a copy of this License +along with the Program. + +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 Program or any portion +of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +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 Program, 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 Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) 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; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, 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 executable. However, as a +special exception, the source code 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. + +If distribution of executable or 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 counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program 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. + + 5. 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 Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program 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 to +this License. + + 7. 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 Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program 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 Program. + +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. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program 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. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 Program +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 Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, 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 + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "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 PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. 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 PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), 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 Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/lib/libcurlemu/README b/lib/libcurlemu/README new file mode 100644 index 00000000000..de1837038a5 --- /dev/null +++ b/lib/libcurlemu/README @@ -0,0 +1,93 @@ +CURL Extension Emulation Library +Version 1.0.3 +Copyright 2004-2005, Steve Blinch +http://code.blitzaffe.com +============================================================================ + +DESCRIPTION + +Provides a pure-PHP implementation of the PHP CURL extension, for use on +systems which do not already have the CURL extension installed. It emulates +all of the curl_* functions normally provided by the CURL extension itself. + +This will automatically detect and use the best CURL implementation available +on your server. It will attempt the following, in order: + +1) Check for the existence of the "real" CURL PHP Extension. If it is +loaded, the library will do nothing (and it will not interfere with the +"real" extension). +2) Check for the existence of the CURL console binary (usually located in +/usr/bin/curl). If found, the library will emulate the CURL PHP +extension (including all curl_* functions) and use the console binary +to execute all requests. +3) If neither the "real" CURL PHP Extension nor the CURL console binary +are available, the library will emulate the CURL PHP extension (including +all curl_* functions) using a native, pure-PHP HTTP client implementation. +This implementation is somewhat limited, but it provides support for most +of the common CURL options. HTTPS (SSL) support is available in this +mode under PHP 4.3.0 if the OpenSSL Extension is loaded. + +Thus, by including this library in your project, you can rely on having some +level of CURL support regardless of the configuration of the server on which +it is being used. + + +USAGE + +Simply copy all of the libcurlemu files into your project directory, then: + +require_once("libcurlemu.inc.php"); + +After this, you can use all of the curl_* functions documented in the PHP +Manual. + + +EXAMPLE + +// CURL Extension Emulation Library Example +// +// Usage should be straightforward; you simply use this script exactly as you +// would normally use the PHP CURL extension functions. + +// first, include libcurlemu.inc.php +require_once('libcurlemu.inc.php'); + +// at this point, libcurlemu has detected the best available CURL solution +// (either the CURL extension, if available, or the CURL commandline binary, +// if available, or as a last resort, HTTPRetriever, our native-PHP HTTP +// client implementation) and has implemented the curl_* functions if +// necessary, so you can use CURL normally and safely assume that all CURL +// functions are available. + +// the rest of this example code is copied straight from the PHP manual's +// reference for the curl_init() function, and will work fine with libcurlemu + +// create a new CURL resource +$ch = curl_init(); + +// set URL and other appropriate options +curl_setopt($ch, CURLOPT_URL, "http://www.example.com/"); +curl_setopt($ch, CURLOPT_HEADER, false); + +// grab URL and pass it to the browser +curl_exec($ch); + +// close CURL resource, and free up system resources +curl_close($ch); + + +LICENSE + +This script is free software; you can redistribute it and/or modify it under the +terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This script 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 General Public License for more +details. + +You should have received a copy of the GNU General Public License along +with this script; if not, write to the Free Software Foundation, Inc., +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/lib/libcurlemu/class_HTTPRetriever.php b/lib/libcurlemu/class_HTTPRetriever.php new file mode 100755 index 00000000000..4b52c2aee13 --- /dev/null +++ b/lib/libcurlemu/class_HTTPRetriever.php @@ -0,0 +1,1069 @@ +get("http://www.google.com/search?hl=en&q=%22".urlencode($keyword)."%22&btnG=Search&meta=")) { + * echo "HTTP request error: #{$http->result_code}: {$http->result_text}"; + * return false; + * } + * echo "HTTP response headers:
";
+ * var_dump($http->response_headers);
+ * echo "

"; + * + * echo "Page content:
";
+ * echo $http->response;
+ * echo "
"; + * // ---------------------------------------------------------------------------- + * + * + * // Example POST request: + * // ---------------------------------------------------------------------------- + * $keyword = "blitzaffe code"; // search Google for this keyword + * $values = array( + * "hl"=>"en", + * "q"=>"%22".urlencode($keyword)."%22", + * "btnG"=>"Search", + * "meta"=>"" + * ); + * // Note: This example is just to demonstrate the POST equivalent of the GET + * // example above; running this script will return a 501 Not Implemented, as + * // Google does not support POST requests. + * if (!$http->post("http://www.google.com/search",$http->make_query_string($values))) { + * echo "HTTP request error: #{$http->result_code}: {$http->result_text}"; + * return false; + * } + * echo "HTTP response headers:
";
+ * var_dump($http->response_headers);
+ * echo "

"; + * + * echo "Page content:
";
+ * echo $http->response;
+ * echo "
"; + * // ---------------------------------------------------------------------------- + * + * + * LICENSE + * + * This script is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This script 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with this script; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +// define user agent ID's +define("UA_EXPLORER",0); +define("UA_MOZILLA",1); +define("UA_FIREFOX",2); +define("UA_OPERA",3); + +// define progress message severity levels +define('HRP_DEBUG',0); +define('HRP_INFO',1); +define('HRP_ERROR',2); + +if (!defined("CURL_PATH")) define("CURL_PATH","/usr/bin/curl"); + +// if the CURL extension is not loaded, but the CURL Emulation Library is found, try +// to load it +if (!extension_loaded("curl") && !defined('HTTPR_NO_REDECLARE_CURL') ) { + foreach (array(dirname(__FILE__)."/",dirname(__FILE__)."/libcurlemu/") as $k=>$libcurlemupath) { + $libcurlemuinc = $libcurlemupath.'libcurlexternal.inc.php'; + if (is_readable($libcurlemuinc)) require_once($libcurlemuinc); + } +} + +class HTTPRetriever { + + // Constructor + function HTTPRetriever() { + // default HTTP headers to send with all requests + $this->headers = array( + "Referer"=>"", + "User-Agent"=>"HTTPRetriever/1.0", + "Connection"=>"close" + ); + + // HTTP version (has no effect if using CURL) + $this->version = "1.1"; + + // Normally, CURL is only used for HTTPS requests; setting this to + // TRUE will force CURL for HTTP requests as well. Not recommended. + $this->force_curl = false; + + // If you don't want to use CURL at all, set this to TRUE. + $this->disable_curl = false; + + // If HTTPS request return an error message about SSL certificates in + // $this->error and you don't care about security, set this to TRUE + $this->insecure_ssl = false; + + // Set the maximum time to wait for a connection + $this->connect_timeout = 15; + + // Set the maximum time to allow a transfer to run, or 0 to disable. + $this->max_time = 0; + + // Set the maximum time for a socket read/write operation, or 0 to disable. + $this->stream_timeout = 0; + + // If you're making an HTTPS request to a host whose SSL certificate + // doesn't match its domain name, AND YOU FULLY UNDERSTAND THE + // SECURITY IMPLICATIONS OF IGNORING THIS PROBLEM, set this to TRUE. + $this->ignore_ssl_hostname = false; + + // If TRUE, the get() and post() methods will close the connection + // and return immediately after receiving the HTTP result code + $this->result_close = false; + + // If set to a positive integer value, retrieved pages will be cached + // for this number of seconds. Any subsequent calls within the cache + // period will return the cached page, without contacting the remote + // server. + $this->caching = false; + + // If $this->caching is enabled, this specifies the folder under which + // cached pages are saved. + $this->cache_path = '/tmp/'; + + // Set these to perform basic HTTP authentication + $this->auth_username = ''; + $this->auth_password = ''; + + // Optionally set this to a valid callback method to have HTTPRetriever + // provide progress messages. Your callback must accept 2 parameters: + // an integer representing the severity (0=debug, 1=information, 2=error), + // and a string representing the progress message + $this->progress_callback = null; + + // Optionally set this to a valid callback method to have HTTPRetriever + // provide bytes-transferred messages. Your callbcak must accept 2 + // parameters: an integer representing the number of bytes transferred, + // and an integer representing the total number of bytes expected (or + // -1 if unknown). + $this->transfer_callback = null; + + // Set this to TRUE if you HTTPRetriever to transparently follow HTTP + // redirects (code 301, 302, 303, and 307). Optionally set this to a + // numeric value to limit the maximum number of redirects to the specified + // value. (Redirection loops are detected automatically.) + // Note that non-GET/HEAD requests will NOT be redirected except on code + // 303, as per HTTP standards. + $this->follow_redirects = false; + } + + // Send an HTTP GET request to $url; if $ipaddress is specified, the + // connection will be made to the selected IP instead of resolving the + // hostname in $url. + // + // If $cookies is set, it should be an array in one of two formats. + // + // Either: $cookies[ 'cookiename' ] = array ( + // '/path/'=>array( + // 'expires'=>time(), + // 'domain'=>'yourdomain.com', + // 'value'=>'cookievalue' + // ) + // ); + // + // Or, a more simplified format: + // $cookies[ 'cookiename' ] = 'value'; + // + // The former format will automatically check to make sure that the path, domain, + // and expiration values match the HTTP request, and will only send the cookie if + // they do match. The latter will force the cookie to be set for the HTTP request + // unconditionally. + // + function get($url,$ipaddress = false,$cookies = false) { + $this->method = "GET"; + $this->post_data = ""; + $this->connect_ip = $ipaddress; + return $this->_execute_request($url,$cookies); + } + + // Send an HTTP POST request to $url containing the POST data $data. See ::get() + // for a description of the remaining arguments. + function post($url,$data="",$ipaddress = false,$cookies = false) { + $this->method = "POST"; + $this->post_data = $data; + $this->connect_ip = $ipaddress; + return $this->_execute_request($url,$cookies); + } + + // Send an HTTP HEAD request to $url. See ::get() for a description of the arguments. + function head($url,$ipaddress = false,$cookies = false) { + $this->method = "HEAD"; + $this->post_data = ""; + $this->connect_ip = $ipaddress; + return $this->_execute_request($url,$cookies); + } + + // send an alternate (non-GET/POST) HTTP request to $url + function custom($method,$url,$data="",$ipaddress = false,$cookies = false) { + $this->method = $method; + $this->post_data = $data; + $this->connect_ip = $ipaddress; + return $this->_execute_request($url,$cookies); + } + + function array_to_query($arrayname,$arraycontents) { + $output = ""; + foreach ($arraycontents as $key=>$value) { + if (is_array($value)) { + $output .= $this->array_to_query(sprintf('%s[%s]',$arrayname,urlencode($key)),$value); + } else { + $output .= sprintf('%s[%s]=%s&',$arrayname,urlencode($key),urlencode($value)); + } + } + return $output; + } + + // builds a query string from the associative array array $data; + // returns a string that can be passed to $this->post() + function make_query_string($data) { + $output = ""; + if (is_array($data)) { + foreach ($data as $name=>$value) { + if (is_array($value)) { + $output .= $this->array_to_query(urlencode($name),$value); + } elseif (is_scalar($value)) { + $output .= urlencode($name)."=".urlencode($value)."&"; + } else { + $output .= urlencode($name)."=".urlencode(serialize($value)).'&'; + } + } + } + return substr($output,0,strlen($output)-1); + } + + + // this is pretty limited... but really, if you're going to spoof you UA, you'll probably + // want to use a Windows OS for the spoof anyway + // + // if you want to set the user agent to a custom string, just assign your string to + // $this->headers["User-Agent"] directly + function set_user_agent($agenttype,$agentversion,$windowsversion) { + $useragents = array( + "Mozilla/4.0 (compatible; MSIE %agent%; Windows NT %os%)", // IE + "Mozilla/5.0 (Windows; U; Windows NT %os%; en-US; rv:%agent%) Gecko/20040514", // Moz + "Mozilla/5.0 (Windows; U; Windows NT %os%; en-US; rv:1.7) Gecko/20040803 Firefox/%agent%", // FFox + "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT %os%) Opera %agent% [en]", // Opera + ); + $agent = $useragents[$agenttype]; + $this->headers["User-Agent"] = str_replace(array("%agent%","%os%"),array($agentversion,$windowsversion),$agent); + } + + // this isn't presently used as it's now handled inline by the request parser + function remove_chunkiness() { + $remaining = $this->response; + $this->response = ""; + + while ($remaining) { + $hexlen = strpos($remaining,"\r"); + $chunksize = substr($remaining,0,$hexlen); + $argstart = strpos($chunksize,';'); + if ($argstart!==false) $chunksize = substr($chunksize,0,$argstart); + $chunksize = (int) @hexdec($chunksize); + + $this->response .= substr($remaining,$hexlen+2,$chunksize); + $remaining = substr($remaining,$hexlen+2+$chunksize+2); + + if (!$chunksize) { + // either we're done, or something's borked... exit + $this->response .= $remaining; + return; + } + } + } + + // (internal) store a page in the cache + function _cache_store($token) { + $values = array( + "stats"=>$this->stats, + "result_code"=>$this->result_code, + "result_text"=>$this->result_text, + "version"=>$this->version, + "response"=>$this->response, + "response_headers"=>$this->response_headers, + "response_cookies"=>$this->response_cookies, + "raw_response"=>$this->raw_response, + ); + $values = serialize($values); + + $filename = $this->cache_path.$token.'.tmp'; + + $fp = @fopen($filename,"w"); + if (!$fp) { + $this->progress(HRP_DEBUG,"Unable to create cache file"); + return false; + } + fwrite($fp,$values); + fclose($fp); + + $this->progress(HRP_DEBUG,"HTTP response stored to cache"); + } + + // (internal) fetch a page from the cache + function _cache_fetch($token) { + $this->cache_hit = false; + $this->progress(HRP_DEBUG,"Checking for cached page value"); + + $filename = $this->cache_path.$token.'.tmp'; + if (!file_exists($filename)) { + $this->progress(HRP_DEBUG,"Page not available in cache"); + return false; + } + + if (time()-filemtime($filename)>$this->caching) { + $this->progress(HRP_DEBUG,"Page in cache is expired"); + @unlink($filename); + return false; + } + + if ($values = file_get_contents($filename)) { + $values = unserialize($values); + if (!$values) { + $this->progress(HRP_DEBUG,"Invalid cache contents"); + return false; + } + + $this->stats = $values["stats"]; + $this->result_code = $values["result_code"]; + $this->result_text = $values["result_text"]; + $this->version = $values["version"]; + $this->response = $values["response"]; + $this->response_headers = $values["response_headers"]; + $this->response_cookies = $values["response_cookies"]; + $this->raw_response = $values["raw_response"]; + + $this->progress(HRP_DEBUG,"Page loaded from cache"); + $this->cache_hit = true; + return true; + } else { + $this->progress(HRP_DEBUG,"Error reading cache file"); + return false; + } + } + + function parent_path($path) { + if (substr($path,0,1)=='/') $path = substr($path,1); + if (substr($path,-1)=='/') $path = substr($path,0,strlen($path)-1); + $path = explode('/',$path); + array_pop($path); + return count($path) ? ('/' . implode('/',$path)) : ''; + } + + // $cookies should be an array in one of two formats. + // + // Either: $cookies[ 'cookiename' ] = array ( + // '/path/'=>array( + // 'expires'=>time(), + // 'domain'=>'yourdomain.com', + // 'value'=>'cookievalue' + // ) + // ); + // + // Or, a more simplified format: + // $cookies[ 'cookiename' ] = 'value'; + // + // The former format will automatically check to make sure that the path, domain, + // and expiration values match the HTTP request, and will only send the cookie if + // they do match. The latter will force the cookie to be set for the HTTP request + // unconditionally. + // + function response_to_request_cookies($cookies,$urlinfo) { + + // check for simplified cookie format (name=value) + $cookiekeys = array_keys($cookies); + if (!count($cookiekeys)) return; + + $testkey = array_pop($cookiekeys); + if (!is_array($cookies[ $testkey ])) { + foreach ($cookies as $k=>$v) $this->request_cookies[$k] = $v; + return; + } + + // must not be simplified format, so parse as complex format: + foreach ($cookies as $name=>$paths) { + foreach ($paths as $path=>$values) { + // make sure the cookie isn't expired + if ( isset($values['expires']) && ($values['expires']request_cookies[$name] = $values['value']; + } + } + } + + // Execute the request for a particular URL, and transparently follow + // HTTP redirects if enabled. If $cookies is specified, it is assumed + // to be an array received from $this->response_cookies and will be + // processed to determine which cookies are valid for this host/URL. + function _execute_request($url,$cookies = false) { + // valid codes for which we transparently follow a redirect + $redirect_codes = array(301,302,303,307); + // valid methods for which we transparently follow a redirect + $redirect_methods = array('GET','HEAD'); + + $request_result = false; + + $this->followed_redirect = false; + $this->response_cookies = array(); + + $previous_redirects = array(); + do { + // send the request + $request_result = $this->_send_request($url,$cookies); + $lasturl = $url; + $url = false; + + // see if a redirect code was received + if ($this->follow_redirects && in_array($this->result_code,$redirect_codes)) { + + // only redirect on a code 303 or if the method was GET/HEAD + if ( ($this->result_code==303) || in_array($this->method,$redirect_methods) ) { + + // parse the information from the OLD URL so that we can handle + // relative links + $oldurlinfo = parse_url($lasturl); + + $url = $this->response_headers['Location']; + + // parse the information in the new URL, and fill in any blanks + // using values from the old URL + $urlinfo = parse_url($url); + foreach ($oldurlinfo as $k=>$v) { + if (!$urlinfo[$k]) $urlinfo[$k] = $v; + } + + // create an absolute path + if (substr($urlinfo['path'],0,1)!='/') { + $baseurl = $oldurlinfo['path']; + if (substr($baseurl,-1)!='/') $baseurl = $this->parent_path($url) . '/'; + $urlinfo['path'] = $baseurl . $urlinfo['path']; + } + + // rebuild the URL + $url = $this->rebuild_url($urlinfo); + + $this->progress(HRP_INFO,'Redirected to '.$url); + } + } + + if ( $url && strlen($url) ) { + + if (isset($previous_redirects[$url])) { + $this->error = "Infinite redirection loop"; + $request_result = false; + break; + } + if ( is_numeric($this->follow_redirects) && (count($previous_redirects)>$this->follow_redirects) ) { + $this->error = "Exceeded redirection limit"; + $request_result = false; + break; + } + + $previous_redirects[$url] = true; + } + + } while ($url && strlen($url)); + + // clear headers that shouldn't persist across multiple requests + $per_request_headers = array('Host','Content-Length'); + foreach ($per_request_headers as $k=>$v) unset($this->headers[$v]); + + if (count($previous_redirects)>1) $this->followed_redirect = array_keys($previous_redirects); + + return $request_result; + } + + // private - sends an HTTP request to $url + function _send_request($url,$cookies = false) { + $this->progress(HRP_INFO,"Initiating {$this->method} request for $url"); + if ($this->caching) { + $cachetoken = md5($url.'|'.$this->post_data); + if ($this->_cache_fetch($cachetoken)) return true; + } + + $time_request_start = $this->getmicrotime(); + + $urldata = parse_url($url); + $http_host = $urldata['host'] . (isset($urldata['port']) ? ':'.$urldata['port'] : ''); + + if (!isset($urldata["port"]) || !$urldata["port"]) $urldata["port"] = ($urldata["scheme"]=="https") ? 443 : 80; + if (!isset($urldata["path"]) || !$urldata["path"]) $urldata["path"] = '/'; + + if (!empty($urldata['user'])) $this->auth_username = $urldata['user']; + if (!empty($urldata['pass'])) $this->auth_password = $urldata['pass']; + + //echo "Sending HTTP/{$this->version} {$this->method} request for ".$urldata["host"].":".$urldata["port"]." page ".$urldata["path"]."
"; + + if ($this->version>"1.0") $this->headers["Host"] = $http_host; + if ($this->method=="POST") { + $this->headers["Content-Length"] = strlen($this->post_data); + if (!isset($this->headers["Content-Type"])) $this->headers["Content-Type"] = "application/x-www-form-urlencoded"; + } + + if ( !empty($this->auth_username) || !empty($this->auth_password) ) { + $this->headers['Authorization'] = 'Basic '.base64_encode($this->auth_username.':'.$this->auth_password); + } else { + unset($this->headers['Authorization']); + } + + if (is_array($cookies)) { + $this->response_to_request_cookies($cookies,$urldata); + } + + if (($this->method=="GET") && (!empty($urldata["query"]))) $urldata["path"] .= "?".$urldata["query"]; + $request = $this->method." ".$urldata["path"]." HTTP/".$this->version."\r\n"; + $request .= $this->build_headers(); + $request .= $this->post_data; + + $this->response = ""; + + // Native SSL support requires the OpenSSL extension, and was introduced in PHP 4.3.0 + $php_ssl_support = extension_loaded("openssl") && version_compare(phpversion(),"4.3.0")>=0; + + // if this is a plain HTTP request, or if it's an HTTPS request and OpenSSL support is available, + // natively perform the HTTP request + if ( ( ($urldata["scheme"]=="http") || ($php_ssl_support && ($urldata["scheme"]=="https")) ) && (!$this->force_curl) ) { + $curl_mode = false; + + $hostname = $this->connect_ip ? $this->connect_ip : $urldata['host']; + if ($urldata["scheme"]=="https") $hostname = 'ssl://'.$hostname; + + $time_connect_start = $this->getmicrotime(); + + $this->progress(HRP_INFO,'Opening socket connection to '.$hostname.' port '.$urldata['port']); + + $this->expected_bytes = -1; + $this->received_bytes = 0; + + $fp = @fsockopen ($hostname,$urldata["port"],$errno,$errstr,$this->connect_timeout); + $time_connected = $this->getmicrotime(); + $connect_time = $time_connected - $time_connect_start; + if ($fp) { + if ($this->stream_timeout) stream_set_timeout($fp,$this->stream_timeout); + $this->progress(HRP_INFO,"Connected; sending request"); + + $this->progress(HRP_DEBUG,$request); + fputs ($fp, $request); + $this->raw_request = $request; + + if ($this->stream_timeout) { + $meta = socket_get_status($fp); + if ($meta['timed_out']) { + $this->error = "Exceeded socket write timeout of ".$this->stream_timeout." seconds"; + $this->progress(HRP_ERROR,$this->error); + return false; + } + } + + $this->progress(HRP_INFO,"Request sent; awaiting reply"); + + $headers_received = false; + $data_length = false; + $chunked = false; + $iterations = 0; + while (!feof($fp)) { + + if ($data_length>0) { + $line = fread($fp,$data_length); + $data_length -= strlen($line); + } else { + $line = @fgets($fp,10240); + if ($chunked) { + $line = trim($line); + if (!strlen($line)) continue; + + list($data_length,) = explode(';',$line); + $data_length = (int) hexdec(trim($data_length)); + + if ($data_length==0) { + $this->progress(HRP_DEBUG,"Done"); + // end of chunked data + break; + } + $this->progress(HRP_DEBUG,"Chunk length $data_length (0x$line)"); + continue; + } + } + + $this->response .= $line; + + $iterations++; + if ($headers_received) { + if ($time_connected>0) { + $time_firstdata = $this->getmicrotime(); + $process_time = $time_firstdata - $time_connected; + $time_connected = 0; + } + $this->received_bytes += strlen($line); + if ($iterations % 20 == 0) { + $this->update_transfer_counters(); + } + } + + + // some dumbass webservers don't respect Connection: close and just + // leave the connection open, so we have to be diligent about + // calculating the content length so we can disconnect at the end of + // the response + if ( (!$headers_received) && (trim($line)=="") ) { + $headers_received = true; + + if (preg_match('/^Content-Length: ([0-9]+)/im',$this->response,$matches)) { + $data_length = (int) $matches[1]; + $this->progress(HRP_DEBUG,"Content length is $data_length"); + $this->expected_bytes = $data_length; + $this->update_transfer_counters(); + } + if (preg_match("/^Transfer-Encoding: chunked/im",$this->response,$matches)) { + $chunked = true; + $this->progress(HRP_DEBUG,"Chunked transfer encoding requested"); + } + + if (preg_match_all("/^Set-Cookie: ((.*?)\=(.*?)(?:;\s*(.*))?)$/im",$this->response,$cookielist,PREG_SET_ORDER)) { + // get the path for which cookies will be valid if no path is specified + $cookiepath = preg_replace('/\/{2,}/','',$urldata['path']); + if (substr($cookiepath,-1)!='/') { + $cookiepath = explode('/',$cookiepath); + array_pop($cookiepath); + $cookiepath = implode('/',$cookiepath) . '/'; + } + // process each cookie + foreach ($cookielist as $k=>$cookiedata) { + list(,$rawcookie,$name,$value,$attributedata) = $cookiedata; + $attributedata = explode(';',trim($attributedata)); + $attributes = array(); + + $cookie = array( + 'value'=>$value, + 'raw'=>trim($rawcookie), + ); + foreach ($attributedata as $k=>$attribute) { + list($attrname,$attrvalue) = explode('=',trim($attribute)); + $cookie[$attrname] = $attrvalue; + } + + if (!isset($cookie['domain']) || !$cookie['domain']) $cookie['domain'] = $urldata['host']; + if (!isset($cookie['path']) || !$cookie['path']) $cookie['path'] = $cookiepath; + if (isset($cookie['expires']) && $cookie['expires']) $cookie['expires'] = strtotime($cookie['expires']); + + if (!$this->validate_response_cookie($cookie,$urldata['host'])) continue; + + // do not store expired cookies; if one exists, unset it + if ( isset($cookie['expires']) && ($cookie['expires']response_cookies[ $name ][ $cookie['path'] ]); + return false; + } + + $this->response_cookies[ $name ][ $cookie['path'] ] = $cookie; + } + } + } + + //$this->progress(HRP_INFO,"Next [$line]"); + if ($this->stream_timeout) { + $meta = socket_get_status($fp); + if ($meta['timed_out']) { + $this->error = "Exceeded socket read timeout of ".$this->stream_timeout." seconds"; + $this->progress(HRP_ERROR,$this->error); + return false; + } + } + + // check time limits if requested + if ($this->max_time>0) { + if ($this->getmicrotime() - $time_request_start > $this->max_time) { + $this->error = "Exceeded maximum transfer time of ".$this->max_time." seconds"; + $this->progress(HRP_ERROR,$this->error); + return false; + break; + } + } + if ($this->result_close) { + if (preg_match_all("/HTTP\/([0-9\.]+) ([0-9]+) (.*?)[\r\n]/",$this->response,$matches)) { + $resultcodes = $matches[2]; + foreach ($resultcodes as $k=>$code) { + if ($code!=100) { + $this->progress(HRP_INFO,'HTTP result code received; closing connection'); + + $this->result_code = $code; + $this->result_text = $matches[3][$k]; + fclose($fp); + + return ($this->result_code==200); + } + } + } + } + } + @fclose ($fp); + + $this->update_transfer_counters(); + + if (is_array($this->response_cookies)) { + // make sure paths are sorted in the order in which they should be applied + // when setting response cookies + foreach ($this->response_cookies as $name=>$paths) { + ksort($this->response_cookies[$name]); + } + } + $this->progress(HRP_INFO,'Request complete'); + } else { + $this->error = strtoupper($urldata["scheme"])." connection to ".$hostname." port ".$urldata["port"]." failed"; + $this->progress(HRP_ERROR,$this->error); + return false; + } + + // perform an HTTP/HTTPS request using CURL + } elseif ( !$this->disable_curl && ( ($urldata["scheme"]=="https") || ($this->force_curl) ) ) { + $this->progress(HRP_INFO,'Passing HTTP request for $url to CURL'); + $curl_mode = true; + if (!$this->_curl_request($url)) return false; + + // unknown protocol + } else { + $this->error = "Unsupported protocol: ".$urldata["scheme"]; + $this->progress(HRP_ERROR,$this->error); + return false; + } + + $this->raw_response = $this->response; + + $totallength = strlen($this->response); + + do { + $headerlength = strpos($this->response,"\r\n\r\n"); + + $response_headers = explode("\r\n",substr($this->response,0,$headerlength)); + $http_status = trim(array_shift($response_headers)); + foreach ($response_headers as $line) { + list($k,$v) = explode(":",$line,2); + $this->response_headers[trim($k)] = trim($v); + } + $this->response = substr($this->response,$headerlength+4); + + /* // Handled in-transfer now + if (($this->response_headers['Transfer-Encoding']=="chunked") && (!$curl_mode)) { + $this->remove_chunkiness(); + } + */ + + if (!preg_match("/^HTTP\/([0-9\.]+) ([0-9]+) (.*?)$/",$http_status,$matches)) { + $matches = array("",$this->version,0,"HTTP request error"); + } + list (,$response_version,$this->result_code,$this->result_text) = $matches; + + // skip HTTP result code 100 (Continue) responses + } while (($this->result_code==100) && ($headerlength)); + + // record some statistics, roughly compatible with CURL's curl_getinfo() + if (!$curl_mode) { + $total_time = $this->getmicrotime() - $time_request_start; + $transfer_time = $total_time - $connect_time; + $this->stats = array( + "total_time"=>$total_time, + "connect_time"=>$connect_time, // time between connection request and connection established + "process_time"=>$process_time, // time between HTTP request and first data (non-headers) received + "url"=>$url, + "content_type"=>$this->response_headers["Content-Type"], + "http_code"=>$this->result_code, + "header_size"=>$headerlength, + "request_size"=>$totallength, + "filetime"=>strtotime($this->response_headers["Date"]), + "pretransfer_time"=>$connect_time, + "size_download"=>$totallength, + "speed_download"=>$transfer_time > 0 ? round($totallength / $transfer_time) : 0, + "download_content_length"=>$totallength, + "upload_content_length"=>0, + "starttransfer_time"=>$connect_time, + ); + } + + + $ok = ($this->result_code==200); + if ($ok && $this->caching) $this->_cache_store($cachetoken); + + return $ok; + } + + function validate_response_cookie($cookie,$actual_hostname) { + // make sure the cookie can't be set for a TLD, eg: '.com' + $cookiehost = $cookie['domain']; + $p = strrpos($cookiehost,'.'); + if ($p===false) return false; + + $tld = strtolower(substr($cookiehost,$p+1)); + $special_domains = array("com", "edu", "net", "org", "gov", "mil", "int"); + $periods_required = in_array($tld,$special_domains) ? 1 : 2; + + $periods = substr_count($cookiehost,'.'); + if ($periods<$periods_required) return false; + + if (substr($actual_hostname,0,1)!='.') $actual_hostname = '.'.$actual_hostname; + if (substr($cookiehost,0,1)!='.') $cookiehost = '.'.$cookiehost; + $domain_match = ( + ($actual_hostname==$cookiehost) || + (substr($actual_hostname,-strlen($cookiehost))==$cookiehost) + ); + + return $domain_match; + + } + + function build_headers() { + $headers = ""; + foreach ($this->headers as $name=>$value) { + $value = trim($value); + if (empty($value)) continue; + $headers .= "{$name}: {$value}\r\n"; + } + + if (isset($this->request_cookies) && is_array($this->request_cookies)) { + $cookielist = array(); + foreach ($this->request_cookies as $name=>$value) { + $cookielist[] = "{$name}={$value}"; + } + if (count($cookielist)) $headers .= "Cookie: ".implode('; ',$cookielist)."\r\n"; + } + + + $headers .= "\r\n"; + + return $headers; + } + + // opposite of parse_url() + function rebuild_url($urlinfo) { + $url = $urlinfo['scheme'].'://'; + + if ($urlinfo['user'] || $urlinfo['pass']) { + $url .= $urlinfo['user']; + if ($urlinfo['pass']) { + if ($urlinfo['user']) $url .= ':'; + $url .= $urlinfo['pass']; + } + $url .= '@'; + } + + $url .= $urlinfo['host']; + if ($urlinfo['port']) $url .= ':'.$urlinfo['port']; + + $url .= $urlinfo['path']; + + if ($urlinfo['query']) $url .= '?'.$urlinfo['query']; + if ($urlinfo['fragment']) $url .= '#'.$urlinfo['fragment']; + + return $url; + } + + function _replace_hostname(&$url,$new_hostname) { + $parts = parse_url($url); + $old_hostname = $parts['host']; + + $parts['host'] = $new_hostname; + + $url = $this->rebuild_url($parts); + + return $old_hostname; + } + + function _curl_request($url) { + $this->error = false; + + // if a direct connection IP address was specified, replace the hostname + // in the URL with the IP address, and set the Host: header to the + // original hostname + if ($this->connect_ip) { + $old_hostname = $this->_replace_hostname($url,$this->connect_ip); + $this->headers["Host"] = $old_hostname; + } + + + unset($this->headers["Content-Length"]); + $headers = explode("\n",$this->build_headers()); + + $ch = curl_init(); + curl_setopt($ch,CURLOPT_URL, $url); + curl_setopt($ch,CURLOPT_USERAGENT, $this->headers["User-Agent"]); + curl_setopt($ch,CURLOPT_HEADER, 1); + curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1); +// curl_setopt($ch,CURLOPT_FOLLOWLOCATION, 1); // native method doesn't support this yet, so it's disabled for consistency + curl_setopt($ch,CURLOPT_TIMEOUT, 10); + curl_setopt($ch,CURLOPT_HTTPHEADER, $headers); + + if ($this->method=="POST") { + curl_setopt($ch,CURLOPT_POST,1); + curl_setopt($ch,CURLOPT_POSTFIELDS,$this->post_data); + } + if ($this->insecure_ssl) { + curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,0); + } + if ($this->ignore_ssl_hostname) { + curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,1); + } + + $this->response = curl_exec ($ch); + if (curl_errno($ch)!=0) { + $this->error = "CURL error #".curl_errno($ch).": ".curl_error($ch); + } + + $this->stats = curl_getinfo($ch); + curl_close($ch); + + return ($this->error === false); + } + + function progress($level,$msg) { + if (is_callable($this->progress_callback)) call_user_func($this->progress_callback,$level,$msg); + } + + // Gets any available HTTPRetriever error message (including both internal + // errors and HTTP errors) + function get_error() { + return $this->error ? $this->error : 'HTTP ' . $this->result_code.': '.$this->result_text; + } + + function get_content_type() { + if (!$ctype = $this->response_headers['Content-Type']) { + $ctype = $this->response_headers['Content-type']; + } + list($ctype,) = explode(';',$ctype); + + return strtolower($ctype); + } + + function update_transfer_counters() { + if (is_callable($this->transfer_callback)) call_user_func($this->transfer_callback,$this->received_bytes,$this->expected_bytes); + } + + function set_transfer_display($enabled = true) { + if ($enabled) { + $this->transfer_callback = array(&$this,'default_transfer_callback'); + } else { + unset($this->transfer_callback); + } + } + + function set_progress_display($enabled = true) { + if ($enabled) { + $this->progress_callback = array(&$this,'default_progress_callback'); + } else { + unset($this->progress_callback); + } + } + + function default_progress_callback($severity,$message) { + $severities = array( + HRP_DEBUG=>'debug', + HRP_INFO=>'info', + HRP_ERROR=>'error', + ); + + echo date('Y-m-d H:i:sa').' ['.$severities[$severity].'] '.$message."\n"; + flush(); + } + + function default_transfer_callback($transferred,$expected) { + $msg = "Transferred " . round($transferred/1024,1); + if ($expected>=0) $msg .= "/" . round($expected/1024,1); + $msg .= "KB"; + if ($expected>0) $msg .= " (".round($transferred*100/$expected,1)."%)"; + echo date('Y-m-d H:i:sa')." $msg\n"; + flush(); + } + + function getmicrotime() { + list($usec, $sec) = explode(" ",microtime()); + return ((float)$usec + (float)$sec); + } +} +?> \ No newline at end of file diff --git a/lib/libcurlemu/libcurlemu.inc.php b/lib/libcurlemu/libcurlemu.inc.php new file mode 100755 index 00000000000..bb04bbc16db --- /dev/null +++ b/lib/libcurlemu/libcurlemu.inc.php @@ -0,0 +1,105 @@ + \ No newline at end of file diff --git a/lib/libcurlemu/libcurlexternal.inc.php b/lib/libcurlemu/libcurlexternal.inc.php new file mode 100755 index 00000000000..f7284f939b6 --- /dev/null +++ b/lib/libcurlemu/libcurlexternal.inc.php @@ -0,0 +1,637 @@ +$v) { + if ( (substr($k,0,8)=="CURLOPT_") && ($v==$curlopt)) return $k; + } + return false; +} + +// Initialize a CURL emulation session +function curl_init($url=false) { + $i = $GLOBALS["_CURLEXT_OPT"]["index"]++; + $GLOBALS["_CURLEXT_OPT"][$i] = array("url"=>$url); + + return $i; +} + +// Set an option for a CURL emulation transfer +function curl_setopt($ch,$option,$value) { + + $opt = &$GLOBALS["_CURLEXT_OPT"][$ch]; + if (!$opt["args"]) $opt["args"] = array(); + $args = &$opt["args"]; + if (!$opt["settings"]) $opt["settings"] = array(); + $settings = &$opt["settings"]; + + switch($option) { + case CURLOPT_URL: + $opt["url"] = $value; + break; + case CURLOPT_VERBOSE: + $opt["verbose"] = $value>0; + break; + case CURLOPT_USERPWD: + if ($value==="") $value = false; + $settings["user"] = $value; + break; + case CURLOPT_PROXYUSERPWD: + if ($value==="") $value = false; + $settings["proxy-user"] = $value; + break; + case CURLOPT_COOKIE: + if ($value==="") $value = false; + if ( is_bool($value) || (strpos($value,"=")!==false) ) $settings["cookie"] = $value; + break; + case CURLOPT_COOKIEFILE: + if ($value==="") $value = false; + $settings["cookie"] = $value; + break; + case CURLOPT_COOKIEJAR: + if ($value==="") $value = false; + $settings["cookie-jar"] = $value; + break; + case CURLOPT_CUSTOMREQUEST: + if ($value==="") $value = false; + $settings["request"] = $value; + break; + case CURLOPT_PROXY: + if ($value==="") $value = false; + $settings["proxy"] = $value; + break; + case CURLOPT_INTERFACE: + if ($value==="") $value = false; + $settings["interface"] = $value; + break; + case CURLOPT_KRB4LEVEL: + if ($value==="") $value = false; + $settings["krb4"] = $value; + break; + case CURLOPT_SSLCERT: + $pass = ""; + if (is_string($settings["cert"])) { + list(,$pass) = explode(":",$settings["cert"]); + if (strlen($pass)) $pass = ":$pass"; + } + $settings["cert"] = $value.$pass; + break; + case CURLOPT_SSLCERTPASSWD: + $filename = ""; + if (is_string($settings["cert"])) { + list($filename,) = explode(":",$settings["cert"]); + } + $settings["cert"] = $filename.":".$value; + break; + case CURLOPT_RANGE: + if ($value==="") $value = false; + $settings["range"] = $value; + break; + case CURLOPT_REFERER: + if ($value==="") $value = false; + $settings["referer"] = $value; + break; + case CURLOPT_NOBODY: + $settings["head"] = $value>0; + break; + case CURLOPT_FAILONERROR: + $opt["fail_on_error"] = $value>0; + break; + case CURLOPT_USERAGENT: + $settings["user-agent"] = $value; + break; + case CURLOPT_HEADER: + $settings["include"] = $value>0; + break; + case CURLOPT_RETURNTRANSFER: + $opt["return_transfer"] = $value>0; + break; + case CURLOPT_TIMEOUT: + $settings["max-time"] = (int) $value; + break; + case CURLOPT_HTTPHEADER: + reset($value); + foreach ($value as $k=>$header) $args[] = "header=".$header; + break; + case CURLOPT_POST: + $settings["data"]["enabled"] = $value>0; + break; + case CURLOPT_POSTFIELDS: + if ($value==="") $value = false; + $settings["data"]["value"] = $value; + break; + case CURLOPT_SSL_VERIFYPEER: + $settings["insecure"] = ($value==0); + break; + case CURLOPT_SSL_VERIFYHOST: + // not supported by the commandline client + break; + case CURLOPT_FOLLOWLOCATION: + $settings["location"] = $value>0; + break; + case CURLOPT_PUT: + $settings["upload-file"]["enabled"] = $value>0; + break; + case CURLOPT_INFILE: + if ($value==="") $value = false; + + if (is_resource($value)) { + + // Ugh, this is a terrible hack. The CURL extension accepts a file handle, but + // the CURL binary obviously wants a filename. Since you can't derive a filename + // from a file handle, we have to make a copy of the file from the file handle, + // then pass the temporary filename to the CURL binary. + + $tmpfilename = tempnam("/tmp","cif"); + $fp = @fopen($tmpfilename,"w"); + if (!$fp) { + trigger_error("CURL emulation library could not create a temporary file for CURLOPT_INFILE; upload aborted",E_USER_WARNING); + } else { + while (!feof($value)) { + $contents = fread($value,8192); + fwrite($fp,$contents); + } + fclose($fp); + // if a temporary file was previously created, unlink it + if ($settings["upload-file"]["value"] && file_exists($settings["upload-file"]["value"])) unlink($settings["upload-file"]["value"]); + + // set the new upload-file filename + $settings["upload-file"]["value"] = $tmpfilename; + } + } else { + trigger_error("CURLOPT_INFILE must specify a valid file resource",E_USER_WARNING); + } + + break; + case CURLOPT_MUTE: + // we're already mute, no? + break; + case CURLOPT_LOW_SPEED_LIMIT: + $settings["speed-limit"] = (int) $value; + break; + case CURLOPT_LOW_SPEED_TIME: + $settings["speed-time"] = (int) $value; + break; + case CURLOPT_RESUME_FROM: + $settings["continue-at"] = (int) $value; + break; + case CURLOPT_CAINFO: + if ($value==="") $value = false; + $settings["cacert"] = $value; + break; + case CURLOPT_SSLVERSION: + $value = (int) $value; + switch($value) { + case 2: + case 3: + unset($settings["sslv2"]); + unset($settings["sslv3"]); + $settings["sslv".$value] = true; + break; + } + break; + case CURLOPT_TIMECONDITION: + // untested - I'm lazy :) + if (!isset($settings["time-cond"]["enabled"])) $settings["time-cond"]["enabled"] = false; + if (!$settings["time-cond"]["value"]) $settings["time-cond"]["value"] = 1; + + $settings["time-cond"]["value"] = abs($settings["time-cond"]["value"]); + if ($value==TIMECOND_ISUNMODSINCE) { + $settings["time-cond"]["value"] *= -1; + } + + break; + case CURLOPT_TIMEVALUE: + // untested - I'm lazy :) + if ($settings["time-cond"]["value"]) { + $sign = $settings["time-cond"]["value"] / abs($settings["time-cond"]["value"]); + } else { + $sign = 1; + } + $settings["time-cond"]["value"] = (int) $value * $sign; + break; + case CURLOPT_FILE: + if (is_resource($value)) { + $opt["output_handle"] = $value; + } else { + trigger_error("CURLOPT_FILE must specify a valid file resource",E_USER_WARNING); + } + break; + case CURLOPT_WRITEHEADER: + if (is_resource($value)) { + $opt["header_handle"] = $value; + } else { + trigger_error("CURLOPT_WRITEHEADER must specify a valid file resource",E_USER_WARNING); + } + break; + case CURLOPT_STDERR: + // not implemented for now - not really relevant + break; + // FTP stuff not implemented + case CURLOPT_QUOTE: + case CURLOPT_POSTQUOTE: + case CURLOPT_UPLOAD: + case CURLOPT_FTPLISTONLY: + case CURLOPT_FTPAPPEND: + case CURLOPT_FTPPORT: + // Other stuff not implemented + case CURLOPT_NETRC: + default: + trigger_error("CURL emulation does not implement CURL option "._curlopt_name($option),E_USER_WARNING); + break; + } +} + +// Perform a CURL emulation session +function curl_exec($ch) { + $opt = &$GLOBALS["_CURLEXT_OPT"][$ch]; + $url = $opt["url"]; + $verbose = $opt["verbose"]; + + // ask commandline CURL to return its statistics at the end of its output + $opt["settings"]["write-out"] = "%{http_code}|%{time_total}|%{time_namelookup}|%{time_connect}|%{time_pretransfer}|%{time_starttransfer}|%{size_download}|%{size_upload}|%{size_header}|%{size_request}|%{speed_download}|%{speed_upload}|||||||%{content_type}|%{url_effective}"; + $writeout_order = array( + CURLINFO_HTTP_CODE, + CURLINFO_TOTAL_TIME, + CURLINFO_NAMELOOKUP_TIME, + CURLINFO_CONNECT_TIME, + CURLINFO_PRETRANSFER_TIME, + CURLINFO_STARTTRANSFER_TIME, + CURLINFO_SIZE_DOWNLOAD, + CURLINFO_SIZE_UPLOAD, + CURLINFO_HEADER_SIZE, + CURLINFO_REQUEST_SIZE, + CURLINFO_SPEED_DOWNLOAD, + CURLINFO_SPEED_UPLOAD, + + // the following 5 items are not provided by commandline CURL, and thus are left empty + CURLINFO_FILETIME, + CURLINFO_REDIRECT_TIME, + CURLINFO_SSL_VERIFYRESULT, + CURLINFO_CONTENT_LENGTH_DOWNLOAD, + CURLINFO_CONTENT_LENGTH_UPLOAD, + CURLINFO_REDIRECT_COUNT, + + CURLINFO_CONTENT_TYPE, + CURLINFO_EFFECTIVE_URL, + ); + + // if the CURLOPT_NOBODY option was specified (to remove the body from the output), + // but an output file handle was set, we need to tell CURL to return the body so + // that we can write it to the output handle and strip it from the output + if ($opt["settings"]["head"] && $opt["output_handle"]) { + unset($opt["settings"]["head"]); + $strip_body = true; + } + // if the CURLOPT_HEADER option was NOT specified, but a header file handle was + // specified, we again need to tell CURL to return the headers so we can write + // them, then strip them from the output + if (!isset($opt["settings"]["include"]) && isset($opt["header_handle"])) { + $opt["settings"]["include"] = true; + $strip_headers = true; + } + + // build the CURL argument list + $arguments = ""; + foreach ($opt["args"] as $k=>$arg) { + list($argname,$argval) = explode('=',$arg,2); + $arguments .= "--$argname ".escapeshellarg($argval)." "; + } + foreach ($opt["settings"] as $argname=>$argval) { + if (is_array($argval)) { + if (isset($argval["enabled"]) && !$argval["enabled"]) continue; + $argval = $argval["value"]; + } + if ($argval===false) continue; + if (is_bool($argval)) $argval = ""; + $arguments .= "--$argname ".escapeshellarg($argval)." "; + } + + // build the CURL commandline and execute it + $cmd = CURL_PATH." ".$arguments." ".escapeshellarg($url); + + if ($verbose) echo "libcurlemu: Executing: $cmd\n"; + exec($cmd,$output,$ret); + + if ($verbose) { + echo "libcurlemu: Result: "; + var_dump($output); + echo "libcurlemu: Exit code: $ret\n"; + } + + // check for errors + $opt["errno"] = $ret; + if ($ret) $opt["error"] = "CURL error #$ret"; + + // die if CURLOPT_FAILONERROR is set and the HTTP result code is greater than 300 + if ($opt["fail_on_error"]) { + if (preg_match("/^HTTP\/1.[0-9]+ ([0-9]{3}) /",$output[0],$matches)) { + $resultcode = (int) $matches[1]; + if ($resultcode>300) die; + } else { + die; // couldn't get result code! + } + } + + // pull the statistics out from the output + $stats = explode('|',array_pop($output)); + foreach ($writeout_order as $k=>$item) { + $opt["stats"][$item] = $stats[$k]; + } + + // build the response string + $output = implode("\r\n",$output); + + + // find the header end position if needed + if ($strip_headers || $strip_body || isset($opt["header_handle"])) { + $headerpos = strpos($output,"\r\n\r\n"); + } + + // if a file handle was provided for header output, extract the headers + // and write them to the handle + if (isset($opt["header_handle"])) { + $headers = substr($output,0,$headerpos); + fwrite($opt["header_handle"],$headers); + } + + // if the caller did not request headers in the output, strip them + if ($strip_headers) { + $output = substr($output,$headerpos+4); + } + + // if the caller did not request the response body in the output, strip it + if ($strip_body) { + if ($strip_headers) { + $body = $output; + $output = ""; + } else { + $body = substr($output,$headerpos+4); + $output = substr($output,0,$headerpos); + } + } + + // if a file handle was provided for output, write the output to it + if (isset($opt["output_handle"])) { + fwrite($opt["output_handle"],$output); + + // if the caller requested that the response be returned, return it + } elseif ($opt["return_transfer"]) { + return $output; + + // otherwise, just echo the output to stdout + } else { + echo $output; + } + return true; +} + +function curl_close($ch) { + $opt = &$GLOBALS["_CURLEXT_OPT"][$ch]; + + if ($opt["settings"]) { + $settings = &$opt["settings"]; + // if the user used CURLOPT_INFILE to specify a file to upload, remove the + // temporary file created for the CURL binary + if ($settings["upload-file"]["value"] && file_exists($settings["upload-file"]["value"])) unlink($settings["upload-file"]["value"]); + } + + unset($GLOBALS["_CURLEXT_OPT"][$ch]); +} + +function curl_errno($ch) { + return (int) $GLOBALS["_CURLEXT_OPT"][$ch]["errno"]; +} + +function curl_error($ch) { + return $GLOBALS["_CURLEXT_OPT"][$ch]["error"]; +} + +function curl_getinfo($ch,$opt=NULL) { + if ($opt) { + return $GLOBALS["_CURLEXT_OPT"][$ch]["stats"][$opt]; + } else { + $curlinfo_tags = array( + "url"=>CURLINFO_EFFECTIVE_URL, + "content_type"=>CURLINFO_CONTENT_TYPE, + "http_code"=>CURLINFO_HTTP_CODE, + "header_size"=>CURLINFO_HEADER_SIZE, + "request_size"=>CURLINFO_REQUEST_SIZE, + "filetime"=>CURLINFO_FILETIME, + "ssl_verify_result"=>CURLINFO_SSL_VERIFYRESULT, + "redirect_count"=>CURLINFO_REDIRECT_COUNT, + "total_time"=>CURLINFO_TOTAL_TIME, + "namelookup_time"=>CURLINFO_NAMELOOKUP_TIME, + "connect_time"=>CURLINFO_CONNECT_TIME, + "pretransfer_time"=>CURLINFO_PRETRANSFER_TIME, + "size_upload"=>CURLINFO_SIZE_UPLOAD, + "size_download"=>CURLINFO_SIZE_DOWNLOAD, + "speed_download"=>CURLINFO_SPEED_DOWNLOAD, + "speed_upload"=>CURLINFO_SPEED_UPLOAD, + "download_content_length"=>CURLINFO_CONTENT_LENGTH_DOWNLOAD, + "upload_content_length"=>CURLINFO_CONTENT_LENGTH_UPLOAD, + "starttransfer_time"=>CURLINFO_STARTTRANSFER_TIME, + "redirect_time"=>CURLINFO_REDIRECT_TIME + ); + $res = array(); + foreach ($curlinfo_tags as $tag=>$opt) { + $res[$tag] = $GLOBALS["_CURLEXT_OPT"][$ch]["stats"][$opt]; + } + return $res; + } +} + +function curl_version() { + return "libcurlemu/".CURLEXT_VERSION."-ext"; +} + +} +?> \ No newline at end of file diff --git a/lib/libcurlemu/libcurlnative.inc.php b/lib/libcurlemu/libcurlnative.inc.php new file mode 100755 index 00000000000..471a8c0b11d --- /dev/null +++ b/lib/libcurlemu/libcurlnative.inc.php @@ -0,0 +1,453 @@ +$v) { + if ( (substr($k,0,8)=="CURLOPT_") && ($v==$curlopt)) return $k; + } + return false; +} + +// Initialize a CURL emulation session +function curl_init() { + $i = $GLOBALS["_CURLNAT_OPT"]["index"]++; + $GLOBALS["_CURLNAT_OPT"][$i] = array(); + $GLOBALS["_CURLNAT_OPT"][$i]["http"] = &new HTTPRetriever(); + $GLOBALS["_CURLNAT_OPT"][$i]["include_body"] = true; + return $i; +} + +// Set an option for a CURL emulation transfer +function curl_setopt($ch,$option,$value) { + + $opt = &$GLOBALS["_CURLNAT_OPT"][$ch]; + if (!$opt["args"]) $opt["args"] = array(); + $args = &$opt["args"]; + if (!$opt["settings"]) $opt["settings"] = array(); + $settings = &$opt["settings"]; + $http = &$opt["http"]; + + switch($option) { + case CURLOPT_URL: + $opt["url"] = $value; + break; + case CURLOPT_CUSTOMREQUEST: + $opt["method"] = $value; + break; + case CURLOPT_REFERER: + $http->headers["Referer"] = $value; + break; + case CURLOPT_NOBODY: + $opt["include_body"] = $value==0; + break; + case CURLOPT_FAILONERROR: + $opt["fail_on_error"] = $value>0; + break; + case CURLOPT_USERAGENT: + $http->headers["User-Agent"] = $value; + break; + case CURLOPT_HEADER: + $opt["include_headers"] = $value>0; + break; + case CURLOPT_RETURNTRANSFER: + $opt["return_transfer"] = $value>0; + break; + case CURLOPT_TIMEOUT: + $opt["max-time"] = (int) $value; + break; + case CURLOPT_HTTPHEADER: + reset($value); + foreach ($value as $k=>$header) { + list($headername,$headervalue) = explode(":",$header); + $http->headers[$headername] = ltrim($headervalue); + } + break; + case CURLOPT_POST: + $opt["post"] = $value>0; + break; + case CURLOPT_POSTFIELDS: + $opt["postdata"] = $value; + break; + case CURLOPT_MUTE: + // we're already mute, no? + break; + case CURLOPT_FILE: + if (is_resource($value)) { + $opt["output_handle"] = $value; + } else { + trigger_error("CURLOPT_FILE must specify a valid file resource",E_USER_WARNING); + } + break; + case CURLOPT_WRITEHEADER: + if (is_resource($value)) { + $opt["header_handle"] = $value; + } else { + trigger_error("CURLOPT_WRITEHEADER must specify a valid file resource",E_USER_WARNING); + } + break; + case CURLOPT_STDERR: + // not implemented for now - not really relevant + break; + + case CURLOPT_SSL_VERIFYPEER: + case CURLOPT_SSL_VERIFYHOST: + // these are automatically disabled using ssl:// anyway + break; + + case CURLOPT_USERPWD: + list($curl_user,$curl_pass) = explode(':',$value,2); + $http->auth_username = $curl_user; + $http->auth_password = $curl_pass; + break; + + // Important stuff not implemented (as it's not yet supported by HTTPRetriever) + case CURLOPT_PUT: + case CURLOPT_INFILE: + case CURLOPT_FOLLOWLOCATION: + case CURLOPT_PROXYUSERPWD: + case CURLOPT_COOKIE: + case CURLOPT_COOKIEFILE: + case CURLOPT_PROXY: + case CURLOPT_RANGE: + case CURLOPT_RESUME_FROM: + + // Things that cannot (reasonably) be implemented here + case CURLOPT_LOW_SPEED_LIMIT: + case CURLOPT_LOW_SPEED_TIME: + case CURLOPT_KRB4LEVEL: + case CURLOPT_SSLCERT: + case CURLOPT_SSLCERTPASSWD: + case CURLOPT_SSLVERSION: + case CURLOPT_INTERFACE: + case CURLOPT_CAINFO: + case CURLOPT_TIMECONDITION: + case CURLOPT_TIMEVALUE: + + // FTP stuff not implemented + case CURLOPT_QUOTE: + case CURLOPT_POSTQUOTE: + case CURLOPT_UPLOAD: + case CURLOPT_FTPLISTONLY: + case CURLOPT_FTPAPPEND: + case CURLOPT_FTPPORT: + + // Other stuff not implemented + case CURLOPT_VERBOSE: + case CURLOPT_NETRC: + default: + trigger_error("CURL emulation does not implement CURL option "._curlopt_name($option),E_USER_WARNING); + break; + } +} + +// Perform a CURL emulation session +function curl_exec($ch) { + $opt = &$GLOBALS["_CURLNAT_OPT"][$ch]; + $url = $opt["url"]; + + $http = &$opt["http"]; + $http->disable_curl = true; // avoid problems with recursion, since we *ARE* CURL + + // set time limits if requested + if ($opt["max-time"]) { + $http->connect_timeout = $opt["max-time"]; + $http->max_time = $opt["max-time"]; + } + + if ($opt["post"]) { + $res = $http->post($url,$opt["postdata"]); + } elseif ($opt["method"]) { + $res = $http->custom($opt["method"],$url,$opt["postdata"]); + } else { + $res = $http->get($url); + } + + // check for errors + $opt["errno"] = (!$res && $http->error) ? 1 : 0; + if ($opt["errno"]) $opt["error"] = $http->error; + + // die if CURLOPT_FAILONERROR is set and the HTTP result code is greater than 300 + if ($opt["fail_on_error"]) { + if ($http->result_code>300) die; + } + + $opt["stats"] = $http->stats; + + + $headers = ""; + foreach ($http->response_headers as $k=>$v) { + $headers .= "$k: $v\r\n"; + } + + // if a file handle was provided for header output, extract the headers + // and write them to the handle + if (isset($opt["header_handle"])) { + fwrite($opt["header_handle"],$headers); + } + + $output = ($opt["include_headers"] ? $headers."\r\n" : "") . ($opt["include_body"] ? $http->response : ""); + + // if a file handle was provided for output, write the output to it + if (isset($opt["output_handle"])) { + fwrite($opt["output_handle"],$output); + + // if the caller requested that the response be returned, return it + } elseif ($opt["return_transfer"]) { + return $output; + + // otherwise, just echo the output to stdout + } else { + echo $output; + } + return true; +} + +function curl_close($ch) { + $opt = &$GLOBALS["_CURLNAT_OPT"][$ch]; + + if ($opt["settings"]) { + $settings = &$opt["settings"]; + // if the user used CURLOPT_INFILE to specify a file to upload, remove the + // temporary file created for the CURL binary + if ($settings["upload-file"]["value"] && file_exists($settings["upload-file"]["value"])) unlink($settings["upload-file"]["value"]); + } + + unset($GLOBALS["_CURLNAT_OPT"][$ch]); +} + +function curl_errno($ch) { + return (int) $GLOBALS["_CURLNAT_OPT"][$ch]["errno"]; +} + +function curl_error($ch) { + return $GLOBALS["_CURLNAT_OPT"][$ch]["error"]; +} + +function curl_getinfo($ch,$opt=NULL) { + if ($opt) { + $curlinfo_tags = array( + CURLINFO_EFFECTIVE_URL=>"url", + CURLINFO_CONTENT_TYPE=>"content_type", + CURLINFO_HTTP_CODE=>"http_code", + CURLINFO_HEADER_SIZE=>"header_size", + CURLINFO_REQUEST_SIZE=>"request_size", + CURLINFO_FILETIME=>"filetime", + CURLINFO_SSL_VERIFYRESULT=>"ssl_verify_result", + CURLINFO_REDIRECT_COUNT=>"redirect_count", + CURLINFO_TOTAL_TIME=>"total_time", + CURLINFO_NAMELOOKUP_TIME=>"namelookup_time", + CURLINFO_CONNECT_TIME=>"connect_time", + CURLINFO_PRETRANSFER_TIME=>"pretransfer_time", + CURLINFO_SIZE_UPLOAD=>"size_upload", + CURLINFO_SIZE_DOWNLOAD=>"size_download", + CURLINFO_SPEED_DOWNLOAD=>"speed_download", + CURLINFO_SPEED_UPLOAD=>"speed_upload", + CURLINFO_CONTENT_LENGTH_DOWNLOAD=>"download_content_length", + CURLINFO_CONTENT_LENGTH_UPLOAD=>"upload_content_length", + CURLINFO_STARTTRANSFER_TIME=>"starttransfer_time", + CURLINFO_REDIRECT_TIME=>"redirect_time" + ); + + $key = $curlinfo_tags[$opt]; + return $GLOBALS["_CURLNAT_OPT"][$ch]["stats"][$key]; + } else { + return $GLOBALS["_CURLNAT_OPT"][$ch]["stats"]; + } +} + +function curl_version() { + return "libcurlemu/".CURLNAT_VERSION."-nat"; +} + +} +?> \ No newline at end of file diff --git a/lib/libcurlemu/readme_moodle.txt b/lib/libcurlemu/readme_moodle.txt new file mode 100644 index 00000000000..046eb2cbe4a --- /dev/null +++ b/lib/libcurlemu/readme_moodle.txt @@ -0,0 +1,7 @@ +Description of libcurlemu v1.0.3 import into Moodle + +Changes: + * example.php - removed + * original HTTPRetriever v1.1.5 replaced by standalone package v1.1.9 + +$Id$ \ No newline at end of file