diff --git a/.buildpath b/.buildpath
new file mode 100644
index 0000000000000000000000000000000000000000..8bcb4b5fd612e3ad55fb07e4bed087c55afd0861
--- /dev/null
+++ b/.buildpath
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<buildpath>
+	<buildpathentry kind="src" path=""/>
+	<buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/>
+</buildpath>
diff --git a/.project b/.project
new file mode 100644
index 0000000000000000000000000000000000000000..fc09879368ec85178bdd683d878d157e52c96fca
--- /dev/null
+++ b/.project
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>batchval</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.wst.validation.validationbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.dltk.core.scriptbuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.php.core.PHPNature</nature>
+	</natures>
+</projectDescription>
diff --git a/lib/.configsnapshots/configsnapshot-2010-01-21 08-49-21.xml b/lib/.configsnapshots/configsnapshot-2010-01-21 08-49-21.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fcb4b523c4051acadbb36fb5998b26d89636eb83
--- /dev/null
+++ b/lib/.configsnapshots/configsnapshot-2010-01-21 08-49-21.xml	
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<pearconfig version="1.0"><php_dir>/Users/bbieber/Documents/workspace/batchval/lib/php</php_dir><ext_dir>/usr/local/lib/php/extensions/no-debug-non-zts-20090626/</ext_dir><cfg_dir>/Users/bbieber/Documents/workspace/batchval/lib/cfg</cfg_dir><doc_dir>/Users/bbieber/Documents/workspace/batchval/lib/docs</doc_dir><bin_dir>/usr/local/bin</bin_dir><data_dir>/Users/bbieber/Documents/workspace/batchval/lib/data</data_dir><www_dir>/Users/bbieber/Documents/workspace/batchval/lib/www</www_dir><test_dir>/Users/bbieber/Documents/workspace/batchval/lib/tests</test_dir><src_dir>/Users/bbieber/Documents/workspace/batchval/lib/src</src_dir><php_bin>/usr/local/bin/php</php_bin><php_ini>/usr/local/lib/php.ini</php_ini><php_prefix></php_prefix><php_suffix></php_suffix></pearconfig>
diff --git a/lib/.pear2registry b/lib/.pear2registry
new file mode 100644
index 0000000000000000000000000000000000000000..acfed69f6d81490d55229c9f4ed88bf6288868b8
Binary files /dev/null and b/lib/.pear2registry differ
diff --git a/lib/.xmlregistry/channels/channel-__uri.xml b/lib/.xmlregistry/channels/channel-__uri.xml
new file mode 100644
index 0000000000000000000000000000000000000000..987a5938d21ff15cb9d5a48bd0f9ba85a01366e8
--- /dev/null
+++ b/lib/.xmlregistry/channels/channel-__uri.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd">
+ <name>__uri</name>
+ <suggestedalias>__uri</suggestedalias>
+ <summary>Pseudo-channel for static packages</summary>
+ <servers>
+  <primary>
+   <xmlrpc>
+    <function version="1.0">****</function>
+   </xmlrpc>
+  </primary>
+ </servers>
+</channel>
diff --git a/lib/.xmlregistry/channels/channel-doc.php.net.xml b/lib/.xmlregistry/channels/channel-doc.php.net.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d221a21bb166d01bd1460fba227cb8c11378aecb
--- /dev/null
+++ b/lib/.xmlregistry/channels/channel-doc.php.net.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd">
+ <name>doc.php.net</name>
+ <summary>PHP Documentation team</summary>
+ <suggestedalias>phpdocs</suggestedalias>
+ <servers>
+  <primary>
+   <rest>
+    <baseurl type="REST1.0">http://doc.php.net/rest/</baseurl>
+    <baseurl type="REST1.1">http://doc.php.net/rest/</baseurl>
+   </rest>
+  </primary>
+ </servers>
+</channel>
diff --git a/lib/.xmlregistry/channels/channel-pear.php.net.xml b/lib/.xmlregistry/channels/channel-pear.php.net.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c7098913388f024799a647718a87e499845383cc
--- /dev/null
+++ b/lib/.xmlregistry/channels/channel-pear.php.net.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd">
+ <name>pear.php.net</name>
+ <suggestedalias>pear</suggestedalias>
+ <summary>PHP Extension and Application Repository</summary>
+ <servers>
+  <primary>
+   <rest>
+    <baseurl type="REST1.0">http://pear.php.net/rest/</baseurl>
+    <baseurl type="REST1.1">http://pear.php.net/rest/</baseurl>
+    <baseurl type="REST1.2">http://pear.php.net/rest/</baseurl>
+    <baseurl type="REST1.3">http://pear.php.net/rest/</baseurl>
+   </rest>
+  </primary>
+  <mirror host="us.pear.php.net">
+   <rest>
+    <baseurl type="REST1.0">http://us.pear.php.net/rest/</baseurl>
+    <baseurl type="REST1.1">http://us.pear.php.net/rest/</baseurl>
+    <baseurl type="REST1.2">http://us.pear.php.net/rest/</baseurl>
+    <baseurl type="REST1.3">http://us.pear.php.net/rest/</baseurl>
+   </rest>
+  </mirror>
+  <mirror host="de.pear.php.net" ssl="yes" port="3452">
+   <rest>
+    <baseurl type="REST1.0">https://de.pear.php.net:3452/rest/</baseurl>
+    <baseurl type="REST1.1">https://de.pear.php.net:3452/rest/</baseurl>
+    <baseurl type="REST1.2">https://de.pear.php.net:3452/rest/</baseurl>
+    <baseurl type="REST1.3">https://de.pear.php.net:3452/rest/</baseurl>
+   </rest>
+  </mirror>
+ </servers>
+</channel>
diff --git a/lib/.xmlregistry/channels/channel-pear2.php.net.xml b/lib/.xmlregistry/channels/channel-pear2.php.net.xml
new file mode 100644
index 0000000000000000000000000000000000000000..dbcaccb25a38268191e77901c6f3be7714a91477
--- /dev/null
+++ b/lib/.xmlregistry/channels/channel-pear2.php.net.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd">
+ <name>pear2.php.net</name>
+ <suggestedalias>pear2</suggestedalias>
+ <summary>PEAR packages for PHP 5.3+ installed by Pyrus</summary>
+ <servers>
+  <primary>
+   <rest>
+    <baseurl type="REST1.0">http://pear2.php.net/rest/</baseurl>
+    <baseurl type="REST1.1">http://pear2.php.net/rest/</baseurl>
+    <baseurl type="REST1.2">http://pear2.php.net/rest/</baseurl>
+    <baseurl type="REST1.3">http://pear2.php.net/rest/</baseurl>
+   </rest>
+  </primary>
+ </servers>
+</channel>
diff --git a/lib/.xmlregistry/channels/channel-pearhub.org.xml b/lib/.xmlregistry/channels/channel-pearhub.org.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5985b4420f545d03a822f99d0b704b23effb1998
--- /dev/null
+++ b/lib/.xmlregistry/channels/channel-pearhub.org.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd">
+ <name>pearhub.org</name>
+ <summary>pearhub PEAR channel</summary>
+ <suggestedalias>pearhub</suggestedalias>
+ <servers>
+  <primary>
+   <rest>
+    <baseurl type="REST1.0">http://pearhub.org/rest/</baseurl>
+    <baseurl type="REST1.1">http://pearhub.org/rest/</baseurl>
+    <baseurl type="REST1.2">http://pearhub.org/rest/</baseurl>
+    <baseurl type="REST1.3">http://pearhub.org/rest/</baseurl>
+   </rest>
+  </primary>
+ </servers>
+</channel>
diff --git a/lib/.xmlregistry/channels/channel-pecl.php.net.xml b/lib/.xmlregistry/channels/channel-pecl.php.net.xml
new file mode 100644
index 0000000000000000000000000000000000000000..cd6b257fc15d400fb0f1bcc38cdf57437c288a2b
--- /dev/null
+++ b/lib/.xmlregistry/channels/channel-pecl.php.net.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<channel version="1.0" xmlns="http://pear.php.net/channel-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/channel-1.0http://pear.php.net/dtd/channel-1.0.xsd">
+ <name>pecl.php.net</name>
+ <suggestedalias>pecl</suggestedalias>
+ <summary>PHP Extension Community Library</summary>
+ <validatepackage version="1.0">PEAR_Validator_PECL</validatepackage>
+ <servers>
+  <primary>
+   <xmlrpc>
+    <function version="1.0">logintest</function>
+    <function version="1.0">package.listLatestReleases</function>
+    <function version="1.0">package.listAll</function>
+    <function version="1.0">package.info</function>
+    <function version="1.0">package.getDownloadURL</function>
+    <function version="1.0">package.getDepDownloadURL</function>
+    <function version="1.0">package.search</function>
+    <function version="1.0">channel.listAll</function>
+   </xmlrpc>
+   <rest>
+    <baseurl type="REST1.0">http://pecl.php.net/rest/</baseurl>
+    <baseurl type="REST1.1">http://pecl.php.net/rest/</baseurl>
+   </rest>
+  </primary>
+ </servers>
+</channel>
diff --git a/lib/.xmlregistry/channels/channelalias-__uri.txt b/lib/.xmlregistry/channels/channelalias-__uri.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9e550d0400c45929b0ab93c775a8a3f04c66f6b9
--- /dev/null
+++ b/lib/.xmlregistry/channels/channelalias-__uri.txt
@@ -0,0 +1 @@
+__uri
\ No newline at end of file
diff --git a/lib/.xmlregistry/channels/channelalias-pear.txt b/lib/.xmlregistry/channels/channelalias-pear.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f4730b991be41ec5fec3805cd68ce31a53963ca5
--- /dev/null
+++ b/lib/.xmlregistry/channels/channelalias-pear.txt
@@ -0,0 +1 @@
+pear.php.net
\ No newline at end of file
diff --git a/lib/.xmlregistry/channels/channelalias-pear2.txt b/lib/.xmlregistry/channels/channelalias-pear2.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7911749f249888395a8754f5cb8ec2880c4039cb
--- /dev/null
+++ b/lib/.xmlregistry/channels/channelalias-pear2.txt
@@ -0,0 +1 @@
+pear2.php.net
\ No newline at end of file
diff --git a/lib/.xmlregistry/channels/channelalias-pearhub.txt b/lib/.xmlregistry/channels/channelalias-pearhub.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6203fad9defa2feb5581a4877298d475e30537e6
--- /dev/null
+++ b/lib/.xmlregistry/channels/channelalias-pearhub.txt
@@ -0,0 +1 @@
+pearhub.org
\ No newline at end of file
diff --git a/lib/.xmlregistry/channels/channelalias-pecl.txt b/lib/.xmlregistry/channels/channelalias-pecl.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2de48f1b005bd518dbb92e2b68df9d49431398d7
--- /dev/null
+++ b/lib/.xmlregistry/channels/channelalias-pecl.txt
@@ -0,0 +1 @@
+pecl.php.net
\ No newline at end of file
diff --git a/lib/.xmlregistry/channels/channelalias-phpdocs.txt b/lib/.xmlregistry/channels/channelalias-phpdocs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1e733d9cdf2d0df16d75b2d3e558d8d290158e9a
--- /dev/null
+++ b/lib/.xmlregistry/channels/channelalias-phpdocs.txt
@@ -0,0 +1 @@
+doc.php.net
\ No newline at end of file
diff --git a/lib/.xmlregistry/packages/pear.php.net/HTTP_Request2/0.5.1-info.xml b/lib/.xmlregistry/packages/pear.php.net/HTTP_Request2/0.5.1-info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..d96e7095d2b7d4a3073a3b816c7ba224b206513e
--- /dev/null
+++ b/lib/.xmlregistry/packages/pear.php.net/HTTP_Request2/0.5.1-info.xml
@@ -0,0 +1,279 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+ <name>HTTP_Request2</name>
+ <channel>pear.php.net</channel>
+ <extends>HTTP_Request</extends>
+ <summary>Provides an easy way to perform HTTP requests.</summary>
+ <description>PHP5 rewrite of HTTP_Request package. Provides cleaner API and pluggable
+Adapters. Currently available are:
+  * Socket adapter, based on old HTTP_Request code,
+  * Curl adapter, wraps around PHP's cURL extension,
+  * Mock adapter, to use for testing packages dependent on HTTP_Request2.
+Supports POST requests with data and file uploads, basic and digest 
+authentication, cookies, proxies, gzip and deflate encodings, redirects,
+monitoring the request progress with Observers...</description>
+ <lead>
+  <name>Alexey Borzov</name>
+  <user>avb</user>
+  <email>avb@php.net</email>
+  <active>yes</active>
+ </lead>
+ <date>2010-01-21</date>
+ <time>08:49:26</time>
+ <version>
+  <release>0.5.1</release>
+  <api>0.5.0</api>
+ </version>
+ <stability>
+  <release>alpha</release>
+  <api>alpha</api>
+ </stability>
+ <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+ <notes>
+* Content-Type request header is no longer removed for POST and PUT requests
+  with empty request body (request #16799).
+* CURLOPT_NOBODY option is now set when doing HEAD requests with Curl adapter.
+ </notes>
+ <contents>
+  <dir name="/">
+   <file baseinstalldir="HTTP" md5sum="1fb55dfe18831f8fe6280280e72ad216" name="tests/_files/response_headers" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="722328bfe89a9c9f7de5a020ed2c4589" name="tests/_files/response_gzip_broken" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="c36530c79c044fde1745b244c38d381f" name="tests/_files/response_gzip" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="12d80db889f528922a31b5c03f693647" name="tests/_files/response_deflate" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="d1d2beb78782f56e8611100a009fb1f6" name="tests/_files/response_cookies" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="120ea8a25e5d487bf68b5f7096440019" name="tests/_files/plaintext.txt" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="fc94fb0c3ed8a8f909dbc7630a0987ff" name="tests/_files/empty.gif" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="22d7f11b85dd00bd8919a4226a5a0388" name="tests/_files/bug_15305" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="96e929eb014758b7b5712be5c45110b9" name="tests/Request2Test.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="7ae3f0ef61d832d166671b044191efbf" name="tests/Request2/ResponseTest.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="ec1c8a2a8ab448183ed831ebd1c20518" name="tests/Request2/MultipartBodyTest.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="3f9867c9347815f15eac65c87b5c55e3" name="tests/Request2/AllTests.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="fb8e45c13ca3fb1c8594e22df9ab90d2" name="tests/Request2/Adapter/MockTest.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="d389619e0dad1f891a8b18117177496f" name="tests/Request2/Adapter/AllTests.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="f8691b81e639395523bc828b569fa716" name="tests/ObserverTest.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="f24af905aa262adefa4f692e2fe2a167" name="tests/AllTests.php" role="test"/>
+   <file baseinstalldir="HTTP" md5sum="07bfa5cff153994f244ceee9bd77de2a" name="Request2/Response.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="10b4d5d88f298a4e2fe7df08e5a218bb" name="Request2/Observer/Log.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="20b26f829852793a0e545c5df648a9b6" name="Request2/MultipartBody.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="a0b53a6710a47bc718dabc0edef36555" name="Request2/Exception.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="496dda12e9ca16561d9237651b1065e3" name="Request2/Adapter/Socket.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="ee8a541eb43be5cd04fd392770f90548" name="Request2/Adapter/Mock.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="90c77d4b4425b4bb63cf6ee921e5e3d1" name="Request2/Adapter/Curl.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="043e3d6edeeec91f7d21bf825090c2dd" name="Request2/Adapter.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="588ec605871ca474a8bcc3bf8f8035fc" name="Request2.php" role="php">
+    <tasks:replace from="@package_version@" to="version" type="package-info"/>
+   </file>
+   <file baseinstalldir="HTTP" md5sum="7e6017dfdf042dbd443ce6c8c024f40d" name="docs/examples/upload-rapidshare.php" role="doc"/>
+  </dir>
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.1.4</min>
+   </php>
+   <pearinstaller>
+    <min>1.5.4</min>
+   </pearinstaller>
+   <package>
+    <name>Net_URL2</name>
+    <channel>pear.php.net</channel>
+    <min>0.2.0</min>
+   </package>
+  </required>
+  <optional>
+   <extension>
+    <name>curl</name>
+   </extension>
+   <extension>
+    <name>fileinfo</name>
+   </extension>
+   <extension>
+    <name>zlib</name>
+   </extension>
+   <extension>
+    <name>openssl</name>
+   </extension>
+  </optional>
+ </dependencies>
+ <phprelease>
+  <changelog>
+   <release>
+    <version>
+     <release>0.5.0</release>
+     <api>0.5.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2009-11-18</date>
+    <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+    <notes>
+* Redirect support added, new configuration parameters 'follow_redirects',
+  'max_redirects' and 'strict_redirects' available
+
+* Implemented workaround for PHP bug #47204, Curl Adapter can now handle
+  Digest authentication and redirects when doing POST requests, unfortunately
+  this requires loading the entire request body into memory.
+* Config parameter 'use_brackets' is propagated to created instances of Net_URL2
+* Prevent memory leaks due to circular references (request #16646)
+* Fixed a misleading error message when timing out due to default_socket_timeout
+* HTTP_Request2::setBody() can now accept an instance of HTTP_Request2_MultipartBody
+  without trying to convert it to string
+* Calling HTTP_Request2::setBody() now clears post parameters and uploads
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.4.1</release>
+     <api>0.4.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2009-09-14</date>
+    <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+    <notes>
+* Decoding of gzipped responses failed if mbstring.func_overload was enabled
+  (bug #16555)
+* Changed boundary generation in multipart bodies to work correctly with
+  rapidshare.com, added first usage example: file uploading to rapidshare.com
+* Added forgotten optional dependency on OpenSSL PHP extension
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.4.0</release>
+     <api>0.4.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2009-05-03</date>
+    <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+    <notes>
+* Added 'store_body' config parameter, if set to false it will prevent storing
+  the response body in Response object (request #15881)
+* HTTP_Request2::setHeader() method now works as documented, setHeader('name') 
+  will remove the 'name' header, while setHeader('name', '') will set 'name'
+  header to empty value (bug #15937)
+* Custom 'Host' header will not be overwritten by generated one (bug #16146)
+* When trying to reuse the connected socket in Socket adapter, make sure that
+  it is still connected (bug #16149)
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.3.0</release>
+     <api>0.3.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2009-01-28</date>
+    <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+    <notes>
+API changes:
+ * Removed HTTP_Request2::getConfigValue() method
+
+Feature additions:
+ * Added digest authentication (RFC 2617) support to Socket adapter. Thanks
+   to Tom Snyder (tomsn at inetoffice dot com) who sent me a prototype
+   implementation for HTTP_Request a couple of years ago.
+ * Added HTTPS proxy support to Socket adapter, this works through CONNECT
+   request described in RFC 2817.
+ * Mock adapter can now throw an Exception instead of returning a response
+   if Exception object is added via its addResponse() method (request #15629)
+
+Other changes and fixes:
+ * Support RFC 3986 by not encoding '~' in POST body (request #15368)
+ * Prevent an error with particular versions of PHP and Curl (bug #15617)
+ * Regular expressions used in HTTP_Request2 are now class constants 
+   (request #15630)
+ * Curl adapter now throws an exception in case of malformed (non-HTTP) 
+   response rather than dies with a fatal error (bug #15716)
+ * Curl handle wasn't closed in Curl adapter in case of error (bug #15721)
+ * Curl adapter sent an extra 'sentHeaders' event and returned bogus 
+   response status when server returned 100-Continue response (bug #15785)
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.2.0</release>
+     <api>0.2.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2009-01-07</date>
+    <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+    <notes>
+API changes:
+ * HTTP_Request2::getConfigValue() is deprecated and will be removed in next 
+   release. Use HTTP_Request2::getConfig().
+ * Changed HTTP_Request2::setConfig() to accept a pair of parameter name and
+   parameter value in addition to array('parameter name' =&gt; 'value')
+ * Added HTTP_Request2::getConfig() method that can return a single 
+   configuration parameter or the whole configuration array
+
+Other additions and changes:
+ * Added a debug Observer that can log request progress to a file or an 
+   instance of PEAR::Log (thanks to David Jean Louis, request #15424)
+ * Added a new 'timeout' parameter that limits total number of seconds
+   a request can take (see requests #5735 and #8964)
+ * Added various SSL protocol options: 'ssl_verify_peer', 'ssl_verify_host',
+   'ssl_cafile', 'ssl_capath', 'ssl_local_cert', 'ssl_passphrase'. Note that
+   'ssl_verify_host' option behaves differently in Socket and Curl Adapters:
+   http://bugs.php.net/bug.php?id=47030
+
+Fixes:
+ * Fixed 'data error' when processing response encoded by 'deflate'
+   encoding (bug #15305)
+ * Curl Adapter now passes full request headers in 'sentHeaders' event
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.1.0</release>
+     <api>0.1.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2008-11-17</date>
+    <license uri="http://opensource.org/licenses/bsd-license.php">BSD License</license>
+    <notes>
+Initial release. The features supported are mostly the same as those of
+HTTP_Request, with the following additional feature requests implemented:
+  * cURL extension support (request #5463)
+  * It is now possible to monitor the file upload progress with Observers
+	(request #7630)
+  * Added 'sentHeaders' notification providing the request headers to the
+    Observers (request #7633)
+  * Added support for 'deflate' encoding (request #11246)
+   </notes>
+   </release>
+  </changelog>
+ </phprelease>
+</package>
diff --git a/lib/.xmlregistry/packages/pear.php.net/Net_URL2/0.3.0-info.xml b/lib/.xmlregistry/packages/pear.php.net/Net_URL2/0.3.0-info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..94e2687faf6a8f4fbf5ee81ad538e9d47770ac91
--- /dev/null
+++ b/lib/.xmlregistry/packages/pear.php.net/Net_URL2/0.3.0-info.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.5.1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.0 http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Net_URL2</name>
+ <channel>pear.php.net</channel>
+ <summary>Class for parsing and handling URL.</summary>
+ <description>Provides parsing of URLs into their constituent parts (scheme, host, path etc.), URL generation, and resolving of relative URLs.</description>
+ <lead>
+  <name>David Coallier</name>
+  <user>davidc</user>
+  <email>davidc@php.net</email>
+  <active>yes</active>
+ </lead>
+ <lead>
+  <name>Christian Schmidt</name>
+  <user>schmidt</user>
+  <email>schmidt@php.net</email>
+  <active>yes</active>
+ </lead>
+ <date>2010-01-21</date>
+ <time>08:49:21</time>
+ <version>
+  <release>0.3.0</release>
+  <api>0.3.0</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+ <notes>* Fixed #14399 (Errors in URL parsing (items #1 and #3))
+* Fixed #14735 (Encode query string values)
+* Fixed #15546 (Add adding __toString())
+* Fixed #15367 (Use RFC 3986-compliant version of rawurlencode() in PHP &lt; 5.2)
+* Fixed #14289 (Add __get() and __set())</notes>
+ <contents>
+  <dir name="/">
+   <file baseinstalldir="Net" name="URL2.php" role="php" md5sum="8a713828923fe747e969966d1d297087"/>
+   <file baseinstalldir="Net" name="docs/example.php" role="doc" md5sum="587a224d39fbffa47cacb9fb67b51da1"/>
+   <file baseinstalldir="Net" name="docs/6470.php" role="doc" md5sum="ea8b73061588566519fd0e55a230f2a6"/>
+  </dir>
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.4.0b1</min>
+   </pearinstaller>
+  </required>
+ </dependencies>
+ <phprelease>
+  <changelog>
+   <release>
+    <version>
+     <release>0.2.0</release>
+     <api>0.2.0</api>
+    </version>
+    <stability>
+     <release>beta</release>
+     <api>beta</api>
+    </stability>
+    <date>2008-06-18</date>
+    <license>BSD</license>
+    <notes>Major rewrite to comply with RFC3986 (bug 11574).
+   Much better support for resolving relative URLs.
+   WARNING: Method and property names has changed to reflect the terminology used in the RFC - THIS RELEASE IS NOT BACKWARDS COMPATIBLE WITH VERSION 0.1.0.</notes>
+   </release>
+   <release>
+    <version>
+     <release>0.1.0</release>
+     <api>0.1.0</api>
+    </version>
+    <stability>
+     <release>beta</release>
+     <api>beta</api>
+    </stability>
+    <date>2007-05-08</date>
+    <license>BSD</license>
+    <notes>Convert to PHP5 only. PHP4 users should continue with version 1.0.15</notes>
+   </release>
+  </changelog>
+ </phprelease>
+</package>
diff --git a/lib/.xmlregistry/packages/pear.php.net/Services_W3C_HTMLValidator/1.0.0RC2-info.xml b/lib/.xmlregistry/packages/pear.php.net/Services_W3C_HTMLValidator/1.0.0RC2-info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e569295045a030a44f755cc7740ff480f08a43f8
--- /dev/null
+++ b/lib/.xmlregistry/packages/pear.php.net/Services_W3C_HTMLValidator/1.0.0RC2-info.xml
@@ -0,0 +1,193 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.8.0alpha1" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0     http://pear.php.net/dtd/tasks-1.0.xsd     http://pear.php.net/dtd/package-2.0     http://pear.php.net/dtd/package-2.0.xsd">
+ <name>Services_W3C_HTMLValidator</name>
+ <channel>pear.php.net</channel>
+ <summary>An Object Oriented Interface to the W3C HTML Validator service.</summary>
+ <description>This package provides an object oriented interface to the API 
+of the W3 HTML Validator application (http://validator.w3.org/).
+With this package you can connect to a running instance of the validator and 
+retrieve the validation results (true|false) as well as the errors and warnings 
+for a web page.
+
+By using the SOAP 1.2 output format from the validator, you are returned simple 
+objects containing all the information from the validator. With this package it is 
+trivial to build a validation system for web publishing.</description>
+ <lead>
+  <name>Brett Bieber</name>
+  <user>saltybeagle</user>
+  <email>brett.bieber@gmail.com</email>
+  <active>yes</active>
+ </lead>
+ <date>2010-01-21</date>
+ <time>08:49:33</time>
+ <version>
+  <release>1.0.0RC2</release>
+  <api>1.0.0RC2</api>
+ </version>
+ <stability>
+  <release>beta</release>
+  <api>beta</api>
+ </stability>
+ <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+ <notes>
+* Switch to HTTP_Request2
+ </notes>
+ <contents>
+  <dir name="/">
+   <file baseinstalldir="Services/W3C" md5sum="45c6625f05fe3a6473a5699277d30ee5" name="tests/Services/W3C/HTMLValidatorTest.php" role="test"/>
+   <file baseinstalldir="Services/W3C" md5sum="a6c033d1b4358eb80358db58939f6f58" name="tests/AllTests.php" role="test"/>
+   <file baseinstalldir="Services/W3C" md5sum="a6cc77ff4a49c95d05d358648e976e82" name="HTMLValidator/Warning.php" role="php"/>
+   <file baseinstalldir="Services/W3C" md5sum="9190cab9f04fb512415377c3f4f15d1c" name="HTMLValidator/Response.php" role="php"/>
+   <file baseinstalldir="Services/W3C" md5sum="f48d22f39da255d34c467c58b5e86a53" name="HTMLValidator/Message.php" role="php"/>
+   <file baseinstalldir="Services/W3C" md5sum="9edcf30a27e376a3260f674cf829ccd5" name="HTMLValidator/Error.php" role="php"/>
+   <file baseinstalldir="Services/W3C" md5sum="fece8e09b2add1806b97d6b9711c3eb3" name="HTMLValidator.php" role="php"/>
+   <file baseinstalldir="Services/W3C" md5sum="dc6608c7334e527a4d641cfa580869dc" name="docs/examples/validate_responsexml.php" role="doc"/>
+   <file baseinstalldir="Services/W3C" md5sum="aefc4bfdaf6fdb3a40a6a1cbf41a2972" name="docs/examples/validate_fragment.php" role="doc"/>
+   <file baseinstalldir="Services/W3C" md5sum="44a9e0b1d85afc5b965034419dd627fc" name="docs/examples/validate_byuri.php" role="doc"/>
+   <file baseinstalldir="Services/W3C" md5sum="85376a75fda57cbb7236fb777192a832" name="docs/examples/validate_byfile.php" role="doc"/>
+   <file baseinstalldir="Services/W3C" md5sum="d55c429e13bb7d0c0b66a0c675e3c3e8" name="docs/examples/PHPUnit/ValidationSuite.php" role="doc"/>
+   <file baseinstalldir="Services/W3C" md5sum="63c4bda34c6babc7feb9fcb64e1e63ff" name="docs/examples/PHPUnit/URIValidationTest.php" role="doc"/>
+   <file baseinstalldir="Services/W3C" md5sum="f31575f3932b5c0cbb9ceb6c8eca7a7b" name="docs/examples/example.html" role="doc"/>
+  </dir>
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.0.0</min>
+   </php>
+   <pearinstaller>
+    <min>1.4.3</min>
+   </pearinstaller>
+   <package>
+    <name>HTTP_Request2</name>
+    <channel>pear.php.net</channel>
+    <min>0.2.0</min>
+   </package>
+  </required>
+ </dependencies>
+ <phprelease>
+  <changelog>
+   <release>
+    <version>
+     <release>0.0.1</release>
+     <api>0.0.1</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2006-11-28</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+First release offers basic functionality only validate($uri) method.
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.1.0</release>
+     <api>0.1.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2006-12-03</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+Added method to validate uploaded file validateFile();
+Add method to validate html fragment validateFragment();
+Added a method to the Response object isValid().
+Add example for validateFragment and validateFile.
+Fix phpdoc commenting and various CS fixes (thanks David Coallier).
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.1.5</release>
+     <api>0.1.5</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2006-12-05</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+Switched methods from private to protected to allow overriding.
+Move all variables which were int 0|1 to booleans (thanks Christian Weiske).
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.2.0</release>
+     <api>0.2.0</api>
+    </version>
+    <stability>
+     <release>alpha</release>
+     <api>alpha</api>
+    </stability>
+    <date>2007-06-07</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+Switch last remaining private member variable to protected (thanks Christian Weiske).
+Move examples into docs directory (thanks Mark Wiesemann)
+Change channel from pear.saltybeagle.com to pear.php.net
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>0.3.0</release>
+     <api>0.3.0</api>
+    </version>
+    <stability>
+     <release>beta</release>
+     <api>beta</api>
+    </stability>
+    <date>2007-06-14</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+New features - return error and warning source context, unique message id, and explanations for the error and warnings.
+http://www.w3.org/Bugs/Public/show_bug.cgi?id=4489
+
+These features will be released in the next version of the W3C Validator (version 0.8.0), until the next release users can test the output by assigning the $validator-&gt;validator_uri to the CVS version for the validator http://qa-dev.w3.org/wmvs/HEAD/check.
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>1.0.0RC1</release>
+     <api>1.0.0RC1</api>
+    </version>
+    <stability>
+     <release>beta</release>
+     <api>beta</api>
+    </stability>
+    <date>2007-07-25</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+New features - return error and warning source context, unique 
+message id, and explanations for the error and warnings.
+http://www.w3.org/Bugs/Public/show_bug.cgi?id=4489
+
+These features are included in the latest version of the W3C Validator (version 
+0.8.0).
+Unit Tests now included, additional documentation and coding standards fixes.
+   </notes>
+   </release>
+   <release>
+    <version>
+     <release>1.0.0RC2</release>
+     <api>1.0.0RC2</api>
+    </version>
+    <stability>
+     <release>beta</release>
+     <api>beta</api>
+    </stability>
+    <date>2009-01-20</date>
+    <license uri="http://www.opensource.org/licenses/bsd-license.php">BSD</license>
+    <notes>
+* Switch to HTTP_Request2
+   </notes>
+   </release>
+  </changelog>
+ </phprelease>
+</package>
diff --git a/lib/.xmlregistry/packages/pearhub.org/Spider/0.1.0-info.xml b/lib/.xmlregistry/packages/pearhub.org/Spider/0.1.0-info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3f2d07e20f1f90538602155f5a45f48943b9da5c
--- /dev/null
+++ b/lib/.xmlregistry/packages/pearhub.org/Spider/0.1.0-info.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.1" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.1" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0     http://pear.php.net/dtd/tasks-1.0.xsd     http://pear.php.net/dtd/package-2.1     http://pear.php.net/dtd/package-2.1.xsd">
+ <name>Spider</name>
+ <channel>pearhub.org</channel>
+ <summary>Package Spider summary.
+</summary>
+ <description>
+Package detailed description here (found in README)
+</description>
+ <lead>
+  <name>Brett Bieber</name>
+  <user>saltybeagle</user>
+  <email>saltybeagle@php.net</email>
+  <active>yes</active>
+ </lead>
+ <date>2010-01-21</date>
+ <time>08:57:07</time>
+ <version>
+  <release>0.1.0</release>
+  <api>0.1.0</api>
+ </version>
+ <stability>
+  <release>alpha</release>
+  <api>alpha</api>
+ </stability>
+ <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license>
+ <notes>Package PEAR2_Spider release notes for version 0.1.0.
+
+Package PEAR2_Spider API release notes for version 0.1.0.</notes>
+ <contents>
+  <dir name="/">
+   <file role="php" name="src/Spider/UriIterator.php" md5sum="8f47d38de045dfe754a9c197b0845af2"/>
+   <file role="php" name="src/Spider/UriFilterInterface.php" md5sum="dcace02f51e3c1f12005b3765a97ccc0"/>
+   <file role="php" name="src/Spider/TidyParser.php" md5sum="989764252f60b4d0c0a4a4cc4c296254"/>
+   <file role="php" name="src/Spider/StyleSheetLogger.php" md5sum="4e323ae9f3129deb4aa519129055d9af"/>
+   <file role="php" name="src/Spider/ParserInterface.php" md5sum="140add631a24ee19cc974e5d190fb526"/>
+   <file role="php" name="src/Spider/Parser.php" md5sum="ce6879e89e1f082ff44821ceb4c429bf"/>
+   <file role="php" name="src/Spider/PageLogger.php" md5sum="f05cc3d2e54fa03e143546ec0a84eb73"/>
+   <file role="php" name="src/Spider/MailtoFilter.php" md5sum="3c116ba7fc28fc47fa4a523212f0363a"/>
+   <file role="php" name="src/Spider/LoggerAbstract.php" md5sum="c26904afb26b2ee2866c18f130aa0afb"/>
+   <file role="php" name="src/Spider/Logger.php" md5sum="e40d93bb8399c2f7dd1d9ccc7d654516"/>
+   <file role="php" name="src/Spider/JavaScriptLogger.php" md5sum="2a17632c40359e9215d64426d89abd6c"/>
+   <file role="php" name="src/Spider/Downloader.php" md5sum="8388b6feeb60b8c433c7151547106804"/>
+   <file role="php" name="src/Spider/DbLogger.php" md5sum="985aa51e4c93e76b6848c1e7060c1d1a"/>
+   <file role="php" name="src/Spider/AnchorFilter.php" md5sum="033b1ae3ea8fee40076f728b4a95fd2b"/>
+   <file role="php" name="src/Spider.php" md5sum="219c9dd9003e1f06a3843da1777f6aa9"/>
+   <file role="doc" name="examples/results.db" md5sum="8967b7f9bf5fedf351c02cd76e6e0e8b"/>
+   <file role="doc" name="examples/batch_validator.php" md5sum="87bdb9b37fc5ff22e3d32ee2f2147093"/>
+   <file role="doc" name="examples/basic.php" md5sum="ae8b39ff256de45d662b9114cbf2d9df"/>
+  </dir>
+ </contents>
+ <dependencies>
+  <required>
+   <php>
+    <min>5.2.0</min>
+   </php>
+   <pearinstaller>
+    <min>2.0.0a1</min>
+   </pearinstaller>
+  </required>
+ </dependencies>
+ <phprelease/>
+</package>
diff --git a/lib/docs/HTTP_Request2/HTTP/docs/examples/upload-rapidshare.php b/lib/docs/HTTP_Request2/HTTP/docs/examples/upload-rapidshare.php
new file mode 100644
index 0000000000000000000000000000000000000000..9773a94c48fed08bb23ad5beffda73d05ff5cd32
--- /dev/null
+++ b/lib/docs/HTTP_Request2/HTTP/docs/examples/upload-rapidshare.php
@@ -0,0 +1,60 @@
+<?php
+/**
+ * Usage example for HTTP_Request2 package: uploading a file to rapidshare.com
+ *
+ * Inspired by Perl usage example: http://images.rapidshare.com/software/rsapi.pl
+ * Rapidshare API description: http://rapidshare.com/dev.html
+ *
+ * $Id: upload-rapidshare.php 287307 2009-08-14 15:40:22Z avb $
+ */
+
+require_once 'HTTP/Request2.php';
+
+// You'll probably want to change this
+$filename = '/etc/passwd';
+
+try {
+    // First step: get an available upload server
+    $request = new HTTP_Request2(
+        'http://rapidshare.com/cgi-bin/rsapi.cgi?sub=nextuploadserver_v1'
+    );
+    $server  = $request->send()->getBody();
+    if (!preg_match('/^(\\d+)$/', $server)) {
+        throw new Exception("Invalid upload server: {$server}");
+    }
+
+    // Calculate file hash, we'll use it later to check upload
+    if (false === ($hash = @md5_file($filename))) {
+        throw new Exception("Cannot calculate MD5 hash of '{$filename}'");
+    }
+
+    // Second step: upload a file to the available server
+    $uploader = new HTTP_Request2(
+        "http://rs{$server}l3.rapidshare.com/cgi-bin/upload.cgi",
+        HTTP_Request2::METHOD_POST
+    );
+    // Adding the file
+    $uploader->addUpload('filecontent', $filename);
+    // This will tell server to return program-friendly output
+    $uploader->addPostParameter('rsapi_v1', '1');
+
+    $response = $uploader->send()->getBody();
+    if (!preg_match_all('/^(File[^=]+)=(.+)$/m', $response, $m, PREG_SET_ORDER)) {
+        throw new Exception("Invalid response: {$response}");
+    }
+    $rspAry = array();
+    foreach ($m as $item) {
+        $rspAry[$item[1]] = $item[2];
+    }
+    // Check that uploaded file has the same hash
+    if (empty($rspAry['File1.4'])) {
+        throw new Exception("MD5 hash data not found in response");
+    } elseif ($hash != strtolower($rspAry['File1.4'])) {
+        throw new Exception("Upload failed, local MD5 is {$hash}, uploaded MD5 is {$rspAry['File1.4']}");
+    }
+    echo "Upload succeeded\nDownload link: {$rspAry['File1.1']}\nDelete link: {$rspAry['File1.2']}\n";
+
+} catch (Exception $e) {
+    echo "Error: " . $e->getMessage();
+}
+?>
diff --git a/lib/docs/Net_URL2/Net/docs/6470.php b/lib/docs/Net_URL2/Net/docs/6470.php
new file mode 100644
index 0000000000000000000000000000000000000000..104de8960852ebff997e4d9d17a209c35c19cb5d
--- /dev/null
+++ b/lib/docs/Net_URL2/Net/docs/6470.php
@@ -0,0 +1,75 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003, Richard Heyes                                     |
+// | All rights reserved.                                                  |
+// |                                                                       |
+// | Redistribution and use in source and binary forms, with or without    |
+// | modification, are permitted provided that the following conditions    |
+// | are met:                                                              |
+// |                                                                       |
+// | o Redistributions of source code must retain the above copyright      |
+// |   notice, this list of conditions and the following disclaimer.       |
+// | o Redistributions in binary form must reproduce the above copyright   |
+// |   notice, this list of conditions and the following disclaimer in the |
+// |   documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote      |
+// |   products derived from this software without specific prior written  |
+// |   permission.                                                         |
+// |                                                                       |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
+// |                                                                       |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard at php net>                            |
+// +-----------------------------------------------------------------------+
+// $Id: 6470.php 235190 2007-05-08 00:04:54Z davidc $
+/**
+* This example will decode the url given and display its
+* constituent parts.
+*/
+    error_reporting(E_ALL | E_STRICT);
+
+    require_once 'Net/URL2.php';
+
+    //$url = &new Net_URL2('https://www.example.com/foo/bar/index.php?foo=bar');
+    Net_URL2::setOption('encode_query_keys', true);
+    $url = new Net_URL2;
+    
+?>
+<html>
+<body>
+
+<pre>
+Protocol...: <?php echo $url->protocol; ?>
+
+Username...: <?php echo $url->user; ?>
+
+Password...: <?php echo $url->pass; ?>
+
+Server.....: <?php echo $url->host; ?>
+
+Port.......: <?php $url->port; ?>
+
+File/path..: <?php $url->path; ?>
+
+Querystring: <?php print_r($url->querystring); ?>
+
+Anchor.....: <?php echo $url->anchor;?>
+
+Full URL...: <?php echo $url->getUrl(); ?>
+
+
+Resolve path (/.././/foo/bar/joe/./././../jabba): <b><?php Net_URL2::resolvePath('/.././/foo/bar/joe/./././../jabba'); ?></b>
+</pre>
+
+</body>
+</html>
diff --git a/lib/docs/Net_URL2/Net/docs/example.php b/lib/docs/Net_URL2/Net/docs/example.php
new file mode 100644
index 0000000000000000000000000000000000000000..d2725ba5aaa8d854de39d43e1e44fbbd6783c1a9
--- /dev/null
+++ b/lib/docs/Net_URL2/Net/docs/example.php
@@ -0,0 +1,74 @@
+<?php
+// +-----------------------------------------------------------------------+
+// | Copyright (c) 2002-2003, Richard Heyes                                     |
+// | All rights reserved.                                                  |
+// |                                                                       |
+// | Redistribution and use in source and binary forms, with or without    |
+// | modification, are permitted provided that the following conditions    |
+// | are met:                                                              |
+// |                                                                       |
+// | o Redistributions of source code must retain the above copyright      |
+// |   notice, this list of conditions and the following disclaimer.       |
+// | o Redistributions in binary form must reproduce the above copyright   |
+// |   notice, this list of conditions and the following disclaimer in the |
+// |   documentation and/or other materials provided with the distribution.|
+// | o The names of the authors may not be used to endorse or promote      |
+// |   products derived from this software without specific prior written  |
+// |   permission.                                                         |
+// |                                                                       |
+// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS   |
+// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT     |
+// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT  |
+// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT      |
+// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT   |
+// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  |
+// |                                                                       |
+// +-----------------------------------------------------------------------+
+// | Author: Richard Heyes <richard at php net>                            |
+// +-----------------------------------------------------------------------+
+// $Id: example.php 235190 2007-05-08 00:04:54Z davidc $
+/**
+* This example will decode the url given and display its
+* constituent parts.
+*/
+    error_reporting(E_ALL | E_STRICT);
+
+    //include('../URL2.php');
+    include('Net/URL2.php');
+
+    //$url = &new Net_URL2('https://www.example.com/foo/bar/index.php?foo=bar');
+    $url = new Net_URL2('https://example.com/pls/portal30/PORTAL30.wwpob_page.changetabs?p_back_url=http%3A%2F%2Fexample.com%2Fservlet%2Fpage%3F_pageid%3D360%2C366%2C368%2C382%26_dad%3Dportal30%26_schema%3DPORTAL30&foo=bar');
+?>
+<html>
+<body>
+
+<pre>
+Protocol...: <?php echo $url->protocol; ?>
+
+Username...: <?php echo $url->user; ?>
+
+Password...: <?php echo $url->pass; ?>
+
+Server.....: <?php echo $url->host; ?>
+
+Port.......: <?php $url->port; ?>
+
+File/path..: <?php $url->path; ?>
+
+Querystring: <?php print_r($url->querystring); ?>
+
+Anchor.....: <?php echo $url->anchor;?>
+
+Full URL...: <?php echo $url->getUrl(); ?>
+
+
+Resolve path (/.././/foo/bar/joe/./././../jabba): <b><?php Net_URL2::resolvePath('/.././/foo/bar/joe/./././../jabba'); ?></b>
+</pre>
+
+</body>
+</html>
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/URIValidationTest.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/URIValidationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..25f65cbe294d59094738ba06f9ffec2b5b1e83cc
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/URIValidationTest.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * This file tests the validation of the web accessible URIs.
+ */
+
+require_once 'Services/W3C/HTMLValidator.php';
+require_once 'PHPUnit/Framework.php';
+ 
+// Call Services_W3C_HTMLValidatorTest::main() if executed directly.
+if (!defined("PHPUnit_MAIN_METHOD")) {
+    define("PHPUnit_MAIN_METHOD", "URIValidationTest::main");
+}
+
+class URIValidationTest extends PHPUnit_Framework_TestCase
+{
+    public $uri = 'http://www.unl.edu/';
+    
+    protected $validator;
+    
+	/**
+     * Runs the test methods of this class.
+     *
+     * @access public
+     * @static
+     * @return void
+     */
+    public static function main()
+    {
+        include_once "PHPUnit/TextUI/TestRunner.php";
+
+        $suite  = new PHPUnit_Framework_TestSuite("URIValidationTest");
+        $result = PHPUnit_TextUI_TestRunner::run($suite);
+    }
+    
+    
+    public function setUp()
+    {
+        $this->validator = new Services_W3C_HTMLValidator();
+    }
+    
+    public function testURI()
+    {
+        $r = $this->validator->validate($this->uri);
+        $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response');
+        if (!$r->isValid()) {
+            $message = $this->constructErrorMessage($r);
+        } else {
+            $message = $r->uri.' is Valid';
+        }
+        $this->assertTrue($r->isValid(), $message);
+    }
+    
+    public function testFile()
+    {
+        $r = $this->validator->validate($this->uri);
+        $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response');
+        if (!$r->isValid()) {
+            $message = $this->constructErrorMessage($r);
+        } else {
+            $message = $r->uri.' is Valid';
+        }
+        $this->assertTrue($r->isValid(), $message);
+    }
+    
+    protected function constructErrorMessage(&$r)
+    {
+        $message = $r->uri.' is NOT VALID: '.count($r->errors).' Errors -'.PHP_EOL;
+        foreach ($r->errors as $error) {
+            $message .= ' - '.$error->message.PHP_EOL;
+        }
+        return $message;
+    }
+}
+
+// Call Services_W3C_HTMLValidatorTest::main() if file is executed directly.
+if (PHPUnit_MAIN_METHOD == "URIValidationTest::main") {
+    URIValidationTest::main();
+}
+?>
\ No newline at end of file
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/ValidationSuite.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/ValidationSuite.php
new file mode 100644
index 0000000000000000000000000000000000000000..cfeeaee834a5aef0dd7304e47750ae75ae3f8505
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/PHPUnit/ValidationSuite.php
@@ -0,0 +1,57 @@
+<?php
+
+if (!defined("PHPUnit_MAIN_METHOD")) {
+    define("PHPUnit_MAIN_METHOD", "ValidationTestSuite::main");
+}
+
+
+require_once 'PHPUnit/Framework.php';
+require_once "PHPUnit/Framework/TestCase.php";
+require_once "PHPUnit/Framework/TestSuite.php";
+require_once dirname(__FILE__).DIRECTORY_SEPARATOR.'URIValidationTest.php';
+
+class ValidationTestSuite extends PHPUnit_Framework_TestSuite
+{
+    function __construct($name)
+    {
+        
+    }
+    
+    /**
+     * Runs the test suite.
+     *
+     * @return unknown
+     */
+    public static function main()
+    {
+        include_once "PHPUnit/TextUI/TestRunner.php";
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    /**
+     * Adds the Services_W3C_HTMLValidatorTest suite.
+     *
+     * @return $suite
+     */
+    public static function suite()
+    {
+        $uris_to_check = array('http://www.unl.edu/',
+                               'http://events.unl.edu/',
+                               'http://www.google.com/');        
+        $suite = new PHPUnit_Framework_TestSuite('ValidationTestSuite tests');
+        /** Add tests, if there is. */
+        foreach ($uris_to_check as $uri) {
+            $test      = new URIValidationTest('testURI');
+            $test->uri = $uri;
+            $suite->addTest($test);
+        }
+
+        return $suite;
+    }
+}
+
+// Call Services_W3C_HTMLValidatorTest::main() if file is executed directly.
+if (PHPUnit_MAIN_METHOD == "ValidationTestSuite::main") {
+    ValidationTestSuite::main();
+}
+?>
\ No newline at end of file
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/example.html b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/example.html
new file mode 100644
index 0000000000000000000000000000000000000000..152f21f449e60820ace637016e104827a924ee12
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/example.html
@@ -0,0 +1,344 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head><title>Documentation of the Programmatic Interface (API) to The W3C Markup Validation Service</title>
+
+
+  
+    
+    <link rev="made" href="mailto:www-validator@w3.org">
+    <link rev="start" href="http://validator.w3.org/" title="Home Page">
+    <style type="text/css" media="all">@import "../base.css";</style>
+    <meta name="keywords" content="HTML, HyperText Markup Language, Validation,
+      W3C Markup Validation Service">
+    <meta name="description" content="W3C's easy-to-use
+      HTML validation service, based on an SGML parser.">
+    <meta name="revision" content="$Id$"><!-- SSI Template Version: $Id$ --></head><body>
+    <div id="banner">
+      <h1 id="title">
+	<a href="http://www.w3.org/"><img alt="W3C" id="logo" src="api_files/w3c_home_nb" height="48"></a>
+        <a href="http://www.w3.org/QA/"><img src="api_files/qa-small.png" alt="QA"></a>
+        Markup Validation Service</h1>
+	<span id="versioninfo"><abbr title="version">v</abbr>0.7.4</span>
+    </div>
+    <ul class="navbar" id="menu">
+        <li><span class="hideme"><a href="#skip" accesskey="2" title="Skip past navigation to main part of page">Skip Navigation</a> |</span>
+        <a href="http://validator.w3.org/" accesskey="1" title="Go to the Home Page for The W3C Markup Validation Service"><strong>Home</strong></a></li>
+        <li><a href="http://validator.w3.org/about.html" title="Information About this Service">About...</a></li>
+        <li><a href="http://validator.w3.org/whatsnew.html" title="The changes made to this service recently">News</a></li>
+        <li><a href="http://validator.w3.org/docs/" accesskey="3" title="Documentation for this Service">Docs</a></li>
+	<li><a href="http://validator.w3.org/docs/help.html" title="Help and answers to frequently asked questions">Help&nbsp;&amp;&nbsp;<acronym title="Frequently Asked Questions">FAQ</acronym></a></li>
+        <li><a href="http://validator.w3.org/feedback.html" accesskey="4" title="How to provide feedback on this service">Feedback</a></li>
+    </ul>
+
+    <div id="main"><!-- This DIV encapsulates everything in this page - necessary for the positioning -->
+
+
+<div class="doc">
+<h2>Markup Validator Web Service API<br>
+SOAP 1.2 validation interface documentation</h2>
+
+<p>Interface applications with the Markup Validator through its <strong>experimental</strong> API. </p>
+<p><strong>Note</strong>: Please be considerate in using this shared, free resource. 
+	Consider <a href="http://validator.w3.org/docs/install.html">Installing your own instance of the validator</a>
+	for smooth and fast operation. Excessive use of the W3C Validation Service
+	will be blocked.</p>
+
+
+<h3 id="TableOfContents">Table of Contents</h3>
+
+    <div id="toc">
+      <ul>
+        <li><a href="#requestformat">Validation Request Format</a></li>
+        <li><a href="#soap12format">SOAP format description</a>
+         <ul>
+          <li><a href="#soap12_sample">sample SOAP 1.2 validation response</a></li>
+          <li><a href="#soap12response">SOAP1.2 response format reference</a></li>
+          <li><a href="#soap12message">SOAP1.2 atomic message (error or warning) format reference</a></li>
+         </ul>
+        </li>
+        <li><a href="#libs">Libraries</a></li>
+        <li><a href="#http_headers">Using HTTP headers to know validation results</a></li>
+      </ul>
+    </div>
+    
+    <p id="skip"></p>
+
+<h3 id="requestformat">Validation Request Format</h3>
+
+<p>Below is a table of the parameter you can use to send a query to the W3C Markup Validator.</p>
+<p>If you want to use W3c's public validation server, use the parameters below
+ in conjunction with the following base URI:<br>
+<kbd>http://validator.w3.org/check</kbd> <br>
+(replace with the address of your own server if you want to call a private instance of the validator)</p>
+
+<p><strong>Note</strong>: If you wish to call the validator programmatically for a batch of documents,
+please make sure that your script will <code>sleep</code> for <strong>at least 1 second</strong> 
+between requests. The Markup Validation service is a free, public service for all, your respect
+is appreciated. thanks.</p>
+
+<table class="refdoc">
+<tbody><tr>
+<th>Parameter</th><th>Description</th><th>Default value</th>
+</tr>
+<tr>
+  <th>uri</th>
+  <td>The <acronym title="Universal Resource Locator">URL</acronym> of the document to validate</td>
+  <td>None, but either this parameter, or <code>uploaded_file</code>, 
+  or <code>fragment</code> must be given.</td>
+</tr>
+<tr>
+  <th>uploaded_file</th>
+  <td>The document to validate, POSTed as multipart/form-data</td>
+  <td>None, but either this parameter, or <code>uri</code>,   
+  or <code>fragment</code> must be given.</td>
+</tr>
+<tr>
+  <th>fragment</th>
+  <td>The source of the document to validate. Full documents only. 
+  At the moment, will only work if data is sent with the UTF-8 encoding.</td>
+  <td>None, but either this parameter, or <code>uri</code>,
+  or <code>uploaded_file</code> must be given.</td>
+</tr>
+<tr>
+  <th>output</th>
+  <td>triggers the various outputs formats of the validator. If unset, the usual
+  Web format will be sent. If set to <code>soap12</code>, the SOAP1.2 interface will 
+  be triggered. See <a href="#soap12format">below for the SOAP 1.2 response format description</a>.</td>
+  <td>unset</td>
+</tr>
+<tr>
+  <th>charset</th>
+  <td>Character encoding override: 
+  Specify the character encoding to use when parsing the document. When used with
+  the auxiliary parameter <code>fbc</code> set to 1, the given encoding will only be used as 
+  a fallback value, in case the charset is absent or unrecognized. Note that this parameter
+  is ignored if validating a <code>fragment</code> with the direct input interface.</td>
+  <td>None, by default the validator detects the charset of the document automatically.</td>
+</tr>
+<tr>
+  <th>doctype</th>
+  <td>Document Type override:
+  Specify the Document Type (DOCTYPE) to use when parsing the document. When used 
+  with  the auxiliary parameter <code>fbd</code> set to 1, the given document type will only be used
+  as a fallback value, in case the document's DOCTYPE declaration is missing or unrecognized.</td>
+  <td>None, by default the validator detects the document type of the document automatically.</td>
+</tr>
+<tr>
+  <th>verbose</th>
+  <td>In the web interface, when set to 1, will make error messages, explanations 
+  and other diagnostics more verbose. In SOAP output, does not have any impact.</td>
+  <td>0 (unset)</td>
+</tr>
+<tr>
+  <th>ss</th>
+  <td> as <em>show source</em>. In the web interface, triggers the display of the source
+  after the validation results. In SOAP output, does not have any impact.</td>
+  <td>0 (unset)</td>
+</tr>
+<tr>
+  <th>outline</th>
+  <td>In the web interface, when set to 1, triggers the display of the document outline 
+  after the validation results. In SOAP output, does not have any impact.</td>  
+  <td>0 (unset)</td>
+</tr>
+</tbody></table>
+<h3 id="soap12format">SOAP format description</h3>
+
+<p>When called with parameter <code>output=soap12</code>, the validator will switch
+to its SOAP 1.2 interface (experimental for now). Below is a sample response, as well as
+a description of the most important elements of the response.</p>
+
+<h4 id="soap12_sample">sample SOAP 1.2 validation response</h4>
+<p>A SOAP response for the validation of a document (invalid) will look like this:</p>
+<pre style="font-size: smaller;"> 
+&lt;?xml version="1.0" encoding="UTF-8"?&gt;
+&lt;env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"&gt;
+&lt;env:Body&gt;
+&lt;<a href="#soap12_markupvalidationresponse">m:markupvalidationresponse</a>
+env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" 
+xmlns:m="http://www.w3.org/2005/10/markup-validator"&gt;
+    &lt;<a href="#soap12_uri">m:uri</a>&gt;http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-bogus-element.html&lt;/m:uri&gt;
+    &lt;<a href="#soap12_checkedby">m:checkedby</a>&gt;http://validator.w3.org/&lt;/m:checkedby&gt;
+    &lt;<a href="#soap12_doctype">m:doctype</a>&gt;-//W3C//DTD XHTML 1.0 Transitional//EN&lt;/m:doctype&gt;
+    &lt;<a href="#soap12_charset">m:charset</a>&gt;utf-8&lt;/m:charset&gt;
+    &lt;<a href="#soap12_validity">m:validity</a>&gt;false&lt;/m:validity&gt;
+    &lt;<a href="#soap12_errors">m:errors</a>&gt;
+        &lt;<a href="#soap12_errorcount">m:errorcount</a>&gt;1&lt;/m:errorcount&gt;
+        &lt;<a href="#soap12_errorlist">m:errorlist</a>&gt;
+          
+            &lt;<a href="#soap12_error">m:error</a>&gt;
+                &lt;<a href="#soap12_line">m:line</a>&gt;13&lt;/m:line&gt;
+                &lt;<a href="#soap12_col">m:col</a>&gt;6&lt;/m:col&gt;                                           
+                &lt;<a href="#soap12_message">m:message</a>&gt;element "foo" undefined&lt;/m:message&gt;
+            &lt;/m:error&gt;
+           
+        &lt;/m:errorlist&gt;
+    &lt;/m:errors&gt;
+    &lt;m:warnings&gt;
+        &lt;m:warningcount&gt;0&lt;/m:warningcount&gt;
+        &lt;m:warninglist&gt;
+        
+        
+        &lt;/m:warninglist&gt;
+    &lt;/m:warnings&gt;
+&lt;/m:markupvalidationresponse&gt;
+&lt;/env:Body&gt;
+&lt;/env:Envelope&gt;
+</pre>
+<h4 id="soap12response">SOAP1.2 response format reference</h4>
+
+<table class="refdoc">
+<tbody><tr><th>element</th><th>description</th></tr>
+<tr>
+  <th id="soap12_markupvalidationresponse">markupvalidationresponse</th>
+  <td>The main element of the validation response. Encloses all other information about the validation results.</td>
+</tr>
+<tr>
+  <th id="soap12_uri">uri</th>
+  <td>the address of the document validated. Will (likely?) be <kbd>upload://Form Submission</kbd>
+  if an uploaded document or fragment was validated.
+  In <a href="http://www.w3.org/WAI/ER/">EARL</a> terms, this is the <kbd>TestSubject</kbd>.
+  </td>
+</tr>
+<tr>
+  <th id="soap12_checkedby">checkedby</th>
+  <td>Location of the service which provided the validation result. 
+  In <a href="http://www.w3.org/WAI/ER/">EARL</a> terms, this is the <kbd>Assertor</kbd>.
+  </td>
+</tr>
+<tr>
+  <th id="soap12_doctype">doctype</th>
+  <td>Detected (or forced) Document Type for the validated document</td>
+</tr>
+<tr>
+  <th id="soap12_charset">charset</th>
+  <td>Detected (or forced) Character Encoding for the validated document</td>
+</tr>
+<tr>
+  <th id="soap12_validity">validity</th>
+  <td>Whether or not the document validated passed or not formal validation (true|false boolean)</td>
+</tr>
+<tr>
+  <th id="soap12_errors">errors</th>
+  <td>Encapsulates all data about errors encountered through the validation process</td>
+</tr>
+<tr>
+  <th id="soap12_errorcount">errorcount</th>
+  <td>a child of <a href="#soap12_errors">errors</a>, counts the number of errors listed</td>
+</tr>
+<tr>
+  <th id="soap12_errorlist">errorlist</th>
+  <td>a child of <a href="#soap12_errors">errors</a>, contains the list of errors (surprise!)</td>
+</tr>
+<tr>
+  <th id="soap12_error">error</th>
+  <td>a child of <a href="#soap12_errorlist">errorlist</a>, contains the information on a single 
+  validation error. </td>
+</tr>
+</tbody></table>
+<p><strong>Note</strong>: <code>warnings</code>, <code>warningcount</code>, 
+<code>warninglist</code> and <code>warning</code> are similar to, respectively,
+<code><a href="#soap12_errors">errors</a></code>, 
+<code><a href="#soap12_errorcount">errorcount</a></code>, 
+<code><a href="#soap12_errorlist">errorlist</a></code> and 
+<code><a href="#soap12_error">error</a></code>.
+</p>
+<h4 id="soap12message">SOAP1.2 atomic message (error or warning) format reference</h4>
+<p>As seen as the example above, the children of the <code><a href="#soap12_error">error</a></code>
+element, but also the <code>warning</code> element are <code>line</code>, <code>col</code> and
+<code>message</code>, defined below:</p>
+<table class="refdoc">
+<tbody><tr><th>element</th><th>description</th></tr>
+<tr>
+  <th id="soap12_line">line</th>
+  <td>Within the source code of the validated document, refers to the line where the error was detected.</td>
+</tr>
+<tr>
+  <th id="soap12_col">col</th>
+  <td>Within the source code of the validated document, refers to the line where the column was detected.</td>
+</tr>
+<tr>
+  <th id="soap12_message">message</th>
+  <td>The actual error message</td>
+</tr>
+<tr>
+  <th id="soap12_messageid">messageid</th>
+  <td>@@ not implemented yet@@ - should be the number of the error, as addressed internally by the validator</td>
+</tr>
+</tbody></table>
+
+<h3 id="libs">Libraries</h3>
+<p>Building of libraries used to interact with the validator's API is <a href="http://www.w3.org/QA/2006/10/validator_api.html">encouraged</a>. If you are the 
+    maintainer of such a library, <a href="http://validator.w3.org/feedback.html">contact us</a> and we will list it here.</p>
+<h4>Known libraries for the W3C Markup Validator API</h4>
+
+<ul>
+   <li><a href="http://search.cpan.org/dist/WebService-Validator-HTML-W3C/">WebService::Validator::HTML::W3C</a> in perl, 
+       by Struan Donald.</li>
+</ul>
+
+<h3 id="http_headers">Using HTTP headers to know validation results</h3>
+
+<p>Every validation result is served via the HTTP protocol, with custom headers giving a simple, quick way
+    to get validation results without having to parse the results body. This is a simple (but poorer) alternative to using 
+    the full API described above.</p>
+    
+<p>The HTTP headers for a validation results page will generally look like:</p>
+<pre>    HEAD 'http://validator.localhost/check?uri=http%3A%2F%2Fwww.w3.org'
+    
+    200 OK
+    [...]
+    Content-Language: en
+    Content-Type: text/html; charset=utf-8
+    X-W3C-Validator-Errors: 0
+    X-W3C-Validator-Recursion: 1
+    X-W3C-Validator-Status: Valid
+</pre>
+
+<p>The headers and their values are as follows:</p>
+<table>
+    <tbody><tr><th>Header</th><th>Value</th><th>Notes</th></tr>
+    <tr>
+        <td>X-W3C-Validator-Status</td>
+        <td><code>Valid</code> or <code>Invalid</code></td>
+        <td>May not be present if validation could not be performed (404 Not found, etc)</td>
+    </tr>
+    <tr>
+        <td>X-W3C-Validator-Errors</td>
+        <td>Number of Errors found during validation. <code>0</code> if no errors found.</td>
+        <td>0 does not necessarily mean "valid" (it may mean that validation could not be performed)</td>
+    </tr>
+    <tr>
+        <td>X-W3C-Validator-Recursion</td>
+        <td>Integer. Generally, <code>1</code>. More if recursively validating validation results. 
+           </td>
+        <td>    The validator will use this in conjunction with its <code>Max Recursion</code> setup to avoid
+               abusive recursion (Denial of Service attack).</td>
+    </tr>
+</tbody></table>
+</div>
+      </div><!-- End of "main" DIV. -->
+
+<address>
+      <a href="http://validator.w3.org/check?uri=referer"><img src="api_files/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88"></a>
+      Feedback: 
+	<a title="Send Feedback for the W3C Markup Validation Service" href="http://validator.w3.org/feedback.html">The W3C Validator Team</a><br>
+      <!-- SSI Template Version: $Id$ -->
+    </address>
+    <p class="copyright">
+      <a rel="Copyright" href="http://www.w3.org/Consortium/Legal/ipr-notice#Copyright">Copyright</a> © 1994-2006
+      <a href="http://www.w3.org/"><acronym title="World Wide Web Consortium">W3C</acronym></a>®
+      (<a href="http://www.csail.mit.edu/"><acronym title="Massachusetts Institute of Technology">MIT</acronym></a>,
+      <a href="http://www.ercim.org/"><acronym title="European Research Consortium for Informatics and Mathematics">ERCIM</acronym></a>,
+      <a href="http://www.keio.ac.jp/">Keio</a>),
+      All Rights Reserved.
+      W3C <a href="http://www.w3.org/Consortium/Legal/ipr-notice#Legal_Disclaimer">liability</a>,
+      <a href="http://www.w3.org/Consortium/Legal/ipr-notice#W3C_Trademarks">trademark</a>,
+      <a rel="Copyright" href="http://www.w3.org/Consortium/Legal/copyright-documents">document use</a>
+      and <a rel="Copyright" href="http://www.w3.org/Consortium/Legal/copyright-software">software licensing</a>
+      rules apply. Your interactions with this site are in accordance
+      with our <a href="http://www.w3.org/Consortium/Legal/privacy-statement#Public">public</a> and
+      <a href="http://www.w3.org/Consortium/Legal/privacy-statement#Members">Member</a> privacy
+      statements.
+    </p>
+
+  </body></html>
\ No newline at end of file
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byfile.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byfile.php
new file mode 100644
index 0000000000000000000000000000000000000000..a9c4be0ca1a9ef348e41f74ee1f6fc42eeb5f7ed
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byfile.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Example usage of Services_W3C_HTMLValidator of validating a local file.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+require_once 'Services/W3C/HTMLValidator.php';
+
+$v = new Services_W3C_HTMLValidator();
+
+echo '<pre>';
+var_dump($v->validateFile('example.html'));
+
+
+?>
\ No newline at end of file
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byuri.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byuri.php
new file mode 100644
index 0000000000000000000000000000000000000000..d34e7823fedda4d30606473676e163d52c177932
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_byuri.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Example file to demonstrate validating a URI.
+ *  
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+require_once 'Services/W3C/HTMLValidator.php';
+
+$v = new Services_W3C_HTMLValidator();
+
+echo '<pre>';
+var_dump($v->validate('http://www.unl.edu/'));
+
+
+?> 
\ No newline at end of file
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_fragment.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_fragment.php
new file mode 100644
index 0000000000000000000000000000000000000000..00248f739afc819d617703e3644bee833a89471e
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_fragment.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * Example file demonstrating how to validate an HTML fragment.
+ *  
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+require_once 'Services/W3C/HTMLValidator.php';
+
+$v = new Services_W3C_HTMLValidator();
+
+echo '<pre>';
+$t = $v->validateFragment('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+    <head><title>HTML Fragment</title></head>
+    <body><p>TEST!</p></body>
+</html>');
+var_dump($r);
+?>
\ No newline at end of file
diff --git a/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_responsexml.php b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_responsexml.php
new file mode 100644
index 0000000000000000000000000000000000000000..8747de3ef9e8115f2d3b81f6ac60ffdec1dfc624
--- /dev/null
+++ b/lib/docs/Services_W3C_HTMLValidator/Services/W3C/docs/examples/validate_responsexml.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Example file which shows how to validate a raw SOAP12 response.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+require_once 'Services/W3C/HTMLValidator.php';
+
+$v = new Services_W3C_HTMLValidator();
+
+echo '<pre>';
+var_dump($v->parseSOAP12Response('<?xml version="1.0" encoding="UTF-8"?>
+<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
+<env:Body>
+<m:markupvalidationresponse
+env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" 
+xmlns:m="http://www.w3.org/2005/10/markup-validator">
+    <m:uri>http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-bogus-element.html</m:uri>
+    <m:checkedby>http://validator.w3.org/</m:checkedby>
+    <m:doctype>-//W3C//DTD XHTML 1.0 Transitional//EN</m:doctype>
+    <m:charset>utf-8</m:charset>
+    <m:validity>false</m:validity>
+    <m:errors>
+        <m:errorcount>1</m:errorcount>
+        <m:errorlist>
+          
+            <m:error>
+                <m:line>13</m:line>
+                <m:col>6</m:col>                                           
+                <m:message>element "foo" undefined</m:message>
+            </m:error>
+           
+        </m:errorlist>
+    </m:errors>
+    <m:warnings>
+        <m:warningcount>0</m:warningcount>
+        <m:warninglist>
+        
+        
+        </m:warninglist>
+    </m:warnings>
+</m:markupvalidationresponse>
+</env:Body>
+</env:Envelope>
+'));
+?>
\ No newline at end of file
diff --git a/lib/docs/Spider/pearhub.org/examples/basic.php b/lib/docs/Spider/pearhub.org/examples/basic.php
new file mode 100644
index 0000000000000000000000000000000000000000..d62330266c85a5f641638a4c9a00b9af6ed5e3bc
--- /dev/null
+++ b/lib/docs/Spider/pearhub.org/examples/basic.php
@@ -0,0 +1,52 @@
+<?php
+
+function autoload($class)
+{
+    $class = str_replace('_', '/', $class);
+    if (file_exists(dirname(__FILE__) . '/../src/' . $class . '.php')) {
+        include dirname(__FILE__) . '/../src/' . $class . '.php';
+    }
+}
+
+spl_autoload_register("autoload");
+
+unlink('results.db');
+
+$dsn              = 'sqlite:results.db';
+$db               = new PDO($dsn);
+$db->exec('create table SpiderPage (
+    id serial,
+    uri varchar(255),
+    primary key(id)
+);');
+$db->exec(
+'create table SpiderJavaScript (
+    id serial,
+    uri varchar(255),
+    script varchar(255),
+    primary key(id)
+);');
+
+$db->exec('create table SpiderStyleSheet (
+    id serial,
+    uri varchar(255),
+    style varchar(255),
+    primary key(id)
+);');
+//$pageLogger       = new Spider_PageLogger($db);
+//$javaScriptLogger = new Spider_JavaScriptLogger($db);
+//$styleSheetLogger = new Spider_StyleSheetLogger($db);
+$logger           = new Spider_Logger();
+$downloader       = new Spider_Downloader();
+$parser           = new Spider_Parser();
+$spider           = new Spider($downloader, $parser);
+
+$spider->addLogger($logger);
+$spider->addUriFilter('Spider_AnchorFilter');
+$spider->addUriFilter('Spider_MailtoFilter');
+//$spider->addLogger($pageLogger);
+//$spider->addLogger($styleSheetLogger);
+//$spider->addLogger($javaScriptLogger);
+
+
+$spider->spider('http://www.unl.edu/fwc/');
diff --git a/lib/docs/Spider/pearhub.org/examples/batch_validator.php b/lib/docs/Spider/pearhub.org/examples/batch_validator.php
new file mode 100644
index 0000000000000000000000000000000000000000..2fc29dfcdb71c535b1b65240b711f41873702aa6
--- /dev/null
+++ b/lib/docs/Spider/pearhub.org/examples/batch_validator.php
@@ -0,0 +1,172 @@
+<?php
+function autoload($class)
+{
+    $class = str_replace('_', '/', $class);
+    if (file_exists(dirname(__FILE__) . '/../src/' . $class . '.php')) {
+        include dirname(__FILE__) . '/../src/' . $class . '.php';
+    }
+}
+
+spl_autoload_register("autoload");
+
+class ValidationLogger extends Spider_LoggerAbstract
+{
+    public $validator;
+    
+    public $page;
+    
+    function __construct($validator, $page)
+    {
+        $this->validator = $validator;
+        $this->page      = $page;
+    }
+    
+    function log($uri, DOMXPath $xpath)
+    {
+        
+        $this->page->maincontentarea .= '<h1>'.htmlentities($uri).'</h1>';
+        
+//        $r = $this->validator->validate($uri);
+//        $this->displayValidationDetails($r);
+    }
+    
+    function displayValidationDetails($r)
+    {
+        if ($r->isValid()) {
+            $this->page->maincontentarea .= '<h2 class="results" class="valid">This page is Valid!</h2>';
+            $this->page->maincontentarea .=  'Passed validation, '.count($r->warnings).' Warnings';
+        } else {
+            $this->page->maincontentarea .= '<h2 class="results" class="invalid">This page is <strong>not</strong> Valid!</h2>';
+            $this->page->maincontentarea .=  'Failed validation, '.count($r->errors).' Errors';
+        }
+        $this->page->maincontentarea .= '<div class="results_container">';
+        if (count($r->warnings)) {
+            $this->page->maincontentarea .= '<h3 class="preparse_warnings">Important Warnings</h3>
+            <p>The validator has found the following problem(s) prior to validation, 
+            which should be addressed in priority:</p>';
+            $this->page->maincontentarea .= '<ol class="warnings">';
+            addMessageSection($r->warnings, 'warn', $this->page);
+            $this->page->maincontentarea .= '</ol>';
+        }
+        if (count($r->errors)) {
+            $this->page->maincontentarea .= '
+            <h3 class="invalid">Validation Output: '.count($r->errors).' Errors</h3>';
+            $this->page->maincontentarea .= '<ol class="error_loop">';
+            addMessageSection($r->errors, 'err', $this->page);
+            $this->page->maincontentarea .= '</ol>';
+        }
+        $this->page->maincontentarea .= '</div>';
+    }
+}
+
+class FileExtensionFilter extends Spider_UriFilterInterface
+{
+    function accept()
+    {
+        $path_parts = pathinfo($this->current());
+        if (!isset($path_parts['extension'])
+            || $path_parts['extension'] == 'html'
+            || $path_parts['extension'] == 'php'
+            || $path_parts['extension'] == 'shtml'
+            || $path_parts['extension'] == 'asp'
+            || $path_parts['extension'] == 'aspx'
+            || $path_parts['extension'] == 'jsp') {
+            return true;
+        }
+        return false;
+    }
+}
+
+/**
+ * Example file demonstrating a user interface for validating pages.
+ *  
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+error_reporting(E_ALL);
+ini_set('display_errors', true);
+require_once 'Services/W3C/HTMLValidator.php';
+require_once 'UNL/Templates.php';
+
+UNL_Templates::$options['version'] = 3;
+
+const VALIDATOR_URI = 'http://validator.unl.edu/check';
+
+$v = new Services_W3C_HTMLValidator();
+$v->validator_uri = VALIDATOR_URI;
+
+$p = UNL_Templates::factory('Document');
+$p->titlegraphic = '<h1>UNL W3C Validator Instance</h1><h2>Hey validator, rock n roll. Rock on.</h2>';
+$p->head .= '<style type="text/css" media="all">@import "'.substr($v->validator_uri,0,-5).'/style/results.css";</style>';
+$p->navlinks = '<ul><li><a href="http://www.unl.edu/wdn/">WDN</a></li></ul>';
+$p->maincontentarea = '';
+
+$uri = '';
+
+if (isset($_GET['uri'])) {
+    $uri = htmlentities($_GET['uri'], ENT_QUOTES);
+}
+
+$p->maincontentarea .= '<form id="form" method="get" action="">
+  <table class="header">
+    <tbody>
+    <tr>
+      <th><label title="Address of Page to Validate" for="uri">Address</label>:</th>
+      <td colspan="2"><input id="uri" name="uri" value="'.$uri.'" size="50" type="text" /></td>
+    </tr>
+  </tbody></table>
+  <fieldset id="revalidate_opts">
+        <div id="revalidate_button" class="submit_button"><input value="Revalidate" title="Validate this document again" type="submit" /></div>
+  </fieldset>
+</form>';
+
+function addMessageSection($messages, $mtype, &$p)
+{
+    foreach ($messages as $message) {
+        addMessage($message, $mtype, $p);
+    }
+}
+
+function addMessage($message, $mtype, &$p)
+{
+    $p->maincontentarea .= '
+    <li class="msg_'.$mtype.'">
+    <span class="err_type">';
+    if ($mtype == 'err') {
+        $p->maincontentarea .= '<img src="'.substr(VALIDATOR_URI, 0, -5).'/images/info_icons/error.png" alt="Error" title="Error" />';
+    } else {
+        $p->maincontentarea .= '<img src="'.substr(VALIDATOR_URI, 0, -5).'/images/info_icons/warning.png" alt="Warning" title="Warning" />';
+    }
+    $p->maincontentarea .= '</span>
+       <em>Line '.$message->line.', Column '.$message->col.'</em>:
+       <span class="msg">'.htmlentities($message->message).'</span>.<pre><code class="input">'.$message->source.'</code></pre>';
+    $p->maincontentarea .= $message->explanation;
+    $p->maincontentarea .= '
+     </li>';
+}
+
+if (isset($_GET['uri'])) {
+    $logger           = new ValidationLogger($v, $p);
+    $downloader       = new Spider_Downloader();
+    $parser           = new Spider_Parser();
+    $spider           = new Spider($downloader, $parser);
+    
+    $spider->addLogger($logger);
+    $spider->addUriFilter('Spider_AnchorFilter');
+    $spider->addUriFilter('Spider_MailtoFilter');
+    $spider->addUriFilter('FileExtensionFilter');
+    
+    $spider->spider($_GET['uri']);
+}
+
+echo $p->toHtml();
+
+?>
diff --git a/lib/docs/Spider/pearhub.org/examples/results.db b/lib/docs/Spider/pearhub.org/examples/results.db
new file mode 100644
index 0000000000000000000000000000000000000000..b934c03407ced1ac7d6d4dfabe196c898692485b
Binary files /dev/null and b/lib/docs/Spider/pearhub.org/examples/results.db differ
diff --git a/lib/downloads/HTTP_Request2-0.5.1.tgz b/lib/downloads/HTTP_Request2-0.5.1.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..1fb02a30c70ae63705bdeaf26bfc1aed1980fafc
Binary files /dev/null and b/lib/downloads/HTTP_Request2-0.5.1.tgz differ
diff --git a/lib/downloads/Net_URL2-0.3.0.tgz b/lib/downloads/Net_URL2-0.3.0.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..19d38a7154681a396327aeb570b25566592aa7a7
Binary files /dev/null and b/lib/downloads/Net_URL2-0.3.0.tgz differ
diff --git a/lib/downloads/Services_W3C_HTMLValidator-1.0.0RC2.tgz b/lib/downloads/Services_W3C_HTMLValidator-1.0.0RC2.tgz
new file mode 100644
index 0000000000000000000000000000000000000000..702b85379c7e58ae3b9c0660625c34ac1c826eac
Binary files /dev/null and b/lib/downloads/Services_W3C_HTMLValidator-1.0.0RC2.tgz differ
diff --git a/lib/php/HTTP/Request2.php b/lib/php/HTTP/Request2.php
new file mode 100644
index 0000000000000000000000000000000000000000..19dfe66a9e58e2b21c9cb8e1b1e2e70fc1075b16
--- /dev/null
+++ b/lib/php/HTTP/Request2.php
@@ -0,0 +1,861 @@
+<?php
+/**
+ * Class representing a HTTP request message
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Request2.php 290921 2009-11-18 17:31:58Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * A class representing an URL as per RFC 3986.
+ */
+require_once 'Net/URL2.php';
+
+/**
+ * Exception class for HTTP_Request2 package
+ */
+require_once 'HTTP/Request2/Exception.php';
+
+/**
+ * Class representing a HTTP request message
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @version    Release: 0.5.1
+ * @link       http://tools.ietf.org/html/rfc2616#section-5
+ */
+class HTTP_Request2 implements SplSubject
+{
+   /**#@+
+    * Constants for HTTP request methods
+    *
+    * @link http://tools.ietf.org/html/rfc2616#section-5.1.1
+    */
+    const METHOD_OPTIONS = 'OPTIONS';
+    const METHOD_GET     = 'GET';
+    const METHOD_HEAD    = 'HEAD';
+    const METHOD_POST    = 'POST';
+    const METHOD_PUT     = 'PUT';
+    const METHOD_DELETE  = 'DELETE';
+    const METHOD_TRACE   = 'TRACE';
+    const METHOD_CONNECT = 'CONNECT';
+   /**#@-*/
+
+   /**#@+
+    * Constants for HTTP authentication schemes
+    *
+    * @link http://tools.ietf.org/html/rfc2617
+    */
+    const AUTH_BASIC  = 'basic';
+    const AUTH_DIGEST = 'digest';
+   /**#@-*/
+
+   /**
+    * Regular expression used to check for invalid symbols in RFC 2616 tokens
+    * @link http://pear.php.net/bugs/bug.php?id=15630
+    */
+    const REGEXP_INVALID_TOKEN = '![\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]!';
+
+   /**
+    * Regular expression used to check for invalid symbols in cookie strings
+    * @link http://pear.php.net/bugs/bug.php?id=15630
+    * @link http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
+    */
+    const REGEXP_INVALID_COOKIE = '/[\s,;]/';
+
+   /**
+    * Fileinfo magic database resource
+    * @var  resource
+    * @see  detectMimeType()
+    */
+    private static $_fileinfoDb;
+
+   /**
+    * Observers attached to the request (instances of SplObserver)
+    * @var  array
+    */
+    protected $observers = array();
+
+   /**
+    * Request URL
+    * @var  Net_URL2
+    */
+    protected $url;
+
+   /**
+    * Request method
+    * @var  string
+    */
+    protected $method = self::METHOD_GET;
+
+   /**
+    * Authentication data
+    * @var  array
+    * @see  getAuth()
+    */
+    protected $auth;
+
+   /**
+    * Request headers
+    * @var  array
+    */
+    protected $headers = array();
+
+   /**
+    * Configuration parameters
+    * @var  array
+    * @see  setConfig()
+    */
+    protected $config = array(
+        'adapter'           => 'HTTP_Request2_Adapter_Socket',
+        'connect_timeout'   => 10,
+        'timeout'           => 0,
+        'use_brackets'      => true,
+        'protocol_version'  => '1.1',
+        'buffer_size'       => 16384,
+        'store_body'        => true,
+
+        'proxy_host'        => '',
+        'proxy_port'        => '',
+        'proxy_user'        => '',
+        'proxy_password'    => '',
+        'proxy_auth_scheme' => self::AUTH_BASIC,
+
+        'ssl_verify_peer'   => true,
+        'ssl_verify_host'   => true,
+        'ssl_cafile'        => null,
+        'ssl_capath'        => null,
+        'ssl_local_cert'    => null,
+        'ssl_passphrase'    => null,
+
+        'digest_compat_ie'  => false,
+
+        'follow_redirects'  => false,
+        'max_redirects'     => 5,
+        'strict_redirects'  => false
+    );
+
+   /**
+    * Last event in request / response handling, intended for observers
+    * @var  array
+    * @see  getLastEvent()
+    */
+    protected $lastEvent = array(
+        'name' => 'start',
+        'data' => null
+    );
+
+   /**
+    * Request body
+    * @var  string|resource
+    * @see  setBody()
+    */
+    protected $body = '';
+
+   /**
+    * Array of POST parameters
+    * @var  array
+    */
+    protected $postParams = array();
+
+   /**
+    * Array of file uploads (for multipart/form-data POST requests)
+    * @var  array
+    */
+    protected $uploads = array();
+
+   /**
+    * Adapter used to perform actual HTTP request
+    * @var  HTTP_Request2_Adapter
+    */
+    protected $adapter;
+
+
+   /**
+    * Constructor. Can set request URL, method and configuration array.
+    *
+    * Also sets a default value for User-Agent header.
+    *
+    * @param    string|Net_Url2     Request URL
+    * @param    string              Request method
+    * @param    array               Configuration for this Request instance
+    */
+    public function __construct($url = null, $method = self::METHOD_GET, array $config = array())
+    {
+        $this->setConfig($config);
+        if (!empty($url)) {
+            $this->setUrl($url);
+        }
+        if (!empty($method)) {
+            $this->setMethod($method);
+        }
+        $this->setHeader('user-agent', 'HTTP_Request2/0.5.1 ' .
+                         '(http://pear.php.net/package/http_request2) ' .
+                         'PHP/' . phpversion());
+    }
+
+   /**
+    * Sets the URL for this request
+    *
+    * If the URL has userinfo part (username & password) these will be removed
+    * and converted to auth data. If the URL does not have a path component,
+    * that will be set to '/'.
+    *
+    * @param    string|Net_URL2 Request URL
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception
+    */
+    public function setUrl($url)
+    {
+        if (is_string($url)) {
+            $url = new Net_URL2(
+                $url, array(Net_URL2::OPTION_USE_BRACKETS => $this->config['use_brackets'])
+            );
+        }
+        if (!$url instanceof Net_URL2) {
+            throw new HTTP_Request2_Exception('Parameter is not a valid HTTP URL');
+        }
+        // URL contains username / password?
+        if ($url->getUserinfo()) {
+            $username = $url->getUser();
+            $password = $url->getPassword();
+            $this->setAuth(rawurldecode($username), $password? rawurldecode($password): '');
+            $url->setUserinfo('');
+        }
+        if ('' == $url->getPath()) {
+            $url->setPath('/');
+        }
+        $this->url = $url;
+
+        return $this;
+    }
+
+   /**
+    * Returns the request URL
+    *
+    * @return   Net_URL2
+    */
+    public function getUrl()
+    {
+        return $this->url;
+    }
+
+   /**
+    * Sets the request method
+    *
+    * @param    string
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception if the method name is invalid
+    */
+    public function setMethod($method)
+    {
+        // Method name should be a token: http://tools.ietf.org/html/rfc2616#section-5.1.1
+        if (preg_match(self::REGEXP_INVALID_TOKEN, $method)) {
+            throw new HTTP_Request2_Exception("Invalid request method '{$method}'");
+        }
+        $this->method = $method;
+
+        return $this;
+    }
+
+   /**
+    * Returns the request method
+    *
+    * @return   string
+    */
+    public function getMethod()
+    {
+        return $this->method;
+    }
+
+   /**
+    * Sets the configuration parameter(s)
+    *
+    * The following parameters are available:
+    * <ul>
+    *   <li> 'adapter'           - adapter to use (string)</li>
+    *   <li> 'connect_timeout'   - Connection timeout in seconds (integer)</li>
+    *   <li> 'timeout'           - Total number of seconds a request can take.
+    *                              Use 0 for no limit, should be greater than
+    *                              'connect_timeout' if set (integer)</li>
+    *   <li> 'use_brackets'      - Whether to append [] to array variable names (bool)</li>
+    *   <li> 'protocol_version'  - HTTP Version to use, '1.0' or '1.1' (string)</li>
+    *   <li> 'buffer_size'       - Buffer size to use for reading and writing (int)</li>
+    *   <li> 'store_body'        - Whether to store response body in response object.
+    *                              Set to false if receiving a huge response and
+    *                              using an Observer to save it (boolean)</li>
+    *   <li> 'proxy_host'        - Proxy server host (string)</li>
+    *   <li> 'proxy_port'        - Proxy server port (integer)</li>
+    *   <li> 'proxy_user'        - Proxy auth username (string)</li>
+    *   <li> 'proxy_password'    - Proxy auth password (string)</li>
+    *   <li> 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)</li>
+    *   <li> 'ssl_verify_peer'   - Whether to verify peer's SSL certificate (bool)</li>
+    *   <li> 'ssl_verify_host'   - Whether to check that Common Name in SSL
+    *                              certificate matches host name (bool)</li>
+    *   <li> 'ssl_cafile'        - Cerificate Authority file to verify the peer
+    *                              with (use with 'ssl_verify_peer') (string)</li>
+    *   <li> 'ssl_capath'        - Directory holding multiple Certificate
+    *                              Authority files (string)</li>
+    *   <li> 'ssl_local_cert'    - Name of a file containing local cerificate (string)</li>
+    *   <li> 'ssl_passphrase'    - Passphrase with which local certificate
+    *                              was encoded (string)</li>
+    *   <li> 'digest_compat_ie'  - Whether to imitate behaviour of MSIE 5 and 6
+    *                              in using URL without query string in digest
+    *                              authentication (boolean)</li>
+    *   <li> 'follow_redirects'  - Whether to automatically follow HTTP Redirects (boolean)</li>
+    *   <li> 'max_redirects'     - Maximum number of redirects to follow (integer)</li>
+    *   <li> 'strict_redirects'  - Whether to keep request method on redirects via status 301 and
+    *                              302 (true, needed for compatibility with RFC 2616)
+    *                              or switch to GET (false, needed for compatibility with most
+    *                              browsers) (boolean)</li>
+    * </ul>
+    *
+    * @param    string|array    configuration parameter name or array
+    *                           ('parameter name' => 'parameter value')
+    * @param    mixed           parameter value if $nameOrConfig is not an array
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception If the parameter is unknown
+    */
+    public function setConfig($nameOrConfig, $value = null)
+    {
+        if (is_array($nameOrConfig)) {
+            foreach ($nameOrConfig as $name => $value) {
+                $this->setConfig($name, $value);
+            }
+
+        } else {
+            if (!array_key_exists($nameOrConfig, $this->config)) {
+                throw new HTTP_Request2_Exception(
+                    "Unknown configuration parameter '{$nameOrConfig}'"
+                );
+            }
+            $this->config[$nameOrConfig] = $value;
+        }
+
+        return $this;
+    }
+
+   /**
+    * Returns the value(s) of the configuration parameter(s)
+    *
+    * @param    string  parameter name
+    * @return   mixed   value of $name parameter, array of all configuration
+    *                   parameters if $name is not given
+    * @throws   HTTP_Request2_Exception If the parameter is unknown
+    */
+    public function getConfig($name = null)
+    {
+        if (null === $name) {
+            return $this->config;
+        } elseif (!array_key_exists($name, $this->config)) {
+            throw new HTTP_Request2_Exception(
+                "Unknown configuration parameter '{$name}'"
+            );
+        }
+        return $this->config[$name];
+    }
+
+   /**
+    * Sets the autentification data
+    *
+    * @param    string  user name
+    * @param    string  password
+    * @param    string  authentication scheme
+    * @return   HTTP_Request2
+    */
+    public function setAuth($user, $password = '', $scheme = self::AUTH_BASIC)
+    {
+        if (empty($user)) {
+            $this->auth = null;
+        } else {
+            $this->auth = array(
+                'user'     => (string)$user,
+                'password' => (string)$password,
+                'scheme'   => $scheme
+            );
+        }
+
+        return $this;
+    }
+
+   /**
+    * Returns the authentication data
+    *
+    * The array has the keys 'user', 'password' and 'scheme', where 'scheme'
+    * is one of the HTTP_Request2::AUTH_* constants.
+    *
+    * @return   array
+    */
+    public function getAuth()
+    {
+        return $this->auth;
+    }
+
+   /**
+    * Sets request header(s)
+    *
+    * The first parameter may be either a full header string 'header: value' or
+    * header name. In the former case $value parameter is ignored, in the latter
+    * the header's value will either be set to $value or the header will be
+    * removed if $value is null. The first parameter can also be an array of
+    * headers, in that case method will be called recursively.
+    *
+    * Note that headers are treated case insensitively as per RFC 2616.
+    *
+    * <code>
+    * $req->setHeader('Foo: Bar'); // sets the value of 'Foo' header to 'Bar'
+    * $req->setHeader('FoO', 'Baz'); // sets the value of 'Foo' header to 'Baz'
+    * $req->setHeader(array('foo' => 'Quux')); // sets the value of 'Foo' header to 'Quux'
+    * $req->setHeader('FOO'); // removes 'Foo' header from request
+    * </code>
+    *
+    * @param    string|array    header name, header string ('Header: value')
+    *                           or an array of headers
+    * @param    string|null     header value, header will be removed if null
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception
+    */
+    public function setHeader($name, $value = null)
+    {
+        if (is_array($name)) {
+            foreach ($name as $k => $v) {
+                if (is_string($k)) {
+                    $this->setHeader($k, $v);
+                } else {
+                    $this->setHeader($v);
+                }
+            }
+        } else {
+            if (null === $value && strpos($name, ':')) {
+                list($name, $value) = array_map('trim', explode(':', $name, 2));
+            }
+            // Header name should be a token: http://tools.ietf.org/html/rfc2616#section-4.2
+            if (preg_match(self::REGEXP_INVALID_TOKEN, $name)) {
+                throw new HTTP_Request2_Exception("Invalid header name '{$name}'");
+            }
+            // Header names are case insensitive anyway
+            $name = strtolower($name);
+            if (null === $value) {
+                unset($this->headers[$name]);
+            } else {
+                $this->headers[$name] = $value;
+            }
+        }
+
+        return $this;
+    }
+
+   /**
+    * Returns the request headers
+    *
+    * The array is of the form ('header name' => 'header value'), header names
+    * are lowercased
+    *
+    * @return   array
+    */
+    public function getHeaders()
+    {
+        return $this->headers;
+    }
+
+   /**
+    * Appends a cookie to "Cookie:" header
+    *
+    * @param    string  cookie name
+    * @param    string  cookie value
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception
+    */
+    public function addCookie($name, $value)
+    {
+        $cookie = $name . '=' . $value;
+        if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) {
+            throw new HTTP_Request2_Exception("Invalid cookie: '{$cookie}'");
+        }
+        $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; ';
+        $this->setHeader('cookie', $cookies . $cookie);
+
+        return $this;
+    }
+
+   /**
+    * Sets the request body
+    *
+    * @param    string  Either a string with the body or filename containing body
+    * @param    bool    Whether first parameter is a filename
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception
+    */
+    public function setBody($body, $isFilename = false)
+    {
+        if (!$isFilename) {
+            if (!$body instanceof HTTP_Request2_MultipartBody) {
+                $this->body = (string)$body;
+            } else {
+                $this->body = $body;
+            }
+        } else {
+            if (!($fp = @fopen($body, 'rb'))) {
+                throw new HTTP_Request2_Exception("Cannot open file {$body}");
+            }
+            $this->body = $fp;
+            if (empty($this->headers['content-type'])) {
+                $this->setHeader('content-type', self::detectMimeType($body));
+            }
+        }
+        $this->postParams = $this->uploads = array();
+
+        return $this;
+    }
+
+   /**
+    * Returns the request body
+    *
+    * @return   string|resource|HTTP_Request2_MultipartBody
+    */
+    public function getBody()
+    {
+        if (self::METHOD_POST == $this->method &&
+            (!empty($this->postParams) || !empty($this->uploads))
+        ) {
+            if ('application/x-www-form-urlencoded' == $this->headers['content-type']) {
+                $body = http_build_query($this->postParams, '', '&');
+                if (!$this->getConfig('use_brackets')) {
+                    $body = preg_replace('/%5B\d+%5D=/', '=', $body);
+                }
+                // support RFC 3986 by not encoding '~' symbol (request #15368)
+                return str_replace('%7E', '~', $body);
+
+            } elseif ('multipart/form-data' == $this->headers['content-type']) {
+                require_once 'HTTP/Request2/MultipartBody.php';
+                return new HTTP_Request2_MultipartBody(
+                    $this->postParams, $this->uploads, $this->getConfig('use_brackets')
+                );
+            }
+        }
+        return $this->body;
+    }
+
+   /**
+    * Adds a file to form-based file upload
+    *
+    * Used to emulate file upload via a HTML form. The method also sets
+    * Content-Type of HTTP request to 'multipart/form-data'.
+    *
+    * If you just want to send the contents of a file as the body of HTTP
+    * request you should use setBody() method.
+    *
+    * @param    string  name of file-upload field
+    * @param    mixed   full name of local file
+    * @param    string  filename to send in the request
+    * @param    string  content-type of file being uploaded
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception
+    */
+    public function addUpload($fieldName, $filename, $sendFilename = null,
+                              $contentType = null)
+    {
+        if (!is_array($filename)) {
+            if (!($fp = @fopen($filename, 'rb'))) {
+                throw new HTTP_Request2_Exception("Cannot open file {$filename}");
+            }
+            $this->uploads[$fieldName] = array(
+                'fp'        => $fp,
+                'filename'  => empty($sendFilename)? basename($filename): $sendFilename,
+                'size'      => filesize($filename),
+                'type'      => empty($contentType)? self::detectMimeType($filename): $contentType
+            );
+        } else {
+            $fps = $names = $sizes = $types = array();
+            foreach ($filename as $f) {
+                if (!is_array($f)) {
+                    $f = array($f);
+                }
+                if (!($fp = @fopen($f[0], 'rb'))) {
+                    throw new HTTP_Request2_Exception("Cannot open file {$f[0]}");
+                }
+                $fps[]   = $fp;
+                $names[] = empty($f[1])? basename($f[0]): $f[1];
+                $sizes[] = filesize($f[0]);
+                $types[] = empty($f[2])? self::detectMimeType($f[0]): $f[2];
+            }
+            $this->uploads[$fieldName] = array(
+                'fp' => $fps, 'filename' => $names, 'size' => $sizes, 'type' => $types
+            );
+        }
+        if (empty($this->headers['content-type']) ||
+            'application/x-www-form-urlencoded' == $this->headers['content-type']
+        ) {
+            $this->setHeader('content-type', 'multipart/form-data');
+        }
+
+        return $this;
+    }
+
+   /**
+    * Adds POST parameter(s) to the request.
+    *
+    * @param    string|array    parameter name or array ('name' => 'value')
+    * @param    mixed           parameter value (can be an array)
+    * @return   HTTP_Request2
+    */
+    public function addPostParameter($name, $value = null)
+    {
+        if (!is_array($name)) {
+            $this->postParams[$name] = $value;
+        } else {
+            foreach ($name as $k => $v) {
+                $this->addPostParameter($k, $v);
+            }
+        }
+        if (empty($this->headers['content-type'])) {
+            $this->setHeader('content-type', 'application/x-www-form-urlencoded');
+        }
+
+        return $this;
+    }
+
+   /**
+    * Attaches a new observer
+    *
+    * @param    SplObserver
+    */
+    public function attach(SplObserver $observer)
+    {
+        foreach ($this->observers as $attached) {
+            if ($attached === $observer) {
+                return;
+            }
+        }
+        $this->observers[] = $observer;
+    }
+
+   /**
+    * Detaches an existing observer
+    *
+    * @param    SplObserver
+    */
+    public function detach(SplObserver $observer)
+    {
+        foreach ($this->observers as $key => $attached) {
+            if ($attached === $observer) {
+                unset($this->observers[$key]);
+                return;
+            }
+        }
+    }
+
+   /**
+    * Notifies all observers
+    */
+    public function notify()
+    {
+        foreach ($this->observers as $observer) {
+            $observer->update($this);
+        }
+    }
+
+   /**
+    * Sets the last event
+    *
+    * Adapters should use this method to set the current state of the request
+    * and notify the observers.
+    *
+    * @param    string  event name
+    * @param    mixed   event data
+    */
+    public function setLastEvent($name, $data = null)
+    {
+        $this->lastEvent = array(
+            'name' => $name,
+            'data' => $data
+        );
+        $this->notify();
+    }
+
+   /**
+    * Returns the last event
+    *
+    * Observers should use this method to access the last change in request.
+    * The following event names are possible:
+    * <ul>
+    *   <li>'connect'                 - after connection to remote server,
+    *                                   data is the destination (string)</li>
+    *   <li>'disconnect'              - after disconnection from server</li>
+    *   <li>'sentHeaders'             - after sending the request headers,
+    *                                   data is the headers sent (string)</li>
+    *   <li>'sentBodyPart'            - after sending a part of the request body,
+    *                                   data is the length of that part (int)</li>
+    *   <li>'receivedHeaders'         - after receiving the response headers,
+    *                                   data is HTTP_Request2_Response object</li>
+    *   <li>'receivedBodyPart'        - after receiving a part of the response
+    *                                   body, data is that part (string)</li>
+    *   <li>'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still
+    *                                   encoded by Content-Encoding</li>
+    *   <li>'receivedBody'            - after receiving the complete response
+    *                                   body, data is HTTP_Request2_Response object</li>
+    * </ul>
+    * Different adapters may not send all the event types. Mock adapter does
+    * not send any events to the observers.
+    *
+    * @return   array   The array has two keys: 'name' and 'data'
+    */
+    public function getLastEvent()
+    {
+        return $this->lastEvent;
+    }
+
+   /**
+    * Sets the adapter used to actually perform the request
+    *
+    * You can pass either an instance of a class implementing HTTP_Request2_Adapter
+    * or a class name. The method will only try to include a file if the class
+    * name starts with HTTP_Request2_Adapter_, it will also try to prepend this
+    * prefix to the class name if it doesn't contain any underscores, so that
+    * <code>
+    * $request->setAdapter('curl');
+    * </code>
+    * will work.
+    *
+    * @param    string|HTTP_Request2_Adapter
+    * @return   HTTP_Request2
+    * @throws   HTTP_Request2_Exception
+    */
+    public function setAdapter($adapter)
+    {
+        if (is_string($adapter)) {
+            if (!class_exists($adapter, false)) {
+                if (false === strpos($adapter, '_')) {
+                    $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter);
+                }
+                if (preg_match('/^HTTP_Request2_Adapter_([a-zA-Z0-9]+)$/', $adapter)) {
+                    include_once str_replace('_', DIRECTORY_SEPARATOR, $adapter) . '.php';
+                }
+                if (!class_exists($adapter, false)) {
+                    throw new HTTP_Request2_Exception("Class {$adapter} not found");
+                }
+            }
+            $adapter = new $adapter;
+        }
+        if (!$adapter instanceof HTTP_Request2_Adapter) {
+            throw new HTTP_Request2_Exception('Parameter is not a HTTP request adapter');
+        }
+        $this->adapter = $adapter;
+
+        return $this;
+    }
+
+   /**
+    * Sends the request and returns the response
+    *
+    * @throws   HTTP_Request2_Exception
+    * @return   HTTP_Request2_Response
+    */
+    public function send()
+    {
+        // Sanity check for URL
+        if (!$this->url instanceof Net_URL2) {
+            throw new HTTP_Request2_Exception('No URL given');
+        } elseif (!$this->url->isAbsolute()) {
+            throw new HTTP_Request2_Exception('Absolute URL required');
+        } elseif (!in_array(strtolower($this->url->getScheme()), array('https', 'http'))) {
+            throw new HTTP_Request2_Exception('Not a HTTP URL');
+        }
+        if (empty($this->adapter)) {
+            $this->setAdapter($this->getConfig('adapter'));
+        }
+        // magic_quotes_runtime may break file uploads and chunked response
+        // processing; see bug #4543
+        if ($magicQuotes = ini_get('magic_quotes_runtime')) {
+            ini_set('magic_quotes_runtime', false);
+        }
+        // force using single byte encoding if mbstring extension overloads
+        // strlen() and substr(); see bug #1781, bug #10605
+        if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
+            $oldEncoding = mb_internal_encoding();
+            mb_internal_encoding('iso-8859-1');
+        }
+
+        try {
+            $response = $this->adapter->sendRequest($this);
+        } catch (Exception $e) {
+        }
+        // cleanup in either case (poor man's "finally" clause)
+        if ($magicQuotes) {
+            ini_set('magic_quotes_runtime', true);
+        }
+        if (!empty($oldEncoding)) {
+            mb_internal_encoding($oldEncoding);
+        }
+        // rethrow the exception
+        if (!empty($e)) {
+            throw $e;
+        }
+        return $response;
+    }
+
+   /**
+    * Tries to detect MIME type of a file
+    *
+    * The method will try to use fileinfo extension if it is available,
+    * deprecated mime_content_type() function in the other case. If neither
+    * works, default 'application/octet-stream' MIME type is returned
+    *
+    * @param    string  filename
+    * @return   string  file MIME type
+    */
+    protected static function detectMimeType($filename)
+    {
+        // finfo extension from PECL available
+        if (function_exists('finfo_open')) {
+            if (!isset(self::$_fileinfoDb)) {
+                self::$_fileinfoDb = @finfo_open(FILEINFO_MIME);
+            }
+            if (self::$_fileinfoDb) {
+                $info = finfo_file(self::$_fileinfoDb, $filename);
+            }
+        }
+        // (deprecated) mime_content_type function available
+        if (empty($info) && function_exists('mime_content_type')) {
+            return mime_content_type($filename);
+        }
+        return empty($info)? 'application/octet-stream': $info;
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/php/HTTP/Request2/Adapter.php b/lib/php/HTTP/Request2/Adapter.php
new file mode 100644
index 0000000000000000000000000000000000000000..16ff131bd155a0bb596caa0d0756adddfcbed7d0
--- /dev/null
+++ b/lib/php/HTTP/Request2/Adapter.php
@@ -0,0 +1,154 @@
+<?php
+/**
+ * Base class for HTTP_Request2 adapters
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Adapter.php 291118 2009-11-21 17:58:23Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class representing a HTTP response
+ */
+require_once 'HTTP/Request2/Response.php';
+
+/**
+ * Base class for HTTP_Request2 adapters
+ *
+ * HTTP_Request2 class itself only defines methods for aggregating the request
+ * data, all actual work of sending the request to the remote server and
+ * receiving its response is performed by adapters.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @version    Release: 0.5.1
+ */
+abstract class HTTP_Request2_Adapter
+{
+   /**
+    * A list of methods that MUST NOT have a request body, per RFC 2616
+    * @var  array
+    */
+    protected static $bodyDisallowed = array('TRACE');
+
+   /**
+    * Methods having defined semantics for request body
+    *
+    * Content-Length header (indicating that the body follows, section 4.3 of
+    * RFC 2616) will be sent for these methods even if no body was added
+    *
+    * @var  array
+    * @link http://pear.php.net/bugs/bug.php?id=12900
+    * @link http://pear.php.net/bugs/bug.php?id=14740
+    */
+    protected static $bodyRequired = array('POST', 'PUT');
+
+   /**
+    * Request being sent
+    * @var  HTTP_Request2
+    */
+    protected $request;
+
+   /**
+    * Request body
+    * @var  string|resource|HTTP_Request2_MultipartBody
+    * @see  HTTP_Request2::getBody()
+    */
+    protected $requestBody;
+
+   /**
+    * Length of the request body
+    * @var  integer
+    */
+    protected $contentLength;
+
+   /**
+    * Sends request to the remote server and returns its response
+    *
+    * @param    HTTP_Request2
+    * @return   HTTP_Request2_Response
+    * @throws   HTTP_Request2_Exception
+    */
+    abstract public function sendRequest(HTTP_Request2 $request);
+
+   /**
+    * Calculates length of the request body, adds proper headers
+    *
+    * @param    array   associative array of request headers, this method will
+    *                   add proper 'Content-Length' and 'Content-Type' headers
+    *                   to this array (or remove them if not needed)
+    */
+    protected function calculateRequestLength(&$headers)
+    {
+        $this->requestBody = $this->request->getBody();
+
+        if (is_string($this->requestBody)) {
+            $this->contentLength = strlen($this->requestBody);
+        } elseif (is_resource($this->requestBody)) {
+            $stat = fstat($this->requestBody);
+            $this->contentLength = $stat['size'];
+            rewind($this->requestBody);
+        } else {
+            $this->contentLength = $this->requestBody->getLength();
+            $headers['content-type'] = 'multipart/form-data; boundary=' .
+                                       $this->requestBody->getBoundary();
+            $this->requestBody->rewind();
+        }
+
+        if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
+            0 == $this->contentLength
+        ) {
+            // No body: send a Content-Length header nonetheless (request #12900),
+            // but do that only for methods that require a body (bug #14740)
+            if (in_array($this->request->getMethod(), self::$bodyRequired)) {
+                $headers['content-length'] = 0;
+            } else {
+                unset($headers['content-length']);
+                // if the method doesn't require a body and doesn't have a
+                // body, don't send a Content-Type header. (request #16799)
+                unset($headers['content-type']);
+            }
+        } else {
+            if (empty($headers['content-type'])) {
+                $headers['content-type'] = 'application/x-www-form-urlencoded';
+            }
+            $headers['content-length'] = $this->contentLength;
+        }
+    }
+}
+?>
diff --git a/lib/php/HTTP/Request2/Adapter/Curl.php b/lib/php/HTTP/Request2/Adapter/Curl.php
new file mode 100644
index 0000000000000000000000000000000000000000..77ab6fae02b026e6944c71ec663dcbb7c5cf5988
--- /dev/null
+++ b/lib/php/HTTP/Request2/Adapter/Curl.php
@@ -0,0 +1,461 @@
+<?php
+/**
+ * Adapter for HTTP_Request2 wrapping around cURL extension
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Curl.php 291118 2009-11-21 17:58:23Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Base class for HTTP_Request2 adapters
+ */
+require_once 'HTTP/Request2/Adapter.php';
+
+/**
+ * Adapter for HTTP_Request2 wrapping around cURL extension
+ *
+ * @category    HTTP
+ * @package     HTTP_Request2
+ * @author      Alexey Borzov <avb@php.net>
+ * @version     Release: 0.5.1
+ */
+class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
+{
+   /**
+    * Mapping of header names to cURL options
+    * @var  array
+    */
+    protected static $headerMap = array(
+        'accept-encoding' => CURLOPT_ENCODING,
+        'cookie'          => CURLOPT_COOKIE,
+        'referer'         => CURLOPT_REFERER,
+        'user-agent'      => CURLOPT_USERAGENT
+    );
+
+   /**
+    * Mapping of SSL context options to cURL options
+    * @var  array
+    */
+    protected static $sslContextMap = array(
+        'ssl_verify_peer' => CURLOPT_SSL_VERIFYPEER,
+        'ssl_cafile'      => CURLOPT_CAINFO,
+        'ssl_capath'      => CURLOPT_CAPATH,
+        'ssl_local_cert'  => CURLOPT_SSLCERT,
+        'ssl_passphrase'  => CURLOPT_SSLCERTPASSWD
+   );
+
+   /**
+    * Response being received
+    * @var  HTTP_Request2_Response
+    */
+    protected $response;
+
+   /**
+    * Whether 'sentHeaders' event was sent to observers
+    * @var  boolean
+    */
+    protected $eventSentHeaders = false;
+
+   /**
+    * Whether 'receivedHeaders' event was sent to observers
+    * @var boolean
+    */
+    protected $eventReceivedHeaders = false;
+
+   /**
+    * Position within request body
+    * @var  integer
+    * @see  callbackReadBody()
+    */
+    protected $position = 0;
+
+   /**
+    * Information about last transfer, as returned by curl_getinfo()
+    * @var  array
+    */
+    protected $lastInfo;
+
+   /**
+    * Sends request to the remote server and returns its response
+    *
+    * @param    HTTP_Request2
+    * @return   HTTP_Request2_Response
+    * @throws   HTTP_Request2_Exception
+    */
+    public function sendRequest(HTTP_Request2 $request)
+    {
+        if (!extension_loaded('curl')) {
+            throw new HTTP_Request2_Exception('cURL extension not available');
+        }
+
+        $this->request              = $request;
+        $this->response             = null;
+        $this->position             = 0;
+        $this->eventSentHeaders     = false;
+        $this->eventReceivedHeaders = false;
+
+        try {
+            if (false === curl_exec($ch = $this->createCurlHandle())) {
+                $errorMessage = 'Error sending request: #' . curl_errno($ch) .
+                                                       ' ' . curl_error($ch);
+            }
+        } catch (Exception $e) {
+        }
+        $this->lastInfo = curl_getinfo($ch);
+        curl_close($ch);
+
+        $response = $this->response;
+        unset($this->request, $this->requestBody, $this->response);
+
+        if (!empty($e)) {
+            throw $e;
+        } elseif (!empty($errorMessage)) {
+            throw new HTTP_Request2_Exception($errorMessage);
+        }
+
+        if (0 < $this->lastInfo['size_download']) {
+            $request->setLastEvent('receivedBody', $response);
+        }
+        return $response;
+    }
+
+   /**
+    * Returns information about last transfer
+    *
+    * @return   array   associative array as returned by curl_getinfo()
+    */
+    public function getInfo()
+    {
+        return $this->lastInfo;
+    }
+
+   /**
+    * Creates a new cURL handle and populates it with data from the request
+    *
+    * @return   resource    a cURL handle, as created by curl_init()
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function createCurlHandle()
+    {
+        $ch = curl_init();
+
+        curl_setopt_array($ch, array(
+            // setup write callbacks
+            CURLOPT_HEADERFUNCTION => array($this, 'callbackWriteHeader'),
+            CURLOPT_WRITEFUNCTION  => array($this, 'callbackWriteBody'),
+            // buffer size
+            CURLOPT_BUFFERSIZE     => $this->request->getConfig('buffer_size'),
+            // connection timeout
+            CURLOPT_CONNECTTIMEOUT => $this->request->getConfig('connect_timeout'),
+            // save full outgoing headers, in case someone is interested
+            CURLINFO_HEADER_OUT    => true,
+            // request url
+            CURLOPT_URL            => $this->request->getUrl()->getUrl()
+        ));
+
+        // set up redirects
+        if (!$this->request->getConfig('follow_redirects')) {
+            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
+        } else {
+            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+            curl_setopt($ch, CURLOPT_MAXREDIRS, $this->request->getConfig('max_redirects'));
+            // limit redirects to http(s), works in 5.2.10+
+            if (defined('CURLOPT_REDIR_PROTOCOLS')) {
+                curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
+            }
+            // works sometime after 5.3.0, http://bugs.php.net/bug.php?id=49571
+            if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR ')) {
+                curl_setopt($ch, CURLOPT_POSTREDIR, 3);
+            }
+        }
+
+        // request timeout
+        if ($timeout = $this->request->getConfig('timeout')) {
+            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
+        }
+
+        // set HTTP version
+        switch ($this->request->getConfig('protocol_version')) {
+            case '1.0':
+                curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
+                break;
+            case '1.1':
+                curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+        }
+
+        // set request method
+        switch ($this->request->getMethod()) {
+            case HTTP_Request2::METHOD_GET:
+                curl_setopt($ch, CURLOPT_HTTPGET, true);
+                break;
+            case HTTP_Request2::METHOD_POST:
+                curl_setopt($ch, CURLOPT_POST, true);
+                break;
+            case HTTP_Request2::METHOD_HEAD:
+                curl_setopt($ch, CURLOPT_NOBODY, true);
+                break;
+            default:
+                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->request->getMethod());
+        }
+
+        // set proxy, if needed
+        if ($host = $this->request->getConfig('proxy_host')) {
+            if (!($port = $this->request->getConfig('proxy_port'))) {
+                throw new HTTP_Request2_Exception('Proxy port not provided');
+            }
+            curl_setopt($ch, CURLOPT_PROXY, $host . ':' . $port);
+            if ($user = $this->request->getConfig('proxy_user')) {
+                curl_setopt($ch, CURLOPT_PROXYUSERPWD, $user . ':' .
+                            $this->request->getConfig('proxy_password'));
+                switch ($this->request->getConfig('proxy_auth_scheme')) {
+                    case HTTP_Request2::AUTH_BASIC:
+                        curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
+                        break;
+                    case HTTP_Request2::AUTH_DIGEST:
+                        curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
+                }
+            }
+        }
+
+        // set authentication data
+        if ($auth = $this->request->getAuth()) {
+            curl_setopt($ch, CURLOPT_USERPWD, $auth['user'] . ':' . $auth['password']);
+            switch ($auth['scheme']) {
+                case HTTP_Request2::AUTH_BASIC:
+                    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+                    break;
+                case HTTP_Request2::AUTH_DIGEST:
+                    curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
+            }
+        }
+
+        // set SSL options
+        if (0 == strcasecmp($this->request->getUrl()->getScheme(), 'https')) {
+            foreach ($this->request->getConfig() as $name => $value) {
+                if ('ssl_verify_host' == $name && null !== $value) {
+                    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $value? 2: 0);
+                } elseif (isset(self::$sslContextMap[$name]) && null !== $value) {
+                    curl_setopt($ch, self::$sslContextMap[$name], $value);
+                }
+            }
+        }
+
+        $headers = $this->request->getHeaders();
+        // make cURL automagically send proper header
+        if (!isset($headers['accept-encoding'])) {
+            $headers['accept-encoding'] = '';
+        }
+
+        // set headers having special cURL keys
+        foreach (self::$headerMap as $name => $option) {
+            if (isset($headers[$name])) {
+                curl_setopt($ch, $option, $headers[$name]);
+                unset($headers[$name]);
+            }
+        }
+
+        $this->calculateRequestLength($headers);
+        if (isset($headers['content-length'])) {
+            $this->workaroundPhpBug47204($ch, $headers);
+        }
+
+        // set headers not having special keys
+        $headersFmt = array();
+        foreach ($headers as $name => $value) {
+            $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
+            $headersFmt[]  = $canonicalName . ': ' . $value;
+        }
+        curl_setopt($ch, CURLOPT_HTTPHEADER, $headersFmt);
+
+        return $ch;
+    }
+
+   /**
+    * Workaround for PHP bug #47204 that prevents rewinding request body
+    *
+    * The workaround consists of reading the entire request body into memory
+    * and setting it as CURLOPT_POSTFIELDS, so it isn't recommended for large
+    * file uploads, use Socket adapter instead.
+    *
+    * @param    resource    cURL handle
+    * @param    array       Request headers
+    */
+    protected function workaroundPhpBug47204($ch, &$headers)
+    {
+        // no redirects, no digest auth -> probably no rewind needed
+        if (!$this->request->getConfig('follow_redirects')
+            && (!($auth = $this->request->getAuth())
+                || HTTP_Request2::AUTH_DIGEST != $auth['scheme'])
+        ) {
+            curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody'));
+
+        // rewind may be needed, read the whole body into memory
+        } else {
+            if ($this->requestBody instanceof HTTP_Request2_MultipartBody) {
+                $this->requestBody = $this->requestBody->__toString();
+
+            } elseif (is_resource($this->requestBody)) {
+                $fp = $this->requestBody;
+                $this->requestBody = '';
+                while (!feof($fp)) {
+                    $this->requestBody .= fread($fp, 16384);
+                }
+            }
+            // curl hangs up if content-length is present
+            unset($headers['content-length']);
+            curl_setopt($ch, CURLOPT_POSTFIELDS, $this->requestBody);
+        }
+    }
+
+   /**
+    * Callback function called by cURL for reading the request body
+    *
+    * @param    resource    cURL handle
+    * @param    resource    file descriptor (not used)
+    * @param    integer     maximum length of data to return
+    * @return   string      part of the request body, up to $length bytes
+    */
+    protected function callbackReadBody($ch, $fd, $length)
+    {
+        if (!$this->eventSentHeaders) {
+            $this->request->setLastEvent(
+                'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
+            );
+            $this->eventSentHeaders = true;
+        }
+        if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
+            0 == $this->contentLength || $this->position >= $this->contentLength
+        ) {
+            return '';
+        }
+        if (is_string($this->requestBody)) {
+            $string = substr($this->requestBody, $this->position, $length);
+        } elseif (is_resource($this->requestBody)) {
+            $string = fread($this->requestBody, $length);
+        } else {
+            $string = $this->requestBody->read($length);
+        }
+        $this->request->setLastEvent('sentBodyPart', strlen($string));
+        $this->position += strlen($string);
+        return $string;
+    }
+
+   /**
+    * Callback function called by cURL for saving the response headers
+    *
+    * @param    resource    cURL handle
+    * @param    string      response header (with trailing CRLF)
+    * @return   integer     number of bytes saved
+    * @see      HTTP_Request2_Response::parseHeaderLine()
+    */
+    protected function callbackWriteHeader($ch, $string)
+    {
+        // we may receive a second set of headers if doing e.g. digest auth
+        if ($this->eventReceivedHeaders || !$this->eventSentHeaders) {
+            // don't bother with 100-Continue responses (bug #15785)
+            if (!$this->eventSentHeaders ||
+                $this->response->getStatus() >= 200
+            ) {
+                $this->request->setLastEvent(
+                    'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
+                );
+            }
+            $upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD);
+            // if body wasn't read by a callback, send event with total body size
+            if ($upload > $this->position) {
+                $this->request->setLastEvent(
+                    'sentBodyPart', $upload - $this->position
+                );
+                $this->position = $upload;
+            }
+            $this->eventSentHeaders = true;
+            // we'll need a new response object
+            if ($this->eventReceivedHeaders) {
+                $this->eventReceivedHeaders = false;
+                $this->response             = null;
+            }
+        }
+        if (empty($this->response)) {
+            $this->response = new HTTP_Request2_Response($string, false);
+        } else {
+            $this->response->parseHeaderLine($string);
+            if ('' == trim($string)) {
+                // don't bother with 100-Continue responses (bug #15785)
+                if (200 <= $this->response->getStatus()) {
+                    $this->request->setLastEvent('receivedHeaders', $this->response);
+                }
+                // for versions lower than 5.2.10, check the redirection URL protocol
+                if ($this->request->getConfig('follow_redirects') && !defined('CURLOPT_REDIR_PROTOCOLS')
+                    && $this->response->isRedirect()
+                ) {
+                    $redirectUrl = new Net_URL2($this->response->getHeader('location'));
+                    if ($redirectUrl->isAbsolute()
+                        && !in_array($redirectUrl->getScheme(), array('http', 'https'))
+                    ) {
+                        return -1;
+                    }
+                }
+                $this->eventReceivedHeaders = true;
+            }
+        }
+        return strlen($string);
+    }
+
+   /**
+    * Callback function called by cURL for saving the response body
+    *
+    * @param    resource    cURL handle (not used)
+    * @param    string      part of the response body
+    * @return   integer     number of bytes saved
+    * @see      HTTP_Request2_Response::appendBody()
+    */
+    protected function callbackWriteBody($ch, $string)
+    {
+        // cURL calls WRITEFUNCTION callback without calling HEADERFUNCTION if
+        // response doesn't start with proper HTTP status line (see bug #15716)
+        if (empty($this->response)) {
+            throw new HTTP_Request2_Exception("Malformed response: {$string}");
+        }
+        if ($this->request->getConfig('store_body')) {
+            $this->response->appendBody($string);
+        }
+        $this->request->setLastEvent('receivedBodyPart', $string);
+        return strlen($string);
+    }
+}
+?>
diff --git a/lib/php/HTTP/Request2/Adapter/Mock.php b/lib/php/HTTP/Request2/Adapter/Mock.php
new file mode 100644
index 0000000000000000000000000000000000000000..2812ce93ea84b972a7a7230cf25cd3d97aef97a2
--- /dev/null
+++ b/lib/php/HTTP/Request2/Adapter/Mock.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * Mock adapter intended for testing
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Mock.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Base class for HTTP_Request2 adapters
+ */
+require_once 'HTTP/Request2/Adapter.php';
+
+/**
+ * Mock adapter intended for testing
+ *
+ * Can be used to test applications depending on HTTP_Request2 package without
+ * actually performing any HTTP requests. This adapter will return responses
+ * previously added via addResponse()
+ * <code>
+ * $mock = new HTTP_Request2_Adapter_Mock();
+ * $mock->addResponse("HTTP/1.1 ... ");
+ *
+ * $request = new HTTP_Request2();
+ * $request->setAdapter($mock);
+ *
+ * // This will return the response set above
+ * $response = $req->send();
+ * </code>
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @version    Release: 0.5.1
+ */
+class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter
+{
+   /**
+    * A queue of responses to be returned by sendRequest()
+    * @var  array
+    */
+    protected $responses = array();
+
+   /**
+    * Returns the next response from the queue built by addResponse()
+    *
+    * If the queue is empty it will return default empty response with status 400,
+    * if an Exception object was added to the queue it will be thrown.
+    *
+    * @param    HTTP_Request2
+    * @return   HTTP_Request2_Response
+    * @throws   Exception
+    */
+    public function sendRequest(HTTP_Request2 $request)
+    {
+        if (count($this->responses) > 0) {
+            $response = array_shift($this->responses);
+            if ($response instanceof HTTP_Request2_Response) {
+                return $response;
+            } else {
+                // rethrow the exception
+                $class   = get_class($response);
+                $message = $response->getMessage();
+                $code    = $response->getCode();
+                throw new $class($message, $code);
+            }
+        } else {
+            return self::createResponseFromString("HTTP/1.1 400 Bad Request\r\n\r\n");
+        }
+    }
+
+   /**
+    * Adds response to the queue
+    *
+    * @param    mixed   either a string, a pointer to an open file,
+    *                   an instance of HTTP_Request2_Response or Exception
+    * @throws   HTTP_Request2_Exception
+    */
+    public function addResponse($response)
+    {
+        if (is_string($response)) {
+            $response = self::createResponseFromString($response);
+        } elseif (is_resource($response)) {
+            $response = self::createResponseFromFile($response);
+        } elseif (!$response instanceof HTTP_Request2_Response &&
+                  !$response instanceof Exception
+        ) {
+            throw new HTTP_Request2_Exception('Parameter is not a valid response');
+        }
+        $this->responses[] = $response;
+    }
+
+   /**
+    * Creates a new HTTP_Request2_Response object from a string
+    *
+    * @param    string
+    * @return   HTTP_Request2_Response
+    * @throws   HTTP_Request2_Exception
+    */
+    public static function createResponseFromString($str)
+    {
+        $parts       = preg_split('!(\r?\n){2}!m', $str, 2);
+        $headerLines = explode("\n", $parts[0]);
+        $response    = new HTTP_Request2_Response(array_shift($headerLines));
+        foreach ($headerLines as $headerLine) {
+            $response->parseHeaderLine($headerLine);
+        }
+        $response->parseHeaderLine('');
+        if (isset($parts[1])) {
+            $response->appendBody($parts[1]);
+        }
+        return $response;
+    }
+
+   /**
+    * Creates a new HTTP_Request2_Response object from a file
+    *
+    * @param    resource    file pointer returned by fopen()
+    * @return   HTTP_Request2_Response
+    * @throws   HTTP_Request2_Exception
+    */
+    public static function createResponseFromFile($fp)
+    {
+        $response = new HTTP_Request2_Response(fgets($fp));
+        do {
+            $headerLine = fgets($fp);
+            $response->parseHeaderLine($headerLine);
+        } while ('' != trim($headerLine));
+
+        while (!feof($fp)) {
+            $response->appendBody(fread($fp, 8192));
+        }
+        return $response;
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/php/HTTP/Request2/Adapter/Socket.php b/lib/php/HTTP/Request2/Adapter/Socket.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d1788c7d4f7a46465221b67a3c35094a29de30f
--- /dev/null
+++ b/lib/php/HTTP/Request2/Adapter/Socket.php
@@ -0,0 +1,1046 @@
+<?php
+/**
+ * Socket-based adapter for HTTP_Request2
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Socket.php 290921 2009-11-18 17:31:58Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Base class for HTTP_Request2 adapters
+ */
+require_once 'HTTP/Request2/Adapter.php';
+
+/**
+ * Socket-based adapter for HTTP_Request2
+ *
+ * This adapter uses only PHP sockets and will work on almost any PHP
+ * environment. Code is based on original HTTP_Request PEAR package.
+ *
+ * @category    HTTP
+ * @package     HTTP_Request2
+ * @author      Alexey Borzov <avb@php.net>
+ * @version     Release: 0.5.1
+ */
+class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
+{
+   /**
+    * Regular expression for 'token' rule from RFC 2616
+    */
+    const REGEXP_TOKEN = '[^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+';
+
+   /**
+    * Regular expression for 'quoted-string' rule from RFC 2616
+    */
+    const REGEXP_QUOTED_STRING = '"(?:\\\\.|[^\\\\"])*"';
+
+   /**
+    * Connected sockets, needed for Keep-Alive support
+    * @var  array
+    * @see  connect()
+    */
+    protected static $sockets = array();
+
+   /**
+    * Data for digest authentication scheme
+    *
+    * The keys for the array are URL prefixes.
+    *
+    * The values are associative arrays with data (realm, nonce, nonce-count,
+    * opaque...) needed for digest authentication. Stored here to prevent making
+    * duplicate requests to digest-protected resources after we have already
+    * received the challenge.
+    *
+    * @var  array
+    */
+    protected static $challenges = array();
+
+   /**
+    * Connected socket
+    * @var  resource
+    * @see  connect()
+    */
+    protected $socket;
+
+   /**
+    * Challenge used for server digest authentication
+    * @var  array
+    */
+    protected $serverChallenge;
+
+   /**
+    * Challenge used for proxy digest authentication
+    * @var  array
+    */
+    protected $proxyChallenge;
+
+   /**
+    * Sum of start time and global timeout, exception will be thrown if request continues past this time
+    * @var  integer
+    */
+    protected $deadline = null;
+
+   /**
+    * Remaining length of the current chunk, when reading chunked response
+    * @var  integer
+    * @see  readChunked()
+    */
+    protected $chunkLength = 0;
+
+   /**
+    * Remaining amount of redirections to follow
+    *
+    * Starts at 'max_redirects' configuration parameter and is reduced on each
+    * subsequent redirect. An Exception will be thrown once it reaches zero.
+    *
+    * @var  integer
+    */
+    protected $redirectCountdown = null;
+
+   /**
+    * Sends request to the remote server and returns its response
+    *
+    * @param    HTTP_Request2
+    * @return   HTTP_Request2_Response
+    * @throws   HTTP_Request2_Exception
+    */
+    public function sendRequest(HTTP_Request2 $request)
+    {
+        $this->request = $request;
+
+        // Use global request timeout if given, see feature requests #5735, #8964
+        if ($timeout = $request->getConfig('timeout')) {
+            $this->deadline = time() + $timeout;
+        } else {
+            $this->deadline = null;
+        }
+
+        try {
+            $keepAlive = $this->connect();
+            $headers   = $this->prepareHeaders();
+            if (false === @fwrite($this->socket, $headers, strlen($headers))) {
+                throw new HTTP_Request2_Exception('Error writing request');
+            }
+            // provide request headers to the observer, see request #7633
+            $this->request->setLastEvent('sentHeaders', $headers);
+            $this->writeBody();
+
+            if ($this->deadline && time() > $this->deadline) {
+                throw new HTTP_Request2_Exception(
+                    'Request timed out after ' .
+                    $request->getConfig('timeout') . ' second(s)'
+                );
+            }
+
+            $response = $this->readResponse();
+
+            if (!$this->canKeepAlive($keepAlive, $response)) {
+                $this->disconnect();
+            }
+
+            if ($this->shouldUseProxyDigestAuth($response)) {
+                return $this->sendRequest($request);
+            }
+            if ($this->shouldUseServerDigestAuth($response)) {
+                return $this->sendRequest($request);
+            }
+            if ($authInfo = $response->getHeader('authentication-info')) {
+                $this->updateChallenge($this->serverChallenge, $authInfo);
+            }
+            if ($proxyInfo = $response->getHeader('proxy-authentication-info')) {
+                $this->updateChallenge($this->proxyChallenge, $proxyInfo);
+            }
+
+        } catch (Exception $e) {
+            $this->disconnect();
+        }
+
+        unset($this->request, $this->requestBody);
+
+        if (!empty($e)) {
+            throw $e;
+        }
+
+        if (!$request->getConfig('follow_redirects') || !$response->isRedirect()) {
+            return $response;
+        } else {
+            return $this->handleRedirect($request, $response);
+        }
+    }
+
+   /**
+    * Connects to the remote server
+    *
+    * @return   bool    whether the connection can be persistent
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function connect()
+    {
+        $secure  = 0 == strcasecmp($this->request->getUrl()->getScheme(), 'https');
+        $tunnel  = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
+        $headers = $this->request->getHeaders();
+        $reqHost = $this->request->getUrl()->getHost();
+        if (!($reqPort = $this->request->getUrl()->getPort())) {
+            $reqPort = $secure? 443: 80;
+        }
+
+        if ($host = $this->request->getConfig('proxy_host')) {
+            if (!($port = $this->request->getConfig('proxy_port'))) {
+                throw new HTTP_Request2_Exception('Proxy port not provided');
+            }
+            $proxy = true;
+        } else {
+            $host  = $reqHost;
+            $port  = $reqPort;
+            $proxy = false;
+        }
+
+        if ($tunnel && !$proxy) {
+            throw new HTTP_Request2_Exception(
+                "Trying to perform CONNECT request without proxy"
+            );
+        }
+        if ($secure && !in_array('ssl', stream_get_transports())) {
+            throw new HTTP_Request2_Exception(
+                'Need OpenSSL support for https:// requests'
+            );
+        }
+
+        // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive
+        // connection token to a proxy server...
+        if ($proxy && !$secure &&
+            !empty($headers['connection']) && 'Keep-Alive' == $headers['connection']
+        ) {
+            $this->request->setHeader('connection');
+        }
+
+        $keepAlive = ('1.1' == $this->request->getConfig('protocol_version') &&
+                      empty($headers['connection'])) ||
+                     (!empty($headers['connection']) &&
+                      'Keep-Alive' == $headers['connection']);
+        $host = ((!$secure || $proxy)? 'tcp://': 'ssl://') . $host;
+
+        $options = array();
+        if ($secure || $tunnel) {
+            foreach ($this->request->getConfig() as $name => $value) {
+                if ('ssl_' == substr($name, 0, 4) && null !== $value) {
+                    if ('ssl_verify_host' == $name) {
+                        if ($value) {
+                            $options['CN_match'] = $reqHost;
+                        }
+                    } else {
+                        $options[substr($name, 4)] = $value;
+                    }
+                }
+            }
+            ksort($options);
+        }
+
+        // Changing SSL context options after connection is established does *not*
+        // work, we need a new connection if options change
+        $remote    = $host . ':' . $port;
+        $socketKey = $remote . (($secure && $proxy)? "->{$reqHost}:{$reqPort}": '') .
+                     (empty($options)? '': ':' . serialize($options));
+        unset($this->socket);
+
+        // We use persistent connections and have a connected socket?
+        // Ensure that the socket is still connected, see bug #16149
+        if ($keepAlive && !empty(self::$sockets[$socketKey]) &&
+            !feof(self::$sockets[$socketKey])
+        ) {
+            $this->socket =& self::$sockets[$socketKey];
+
+        } elseif ($secure && $proxy && !$tunnel) {
+            $this->establishTunnel();
+            $this->request->setLastEvent(
+                'connect', "ssl://{$reqHost}:{$reqPort} via {$host}:{$port}"
+            );
+            self::$sockets[$socketKey] =& $this->socket;
+
+        } else {
+            // Set SSL context options if doing HTTPS request or creating a tunnel
+            $context = stream_context_create();
+            foreach ($options as $name => $value) {
+                if (!stream_context_set_option($context, 'ssl', $name, $value)) {
+                    throw new HTTP_Request2_Exception(
+                        "Error setting SSL context option '{$name}'"
+                    );
+                }
+            }
+            $this->socket = @stream_socket_client(
+                $remote, $errno, $errstr,
+                $this->request->getConfig('connect_timeout'),
+                STREAM_CLIENT_CONNECT, $context
+            );
+            if (!$this->socket) {
+                throw new HTTP_Request2_Exception(
+                    "Unable to connect to {$remote}. Error #{$errno}: {$errstr}"
+                );
+            }
+            $this->request->setLastEvent('connect', $remote);
+            self::$sockets[$socketKey] =& $this->socket;
+        }
+        return $keepAlive;
+    }
+
+   /**
+    * Establishes a tunnel to a secure remote server via HTTP CONNECT request
+    *
+    * This method will fail if 'ssl_verify_peer' is enabled. Probably because PHP
+    * sees that we are connected to a proxy server (duh!) rather than the server
+    * that presents its certificate.
+    *
+    * @link     http://tools.ietf.org/html/rfc2817#section-5.2
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function establishTunnel()
+    {
+        $donor   = new self;
+        $connect = new HTTP_Request2(
+            $this->request->getUrl(), HTTP_Request2::METHOD_CONNECT,
+            array_merge($this->request->getConfig(),
+                        array('adapter' => $donor))
+        );
+        $response = $connect->send();
+        // Need any successful (2XX) response
+        if (200 > $response->getStatus() || 300 <= $response->getStatus()) {
+            throw new HTTP_Request2_Exception(
+                'Failed to connect via HTTPS proxy. Proxy response: ' .
+                $response->getStatus() . ' ' . $response->getReasonPhrase()
+            );
+        }
+        $this->socket = $donor->socket;
+
+        $modes = array(
+            STREAM_CRYPTO_METHOD_TLS_CLIENT,
+            STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
+            STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
+            STREAM_CRYPTO_METHOD_SSLv2_CLIENT
+        );
+
+        foreach ($modes as $mode) {
+            if (stream_socket_enable_crypto($this->socket, true, $mode)) {
+                return;
+            }
+        }
+        throw new HTTP_Request2_Exception(
+            'Failed to enable secure connection when connecting through proxy'
+        );
+    }
+
+   /**
+    * Checks whether current connection may be reused or should be closed
+    *
+    * @param    boolean                 whether connection could be persistent
+    *                                   in the first place
+    * @param    HTTP_Request2_Response  response object to check
+    * @return   boolean
+    */
+    protected function canKeepAlive($requestKeepAlive, HTTP_Request2_Response $response)
+    {
+        // Do not close socket on successful CONNECT request
+        if (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod() &&
+            200 <= $response->getStatus() && 300 > $response->getStatus()
+        ) {
+            return true;
+        }
+
+        $lengthKnown = 'chunked' == strtolower($response->getHeader('transfer-encoding')) ||
+                       null !== $response->getHeader('content-length');
+        $persistent  = 'keep-alive' == strtolower($response->getHeader('connection')) ||
+                       (null === $response->getHeader('connection') &&
+                        '1.1' == $response->getVersion());
+        return $requestKeepAlive && $lengthKnown && $persistent;
+    }
+
+   /**
+    * Disconnects from the remote server
+    */
+    protected function disconnect()
+    {
+        if (is_resource($this->socket)) {
+            fclose($this->socket);
+            $this->socket = null;
+            $this->request->setLastEvent('disconnect');
+        }
+    }
+
+   /**
+    * Handles HTTP redirection
+    *
+    * This method will throw an Exception if redirect to a non-HTTP(S) location
+    * is attempted, also if number of redirects performed already is equal to
+    * 'max_redirects' configuration parameter.
+    *
+    * @param    HTTP_Request2               Original request
+    * @param    HTTP_Request2_Response      Response containing redirect
+    * @return   HTTP_Request2_Response      Response from a new location
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function handleRedirect(HTTP_Request2 $request,
+                                      HTTP_Request2_Response $response)
+    {
+        if (is_null($this->redirectCountdown)) {
+            $this->redirectCountdown = $request->getConfig('max_redirects');
+        }
+        if (0 == $this->redirectCountdown) {
+            // Copying cURL behaviour
+            throw new HTTP_Request2_Exception(
+                'Maximum (' . $request->getConfig('max_redirects') . ') redirects followed'
+            );
+        }
+        $redirectUrl = new Net_URL2(
+            $response->getHeader('location'),
+            array(Net_URL2::OPTION_USE_BRACKETS => $request->getConfig('use_brackets'))
+        );
+        // refuse non-HTTP redirect
+        if ($redirectUrl->isAbsolute()
+            && !in_array($redirectUrl->getScheme(), array('http', 'https'))
+        ) {
+            throw new HTTP_Request2_Exception(
+                'Refusing to redirect to a non-HTTP URL ' . $redirectUrl->__toString()
+            );
+        }
+        // Theoretically URL should be absolute (see http://tools.ietf.org/html/rfc2616#section-14.30),
+        // but in practice it is often not
+        if (!$redirectUrl->isAbsolute()) {
+            $redirectUrl = $request->getUrl()->resolve($redirectUrl);
+        }
+        $redirect = clone $request;
+        $redirect->setUrl($redirectUrl);
+        if (303 == $response->getStatus() || (!$request->getConfig('strict_redirects')
+             && in_array($response->getStatus(), array(301, 302)))
+        ) {
+            $redirect->setMethod(HTTP_Request2::METHOD_GET);
+            $redirect->setBody('');
+        }
+
+        if (0 < $this->redirectCountdown) {
+            $this->redirectCountdown--;
+        }
+        return $this->sendRequest($redirect);
+    }
+
+   /**
+    * Checks whether another request should be performed with server digest auth
+    *
+    * Several conditions should be satisfied for it to return true:
+    *   - response status should be 401
+    *   - auth credentials should be set in the request object
+    *   - response should contain WWW-Authenticate header with digest challenge
+    *   - there is either no challenge stored for this URL or new challenge
+    *     contains stale=true parameter (in other case we probably just failed
+    *     due to invalid username / password)
+    *
+    * The method stores challenge values in $challenges static property
+    *
+    * @param    HTTP_Request2_Response  response to check
+    * @return   boolean whether another request should be performed
+    * @throws   HTTP_Request2_Exception in case of unsupported challenge parameters
+    */
+    protected function shouldUseServerDigestAuth(HTTP_Request2_Response $response)
+    {
+        // no sense repeating a request if we don't have credentials
+        if (401 != $response->getStatus() || !$this->request->getAuth()) {
+            return false;
+        }
+        if (!$challenge = $this->parseDigestChallenge($response->getHeader('www-authenticate'))) {
+            return false;
+        }
+
+        $url    = $this->request->getUrl();
+        $scheme = $url->getScheme();
+        $host   = $scheme . '://' . $url->getHost();
+        if ($port = $url->getPort()) {
+            if ((0 == strcasecmp($scheme, 'http') && 80 != $port) ||
+                (0 == strcasecmp($scheme, 'https') && 443 != $port)
+            ) {
+                $host .= ':' . $port;
+            }
+        }
+
+        if (!empty($challenge['domain'])) {
+            $prefixes = array();
+            foreach (preg_split('/\\s+/', $challenge['domain']) as $prefix) {
+                // don't bother with different servers
+                if ('/' == substr($prefix, 0, 1)) {
+                    $prefixes[] = $host . $prefix;
+                }
+            }
+        }
+        if (empty($prefixes)) {
+            $prefixes = array($host . '/');
+        }
+
+        $ret = true;
+        foreach ($prefixes as $prefix) {
+            if (!empty(self::$challenges[$prefix]) &&
+                (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
+            ) {
+                // probably credentials are invalid
+                $ret = false;
+            }
+            self::$challenges[$prefix] =& $challenge;
+        }
+        return $ret;
+    }
+
+   /**
+    * Checks whether another request should be performed with proxy digest auth
+    *
+    * Several conditions should be satisfied for it to return true:
+    *   - response status should be 407
+    *   - proxy auth credentials should be set in the request object
+    *   - response should contain Proxy-Authenticate header with digest challenge
+    *   - there is either no challenge stored for this proxy or new challenge
+    *     contains stale=true parameter (in other case we probably just failed
+    *     due to invalid username / password)
+    *
+    * The method stores challenge values in $challenges static property
+    *
+    * @param    HTTP_Request2_Response  response to check
+    * @return   boolean whether another request should be performed
+    * @throws   HTTP_Request2_Exception in case of unsupported challenge parameters
+    */
+    protected function shouldUseProxyDigestAuth(HTTP_Request2_Response $response)
+    {
+        if (407 != $response->getStatus() || !$this->request->getConfig('proxy_user')) {
+            return false;
+        }
+        if (!($challenge = $this->parseDigestChallenge($response->getHeader('proxy-authenticate')))) {
+            return false;
+        }
+
+        $key = 'proxy://' . $this->request->getConfig('proxy_host') .
+               ':' . $this->request->getConfig('proxy_port');
+
+        if (!empty(self::$challenges[$key]) &&
+            (empty($challenge['stale']) || strcasecmp('true', $challenge['stale']))
+        ) {
+            $ret = false;
+        } else {
+            $ret = true;
+        }
+        self::$challenges[$key] = $challenge;
+        return $ret;
+    }
+
+   /**
+    * Extracts digest method challenge from (WWW|Proxy)-Authenticate header value
+    *
+    * There is a problem with implementation of RFC 2617: several of the parameters
+    * are defined as quoted-string there and thus may contain backslash escaped
+    * double quotes (RFC 2616, section 2.2). However, RFC 2617 defines unq(X) as
+    * just value of quoted-string X without surrounding quotes, it doesn't speak
+    * about removing backslash escaping.
+    *
+    * Now realm parameter is user-defined and human-readable, strange things
+    * happen when it contains quotes:
+    *   - Apache allows quotes in realm, but apparently uses realm value without
+    *     backslashes for digest computation
+    *   - Squid allows (manually escaped) quotes there, but it is impossible to
+    *     authorize with either escaped or unescaped quotes used in digest,
+    *     probably it can't parse the response (?)
+    *   - Both IE and Firefox display realm value with backslashes in
+    *     the password popup and apparently use the same value for digest
+    *
+    * HTTP_Request2 follows IE and Firefox (and hopefully RFC 2617) in
+    * quoted-string handling, unfortunately that means failure to authorize
+    * sometimes
+    *
+    * @param    string  value of WWW-Authenticate or Proxy-Authenticate header
+    * @return   mixed   associative array with challenge parameters, false if
+    *                   no challenge is present in header value
+    * @throws   HTTP_Request2_Exception in case of unsupported challenge parameters
+    */
+    protected function parseDigestChallenge($headerValue)
+    {
+        $authParam   = '(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
+                       self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')';
+        $challenge   = "!(?<=^|\\s|,)Digest ({$authParam}\\s*(,\\s*|$))+!";
+        if (!preg_match($challenge, $headerValue, $matches)) {
+            return false;
+        }
+
+        preg_match_all('!' . $authParam . '!', $matches[0], $params);
+        $paramsAry   = array();
+        $knownParams = array('realm', 'domain', 'nonce', 'opaque', 'stale',
+                             'algorithm', 'qop');
+        for ($i = 0; $i < count($params[0]); $i++) {
+            // section 3.2.1: Any unrecognized directive MUST be ignored.
+            if (in_array($params[1][$i], $knownParams)) {
+                if ('"' == substr($params[2][$i], 0, 1)) {
+                    $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
+                } else {
+                    $paramsAry[$params[1][$i]] = $params[2][$i];
+                }
+            }
+        }
+        // we only support qop=auth
+        if (!empty($paramsAry['qop']) &&
+            !in_array('auth', array_map('trim', explode(',', $paramsAry['qop'])))
+        ) {
+            throw new HTTP_Request2_Exception(
+                "Only 'auth' qop is currently supported in digest authentication, " .
+                "server requested '{$paramsAry['qop']}'"
+            );
+        }
+        // we only support algorithm=MD5
+        if (!empty($paramsAry['algorithm']) && 'MD5' != $paramsAry['algorithm']) {
+            throw new HTTP_Request2_Exception(
+                "Only 'MD5' algorithm is currently supported in digest authentication, " .
+                "server requested '{$paramsAry['algorithm']}'"
+            );
+        }
+
+        return $paramsAry;
+    }
+
+   /**
+    * Parses [Proxy-]Authentication-Info header value and updates challenge
+    *
+    * @param    array   challenge to update
+    * @param    string  value of [Proxy-]Authentication-Info header
+    * @todo     validate server rspauth response
+    */
+    protected function updateChallenge(&$challenge, $headerValue)
+    {
+        $authParam   = '!(' . self::REGEXP_TOKEN . ')\\s*=\\s*(' .
+                       self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')!';
+        $paramsAry   = array();
+
+        preg_match_all($authParam, $headerValue, $params);
+        for ($i = 0; $i < count($params[0]); $i++) {
+            if ('"' == substr($params[2][$i], 0, 1)) {
+                $paramsAry[$params[1][$i]] = substr($params[2][$i], 1, -1);
+            } else {
+                $paramsAry[$params[1][$i]] = $params[2][$i];
+            }
+        }
+        // for now, just update the nonce value
+        if (!empty($paramsAry['nextnonce'])) {
+            $challenge['nonce'] = $paramsAry['nextnonce'];
+            $challenge['nc']    = 1;
+        }
+    }
+
+   /**
+    * Creates a value for [Proxy-]Authorization header when using digest authentication
+    *
+    * @param    string  user name
+    * @param    string  password
+    * @param    string  request URL
+    * @param    array   digest challenge parameters
+    * @return   string  value of [Proxy-]Authorization request header
+    * @link     http://tools.ietf.org/html/rfc2617#section-3.2.2
+    */
+    protected function createDigestResponse($user, $password, $url, &$challenge)
+    {
+        if (false !== ($q = strpos($url, '?')) &&
+            $this->request->getConfig('digest_compat_ie')
+        ) {
+            $url = substr($url, 0, $q);
+        }
+
+        $a1 = md5($user . ':' . $challenge['realm'] . ':' . $password);
+        $a2 = md5($this->request->getMethod() . ':' . $url);
+
+        if (empty($challenge['qop'])) {
+            $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $a2);
+        } else {
+            $challenge['cnonce'] = 'Req2.' . rand();
+            if (empty($challenge['nc'])) {
+                $challenge['nc'] = 1;
+            }
+            $nc     = sprintf('%08x', $challenge['nc']++);
+            $digest = md5($a1 . ':' . $challenge['nonce'] . ':' . $nc . ':' .
+                          $challenge['cnonce'] . ':auth:' . $a2);
+        }
+        return 'Digest username="' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $user) . '", ' .
+               'realm="' . $challenge['realm'] . '", ' .
+               'nonce="' . $challenge['nonce'] . '", ' .
+               'uri="' . $url . '", ' .
+               'response="' . $digest . '"' .
+               (!empty($challenge['opaque'])?
+                ', opaque="' . $challenge['opaque'] . '"':
+                '') .
+               (!empty($challenge['qop'])?
+                ', qop="auth", nc=' . $nc . ', cnonce="' . $challenge['cnonce'] . '"':
+                '');
+    }
+
+   /**
+    * Adds 'Authorization' header (if needed) to request headers array
+    *
+    * @param    array   request headers
+    * @param    string  request host (needed for digest authentication)
+    * @param    string  request URL (needed for digest authentication)
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function addAuthorizationHeader(&$headers, $requestHost, $requestUrl)
+    {
+        if (!($auth = $this->request->getAuth())) {
+            return;
+        }
+        switch ($auth['scheme']) {
+            case HTTP_Request2::AUTH_BASIC:
+                $headers['authorization'] =
+                    'Basic ' . base64_encode($auth['user'] . ':' . $auth['password']);
+                break;
+
+            case HTTP_Request2::AUTH_DIGEST:
+                unset($this->serverChallenge);
+                $fullUrl = ('/' == $requestUrl[0])?
+                           $this->request->getUrl()->getScheme() . '://' .
+                            $requestHost . $requestUrl:
+                           $requestUrl;
+                foreach (array_keys(self::$challenges) as $key) {
+                    if ($key == substr($fullUrl, 0, strlen($key))) {
+                        $headers['authorization'] = $this->createDigestResponse(
+                            $auth['user'], $auth['password'],
+                            $requestUrl, self::$challenges[$key]
+                        );
+                        $this->serverChallenge =& self::$challenges[$key];
+                        break;
+                    }
+                }
+                break;
+
+            default:
+                throw new HTTP_Request2_Exception(
+                    "Unknown HTTP authentication scheme '{$auth['scheme']}'"
+                );
+        }
+    }
+
+   /**
+    * Adds 'Proxy-Authorization' header (if needed) to request headers array
+    *
+    * @param    array   request headers
+    * @param    string  request URL (needed for digest authentication)
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function addProxyAuthorizationHeader(&$headers, $requestUrl)
+    {
+        if (!$this->request->getConfig('proxy_host') ||
+            !($user = $this->request->getConfig('proxy_user')) ||
+            (0 == strcasecmp('https', $this->request->getUrl()->getScheme()) &&
+             HTTP_Request2::METHOD_CONNECT != $this->request->getMethod())
+        ) {
+            return;
+        }
+
+        $password = $this->request->getConfig('proxy_password');
+        switch ($this->request->getConfig('proxy_auth_scheme')) {
+            case HTTP_Request2::AUTH_BASIC:
+                $headers['proxy-authorization'] =
+                    'Basic ' . base64_encode($user . ':' . $password);
+                break;
+
+            case HTTP_Request2::AUTH_DIGEST:
+                unset($this->proxyChallenge);
+                $proxyUrl = 'proxy://' . $this->request->getConfig('proxy_host') .
+                            ':' . $this->request->getConfig('proxy_port');
+                if (!empty(self::$challenges[$proxyUrl])) {
+                    $headers['proxy-authorization'] = $this->createDigestResponse(
+                        $user, $password,
+                        $requestUrl, self::$challenges[$proxyUrl]
+                    );
+                    $this->proxyChallenge =& self::$challenges[$proxyUrl];
+                }
+                break;
+
+            default:
+                throw new HTTP_Request2_Exception(
+                    "Unknown HTTP authentication scheme '" .
+                    $this->request->getConfig('proxy_auth_scheme') . "'"
+                );
+        }
+    }
+
+
+   /**
+    * Creates the string with the Request-Line and request headers
+    *
+    * @return   string
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function prepareHeaders()
+    {
+        $headers = $this->request->getHeaders();
+        $url     = $this->request->getUrl();
+        $connect = HTTP_Request2::METHOD_CONNECT == $this->request->getMethod();
+        $host    = $url->getHost();
+
+        $defaultPort = 0 == strcasecmp($url->getScheme(), 'https')? 443: 80;
+        if (($port = $url->getPort()) && $port != $defaultPort || $connect) {
+            $host .= ':' . (empty($port)? $defaultPort: $port);
+        }
+        // Do not overwrite explicitly set 'Host' header, see bug #16146
+        if (!isset($headers['host'])) {
+            $headers['host'] = $host;
+        }
+
+        if ($connect) {
+            $requestUrl = $host;
+
+        } else {
+            if (!$this->request->getConfig('proxy_host') ||
+                0 == strcasecmp($url->getScheme(), 'https')
+            ) {
+                $requestUrl = '';
+            } else {
+                $requestUrl = $url->getScheme() . '://' . $host;
+            }
+            $path        = $url->getPath();
+            $query       = $url->getQuery();
+            $requestUrl .= (empty($path)? '/': $path) . (empty($query)? '': '?' . $query);
+        }
+
+        if ('1.1' == $this->request->getConfig('protocol_version') &&
+            extension_loaded('zlib') && !isset($headers['accept-encoding'])
+        ) {
+            $headers['accept-encoding'] = 'gzip, deflate';
+        }
+
+        $this->addAuthorizationHeader($headers, $host, $requestUrl);
+        $this->addProxyAuthorizationHeader($headers, $requestUrl);
+        $this->calculateRequestLength($headers);
+
+        $headersStr = $this->request->getMethod() . ' ' . $requestUrl . ' HTTP/' .
+                      $this->request->getConfig('protocol_version') . "\r\n";
+        foreach ($headers as $name => $value) {
+            $canonicalName = implode('-', array_map('ucfirst', explode('-', $name)));
+            $headersStr   .= $canonicalName . ': ' . $value . "\r\n";
+        }
+        return $headersStr . "\r\n";
+    }
+
+   /**
+    * Sends the request body
+    *
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function writeBody()
+    {
+        if (in_array($this->request->getMethod(), self::$bodyDisallowed) ||
+            0 == $this->contentLength
+        ) {
+            return;
+        }
+
+        $position   = 0;
+        $bufferSize = $this->request->getConfig('buffer_size');
+        while ($position < $this->contentLength) {
+            if (is_string($this->requestBody)) {
+                $str = substr($this->requestBody, $position, $bufferSize);
+            } elseif (is_resource($this->requestBody)) {
+                $str = fread($this->requestBody, $bufferSize);
+            } else {
+                $str = $this->requestBody->read($bufferSize);
+            }
+            if (false === @fwrite($this->socket, $str, strlen($str))) {
+                throw new HTTP_Request2_Exception('Error writing request');
+            }
+            // Provide the length of written string to the observer, request #7630
+            $this->request->setLastEvent('sentBodyPart', strlen($str));
+            $position += strlen($str);
+        }
+    }
+
+   /**
+    * Reads the remote server's response
+    *
+    * @return   HTTP_Request2_Response
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function readResponse()
+    {
+        $bufferSize = $this->request->getConfig('buffer_size');
+
+        do {
+            $response = new HTTP_Request2_Response($this->readLine($bufferSize), true);
+            do {
+                $headerLine = $this->readLine($bufferSize);
+                $response->parseHeaderLine($headerLine);
+            } while ('' != $headerLine);
+        } while (in_array($response->getStatus(), array(100, 101)));
+
+        $this->request->setLastEvent('receivedHeaders', $response);
+
+        // No body possible in such responses
+        if (HTTP_Request2::METHOD_HEAD == $this->request->getMethod() ||
+            (HTTP_Request2::METHOD_CONNECT == $this->request->getMethod() &&
+             200 <= $response->getStatus() && 300 > $response->getStatus()) ||
+            in_array($response->getStatus(), array(204, 304))
+        ) {
+            return $response;
+        }
+
+        $chunked = 'chunked' == $response->getHeader('transfer-encoding');
+        $length  = $response->getHeader('content-length');
+        $hasBody = false;
+        if ($chunked || null === $length || 0 < intval($length)) {
+            // RFC 2616, section 4.4:
+            // 3. ... If a message is received with both a
+            // Transfer-Encoding header field and a Content-Length header field,
+            // the latter MUST be ignored.
+            $toRead = ($chunked || null === $length)? null: $length;
+            $this->chunkLength = 0;
+
+            while (!feof($this->socket) && (is_null($toRead) || 0 < $toRead)) {
+                if ($chunked) {
+                    $data = $this->readChunked($bufferSize);
+                } elseif (is_null($toRead)) {
+                    $data = $this->fread($bufferSize);
+                } else {
+                    $data    = $this->fread(min($toRead, $bufferSize));
+                    $toRead -= strlen($data);
+                }
+                if ('' == $data && (!$this->chunkLength || feof($this->socket))) {
+                    break;
+                }
+
+                $hasBody = true;
+                if ($this->request->getConfig('store_body')) {
+                    $response->appendBody($data);
+                }
+                if (!in_array($response->getHeader('content-encoding'), array('identity', null))) {
+                    $this->request->setLastEvent('receivedEncodedBodyPart', $data);
+                } else {
+                    $this->request->setLastEvent('receivedBodyPart', $data);
+                }
+            }
+        }
+
+        if ($hasBody) {
+            $this->request->setLastEvent('receivedBody', $response);
+        }
+        return $response;
+    }
+
+   /**
+    * Reads until either the end of the socket or a newline, whichever comes first
+    *
+    * Strips the trailing newline from the returned data, handles global
+    * request timeout. Method idea borrowed from Net_Socket PEAR package.
+    *
+    * @param    int     buffer size to use for reading
+    * @return   Available data up to the newline (not including newline)
+    * @throws   HTTP_Request2_Exception     In case of timeout
+    */
+    protected function readLine($bufferSize)
+    {
+        $line = '';
+        while (!feof($this->socket)) {
+            if ($this->deadline) {
+                stream_set_timeout($this->socket, max($this->deadline - time(), 1));
+            }
+            $line .= @fgets($this->socket, $bufferSize);
+            $info  = stream_get_meta_data($this->socket);
+            if ($info['timed_out'] || $this->deadline && time() > $this->deadline) {
+                $reason = $this->deadline
+                          ? 'after ' . $this->request->getConfig('timeout') . ' second(s)'
+                          : 'due to default_socket_timeout php.ini setting';
+                throw new HTTP_Request2_Exception("Request timed out {$reason}");
+            }
+            if (substr($line, -1) == "\n") {
+                return rtrim($line, "\r\n");
+            }
+        }
+        return $line;
+    }
+
+   /**
+    * Wrapper around fread(), handles global request timeout
+    *
+    * @param    int     Reads up to this number of bytes
+    * @return   Data read from socket
+    * @throws   HTTP_Request2_Exception     In case of timeout
+    */
+    protected function fread($length)
+    {
+        if ($this->deadline) {
+            stream_set_timeout($this->socket, max($this->deadline - time(), 1));
+        }
+        $data = fread($this->socket, $length);
+        $info = stream_get_meta_data($this->socket);
+        if ($info['timed_out'] || $this->deadline && time() > $this->deadline) {
+            $reason = $this->deadline
+                      ? 'after ' . $this->request->getConfig('timeout') . ' second(s)'
+                      : 'due to default_socket_timeout php.ini setting';
+            throw new HTTP_Request2_Exception("Request timed out {$reason}");
+        }
+        return $data;
+    }
+
+   /**
+    * Reads a part of response body encoded with chunked Transfer-Encoding
+    *
+    * @param    int     buffer size to use for reading
+    * @return   string
+    * @throws   HTTP_Request2_Exception
+    */
+    protected function readChunked($bufferSize)
+    {
+        // at start of the next chunk?
+        if (0 == $this->chunkLength) {
+            $line = $this->readLine($bufferSize);
+            if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
+                throw new HTTP_Request2_Exception(
+                    "Cannot decode chunked response, invalid chunk length '{$line}'"
+                );
+            } else {
+                $this->chunkLength = hexdec($matches[1]);
+                // Chunk with zero length indicates the end
+                if (0 == $this->chunkLength) {
+                    $this->readLine($bufferSize);
+                    return '';
+                }
+            }
+        }
+        $data = $this->fread(min($this->chunkLength, $bufferSize));
+        $this->chunkLength -= strlen($data);
+        if (0 == $this->chunkLength) {
+            $this->readLine($bufferSize); // Trailing CRLF
+        }
+        return $data;
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/php/HTTP/Request2/Exception.php b/lib/php/HTTP/Request2/Exception.php
new file mode 100644
index 0000000000000000000000000000000000000000..d86a14401f97693fc82f2ce3750288af6a43382b
--- /dev/null
+++ b/lib/php/HTTP/Request2/Exception.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Exception class for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Exception.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Base class for exceptions in PEAR
+ */
+require_once 'PEAR/Exception.php';
+
+/**
+ * Exception class for HTTP_Request2 package
+ *
+ * Such a class is required by the Exception RFC:
+ * http://pear.php.net/pepr/pepr-proposal-show.php?id=132
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @version    Release: 0.5.1
+ */
+class HTTP_Request2_Exception extends PEAR_Exception
+{
+}
+?>
\ No newline at end of file
diff --git a/lib/php/HTTP/Request2/MultipartBody.php b/lib/php/HTTP/Request2/MultipartBody.php
new file mode 100644
index 0000000000000000000000000000000000000000..efc2e254ab889025729e5089bce0555b92d3bb39
--- /dev/null
+++ b/lib/php/HTTP/Request2/MultipartBody.php
@@ -0,0 +1,274 @@
+<?php
+/**
+ * Helper class for building multipart/form-data request body
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: MultipartBody.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class for building multipart/form-data request body
+ *
+ * The class helps to reduce memory consumption by streaming large file uploads
+ * from disk, it also allows monitoring of upload progress (see request #7630)
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @version    Release: 0.5.1
+ * @link       http://tools.ietf.org/html/rfc1867
+ */
+class HTTP_Request2_MultipartBody
+{
+   /**
+    * MIME boundary
+    * @var  string
+    */
+    private $_boundary;
+
+   /**
+    * Form parameters added via {@link HTTP_Request2::addPostParameter()}
+    * @var  array
+    */
+    private $_params = array();
+
+   /**
+    * File uploads added via {@link HTTP_Request2::addUpload()}
+    * @var  array
+    */
+    private $_uploads = array();
+
+   /**
+    * Header for parts with parameters
+    * @var  string
+    */
+    private $_headerParam = "--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n";
+
+   /**
+    * Header for parts with uploads
+    * @var  string
+    */
+    private $_headerUpload = "--%s\r\nContent-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n";
+
+   /**
+    * Current position in parameter and upload arrays
+    *
+    * First number is index of "current" part, second number is position within
+    * "current" part
+    *
+    * @var  array
+    */
+    private $_pos = array(0, 0);
+
+
+   /**
+    * Constructor. Sets the arrays with POST data.
+    *
+    * @param    array   values of form fields set via {@link HTTP_Request2::addPostParameter()}
+    * @param    array   file uploads set via {@link HTTP_Request2::addUpload()}
+    * @param    bool    whether to append brackets to array variable names
+    */
+    public function __construct(array $params, array $uploads, $useBrackets = true)
+    {
+        $this->_params = self::_flattenArray('', $params, $useBrackets);
+        foreach ($uploads as $fieldName => $f) {
+            if (!is_array($f['fp'])) {
+                $this->_uploads[] = $f + array('name' => $fieldName);
+            } else {
+                for ($i = 0; $i < count($f['fp']); $i++) {
+                    $upload = array(
+                        'name' => ($useBrackets? $fieldName . '[' . $i . ']': $fieldName)
+                    );
+                    foreach (array('fp', 'filename', 'size', 'type') as $key) {
+                        $upload[$key] = $f[$key][$i];
+                    }
+                    $this->_uploads[] = $upload;
+                }
+            }
+        }
+    }
+
+   /**
+    * Returns the length of the body to use in Content-Length header
+    *
+    * @return   integer
+    */
+    public function getLength()
+    {
+        $boundaryLength     = strlen($this->getBoundary());
+        $headerParamLength  = strlen($this->_headerParam) - 4 + $boundaryLength;
+        $headerUploadLength = strlen($this->_headerUpload) - 8 + $boundaryLength;
+        $length             = $boundaryLength + 6;
+        foreach ($this->_params as $p) {
+            $length += $headerParamLength + strlen($p[0]) + strlen($p[1]) + 2;
+        }
+        foreach ($this->_uploads as $u) {
+            $length += $headerUploadLength + strlen($u['name']) + strlen($u['type']) +
+                       strlen($u['filename']) + $u['size'] + 2;
+        }
+        return $length;
+    }
+
+   /**
+    * Returns the boundary to use in Content-Type header
+    *
+    * @return   string
+    */
+    public function getBoundary()
+    {
+        if (empty($this->_boundary)) {
+            $this->_boundary = '--' . md5('PEAR-HTTP_Request2-' . microtime());
+        }
+        return $this->_boundary;
+    }
+
+   /**
+    * Returns next chunk of request body
+    *
+    * @param    integer Amount of bytes to read
+    * @return   string  Up to $length bytes of data, empty string if at end
+    */
+    public function read($length)
+    {
+        $ret         = '';
+        $boundary    = $this->getBoundary();
+        $paramCount  = count($this->_params);
+        $uploadCount = count($this->_uploads);
+        while ($length > 0 && $this->_pos[0] <= $paramCount + $uploadCount) {
+            $oldLength = $length;
+            if ($this->_pos[0] < $paramCount) {
+                $param = sprintf($this->_headerParam, $boundary,
+                                 $this->_params[$this->_pos[0]][0]) .
+                         $this->_params[$this->_pos[0]][1] . "\r\n";
+                $ret    .= substr($param, $this->_pos[1], $length);
+                $length -= min(strlen($param) - $this->_pos[1], $length);
+
+            } elseif ($this->_pos[0] < $paramCount + $uploadCount) {
+                $pos    = $this->_pos[0] - $paramCount;
+                $header = sprintf($this->_headerUpload, $boundary,
+                                  $this->_uploads[$pos]['name'],
+                                  $this->_uploads[$pos]['filename'],
+                                  $this->_uploads[$pos]['type']);
+                if ($this->_pos[1] < strlen($header)) {
+                    $ret    .= substr($header, $this->_pos[1], $length);
+                    $length -= min(strlen($header) - $this->_pos[1], $length);
+                }
+                $filePos  = max(0, $this->_pos[1] - strlen($header));
+                if ($length > 0 && $filePos < $this->_uploads[$pos]['size']) {
+                    $ret     .= fread($this->_uploads[$pos]['fp'], $length);
+                    $length  -= min($length, $this->_uploads[$pos]['size'] - $filePos);
+                }
+                if ($length > 0) {
+                    $start   = $this->_pos[1] + ($oldLength - $length) -
+                               strlen($header) - $this->_uploads[$pos]['size'];
+                    $ret    .= substr("\r\n", $start, $length);
+                    $length -= min(2 - $start, $length);
+                }
+
+            } else {
+                $closing  = '--' . $boundary . "--\r\n";
+                $ret     .= substr($closing, $this->_pos[1], $length);
+                $length  -= min(strlen($closing) - $this->_pos[1], $length);
+            }
+            if ($length > 0) {
+                $this->_pos     = array($this->_pos[0] + 1, 0);
+            } else {
+                $this->_pos[1] += $oldLength;
+            }
+        }
+        return $ret;
+    }
+
+   /**
+    * Sets the current position to the start of the body
+    *
+    * This allows reusing the same body in another request
+    */
+    public function rewind()
+    {
+        $this->_pos = array(0, 0);
+        foreach ($this->_uploads as $u) {
+            rewind($u['fp']);
+        }
+    }
+
+   /**
+    * Returns the body as string
+    *
+    * Note that it reads all file uploads into memory so it is a good idea not
+    * to use this method with large file uploads and rely on read() instead.
+    *
+    * @return   string
+    */
+    public function __toString()
+    {
+        $this->rewind();
+        return $this->read($this->getLength());
+    }
+
+
+   /**
+    * Helper function to change the (probably multidimensional) associative array
+    * into the simple one.
+    *
+    * @param    string  name for item
+    * @param    mixed   item's values
+    * @param    bool    whether to append [] to array variables' names
+    * @return   array   array with the following items: array('item name', 'item value');
+    */
+    private static function _flattenArray($name, $values, $useBrackets)
+    {
+        if (!is_array($values)) {
+            return array(array($name, $values));
+        } else {
+            $ret = array();
+            foreach ($values as $k => $v) {
+                if (empty($name)) {
+                    $newName = $k;
+                } elseif ($useBrackets) {
+                    $newName = $name . '[' . $k . ']';
+                } else {
+                    $newName = $name;
+                }
+                $ret = array_merge($ret, self::_flattenArray($newName, $v, $useBrackets));
+            }
+            return $ret;
+        }
+    }
+}
+?>
diff --git a/lib/php/HTTP/Request2/Observer/Log.php b/lib/php/HTTP/Request2/Observer/Log.php
new file mode 100644
index 0000000000000000000000000000000000000000..e7a064b9603cb040a98342e56e6cd6efe56896c0
--- /dev/null
+++ b/lib/php/HTTP/Request2/Observer/Log.php
@@ -0,0 +1,215 @@
+<?php
+/**
+ * An observer useful for debugging / testing.
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category HTTP
+ * @package  HTTP_Request2
+ * @author   David Jean Louis <izi@php.net>
+ * @author   Alexey Borzov <avb@php.net>
+ * @license  http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version  SVN: $Id: Log.php 290743 2009-11-14 13:27:49Z avb $
+ * @link     http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Exception class for HTTP_Request2 package
+ */
+require_once 'HTTP/Request2/Exception.php';
+
+/**
+ * A debug observer useful for debugging / testing.
+ *
+ * This observer logs to a log target data corresponding to the various request
+ * and response events, it logs by default to php://output but can be configured
+ * to log to a file or via the PEAR Log package.
+ *
+ * A simple example:
+ * <code>
+ * require_once 'HTTP/Request2.php';
+ * require_once 'HTTP/Request2/Observer/Log.php';
+ *
+ * $request  = new HTTP_Request2('http://www.example.com');
+ * $observer = new HTTP_Request2_Observer_Log();
+ * $request->attach($observer);
+ * $request->send();
+ * </code>
+ *
+ * A more complex example with PEAR Log:
+ * <code>
+ * require_once 'HTTP/Request2.php';
+ * require_once 'HTTP/Request2/Observer/Log.php';
+ * require_once 'Log.php';
+ *
+ * $request  = new HTTP_Request2('http://www.example.com');
+ * // we want to log with PEAR log
+ * $observer = new HTTP_Request2_Observer_Log(Log::factory('console'));
+ *
+ * // we only want to log received headers
+ * $observer->events = array('receivedHeaders');
+ *
+ * $request->attach($observer);
+ * $request->send();
+ * </code>
+ *
+ * @category HTTP
+ * @package  HTTP_Request2
+ * @author   David Jean Louis <izi@php.net>
+ * @author   Alexey Borzov <avb@php.net>
+ * @license  http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version  Release: 0.5.1
+ * @link     http://pear.php.net/package/HTTP_Request2
+ */
+class HTTP_Request2_Observer_Log implements SplObserver
+{
+    // properties {{{
+
+    /**
+     * The log target, it can be a a resource or a PEAR Log instance.
+     *
+     * @var resource|Log $target
+     */
+    protected $target = null;
+
+    /**
+     * The events to log.
+     *
+     * @var array $events
+     */
+    public $events = array(
+        'connect',
+        'sentHeaders',
+        'sentBodyPart',
+        'receivedHeaders',
+        'receivedBody',
+        'disconnect',
+    );
+
+    // }}}
+    // __construct() {{{
+
+    /**
+     * Constructor.
+     *
+     * @param mixed $target Can be a file path (default: php://output), a resource,
+     *                      or an instance of the PEAR Log class.
+     * @param array $events Array of events to listen to (default: all events)
+     *
+     * @return void
+     */
+    public function __construct($target = 'php://output', array $events = array())
+    {
+        if (!empty($events)) {
+            $this->events = $events;
+        }
+        if (is_resource($target) || $target instanceof Log) {
+            $this->target = $target;
+        } elseif (false === ($this->target = @fopen($target, 'w'))) {
+            throw new HTTP_Request2_Exception("Unable to open '{$target}'");
+        }
+    }
+
+    // }}}
+    // update() {{{
+
+    /**
+     * Called when the request notifies us of an event.
+     *
+     * @param HTTP_Request2 $subject The HTTP_Request2 instance
+     *
+     * @return void
+     */
+    public function update(SplSubject $subject)
+    {
+        $event = $subject->getLastEvent();
+        if (!in_array($event['name'], $this->events)) {
+            return;
+        }
+
+        switch ($event['name']) {
+        case 'connect':
+            $this->log('* Connected to ' . $event['data']);
+            break;
+        case 'sentHeaders':
+            $headers = explode("\r\n", $event['data']);
+            array_pop($headers);
+            foreach ($headers as $header) {
+                $this->log('> ' . $header);
+            }
+            break;
+        case 'sentBodyPart':
+            $this->log('> ' . $event['data'] . ' byte(s) sent');
+            break;
+        case 'receivedHeaders':
+            $this->log(sprintf('< HTTP/%s %s %s',
+                $event['data']->getVersion(),
+                $event['data']->getStatus(),
+                $event['data']->getReasonPhrase()));
+            $headers = $event['data']->getHeader();
+            foreach ($headers as $key => $val) {
+                $this->log('< ' . $key . ': ' . $val);
+            }
+            $this->log('< ');
+            break;
+        case 'receivedBody':
+            $this->log($event['data']->getBody());
+            break;
+        case 'disconnect':
+            $this->log('* Disconnected');
+            break;
+        }
+    }
+
+    // }}}
+    // log() {{{
+
+    /**
+     * Logs the given message to the configured target.
+     *
+     * @param string $message Message to display
+     *
+     * @return void
+     */
+    protected function log($message)
+    {
+        if ($this->target instanceof Log) {
+            $this->target->debug($message);
+        } elseif (is_resource($this->target)) {
+            fwrite($this->target, $message . "\r\n");
+        }
+    }
+
+    // }}}
+}
+
+?>
\ No newline at end of file
diff --git a/lib/php/HTTP/Request2/Response.php b/lib/php/HTTP/Request2/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..217fbdf6b913e3fa29465539e86763b8a4d85a62
--- /dev/null
+++ b/lib/php/HTTP/Request2/Response.php
@@ -0,0 +1,559 @@
+<?php
+/**
+ * Class representing a HTTP response
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Response.php 290520 2009-11-11 20:09:42Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Exception class for HTTP_Request2 package
+ */
+require_once 'HTTP/Request2/Exception.php';
+
+/**
+ * Class representing a HTTP response
+ *
+ * The class is designed to be used in "streaming" scenario, building the
+ * response as it is being received:
+ * <code>
+ * $statusLine = read_status_line();
+ * $response = new HTTP_Request2_Response($statusLine);
+ * do {
+ *     $headerLine = read_header_line();
+ *     $response->parseHeaderLine($headerLine);
+ * } while ($headerLine != '');
+ *
+ * while ($chunk = read_body()) {
+ *     $response->appendBody($chunk);
+ * }
+ *
+ * var_dump($response->getHeader(), $response->getCookies(), $response->getBody());
+ * </code>
+ *
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @version    Release: 0.5.1
+ * @link       http://tools.ietf.org/html/rfc2616#section-6
+ */
+class HTTP_Request2_Response
+{
+   /**
+    * HTTP protocol version (e.g. 1.0, 1.1)
+    * @var  string
+    */
+    protected $version;
+
+   /**
+    * Status code
+    * @var  integer
+    * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
+    */
+    protected $code;
+
+   /**
+    * Reason phrase
+    * @var  string
+    * @link http://tools.ietf.org/html/rfc2616#section-6.1.1
+    */
+    protected $reasonPhrase;
+
+   /**
+    * Associative array of response headers
+    * @var  array
+    */
+    protected $headers = array();
+
+   /**
+    * Cookies set in the response
+    * @var  array
+    */
+    protected $cookies = array();
+
+   /**
+    * Name of last header processed by parseHederLine()
+    *
+    * Used to handle the headers that span multiple lines
+    *
+    * @var  string
+    */
+    protected $lastHeader = null;
+
+   /**
+    * Response body
+    * @var  string
+    */
+    protected $body = '';
+
+   /**
+    * Whether the body is still encoded by Content-Encoding
+    *
+    * cURL provides the decoded body to the callback; if we are reading from
+    * socket the body is still gzipped / deflated
+    *
+    * @var  bool
+    */
+    protected $bodyEncoded;
+
+   /**
+    * Associative array of HTTP status code / reason phrase.
+    *
+    * @var  array
+    * @link http://tools.ietf.org/html/rfc2616#section-10
+    */
+    protected static $phrases = array(
+
+        // 1xx: Informational - Request received, continuing process
+        100 => 'Continue',
+        101 => 'Switching Protocols',
+
+        // 2xx: Success - The action was successfully received, understood and
+        // accepted
+        200 => 'OK',
+        201 => 'Created',
+        202 => 'Accepted',
+        203 => 'Non-Authoritative Information',
+        204 => 'No Content',
+        205 => 'Reset Content',
+        206 => 'Partial Content',
+
+        // 3xx: Redirection - Further action must be taken in order to complete
+        // the request
+        300 => 'Multiple Choices',
+        301 => 'Moved Permanently',
+        302 => 'Found',  // 1.1
+        303 => 'See Other',
+        304 => 'Not Modified',
+        305 => 'Use Proxy',
+        307 => 'Temporary Redirect',
+
+        // 4xx: Client Error - The request contains bad syntax or cannot be
+        // fulfilled
+        400 => 'Bad Request',
+        401 => 'Unauthorized',
+        402 => 'Payment Required',
+        403 => 'Forbidden',
+        404 => 'Not Found',
+        405 => 'Method Not Allowed',
+        406 => 'Not Acceptable',
+        407 => 'Proxy Authentication Required',
+        408 => 'Request Timeout',
+        409 => 'Conflict',
+        410 => 'Gone',
+        411 => 'Length Required',
+        412 => 'Precondition Failed',
+        413 => 'Request Entity Too Large',
+        414 => 'Request-URI Too Long',
+        415 => 'Unsupported Media Type',
+        416 => 'Requested Range Not Satisfiable',
+        417 => 'Expectation Failed',
+
+        // 5xx: Server Error - The server failed to fulfill an apparently
+        // valid request
+        500 => 'Internal Server Error',
+        501 => 'Not Implemented',
+        502 => 'Bad Gateway',
+        503 => 'Service Unavailable',
+        504 => 'Gateway Timeout',
+        505 => 'HTTP Version Not Supported',
+        509 => 'Bandwidth Limit Exceeded',
+
+    );
+
+   /**
+    * Constructor, parses the response status line
+    *
+    * @param    string  Response status line (e.g. "HTTP/1.1 200 OK")
+    * @param    bool    Whether body is still encoded by Content-Encoding
+    * @throws   HTTP_Request2_Exception if status line is invalid according to spec
+    */
+    public function __construct($statusLine, $bodyEncoded = true)
+    {
+        if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) {
+            throw new HTTP_Request2_Exception("Malformed response: {$statusLine}");
+        }
+        $this->version = $m[1];
+        $this->code    = intval($m[2]);
+        if (!empty($m[3])) {
+            $this->reasonPhrase = trim($m[3]);
+        } elseif (!empty(self::$phrases[$this->code])) {
+            $this->reasonPhrase = self::$phrases[$this->code];
+        }
+        $this->bodyEncoded = (bool)$bodyEncoded;
+    }
+
+   /**
+    * Parses the line from HTTP response filling $headers array
+    *
+    * The method should be called after reading the line from socket or receiving
+    * it into cURL callback. Passing an empty string here indicates the end of
+    * response headers and triggers additional processing, so be sure to pass an
+    * empty string in the end.
+    *
+    * @param    string  Line from HTTP response
+    */
+    public function parseHeaderLine($headerLine)
+    {
+        $headerLine = trim($headerLine, "\r\n");
+
+        // empty string signals the end of headers, process the received ones
+        if ('' == $headerLine) {
+            if (!empty($this->headers['set-cookie'])) {
+                $cookies = is_array($this->headers['set-cookie'])?
+                           $this->headers['set-cookie']:
+                           array($this->headers['set-cookie']);
+                foreach ($cookies as $cookieString) {
+                    $this->parseCookie($cookieString);
+                }
+                unset($this->headers['set-cookie']);
+            }
+            foreach (array_keys($this->headers) as $k) {
+                if (is_array($this->headers[$k])) {
+                    $this->headers[$k] = implode(', ', $this->headers[$k]);
+                }
+            }
+
+        // string of the form header-name: header value
+        } elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) {
+            $name  = strtolower($m[1]);
+            $value = trim($m[2]);
+            if (empty($this->headers[$name])) {
+                $this->headers[$name] = $value;
+            } else {
+                if (!is_array($this->headers[$name])) {
+                    $this->headers[$name] = array($this->headers[$name]);
+                }
+                $this->headers[$name][] = $value;
+            }
+            $this->lastHeader = $name;
+
+        // continuation of a previous header
+        } elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) {
+            if (!is_array($this->headers[$this->lastHeader])) {
+                $this->headers[$this->lastHeader] .= ' ' . trim($m[1]);
+            } else {
+                $key = count($this->headers[$this->lastHeader]) - 1;
+                $this->headers[$this->lastHeader][$key] .= ' ' . trim($m[1]);
+            }
+        }
+    }
+
+   /**
+    * Parses a Set-Cookie header to fill $cookies array
+    *
+    * @param    string    value of Set-Cookie header
+    * @link     http://web.archive.org/web/20080331104521/http://cgi.netscape.com/newsref/std/cookie_spec.html
+    */
+    protected function parseCookie($cookieString)
+    {
+        $cookie = array(
+            'expires' => null,
+            'domain'  => null,
+            'path'    => null,
+            'secure'  => false
+        );
+
+        // Only a name=value pair
+        if (!strpos($cookieString, ';')) {
+            $pos = strpos($cookieString, '=');
+            $cookie['name']  = trim(substr($cookieString, 0, $pos));
+            $cookie['value'] = trim(substr($cookieString, $pos + 1));
+
+        // Some optional parameters are supplied
+        } else {
+            $elements = explode(';', $cookieString);
+            $pos = strpos($elements[0], '=');
+            $cookie['name']  = trim(substr($elements[0], 0, $pos));
+            $cookie['value'] = trim(substr($elements[0], $pos + 1));
+
+            for ($i = 1; $i < count($elements); $i++) {
+                if (false === strpos($elements[$i], '=')) {
+                    $elName  = trim($elements[$i]);
+                    $elValue = null;
+                } else {
+                    list ($elName, $elValue) = array_map('trim', explode('=', $elements[$i]));
+                }
+                $elName = strtolower($elName);
+                if ('secure' == $elName) {
+                    $cookie['secure'] = true;
+                } elseif ('expires' == $elName) {
+                    $cookie['expires'] = str_replace('"', '', $elValue);
+                } elseif ('path' == $elName || 'domain' == $elName) {
+                    $cookie[$elName] = urldecode($elValue);
+                } else {
+                    $cookie[$elName] = $elValue;
+                }
+            }
+        }
+        $this->cookies[] = $cookie;
+    }
+
+   /**
+    * Appends a string to the response body
+    * @param    string
+    */
+    public function appendBody($bodyChunk)
+    {
+        $this->body .= $bodyChunk;
+    }
+
+   /**
+    * Returns the status code
+    * @return   integer
+    */
+    public function getStatus()
+    {
+        return $this->code;
+    }
+
+   /**
+    * Returns the reason phrase
+    * @return   string
+    */
+    public function getReasonPhrase()
+    {
+        return $this->reasonPhrase;
+    }
+
+   /**
+    * Whether response is a redirect that can be automatically handled by HTTP_Request2
+    * @return   bool
+    */
+    public function isRedirect()
+    {
+        return in_array($this->code, array(300, 301, 302, 303, 307))
+               && isset($this->headers['location']);
+    }
+
+   /**
+    * Returns either the named header or all response headers
+    *
+    * @param    string          Name of header to return
+    * @return   string|array    Value of $headerName header (null if header is
+    *                           not present), array of all response headers if
+    *                           $headerName is null
+    */
+    public function getHeader($headerName = null)
+    {
+        if (null === $headerName) {
+            return $this->headers;
+        } else {
+            $headerName = strtolower($headerName);
+            return isset($this->headers[$headerName])? $this->headers[$headerName]: null;
+        }
+    }
+
+   /**
+    * Returns cookies set in response
+    *
+    * @return   array
+    */
+    public function getCookies()
+    {
+        return $this->cookies;
+    }
+
+   /**
+    * Returns the body of the response
+    *
+    * @return   string
+    * @throws   HTTP_Request2_Exception if body cannot be decoded
+    */
+    public function getBody()
+    {
+        if (!$this->bodyEncoded ||
+            !in_array(strtolower($this->getHeader('content-encoding')), array('gzip', 'deflate'))
+        ) {
+            return $this->body;
+
+        } else {
+            if (extension_loaded('mbstring') && (2 & ini_get('mbstring.func_overload'))) {
+                $oldEncoding = mb_internal_encoding();
+                mb_internal_encoding('iso-8859-1');
+            }
+
+            try {
+                switch (strtolower($this->getHeader('content-encoding'))) {
+                    case 'gzip':
+                        $decoded = self::decodeGzip($this->body);
+                        break;
+                    case 'deflate':
+                        $decoded = self::decodeDeflate($this->body);
+                }
+            } catch (Exception $e) {
+            }
+
+            if (!empty($oldEncoding)) {
+                mb_internal_encoding($oldEncoding);
+            }
+            if (!empty($e)) {
+                throw $e;
+            }
+            return $decoded;
+        }
+    }
+
+   /**
+    * Get the HTTP version of the response
+    *
+    * @return   string
+    */
+    public function getVersion()
+    {
+        return $this->version;
+    }
+
+   /**
+    * Decodes the message-body encoded by gzip
+    *
+    * The real decoding work is done by gzinflate() built-in function, this
+    * method only parses the header and checks data for compliance with
+    * RFC 1952
+    *
+    * @param    string  gzip-encoded data
+    * @return   string  decoded data
+    * @throws   HTTP_Request2_Exception
+    * @link     http://tools.ietf.org/html/rfc1952
+    */
+    public static function decodeGzip($data)
+    {
+        $length = strlen($data);
+        // If it doesn't look like gzip-encoded data, don't bother
+        if (18 > $length || strcmp(substr($data, 0, 2), "\x1f\x8b")) {
+            return $data;
+        }
+        if (!function_exists('gzinflate')) {
+            throw new HTTP_Request2_Exception('Unable to decode body: gzip extension not available');
+        }
+        $method = ord(substr($data, 2, 1));
+        if (8 != $method) {
+            throw new HTTP_Request2_Exception('Error parsing gzip header: unknown compression method');
+        }
+        $flags = ord(substr($data, 3, 1));
+        if ($flags & 224) {
+            throw new HTTP_Request2_Exception('Error parsing gzip header: reserved bits are set');
+        }
+
+        // header is 10 bytes minimum. may be longer, though.
+        $headerLength = 10;
+        // extra fields, need to skip 'em
+        if ($flags & 4) {
+            if ($length - $headerLength - 2 < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $extraLength = unpack('v', substr($data, 10, 2));
+            if ($length - $headerLength - 2 - $extraLength[1] < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $headerLength += $extraLength[1] + 2;
+        }
+        // file name, need to skip that
+        if ($flags & 8) {
+            if ($length - $headerLength - 1 < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $filenameLength = strpos(substr($data, $headerLength), chr(0));
+            if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $headerLength += $filenameLength + 1;
+        }
+        // comment, need to skip that also
+        if ($flags & 16) {
+            if ($length - $headerLength - 1 < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $commentLength = strpos(substr($data, $headerLength), chr(0));
+            if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $headerLength += $commentLength + 1;
+        }
+        // have a CRC for header. let's check
+        if ($flags & 2) {
+            if ($length - $headerLength - 2 < 8) {
+                throw new HTTP_Request2_Exception('Error parsing gzip header: data too short');
+            }
+            $crcReal   = 0xffff & crc32(substr($data, 0, $headerLength));
+            $crcStored = unpack('v', substr($data, $headerLength, 2));
+            if ($crcReal != $crcStored[1]) {
+                throw new HTTP_Request2_Exception('Header CRC check failed');
+            }
+            $headerLength += 2;
+        }
+        // unpacked data CRC and size at the end of encoded data
+        $tmp = unpack('V2', substr($data, -8));
+        $dataCrc  = $tmp[1];
+        $dataSize = $tmp[2];
+
+        // finally, call the gzinflate() function
+        // don't pass $dataSize to gzinflate, see bugs #13135, #14370
+        $unpacked = gzinflate(substr($data, $headerLength, -8));
+        if (false === $unpacked) {
+            throw new HTTP_Request2_Exception('gzinflate() call failed');
+        } elseif ($dataSize != strlen($unpacked)) {
+            throw new HTTP_Request2_Exception('Data size check failed');
+        } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) {
+            throw new HTTP_Request2_Exception('Data CRC check failed');
+        }
+        return $unpacked;
+    }
+
+   /**
+    * Decodes the message-body encoded by deflate
+    *
+    * @param    string  deflate-encoded data
+    * @return   string  decoded data
+    * @throws   HTTP_Request2_Exception
+    */
+    public static function decodeDeflate($data)
+    {
+        if (!function_exists('gzuncompress')) {
+            throw new HTTP_Request2_Exception('Unable to decode body: gzip extension not available');
+        }
+        // RFC 2616 defines 'deflate' encoding as zlib format from RFC 1950,
+        // while many applications send raw deflate stream from RFC 1951.
+        // We should check for presence of zlib header and use gzuncompress() or
+        // gzinflate() as needed. See bug #15305
+        $header = unpack('n', substr($data, 0, 2));
+        return (0 == $header[1] % 31)? gzuncompress($data): gzinflate($data);
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/php/Net/URL2.php b/lib/php/Net/URL2.php
new file mode 100644
index 0000000000000000000000000000000000000000..08cc7b11cb84e927aba0a96f67795e8789cce6e2
--- /dev/null
+++ b/lib/php/Net/URL2.php
@@ -0,0 +1,913 @@
+<?php
+/**
+ * Net_URL2, a class representing a URL as per RFC 3986.
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2007-2009, Peytz & Co. A/S
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *   * Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in
+ *     the documentation and/or other materials provided with the distribution.
+ *   * Neither the name of the Net_URL2 nor the names of its contributors may
+ *     be used to endorse or promote products derived from this software
+ *     without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category  Networking
+ * @package   Net_URL2
+ * @author    Christian Schmidt <schmidt@php.net>
+ * @copyright 2007-2009 Peytz & Co. A/S
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version   CVS: $Id: URL2.php 289017 2009-09-30 20:16:39Z schmidt $
+ * @link      http://www.rfc-editor.org/rfc/rfc3986.txt
+ */
+
+/**
+ * Represents a URL as per RFC 3986.
+ *
+ * @category  Networking
+ * @package   Net_URL2
+ * @author    Christian Schmidt <schmidt@php.net>
+ * @copyright 2007-2009 Peytz & Co. A/S
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version   Release: @package_version@
+ * @link      http://pear.php.net/package/Net_URL2
+ */
+class Net_URL2
+{
+    /**
+     * Do strict parsing in resolve() (see RFC 3986, section 5.2.2). Default
+     * is true.
+     */
+    const OPTION_STRICT = 'strict';
+
+    /**
+     * Represent arrays in query using PHP's [] notation. Default is true.
+     */
+    const OPTION_USE_BRACKETS = 'use_brackets';
+
+    /**
+     * URL-encode query variable keys. Default is true.
+     */
+    const OPTION_ENCODE_KEYS = 'encode_keys';
+
+    /**
+     * Query variable separators when parsing the query string. Every character
+     * is considered a separator. Default is "&".
+     */
+    const OPTION_SEPARATOR_INPUT = 'input_separator';
+
+    /**
+     * Query variable separator used when generating the query string. Default
+     * is "&".
+     */
+    const OPTION_SEPARATOR_OUTPUT = 'output_separator';
+
+    /**
+     * Default options corresponds to how PHP handles $_GET.
+     */
+    private $_options = array(
+        self::OPTION_STRICT           => true,
+        self::OPTION_USE_BRACKETS     => true,
+        self::OPTION_ENCODE_KEYS      => true,
+        self::OPTION_SEPARATOR_INPUT  => '&',
+        self::OPTION_SEPARATOR_OUTPUT => '&',
+        );
+
+    /**
+     * @var  string|bool
+     */
+    private $_scheme = false;
+
+    /**
+     * @var  string|bool
+     */
+    private $_userinfo = false;
+
+    /**
+     * @var  string|bool
+     */
+    private $_host = false;
+
+    /**
+     * @var  string|bool
+     */
+    private $_port = false;
+
+    /**
+     * @var  string
+     */
+    private $_path = '';
+
+    /**
+     * @var  string|bool
+     */
+    private $_query = false;
+
+    /**
+     * @var  string|bool
+     */
+    private $_fragment = false;
+
+    /**
+     * Constructor.
+     *
+     * @param string $url     an absolute or relative URL
+     * @param array  $options an array of OPTION_xxx constants
+     */
+    public function __construct($url, $options = null)
+    {
+        if (is_array($options)) {
+            foreach ($options as $optionName => $value) {
+                $this->setOption($optionName, $value);
+            }
+        }
+
+        // The regular expression is copied verbatim from RFC 3986, appendix B.
+        // The expression does not validate the URL but matches any string.
+        preg_match('!^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?!',
+                   $url,
+                   $matches);
+
+        // "path" is always present (possibly as an empty string); the rest
+        // are optional.
+        $this->_scheme = !empty($matches[1]) ? $matches[2] : false;
+        $this->setAuthority(!empty($matches[3]) ? $matches[4] : false);
+        $this->_path = $matches[5];
+        $this->_query = !empty($matches[6]) ? $matches[7] : false;
+        $this->_fragment = !empty($matches[8]) ? $matches[9] : false;
+    }
+
+    /**
+     * Magic Setter.
+     *
+     * This method will magically set the value of a private variable ($var)
+     * with the value passed as the args
+     *
+     * @param  string $var      The private variable to set.
+     * @param  mixed  $arg      An argument of any type.
+     * @return void
+     */
+    public function __set($var, $arg)
+    {
+        $method = 'set' . $var;
+        if (method_exists($this, $method)) {
+            $this->$method($arg);
+        }
+    }
+    
+    /**
+     * Magic Getter.
+     *
+     * This is the magic get method to retrieve the private variable 
+     * that was set by either __set() or it's setter...
+     * 
+     * @param  string $var         The property name to retrieve.
+     * @return mixed  $this->$var  Either a boolean false if the
+     *                             property is not set or the value
+     *                             of the private property.
+     */
+    public function __get($var)
+    {
+        $method = 'get' . $var;
+        if (method_exists($this, $method)) {
+            return $this->$method();
+        }
+        
+        return false;
+    }
+    
+    /**
+     * Returns the scheme, e.g. "http" or "urn", or false if there is no
+     * scheme specified, i.e. if this is a relative URL.
+     *
+     * @return  string|bool
+     */
+    public function getScheme()
+    {
+        return $this->_scheme;
+    }
+
+    /**
+     * Sets the scheme, e.g. "http" or "urn". Specify false if there is no
+     * scheme specified, i.e. if this is a relative URL.
+     *
+     * @param string|bool $scheme e.g. "http" or "urn", or false if there is no
+     *                            scheme specified, i.e. if this is a relative
+     *                            URL
+     *
+     * @return void
+     * @see    getScheme()
+     */
+    public function setScheme($scheme)
+    {
+        $this->_scheme = $scheme;
+    }
+
+    /**
+     * Returns the user part of the userinfo part (the part preceding the first
+     *  ":"), or false if there is no userinfo part.
+     *
+     * @return  string|bool
+     */
+    public function getUser()
+    {
+        return $this->_userinfo !== false
+            ? preg_replace('@:.*$@', '', $this->_userinfo)
+            : false;
+    }
+
+    /**
+     * Returns the password part of the userinfo part (the part after the first
+     *  ":"), or false if there is no userinfo part (i.e. the URL does not
+     * contain "@" in front of the hostname) or the userinfo part does not
+     * contain ":".
+     *
+     * @return  string|bool
+     */
+    public function getPassword()
+    {
+        return $this->_userinfo !== false
+            ? substr(strstr($this->_userinfo, ':'), 1)
+            : false;
+    }
+
+    /**
+     * Returns the userinfo part, or false if there is none, i.e. if the
+     * authority part does not contain "@".
+     *
+     * @return  string|bool
+     */
+    public function getUserinfo()
+    {
+        return $this->_userinfo;
+    }
+
+    /**
+     * Sets the userinfo part. If two arguments are passed, they are combined
+     * in the userinfo part as username ":" password.
+     *
+     * @param string|bool $userinfo userinfo or username
+     * @param string|bool $password optional password, or false
+     *
+     * @return void
+     */
+    public function setUserinfo($userinfo, $password = false)
+    {
+        $this->_userinfo = $userinfo;
+        if ($password !== false) {
+            $this->_userinfo .= ':' . $password;
+        }
+    }
+
+    /**
+     * Returns the host part, or false if there is no authority part, e.g.
+     * relative URLs.
+     *
+     * @return  string|bool a hostname, an IP address, or false
+     */
+    public function getHost()
+    {
+        return $this->_host;
+    }
+
+    /**
+     * Sets the host part. Specify false if there is no authority part, e.g.
+     * relative URLs.
+     *
+     * @param string|bool $host a hostname, an IP address, or false
+     *
+     * @return void
+     */
+    public function setHost($host)
+    {
+        $this->_host = $host;
+    }
+
+    /**
+     * Returns the port number, or false if there is no port number specified,
+     * i.e. if the default port is to be used.
+     *
+     * @return  string|bool
+     */
+    public function getPort()
+    {
+        return $this->_port;
+    }
+
+    /**
+     * Sets the port number. Specify false if there is no port number specified,
+     * i.e. if the default port is to be used.
+     *
+     * @param string|bool $port a port number, or false
+     *
+     * @return void
+     */
+    public function setPort($port)
+    {
+        $this->_port = $port;
+    }
+
+    /**
+     * Returns the authority part, i.e. [ userinfo "@" ] host [ ":" port ], or
+     * false if there is no authority.
+     *
+     * @return string|bool
+     */
+    public function getAuthority()
+    {
+        if (!$this->_host) {
+            return false;
+        }
+
+        $authority = '';
+
+        if ($this->_userinfo !== false) {
+            $authority .= $this->_userinfo . '@';
+        }
+
+        $authority .= $this->_host;
+
+        if ($this->_port !== false) {
+            $authority .= ':' . $this->_port;
+        }
+
+        return $authority;
+    }
+
+    /**
+     * Sets the authority part, i.e. [ userinfo "@" ] host [ ":" port ]. Specify
+     * false if there is no authority.
+     *
+     * @param string|false $authority a hostname or an IP addresse, possibly
+     *                                with userinfo prefixed and port number
+     *                                appended, e.g. "foo:bar@example.org:81".
+     *
+     * @return void
+     */
+    public function setAuthority($authority)
+    {
+        $this->_userinfo = false;
+        $this->_host     = false;
+        $this->_port     = false;
+        if (preg_match('@^(([^\@]*)\@)?([^:]+)(:(\d*))?$@', $authority, $reg)) {
+            if ($reg[1]) {
+                $this->_userinfo = $reg[2];
+            }
+
+            $this->_host = $reg[3];
+            if (isset($reg[5])) {
+                $this->_port = $reg[5];
+            }
+        }
+    }
+
+    /**
+     * Returns the path part (possibly an empty string).
+     *
+     * @return string
+     */
+    public function getPath()
+    {
+        return $this->_path;
+    }
+
+    /**
+     * Sets the path part (possibly an empty string).
+     *
+     * @param string $path a path
+     *
+     * @return void
+     */
+    public function setPath($path)
+    {
+        $this->_path = $path;
+    }
+
+    /**
+     * Returns the query string (excluding the leading "?"), or false if "?"
+     * is not present in the URL.
+     *
+     * @return  string|bool
+     * @see     self::getQueryVariables()
+     */
+    public function getQuery()
+    {
+        return $this->_query;
+    }
+
+    /**
+     * Sets the query string (excluding the leading "?"). Specify false if "?"
+     * is not present in the URL.
+     *
+     * @param string|bool $query a query string, e.g. "foo=1&bar=2"
+     *
+     * @return void
+     * @see   self::setQueryVariables()
+     */
+    public function setQuery($query)
+    {
+        $this->_query = $query;
+    }
+
+    /**
+     * Returns the fragment name, or false if "#" is not present in the URL.
+     *
+     * @return  string|bool
+     */
+    public function getFragment()
+    {
+        return $this->_fragment;
+    }
+
+    /**
+     * Sets the fragment name. Specify false if "#" is not present in the URL.
+     *
+     * @param string|bool $fragment a fragment excluding the leading "#", or
+     *                              false
+     *
+     * @return void
+     */
+    public function setFragment($fragment)
+    {
+        $this->_fragment = $fragment;
+    }
+
+    /**
+     * Returns the query string like an array as the variables would appear in
+     * $_GET in a PHP script. If the URL does not contain a "?", an empty array
+     * is returned.
+     *
+     * @return  array
+     */
+    public function getQueryVariables()
+    {
+        $pattern = '/[' .
+                   preg_quote($this->getOption(self::OPTION_SEPARATOR_INPUT), '/') .
+                   ']/';
+        $parts   = preg_split($pattern, $this->_query, -1, PREG_SPLIT_NO_EMPTY);
+        $return  = array();
+
+        foreach ($parts as $part) {
+            if (strpos($part, '=') !== false) {
+                list($key, $value) = explode('=', $part, 2);
+            } else {
+                $key   = $part;
+                $value = null;
+            }
+
+            if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
+                $key = rawurldecode($key);
+            }
+            $value = rawurldecode($value);
+
+            if ($this->getOption(self::OPTION_USE_BRACKETS) &&
+                preg_match('#^(.*)\[([0-9a-z_-]*)\]#i', $key, $matches)) {
+
+                $key = $matches[1];
+                $idx = $matches[2];
+
+                // Ensure is an array
+                if (empty($return[$key]) || !is_array($return[$key])) {
+                    $return[$key] = array();
+                }
+
+                // Add data
+                if ($idx === '') {
+                    $return[$key][] = $value;
+                } else {
+                    $return[$key][$idx] = $value;
+                }
+            } elseif (!$this->getOption(self::OPTION_USE_BRACKETS)
+                      && !empty($return[$key])
+            ) {
+                $return[$key]   = (array) $return[$key];
+                $return[$key][] = $value;
+            } else {
+                $return[$key] = $value;
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * Sets the query string to the specified variable in the query string.
+     *
+     * @param array $array (name => value) array
+     *
+     * @return void
+     */
+    public function setQueryVariables(array $array)
+    {
+        if (!$array) {
+            $this->_query = false;
+        } else {
+            foreach ($array as $name => $value) {
+                if ($this->getOption(self::OPTION_ENCODE_KEYS)) {
+                    $name = self::urlencode($name);
+                }
+
+                if (is_array($value)) {
+                    foreach ($value as $k => $v) {
+                        $parts[] = $this->getOption(self::OPTION_USE_BRACKETS)
+                            ? sprintf('%s[%s]=%s', $name, $k, $v)
+                            : ($name . '=' . $v);
+                    }
+                } elseif (!is_null($value)) {
+                    $parts[] = $name . '=' . self::urlencode($value);
+                } else {
+                    $parts[] = $name;
+                }
+            }
+            $this->_query = implode($this->getOption(self::OPTION_SEPARATOR_OUTPUT),
+                                    $parts);
+        }
+    }
+
+    /**
+     * Sets the specified variable in the query string.
+     *
+     * @param string $name  variable name
+     * @param mixed  $value variable value
+     *
+     * @return  array
+     */
+    public function setQueryVariable($name, $value)
+    {
+        $array = $this->getQueryVariables();
+        $array[$name] = $value;
+        $this->setQueryVariables($array);
+    }
+
+    /**
+     * Removes the specifed variable from the query string.
+     *
+     * @param string $name a query string variable, e.g. "foo" in "?foo=1"
+     *
+     * @return void
+     */
+    public function unsetQueryVariable($name)
+    {
+        $array = $this->getQueryVariables();
+        unset($array[$name]);
+        $this->setQueryVariables($array);
+    }
+
+    /**
+     * Returns a string representation of this URL.
+     *
+     * @return  string
+     */
+    public function getURL()
+    {
+        // See RFC 3986, section 5.3
+        $url = "";
+
+        if ($this->_scheme !== false) {
+            $url .= $this->_scheme . ':';
+        }
+
+        $authority = $this->getAuthority();
+        if ($authority !== false) {
+            $url .= '//' . $authority;
+        }
+        $url .= $this->_path;
+
+        if ($this->_query !== false) {
+            $url .= '?' . $this->_query;
+        }
+
+        if ($this->_fragment !== false) {
+            $url .= '#' . $this->_fragment;
+        }
+    
+        return $url;
+    }
+
+    /**
+     * Returns a string representation of this URL.
+     *
+     * @return  string
+     * @see toString()
+     */
+    public function __toString()
+    {
+        return $this->getURL();
+    }
+
+    /** 
+     * Returns a normalized string representation of this URL. This is useful
+     * for comparison of URLs.
+     *
+     * @return  string
+     */
+    public function getNormalizedURL()
+    {
+        $url = clone $this;
+        $url->normalize();
+        return $url->getUrl();
+    }
+
+    /** 
+     * Returns a normalized Net_URL2 instance.
+     *
+     * @return  Net_URL2
+     */
+    public function normalize()
+    {
+        // See RFC 3886, section 6
+
+        // Schemes are case-insensitive
+        if ($this->_scheme) {
+            $this->_scheme = strtolower($this->_scheme);
+        }
+
+        // Hostnames are case-insensitive
+        if ($this->_host) {
+            $this->_host = strtolower($this->_host);
+        }
+
+        // Remove default port number for known schemes (RFC 3986, section 6.2.3)
+        if ($this->_port &&
+            $this->_scheme &&
+            $this->_port == getservbyname($this->_scheme, 'tcp')) {
+
+            $this->_port = false;
+        }
+
+        // Normalize case of %XX percentage-encodings (RFC 3986, section 6.2.2.1)
+        foreach (array('_userinfo', '_host', '_path') as $part) {
+            if ($this->$part) {
+                $this->$part = preg_replace('/%[0-9a-f]{2}/ie',
+                                            'strtoupper("\0")',
+                                            $this->$part);
+            }
+        }
+
+        // Path segment normalization (RFC 3986, section 6.2.2.3)
+        $this->_path = self::removeDotSegments($this->_path);
+
+        // Scheme based normalization (RFC 3986, section 6.2.3)
+        if ($this->_host && !$this->_path) {
+            $this->_path = '/';
+        }
+    }
+
+    /**
+     * Returns whether this instance represents an absolute URL.
+     *
+     * @return  bool
+     */
+    public function isAbsolute()
+    {
+        return (bool) $this->_scheme;
+    }
+
+    /**
+     * Returns an Net_URL2 instance representing an absolute URL relative to
+     * this URL.
+     *
+     * @param Net_URL2|string $reference relative URL
+     *
+     * @return Net_URL2
+     */
+    public function resolve($reference)
+    {
+        if (!$reference instanceof Net_URL2) {
+            $reference = new self($reference);
+        }
+        if (!$this->isAbsolute()) {
+            throw new Exception('Base-URL must be absolute');
+        }
+
+        // A non-strict parser may ignore a scheme in the reference if it is
+        // identical to the base URI's scheme.
+        if (!$this->getOption(self::OPTION_STRICT) && $reference->_scheme == $this->_scheme) {
+            $reference->_scheme = false;
+        }
+
+        $target = new self('');
+        if ($reference->_scheme !== false) {
+            $target->_scheme = $reference->_scheme;
+            $target->setAuthority($reference->getAuthority());
+            $target->_path  = self::removeDotSegments($reference->_path);
+            $target->_query = $reference->_query;
+        } else {
+            $authority = $reference->getAuthority();
+            if ($authority !== false) {
+                $target->setAuthority($authority);
+                $target->_path  = self::removeDotSegments($reference->_path);
+                $target->_query = $reference->_query;
+            } else {
+                if ($reference->_path == '') {
+                    $target->_path = $this->_path;
+                    if ($reference->_query !== false) {
+                        $target->_query = $reference->_query;
+                    } else {
+                        $target->_query = $this->_query;
+                    }
+                } else {
+                    if (substr($reference->_path, 0, 1) == '/') {
+                        $target->_path = self::removeDotSegments($reference->_path);
+                    } else {
+                        // Merge paths (RFC 3986, section 5.2.3)
+                        if ($this->_host !== false && $this->_path == '') {
+                            $target->_path = '/' . $this->_path;
+                        } else {
+                            $i = strrpos($this->_path, '/');
+                            if ($i !== false) {
+                                $target->_path = substr($this->_path, 0, $i + 1);
+                            }
+                            $target->_path .= $reference->_path;
+                        }
+                        $target->_path = self::removeDotSegments($target->_path);
+                    }
+                    $target->_query = $reference->_query;
+                }
+                $target->setAuthority($this->getAuthority());
+            }
+            $target->_scheme = $this->_scheme;
+        }
+
+        $target->_fragment = $reference->_fragment;
+
+        return $target;
+    }
+
+    /**
+     * Removes dots as described in RFC 3986, section 5.2.4, e.g.
+     * "/foo/../bar/baz" => "/bar/baz"
+     *
+     * @param string $path a path
+     *
+     * @return string a path
+     */
+    public static function removeDotSegments($path)
+    {
+        $output = '';
+
+        // Make sure not to be trapped in an infinite loop due to a bug in this
+        // method
+        $j = 0; 
+        while ($path && $j++ < 100) {
+            if (substr($path, 0, 2) == './') {
+                // Step 2.A
+                $path = substr($path, 2);
+            } elseif (substr($path, 0, 3) == '../') {
+                // Step 2.A
+                $path = substr($path, 3);
+            } elseif (substr($path, 0, 3) == '/./' || $path == '/.') {
+                // Step 2.B
+                $path = '/' . substr($path, 3);
+            } elseif (substr($path, 0, 4) == '/../' || $path == '/..') {
+                // Step 2.C
+                $path   = '/' . substr($path, 4);
+                $i      = strrpos($output, '/');
+                $output = $i === false ? '' : substr($output, 0, $i);
+            } elseif ($path == '.' || $path == '..') {
+                // Step 2.D
+                $path = '';
+            } else {
+                // Step 2.E
+                $i = strpos($path, '/');
+                if ($i === 0) {
+                    $i = strpos($path, '/', 1);
+                }
+                if ($i === false) {
+                    $i = strlen($path);
+                }
+                $output .= substr($path, 0, $i);
+                $path = substr($path, $i);
+            }
+        }
+
+        return $output;
+    }
+
+    /**
+     * Percent-encodes all non-alphanumeric characters except these: _ . - ~
+     * Similar to PHP's rawurlencode(), except that it also encodes ~ in PHP
+     * 5.2.x and earlier.
+     *
+     * @param  $raw the string to encode
+     * @return string
+     */
+    public static function urlencode($string)
+    {
+    	$encoded = rawurlencode($string);
+	// This is only necessary in PHP < 5.3.
+	$encoded = str_replace('%7E', '~', $encoded);
+	return $encoded;
+    }
+
+    /**
+     * Returns a Net_URL2 instance representing the canonical URL of the
+     * currently executing PHP script.
+     * 
+     * @return  string
+     */
+    public static function getCanonical()
+    {
+        if (!isset($_SERVER['REQUEST_METHOD'])) {
+            // ALERT - no current URL
+            throw new Exception('Script was not called through a webserver');
+        }
+
+        // Begin with a relative URL
+        $url = new self($_SERVER['PHP_SELF']);
+        $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
+        $url->_host   = $_SERVER['SERVER_NAME'];
+        $port = $_SERVER['SERVER_PORT'];
+        if ($url->_scheme == 'http' && $port != 80 ||
+            $url->_scheme == 'https' && $port != 443) {
+
+            $url->_port = $port;
+        }
+        return $url;
+    }
+
+    /**
+     * Returns the URL used to retrieve the current request.
+     *
+     * @return  string
+     */
+    public static function getRequestedURL()
+    {
+        return self::getRequested()->getUrl();
+    }
+
+    /**
+     * Returns a Net_URL2 instance representing the URL used to retrieve the
+     * current request.
+     *
+     * @return  Net_URL2
+     */
+    public static function getRequested()
+    {
+        if (!isset($_SERVER['REQUEST_METHOD'])) {
+            // ALERT - no current URL
+            throw new Exception('Script was not called through a webserver');
+        }
+
+        // Begin with a relative URL
+        $url = new self($_SERVER['REQUEST_URI']);
+        $url->_scheme = isset($_SERVER['HTTPS']) ? 'https' : 'http';
+        // Set host and possibly port
+        $url->setAuthority($_SERVER['HTTP_HOST']);
+        return $url;
+    }
+
+    /**
+     * Sets the specified option.
+     *
+     * @param string $optionName a self::OPTION_ constant
+     * @param mixed  $value      option value  
+     *
+     * @return void
+     * @see  self::OPTION_STRICT
+     * @see  self::OPTION_USE_BRACKETS
+     * @see  self::OPTION_ENCODE_KEYS
+     */
+    function setOption($optionName, $value)
+    {
+        if (!array_key_exists($optionName, $this->_options)) {
+            return false;
+        }
+        $this->_options[$optionName] = $value;
+    }
+
+    /**
+     * Returns the value of the specified option.
+     *
+     * @param string $optionName The name of the option to retrieve
+     *
+     * @return  mixed
+     */
+    function getOption($optionName)
+    {
+        return isset($this->_options[$optionName])
+            ? $this->_options[$optionName] : false;
+    }
+}
diff --git a/lib/php/Services/W3C/HTMLValidator.php b/lib/php/Services/W3C/HTMLValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..c47291ea45787f4bcb76d4fde2894228d5f42ace
--- /dev/null
+++ b/lib/php/Services/W3C/HTMLValidator.php
@@ -0,0 +1,407 @@
+<?php
+/**
+ * This file contains the base class for utilizing an instance of the
+ * W3 HTML Validator.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+/**
+ * require the HTTP_Request2 class for sending requests to the validator.
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Uses response object.
+ */
+require_once 'Services/W3C/HTMLValidator/Response.php';
+
+require_once 'Services/W3C/HTMLValidator/Error.php';
+
+require_once 'Services/W3C/HTMLValidator/Warning.php';
+
+/**
+ * A simple class for utilizing the W3C HTML Validator service.
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ */
+class Services_W3C_HTMLValidator
+{
+    /**
+     * URI to the w3 validator.
+     * 
+     * @var string
+     */
+    public $validator_uri = 'http://validator.w3.org/check';
+    
+    /**
+     * The URL of the document to validate
+     * @var string
+     */
+    public $uri;
+    
+    /**
+     * Internally used filename of a file to upload to the validator
+     * POSTed as multipart/form-data
+     * @var string
+     */
+    public $uploaded_file;
+    
+    /**
+     * HTML fragment to validate.
+     *  
+     * Full documents only. At the moment, will only work if data is sent with the
+     * UTF-8 encoding.
+     * @var string
+     */
+    public $fragment;
+    
+    /**
+     * Output format
+     * 
+     * Triggers the various outputs formats of the validator. If unset, the usual 
+     * Web format will be sent. If set to soap12, the SOAP1.2 interface will be 
+     * triggered. See below for the SOAP 1.2 response format description.
+     */
+    public $output = 'soap12';
+    
+    /**
+     * Character encoding
+     * 
+     * Character encoding override: Specify the character encoding to use when 
+     * parsing the document. When used with the auxiliary parameter fbc set to 1, 
+     * the given encoding will only be used as a fallback value, in case the charset
+     * is absent or unrecognized. Note that this parameter is ignored if validating
+     * a fragment with the direct input interface.
+     * @var string
+     */
+    public $charset;
+    
+    /**
+     * Fall Back Character Set
+     * 
+     * When no character encoding is detected in the document, use the value in 
+     * $charset as the fallback character set.
+     *
+     * @var bool
+     */
+    public $fbc;
+    
+    /**
+     * Document type
+     * 
+     * Document Type override: Specify the Document Type (DOCTYPE) to use when 
+     * parsing the document. When used with the auxiliary parameter fbd set to 1, 
+     * the given document type will only be used as a fallback value, in case the
+     * document's DOCTYPE declaration is missing or unrecognized.
+     * @var string
+     */
+    public $doctype;
+    
+    /**
+     * Fall back doctype
+     * 
+     * When set to 1, use the value stored in $doctype when the document type
+     * cannot be automatically determined.
+     * 
+     * @var bool
+     */
+    public $fbd;
+    
+    /**
+     * Verbose output
+     * 
+     * In the web interface, when set to 1, will make error messages, explanations 
+     * and other diagnostics more verbose.
+     * In SOAP output, does not have any impact.
+     * @var bool
+     */
+    public $verbose = false;
+    
+    /**
+     * Show source
+     * 
+     * In the web interface, triggers the display of the source after the validation
+     * results. In SOAP output, does not have any impact.
+     * @var bool
+     */
+    public $ss = false;
+    
+    /**
+     * outline
+     * 
+     * In the web interface, when set to 1, triggers the display of the document 
+     * outline after the validation results. In SOAP output, does not have any
+     * impact.
+     * @var bool
+     */
+    public $outline = false;
+    
+    /**
+     * HTTP_Request2 object.
+     * @var object
+     */
+    protected $request;
+    
+    /**
+     * Constructor for the class.
+     * 
+     * @param array $options An array of options
+     */
+    function __construct($options = array())
+    {
+        $this->setOptions($options);
+        
+        $this->setRequest(new HTTP_Request2());
+    }
+    
+    /**
+     * Sets options for the class.
+     * Pass an associative array of options for the class.
+     *
+     * @param array $options array of options
+     * 
+     * @return void
+     */
+    function setOptions($options)
+    {
+        foreach ($options as $option=>$val) {
+            $this->$option = $val;
+        }
+    }
+    
+    /**
+     * Sets the HTTP request object to use.
+     *
+     * @param HTTP_Request2 $request The request object.
+     * 
+     * @return Services_W3C_HTMLValidator
+     */
+    function setRequest(HTTP_Request2 $request)
+    {
+        $this->request = $request;
+        
+        return $this;
+    }
+    
+    /**
+     * Validates a given URI
+     * 
+     * Executes the validator using the current parameters and returns a Response 
+     * object on success.
+     *
+     * @param string $uri The address to the page to validate ex: http://example.com/
+     * 
+     * @return mixed object Services_W3C_HTMLValidator | bool false
+     */
+    function validate($uri)
+    {
+        $this->uri = $uri;
+        $this->buildRequest('uri');
+        if ($response = $this->sendRequest()) {
+            return $this->parseSOAP12Response($response->getBody());
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Validates the local file
+     * 
+     * Requests validation on the local file, from an instance of the W3C validator.
+     * The file is posted to the W3 validator using multipart/form-data.
+     * 
+     * @param string $file file to be validated.
+     * 
+     * @return mixed object Services_W3C_HTMLValidator | bool fals
+     */
+    function validateFile($file)
+    {
+        if (file_exists($file)) {
+            $this->uploaded_file = $file;
+            $this->buildRequest('file');
+            if ($response = $this->sendRequest()) {
+                return $this->parseSOAP12Response($response->getBody());
+            } else {
+                return false;
+            }
+        } else {
+            // No such file!
+            return false;
+        }
+    }
+    
+    /**
+     * Validate an html string
+     * 
+     * @param string $html full html document fragment
+     * 
+     * @return mixed object Services_W3C_HTMLValidator | bool fals
+     */
+    function validateFragment($html)
+    {
+        $this->fragment = $html;
+        $this->buildRequest('fragment');
+        if ($response = $this->sendRequest()) {
+            return $this->parseSOAP12Response($response->getBody());
+        } else {
+            return false;
+        }
+    }
+    
+    /**
+     * Prepares a request object to send to the validator.
+     *
+     * @param string $type uri, file, or fragment
+     * 
+     * @return void
+     */
+    protected function buildRequest($type = 'uri')
+    {
+        $this->request->setURL($this->validator_uri);
+        switch ($type) {
+        case 'uri':
+        default:
+            $this->request->setMethod(HTTP_Request2::METHOD_GET);
+            $this->setQueryVariable('uri', $this->uri);
+            $method = 'setQueryVariable';
+            break;
+        case 'file':
+            $this->request->setMethod(HTTP_Request2::METHOD_POST);
+            $this->request->addUpload('uploaded_file',
+                                     $this->uploaded_file,
+                                     null,
+                                     'text/html');
+            $method = 'addPostParameter';
+            break;
+        case 'fragment':
+            $this->request->setMethod(HTTP_Request2::METHOD_POST);
+            $this->addPostParameter('fragment', $this->fragment);
+            $method = 'addPostParameter';
+            break;
+        }
+        
+        foreach (array( 'charset',
+                        'fbc',
+                        'doctype',
+                        'fbd',
+                        'verbose',
+                        'ss',
+                        'outline',
+                        'output') as $option) {
+            if (isset($this->$option)) {
+                if (is_bool($this->$option)) {
+                    $this->$method($option, intval($this->$option));
+                } else {
+                    $this->$method($option, $this->$option);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Set a querystring variable for the request
+     * 
+     * @param string $name  Name of the querystring parameter
+     * @param mixed  $value Value of the parameter
+     * 
+     * @return void
+     */
+    protected function setQueryVariable($name, $value = '')
+    {
+        $url =& $this->request->getURL();
+        $url->setQueryVariable($name, $value);
+        $this->request->setURL($url);
+    }
+    
+    /**
+     * Add post data to the request
+     * 
+     * @param string $name  Name of the post field
+     * @param mixed  $value Value of the field
+     * 
+     * @return void
+     */
+    protected function addPostParameter($name, $value = '')
+    {
+        $this->request->addPostParameter($name, $value);
+    }
+    
+    /**
+     * Actually sends the request to the HTML Validator service
+     *
+     * @return bool true | false
+     */
+    protected function sendRequest()
+    {
+        try {
+            return $this->request->send();
+        } catch (Exception $e) {
+            throw new Exception('Error sending request', null, $e);
+            return false;
+        }
+        
+    }
+    
+    /**
+     * Parse an XML response from the validator
+     * 
+     * This function parses a SOAP 1.2 response xml string from the validator.
+     *
+     * @param string $xml The raw soap12 XML response from the validator.
+     * 
+     * @return mixed object Services_W3C_HTMLValidator_Response | bool false
+     */
+    static function parseSOAP12Response($xml)
+    {
+        $doc = new DOMDocument();
+        if ($doc->loadXML($xml)) {
+            $response = new Services_W3C_HTMLValidator_Response();
+            
+            // Get the standard CDATA elements
+            foreach (array('uri','checkedby','doctype','charset') as $var) {
+                $element = $doc->getElementsByTagName($var);
+                if ($element->length) {
+                    $response->$var = $element->item(0)->nodeValue;
+                }
+            }
+            // Handle the bool element validity
+            $element = $doc->getElementsByTagName('validity');
+            if ($element->length &&
+                $element->item(0)->nodeValue == 'true') {
+                $response->validity = true;
+            } else {
+                $response->validity = false;
+            }
+            if (!$response->validity) {
+                $errors = $doc->getElementsByTagName('error');
+                foreach ($errors as $error) {
+                    $response->errors[] =
+                        new Services_W3C_HTMLValidator_Error($error);
+                }
+            }
+            $warnings = $doc->getElementsByTagName('warning');
+            foreach ($warnings as $warning) {
+                $response->warnings[] =
+                    new Services_W3C_HTMLValidator_Warning($warning);
+            }
+            return $response;
+        } else {
+            // Could not load the XML.
+            return false;
+        }
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/php/Services/W3C/HTMLValidator/Error.php b/lib/php/Services/W3C/HTMLValidator/Error.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a222cebe1ed64eff0ae10b404074347ebc0ed82
--- /dev/null
+++ b/lib/php/Services/W3C/HTMLValidator/Error.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * This file contains a simple class for error messages returned from the validator.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+/**
+ * Extends from the Message class
+ */
+require_once 'Services/W3C/HTMLValidator/Message.php';
+
+/**
+ * The message class holds an error response from the W3 validator.
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ */
+class Services_W3C_HTMLValidator_Error extends Services_W3C_HTMLValidator_Message
+{
+    
+}
+
+?>
\ No newline at end of file
diff --git a/lib/php/Services/W3C/HTMLValidator/Message.php b/lib/php/Services/W3C/HTMLValidator/Message.php
new file mode 100644
index 0000000000000000000000000000000000000000..4274bc6138d03f4f3792bafc043c92cc49249182
--- /dev/null
+++ b/lib/php/Services/W3C/HTMLValidator/Message.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * This file provides a base class for messages from a W3C validator.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+/**
+ * The message class holds a response from the W3 validator.
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ */ 
+class Services_W3C_HTMLValidator_Message
+{
+    /**
+     * line corresponding to the message
+     * 
+     * Within the source code of the validated document, refers to the line which
+     * caused this message.
+     * @var int
+     */
+    public $line;
+    
+    /**
+     * column corresponding to the message
+     * 
+     * Within the source code of the validated document, refers to the column within
+     * the line for the message.
+     * @var int
+     */
+    public $col;
+    
+    /**
+     * The actual message
+     * @var string
+     */
+    public $message;
+    
+    /**
+     * Unique ID for this message
+     * 
+     * not implemented yet. should be the number of the error, as addressed
+     * internally by the validator
+     * @var int
+     */
+    public $messageid;
+    
+    /**
+     * Explanation for this message.
+     * 
+     * HTML snippet which describes the message, usually with information on
+     * how to correct the problem.
+     * @var string
+     */
+    public $explanation;
+    
+    /**
+     * Source which caused the message.
+     * 
+     * the snippet of HTML code which invoked the message to give the 
+     * context of the e
+     * @var string
+     */
+    public $source;
+    
+    /**
+     * Constructor for a response message
+     *
+     * @param object $node A dom document node.
+     */
+    function __construct($node = null)
+    {
+        if (isset($node)) {
+            foreach (get_class_vars('Services_W3C_HTMLValidator_Message') as
+                $var => $val) {
+                $element = $node->getElementsByTagName($var);
+                if ($element->length) {
+                    $this->$var = $element->item(0)->nodeValue;
+                }
+            }
+        }
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/php/Services/W3C/HTMLValidator/Response.php b/lib/php/Services/W3C/HTMLValidator/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..9184790be6289056cf683a432ea7a436e127a798
--- /dev/null
+++ b/lib/php/Services/W3C/HTMLValidator/Response.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * File contains a simple class structure for holding a response from the
+ * W3C HTMLValidator software.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+/**
+ * Simple class for a W3C HTML Validator Response.
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ */
+class Services_W3C_HTMLValidator_Response
+{
+    /**
+     * the address of the document validated
+     * 
+     * Will (likely?) be upload://Form Submission  
+     * if an uploaded document or fragment was validated. In EARL terms, this is
+     * the TestSubject.
+     * @var string
+     */
+    public $uri;
+    
+    /**
+     * Location of the service which provided the validation result. In EARL terms,
+     * this is the Assertor.
+     * @var string
+     */
+    public $checkedby;
+    
+    /**
+     * Detected (or forced) Document Type for the validated document
+     * @var string
+     */
+    public $doctype;
+    
+    /**
+     * Detected (or forced) Character Encoding for the validated document
+     * @var string
+     */
+    public $charset;
+    
+    /**
+     * Whether or not the document validated passed or not formal validation 
+     * (true|false boolean)
+     * @var bool
+     */
+    public $validity;
+    
+    /**
+     * Array of HTMLValidator_Error objects (if applicable)
+     * @var array
+     */
+    public $errors = array();
+    
+    /**
+     * Array of HTMLValidator_Warning objects (if applicable)
+     * @var array
+     */
+    public $warnings = array();
+    
+    /**
+     * Returns the validity of the checked document.
+     * 
+     * @return bool
+     */
+    function isValid()
+    {
+        if ($this->validity) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+}
+
+?>
\ No newline at end of file
diff --git a/lib/php/Services/W3C/HTMLValidator/Warning.php b/lib/php/Services/W3C/HTMLValidator/Warning.php
new file mode 100644
index 0000000000000000000000000000000000000000..2032ef9124801e36324d3f163ef2dce2b383ff08
--- /dev/null
+++ b/lib/php/Services/W3C/HTMLValidator/Warning.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * This file contains a simple class for warning messages returned from a validator.
+ * 
+ * PHP versions 5
+ * 
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+/**
+ * Extends from the Message class
+ */
+require_once 'Services/W3C/HTMLValidator/Message.php';
+
+/**
+ * The message class holds an error response from the W3 validator.
+ *
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ */
+class Services_W3C_HTMLValidator_Warning extends Services_W3C_HTMLValidator_Message
+{
+    
+}
+
+?>
\ No newline at end of file
diff --git a/lib/php/Spider.php b/lib/php/Spider.php
new file mode 100644
index 0000000000000000000000000000000000000000..aacc1817b4ebc961562883f318aa336e0fea7360
--- /dev/null
+++ b/lib/php/Spider.php
@@ -0,0 +1,167 @@
+<?php
+/**
+ * pear2\Spider\Main
+ *
+ * PHP version 5
+ *
+ * @category  Yourcategory
+ * @package   PEAR2_Spider
+ * @author    Your Name <handle@php.net>
+ * @copyright 2010 Your Name
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @version   SVN: $Id$
+ * @link      http://svn.php.net/repository/pear2/PEAR2_Spider
+ */
+
+/**
+ * Main class for PEAR2_Spider
+ *
+ * @category  Yourcategory
+ * @package   PEAR2_Spider
+ * @author    Your Name <handle@php.net>
+ * @copyright 2010 Your Name
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @link      http://svn.php.net/repository/pear2/PEAR2_Spider
+ */
+class Spider
+{
+    const MAX_DEPTH = 50;
+
+    protected $loggers = array();
+    protected $filters = array();
+    protected $downloader = null;
+    protected $parser = null;
+    protected $visited = array();
+
+    public function __construct(
+        Spider_Downloader $downloader,
+        Spider_Parser $parser)
+    {
+        $this->setDownloader($downloader);
+        $this->setParser($parser);
+    }
+
+    public function setDownloader(Spider_Downloader $downloader)
+    {
+        $this->downloader = $downloader;
+    }
+
+    public function setParser(Spider_ParserInterface $parser)
+    {
+        $this->parser = $parser;
+    }
+
+    public function addLogger(Spider_LoggerAbstract $logger)
+    {
+        if (!in_array($logger, $this->loggers)) {
+            $this->loggers[] = $logger;
+        }
+    }
+    
+    public function addUriFilter($filterClass)
+    {
+        if (!in_array($filterClass, $this->filters)) {
+            $this->filters[] = $filterClass;
+        }
+    }
+
+    public function spider($baseUri)
+    {
+        if (!filter_var($baseUri, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) {
+            throw new Exception('Invalid URI: ' . $baseUri);
+        }
+        $this->spiderPage($baseUri, $baseUri);
+    }
+
+    protected function spiderPage($baseUri, $uri, $depth = 1)
+    {
+
+        $this->visited[$uri] = true;
+
+        $content = $this->downloader->download($uri);
+        $xpath   = $this->parser->parse($content, $uri);
+
+        foreach ($this->loggers as $logger) {
+            $logger->log($uri, $xpath);
+        }
+
+        // spider sub-pages
+        if ($depth < self::MAX_DEPTH) {
+            $subUris = $this->getUris($baseUri, $xpath);
+            
+            foreach ($this->filters as $filter_class) {
+                $subUris = new $filter_class($subUris);
+            }
+            
+            foreach ($subUris as $subUri) {
+                if (!array_key_exists($subUri, $this->visited)) {
+                    try {
+                        $this->spiderPage($baseUri, $subUri, $depth + 1);
+                    } catch(Exception $e) {
+                        throw new Exception($baseUri . ' linked to a page that does not exist!' .$subUri, 404, $e);
+                    }
+                }
+            }
+        }
+    }
+
+    protected function getUris($baseUri, DOMXPath $xpath)
+    {
+        $uris = array();
+
+        $baseHrefNodes = $xpath->query(
+            "//xhtml:base/@href"
+        );
+
+        if ($baseHrefNodes->length > 0) {
+            $baseHref = (string)$baseHrefNodes->item(0)->nodeValue;
+        } else {
+            $baseHref = '';
+        }
+
+        $nodes = $xpath->query(
+            "//xhtml:a[@href]/@href"
+        );
+
+        foreach ($nodes as $node) {
+            $uri = $this->absolutePath((string)$node->nodeValue, $baseUri);
+            
+            if (!empty($uri)) {
+                if (strncmp($baseUri, $uri, strlen($baseUri)) === 0) {
+                    $uris[] = $uri;
+                } elseif (
+                       $uri != '.'
+                    && preg_match('!^(https?|ftp)://!i', $uri) === 0
+                ) {
+                    $uris[] = $baseHref . $uri;
+                }
+            }
+        }
+
+        return new Spider_UriIterator($uris);
+    }
+    
+    public function absolutePath($relativeUri, $baseUri)
+    {
+        $new_base_url = $baseUri;
+        $base_url_parts = parse_url($baseUri);
+        
+        if (substr($baseUri, -1) != '/') {
+            $path = pathinfo($base_url_parts['path']);
+            $new_base_url = substr($new_base_url, 0, strlen($new_base_url)-strlen($path['basename']));
+        }
+        
+        $new_txt = '';
+    
+        if (!filter_var($relativeUri, FILTER_VALIDATE_URL, FILTER_FLAG_SCHEME_REQUIRED)) {
+             if (substr($relativeUri, 0, 1) == '/') {
+                 $new_base_url = $base_url_parts['scheme'].'://'.$base_url_parts['host'];
+             }
+             $new_txt .= $new_base_url;
+        }
+        
+        $absoluteUri = $new_txt.$relativeUri;
+        
+        return $absoluteUri;
+    }
+}
diff --git a/lib/php/Spider/AnchorFilter.php b/lib/php/Spider/AnchorFilter.php
new file mode 100644
index 0000000000000000000000000000000000000000..684a4bad4f38417957923bb88f021c134330c938
--- /dev/null
+++ b/lib/php/Spider/AnchorFilter.php
@@ -0,0 +1,13 @@
+<?php
+class Spider_AnchorFilter extends Spider_UriFilterInterface
+{
+    function accept()
+    {
+        return true;
+    }
+    
+    function current()
+    {
+        return preg_replace('/#(.*)/', '', parent::current());
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/DbLogger.php b/lib/php/Spider/DbLogger.php
new file mode 100644
index 0000000000000000000000000000000000000000..7a2c571de62b473a808da2852987a9d1914bcec4
--- /dev/null
+++ b/lib/php/Spider/DbLogger.php
@@ -0,0 +1,10 @@
+<?php
+abstract class Spider_DbLogger extends Spider_LoggerAbstract
+{
+    protected $db = null;
+
+    public function __construct(PDO $db)
+    {
+        $this->db = $db;
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/Downloader.php b/lib/php/Spider/Downloader.php
new file mode 100644
index 0000000000000000000000000000000000000000..118471cb0ccfbc585da31a26aae5a1412f86cc2c
--- /dev/null
+++ b/lib/php/Spider/Downloader.php
@@ -0,0 +1,36 @@
+<?php
+class Spider_Downloader
+{
+    private $curl = null;
+
+    public function __construct()
+    {
+        $this->curl = curl_init();
+
+        curl_setopt_array(
+            $this->curl,
+            array(
+                CURLOPT_AUTOREFERER    => true,
+                CURLOPT_FOLLOWLOCATION => true,
+                CURLOPT_RETURNTRANSFER => true,
+                CURLOPT_SSL_VERIFYPEER => false,
+                CURLOPT_USERAGENT      => 'silverorange-spider',
+            )
+        );
+    }
+
+    public function download($uri)
+    {
+        curl_setopt($this->curl, CURLOPT_URL, $uri);
+        $result = curl_exec($this->curl);
+        if (!$result) {
+            throw new Exception('Error downloading ' . $uri. $result);
+        }
+        return $result;
+    }
+
+    public function __destruct()
+    {
+        curl_close($this->curl);
+    }
+}
diff --git a/lib/php/Spider/JavaScriptLogger.php b/lib/php/Spider/JavaScriptLogger.php
new file mode 100644
index 0000000000000000000000000000000000000000..f3df2a4c1b22063f4601ab3b9f8a0182f2cd6b99
--- /dev/null
+++ b/lib/php/Spider/JavaScriptLogger.php
@@ -0,0 +1,36 @@
+<?php
+class Spider_JavaScriptLogger extends Spider_DbLogger
+{
+    public function log($uri, DOMXPath $xpath)
+    {
+        $scripts = $this->getScripts($xpath);
+        foreach ($scripts as $script) {
+            $this->saveScript($uri, $script);
+        }
+    }
+
+    protected function getScripts(DOMXPath $xpath)
+    {
+        $scripts = array();
+
+        $nodes = $xpath->query(
+            "//xhtml:script[@type='text/javascript' and @src]/@src"
+        );
+
+        foreach ($nodes as $node) {
+            $scripts[] = (string)$node->nodeValue;
+        }
+
+        return $scripts;
+    }
+
+    protected function saveScript($uri, $script)
+    {
+        $statement = $this->db->prepare(
+            'insert into SpiderJavaScript (uri, script) ' .
+            'values (:uri, :script)'
+        );
+
+        $statement->execute(array($uri, $script));
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/Logger.php b/lib/php/Spider/Logger.php
new file mode 100644
index 0000000000000000000000000000000000000000..5f698baaee43935d33b272104d947b14091189f7
--- /dev/null
+++ b/lib/php/Spider/Logger.php
@@ -0,0 +1,8 @@
+<?php
+class Spider_Logger extends Spider_LoggerAbstract
+{
+    public function log($uri, DOMXPath $xpath)
+    {
+        echo $uri . PHP_EOL;
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/LoggerAbstract.php b/lib/php/Spider/LoggerAbstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..73748abeca5c6332867531365529015ffec33207
--- /dev/null
+++ b/lib/php/Spider/LoggerAbstract.php
@@ -0,0 +1,5 @@
+<?php
+abstract class Spider_LoggerAbstract
+{
+    abstract public function log($uri, DOMXPath $xpath);
+}
\ No newline at end of file
diff --git a/lib/php/Spider/MailtoFilter.php b/lib/php/Spider/MailtoFilter.php
new file mode 100644
index 0000000000000000000000000000000000000000..c7486579929651a680c8f6b0082d55648b2790e6
--- /dev/null
+++ b/lib/php/Spider/MailtoFilter.php
@@ -0,0 +1,11 @@
+<?php
+class Spider_MailtoFilter extends Spider_UriFilterInterface
+{
+    function accept()
+    {
+        if (substr($this->current(), 0, 7) == 'mailto:') {
+            return false;
+        }
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/PageLogger.php b/lib/php/Spider/PageLogger.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c2ba4c5250998ffe90aaae8eee8fae95e5cceb4
--- /dev/null
+++ b/lib/php/Spider/PageLogger.php
@@ -0,0 +1,18 @@
+<?php
+class Spider_PageLogger extends Spider_DbLogger
+{
+    public function log($uri, DOMXPath $xpath)
+    {
+        $this->savePage($uri);
+    }
+
+    protected function savePage($uri)
+    {
+        $statement = $this->db->prepare(
+            'insert into SpiderPage (uri) ' .
+            'values (:uri)'
+        );
+
+        $statement->execute(array($uri));
+    }
+}
diff --git a/lib/php/Spider/Parser.php b/lib/php/Spider/Parser.php
new file mode 100644
index 0000000000000000000000000000000000000000..7c839d3629faf8cb67b2bc237a0da442d284c8d8
--- /dev/null
+++ b/lib/php/Spider/Parser.php
@@ -0,0 +1,21 @@
+<?php
+class Spider_Parser implements Spider_ParserInterface
+{
+    public function parse($content)
+    {
+        return $this->getXPath($content);
+    }
+
+    protected function getXPath($content)
+    {
+        $document = new DOMDocument();
+        $document->strictErrorChecking = false;
+        $document->loadXML(
+            $content,
+            LIBXML_NOERROR | LIBXML_NONET | LIBXML_NOWARNING
+        );
+        $xpath = new DOMXPAth($document);
+        $xpath->registerNamespace('xhtml', 'http://www.w3.org/1999/xhtml');
+        return $xpath;
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/ParserInterface.php b/lib/php/Spider/ParserInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..417e895b27a08932bd3ca2df161ebb1f6a1f6004
--- /dev/null
+++ b/lib/php/Spider/ParserInterface.php
@@ -0,0 +1,13 @@
+<?php
+interface Spider_ParserInterface
+{
+    /**
+     * parse the content and return xpath object
+     * 
+     * @param string $content The content
+     * 
+     * @return DOMXPAth
+     */
+    public function parse($content);
+
+}
\ No newline at end of file
diff --git a/lib/php/Spider/StyleSheetLogger.php b/lib/php/Spider/StyleSheetLogger.php
new file mode 100644
index 0000000000000000000000000000000000000000..8eab27df78fafa2acba62477cf7bfefce5bd319d
--- /dev/null
+++ b/lib/php/Spider/StyleSheetLogger.php
@@ -0,0 +1,37 @@
+<?php
+class Spider_StyleSheetLogger extends Spider_DbLogger
+{
+    public function log($uri, DOMXPath $xpath)
+    {
+        $styles = $this->getStyles($xpath);
+        foreach ($styles as $style) {
+            $this->saveStyle($uri, $style);
+        }
+    }
+
+    protected function getStyles(DOMXPath $xpath)
+    {
+        $styles = array();
+
+        $nodes = $xpath->query(
+            "//xhtml:link[@rel='stylesheet' and @type='text/css' and @href]/" .
+            "@href"
+        );
+
+        foreach ($nodes as $node) {
+            $styles[] = (string)$node->nodeValue;
+        }
+
+        return $styles;
+    }
+
+    protected function saveStyle($uri, $style)
+    {
+        $statement = $this->db->prepare(
+            'insert into SpiderStyleSheet (uri, style) ' .
+            'values (:uri, :style)'
+        );
+
+        $statement->execute(array($uri, $style));
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/TidyParser.php b/lib/php/Spider/TidyParser.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cd8b8b0560979fdc831257e2c39535cefddccba
--- /dev/null
+++ b/lib/php/Spider/TidyParser.php
@@ -0,0 +1,21 @@
+<?php
+class Spider_TidyParser extends Spider_Parser
+{
+    public function parse($content)
+    {
+        $content = $this->tidy($content);
+        return $this->getXPath($content);
+    }
+
+    protected function tidy($content)
+    {
+        $config = array(
+            'output-xhtml' => true,
+        );
+
+        $tidy = tidy_parse_string($content, $config, 'utf8');
+        $tidy->cleanRepair();
+
+        return (string)$tidy;
+    }
+}
\ No newline at end of file
diff --git a/lib/php/Spider/UriFilterInterface.php b/lib/php/Spider/UriFilterInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c502442a033f2a0873ddd04988547e2902505a20
--- /dev/null
+++ b/lib/php/Spider/UriFilterInterface.php
@@ -0,0 +1,5 @@
+<?php
+abstract class Spider_UriFilterInterface extends FilterIterator
+{
+    
+}
\ No newline at end of file
diff --git a/lib/php/Spider/UriIterator.php b/lib/php/Spider/UriIterator.php
new file mode 100644
index 0000000000000000000000000000000000000000..04bccaaacddd5e92d6985096a5bd9014a0887961
--- /dev/null
+++ b/lib/php/Spider/UriIterator.php
@@ -0,0 +1,5 @@
+<?php
+class Spider_UriIterator extends ArrayIterator
+{
+
+}
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/AllTests.php b/lib/tests/HTTP_Request2/HTTP/tests/AllTests.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e84beadd4a85bb4eb27e6f29dd2bbdd1e5c98ca
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/AllTests.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: AllTests.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'HTTP_Request2_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+chdir(dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR);
+
+require_once dirname(__FILE__) . '/Request2Test.php';
+require_once dirname(__FILE__) . '/ObserverTest.php';
+require_once dirname(__FILE__) . '/Request2/AllTests.php';
+
+class HTTP_Request2_AllTests
+{
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package');
+
+        $suite->addTest(Request2_AllTests::suite());
+        $suite->addTestSuite('HTTP_Request2Test');
+        $suite->addTestSuite('HTTP_Request2_ObserverTest');
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'HTTP_Request2_AllTests::main') {
+    HTTP_Request2_AllTests::main();
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/ObserverTest.php b/lib/tests/HTTP_Request2/HTTP/tests/ObserverTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b5578d658585e26e82be1af4b711e984b508abbd
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/ObserverTest.php
@@ -0,0 +1,120 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: ObserverTest.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class representing a HTTP request
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * PHPUnit Test Case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * Mock observer
+ */
+class HTTP_Request2_MockObserver implements SplObserver
+{
+    public $calls = 0;
+
+    public $event;
+
+    public function update (SplSubject $subject)
+    {
+        $this->calls++;
+        $this->event = $subject->getLastEvent();
+    }
+}
+
+/**
+ * Unit test for subject-observer pattern implementation in HTTP_Request2
+ */
+class HTTP_Request2_ObserverTest extends PHPUnit_Framework_TestCase
+{
+    public function testSetLastEvent()
+    {
+        $request  = new HTTP_Request2();
+        $observer = new HTTP_Request2_MockObserver();
+        $request->attach($observer);
+
+        $request->setLastEvent('foo', 'bar');
+        $this->assertEquals(1, $observer->calls);
+        $this->assertEquals(array('name' => 'foo', 'data' => 'bar'), $observer->event);
+
+        $request->setLastEvent('baz');
+        $this->assertEquals(2, $observer->calls);
+        $this->assertEquals(array('name' => 'baz', 'data' => null), $observer->event);
+    }
+
+    public function testAttachOnlyOnce()
+    {
+        $request   = new HTTP_Request2();
+        $observer  = new HTTP_Request2_MockObserver();
+        $observer2 = new HTTP_Request2_MockObserver();
+        $request->attach($observer);
+        $request->attach($observer2);
+        $request->attach($observer);
+
+        $request->setLastEvent('event', 'data');
+        $this->assertEquals(1, $observer->calls);
+        $this->assertEquals(1, $observer2->calls);
+    }
+
+    public function testDetach()
+    {
+        $request   = new HTTP_Request2();
+        $observer  = new HTTP_Request2_MockObserver();
+        $observer2 = new HTTP_Request2_MockObserver();
+
+        $request->attach($observer);
+        $request->detach($observer2); // should not be a error
+        $request->setLastEvent('first');
+
+        $request->detach($observer);
+        $request->setLastEvent('second');
+        $this->assertEquals(1, $observer->calls);
+        $this->assertEquals(array('name' => 'first', 'data' => null), $observer->event);
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/AllTests.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/AllTests.php
new file mode 100644
index 0000000000000000000000000000000000000000..05f9f8764a387e490a6ad4d15de1bb0656fb91b4
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/AllTests.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: AllTests.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Request2_Adapter_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+require_once dirname(__FILE__) . '/MockTest.php';
+
+class Request2_Adapter_AllTests
+{
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package - Request2 - Adapter');
+
+        $suite->addTestSuite('HTTP_Request2_Adapter_MockTest');
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Request2_Adapter_AllTests::main') {
+    Request2_Adapter_AllTests::main();
+}
+?>
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/MockTest.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/MockTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b07036fb62d8e5772e215cacf150eaa5447a6870
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/Adapter/MockTest.php
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: MockTest.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class representing a HTTP request
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * Mock adapter intended for testing
+ */
+require_once 'HTTP/Request2/Adapter/Mock.php';
+
+/**
+ * PHPUnit Test Case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * Unit test for HTTP_Request2_Response class
+ */
+class HTTP_Request2_Adapter_MockTest extends PHPUnit_Framework_TestCase
+{
+    public function testDefaultResponse()
+    {
+        $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_GET,
+                                 array('adapter' => 'mock'));
+        $response = $req->send();
+        $this->assertEquals(400, $response->getStatus());
+        $this->assertEquals(0, count($response->getHeader()));
+        $this->assertEquals('', $response->getBody());
+    }
+
+    public function testResponseFromString()
+    {
+        $mock = new HTTP_Request2_Adapter_Mock();
+        $mock->addResponse(
+            "HTTP/1.1 200 OK\r\n" .
+            "Content-Type: text/plain; charset=iso-8859-1\r\n" .
+            "\r\n" .
+            "This is a string"
+        );
+        $req = new HTTP_Request2('http://www.example.com/');
+        $req->setAdapter($mock);
+
+        $response = $req->send();
+        $this->assertEquals(200, $response->getStatus());
+        $this->assertEquals(1, count($response->getHeader()));
+        $this->assertEquals('This is a string', $response->getBody());
+    }
+
+    public function testResponseFromFile()
+    {
+        $mock = new HTTP_Request2_Adapter_Mock();
+        $mock->addResponse(fopen(dirname(dirname(dirname(__FILE__))) .
+                           '/_files/response_headers', 'rb'));
+
+        $req = new HTTP_Request2('http://www.example.com/');
+        $req->setAdapter($mock);
+
+        $response = $req->send();
+        $this->assertEquals(200, $response->getStatus());
+        $this->assertEquals(7, count($response->getHeader()));
+        $this->assertEquals('Nothing to see here, move along.', $response->getBody());
+    }
+
+    public function testResponsesQueue()
+    {
+        $mock = new HTTP_Request2_Adapter_Mock();
+        $mock->addResponse(
+            "HTTP/1.1 301 Over there\r\n" .
+            "Location: http://www.example.com/newpage.html\r\n" .
+            "\r\n" .
+            "The document is over there"
+        );
+        $mock->addResponse(
+            "HTTP/1.1 200 OK\r\n" .
+            "Content-Type: text/plain; charset=iso-8859-1\r\n" .
+            "\r\n" .
+            "This is a string"
+        );
+
+        $req = new HTTP_Request2('http://www.example.com/');
+        $req->setAdapter($mock);
+        $this->assertEquals(301, $req->send()->getStatus());
+        $this->assertEquals(200, $req->send()->getStatus());
+        $this->assertEquals(400, $req->send()->getStatus());
+    }
+
+    public function testResponseException()
+    {
+        $mock = new HTTP_Request2_Adapter_Mock();
+        $mock->addResponse(
+            new HTTP_Request2_Exception('Shit happens')
+        );
+        $req = new HTTP_Request2('http://www.example.com/');
+        $req->setAdapter($mock);
+        try {
+            $req->send();
+        } catch (Exception $e) {
+            $this->assertEquals('Shit happens', $e->getMessage());
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+}
+?>
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/AllTests.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/AllTests.php
new file mode 100644
index 0000000000000000000000000000000000000000..826b77d4f611837bcec680e0e898ca761c120fa2
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/AllTests.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: AllTests.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Request2_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+require_once dirname(__FILE__) . '/MultipartBodyTest.php';
+require_once dirname(__FILE__) . '/ResponseTest.php';
+require_once dirname(__FILE__) . '/Adapter/AllTests.php';
+
+class Request2_AllTests
+{
+    public static function main()
+    {
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package - Request2');
+
+        $suite->addTestSuite('HTTP_Request2_MultipartBodyTest');
+        $suite->addTestSuite('HTTP_Request2_ResponseTest');
+        $suite->addTest(Request2_Adapter_AllTests::suite());
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Request2_AllTests::main') {
+    Request2_AllTests::main();
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/MultipartBodyTest.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/MultipartBodyTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..9fd5d64a4d81db747ce35529bbb94e4d1d40905f
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/MultipartBodyTest.php
@@ -0,0 +1,109 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: MultipartBodyTest.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class representing a HTTP request
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * PHPUnit Test Case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * Unit test for HTTP_Request2_MultipartBody class
+ */
+class HTTP_Request2_MultipartBodyTest extends PHPUnit_Framework_TestCase
+{
+    public function testUploadSimple()
+    {
+        $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST);
+        $body = $req->addPostParameter('foo', 'I am a parameter')
+                    ->addUpload('upload', dirname(dirname(__FILE__)) . '/_files/plaintext.txt')
+                    ->getBody();
+
+        $this->assertTrue($body instanceof HTTP_Request2_MultipartBody);
+        $asString = $body->__toString();
+        $boundary = $body->getBoundary();
+        $this->assertEquals($body->getLength(), strlen($asString));
+        $this->assertContains('This is a test.', $asString);
+        $this->assertContains('I am a parameter', $asString);
+        $this->assertRegexp("!--{$boundary}--\r\n$!", $asString);
+    }
+
+    public function testStreaming()
+    {
+        $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST);
+        $body = $req->addPostParameter('foo', 'I am a parameter')
+                    ->addUpload('upload', dirname(dirname(__FILE__)) . '/_files/plaintext.txt')
+                    ->getBody();
+        $asString = '';
+        while ($part = $body->read(10)) {
+            $asString .= $part;
+        }
+        $this->assertEquals($body->getLength(), strlen($asString));
+        $this->assertContains('This is a test.', $asString);
+        $this->assertContains('I am a parameter', $asString);
+    }
+
+    public function testUploadArray()
+    {
+        $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST);
+        $body = $req->addUpload('upload', array(
+                                    array(dirname(dirname(__FILE__)) . '/_files/plaintext.txt', 'bio.txt', 'text/plain'),
+                                    array(dirname(dirname(__FILE__)) . '/_files/empty.gif', 'photo.gif', 'image/gif')
+                                ))
+                    ->getBody();
+        $asString = $body->__toString();
+        $this->assertContains(file_get_contents(dirname(dirname(__FILE__)) . '/_files/empty.gif'), $asString);
+        $this->assertContains('name="upload[0]"; filename="bio.txt"', $asString);
+        $this->assertContains('name="upload[1]"; filename="photo.gif"', $asString);
+
+        $body2 = $req->setConfig(array('use_brackets' => false))->getBody();
+        $asString = $body2->__toString();
+        $this->assertContains('name="upload"; filename="bio.txt"', $asString);
+        $this->assertContains('name="upload"; filename="photo.gif"', $asString);
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2/ResponseTest.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2/ResponseTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..b605e066bf704b49c2eec5cedff7d18e9b708de8
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2/ResponseTest.php
@@ -0,0 +1,149 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: ResponseTest.php 290192 2009-11-03 21:29:32Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class representing a HTTP response
+ */
+require_once 'HTTP/Request2/Response.php';
+
+/**
+ * PHPUnit Test Case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * Unit test for HTTP_Request2_Response class
+ */
+class HTTP_Request2_ResponseTest extends PHPUnit_Framework_TestCase
+{
+    public function testParseStatusLine()
+    {
+        $response = new HTTP_Request2_Response('HTTP/1.1 200 OK');
+        $this->assertEquals('1.1', $response->getVersion());
+        $this->assertEquals(200, $response->getStatus());
+        $this->assertEquals('OK', $response->getReasonPhrase());
+
+        $response2 = new HTTP_Request2_Response('HTTP/1.2 222 Nishtyak!');
+        $this->assertEquals('1.2', $response2->getVersion());
+        $this->assertEquals(222, $response2->getStatus());
+        $this->assertEquals('Nishtyak!', $response2->getReasonPhrase());
+
+        try {
+            $response3 = new HTTP_Request2_Response('Invalid status line');
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testParseHeaders()
+    {
+        $response = $this->readResponseFromFile('response_headers');
+        $this->assertEquals(7, count($response->getHeader()));
+        $this->assertEquals('PHP/6.2.2', $response->getHeader('X-POWERED-BY'));
+        $this->assertEquals('text/html; charset=windows-1251', $response->getHeader('cOnTeNt-TyPe'));
+        $this->assertEquals('accept-charset, user-agent', $response->getHeader('vary'));
+    }
+
+    public function testParseCookies()
+    {
+        $response = $this->readResponseFromFile('response_cookies');
+        $cookies  = $response->getCookies();
+        $this->assertEquals(4, count($cookies));
+        $expected = array(
+            array('name' => 'foo', 'value' => 'bar', 'expires' => null,
+                  'domain' => null, 'path' => null, 'secure' => false),
+            array('name' => 'PHPSESSID', 'value' => '1234567890abcdef1234567890abcdef',
+                  'expires' => null, 'domain' => null, 'path' => '/', 'secure' => true),
+            array('name' => 'A', 'value' => 'B=C', 'expires' => null,
+                  'domain' => null, 'path' => null, 'secure' => false),
+            array('name' => 'baz', 'value' => '%20a%20value', 'expires' => 'Sun, 03 Jan 2010 03:04:05 GMT',
+                  'domain' => 'pear.php.net', 'path' => null, 'secure' => false),
+        );
+        foreach ($cookies as $k => $cookie) {
+            $this->assertEquals($expected[$k], $cookie);
+        }
+    }
+
+    public function testGzipEncoding()
+    {
+        $response = $this->readResponseFromFile('response_gzip');
+        $this->assertEquals('0e964e9273c606c46afbd311b5ad4d77', md5($response->getBody()));
+
+        try {
+            $response = $this->readResponseFromFile('response_gzip_broken');
+            $body = $response->getBody();
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testDeflateEncoding()
+    {
+        $response = $this->readResponseFromFile('response_deflate');
+        $this->assertEquals('0e964e9273c606c46afbd311b5ad4d77', md5($response->getBody()));
+    }
+
+    public function testBug15305()
+    {
+        $response = $this->readResponseFromFile('bug_15305');
+        $this->assertEquals('c8c5088fc8a7652afef380f086c010a6', md5($response->getBody()));
+    }
+
+    protected function readResponseFromFile($filename)
+    {
+        $fp       = fopen(dirname(dirname(__FILE__)) . '/_files/' . $filename, 'rb');
+        $response = new HTTP_Request2_Response(fgets($fp));
+        do {
+            $headerLine = fgets($fp);
+            $response->parseHeaderLine($headerLine);
+        } while ('' != trim($headerLine));
+
+        while (!feof($fp)) {
+            $response->appendBody(fread($fp, 1024));
+        }
+        return $response;
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/Request2Test.php b/lib/tests/HTTP_Request2/HTTP/tests/Request2Test.php
new file mode 100644
index 0000000000000000000000000000000000000000..34d30a5309d55d4de9fd9188eb627964d1fb35b4
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/Request2Test.php
@@ -0,0 +1,297 @@
+<?php
+/**
+ * Unit tests for HTTP_Request2 package
+ *
+ * PHP version 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2008, 2009, Alexey Borzov <avb@php.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *    * Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *    * Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *    * The names of the authors may not be used to endorse or promote products
+ *      derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @category   HTTP
+ * @package    HTTP_Request2
+ * @author     Alexey Borzov <avb@php.net>
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    SVN: $Id: Request2Test.php 290921 2009-11-18 17:31:58Z avb $
+ * @link       http://pear.php.net/package/HTTP_Request2
+ */
+
+/**
+ * Class representing a HTTP request
+ */
+require_once 'HTTP/Request2.php';
+
+/**
+ * PHPUnit Test Case
+ */
+require_once 'PHPUnit/Framework/TestCase.php';
+
+/**
+ * Unit test for HTTP_Request2 class
+ */
+class HTTP_Request2Test extends PHPUnit_Framework_TestCase
+{
+    public function testConstructorSetsDefaults()
+    {
+        $url = new Net_URL2('http://www.example.com/foo');
+        $req = new HTTP_Request2($url, HTTP_Request2::METHOD_POST, array('connect_timeout' => 666));
+
+        $this->assertSame($url, $req->getUrl());
+        $this->assertEquals(HTTP_Request2::METHOD_POST, $req->getMethod());
+        $this->assertEquals(666, $req->getConfig('connect_timeout'));
+    }
+
+    public function testSetUrl()
+    {
+        $urlString = 'http://www.example.com/foo/bar.php';
+        $url       = new Net_URL2($urlString);
+
+        $req1 = new HTTP_Request2();
+        $req1->setUrl($url);
+        $this->assertSame($url, $req1->getUrl());
+
+        $req2 = new HTTP_Request2();
+        $req2->setUrl($urlString);
+        $this->assertType('Net_URL2', $req2->getUrl());
+        $this->assertEquals($urlString, $req2->getUrl()->getUrl());
+
+        try {
+            $req3 = new HTTP_Request2();
+            $req3->setUrl(array('This will cause an error'));
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testConvertUserinfoToAuth()
+    {
+        $req = new HTTP_Request2();
+        $req->setUrl('http://foo:b%40r@www.example.com/');
+
+        $this->assertEquals('', (string)$req->getUrl()->getUserinfo());
+        $this->assertEquals(
+            array('user' => 'foo', 'password' => 'b@r', 'scheme' => HTTP_Request2::AUTH_BASIC),
+            $req->getAuth()
+        );
+    }
+
+    public function testSetMethod()
+    {
+        $req = new HTTP_Request2();
+        $req->setMethod(HTTP_Request2::METHOD_PUT);
+        $this->assertEquals(HTTP_Request2::METHOD_PUT, $req->getMethod());
+        try {
+            $req->setMethod('Invalid method');
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testSetAndGetConfig()
+    {
+        $req = new HTTP_Request2();
+        $this->assertArrayHasKey('connect_timeout', $req->getConfig());
+
+        $req->setConfig(array('connect_timeout' => 123));
+        $this->assertEquals(123, $req->getConfig('connect_timeout'));
+        try {
+            $req->setConfig(array('foo' => 'unknown parameter'));
+        } catch (HTTP_Request2_Exception $e) {
+            try {
+                $req->getConfig('bar');
+            } catch (HTTP_Request2_Exception $e) {
+                return;
+            }
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testHeaders()
+    {
+        $req = new HTTP_Request2();
+        $autoHeaders = $req->getHeaders();
+
+        $req->setHeader('Foo', 'Bar');
+        $req->setHeader('Foo-Bar: value');
+        $req->setHeader(array('Another-Header' => 'another value', 'Yet-Another: other_value'));
+        $this->assertEquals(
+            array('foo-bar' => 'value', 'another-header' => 'another value',
+            'yet-another' => 'other_value', 'foo' => 'Bar') + $autoHeaders,
+            $req->getHeaders()
+        );
+
+        $req->setHeader('FOO-BAR');
+        $req->setHeader(array('aNOTHER-hEADER'));
+        $this->assertEquals(
+            array('yet-another' => 'other_value', 'foo' => 'Bar') + $autoHeaders,
+            $req->getHeaders()
+        );
+
+        try {
+            $req->setHeader('Invalid header', 'value');
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testBug15937()
+    {
+        $req = new HTTP_Request2();
+        $autoHeaders = $req->getHeaders();
+
+        $req->setHeader('Expect: ');
+        $req->setHeader('Foo', '');
+        $this->assertEquals(
+            array('expect' => '', 'foo' => '') + $autoHeaders,
+            $req->getHeaders()
+        );
+    }
+
+    public function testCookies()
+    {
+        $req = new HTTP_Request2();
+        $req->addCookie('name', 'value');
+        $req->addCookie('foo', 'bar');
+        $headers = $req->getHeaders();
+        $this->assertEquals('name=value; foo=bar', $headers['cookie']);
+
+        try {
+            $req->addCookie('invalid cookie', 'value');
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testPlainBody()
+    {
+        $req = new HTTP_Request2();
+        $req->setBody('A string');
+        $this->assertEquals('A string', $req->getBody());
+
+        $req->setBody(dirname(__FILE__) . '/_files/plaintext.txt', true);
+        $headers = $req->getHeaders();
+        $this->assertContains(
+            $headers['content-type'],
+            array('text/plain', 'application/octet-stream')
+        );
+        $this->assertEquals('This is a test.', fread($req->getBody(), 1024));
+
+        try {
+            $req->setBody('missing file', true);
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testUrlencodedBody()
+    {
+        $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST);
+        $req->addPostParameter('foo', 'bar');
+        $req->addPostParameter(array('baz' => 'quux'));
+        $req->addPostParameter('foobar', array('one', 'two'));
+        $this->assertEquals(
+            'foo=bar&baz=quux&foobar%5B0%5D=one&foobar%5B1%5D=two',
+            $req->getBody()
+        );
+
+        $req->setConfig(array('use_brackets' => false));
+        $this->assertEquals(
+            'foo=bar&baz=quux&foobar=one&foobar=two',
+            $req->getBody()
+        );
+    }
+
+    public function testRequest15368()
+    {
+        $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST);
+        $req->addPostParameter('foo', 'te~st');
+        $this->assertContains('~', $req->getBody());
+    }
+
+    public function testUpload()
+    {
+        $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST);
+        $req->addUpload('upload', dirname(__FILE__) . '/_files/plaintext.txt');
+
+        $headers = $req->getHeaders();
+        $this->assertEquals('multipart/form-data', $headers['content-type']);
+
+        try {
+            $req->addUpload('upload_2', 'missing file');
+        } catch (HTTP_Request2_Exception $e) {
+            return;
+        }
+        $this->fail('Expected HTTP_Request2_Exception was not thrown');
+    }
+
+    public function testPropagateUseBracketsToNetURL2()
+    {
+        $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_GET,
+                                 array('use_brackets' => false));
+        $req->getUrl()->setQueryVariable('foo', array('bar', 'baz'));
+        $this->assertEquals('http://www.example.com/?foo=bar&foo=baz', $req->getUrl()->__toString());
+
+        $req->setConfig('use_brackets', true)->setUrl('http://php.example.com/');
+        $req->getUrl()->setQueryVariable('foo', array('bar', 'baz'));
+        $this->assertEquals('http://php.example.com/?foo[0]=bar&foo[1]=baz', $req->getUrl()->__toString());
+    }
+
+    public function testSetBodyRemovesPostParameters()
+    {
+        $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST);
+        $req->addPostParameter('foo', 'bar');
+        $req->setBody('');
+        $this->assertEquals('', $req->getBody());
+    }
+
+    public function testPostParametersPrecedeSetBodyForPost()
+    {
+        $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST);
+        $req->setBody('Request body');
+        $req->addPostParameter('foo', 'bar');
+
+        $this->assertEquals('foo=bar', $req->getBody());
+
+        $req->setMethod(HTTP_Request2::METHOD_PUT);
+        $this->assertEquals('Request body', $req->getBody());
+    }
+
+    public function testSetMultipartBody()
+    {
+        $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST);
+        $body = new HTTP_Request2_MultipartBody(array('foo' => 'bar'), array());
+        $req->setBody($body);
+        $this->assertSame($body, $req->getBody());
+    }
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/bug_15305 b/lib/tests/HTTP_Request2/HTTP/tests/_files/bug_15305
new file mode 100644
index 0000000000000000000000000000000000000000..bbf6c70006b8062fd656063ee9cbbff16edf7f04
Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/bug_15305 differ
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/empty.gif b/lib/tests/HTTP_Request2/HTTP/tests/_files/empty.gif
new file mode 100644
index 0000000000000000000000000000000000000000..1d11fa9ada9e93505b3d736acb204083f45d5fbf
Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/empty.gif differ
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/plaintext.txt b/lib/tests/HTTP_Request2/HTTP/tests/_files/plaintext.txt
new file mode 100644
index 0000000000000000000000000000000000000000..273c1a9ffdc201dbca7d19b275bf1cbc88aaa4cb
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/_files/plaintext.txt
@@ -0,0 +1 @@
+This is a test.
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_cookies b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_cookies
new file mode 100644
index 0000000000000000000000000000000000000000..8f0ab6b64d490cd12b0adc653e03211a44822b4b
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_cookies
@@ -0,0 +1,13 @@
+HTTP/1.1 200 OK
+Date: Fri, 01 Jan 2010 02:03:04 GMT
+Server: Apache/3.0.1 (Unix)
+X-Powered-By: PHP/6.2.2
+Set-Cookie: foo=bar
+Set-Cookie: PHPSESSID=1234567890abcdef1234567890abcdef; path=/; secure
+Set-Cookie: A=B=C
+Set-Cookie: baz=%20a%20value; expires=Sun, 03 Jan 2010 03:04:05 GMT; domain=pear.php.net
+Content-Type: text/html; charset=windows-1251
+Content-Length: 32
+Connection: close
+
+Nothing to see here, move along.
\ No newline at end of file
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_deflate b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_deflate
new file mode 100644
index 0000000000000000000000000000000000000000..e4e8adfd0d46af40addef96b0e95dad6243e6232
Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_deflate differ
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip
new file mode 100644
index 0000000000000000000000000000000000000000..79ad3b6eaac6737c9ae4a577347cb52e26ea5209
Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip differ
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip_broken b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip_broken
new file mode 100644
index 0000000000000000000000000000000000000000..0df0d1522f8a377ac905b20db95eb29dc1428493
Binary files /dev/null and b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_gzip_broken differ
diff --git a/lib/tests/HTTP_Request2/HTTP/tests/_files/response_headers b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_headers
new file mode 100644
index 0000000000000000000000000000000000000000..f60787bd566db41bc4680d2e6a93aba607e3a34f
--- /dev/null
+++ b/lib/tests/HTTP_Request2/HTTP/tests/_files/response_headers
@@ -0,0 +1,12 @@
+HTTP/1.1 200 OK
+Date: Fri, 01 Jan 2010 02:03:04 GMT
+Vary: accept-charset
+Server: Apache/3.0.1 (Unix)
+X-Powered-By:                PHP/6.2.2
+Vary: user-agent
+Content-Type: text/html;
+    charset=windows-1251
+Content-Length: 32
+Connection: close
+
+Nothing to see here, move along.
\ No newline at end of file
diff --git a/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/AllTests.php b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/AllTests.php
new file mode 100644
index 0000000000000000000000000000000000000000..592d9215bf38e346d6a1698c377bb72e7f5301ef
--- /dev/null
+++ b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/AllTests.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Test suite for Services_W3C_HTMLValidator
+ *
+ * PHP versions 5
+ *
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Services_W3C_HTMLValidator_AllTests::main');
+}
+
+require_once 'PHPUnit/Framework/TestSuite.php';
+require_once 'PHPUnit/TextUI/TestRunner.php';
+
+
+require_once 'Services/W3C/HTMLValidatorTest.php';
+
+/**
+ * Class for running all test suites for Services_W3C_HTMLvalidator.
+ *
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+
+class Services_W3C_HTMLValidator_AllTests
+{
+    /**
+     * Runs the test suite.
+     *
+     * @return unknown
+     */
+    public static function main()
+    {
+
+        PHPUnit_TextUI_TestRunner::run(self::suite());
+    }
+
+    /**
+     * Adds the Services_W3C_HTMLValidatorTest suite.
+     *
+     * @return $suite
+     */
+    public static function suite()
+    {
+        $suite = new PHPUnit_Framework_TestSuite('Services_W3C_HTMLValidator tests');
+        /** Add testsuites, if there is. */
+        $suite->addTestSuite('Services_W3C_HTMLValidatorTest');
+
+        return $suite;
+    }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Services_W3C_HTMLValidator_AllTests::main') {
+    Services_W3C_HTMLValidator_AllTests::main();
+}
+?>
\ No newline at end of file
diff --git a/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/Services/W3C/HTMLValidatorTest.php b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/Services/W3C/HTMLValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7c0fb70f2eee6b6935d7b78e19c67801791988e1
--- /dev/null
+++ b/lib/tests/Services_W3C_HTMLValidator/Services/W3C/tests/Services/W3C/HTMLValidatorTest.php
@@ -0,0 +1,205 @@
+<?php
+/**
+ * Test suite for the Services_W3C_HTMLValidator class
+ *
+ * PHP versions 5
+ *
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @version  CVS: $id$
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+// Call Services_W3C_HTMLValidatorTest::main() if executed directly.
+if (!defined("PHPUnit_MAIN_METHOD")) {
+    define("PHPUnit_MAIN_METHOD", "Services_W3C_HTMLValidatorTest::main");
+}
+
+require_once "PHPUnit/Framework/TestCase.php";
+require_once "PHPUnit/Framework/TestSuite.php";
+
+require_once 'Services/W3C/HTMLValidator.php';
+
+/**
+ * Test class for Services_W3C_HTMLValidator.
+ * Generated by PHPUnit_Util_Skeleton on 2007-04-09 at 10:51:02.
+ *
+ * @category Services
+ * @package  Services_W3C_HTMLValidator
+ * @author   Brett Bieber <brett.bieber@gmail.com>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD
+ * @link     http://pear.php.net/package/Services_W3C_HTMLValidator
+ * @since    File available since Release 0.2.0
+ */
+class Services_W3C_HTMLValidatorTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Runs the test methods of this class.
+     *
+     * @access public
+     * @static
+     * @return void
+     */
+    public static function main()
+    {
+        include_once "PHPUnit/TextUI/TestRunner.php";
+
+        $suite  = new PHPUnit_Framework_TestSuite("Services_W3C_HTMLValidatorTest");
+        $result = PHPUnit_TextUI_TestRunner::run($suite);
+    }
+
+    /**
+     * Sets up the fixture, for example, open a network connection.
+     * This method is called before a test is executed.
+     *
+     * @access protected
+     * @return void
+     */
+    protected function setUp()
+    {
+    }
+
+    /**
+     * Tears down the fixture, for example, close a network connection.
+     * This method is called after a test is executed.
+     *
+     * @access protected
+     * @return void
+     */
+    protected function tearDown()
+    {
+    }
+
+    /**
+     * Tests setting the options.
+     *
+     * @return void
+     */
+    public function testSetOptions()
+    {
+        $test = new Services_W3C_HTMLValidator(array(
+        'uri'=>'foo',
+        'validator_uri'=>'bar'));
+        // Test that value was set.
+        $this->assertEquals($test->uri, 'foo');
+        $this->assertEquals($test->validator_uri, 'bar');
+    }
+
+    /**
+     * Tests validating a URI
+     *
+     * @return void
+     */
+    public function testValidate()
+    {
+        $uri = 'http://www.w3.org/';
+        $v   = new Services_W3C_HTMLValidator();
+        $r   = $v->validate($uri);
+        $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response');
+        $this->assertEquals($r->isValid(), true);
+        $this->assertEquals(count($r->errors), 0);
+        $this->assertEquals($r->uri, $uri);
+        $this->assertEquals($r->checkedby.'check', $v->validator_uri);
+    }
+
+    /**
+     * Tests validating a local file.
+     *
+     * @return void
+     */
+    public function testValidateFile()
+    {
+        $v       = new Services_W3C_HTMLValidator();
+        $doc_dir = dirname(realpath(__FILE__));
+        $file    = '/../../../docs/examples/example.html';
+        $r       = $v->validateFile($doc_dir.$file);
+        $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response');
+        $this->assertEquals($r->isValid(), false);
+        $this->assertEquals($r->charset, 'utf-8');
+        $this->assertEquals(count($r->errors), 37);
+        $this->assertEquals(get_class($r->errors[0]),
+            'Services_W3C_HTMLValidator_Error');
+    }
+
+    /**
+     * Tests validating a fragment of html
+     *
+     * @return void
+     */
+    public function testValidateFragment()
+    {
+        $v = new Services_W3C_HTMLValidator();
+        $r = $v->validateFragment('<!DOCTYPE html PUBLIC '.
+        '"-//W3C//DTD XHTML 1.0 Strict//EN"
+"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head><title>HTML Fragment</title></head>
+    <body><p>TEST!</p></body>
+</html>');
+        $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response');
+        $this->assertEquals($r->isValid(), true);
+        $this->assertEquals($r->charset, 'utf-8');
+        $this->assertEquals($r->uri, 'upload://Form Submission');
+    }
+
+    /**
+     * Tests parsing the raw soap12 response.
+     *
+     * @return void
+     */
+    public function testParseSOAP12Response()
+    {
+        $v = new Services_W3C_HTMLValidator();
+        $r = $v->parseSOAP12Response('<?xml version="1.0" encoding="UTF-8"?>
+<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
+<env:Body>
+<m:markupvalidationresponse
+env:encodingStyle="http://www.w3.org/2003/05/soap-encoding"
+xmlns:m="http://www.w3.org/2005/10/markup-validator">
+    <m:uri>http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-bogus-element.html</m:uri>
+    <m:checkedby>http://validator.w3.org/</m:checkedby>
+    <m:doctype>-//W3C//DTD XHTML 1.0 Transitional//EN</m:doctype>
+    <m:charset>utf-8</m:charset>
+    <m:validity>false</m:validity>
+    <m:errors>
+        <m:errorcount>1</m:errorcount>
+        <m:errorlist>
+
+            <m:error>
+                <m:line>13</m:line>
+                <m:col>6</m:col>
+                <m:message>element "foo" undefined</m:message>
+            </m:error>
+
+        </m:errorlist>
+    </m:errors>
+    <m:warnings>
+        <m:warningcount>0</m:warningcount>
+        <m:warninglist>
+
+
+        </m:warninglist>
+    </m:warnings>
+</m:markupvalidationresponse>
+</env:Body>
+</env:Envelope>
+');
+        $this->assertEquals(get_class($r), 'Services_W3C_HTMLValidator_Response');
+        $this->assertEquals(serialize($r), 'O:35:"Services_W3C_HTMLValidator_Respo'.
+        'nse":7:{s:3:"uri";s:66:"http://qa-dev.w3.org/wmvs/HEAD/dev/tests/xhtml1-b'.
+        'ogus-element.html";s:9:"checkedby";s:24:"http://validator.w3.org/";s:7:"d'.
+        'octype";s:38:"-//W3C//DTD XHTML 1.0 Transitional//EN";s:7:"charset";s:5:"'.
+        'utf-8";s:8:"validity";b:0;s:6:"errors";a:1:{i:0;O:32:"Services_W3C_HTMLVa'.
+        'lidator_Error":6:{s:4:"line";s:2:"13";s:3:"col";s:1:"6";s:7:"message";s:2'.
+        '3:"element "foo" undefined";s:9:"messageid";N;s:11:"explanation";N;s:6:"s'.
+        'ource";N;}}s:8:"warnings";a:0:{}}');
+    }
+}
+
+// Call Services_W3C_HTMLValidatorTest::main() if file is executed directly.
+if (PHPUnit_MAIN_METHOD == "Services_W3C_HTMLValidatorTest::main") {
+    Services_W3C_HTMLValidatorTest::main();
+}
+?>
diff --git a/www/index.php b/www/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..94a270f4081401b5fda9f60cedd0a1eb14c6e3db
--- /dev/null
+++ b/www/index.php
@@ -0,0 +1,7 @@
+<?php
+
+
+require_once 'UNL/Autoload.php';
+$page = UNL_Templates::factory('Document');
+
+echo $page;