package.xml0000666000000000000000000006545212266024053010101 0ustar HTTP_Request2 pear.php.net HTTP_Request Provides an easy way to perform HTTP requests. PHP5 rewrite of HTTP_Request package (with parts of HTTP_Client). Provides cleaner API and pluggable Adapters: * 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, managing cookies across requests, proxies, gzip and deflate encodings, redirects, monitoring the request progress with Observers... Alexey Borzov avb avb@php.net yes 2014-01-16 2.2.1 2.2.0 stable stable BSD 3-Clause License * Fixed a bogus timeout Exception in Socket adapter after waiting for "100 Continue" response: the same one-second timeout was used for further socket operations if explicit 'timeout' parameter was not set. Thanks to Andrea Brancatelli (abrancatelli at schema31 dot it) for the report. * Bundled a separate LICENSE file (request #20175). Updated phrasing and links to mention 3-Clause BSD license the package actually uses. 5.2.0 1.9.2 Net_URL2 pear.php.net 2.0.0 PEAR pear.php.net 1.9.2 curl fileinfo zlib openssl 2014-01-12 2.2.0 2.2.0 stable stable BSD License New features * Socket adapter will send "Expect: 100-continue" header and wait for "100 Continue" response by default before sending large request body (request #19233). This can be disabled by setting an empty "Expect" header, the same way as done with Curl adapter (see bug #15937) * It is possible to specify a local IP address to bind to using 'local_ip' configuration parameter (request #19515) Other changes and fixes * An infinite loop was possible when using a stream wrapper instead of a regular file with MultipartBody (bug #19934) * Socket adapter will properly send chunked request body if "Transfer-Encoding: chunked" header is set for the request (bug #20125) * Updated Public Suffix List to the latest version and updated its download script * Unit tests fixes 2012-04-08 2.1.1 2.1.0 stable stable BSD License Fixes for SOCKS5 proxies support in Socket adapter 2012-04-07 2.1.0 2.1.0 stable stable BSD License New features: * Mock adapter can return responses based on request URL (request #19276) * Support for SOCKS5 proxies, added 'proxy_type' configuration parameter (request #19332) * Proxy configuration may be given as an URL, e.g. $request->setConfig('proxy', 'socks5://localhost:1080'); Other changes and fixes: * Coding standards fixes (request #14990) * Unit tests now run from SVN checkout and under PHPUnit 3.6.x * Explicit dependency on PEAR (until PEAR_Exception is a separate package) * Get rid of track_errors, use a more robust solution (bug #19337) * Additional class_exists() check in setAdapter() (request #19344) * Public suffix list updated to current version 2011-10-20 2.0.0 2.0.0 stable stable BSD License 2.0.0RC2 repackaged as stable and depending on stable Net_URL2. No code changes. 2011-10-01 2.0.0RC2 2.0.0 beta stable BSD License * Added an accessor method for HTTP_Request2_Response::$phrases (request #18716) * HTTP_Request2::send() throws an exception if URL is not provided rather than dies with a fatal error (bug #18755) * Public Suffix List updated to current version 2011-05-06 2.0.0RC1 2.0.0 beta stable BSD License SSL options for Curl Adapter are always set, this prevents errors when redirecting from HTTP to HTTPS (bug #18443) 2.0.0beta3 2.0.0 beta beta 2011-04-03 BSD License * Added getEffectiveUrl() method to Response object, it returns the URL response was received from, possibly after redirects (request #18412) * Curl Adapter didn't send body for PUT requests sometimes (bug #18421) 2.0.0beta2 2.0.0 beta beta 2011-03-25 BSD License * Unit tests can now be run under recent PHPUnit versions (3.5+) * Public Suffix List updated to current version * PHP warning produced by stream_socket_client() in Socket adapter is now added to Exception message (bug #18331) 2.0.0beta1 2.0.0 beta beta 2011-02-27 BSD License Additions and changes: * Implemented cookie jar that allows to store and pass cookies across HTTP requests (see request #18225) * Added several specialized subclasses of HTTP_Request2_Exception, they are now thrown instead of the parent. Also added error codes and possibility to get native error code (as returned by stream_socket_client() and curl_errno()) (request #16762) * An additional 'sentBody' event is now sent to Observers (request #16828) * setBody() and addUpload() can now accept file pointers (request #16863) Bugfixes: * Incorrect check in Socket Adapter prevented Keep-alive from working in some cases (bug #17031) 0.6.0 0.6.0 alpha alpha 2011-02-14 BSD License Additions and changes: * Added test suite that interacts with a webserver. Please refer to tests/NetworkConfig.php.dist for instructions. * Packaging changes: docs/ and tests/ contents are installed without redundant subdirectories. * Added a $replace parameter to HTTP_Request2::setHeader() that controls whether new header value will overwrite previous one or be appended to it (request #17507) Bugfixes: * Fixed a typo in Curl Adapter that prevented 'strict_redirects' from working * Curl Adapter will throw an exception if CURLOPT_FOLLOWLOCATION can not be enabled due to PHP setup (bug #17450) * Allow parameters in manually set Content-Type headers (bug #17460) * Properly reset redirect limit if multiple requests are performed with the same instance of Socket Adapter (bug #17826) * Response::getBody() no longer tries to decode empty strings (bug #18169) 0.5.2 0.5.0 alpha alpha 2010-04-21 BSD License * magic_quotes_runtime PHP setting could be incorrectly enabled after performing the request (bug #16440) * Unit tests fixes (bugs #17079, #17106, #17326) * Observer_Log now appends to the log file rather than rewrites it (thanks to troelskn at gmail dot com for reporting) 0.5.1 0.5.0 alpha alpha 2009-11-21 BSD License * 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. 0.5.0 0.5.0 alpha alpha 2009-11-18 BSD License * 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 0.4.1 0.4.0 alpha alpha 2009-09-14 BSD License * 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 0.4.0 0.4.0 alpha alpha 2009-05-03 BSD License * 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) 0.3.0 0.3.0 alpha alpha 2009-01-28 BSD License 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) 0.2.0 0.2.0 alpha alpha 2009-01-07 BSD License 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' => '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 0.1.0 0.1.0 alpha alpha 2008-11-17 BSD License 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) HTTP_Request2-2.2.1/HTTP/Request2/Adapter/Curl.php0000666000000000000000000005435712266024053016264 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @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 * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ 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 ); /** * Mapping of CURLE_* constants to Exception subclasses and error codes * @var array */ protected static $errorMap = array( CURLE_UNSUPPORTED_PROTOCOL => array('HTTP_Request2_MessageException', HTTP_Request2_Exception::NON_HTTP_REDIRECT), CURLE_COULDNT_RESOLVE_PROXY => array('HTTP_Request2_ConnectionException'), CURLE_COULDNT_RESOLVE_HOST => array('HTTP_Request2_ConnectionException'), CURLE_COULDNT_CONNECT => array('HTTP_Request2_ConnectionException'), // error returned from write callback CURLE_WRITE_ERROR => array('HTTP_Request2_MessageException', HTTP_Request2_Exception::NON_HTTP_REDIRECT), CURLE_OPERATION_TIMEOUTED => array('HTTP_Request2_MessageException', HTTP_Request2_Exception::TIMEOUT), CURLE_HTTP_RANGE_ERROR => array('HTTP_Request2_MessageException'), CURLE_SSL_CONNECT_ERROR => array('HTTP_Request2_ConnectionException'), CURLE_LIBRARY_NOT_FOUND => array('HTTP_Request2_LogicException', HTTP_Request2_Exception::MISCONFIGURATION), CURLE_FUNCTION_NOT_FOUND => array('HTTP_Request2_LogicException', HTTP_Request2_Exception::MISCONFIGURATION), CURLE_ABORTED_BY_CALLBACK => array('HTTP_Request2_MessageException', HTTP_Request2_Exception::NON_HTTP_REDIRECT), CURLE_TOO_MANY_REDIRECTS => array('HTTP_Request2_MessageException', HTTP_Request2_Exception::TOO_MANY_REDIRECTS), CURLE_SSL_PEER_CERTIFICATE => array('HTTP_Request2_ConnectionException'), CURLE_GOT_NOTHING => array('HTTP_Request2_MessageException'), CURLE_SSL_ENGINE_NOTFOUND => array('HTTP_Request2_LogicException', HTTP_Request2_Exception::MISCONFIGURATION), CURLE_SSL_ENGINE_SETFAILED => array('HTTP_Request2_LogicException', HTTP_Request2_Exception::MISCONFIGURATION), CURLE_SEND_ERROR => array('HTTP_Request2_MessageException'), CURLE_RECV_ERROR => array('HTTP_Request2_MessageException'), CURLE_SSL_CERTPROBLEM => array('HTTP_Request2_LogicException', HTTP_Request2_Exception::INVALID_ARGUMENT), CURLE_SSL_CIPHER => array('HTTP_Request2_ConnectionException'), CURLE_SSL_CACERT => array('HTTP_Request2_ConnectionException'), CURLE_BAD_CONTENT_ENCODING => array('HTTP_Request2_MessageException'), ); /** * 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; /** * Creates a subclass of HTTP_Request2_Exception from curl error data * * @param resource $ch curl handle * * @return HTTP_Request2_Exception */ protected static function wrapCurlError($ch) { $nativeCode = curl_errno($ch); $message = 'Curl error: ' . curl_error($ch); if (!isset(self::$errorMap[$nativeCode])) { return new HTTP_Request2_Exception($message, 0, $nativeCode); } else { $class = self::$errorMap[$nativeCode][0]; $code = empty(self::$errorMap[$nativeCode][1]) ? 0 : self::$errorMap[$nativeCode][1]; return new $class($message, $code, $nativeCode); } } /** * Sends request to the remote server and returns its response * * @param HTTP_Request2 $request HTTP request message * * @return HTTP_Request2_Response * @throws HTTP_Request2_Exception */ public function sendRequest(HTTP_Request2 $request) { if (!extension_loaded('curl')) { throw new HTTP_Request2_LogicException( 'cURL extension not available', HTTP_Request2_Exception::MISCONFIGURATION ); } $this->request = $request; $this->response = null; $this->position = 0; $this->eventSentHeaders = false; $this->eventReceivedHeaders = false; try { if (false === curl_exec($ch = $this->createCurlHandle())) { $e = self::wrapCurlError($ch); } } catch (Exception $e) { } if (isset($ch)) { $this->lastInfo = curl_getinfo($ch); curl_close($ch); } $response = $this->response; unset($this->request, $this->requestBody, $this->response); if (!empty($e)) { throw $e; } if ($jar = $request->getCookieJar()) { $jar->addCookiesFromResponse($response, $request->getUrl()); } 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_LogicException * @throws HTTP_Request2_NotImplementedException */ 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 { if (!@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true)) { throw new HTTP_Request2_LogicException( 'Redirect support in curl is unavailable due to open_basedir or safe_mode setting', HTTP_Request2_Exception::MISCONFIGURATION ); } 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 in 5.3.2+, http://bugs.php.net/bug.php?id=49571 if ($this->request->getConfig('strict_redirects') && defined('CURLOPT_POSTREDIR')) { curl_setopt($ch, CURLOPT_POSTREDIR, 3); } } // set local IP via CURLOPT_INTERFACE (request #19515) if ($ip = $this->request->getConfig('local_ip')) { curl_setopt($ch, CURLOPT_INTERFACE, $ip); } // 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; case HTTP_Request2::METHOD_PUT: curl_setopt($ch, CURLOPT_UPLOAD, 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_LogicException( 'Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE ); } 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); } } if ($type = $this->request->getConfig('proxy_type')) { switch ($type) { case 'http': curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); break; case 'socks5': curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5); break; default: throw new HTTP_Request2_NotImplementedException( "Proxy type '{$type}' is not supported" ); } } } // 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 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'] = ''; } if (($jar = $this->request->getCookieJar()) && ($cookies = $jar->getMatching($this->request->getUrl(), true)) ) { $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies; } // 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']) || isset($headers['transfer-encoding'])) { $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 $ch cURL handle * @param array &$headers 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')); } else { // rewind may be needed, read the whole body into memory 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 $ch cURL handle * @param resource $fd file descriptor (not used) * @param integer $length 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 $ch cURL handle * @param string $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; } if ($upload && (!$this->eventSentHeaders || $this->response->getStatus() >= 200) ) { $this->request->setLastEvent('sentBody', $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, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL) ); } 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); } if ($this->request->getConfig('follow_redirects') && $this->response->isRedirect()) { $redirectUrl = new Net_URL2($this->response->getHeader('location')); // for versions lower than 5.2.10, check the redirection URL protocol if (!defined('CURLOPT_REDIR_PROTOCOLS') && $redirectUrl->isAbsolute() && !in_array($redirectUrl->getScheme(), array('http', 'https')) ) { return -1; } if ($jar = $this->request->getCookieJar()) { $jar->addCookiesFromResponse($this->response, $this->request->getUrl()); if (!$redirectUrl->isAbsolute()) { $redirectUrl = $this->request->getUrl()->resolve($redirectUrl); } if ($cookies = $jar->getMatching($redirectUrl, true)) { curl_setopt($ch, CURLOPT_COOKIE, $cookies); } } } $this->eventReceivedHeaders = true; } } return strlen($string); } /** * Callback function called by cURL for saving the response body * * @param resource $ch cURL handle (not used) * @param string $string part of the response body * * @return integer number of bytes saved * @throws HTTP_Request2_MessageException * @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_MessageException( "Malformed response: {$string}", HTTP_Request2_Exception::MALFORMED_RESPONSE ); } if ($this->request->getConfig('store_body')) { $this->response->appendBody($string); } $this->request->setLastEvent('receivedBodyPart', $string); return strlen($string); } } ?> HTTP_Request2-2.2.1/HTTP/Request2/Adapter/Mock.php0000666000000000000000000001262712266024053016242 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @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() * * $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(); * * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ 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() * * Only responses without explicit URLs or with URLs equal to request URL * will be considered. If matching response is not found or the queue is * empty then default empty response with status 400 will be returned, * if an Exception object was added to the queue it will be thrown. * * @param HTTP_Request2 $request HTTP request message * * @return HTTP_Request2_Response * @throws Exception */ public function sendRequest(HTTP_Request2 $request) { $requestUrl = (string)$request->getUrl(); $response = null; foreach ($this->responses as $k => $v) { if (!$v[1] || $requestUrl == $v[1]) { $response = $v[0]; array_splice($this->responses, $k, 1); break; } } if (!$response) { return self::createResponseFromString("HTTP/1.1 400 Bad Request\r\n\r\n"); } elseif ($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); } } /** * Adds response to the queue * * @param mixed $response either a string, a pointer to an open file, * an instance of HTTP_Request2_Response or Exception * @param string $url A request URL this response should be valid for * (see {@link http://pear.php.net/bugs/bug.php?id=19276}) * * @throws HTTP_Request2_Exception */ public function addResponse($response, $url = null) { 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[] = array($response, $url); } /** * Creates a new HTTP_Request2_Response object from a string * * @param string $str string containing HTTP response message * * @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 $fp 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; } } ?>HTTP_Request2-2.2.1/HTTP/Request2/Adapter/Socket.php0000666000000000000000000012653412266024053016604 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Base class for HTTP_Request2 adapters */ require_once 'HTTP/Request2/Adapter.php'; /** Socket wrapper class */ require_once 'HTTP/Request2/SocketWrapper.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 * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ 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 HTTP_Request2_SocketWrapper * @see connect() */ protected $socket; /** * Challenge used for server digest authentication * @var array */ protected $serverChallenge; /** * Challenge used for proxy digest authentication * @var array */ protected $proxyChallenge; /** * 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; /** * Whether to wait for "100 Continue" response before sending request body * @var bool */ protected $expect100Continue = false; /** * Sends request to the remote server and returns its response * * @param HTTP_Request2 $request HTTP request message * * @return HTTP_Request2_Response * @throws HTTP_Request2_Exception */ public function sendRequest(HTTP_Request2 $request) { $this->request = $request; try { $keepAlive = $this->connect(); $headers = $this->prepareHeaders(); $this->socket->write($headers); // provide request headers to the observer, see request #7633 $this->request->setLastEvent('sentHeaders', $headers); if (!$this->expect100Continue) { $this->writeBody(); $response = $this->readResponse(); } else { $response = $this->readResponse(); if (!$response || 100 == $response->getStatus()) { $this->expect100Continue = false; // either got "100 Continue" or timed out -> send body $this->writeBody(); $response = $this->readResponse(); } } if ($jar = $request->getCookieJar()) { $jar->addCookiesFromResponse($response, $request->getUrl()); } 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)) { $this->redirectCountdown = null; throw $e; } if (!$request->getConfig('follow_redirects') || !$response->isRedirect()) { $this->redirectCountdown = null; 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; } $httpProxy = $socksProxy = false; if (!($host = $this->request->getConfig('proxy_host'))) { $host = $reqHost; $port = $reqPort; } else { if (!($port = $this->request->getConfig('proxy_port'))) { throw new HTTP_Request2_LogicException( 'Proxy port not provided', HTTP_Request2_Exception::MISSING_VALUE ); } if ('http' == ($type = $this->request->getConfig('proxy_type'))) { $httpProxy = true; } elseif ('socks5' == $type) { $socksProxy = true; } else { throw new HTTP_Request2_NotImplementedException( "Proxy type '{$type}' is not supported" ); } } if ($tunnel && !$httpProxy) { throw new HTTP_Request2_LogicException( "Trying to perform CONNECT request without proxy", HTTP_Request2_Exception::MISSING_VALUE ); } if ($secure && !in_array('ssl', stream_get_transports())) { throw new HTTP_Request2_LogicException( 'Need OpenSSL support for https:// requests', HTTP_Request2_Exception::MISCONFIGURATION ); } // RFC 2068, section 19.7.1: A client MUST NOT send the Keep-Alive // connection token to a proxy server... if ($httpProxy && !$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']); $options = array(); if ($ip = $this->request->getConfig('local_ip')) { $options['socket'] = array( 'bindto' => (false === strpos($ip, ':') ? $ip : '[' . $ip . ']') . ':0' ); } if ($secure || $tunnel) { $options['ssl'] = array(); foreach ($this->request->getConfig() as $name => $value) { if ('ssl_' == substr($name, 0, 4) && null !== $value) { if ('ssl_verify_host' == $name) { if ($value) { $options['ssl']['CN_match'] = $reqHost; } } else { $options['ssl'][substr($name, 4)] = $value; } } } ksort($options['ssl']); } // Use global request timeout if given, see feature requests #5735, #8964 if ($timeout = $this->request->getConfig('timeout')) { $deadline = time() + $timeout; } else { $deadline = null; } // Changing SSL context options after connection is established does *not* // work, we need a new connection if options change $remote = ((!$secure || $httpProxy || $socksProxy)? 'tcp://': 'ssl://') . $host . ':' . $port; $socketKey = $remote . ( ($secure && $httpProxy || $socksProxy) ? "->{$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]) && !self::$sockets[$socketKey]->eof() ) { $this->socket =& self::$sockets[$socketKey]; } else { if ($socksProxy) { require_once 'HTTP/Request2/SOCKS5.php'; $this->socket = new HTTP_Request2_SOCKS5( $remote, $this->request->getConfig('connect_timeout'), $options, $this->request->getConfig('proxy_user'), $this->request->getConfig('proxy_password') ); // handle request timeouts ASAP $this->socket->setDeadline($deadline, $this->request->getConfig('timeout')); $this->socket->connect($reqHost, $reqPort); if (!$secure) { $conninfo = "tcp://{$reqHost}:{$reqPort} via {$remote}"; } else { $this->socket->enableCrypto(); $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}"; } } elseif ($secure && $httpProxy && !$tunnel) { $this->establishTunnel(); $conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}"; } else { $this->socket = new HTTP_Request2_SocketWrapper( $remote, $this->request->getConfig('connect_timeout'), $options ); } $this->request->setLastEvent('connect', empty($conninfo)? $remote: $conninfo); self::$sockets[$socketKey] =& $this->socket; } $this->socket->setDeadline($deadline, $this->request->getConfig('timeout')); 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_ConnectionException( 'Failed to connect via HTTPS proxy. Proxy response: ' . $response->getStatus() . ' ' . $response->getReasonPhrase() ); } $this->socket = $donor->socket; $this->socket->enableCrypto(); } /** * Checks whether current connection may be reused or should be closed * * @param boolean $requestKeepAlive whether connection could * be persistent in the first place * @param HTTP_Request2_Response $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') // no body possible for such responses, see also request #17031 || HTTP_Request2::METHOD_HEAD == $this->request->getMethod() || in_array($response->getStatus(), array(204, 304)); $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 (!empty($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 $request Original request * @param HTTP_Request2_Response $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) { $this->redirectCountdown = null; // Copying cURL behaviour throw new HTTP_Request2_MessageException( 'Maximum (' . $request->getConfig('max_redirects') . ') redirects followed', HTTP_Request2_Exception::TOO_MANY_REDIRECTS ); } $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')) ) { $this->redirectCountdown = null; throw new HTTP_Request2_MessageException( 'Refusing to redirect to a non-HTTP URL ' . $redirectUrl->__toString(), HTTP_Request2_Exception::NON_HTTP_REDIRECT ); } // 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 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 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 $headerValue 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_NotImplementedException 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_NotImplementedException( "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_NotImplementedException( "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 challenge to update * @param string $headerValue 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 user name * @param string $password password * @param string $url request URL * @param array &$challenge 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 &$headers request headers * @param string $requestHost request host (needed for digest authentication) * @param string $requestUrl request URL (needed for digest authentication) * * @throws HTTP_Request2_NotImplementedException */ 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_NotImplementedException( "Unknown HTTP authentication scheme '{$auth['scheme']}'" ); } } /** * Adds 'Proxy-Authorization' header (if needed) to request headers array * * @param array &$headers request headers * @param string $requestUrl request URL (needed for digest authentication) * * @throws HTTP_Request2_NotImplementedException */ 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_NotImplementedException( "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') || 'http' != $this->request->getConfig('proxy_type') || 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'; } if (($jar = $this->request->getCookieJar()) && ($cookies = $jar->getMatching($this->request->getUrl(), true)) ) { $headers['cookie'] = (empty($headers['cookie'])? '': $headers['cookie'] . '; ') . $cookies; } $this->addAuthorizationHeader($headers, $host, $requestUrl); $this->addProxyAuthorizationHeader($headers, $requestUrl); $this->calculateRequestLength($headers); if ('1.1' == $this->request->getConfig('protocol_version')) { $this->updateExpectHeader($headers); } else { $this->expect100Continue = false; } $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"; } /** * Adds or removes 'Expect: 100-continue' header from request headers * * Also sets the $expect100Continue property. Parsing of existing header * is somewhat needed due to its complex structure and due to the * requirement in section 8.2.3 of RFC 2616: * > A client MUST NOT send an Expect request-header field (section * > 14.20) with the "100-continue" expectation if it does not intend * > to send a request body. * * @param array &$headers Array of headers prepared for the request * * @throws HTTP_Request2_LogicException * @link http://pear.php.net/bugs/bug.php?id=19233 * @link http://tools.ietf.org/html/rfc2616#section-8.2.3 */ protected function updateExpectHeader(&$headers) { $this->expect100Continue = false; $expectations = array(); if (isset($headers['expect'])) { if ('' === $headers['expect']) { // empty 'Expect' header is technically invalid, so just get rid of it unset($headers['expect']); return; } // build regexp to parse the value of existing Expect header $expectParam = ';\s*' . self::REGEXP_TOKEN . '(?:\s*=\s*(?:' . self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . '))?\s*'; $expectExtension = self::REGEXP_TOKEN . '(?:\s*=\s*(?:' . self::REGEXP_TOKEN . '|' . self::REGEXP_QUOTED_STRING . ')\s*(?:' . $expectParam . ')*)?'; $expectItem = '!(100-continue|' . $expectExtension . ')!A'; $pos = 0; $length = strlen($headers['expect']); while ($pos < $length) { $pos += strspn($headers['expect'], " \t", $pos); if (',' === substr($headers['expect'], $pos, 1)) { $pos++; continue; } elseif (!preg_match($expectItem, $headers['expect'], $m, 0, $pos)) { throw new HTTP_Request2_LogicException( "Cannot parse value '{$headers['expect']}' of Expect header", HTTP_Request2_Exception::INVALID_ARGUMENT ); } else { $pos += strlen($m[0]); if (strcasecmp('100-continue', $m[0])) { $expectations[] = $m[0]; } } } } if (1024 < $this->contentLength) { $expectations[] = '100-continue'; $this->expect100Continue = true; } if (empty($expectations)) { unset($headers['expect']); } else { $headers['expect'] = implode(',', $expectations); } } /** * Sends the request body * * @throws HTTP_Request2_MessageException */ protected function writeBody() { if (in_array($this->request->getMethod(), self::$bodyDisallowed) || 0 == $this->contentLength ) { return; } $position = 0; $bufferSize = $this->request->getConfig('buffer_size'); $headers = $this->request->getHeaders(); $chunked = isset($headers['transfer-encoding']); 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 (!$chunked) { $this->socket->write($str); } else { $this->socket->write(dechex(strlen($str)) . "\r\n{$str}\r\n"); } // Provide the length of written string to the observer, request #7630 $this->request->setLastEvent('sentBodyPart', strlen($str)); $position += strlen($str); } // write zero-length chunk if ($chunked) { $this->socket->write("0\r\n\r\n"); } $this->request->setLastEvent('sentBody', $this->contentLength); } /** * Reads the remote server's response * * @return HTTP_Request2_Response * @throws HTTP_Request2_Exception */ protected function readResponse() { $bufferSize = $this->request->getConfig('buffer_size'); // http://tools.ietf.org/html/rfc2616#section-8.2.3 // ...the client SHOULD NOT wait for an indefinite period before sending the request body $timeout = $this->expect100Continue ? 1 : null; do { try { $response = new HTTP_Request2_Response( $this->socket->readLine($bufferSize, $timeout), true, $this->request->getUrl() ); do { $headerLine = $this->socket->readLine($bufferSize); $response->parseHeaderLine($headerLine); } while ('' != $headerLine); } catch (HTTP_Request2_MessageException $e) { if (HTTP_Request2_Exception::TIMEOUT === $e->getCode() && $this->expect100Continue ) { return null; } throw $e; } if ($this->expect100Continue && 100 == $response->getStatus()) { return $response; } } 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 (!$this->socket->eof() && (is_null($toRead) || 0 < $toRead)) { if ($chunked) { $data = $this->readChunked($bufferSize); } elseif (is_null($toRead)) { $data = $this->socket->read($bufferSize); } else { $data = $this->socket->read(min($toRead, $bufferSize)); $toRead -= strlen($data); } if ('' == $data && (!$this->chunkLength || $this->socket->eof())) { 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 a part of response body encoded with chunked Transfer-Encoding * * @param int $bufferSize buffer size to use for reading * * @return string * @throws HTTP_Request2_MessageException */ protected function readChunked($bufferSize) { // at start of the next chunk? if (0 == $this->chunkLength) { $line = $this->socket->readLine($bufferSize); if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) { throw new HTTP_Request2_MessageException( "Cannot decode chunked response, invalid chunk length '{$line}'", HTTP_Request2_Exception::DECODE_ERROR ); } else { $this->chunkLength = hexdec($matches[1]); // Chunk with zero length indicates the end if (0 == $this->chunkLength) { $this->socket->readLine($bufferSize); return ''; } } } $data = $this->socket->read(min($this->chunkLength, $bufferSize)); $this->chunkLength -= strlen($data); if (0 == $this->chunkLength) { $this->socket->readLine($bufferSize); // Trailing CRLF } return $data; } } ?>HTTP_Request2-2.2.1/HTTP/Request2/Observer/Log.php0000666000000000000000000001256712266024053016304 0ustar * @author Alexey Borzov * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @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: * * 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(); * * * A more complex example with PEAR Log: * * 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(); * * * @category HTTP * @package HTTP_Request2 * @author David Jean Louis * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.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', 'sentBody', '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, 'ab'))) { 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 'sentBody': $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"); } } // }}} } ?>HTTP_Request2-2.2.1/HTTP/Request2/Adapter.php0000666000000000000000000001121212266024053015336 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @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 * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ 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 $request HTTP request message * * @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 &$headers 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'; } // Content-Length should not be sent for chunked Transfer-Encoding (bug #20125) if (!isset($headers['transfer-encoding'])) { $headers['content-length'] = $this->contentLength; } } } } ?> HTTP_Request2-2.2.1/HTTP/Request2/CookieJar.php0000666000000000000000000004322312266024053015633 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Class representing a HTTP request message */ require_once 'HTTP/Request2.php'; /** * Stores cookies and passes them between HTTP requests * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: @package_version@ * @link http://pear.php.net/package/HTTP_Request2 */ class HTTP_Request2_CookieJar implements Serializable { /** * Array of stored cookies * * The array is indexed by domain, path and cookie name * .example.com * / * some_cookie => cookie data * /subdir * other_cookie => cookie data * .example.org * ... * * @var array */ protected $cookies = array(); /** * Whether session cookies should be serialized when serializing the jar * @var bool */ protected $serializeSession = false; /** * Whether Public Suffix List should be used for domain matching * @var bool */ protected $useList = true; /** * Array with Public Suffix List data * @var array * @link http://publicsuffix.org/ */ protected static $psl = array(); /** * Class constructor, sets various options * * @param bool $serializeSessionCookies Controls serializing session cookies, * see {@link serializeSessionCookies()} * @param bool $usePublicSuffixList Controls using Public Suffix List, * see {@link usePublicSuffixList()} */ public function __construct( $serializeSessionCookies = false, $usePublicSuffixList = true ) { $this->serializeSessionCookies($serializeSessionCookies); $this->usePublicSuffixList($usePublicSuffixList); } /** * Returns current time formatted in ISO-8601 at UTC timezone * * @return string */ protected function now() { $dt = new DateTime(); $dt->setTimezone(new DateTimeZone('UTC')); return $dt->format(DateTime::ISO8601); } /** * Checks cookie array for correctness, possibly updating its 'domain', 'path' and 'expires' fields * * The checks are as follows: * - cookie array should contain 'name' and 'value' fields; * - name and value should not contain disallowed symbols; * - 'expires' should be either empty parseable by DateTime; * - 'domain' and 'path' should be either not empty or an URL where * cookie was set should be provided. * - if $setter is provided, then document at that URL should be allowed * to set a cookie for that 'domain'. If $setter is not provided, * then no domain checks will be made. * * 'expires' field will be converted to ISO8601 format from COOKIE format, * 'domain' and 'path' will be set from setter URL if empty. * * @param array $cookie cookie data, as returned by * {@link HTTP_Request2_Response::getCookies()} * @param Net_URL2 $setter URL of the document that sent Set-Cookie header * * @return array Updated cookie array * @throws HTTP_Request2_LogicException * @throws HTTP_Request2_MessageException */ protected function checkAndUpdateFields(array $cookie, Net_URL2 $setter = null) { if ($missing = array_diff(array('name', 'value'), array_keys($cookie))) { throw new HTTP_Request2_LogicException( "Cookie array should contain 'name' and 'value' fields", HTTP_Request2_Exception::MISSING_VALUE ); } if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['name'])) { throw new HTTP_Request2_LogicException( "Invalid cookie name: '{$cookie['name']}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } if (preg_match(HTTP_Request2::REGEXP_INVALID_COOKIE, $cookie['value'])) { throw new HTTP_Request2_LogicException( "Invalid cookie value: '{$cookie['value']}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } $cookie += array('domain' => '', 'path' => '', 'expires' => null, 'secure' => false); // Need ISO-8601 date @ UTC timezone if (!empty($cookie['expires']) && !preg_match('/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\+0000$/', $cookie['expires']) ) { try { $dt = new DateTime($cookie['expires']); $dt->setTimezone(new DateTimeZone('UTC')); $cookie['expires'] = $dt->format(DateTime::ISO8601); } catch (Exception $e) { throw new HTTP_Request2_LogicException($e->getMessage()); } } if (empty($cookie['domain']) || empty($cookie['path'])) { if (!$setter) { throw new HTTP_Request2_LogicException( 'Cookie misses domain and/or path component, cookie setter URL needed', HTTP_Request2_Exception::MISSING_VALUE ); } if (empty($cookie['domain'])) { if ($host = $setter->getHost()) { $cookie['domain'] = $host; } else { throw new HTTP_Request2_LogicException( 'Setter URL does not contain host part, can\'t set cookie domain', HTTP_Request2_Exception::MISSING_VALUE ); } } if (empty($cookie['path'])) { $path = $setter->getPath(); $cookie['path'] = empty($path)? '/': substr($path, 0, strrpos($path, '/') + 1); } } if ($setter && !$this->domainMatch($setter->getHost(), $cookie['domain'])) { throw new HTTP_Request2_MessageException( "Domain " . $setter->getHost() . " cannot set cookies for " . $cookie['domain'] ); } return $cookie; } /** * Stores a cookie in the jar * * @param array $cookie cookie data, as returned by * {@link HTTP_Request2_Response::getCookies()} * @param Net_URL2 $setter URL of the document that sent Set-Cookie header * * @throws HTTP_Request2_Exception */ public function store(array $cookie, Net_URL2 $setter = null) { $cookie = $this->checkAndUpdateFields($cookie, $setter); if (strlen($cookie['value']) && (is_null($cookie['expires']) || $cookie['expires'] > $this->now()) ) { if (!isset($this->cookies[$cookie['domain']])) { $this->cookies[$cookie['domain']] = array(); } if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) { $this->cookies[$cookie['domain']][$cookie['path']] = array(); } $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie; } elseif (isset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']])) { unset($this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']]); } } /** * Adds cookies set in HTTP response to the jar * * @param HTTP_Request2_Response $response HTTP response message * @param Net_URL2 $setter original request URL, needed for * setting default domain/path */ public function addCookiesFromResponse(HTTP_Request2_Response $response, Net_URL2 $setter) { foreach ($response->getCookies() as $cookie) { $this->store($cookie, $setter); } } /** * Returns all cookies matching a given request URL * * The following checks are made: * - cookie domain should match request host * - cookie path should be a prefix for request path * - 'secure' cookies will only be sent for HTTPS requests * * @param Net_URL2 $url Request url * @param bool $asString Whether to return cookies as string for "Cookie: " header * * @return array|string Matching cookies */ public function getMatching(Net_URL2 $url, $asString = false) { $host = $url->getHost(); $path = $url->getPath(); $secure = 0 == strcasecmp($url->getScheme(), 'https'); $matched = $ret = array(); foreach (array_keys($this->cookies) as $domain) { if ($this->domainMatch($host, $domain)) { foreach (array_keys($this->cookies[$domain]) as $cPath) { if (0 === strpos($path, $cPath)) { foreach ($this->cookies[$domain][$cPath] as $name => $cookie) { if (!$cookie['secure'] || $secure) { $matched[$name][strlen($cookie['path'])] = $cookie; } } } } } } foreach ($matched as $cookies) { krsort($cookies); $ret = array_merge($ret, $cookies); } if (!$asString) { return $ret; } else { $str = ''; foreach ($ret as $c) { $str .= (empty($str)? '': '; ') . $c['name'] . '=' . $c['value']; } return $str; } } /** * Returns all cookies stored in a jar * * @return array */ public function getAll() { $cookies = array(); foreach (array_keys($this->cookies) as $domain) { foreach (array_keys($this->cookies[$domain]) as $path) { foreach ($this->cookies[$domain][$path] as $name => $cookie) { $cookies[] = $cookie; } } } return $cookies; } /** * Sets whether session cookies should be serialized when serializing the jar * * @param boolean $serialize serialize? */ public function serializeSessionCookies($serialize) { $this->serializeSession = (bool)$serialize; } /** * Sets whether Public Suffix List should be used for restricting cookie-setting * * Without PSL {@link domainMatch()} will only prevent setting cookies for * top-level domains like '.com' or '.org'. However, it will not prevent * setting a cookie for '.co.uk' even though only third-level registrations * are possible in .uk domain. * * With the List it is possible to find the highest level at which a domain * may be registered for a particular top-level domain and consequently * prevent cookies set for '.co.uk' or '.msk.ru'. The same list is used by * Firefox, Chrome and Opera browsers to restrict cookie setting. * * Note that PSL is licensed differently to HTTP_Request2 package (refer to * the license information in public-suffix-list.php), so you can disable * its use if this is an issue for you. * * @param boolean $useList use the list? * * @link http://publicsuffix.org/learn/ */ public function usePublicSuffixList($useList) { $this->useList = (bool)$useList; } /** * Returns string representation of object * * @return string * * @see Serializable::serialize() */ public function serialize() { $cookies = $this->getAll(); if (!$this->serializeSession) { for ($i = count($cookies) - 1; $i >= 0; $i--) { if (empty($cookies[$i]['expires'])) { unset($cookies[$i]); } } } return serialize(array( 'cookies' => $cookies, 'serializeSession' => $this->serializeSession, 'useList' => $this->useList )); } /** * Constructs the object from serialized string * * @param string $serialized string representation * * @see Serializable::unserialize() */ public function unserialize($serialized) { $data = unserialize($serialized); $now = $this->now(); $this->serializeSessionCookies($data['serializeSession']); $this->usePublicSuffixList($data['useList']); foreach ($data['cookies'] as $cookie) { if (!empty($cookie['expires']) && $cookie['expires'] <= $now) { continue; } if (!isset($this->cookies[$cookie['domain']])) { $this->cookies[$cookie['domain']] = array(); } if (!isset($this->cookies[$cookie['domain']][$cookie['path']])) { $this->cookies[$cookie['domain']][$cookie['path']] = array(); } $this->cookies[$cookie['domain']][$cookie['path']][$cookie['name']] = $cookie; } } /** * Checks whether a cookie domain matches a request host. * * The method is used by {@link store()} to check for whether a document * at given URL can set a cookie with a given domain attribute and by * {@link getMatching()} to find cookies matching the request URL. * * @param string $requestHost request host * @param string $cookieDomain cookie domain * * @return bool match success */ public function domainMatch($requestHost, $cookieDomain) { if ($requestHost == $cookieDomain) { return true; } // IP address, we require exact match if (preg_match('/^(?:\d{1,3}\.){3}\d{1,3}$/', $requestHost)) { return false; } if ('.' != $cookieDomain[0]) { $cookieDomain = '.' . $cookieDomain; } // prevents setting cookies for '.com' and similar domains if (!$this->useList && substr_count($cookieDomain, '.') < 2 || $this->useList && !self::getRegisteredDomain($cookieDomain) ) { return false; } return substr('.' . $requestHost, -strlen($cookieDomain)) == $cookieDomain; } /** * Removes subdomains to get the registered domain (the first after top-level) * * The method will check Public Suffix List to find out where top-level * domain ends and registered domain starts. It will remove domain parts * to the left of registered one. * * @param string $domain domain name * * @return string|bool registered domain, will return false if $domain is * either invalid or a TLD itself */ public static function getRegisteredDomain($domain) { $domainParts = explode('.', ltrim($domain, '.')); // load the list if needed if (empty(self::$psl)) { $path = '@data_dir@' . DIRECTORY_SEPARATOR . 'HTTP_Request2'; if (0 === strpos($path, '@' . 'data_dir@')) { $path = realpath( dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'data' ); } self::$psl = include_once $path . DIRECTORY_SEPARATOR . 'public-suffix-list.php'; } if (!($result = self::checkDomainsList($domainParts, self::$psl))) { // known TLD, invalid domain name return false; } // unknown TLD if (!strpos($result, '.')) { // fallback to checking that domain "has at least two dots" if (2 > ($count = count($domainParts))) { return false; } return $domainParts[$count - 2] . '.' . $domainParts[$count - 1]; } return $result; } /** * Recursive helper method for {@link getRegisteredDomain()} * * @param array $domainParts remaining domain parts * @param mixed $listNode node in {@link HTTP_Request2_CookieJar::$psl} to check * * @return string|null concatenated domain parts, null in case of error */ protected static function checkDomainsList(array $domainParts, $listNode) { $sub = array_pop($domainParts); $result = null; if (!is_array($listNode) || is_null($sub) || array_key_exists('!' . $sub, $listNode) ) { return $sub; } elseif (array_key_exists($sub, $listNode)) { $result = self::checkDomainsList($domainParts, $listNode[$sub]); } elseif (array_key_exists('*', $listNode)) { $result = self::checkDomainsList($domainParts, $listNode['*']); } else { return $sub; } return (strlen($result) > 0) ? ($result . '.' . $sub) : null; } } ?>HTTP_Request2-2.2.1/HTTP/Request2/Exception.php0000666000000000000000000001204712266024053015723 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** * Base class for exceptions in PEAR */ require_once 'PEAR/Exception.php'; /** * Base exception class for HTTP_Request2 package * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=132 */ class HTTP_Request2_Exception extends PEAR_Exception { /** An invalid argument was passed to a method */ const INVALID_ARGUMENT = 1; /** Some required value was not available */ const MISSING_VALUE = 2; /** Request cannot be processed due to errors in PHP configuration */ const MISCONFIGURATION = 3; /** Error reading the local file */ const READ_ERROR = 4; /** Server returned a response that does not conform to HTTP protocol */ const MALFORMED_RESPONSE = 10; /** Failure decoding Content-Encoding or Transfer-Encoding of response */ const DECODE_ERROR = 20; /** Operation timed out */ const TIMEOUT = 30; /** Number of redirects exceeded 'max_redirects' configuration parameter */ const TOO_MANY_REDIRECTS = 40; /** Redirect to a protocol other than http(s):// */ const NON_HTTP_REDIRECT = 50; /** * Native error code * @var int */ private $_nativeCode; /** * Constructor, can set package error code and native error code * * @param string $message exception message * @param int $code package error code, one of class constants * @param int $nativeCode error code from underlying PHP extension */ public function __construct($message = null, $code = null, $nativeCode = null) { parent::__construct($message, $code); $this->_nativeCode = $nativeCode; } /** * Returns error code produced by underlying PHP extension * * For Socket Adapter this may contain error number returned by * stream_socket_client(), for Curl Adapter this will contain error number * returned by curl_errno() * * @return integer */ public function getNativeCode() { return $this->_nativeCode; } } /** * Exception thrown in case of missing features * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ class HTTP_Request2_NotImplementedException extends HTTP_Request2_Exception { } /** * Exception that represents error in the program logic * * This exception usually implies a programmer's error, like passing invalid * data to methods or trying to use PHP extensions that weren't installed or * enabled. Usually exceptions of this kind will be thrown before request even * starts. * * The exception will usually contain a package error code. * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ class HTTP_Request2_LogicException extends HTTP_Request2_Exception { } /** * Exception thrown when connection to a web or proxy server fails * * The exception will not contain a package error code, but will contain * native error code, as returned by stream_socket_client() or curl_errno(). * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ class HTTP_Request2_ConnectionException extends HTTP_Request2_Exception { } /** * Exception thrown when sending or receiving HTTP message fails * * The exception may contain both package error code and native error code. * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 */ class HTTP_Request2_MessageException extends HTTP_Request2_Exception { } ?>HTTP_Request2-2.2.1/HTTP/Request2/MultipartBody.php0000666000000000000000000002244412266024053016566 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Exception class for HTTP_Request2 package */ require_once 'HTTP/Request2/Exception.php'; /** * 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 * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 * @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 $params values of form fields set via * {@link HTTP_Request2::addPostParameter()} * @param array $uploads file uploads set via * {@link HTTP_Request2::addUpload()} * @param bool $useBrackets 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 $length Number of bytes to read * * @return string Up to $length bytes of data, empty string if at end * @throws HTTP_Request2_LogicException */ 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 ($filePos < $this->_uploads[$pos]['size']) { while ($length > 0 && !feof($this->_uploads[$pos]['fp'])) { if (false === ($chunk = fread($this->_uploads[$pos]['fp'], $length))) { throw new HTTP_Request2_LogicException( 'Failed reading file upload', HTTP_Request2_Exception::READ_ERROR ); } $ret .= $chunk; $length -= strlen($chunk); } } 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 name for item * @param mixed $values item's values * @param bool $useBrackets 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; } } } ?> HTTP_Request2-2.2.1/HTTP/Request2/SocketWrapper.php0000666000000000000000000002363312266024053016561 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Exception classes for HTTP_Request2 package */ require_once 'HTTP/Request2/Exception.php'; /** * Socket wrapper class used by Socket Adapter * * Needed to properly handle connection errors, global timeout support and * similar things. Loosely based on Net_Socket used by older HTTP_Request. * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 * @link http://pear.php.net/bugs/bug.php?id=19332 * @link http://tools.ietf.org/html/rfc1928 */ class HTTP_Request2_SocketWrapper { /** * PHP warning messages raised during stream_socket_client() call * @var array */ protected $connectionWarnings = array(); /** * Connected socket * @var resource */ protected $socket; /** * Sum of start time and global timeout, exception will be thrown if request continues past this time * @var integer */ protected $deadline; /** * Global timeout value, mostly for exception messages * @var integer */ protected $timeout; /** * Class constructor, tries to establish connection * * @param string $address Address for stream_socket_client() call, * e.g. 'tcp://localhost:80' * @param int $timeout Connection timeout (seconds) * @param array $contextOptions Context options * * @throws HTTP_Request2_LogicException * @throws HTTP_Request2_ConnectionException */ public function __construct($address, $timeout, array $contextOptions = array()) { if (!empty($contextOptions) && !isset($contextOptions['socket']) && !isset($contextOptions['ssl']) ) { // Backwards compatibility with 2.1.0 and 2.1.1 releases $contextOptions = array('ssl' => $contextOptions); } $context = stream_context_create(); foreach ($contextOptions as $wrapper => $options) { foreach ($options as $name => $value) { if (!stream_context_set_option($context, $wrapper, $name, $value)) { throw new HTTP_Request2_LogicException( "Error setting '{$wrapper}' wrapper context option '{$name}'" ); } } } set_error_handler(array($this, 'connectionWarningsHandler')); $this->socket = stream_socket_client( $address, $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $context ); restore_error_handler(); // if we fail to bind to a specified local address (see request #19515), // connection still succeeds, albeit with a warning. Throw an Exception // with the warning text in this case as that connection is unlikely // to be what user wants and as Curl throws an error in similar case. if ($this->connectionWarnings) { if ($this->socket) { fclose($this->socket); } $error = $errstr ? $errstr : implode("\n", $this->connectionWarnings); throw new HTTP_Request2_ConnectionException( "Unable to connect to {$address}. Error: {$error}", 0, $errno ); } } /** * Destructor, disconnects socket */ public function __destruct() { fclose($this->socket); } /** * Wrapper around fread(), handles global request timeout * * @param int $length Reads up to this number of bytes * * @return string Data read from socket * @throws HTTP_Request2_MessageException In case of timeout */ public function read($length) { if ($this->deadline) { stream_set_timeout($this->socket, max($this->deadline - time(), 1)); } $data = fread($this->socket, $length); $this->checkTimeout(); return $data; } /** * 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 $bufferSize buffer size to use for reading * @param int $localTimeout timeout value to use just for this call * (used when waiting for "100 Continue" response) * * @return string Available data up to the newline (not including newline) * @throws HTTP_Request2_MessageException In case of timeout */ public function readLine($bufferSize, $localTimeout = null) { $line = ''; while (!feof($this->socket)) { if (null !== $localTimeout) { stream_set_timeout($this->socket, $localTimeout); } elseif ($this->deadline) { stream_set_timeout($this->socket, max($this->deadline - time(), 1)); } $line .= @fgets($this->socket, $bufferSize); if (null === $localTimeout) { $this->checkTimeout(); } else { $info = stream_get_meta_data($this->socket); // reset socket timeout if we don't have request timeout specified, // prevents further calls failing with a bogus Exception if (!$this->deadline) { $default = (int)@ini_get('default_socket_timeout'); stream_set_timeout($this->socket, $default > 0 ? $default : PHP_INT_MAX); } if ($info['timed_out']) { throw new HTTP_Request2_MessageException( "readLine() call timed out", HTTP_Request2_Exception::TIMEOUT ); } } if (substr($line, -1) == "\n") { return rtrim($line, "\r\n"); } } return $line; } /** * Wrapper around fwrite(), handles global request timeout * * @param string $data String to be written * * @return int * @throws HTTP_Request2_MessageException */ public function write($data) { if ($this->deadline) { stream_set_timeout($this->socket, max($this->deadline - time(), 1)); } $written = fwrite($this->socket, $data); $this->checkTimeout(); // http://www.php.net/manual/en/function.fwrite.php#96951 if ($written < strlen($data)) { throw new HTTP_Request2_MessageException('Error writing request'); } return $written; } /** * Tests for end-of-file on a socket * * @return bool */ public function eof() { return feof($this->socket); } /** * Sets request deadline * * @param int $deadline Exception will be thrown if request continues * past this time * @param int $timeout Original request timeout value, to use in * Exception message */ public function setDeadline($deadline, $timeout) { $this->deadline = $deadline; $this->timeout = $timeout; } /** * Turns on encryption on a socket * * @throws HTTP_Request2_ConnectionException */ public function enableCrypto() { $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_ConnectionException( 'Failed to enable secure connection when connecting through proxy' ); } /** * Throws an Exception if stream timed out * * @throws HTTP_Request2_MessageException */ protected function checkTimeout() { $info = stream_get_meta_data($this->socket); if ($info['timed_out'] || $this->deadline && time() > $this->deadline) { $reason = $this->deadline ? "after {$this->timeout} second(s)" : 'due to default_socket_timeout php.ini setting'; throw new HTTP_Request2_MessageException( "Request timed out {$reason}", HTTP_Request2_Exception::TIMEOUT ); } } /** * Error handler to use during stream_socket_client() call * * One stream_socket_client() call may produce *multiple* PHP warnings * (especially OpenSSL-related), we keep them in an array to later use for * the message of HTTP_Request2_ConnectionException * * @param int $errno error level * @param string $errstr error message * * @return bool */ protected function connectionWarningsHandler($errno, $errstr) { if ($errno & E_WARNING) { array_unshift($this->connectionWarnings, $errstr); } return true; } } ?> HTTP_Request2-2.2.1/HTTP/Request2/SOCKS5.php0000666000000000000000000001136412266024053014735 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Socket wrapper class used by Socket Adapter */ require_once 'HTTP/Request2/SocketWrapper.php'; /** * SOCKS5 proxy connection class (used by Socket Adapter) * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 * @link http://pear.php.net/bugs/bug.php?id=19332 * @link http://tools.ietf.org/html/rfc1928 */ class HTTP_Request2_SOCKS5 extends HTTP_Request2_SocketWrapper { /** * Constructor, tries to connect and authenticate to a SOCKS5 proxy * * @param string $address Proxy address, e.g. 'tcp://localhost:1080' * @param int $timeout Connection timeout (seconds) * @param array $contextOptions Stream context options * @param string $username Proxy user name * @param string $password Proxy password * * @throws HTTP_Request2_LogicException * @throws HTTP_Request2_ConnectionException * @throws HTTP_Request2_MessageException */ public function __construct( $address, $timeout = 10, array $contextOptions = array(), $username = null, $password = null ) { parent::__construct($address, $timeout, $contextOptions); if (strlen($username)) { $request = pack('C4', 5, 2, 0, 2); } else { $request = pack('C3', 5, 1, 0); } $this->write($request); $response = unpack('Cversion/Cmethod', $this->read(3)); if (5 != $response['version']) { throw new HTTP_Request2_MessageException( 'Invalid version received from SOCKS5 proxy: ' . $response['version'], HTTP_Request2_Exception::MALFORMED_RESPONSE ); } switch ($response['method']) { case 2: $this->performAuthentication($username, $password); case 0: break; default: throw new HTTP_Request2_ConnectionException( "Connection rejected by proxy due to unsupported auth method" ); } } /** * Performs username/password authentication for SOCKS5 * * @param string $username Proxy user name * @param string $password Proxy password * * @throws HTTP_Request2_ConnectionException * @throws HTTP_Request2_MessageException * @link http://tools.ietf.org/html/rfc1929 */ protected function performAuthentication($username, $password) { $request = pack('C2', 1, strlen($username)) . $username . pack('C', strlen($password)) . $password; $this->write($request); $response = unpack('Cvn/Cstatus', $this->read(3)); if (1 != $response['vn'] || 0 != $response['status']) { throw new HTTP_Request2_ConnectionException( 'Connection rejected by proxy due to invalid username and/or password' ); } } /** * Connects to a remote host via proxy * * @param string $remoteHost Remote host * @param int $remotePort Remote port * * @throws HTTP_Request2_ConnectionException * @throws HTTP_Request2_MessageException */ public function connect($remoteHost, $remotePort) { $request = pack('C5', 0x05, 0x01, 0x00, 0x03, strlen($remoteHost)) . $remoteHost . pack('n', $remotePort); $this->write($request); $response = unpack('Cversion/Creply/Creserved', $this->read(1024)); if (5 != $response['version'] || 0 != $response['reserved']) { throw new HTTP_Request2_MessageException( 'Invalid response received from SOCKS5 proxy', HTTP_Request2_Exception::MALFORMED_RESPONSE ); } elseif (0 != $response['reply']) { throw new HTTP_Request2_ConnectionException( "Unable to connect to {$remoteHost}:{$remotePort} through SOCKS5 proxy", 0, $response['reply'] ); } } } ?>HTTP_Request2-2.2.1/HTTP/Request2/Response.php0000666000000000000000000005237112266024053015567 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @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: * * $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()); * * * @category HTTP * @package HTTP_Request2 * @author Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 * @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; /** * Effective URL (may be different from original request URL in case of redirects) * @var string */ protected $effectiveUrl; /** * 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', ); /** * Returns the default reason phrase for the given code or all reason phrases * * @param int $code Response code * * @return string|array|null Default reason phrase for $code if $code is given * (null if no phrase is available), array of all * reason phrases if $code is null * @link http://pear.php.net/bugs/18716 */ public static function getDefaultReasonPhrase($code = null) { if (null === $code) { return self::$phrases; } else { return isset(self::$phrases[$code]) ? self::$phrases[$code] : null; } } /** * Constructor, parses the response status line * * @param string $statusLine Response status line (e.g. "HTTP/1.1 200 OK") * @param bool $bodyEncoded Whether body is still encoded by Content-Encoding * @param string $effectiveUrl Effective URL of the response * * @throws HTTP_Request2_MessageException if status line is invalid according to spec */ public function __construct($statusLine, $bodyEncoded = true, $effectiveUrl = null) { if (!preg_match('!^HTTP/(\d\.\d) (\d{3})(?: (.+))?!', $statusLine, $m)) { throw new HTTP_Request2_MessageException( "Malformed response: {$statusLine}", HTTP_Request2_Exception::MALFORMED_RESPONSE ); } $this->version = $m[1]; $this->code = intval($m[2]); $this->reasonPhrase = !empty($m[3]) ? trim($m[3]) : self::getDefaultReasonPhrase($this->code); $this->bodyEncoded = (bool)$bodyEncoded; $this->effectiveUrl = (string)$effectiveUrl; } /** * 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 $headerLine Line from HTTP response */ public function parseHeaderLine($headerLine) { $headerLine = trim($headerLine, "\r\n"); if ('' == $headerLine) { // empty string signals the end of headers, process the received ones 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]); } } } elseif (preg_match('!^([^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+):(.+)$!', $headerLine, $m)) { // string of the form header-name: header value $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; } elseif (preg_match('!^\s+(.+)$!', $headerLine, $m) && $this->lastHeader) { // continuation of a previous header 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 $cookieString 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 ); if (!strpos($cookieString, ';')) { // Only a name=value pair $pos = strpos($cookieString, '='); $cookie['name'] = trim(substr($cookieString, 0, $pos)); $cookie['value'] = trim(substr($cookieString, $pos + 1)); } else { // Some optional parameters are supplied $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 $bodyChunk part of response body */ public function appendBody($bodyChunk) { $this->body .= $bodyChunk; } /** * Returns the effective URL of the response * * This may be different from the request URL if redirects were followed. * * @return string * @link http://pear.php.net/bugs/bug.php?id=18412 */ public function getEffectiveUrl() { return $this->effectiveUrl; } /** * 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 $headerName 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 (0 == strlen($this->body) || !$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('8bit'); } 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 $data gzip-encoded data * * @return string decoded data * @throws HTTP_Request2_LogicException * @throws HTTP_Request2_MessageException * @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_LogicException( 'Unable to decode body: gzip extension not available', HTTP_Request2_Exception::MISCONFIGURATION ); } $method = ord(substr($data, 2, 1)); if (8 != $method) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: unknown compression method', HTTP_Request2_Exception::DECODE_ERROR ); } $flags = ord(substr($data, 3, 1)); if ($flags & 224) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: reserved bits are set', HTTP_Request2_Exception::DECODE_ERROR ); } // 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_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $extraLength = unpack('v', substr($data, 10, 2)); if ($length - $headerLength - 2 - $extraLength[1] < 8) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $headerLength += $extraLength[1] + 2; } // file name, need to skip that if ($flags & 8) { if ($length - $headerLength - 1 < 8) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $filenameLength = strpos(substr($data, $headerLength), chr(0)); if (false === $filenameLength || $length - $headerLength - $filenameLength - 1 < 8) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $headerLength += $filenameLength + 1; } // comment, need to skip that also if ($flags & 16) { if ($length - $headerLength - 1 < 8) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $commentLength = strpos(substr($data, $headerLength), chr(0)); if (false === $commentLength || $length - $headerLength - $commentLength - 1 < 8) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $headerLength += $commentLength + 1; } // have a CRC for header. let's check if ($flags & 2) { if ($length - $headerLength - 2 < 8) { throw new HTTP_Request2_MessageException( 'Error parsing gzip header: data too short', HTTP_Request2_Exception::DECODE_ERROR ); } $crcReal = 0xffff & crc32(substr($data, 0, $headerLength)); $crcStored = unpack('v', substr($data, $headerLength, 2)); if ($crcReal != $crcStored[1]) { throw new HTTP_Request2_MessageException( 'Header CRC check failed', HTTP_Request2_Exception::DECODE_ERROR ); } $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_MessageException( 'gzinflate() call failed', HTTP_Request2_Exception::DECODE_ERROR ); } elseif ($dataSize != strlen($unpacked)) { throw new HTTP_Request2_MessageException( 'Data size check failed', HTTP_Request2_Exception::DECODE_ERROR ); } elseif ((0xffffffff & $dataCrc) != (0xffffffff & crc32($unpacked))) { throw new HTTP_Request2_Exception( 'Data CRC check failed', HTTP_Request2_Exception::DECODE_ERROR ); } return $unpacked; } /** * Decodes the message-body encoded by deflate * * @param string $data deflate-encoded data * * @return string decoded data * @throws HTTP_Request2_LogicException */ public static function decodeDeflate($data) { if (!function_exists('gzuncompress')) { throw new HTTP_Request2_LogicException( 'Unable to decode body: gzip extension not available', HTTP_Request2_Exception::MISCONFIGURATION ); } // 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); } } ?>HTTP_Request2-2.2.1/HTTP/Request2.php0000666000000000000000000011023312266024053013761 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @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 * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @version Release: 2.2.1 * @link http://pear.php.net/package/HTTP_Request2 * @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, 'local_ip' => null, 'proxy_host' => '', 'proxy_port' => '', 'proxy_user' => '', 'proxy_password' => '', 'proxy_auth_scheme' => self::AUTH_BASIC, 'proxy_type' => 'http', '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; /** * Cookie jar to persist cookies between requests * @var HTTP_Request2_CookieJar */ protected $cookieJar = null; /** * Constructor. Can set request URL, method and configuration array. * * Also sets a default value for User-Agent header. * * @param string|Net_Url2 $url Request URL * @param string $method Request method * @param array $config 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/2.2.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 $url Request URL * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException */ 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_LogicException( 'Parameter is not a valid HTTP URL', HTTP_Request2_Exception::INVALID_ARGUMENT ); } // 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 $method one of the methods defined in RFC 2616 * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException 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_LogicException( "Invalid request method '{$method}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } $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: *
    *
  • 'adapter' - adapter to use (string)
  • *
  • 'connect_timeout' - Connection timeout in seconds (integer)
  • *
  • 'timeout' - Total number of seconds a request can take. * Use 0 for no limit, should be greater than * 'connect_timeout' if set (integer)
  • *
  • 'use_brackets' - Whether to append [] to array variable names (bool)
  • *
  • 'protocol_version' - HTTP Version to use, '1.0' or '1.1' (string)
  • *
  • 'buffer_size' - Buffer size to use for reading and writing (int)
  • *
  • '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)
  • *
  • 'local_ip' - Specifies the IP address that will be used for accessing * the network (string)
  • *
  • 'proxy_type' - Proxy type, 'http' or 'socks5' (string)
  • *
  • 'proxy_host' - Proxy server host (string)
  • *
  • 'proxy_port' - Proxy server port (integer)
  • *
  • 'proxy_user' - Proxy auth username (string)
  • *
  • 'proxy_password' - Proxy auth password (string)
  • *
  • 'proxy_auth_scheme' - Proxy auth scheme, one of HTTP_Request2::AUTH_* constants (string)
  • *
  • 'proxy' - Shorthand for proxy_* parameters, proxy given as URL, * e.g. 'socks5://localhost:1080/' (string)
  • *
  • 'ssl_verify_peer' - Whether to verify peer's SSL certificate (bool)
  • *
  • 'ssl_verify_host' - Whether to check that Common Name in SSL * certificate matches host name (bool)
  • *
  • 'ssl_cafile' - Cerificate Authority file to verify the peer * with (use with 'ssl_verify_peer') (string)
  • *
  • 'ssl_capath' - Directory holding multiple Certificate * Authority files (string)
  • *
  • 'ssl_local_cert' - Name of a file containing local cerificate (string)
  • *
  • 'ssl_passphrase' - Passphrase with which local certificate * was encoded (string)
  • *
  • 'digest_compat_ie' - Whether to imitate behaviour of MSIE 5 and 6 * in using URL without query string in digest * authentication (boolean)
  • *
  • 'follow_redirects' - Whether to automatically follow HTTP Redirects (boolean)
  • *
  • 'max_redirects' - Maximum number of redirects to follow (integer)
  • *
  • '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)
  • *
* * @param string|array $nameOrConfig configuration parameter name or array * ('parameter name' => 'parameter value') * @param mixed $value parameter value if $nameOrConfig is not an array * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException If the parameter is unknown */ public function setConfig($nameOrConfig, $value = null) { if (is_array($nameOrConfig)) { foreach ($nameOrConfig as $name => $value) { $this->setConfig($name, $value); } } elseif ('proxy' == $nameOrConfig) { $url = new Net_URL2($value); $this->setConfig(array( 'proxy_type' => $url->getScheme(), 'proxy_host' => $url->getHost(), 'proxy_port' => $url->getPort(), 'proxy_user' => rawurldecode($url->getUser()), 'proxy_password' => rawurldecode($url->getPassword()) )); } else { if (!array_key_exists($nameOrConfig, $this->config)) { throw new HTTP_Request2_LogicException( "Unknown configuration parameter '{$nameOrConfig}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } $this->config[$nameOrConfig] = $value; } return $this; } /** * Returns the value(s) of the configuration parameter(s) * * @param string $name parameter name * * @return mixed value of $name parameter, array of all configuration * parameters if $name is not given * @throws HTTP_Request2_LogicException 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_LogicException( "Unknown configuration parameter '{$name}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } return $this->config[$name]; } /** * Sets the autentification data * * @param string $user user name * @param string $password password * @param string $scheme 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. * * * $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 * * * @param string|array $name header name, header string ('Header: value') * or an array of headers * @param string|array|null $value header value if $name is not an array, * header will be removed if value is null * @param bool $replace whether to replace previous header with the * same name or append to its value * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException */ public function setHeader($name, $value = null, $replace = true) { if (is_array($name)) { foreach ($name as $k => $v) { if (is_string($k)) { $this->setHeader($k, $v, $replace); } else { $this->setHeader($v, null, $replace); } } } 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_LogicException( "Invalid header name '{$name}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } // Header names are case insensitive anyway $name = strtolower($name); if (null === $value) { unset($this->headers[$name]); } else { if (is_array($value)) { $value = implode(', ', array_map('trim', $value)); } elseif (is_string($value)) { $value = trim($value); } if (!isset($this->headers[$name]) || $replace) { $this->headers[$name] = $value; } 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; } /** * Adds a cookie to the request * * If the request does not have a CookieJar object set, this method simply * appends a cookie to "Cookie:" header. * * If a CookieJar object is available, the cookie is stored in that object. * Data from request URL will be used for setting its 'domain' and 'path' * parameters, 'expires' and 'secure' will be set to null and false, * respectively. If you need further control, use CookieJar's methods. * * @param string $name cookie name * @param string $value cookie value * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException * @see setCookieJar() */ public function addCookie($name, $value) { if (!empty($this->cookieJar)) { $this->cookieJar->store( array('name' => $name, 'value' => $value), $this->url ); } else { $cookie = $name . '=' . $value; if (preg_match(self::REGEXP_INVALID_COOKIE, $cookie)) { throw new HTTP_Request2_LogicException( "Invalid cookie: '{$cookie}'", HTTP_Request2_Exception::INVALID_ARGUMENT ); } $cookies = empty($this->headers['cookie'])? '': $this->headers['cookie'] . '; '; $this->setHeader('cookie', $cookies . $cookie); } return $this; } /** * Sets the request body * * If you provide file pointer rather than file name, it should support * fstat() and rewind() operations. * * @param string|resource|HTTP_Request2_MultipartBody $body Either a * string with the body or filename containing body or * pointer to an open file or object with multipart body data * @param bool $isFilename Whether * first parameter is a filename * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException */ public function setBody($body, $isFilename = false) { if (!$isFilename && !is_resource($body)) { if (!$body instanceof HTTP_Request2_MultipartBody) { $this->body = (string)$body; } else { $this->body = $body; } } else { $fileData = $this->fopenWrapper($body, empty($this->headers['content-type'])); $this->body = $fileData['fp']; if (empty($this->headers['content-type'])) { $this->setHeader('content-type', $fileData['type']); } } $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 (0 === strpos($this->headers['content-type'], 'application/x-www-form-urlencoded')) { $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 (0 === strpos($this->headers['content-type'], 'multipart/form-data')) { 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. * * If you provide file pointers rather than file names, they should support * fstat() and rewind() operations. * * @param string $fieldName name of file-upload field * @param string|resource|array $filename full name of local file, * pointer to open file or an array of files * @param string $sendFilename filename to send in the request * @param string $contentType content-type of file being uploaded * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException */ public function addUpload( $fieldName, $filename, $sendFilename = null, $contentType = null ) { if (!is_array($filename)) { $fileData = $this->fopenWrapper($filename, empty($contentType)); $this->uploads[$fieldName] = array( 'fp' => $fileData['fp'], 'filename' => !empty($sendFilename)? $sendFilename :(is_string($filename)? basename($filename): 'anonymous.blob') , 'size' => $fileData['size'], 'type' => empty($contentType)? $fileData['type']: $contentType ); } else { $fps = $names = $sizes = $types = array(); foreach ($filename as $f) { if (!is_array($f)) { $f = array($f); } $fileData = $this->fopenWrapper($f[0], empty($f[2])); $fps[] = $fileData['fp']; $names[] = !empty($f[1])? $f[1] :(is_string($f[0])? basename($f[0]): 'anonymous.blob'); $sizes[] = $fileData['size']; $types[] = empty($f[2])? $fileData['type']: $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 $name parameter name or array ('name' => 'value') * @param mixed $value 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 $observer any object implementing SplObserver */ public function attach(SplObserver $observer) { foreach ($this->observers as $attached) { if ($attached === $observer) { return; } } $this->observers[] = $observer; } /** * Detaches an existing observer * * @param SplObserver $observer any object implementing 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 $name event name * @param mixed $data 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: *
    *
  • 'connect' - after connection to remote server, * data is the destination (string)
  • *
  • 'disconnect' - after disconnection from server
  • *
  • 'sentHeaders' - after sending the request headers, * data is the headers sent (string)
  • *
  • 'sentBodyPart' - after sending a part of the request body, * data is the length of that part (int)
  • *
  • 'sentBody' - after sending the whole request body, * data is request body length (int)
  • *
  • 'receivedHeaders' - after receiving the response headers, * data is HTTP_Request2_Response object
  • *
  • 'receivedBodyPart' - after receiving a part of the response * body, data is that part (string)
  • *
  • 'receivedEncodedBodyPart' - as 'receivedBodyPart', but data is still * encoded by Content-Encoding
  • *
  • 'receivedBody' - after receiving the complete response * body, data is HTTP_Request2_Response object
  • *
* 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 * * $request->setAdapter('curl'); * * will work. * * @param string|HTTP_Request2_Adapter $adapter Adapter to use * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException */ public function setAdapter($adapter) { if (is_string($adapter)) { if (!class_exists($adapter, false)) { if (false === strpos($adapter, '_')) { $adapter = 'HTTP_Request2_Adapter_' . ucfirst($adapter); } if (!class_exists($adapter, false) && 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_LogicException( "Class {$adapter} not found", HTTP_Request2_Exception::MISSING_VALUE ); } } $adapter = new $adapter; } if (!$adapter instanceof HTTP_Request2_Adapter) { throw new HTTP_Request2_LogicException( 'Parameter is not a HTTP request adapter', HTTP_Request2_Exception::INVALID_ARGUMENT ); } $this->adapter = $adapter; return $this; } /** * Sets the cookie jar * * A cookie jar is used to maintain cookies across HTTP requests and * responses. Cookies from jar will be automatically added to the request * headers based on request URL. * * @param HTTP_Request2_CookieJar|bool $jar Existing CookieJar object, true to * create a new one, false to remove * * @return HTTP_Request2 * @throws HTTP_Request2_LogicException */ public function setCookieJar($jar = true) { if (!class_exists('HTTP_Request2_CookieJar', false)) { require_once 'HTTP/Request2/CookieJar.php'; } if ($jar instanceof HTTP_Request2_CookieJar) { $this->cookieJar = $jar; } elseif (true === $jar) { $this->cookieJar = new HTTP_Request2_CookieJar(); } elseif (!$jar) { $this->cookieJar = null; } else { throw new HTTP_Request2_LogicException( 'Invalid parameter passed to setCookieJar()', HTTP_Request2_Exception::INVALID_ARGUMENT ); } return $this; } /** * Returns current CookieJar object or null if none * * @return HTTP_Request2_CookieJar|null */ public function getCookieJar() { return $this->cookieJar; } /** * 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 || !$this->url->isAbsolute() || !in_array(strtolower($this->url->getScheme()), array('https', 'http')) ) { throw new HTTP_Request2_LogicException( 'HTTP_Request2 needs an absolute HTTP(S) request URL, ' . ($this->url instanceof Net_URL2 ? "'" . $this->url->__toString() . "'" : 'none') . ' given', HTTP_Request2_Exception::INVALID_ARGUMENT ); } if (empty($this->adapter)) { $this->setAdapter($this->getConfig('adapter')); } // magic_quotes_runtime may break file uploads and chunked response // processing; see bug #4543. Don't use ini_get() here; see bug #16440. if ($magicQuotes = get_magic_quotes_runtime()) { 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('8bit'); } try { $response = $this->adapter->sendRequest($this); } catch (Exception $e) { } // cleanup in either case (poor man's "finally" clause) if ($magicQuotes) { set_magic_quotes_runtime(true); } if (!empty($oldEncoding)) { mb_internal_encoding($oldEncoding); } // rethrow the exception if (!empty($e)) { throw $e; } return $response; } /** * Wrapper around fopen()/fstat() used by setBody() and addUpload() * * @param string|resource $file file name or pointer to open file * @param bool $detectType whether to try autodetecting MIME * type of file, will only work if $file is a * filename, not pointer * * @return array array('fp' => file pointer, 'size' => file size, 'type' => MIME type) * @throws HTTP_Request2_LogicException */ protected function fopenWrapper($file, $detectType = false) { if (!is_string($file) && !is_resource($file)) { throw new HTTP_Request2_LogicException( "Filename or file pointer resource expected", HTTP_Request2_Exception::INVALID_ARGUMENT ); } $fileData = array( 'fp' => is_string($file)? null: $file, 'type' => 'application/octet-stream', 'size' => 0 ); if (is_string($file)) { if (!($fileData['fp'] = @fopen($file, 'rb'))) { $error = error_get_last(); throw new HTTP_Request2_LogicException( $error['message'], HTTP_Request2_Exception::READ_ERROR ); } if ($detectType) { $fileData['type'] = self::detectMimeType($file); } } if (!($stat = fstat($fileData['fp']))) { throw new HTTP_Request2_LogicException( "fstat() call failed", HTTP_Request2_Exception::READ_ERROR ); } $fileData['size'] = $stat['size']; return $fileData; } /** * 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 file name * * @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; } } ?> HTTP_Request2-2.2.1/tests/_files/bug_153050000666000000000000000000003772212266024053014632 0ustar HTTP/1.1 200 Ok Date: Tue, 06 Jan 2009 20:53:22 GMT Server: Microsoft-IIS/6.0 P3p: CP="BUS CUR CONo FIN IVDo ONL OUR PHY SAMo TELo" S: TK2PTLRENB01 X-Powered-By: ASP.NET X-Aspnet-Version: 2.0.50727 Pragma: no-cache Cache-Control: no-cache Content-Type: text/html; charset=utf-8 Content-Encoding: deflate Content-Length: 15998 vH&*ΔT3eJd)e,9]y\n$a%+/k }~61qPΜna{C RwB,>yĬ #wg* +4cy8:cE%ߗO~uG>9VeB4⍣;( sXx,gp4ʖ?MJl̥~|@(6VHӯ(@ uaĭűѿil<eFdkaSk\@ x,np} JbI$HUƮorbٝUF΁A!݂3cQTs^꿺ΉWWKD$ǃu,U@x-"+tzz-N NB\)K(ɡ|y9#E,ØEF"2KqYDo]x(1ɝc VMq~Q (r"<'~P+ܳxbXdn~uYMfusڡWW'|k^坽zܬεj\oU_W:{})y@`S.vgp] yPy Tw*5J}AxL?;/_{rOT/Cا1 %9yϋ^Y4)+!wy/AU2$s@,5v@:";dTmɼ2nj-Q'+Jxh#~ܵͣRȚ8d"Ay&%@#gJA ӥ,jCB&_k~QP0jNwnӱaP $E e}KVh3~ O׹!ŏE_As"3+$>~ s_8? DD]ޟ@ L_8_qUQK2Xt$U$}=8LO,cu FKem2IȪ C!QQZדEa\[w9qC bԖ)f]Kv*FqY9 x $}՘~X{!ewD O`tTJǠ9QЂL̫).// (qBDKSQqX(fx'bqLis2^ |QsjЃ/+?&V⸰ ,#s6L?['2՚fHKr.Gr! Kl"Gcw hU 5qL)XDc*I`w{͹>jdC,p$>N(.h<3˱f?8 {W)a\[ɊZs,0:Gš`@./o!\ ŁspOA=8!agQ3EǙRpfFp*THawM `J?Tz)vćJs }5h5;#3X%xy=Ϻ4TE I zz9q2V^OjJ0i!3M4A=Iu}kM8'V/ ~{`BܞxI77O;ʒi%Q\$I<M sl8QD؉UV[KU؝ #֢Q(PE`BS3 }P|p-;ul—"80Ch4xӀ,!!^mAσMݨ' B⵰jըI} \-=/!'!\I i yt.y _9Z5g\Ae;wec<$Γ8;`su oB1:Y5RW(q:ܨ;35/J/mj:3+A($ 8Ȧkp5vF'ӭ4n7umuM7q]AKV H |.O+<=V`#yR $,FA6+X!fO1>%5bWF`gQ=b6'Ww"%‡F# _R y ~s#zW]n:¿(鳈lνPZ_³0.j$|%Ԧhs[ 4If\zL"/_8;?>~6)Ru 81LbZ8v[T=!TBR hmDi  -H0/!|M=hx3Vf x}l~84 2DZޘ>;_[ДcC4b4!`[5hr3(%TֈBJ2ˉIΑ8r]Xe#(rv&WU_o1+FRE,t3S MӜ߉dx@\\|gyTGdY2u}R\OwQ꼹~n7$:vb⇷?_5Wl⬝5}hdeߙqSz zګXLqzAZ=_F~Wo/,i9IiI˚%ӷ9TWEVq\KcdCAKW~ sQ;,,|$ 2Oѕ= :z!K= m:1?S'*/B%ZJth6k p(03)Ah&pd 28gڤ4cA I"gZV6OY˜ȡrln(Ih UPw@zjE9Op~E'4:l,E:U$ a VX'S ][oK9SM#Vuk>j5dˈmM$ k-cpo(tgGse֘Lks8&alna>U4C3}z&*8esȬxlgz.R3gf>Y.a33pG%[yX6*Oc4NߨTZcxj^oճAe z=\ \/$ldbs7,Erv :OӅgxZȟ u3|q_zF{[kUZ3vZ1<;aόVMk?l1jwWO!Uw1ִZ?;hՆQ= CkxPDpa9ܝG} &iطSJspZojv5(fgϧz}C8y/7wv/-S:-.ǺqTG)ieA0_8Io>Rx+֫>wg'=1L  H3vn]o荪Ĭ=OVuV8"f W;+Nj+4u.E[I%ΩnWwLÉ{!8no>Ả=C߂C(]1Le(}TA{;^W>{nֱʛeEԄ`6A)Q89̇,o9Y1\_P7 $]7+hGE0*FA6&% Ya9s\0"'ˠ4n[4iՐmRYX8k|0(Y͑I#W\߼8Z8' {m}i}PѐV4F^"*6Y@n;= L/2ab@slbIY)Z*dHi>$k9_Jݛ邇R^QL3S9iH{_ܤMu;Aw @$s'&|o m!OlN,RL\DIIѺɀ`> C$ֽQtRboWcWV]kՓ7zV4\*_z-9zլikTAn sf|iɀz׭Tkߥ wN8~aORIo<iE14 &?R3QRjMj_OQl6w(i ѪZZJ#F#ZXQ!h}ᒉ `n]bG| alFBMhެVFU ΜI;2\1%8ݵJFVɷ>]12G3'&]:#?)FĮ*S+%Ƣ G g)8Rp"t GzZJL![zZi }۽A9lFsXZ^$X+oh,`&&Z{g1O]٘]DRh]z}^T5W>j VNdj^PĎTv Jiن ;Ʊޭ %ӂ!p,@з3]H௠xJ/a~6%隚T& ]/,ΔWlBD5~]{)Ua 3;gA]!nu>'l^S`8ْFp[@>Zx0L/Dk^L$اaH{C!Zӌv[kϱ{q> TvK1Ā֒QQ$vrPM^R釷/C1D[(BA\dk.$toL].M,Ť Wu)ѴK~- jXLXC_&3(Oܳ Dv V-l"Z\mq;&RoV^G i/$#+$qigSɟ%Kq?oO-_>D%<3bĻ<ȋCfY|DQ2K oV()PikjM*/^nY<:+^~XQ,)9d%+]$2hZm˞UZoXkZ_kMcخ׌`ZndR6WxT+x %ߍ|o5R"@UŏIovRJQ(ߔ[B &<kwQUإQ\j/hzU=8Ʌ}KL 9ICEP9 b/֯&iB6y 썋EɥN#\dw^ka|nQ- zB[] 'CT۠(b}_xL|j)\lٵZN F""F7-Y,sCLBI,~)L/Q#d5ΒÑި6RqkU;z2B͘͸ ׌VGG;"z1$1Zb7ިUz~ڪ WfԆ^Gؿuh*\NbEOύ1vjsU7a &槙5};/'bc:GIuA҃,3D8&RD&,LO+Hoj5P" +*dS"w~eJͱѺ&Rk+IJ 7NOz{= "Ť QE.bjD֬j IjKw-L>6AXF#"f ;v`7[^h*cɵZeZ.(]Xro M0GFҌz跓 M'M ʕrJ&Ȓ '_HY^ܽz*UoI".5jꑌen  s3*qvvj ZM7z}C;jYo~Wm2S&~!iL;8h=voN5Z ZS }}S\%g ħzP!T=O$MZ ǾrrЎ.|9~_N,)|;ͳ;pLoY6࿪)r0 ji9XRI/wR)c;j/#yc*_~K?i)\2Q1˷ Z }_ ?|O%-a!~v@aYʡ'q蹏ʈXYB,:1sRS&?FTo c0"q:z,!gC40H`^ōS&lIdh Cr7IecB^@GO%$%+̕~gHL R~ܼ8ŌE(]kʣ/f;9TVejZo=}v-vKk-]R=_8`rҧ+ʩa)z8P䅠ݮ!CP2lA)%~"^wg^k4[7F$QLj(/DGD|/P)ۗ*rdJEh&7|h7E":*Oہfe'zC/t&}*#ٜʍi'FԾ40r ŴN$1tC\5+cxyί3+#>g·?o)t @hE/ $q5SGCpO1k'^&VTH*Rpo'_qTZoaZ@ie(VD${rA C| }z.=#se ĺH[C˲4 G̓ܔKzt7f֔ن>/ tbzf"T7w< Ƅj6Ns,Lp] ݒ WW=jMiqSSa/i(;ؙ\q]\]?!e 3 Fn@Vi+VG}z)1fMeês+77VUMoKnWI`M!0ȴl,nij( +IZ}XOE]pч+'IV5-P*"JW?Z-:4Sz R4CP.յ<!h_VmJ3y41{ao=EV>~u=ҫHxQJXG%ZCfŘ*FQQ,gѱ_Ozڨ3z{QK>5OJ'ԬͺLk1U8وO3ϡTT*'puY׾ d29Քc۟eǂ)BʽErƫ纥k0'js im|[@>#c]n?6UW#!z5Ov:7쭉n(tx N.ǡOpc1m]t˨ Y7zک?z2Nwz{%?:bwbBk`g$i^}0=9Fl J.c!mO ,,sHNAUIђ*C[꧍.^k7V6FL  sgag75єCBRw]CB#ߋpu?[!ؘz3:@׀,Pb ךFS(?O "vB݃|q-Yk5Hq04܄[ğ̳9͔N815@֪w%yE8a Mǁe :IVfuŚ>"T֭g=u%Jz23W.RkV_\?LMݴ V`䎡u>m uf6XD?~i>,y$iDS 3674&K ޔ˰/4)79-$dRI}EMNIMq֍rJ_%jqjJLݗV˔JlP+?'\$ h;xuy74aO^l 5eC# Uֈh_>'&!r)Pñ鵴WObj\}=Iv(6Z詞|٨#g%jwi&O܆IITlkv nj 377.y7tS6"7&L[v#e^̶)Ah5pCRQc1WO&gx#괛^Z}uɻW7SDU{D绰d&ArV{ ؂:'b#b/Ž\\p\;| C$VP9A%d#J"D{"C nو#D\ wFsyԲV"o<[[Tiōx72I)ZF FKpAg#n֕:`k8w׳W-T{L]Sx4vE5_67_WG,lk4oiTol>Qij ,wCݠ$$ZH$[[r. e&YgX+t+4 ֶPs@%+wڮ L(uizh{fצ^^

wW#Qi8E}DMOD`IJŝ,Z_UfUzdStF΄KӫDmGPӇI]$Rѫ#;S!"Ry~E <*< Z/vO!,S}#(<(\[cPQK$9YJ ge㋪)? vVHŠkc ԋ'Ґt$fJ }ljym:Jϛ PgiӟUԳwRƦwp̪ ǵM7Y8nnz*6kr-umHi~[.ͽN]hl裤iYOfoI{=Jک$.q-j%Y aD1Qo^MڎF^+ 67B4UhUxAՠ$!&_f@ESļHJ"Gdu bf4[&xP@5J֒rbCy(\{CzmED,O8,-lZWZ ڴGK^wC_2t9B%#M ULhέ~<ڋH>pW{Ѹ7;c(F"+N(IȸA{ɍlLq.vl:^ټ~AG>b)^B\Buhܳ;{+ɠMYɅAƷnyWP9,IBq@6?-n\MKf27Ei4V giSWr4#H% `c@2I^uy^Ne* %1X, jUkV1[ kuQ Vu]!`Ov)\]Fc )_K0Sq=p< qF ?4l>J:Bͭ;ϗ*tLd)I38 h_Q73GHOF,@3uZK[9lY0l4Tt <JpS7ࡉ&ӣ}hbnWb90/v5 `X+J[,c(dD%fh<`gIj@mEP*KcZWA$l]IukZT!@CkڹZAP TM|זkY;t8 y$d=לnx7_3F35?V6uZR JcI"Serkm&M%@dvy7HLW,%,zjx?{y6\b|qv6[CotT.lEZA&>)Sx"Fx] uHV955zۨ{";ZZ\! r:=gr?QZF'Uq9celꑮB&`OyMtz~`l= ˆ)Ȝ~|NFzvD6>XqLķ 7^L6jZO5p|_D-Z7'Y`[ץl4Σs| IS}!P Re2ʇ9ums2Sꪜm|V/TR7l}^(fg]gyضtihԿsw9g{U'r&^P777_5wN;+ th4= e rGF*'/{aw5\йO>Qh7>@vg^Ї߷VKӅ b&)B~?DmlID-5~ '`U:`^8vhӯZoHwM_*'>?9*+a. ߫l1?(-?G^!wC t,QQOT\^1Ut\Gs (wĖUqb)b2f Hom)\Gr=P]+ŽtsW@#-E)9pOJ!XyE¼NN`-dPVCiMzllF*O1c Jr( ڼ8;7 Z!X>ʠ\艾cSjZ`3o>OVCh)G5t*y=ҪtZKhW9nmV.Tؓ L;Mޖ\ٗu !< "aBO7 !-ÍEH !_?(SÌ":ƒ].'CYn;Q@p*taN:Z1Z/7C@_1a.FKQVMH\/KM,PIg5ybCeY0߁ ݿ“n 6A)فKf3Ҳ4,vt `" 2r1$zYQT+x—[)ɩHWAj_S{N̦ǘKZCPF= B=[ 6yՑsʫ =\٤/]Z6C:4.U<DYsvpe^`~N{_V Z] f+}a]W⓰=~t,a qp9֍cݺs~ YS~i-Fiv*A}&OQ1eEȅbV2"DA=Ci#scvqʚxc;~)s_cTq`? UsFK%kBHTTP_Request2-2.2.1/tests/_files/bug_181690000666000000000000000000000033512266024053014633 0ustar 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 Content-Type: text/plain; charset=iso-8859-1 Content-Encoding: deflate Content-Length: 0 Connection: close HTTP_Request2-2.2.1/tests/_files/empty.gif0000666000000000000000000000005312266024053015205 0ustar GIF89a!,D;HTTP_Request2-2.2.1/tests/_files/plaintext.txt0000666000000000000000000000001712266024053016131 0ustar This is a test.HTTP_Request2-2.2.1/tests/_files/response_cookies0000666000000000000000000000065612266024053016666 0ustar 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.HTTP_Request2-2.2.1/tests/_files/response_deflate0000666000000000000000000000316612266024053016635 0ustar 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 Content-Type: text/plain; charset=iso-8859-1 Content-Encoding: deflate Connection: close xVMs6Wdv)-f$R%);)[LIB%Hi_߷ gCSbXv/aʤ#vR EGhiy ʐD2nlu1Վ5͖{O6k ?8__eVfcn{yr{0Y.u$(j]'fUӬ_^nA8//?\A: %uzĂ<[9#t,\h6aJS 3 yAxO,IQLr:H<"iO2N#t5O)RȩLFǵn蚦"JNdzJ^4D5FYr81,J1L@N$C$0dL&{zz Mhi,6\MDWG2'OGpv:)QWIAXQ0 n@{my-y,Fh̯TTM%"CB(qFݰy"2LEg8@̹,u:+9):X}:%m4LHBq37" ^8˝L /e+{qvUx2k*k F1`A"q ǽtLϾ0V /p1E8.NlG{(vV$<>%+U ;1fNNAfJdE*HTTP_Request2-2.2.1/tests/_files/response_gzip0000666000000000000000000000321012266024053016170 0ustar 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 Content-Type: text/plain; charset=iso-8859-1 Content-Encoding: gzip Connection: close {bzG3_01.txtVMs6Wdv)-f$R%);)[LIB%Hi_߷ gCSbXv/aʤ#vR EGhiy ʐD2nlu1Վ5͖{O6k ?8__eVfcn{yr{0Y.u$(j]'fUӬ_^nA8//?\A: %uzĂ<[9#t,\h6aJS 3 yAxO,IQLr:H<"iO2N#t5O)RȩLFǵn蚦"JNdzJ^4D5FYr81,J1L@N$C$0dL&{zz Mhi,6\MDWG2'OGpv:)QWIAXQ0 n@{my-y,Fh̯TTM%"CB(qFݰy"2LEg8@̹,u:+9):X}:%m4LHBq37" ^8˝L /e+{qvUx2k*k F1`A"q ǽtLϾ0V /p1E8.NlG{(vV$<>%+U ;1fNNAfJd;` HTTP_Request2-2.2.1/tests/_files/response_gzip_broken0000666000000000000000000000033512266024053017535 0ustar 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 Content-Type: text/plain; charset=iso-8859-1 Content-Encoding: gzip Connection: close {bzG3_01.txtVMHTTP_Request2-2.2.1/tests/_files/response_headers0000666000000000000000000000044012266024053016634 0ustar 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.HTTP_Request2-2.2.1/tests/_network/basicauth.php0000666000000000000000000000220712266024053016426 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ $user = isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null; $pass = isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null; $wantedUser = isset($_GET['user']) ? $_GET['user'] : null; $wantedPass = isset($_GET['pass']) ? $_GET['pass'] : null; if (!$user || !$pass || $user != $wantedUser || $pass != $wantedPass) { header('WWW-Authenticate: Basic realm="HTTP_Request2 tests"', true, 401); echo "Login required"; } else { echo "Username={$user};Password={$pass}"; } ?>HTTP_Request2-2.2.1/tests/_network/bug19934.php0000666000000000000000000000142412266024053015652 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ for ($i = 0; $i < 20; $i++) { for ($j = 0; $j < 10; $j++) { echo str_repeat((string)$j, 98) . "\r\n"; } flush(); usleep(50000); }HTTP_Request2-2.2.1/tests/_network/cookies.php0000666000000000000000000000124612266024053016121 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ ksort($_COOKIE); echo serialize($_COOKIE); ?>HTTP_Request2-2.2.1/tests/_network/digestauth.php0000666000000000000000000000532012266024053016623 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** * Mostly borrowed from PHP manual and Socket Adapter implementation * * @link http://php.net/manual/en/features.http-auth.php */ /** * Parses the Digest auth header * * @param string $txt */ function http_digest_parse($txt) { $token = '[^\x00-\x1f\x7f-\xff()<>@,;:\\\\"/\[\]?={}\s]+'; $quoted = '"(?:\\\\.|[^\\\\"])*"'; // protect against missing data $needed_parts = array_flip(array('nonce', 'nc', 'cnonce', 'qop', 'username', 'uri', 'response')); $data = array(); preg_match_all("!({$token})\\s*=\\s*({$token}|{$quoted})!", $txt, $matches); for ($i = 0; $i < count($matches[0]); $i++) { // ignore unneeded parameters if (isset($needed_parts[$matches[1][$i]])) { unset($needed_parts[$matches[1][$i]]); if ('"' == substr($matches[2][$i], 0, 1)) { $data[$matches[1][$i]] = substr($matches[2][$i], 1, -1); } else { $data[$matches[1][$i]] = $matches[2][$i]; } } } return !empty($needed_parts) ? false : $data; } $realm = 'HTTP_Request2 tests'; $wantedUser = isset($_GET['user']) ? $_GET['user'] : null; $wantedPass = isset($_GET['pass']) ? $_GET['pass'] : null; $validAuth = false; if (!empty($_SERVER['PHP_AUTH_DIGEST']) && ($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) && $wantedUser == $data['username'] ) { // generate the valid response $a1 = md5($data['username'] . ':' . $realm . ':' . $wantedPass); $a2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']); $response = md5($a1. ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $a2); // check valid response against existing one $validAuth = ($data['response'] == $response); } if (!$validAuth || empty($_SERVER['PHP_AUTH_DIGEST'])) { header('WWW-Authenticate: Digest realm="' . $realm . '",qop="auth",nonce="' . uniqid() . '"', true, 401); echo "Login required"; } else { echo "Username={$user}"; } ?>HTTP_Request2-2.2.1/tests/_network/getparameters.php0000666000000000000000000000124012266024053017322 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ ksort($_GET); echo serialize($_GET); ?>HTTP_Request2-2.2.1/tests/_network/postparameters.php0000666000000000000000000000124212266024053017532 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ ksort($_POST); echo serialize($_POST); ?>HTTP_Request2-2.2.1/tests/_network/rawpostdata.php0000666000000000000000000000122112266024053017007 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ readfile('php://input'); ?>HTTP_Request2-2.2.1/tests/_network/redirects.php0000666000000000000000000000323312266024053016447 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ $redirects = isset($_GET['redirects'])? $_GET['redirects']: 1; $https = !empty($_SERVER['HTTPS']) && ('off' != strtolower($_SERVER['HTTPS'])); $special = isset($_GET['special'])? $_GET['special']: null; if ('ftp' == $special) { header('Location: ftp://localhost/pub/exploit.exe', true, 301); } elseif ('relative' == $special) { header('Location: ./getparameters.php?msg=did%20relative%20redirect', true, 302); } elseif ('cookie' == $special) { setcookie('cookie_on_redirect', 'success'); header('Location: ./cookies.php', true, 302); } elseif ($redirects > 0) { $url = ($https? 'https': 'http') . '://' . $_SERVER['SERVER_NAME'] . (($https && 443 == $_SERVER['SERVER_PORT'] || !$https && 80 == $_SERVER['SERVER_PORT']) ? '' : ':' . $_SERVER['SERVER_PORT']) . $_SERVER['PHP_SELF'] . '?redirects=' . (--$redirects); header('Location: ' . $url, true, 302); } else { echo "Method=" . $_SERVER['REQUEST_METHOD'] . ';'; var_dump($_POST); var_dump($_GET); } ?>HTTP_Request2-2.2.1/tests/_network/setcookie.php0000666000000000000000000000143112266024053016446 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ $name = empty($_GET['name'])? 'foo': $_GET['name']; $value = empty($_GET['value'])? 'bar': $_GET['value']; setcookie($name, $value); echo "Cookie set!"; ?>HTTP_Request2-2.2.1/tests/_network/timeout.php0000666000000000000000000000120412266024053016145 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ sleep(5); ?>HTTP_Request2-2.2.1/tests/_network/uploads.php0000666000000000000000000000204412266024053016131 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ if (isset($_GET['slowpoke'])) { sleep(3); } if (!empty($_FILES)) { foreach ($_FILES as $name => $file) { if (is_array($file['name'])) { foreach($file['name'] as $k => $v) { echo "{$name}[{$k}] {$v} {$file['type'][$k]} {$file['size'][$k]}\n"; } } else { echo "{$name} {$file['name']} {$file['type']} {$file['size']}\n"; } } } ?>HTTP_Request2-2.2.1/tests/Request2/Adapter/AllTests.php0000666000000000000000000000457512266024053017472 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ if (!defined('PHPUnit_MAIN_METHOD')) { define('PHPUnit_MAIN_METHOD', 'Request2_Adapter_AllTests::main'); } require_once dirname(__FILE__) . '/MockTest.php'; require_once dirname(__FILE__) . '/SkippedTests.php'; require_once dirname(__FILE__) . '/SocketTest.php'; require_once dirname(__FILE__) . '/SocketProxyTest.php'; require_once dirname(__FILE__) . '/CurlTest.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'); if (defined('HTTP_REQUEST2_TESTS_BASE_URL') && HTTP_REQUEST2_TESTS_BASE_URL) { $suite->addTestSuite('HTTP_Request2_Adapter_SocketTest'); } else { $suite->addTestSuite('HTTP_Request2_Adapter_Skip_SocketTest'); } if (defined('HTTP_REQUEST2_TESTS_PROXY_HOST') && HTTP_REQUEST2_TESTS_PROXY_HOST && defined('HTTP_REQUEST2_TESTS_BASE_URL') && HTTP_REQUEST2_TESTS_BASE_URL ) { $suite->addTestSuite('HTTP_Request2_Adapter_SocketProxyTest'); } else { $suite->addTestSuite('HTTP_Request2_Adapter_Skip_SocketProxyTest'); } if (defined('HTTP_REQUEST2_TESTS_BASE_URL') && HTTP_REQUEST2_TESTS_BASE_URL && extension_loaded('curl') ) { $suite->addTestSuite('HTTP_Request2_Adapter_CurlTest'); } else { $suite->addTestSuite('HTTP_Request2_Adapter_Skip_CurlTest'); } return $suite; } } if (PHPUnit_MAIN_METHOD == 'Request2_Adapter_AllTests::main') { Request2_Adapter_AllTests::main(); } ?> HTTP_Request2-2.2.1/tests/Request2/Adapter/CommonNetworkTest.php0000666000000000000000000003202012266024053021363 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(dirname(dirname(__FILE__))) . '/TestHelper.php'; /** Class representing a HTTP request */ require_once 'HTTP/Request2.php'; /** Class for building multipart/form-data request body */ require_once 'HTTP/Request2/MultipartBody.php'; class SlowpokeBody extends HTTP_Request2_MultipartBody { protected $doSleep; public function rewind() { $this->doSleep = true; parent::rewind(); } public function read($length) { if ($this->doSleep) { sleep(3); $this->doSleep = false; } return parent::read($length); } } class HeaderObserver implements SplObserver { public $headers; public function update(SplSubject $subject) { $event = $subject->getLastEvent(); // force a timeout when writing request body if ('sentHeaders' == $event['name']) { $this->headers = $event['data']; } } } /** * Tests for HTTP_Request2 package that require a working webserver * * The class contains some common tests that should be run for all Adapters, * it is extended by their unit tests. * * You need to properly set up this test suite, refer to NetworkConfig.php.dist */ abstract class HTTP_Request2_Adapter_CommonNetworkTest extends PHPUnit_Framework_TestCase { /** * HTTP Request object * @var HTTP_Request2 */ protected $request; /** * Base URL for remote test files * @var string */ protected $baseUrl; /** * Configuration for HTTP Request object * @var array */ protected $config = array(); protected function setUp() { if (!defined('HTTP_REQUEST2_TESTS_BASE_URL') || !HTTP_REQUEST2_TESTS_BASE_URL) { $this->markTestSkipped('Base URL is not configured'); } else { $this->baseUrl = rtrim(HTTP_REQUEST2_TESTS_BASE_URL, '/') . '/'; $name = strtolower(preg_replace('/^test/i', '', $this->getName())) . '.php'; $this->request = new HTTP_Request2( $this->baseUrl . $name, HTTP_Request2::METHOD_GET, $this->config ); } } /** * Tests possibility to send GET parameters * * NB: Currently there are problems with Net_URL2::setQueryVariables(), thus * array structure is simple: http://pear.php.net/bugs/bug.php?id=18267 */ public function testGetParameters() { $data = array( 'bar' => array( 'key' => 'value' ), 'foo' => 'some value', 'numbered' => array('first', 'second') ); $this->request->getUrl()->setQueryVariables($data); $response = $this->request->send(); $this->assertEquals(serialize($data), $response->getBody()); } public function testPostParameters() { $data = array( 'bar' => array( 'key' => 'some other value' ), 'baz' => array( 'key1' => array( 'key2' => 'yet another value' ) ), 'foo' => 'some value', 'indexed' => array('first', 'second') ); $this->request->setMethod(HTTP_Request2::METHOD_POST) ->addPostParameter($data); $response = $this->request->send(); $this->assertEquals(serialize($data), $response->getBody()); } public function testUploads() { $this->request->setMethod(HTTP_Request2::METHOD_POST) ->addUpload('foo', dirname(dirname(dirname(__FILE__))) . '/_files/empty.gif', 'picture.gif', 'image/gif') ->addUpload('bar', array( array(dirname(dirname(dirname(__FILE__))) . '/_files/empty.gif', null, 'image/gif'), array(dirname(dirname(dirname(__FILE__))) . '/_files/plaintext.txt', 'secret.txt', 'text/x-whatever') )); $response = $this->request->send(); $this->assertContains("foo picture.gif image/gif 43", $response->getBody()); $this->assertContains("bar[0] empty.gif image/gif 43", $response->getBody()); $this->assertContains("bar[1] secret.txt text/x-whatever 15", $response->getBody()); } public function testRawPostData() { $data = 'Nothing to see here, move along'; $this->request->setMethod(HTTP_Request2::METHOD_POST) ->setBody($data); $response = $this->request->send(); $this->assertEquals($data, $response->getBody()); } public function testCookies() { $cookies = array( 'CUSTOMER' => 'WILE_E_COYOTE', 'PART_NUMBER' => 'ROCKET_LAUNCHER_0001' ); foreach ($cookies as $k => $v) { $this->request->addCookie($k, $v); } $response = $this->request->send(); $this->assertEquals(serialize($cookies), $response->getBody()); } public function testTimeout() { $this->request->setConfig('timeout', 2); try { $this->request->send(); $this->fail('Expected HTTP_Request2_Exception was not thrown'); } catch (HTTP_Request2_MessageException $e) { $this->assertEquals(HTTP_Request2_Exception::TIMEOUT, $e->getCode()); } } public function testTimeoutInRequest() { $this->request->setConfig('timeout', 2) ->setUrl($this->baseUrl . 'postparameters.php') ->setBody(new SlowpokeBody(array('foo' => 'some value'), array())); try { $this->request->send(); $this->fail('Expected HTTP_Request2_MessageException was not thrown'); } catch (HTTP_Request2_MessageException $e) { $this->assertEquals(HTTP_Request2_Exception::TIMEOUT, $e->getCode()); } } public function testBasicAuth() { $this->request->getUrl()->setQueryVariables(array( 'user' => 'luser', 'pass' => 'qwerty' )); $wrong = clone $this->request; $this->request->setAuth('luser', 'qwerty'); $response = $this->request->send(); $this->assertEquals(200, $response->getStatus()); $wrong->setAuth('luser', 'password'); $response = $wrong->send(); $this->assertEquals(401, $response->getStatus()); } public function testDigestAuth() { $this->request->getUrl()->setQueryVariables(array( 'user' => 'luser', 'pass' => 'qwerty' )); $wrong = clone $this->request; $this->request->setAuth('luser', 'qwerty', HTTP_Request2::AUTH_DIGEST); $response = $this->request->send(); $this->assertEquals(200, $response->getStatus()); $wrong->setAuth('luser', 'password', HTTP_Request2::AUTH_DIGEST); $response = $wrong->send(); $this->assertEquals(401, $response->getStatus()); } public function testRedirectsDefault() { $this->request->setUrl($this->baseUrl . 'redirects.php') ->setConfig(array('follow_redirects' => true, 'strict_redirects' => false)) ->setMethod(HTTP_Request2::METHOD_POST) ->addPostParameter('foo', 'foo value'); $response = $this->request->send(); $this->assertContains('Method=GET', $response->getBody()); $this->assertNotContains('foo', $response->getBody()); $this->assertEquals($this->baseUrl . 'redirects.php?redirects=0', $response->getEffectiveUrl()); } public function testRedirectsStrict() { $this->request->setUrl($this->baseUrl . 'redirects.php') ->setConfig(array('follow_redirects' => true, 'strict_redirects' => true)) ->setMethod(HTTP_Request2::METHOD_POST) ->addPostParameter('foo', 'foo value'); $response = $this->request->send(); $this->assertContains('Method=POST', $response->getBody()); $this->assertContains('foo', $response->getBody()); } public function testRedirectsLimit() { $this->request->setUrl($this->baseUrl . 'redirects.php?redirects=4') ->setConfig(array('follow_redirects' => true, 'max_redirects' => 2)); try { $this->request->send(); $this->fail('Expected HTTP_Request2_Exception was not thrown'); } catch (HTTP_Request2_MessageException $e) { $this->assertEquals(HTTP_Request2_Exception::TOO_MANY_REDIRECTS, $e->getCode()); } } public function testRedirectsRelative() { $this->request->setUrl($this->baseUrl . 'redirects.php?special=relative') ->setConfig(array('follow_redirects' => true)); $response = $this->request->send(); $this->assertContains('did relative', $response->getBody()); } public function testRedirectsNonHTTP() { $this->request->setUrl($this->baseUrl . 'redirects.php?special=ftp') ->setConfig(array('follow_redirects' => true)); try { $this->request->send(); $this->fail('Expected HTTP_Request2_Exception was not thrown'); } catch (HTTP_Request2_MessageException $e) { $this->assertEquals(HTTP_Request2_Exception::NON_HTTP_REDIRECT, $e->getCode()); } } public function testCookieJar() { $this->request->setUrl($this->baseUrl . 'setcookie.php?name=cookie_name&value=cookie_value'); $req2 = clone $this->request; $this->request->setCookieJar()->send(); $jar = $this->request->getCookieJar(); $jar->store( array('name' => 'foo', 'value' => 'bar'), $this->request->getUrl() ); $response = $req2->setUrl($this->baseUrl . 'cookies.php')->setCookieJar($jar)->send(); $this->assertEquals( serialize(array('cookie_name' => 'cookie_value', 'foo' => 'bar')), $response->getBody() ); } public function testCookieJarAndRedirect() { $this->request->setUrl($this->baseUrl . 'redirects.php?special=cookie') ->setConfig('follow_redirects', true) ->setCookieJar(); $response = $this->request->send(); $this->assertEquals(serialize(array('cookie_on_redirect' => 'success')), $response->getBody()); } /** * @link http://pear.php.net/bugs/bug.php?id=20125 */ public function testChunkedRequest() { $data = array( 'long' => str_repeat('a', 1000), 'very_long' => str_repeat('b', 2000) ); $this->request->setMethod(HTTP_Request2::METHOD_POST) ->setUrl($this->baseUrl . 'postparameters.php') ->setConfig('buffer_size', 512) ->setHeader('Transfer-Encoding', 'chunked') ->addPostParameter($data); $response = $this->request->send(); $this->assertEquals(serialize($data), $response->getBody()); } /** * @link http://pear.php.net/bugs/bug.php?id=19233 * @link http://pear.php.net/bugs/bug.php?id=15937 */ public function testPreventExpectHeader() { $fp = fopen(dirname(dirname(dirname(__FILE__))) . '/_files/bug_15305', 'rb'); $observer = new HeaderObserver(); $body = new HTTP_Request2_MultipartBody( array(), array( 'upload' => array( 'fp' => $fp, 'filename' => 'bug_15305', 'type' => 'application/octet-stream', 'size' => 16338 ) ) ); $this->request->setMethod(HTTP_Request2::METHOD_POST) ->setUrl($this->baseUrl . 'uploads.php') ->setHeader('Expect', '') ->setBody($body) ->attach($observer); $response = $this->request->send(); $this->assertNotContains('Expect:', $observer->headers); $this->assertContains('upload bug_15305 application/octet-stream 16338', $response->getBody()); } } ?>HTTP_Request2-2.2.1/tests/Request2/Adapter/CurlTest.php0000666000000000000000000000733012266024053017474 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Tests for HTTP_Request2 package that require a working webserver */ require_once dirname(__FILE__) . '/CommonNetworkTest.php'; /** Adapter for HTTP_Request2 wrapping around cURL extension */ /** * Unit test for Curl Adapter of HTTP_Request2 */ class HTTP_Request2_Adapter_CurlTest extends HTTP_Request2_Adapter_CommonNetworkTest { /** * Configuration for HTTP Request object * @var array */ protected $config = array( 'adapter' => 'HTTP_Request2_Adapter_Curl' ); /** * Checks whether redirect support in cURL is disabled by safe_mode or open_basedir * @return bool */ protected function isRedirectSupportDisabled() { return ini_get('safe_mode') || ini_get('open_basedir'); } public function testRedirectsDefault() { if ($this->isRedirectSupportDisabled()) { $this->markTestSkipped('Redirect support in cURL is disabled by safe_mode or open_basedir setting'); } else { parent::testRedirectsDefault(); } } public function testRedirectsStrict() { if ($this->isRedirectSupportDisabled()) { $this->markTestSkipped('Redirect support in cURL is disabled by safe_mode or open_basedir setting'); } else { parent::testRedirectsStrict(); } } public function testRedirectsLimit() { if ($this->isRedirectSupportDisabled()) { $this->markTestSkipped('Redirect support in cURL is disabled by safe_mode or open_basedir setting'); } else { parent::testRedirectsLimit(); } } public function testRedirectsRelative() { if ($this->isRedirectSupportDisabled()) { $this->markTestSkipped('Redirect support in cURL is disabled by safe_mode or open_basedir setting'); } else { parent::testRedirectsRelative(); } } public function testRedirectsNonHTTP() { if ($this->isRedirectSupportDisabled()) { $this->markTestSkipped('Redirect support in cURL is disabled by safe_mode or open_basedir setting'); } else { parent::testRedirectsNonHTTP(); } } public function testCookieJarAndRedirect() { if ($this->isRedirectSupportDisabled()) { $this->markTestSkipped('Redirect support in cURL is disabled by safe_mode or open_basedir setting'); } else { parent::testCookieJarAndRedirect(); } } public function testBug17450() { if (!$this->isRedirectSupportDisabled()) { $this->markTestSkipped('Neither safe_mode nor open_basedir is enabled'); } $this->request->setUrl($this->baseUrl . 'redirects.php') ->setConfig(array('follow_redirects' => true)); try { $this->request->send(); $this->fail('Expected HTTP_Request2_Exception was not thrown'); } catch (HTTP_Request2_LogicException $e) { $this->assertEquals(HTTP_Request2_Exception::MISCONFIGURATION, $e->getCode()); } } } ?>HTTP_Request2-2.2.1/tests/Request2/Adapter/MockTest.php0000666000000000000000000001220412266024053017454 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(dirname(dirname(__FILE__))) . '/TestHelper.php'; /** * Class representing a HTTP request */ require_once 'HTTP/Request2.php'; /** * Mock adapter intended for testing */ require_once 'HTTP/Request2/Adapter/Mock.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()); } /** * Returning URL-specific responses * @link http://pear.php.net/bugs/bug.php?id=19276 */ public function testRequest19276() { $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 response from example.org", 'http://example.org/' ); $mock->addResponse( "HTTP/1.1 200 OK\r\n" . "Content-Type: text/plain; charset=iso-8859-1\r\n" . "\r\n" . "This is a response from example.com", 'http://example.com/' ); $req1 = new HTTP_Request2('http://localhost/'); $req1->setAdapter($mock); $this->assertEquals(400, $req1->send()->getStatus()); $req2 = new HTTP_Request2('http://example.com/'); $req2->setAdapter($mock); $this->assertContains('example.com', $req2->send()->getBody()); $req3 = new HTTP_Request2('http://example.org'); $req3->setAdapter($mock); $this->assertContains('example.org', $req3->send()->getBody()); } 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'); } } ?> HTTP_Request2-2.2.1/tests/Request2/Adapter/SkippedTests.php0000666000000000000000000000317412266024053020353 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(dirname(dirname(__FILE__))) . '/TestHelper.php'; /** * Shows a skipped test if networked tests are not configured */ class HTTP_Request2_Adapter_Skip_SocketTest extends PHPUnit_Framework_TestCase { public function testSocketAdapter() { $this->markTestSkipped('Socket Adapter tests need base URL configured.'); } } /** * Shows a skipped test if proxy is not configured */ class HTTP_Request2_Adapter_Skip_SocketProxyTest extends PHPUnit_Framework_TestCase { public function testSocketAdapterWithProxy() { $this->markTestSkipped('Socket Adapter proxy tests need base URL and proxy configured'); } } /** * Shows a skipped test if networked tests are not configured or cURL extension is unavailable */ class HTTP_Request2_Adapter_Skip_CurlTest extends PHPUnit_Framework_TestCase { public function testCurlAdapter() { $this->markTestSkipped('Curl Adapter tests need base URL configured and curl extension available'); } } ?>HTTP_Request2-2.2.1/tests/Request2/Adapter/SocketProxyTest.php0000666000000000000000000000346412266024053021065 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Tests for HTTP_Request2 package that require a working webserver */ require_once dirname(__FILE__) . '/CommonNetworkTest.php'; /** * Unit test for Socket Adapter of HTTP_Request2 working through proxy */ class HTTP_Request2_Adapter_SocketProxyTest extends HTTP_Request2_Adapter_CommonNetworkTest { /** * Configuration for HTTP Request object * @var array */ protected $config = array( 'adapter' => 'HTTP_Request2_Adapter_Socket' ); protected function setUp() { if (!defined('HTTP_REQUEST2_TESTS_PROXY_HOST') || !HTTP_REQUEST2_TESTS_PROXY_HOST) { $this->markTestSkipped('Proxy is not configured'); } else { $this->config += array( 'proxy_host' => HTTP_REQUEST2_TESTS_PROXY_HOST, 'proxy_port' => HTTP_REQUEST2_TESTS_PROXY_PORT, 'proxy_user' => HTTP_REQUEST2_TESTS_PROXY_USER, 'proxy_password' => HTTP_REQUEST2_TESTS_PROXY_PASSWORD, 'proxy_auth_scheme' => HTTP_REQUEST2_TESTS_PROXY_AUTH_SCHEME, 'proxy_type' => HTTP_REQUEST2_TESTS_PROXY_TYPE ); parent::setUp(); } } } ?>HTTP_Request2-2.2.1/tests/Request2/Adapter/SocketTest.php0000666000000000000000000001254512266024053020023 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Tests for HTTP_Request2 package that require a working webserver */ require_once dirname(__FILE__) . '/CommonNetworkTest.php'; /** Socket-based adapter for HTTP_Request2 */ require_once 'HTTP/Request2/Adapter/Socket.php'; /** * Unit test for Socket Adapter of HTTP_Request2 */ class HTTP_Request2_Adapter_SocketTest extends HTTP_Request2_Adapter_CommonNetworkTest { /** * Configuration for HTTP Request object * @var array */ protected $config = array( 'adapter' => 'HTTP_Request2_Adapter_Socket' ); public function testBug17826() { $adapter = new HTTP_Request2_Adapter_Socket(); $request1 = new HTTP_Request2($this->baseUrl . 'redirects.php?redirects=2'); $request1->setConfig(array('follow_redirects' => true, 'max_redirects' => 3)) ->setAdapter($adapter) ->send(); $request2 = new HTTP_Request2($this->baseUrl . 'redirects.php?redirects=2'); $request2->setConfig(array('follow_redirects' => true, 'max_redirects' => 3)) ->setAdapter($adapter) ->send(); } /** * Infinite loop with stream wrapper passed as upload * * Dunno how the original reporter managed to pass a file pointer * that doesn't support fstat() to MultipartBody, maybe he didn't use * addUpload(). So we don't use it, either. * * @link http://pear.php.net/bugs/bug.php?id=19934 */ public function testBug19934() { if (!in_array('http', stream_get_wrappers())) { $this->markTestSkipped("This test requires an HTTP fopen wrapper enabled"); } $fp = fopen($this->baseUrl . '/bug19934.php', 'rb'); $body = new HTTP_Request2_MultipartBody( array(), array( 'upload' => array( 'fp' => $fp, 'filename' => 'foo.txt', 'type' => 'text/plain', 'size' => 20000 ) ) ); $this->request->setMethod(HTTP_Request2::METHOD_POST) ->setUrl($this->baseUrl . 'uploads.php') ->setBody($body); set_error_handler(array($this, 'rewindWarningsHandler')); $response = $this->request->send(); restore_error_handler(); $this->assertContains("upload foo.txt text/plain 20000", $response->getBody()); } public function rewindWarningsHandler($errno, $errstr) { if (($errno & E_WARNING) && false !== strpos($errstr, 'rewind')) { return true; } return false; } /** * Do not send request body twice to URLs protected by digest auth * * @link http://pear.php.net/bugs/bug.php?id=19233 */ public function test100ContinueHandling() { if (!defined('HTTP_REQUEST2_TESTS_DIGEST_URL') || !HTTP_REQUEST2_TESTS_DIGEST_URL) { $this->markTestSkipped('This test requires an URL protected by server digest auth'); } $fp = fopen(dirname(dirname(dirname(__FILE__))) . '/_files/bug_15305', 'rb'); $body = $this->getMock( 'HTTP_Request2_MultipartBody', array('read'), array( array(), array( 'upload' => array( 'fp' => $fp, 'filename' => 'bug_15305', 'type' => 'application/octet-stream', 'size' => 16338 ) ) ) ); $body->expects($this->never())->method('read'); $this->request->setMethod(HTTP_Request2::METHOD_POST) ->setUrl(HTTP_REQUEST2_TESTS_DIGEST_URL) ->setBody($body); $this->assertEquals(401, $this->request->send()->getStatus()); } public function test100ContinueTimeoutBug() { $fp = fopen(dirname(dirname(dirname(__FILE__))) . '/_files/bug_15305', 'rb'); $body = new HTTP_Request2_MultipartBody( array(), array( 'upload' => array( 'fp' => $fp, 'filename' => 'bug_15305', 'type' => 'application/octet-stream', 'size' => 16338 ) ) ); $this->request->setMethod(HTTP_Request2::METHOD_POST) ->setUrl($this->baseUrl . 'uploads.php?slowpoke') ->setBody($body); $response = $this->request->send(); $this->assertContains('upload bug_15305 application/octet-stream 16338', $response->getBody()); } } ?>HTTP_Request2-2.2.1/tests/Request2/AllTests.php0000666000000000000000000000325112266024053016100 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ if (!defined('PHPUnit_MAIN_METHOD')) { define('PHPUnit_MAIN_METHOD', 'Request2_AllTests::main'); } require_once dirname(__FILE__) . '/CookieJarTest.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() { if (!function_exists('phpunit_autoload')) { require_once 'PHPUnit/TextUI/TestRunner.php'; } PHPUnit_TextUI_TestRunner::run(self::suite()); } public static function suite() { $suite = new PHPUnit_Framework_TestSuite('HTTP_Request2 package - Request2'); $suite->addTestSuite('HTTP_Request2_CookieJarTest'); $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(); } ?>HTTP_Request2-2.2.1/tests/Request2/CookieJarTest.php0000666000000000000000000003105012266024053017051 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(dirname(__FILE__)) . '/TestHelper.php'; /** Stores cookies and passes them between HTTP requests */ require_once 'HTTP/Request2/CookieJar.php'; /** * Unit test for HTTP_Request2_CookieJar class */ class HTTP_Request2_CookieJarTest extends PHPUnit_Framework_TestCase { /** * Cookie jar instance being tested * @var HTTP_Request2_CookieJar */ protected $jar; protected function setUp() { $this->jar = new HTTP_Request2_CookieJar(); } /** * Test that we can't store junk "cookies" in jar * * @dataProvider invalidCookieProvider * @expectedException HTTP_Request2_LogicException */ public function testStoreInvalid($cookie) { $this->jar->store($cookie); } /** * * @dataProvider noPSLDomainsProvider */ public function testDomainMatchNoPSL($requestHost, $cookieDomain, $expected) { $this->jar->usePublicSuffixList(false); $this->assertEquals($expected, $this->jar->domainMatch($requestHost, $cookieDomain)); } /** * * @dataProvider PSLDomainsProvider */ public function testDomainMatchPSL($requestHost, $cookieDomain, $expected) { $this->jar->usePublicSuffixList(true); $this->assertEquals($expected, $this->jar->domainMatch($requestHost, $cookieDomain)); } public function testConvertExpiresToISO8601() { $dt = new DateTime(); $dt->setTimezone(new DateTimeZone('UTC')); $dt->modify('+1 day'); $this->jar->store(array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', 'expires' => $dt->format(DateTime::COOKIE), 'secure' => false )); $cookies = $this->jar->getAll(); $this->assertEquals($cookies[0]['expires'], $dt->format(DateTime::ISO8601)); } public function testProblem2038() { $this->jar->store(array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', 'expires' => 'Sun, 01 Jan 2040 03:04:05 GMT', 'secure' => false )); $cookies = $this->jar->getAll(); $this->assertEquals(array(array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', 'expires' => '2040-01-01T03:04:05+0000', 'secure' => false )), $cookies); } public function testStoreExpired() { $base = array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', 'secure' => false ); $dt = new DateTime(); $dt->setTimezone(new DateTimeZone('UTC')); $dt->modify('-1 day'); $yesterday = $dt->format(DateTime::COOKIE); $dt->modify('+2 days'); $tomorrow = $dt->format(DateTime::COOKIE); $this->jar->store($base + array('expires' => $yesterday)); $this->assertEquals(0, count($this->jar->getAll())); $this->jar->store($base + array('expires' => $tomorrow)); $this->assertEquals(1, count($this->jar->getAll())); $this->jar->store($base + array('expires' => $yesterday)); $this->assertEquals(0, count($this->jar->getAll())); } /** * * @dataProvider cookieAndSetterProvider */ public function testGetDomainAndPathFromSetter($cookie, $setter, $expected) { $this->jar->store($cookie, $setter); $expected = array_merge($cookie, $expected); $cookies = $this->jar->getAll(); $this->assertEquals($expected, $cookies[0]); } /** * * @dataProvider cookieMatchProvider */ public function testGetMatchingCookies($url, $expectedCount) { $cookies = array( array('domain' => '.example.com', 'path' => '/', 'secure' => false), array('domain' => '.example.com', 'path' => '/', 'secure' => true), array('domain' => '.example.com', 'path' => '/path', 'secure' => false), array('domain' => '.example.com', 'path' => '/other', 'secure' => false), array('domain' => 'example.com', 'path' => '/', 'secure' => false), array('domain' => 'www.example.com', 'path' => '/', 'secure' => false), array('domain' => 'specific.example.com', 'path' => '/path', 'secure' => false), array('domain' => 'nowww.example.com', 'path' => '/', 'secure' => false), ); for ($i = 0; $i < count($cookies); $i++) { $this->jar->store($cookies[$i] + array('expires' => null, 'name' => "cookie{$i}", 'value' => "cookie_{$i}_value")); } $this->assertEquals($expectedCount, count($this->jar->getMatching(new Net_URL2($url)))); } public function testLongestPathFirst() { $cookie = array( 'name' => 'foo', 'domain' => '.example.com', ); foreach (array('/', '/specific/path/', '/specific/') as $path) { $this->jar->store($cookie + array('path' => $path, 'value' => str_replace('/', '_', $path))); } $this->assertEquals( 'foo=_specific_path_; foo=_specific_; foo=_', $this->jar->getMatching(new Net_URL2('http://example.com/specific/path/file.php'), true) ); } public function testSerializable() { $dt = new DateTime(); $dt->setTimezone(new DateTimeZone('UTC')); $dt->modify('+1 day'); $cookie = array('domain' => '.example.com', 'path' => '/', 'secure' => false, 'value' => 'foo'); $this->jar->store($cookie + array('name' => 'session', 'expires' => null)); $this->jar->store($cookie + array('name' => 'long', 'expires' => $dt->format(DateTime::COOKIE))); $newJar = unserialize(serialize($this->jar)); $cookies = $newJar->getAll(); $this->assertEquals(1, count($cookies)); $this->assertEquals('long', $cookies[0]['name']); $this->jar->serializeSessionCookies(true); $newJar = unserialize(serialize($this->jar)); $this->assertEquals($this->jar->getAll(), $newJar->getAll()); } public function testRemoveExpiredOnUnserialize() { $dt = new DateTime(); $dt->setTimezone(new DateTimeZone('UTC')); $dt->modify('+2 seconds'); $this->jar->store(array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', 'expires' => $dt->format(DateTime::COOKIE), )); $serialized = serialize($this->jar); sleep(2); $newJar = unserialize($serialized); $this->assertEquals(array(), $newJar->getAll()); } public static function invalidCookieProvider() { return array( array(array()), array(array('name' => 'foo')), array(array( 'name' => 'a name', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', )), array(array( 'name' => 'foo', 'value' => 'a value', 'domain' => '.example.com', 'path' => '/', )), array(array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => null, )), array(array( 'name' => 'foo', 'value' => 'bar', 'domain' => null, 'path' => '/', )), array(array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => '/', 'expires' => 'invalid date', )), ); } public static function noPSLdomainsProvider() { return array( array('localhost', 'localhost', true), array('www.example.com', 'www.example.com', true), array('127.0.0.1', '127.0.0.1', true), array('127.0.0.1', '.0.0.1', false), array('www.example.com', '.example.com', true), array('deep.within.example.com', '.example.com', true), array('example.com', '.com', false), array('anotherexample.com', 'example.com', false), array('whatever.msk.ru', '.msk.ru', true), array('whatever.co.uk', '.co.uk', true), array('whatever.uk', '.whatever.uk', true), array('whatever.tokyo.jp', '.whatever.tokyo.jp', true), array('metro.tokyo.jp', '.metro.tokyo.jp', true), array('foo.bar', '.foo.bar', true) ); } public static function PSLdomainsProvider() { return array( array('localhost', 'localhost', true), array('www.example.com', 'www.example.com', true), array('127.0.0.1', '127.0.0.1', true), array('127.0.0.1', '.0.0.1', false), array('www.example.com', '.example.com', true), array('deep.within.example.com', '.example.com', true), array('example.com', '.com', false), array('anotherexample.com', 'example.com', false), array('whatever.msk.ru', '.msk.ru', false), array('whatever.co.uk', '.co.uk', false), array('whatever.uk', '.whatever.uk', false), array('whatever.tr', '.whatever.tr', false), array('nic.tr', '.nic.tr', true), array('foo.bar', '.foo.bar', true) ); } public static function cookieAndSetterProvider() { return array( array( array( 'name' => 'foo', 'value' => 'bar', 'domain' => null, 'path' => null, 'expires' => null, 'secure' => false ), new Net_Url2('http://example.com/directory/file.php'), array( 'domain' => 'example.com', 'path' => '/directory/' ) ), array( array( 'name' => 'foo', 'value' => 'bar', 'domain' => '.example.com', 'path' => null, 'expires' => null, 'secure' => false ), new Net_Url2('http://example.com/path/to/file.php'), array( 'path' => '/path/to/' ) ), array( array( 'name' => 'foo', 'value' => 'bar', 'domain' => null, 'path' => '/', 'expires' => null, 'secure' => false ), new Net_Url2('http://example.com/another/file.php'), array( 'domain' => 'example.com' ) ) ); } public static function cookieMatchProvider() { return array( array('http://www.example.com/path/file.php', 4), array('https://www.example.com/path/file.php', 5), array('http://example.com/path/file.php', 3), array('http://specific.example.com/path/file.php', 4), array('http://specific.example.com/other/file.php', 3), array('http://another.example.com/another', 2) ); } } ?>HTTP_Request2-2.2.1/tests/Request2/MultipartBodyTest.php0000666000000000000000000000766212266024053020016 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(dirname(__FILE__)) . '/TestHelper.php'; /** * Class representing a HTTP request */ require_once 'HTTP/Request2.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); } /** * * @expectedException HTTP_Request2_LogicException */ public function testRequest16863() { $req = new HTTP_Request2(null, HTTP_Request2::METHOD_POST); $fp = fopen(dirname(dirname(__FILE__)) . '/_files/plaintext.txt', 'rb'); $body = $req->addUpload('upload', $fp) ->getBody(); $asString = $body->__toString(); $this->assertContains('name="upload"; filename="anonymous.blob"', $asString); $this->assertContains('This is a test.', $asString); $req->addUpload('bad_upload', fopen('php://input', 'rb')); } 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(fopen(dirname(dirname(__FILE__)) . '/_files/empty.gif', 'rb'), '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); } } ?>HTTP_Request2-2.2.1/tests/Request2/ResponseTest.php0000666000000000000000000001106612266024053017006 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(dirname(__FILE__)) . '/TestHelper.php'; /** * Class representing a HTTP response */ require_once 'HTTP/Request2/Response.php'; /** * Unit test for HTTP_Request2_Response class */ class HTTP_Request2_ResponseTest extends PHPUnit_Framework_TestCase { /** * * @expectedException HTTP_Request2_MessageException */ 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()); $response3 = new HTTP_Request2_Response('Invalid status line'); } 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); } } /** * * @expectedException HTTP_Request2_MessageException */ public function testGzipEncoding() { $response = $this->readResponseFromFile('response_gzip'); $this->assertEquals('0e964e9273c606c46afbd311b5ad4d77', md5($response->getBody())); $response = $this->readResponseFromFile('response_gzip_broken'); $body = $response->getBody(); } 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())); } public function testBug18169() { $response = $this->readResponseFromFile('bug_18169'); $this->assertEquals('', $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; } } ?>HTTP_Request2-2.2.1/tests/AllTests.php0000666000000000000000000000304212266024053014364 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ if (!defined('PHPUnit_MAIN_METHOD')) { define('PHPUnit_MAIN_METHOD', 'HTTP_Request2_AllTests::main'); } 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() { if (!function_exists('phpunit_autoload')) { require_once 'PHPUnit/TextUI/TestRunner.php'; } 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(); } ?>HTTP_Request2-2.2.1/tests/NetworkConfig.php.dist0000666000000000000000000000375112266024053016361 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** * This file contains configuration needed for running HTTP_Request2 tests * that interact with the network. Do not edit this file, copy it to * NetworkConfig.php and edit the copy instead. */ /** * Base URL for HTTP_Request2 Adapters tests * * To enable the tests that actually perform network interaction, you should * copy the contents of _network directory to a directory under your web * server's document root or create a symbolic link to _network directory * there. Set this constant to point to the URL of that directory. */ define('HTTP_REQUEST2_TESTS_BASE_URL', null); /** * URL that is protected by server digest authentication * * This is needed for testing of 100 Continue handling, we can't implement * digest in PHP since it will kick in a bit later */ define('HTTP_REQUEST2_TESTS_DIGEST_URL', null); /**#@+ * Proxy setup for Socket Adapter tests * * Set these constants to run additional tests for Socket Adapter using a HTTP * proxy. If proxy host is not set then the tests will not be run. */ define('HTTP_REQUEST2_TESTS_PROXY_HOST', null); define('HTTP_REQUEST2_TESTS_PROXY_PORT', 8080); define('HTTP_REQUEST2_TESTS_PROXY_USER', ''); define('HTTP_REQUEST2_TESTS_PROXY_PASSWORD', ''); define('HTTP_REQUEST2_TESTS_PROXY_AUTH_SCHEME', 'basic'); define('HTTP_REQUEST2_TESTS_PROXY_TYPE', 'http'); /**#@-*/ ?>HTTP_Request2-2.2.1/tests/ObserverTest.php0000666000000000000000000000540012266024053015260 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(__FILE__) . '/TestHelper.php'; /** * Class representing a HTTP request */ require_once 'HTTP/Request2.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); } } ?>HTTP_Request2-2.2.1/tests/Request2Test.php0000666000000000000000000003067012266024053015212 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Sets up includes */ require_once dirname(__FILE__) . '/TestHelper.php'; /** * Class representing a HTTP request */ require_once 'HTTP/Request2.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')); } /** * * @expectedException HTTP_Request2_LogicException */ 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->assertInstanceOf('Net_URL2', $req2->getUrl()); $this->assertEquals($urlString, $req2->getUrl()->getUrl()); $req3 = new HTTP_Request2(); $req3->setUrl(array('This will cause an error')); } 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() ); } /** * * @expectedException HTTP_Request2_LogicException */ public function testSetMethod() { $req = new HTTP_Request2(); $req->setMethod(HTTP_Request2::METHOD_PUT); $this->assertEquals(HTTP_Request2::METHOD_PUT, $req->getMethod()); $req->setMethod('Invalid method'); } 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')); $this->fail('Expected HTTP_Request2_LogicException was not thrown'); } catch (HTTP_Request2_LogicException $e) {} try { $req->getConfig('bar'); $this->fail('Expected HTTP_Request2_LogicException was not thrown'); } catch (HTTP_Request2_LogicException $e) {} } public function testSetProxyAsUrl() { $req = new HTTP_Request2(); $req->setConfig('proxy', 'socks5://foo:bar%25baz@localhost:1080/'); $this->assertEquals('socks5', $req->getConfig('proxy_type')); $this->assertEquals('localhost', $req->getConfig('proxy_host')); $this->assertEquals(1080, $req->getConfig('proxy_port')); $this->assertEquals('foo', $req->getConfig('proxy_user')); $this->assertEquals('bar%baz', $req->getConfig('proxy_password')); } /** * * @expectedException HTTP_Request2_LogicException */ 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() ); $req->setHeader('Invalid header', 'value'); } 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 testRequest17507() { $req = new HTTP_Request2(); $req->setHeader('accept-charset', 'iso-8859-1'); $req->setHeader('accept-charset', array('windows-1251', 'utf-8'), false); $req->setHeader(array('accept' => 'text/html')); $req->setHeader(array('accept' => 'image/gif'), null, false); $headers = $req->getHeaders(); $this->assertEquals('iso-8859-1, windows-1251, utf-8', $headers['accept-charset']); $this->assertEquals('text/html, image/gif', $headers['accept']); } /** * * @expectedException HTTP_Request2_LogicException */ 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']); $req->addCookie('invalid cookie', 'value'); } /** * * @expectedException HTTP_Request2_LogicException */ 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->assertRegexp( '!^(text/plain|application/octet-stream)!', $headers['content-type'] ); $this->assertEquals('This is a test.', fread($req->getBody(), 1024)); $req->setBody('missing file', true); } /** * * @expectedException HTTP_Request2_LogicException */ public function testRequest16863() { $req = new HTTP_Request2(); $req->setBody(fopen(dirname(__FILE__) . '/_files/plaintext.txt', 'rb')); $headers = $req->getHeaders(); $this->assertEquals('application/octet-stream', $headers['content-type']); $req->setBody(fopen('php://input', 'rb')); } 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()); } /** * * @expectedException HTTP_Request2_LogicException * @expectedExceptionMessage missing file */ 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']); $req->addUpload('upload_2', 'missing file'); } 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() { require_once 'HTTP/Request2/MultipartBody.php'; $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()); } public function testBug17460() { $req = new HTTP_Request2('http://www.example.com/', HTTP_Request2::METHOD_POST); $req->addPostParameter('foo', 'bar') ->setHeader('content-type', 'application/x-www-form-urlencoded; charset=UTF-8'); $this->assertEquals('foo=bar', $req->getBody()); } /** * * @expectedException HTTP_Request2_LogicException */ public function testCookieJar() { $req = new HTTP_Request2(); $this->assertNull($req->getCookieJar()); $req->setCookieJar(); $jar = $req->getCookieJar(); $this->assertInstanceOf('HTTP_Request2_CookieJar', $jar); $req2 = new HTTP_Request2(); $req2->setCookieJar($jar); $this->assertSame($jar, $req2->getCookieJar()); $req2->setCookieJar(null); $this->assertNull($req2->getCookieJar()); $req2->setCookieJar('foo'); } public function testAddCookieToJar() { $req = new HTTP_Request2(); $req->setCookieJar(); try { $req->addCookie('foo', 'bar'); $this->fail('Expected HTTP_Request2_Exception was not thrown'); } catch (HTTP_Request2_LogicException $e) { } $req->setUrl('http://example.com/path/file.php'); $req->addCookie('foo', 'bar'); $this->assertArrayNotHasKey('cookie', $req->getHeaders()); $cookies = $req->getCookieJar()->getAll(); $this->assertEquals( array( 'name' => 'foo', 'value' => 'bar', 'domain' => 'example.com', 'path' => '/path/', 'expires' => null, 'secure' => false ), $cookies[0] ); } /** * @expectedException HTTP_Request2_LogicException * @expectedExceptionMessage none */ public function testDisallowEmptyUrls() { $req = new HTTP_Request2(); $req->send(); } /** * @expectedException HTTP_Request2_LogicException * @expectedExceptionMessage '/foo/bar.php' */ public function testDisallowRelativeUrls() { $req = new HTTP_Request2('/foo/bar.php'); $req->send(); } } ?>HTTP_Request2-2.2.1/tests/TestHelper.php0000666000000000000000000000334412266024053014715 0ustar * @copyright 2008-2014 Alexey Borzov * @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License * @link http://pear.php.net/package/HTTP_Request2 */ /** Include PHPUnit dependencies based on version */ require_once 'PHPUnit/Runner/Version.php'; // If running from SVN checkout, update include_path if ('@' . 'package_version@' == '@package_version@') { $classPath = realpath(dirname(dirname(__FILE__))); $includePath = array_map('realpath', explode(PATH_SEPARATOR, get_include_path())); if (0 !== ($key = array_search($classPath, $includePath))) { if (false !== $key) { unset($includePath[$key]); } set_include_path($classPath . PATH_SEPARATOR . implode(PATH_SEPARATOR, $includePath)); } } $phpunitVersion = PHPUnit_Runner_Version::id(); if ($phpunitVersion == '@' . 'package_version@' || !version_compare($phpunitVersion, '3.8', '<=')) { echo "This version of PHPUnit is not supported."; exit(1); } elseif (version_compare($phpunitVersion, '3.5.0', '>=')) { require_once 'PHPUnit/Autoload.php'; } else { require_once 'PHPUnit/Framework.php'; } if (!defined('HTTP_REQUEST2_TESTS_BASE_URL') && is_readable(dirname(__FILE__) . '/NetworkConfig.php') ) { require_once dirname(__FILE__) . '/NetworkConfig.php'; } ?>HTTP_Request2-2.2.1/docs/LICENSE0000666000000000000000000000304512266024053012716 0ustar HTTP_Request2 Copyright (c) 2008-2014, Alexey Borzov All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of Alexey Borzov nor the names of his 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 HOLDER 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. HTTP_Request2-2.2.1/docs/examples/upload-rapidshare.php0000666000000000000000000000407612266024053017651 0ustar 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(); } ?> HTTP_Request2-2.2.1/data/generate-list.php0000666000000000000000000000526512266024053015154 0ustar "); } if (0 == ($count = count($valueTree))) { fwrite($fp, 'true'); } else { fwrite($fp, "array(\n"); for ($keys = array_keys($valueTree), $i = 0; $i < $count; $i++) { writeNode($fp, $valueTree[$keys[$i]], $keys[$i], $indent + 1); if ($i + 1 != $count) { fwrite($fp, ",\n"); } else { fwrite($fp, "\n"); } } fwrite($fp, str_repeat(' ', $indent) . ")"); } } try { $request = new HTTP_Request2(LIST_URL); $response = $request->send(); if (200 != $response->getStatus()) { throw new Exception("List download URL returned status: " . $response->getStatus() . ' ' . $response->getReasonPhrase()); } $list = $response->getBody(); if (false === strpos($list, '// ===BEGIN ICANN DOMAINS===')) { throw new Exception("List download URL does not contain expected phrase"); } if (!($fp = @fopen(OUTPUT_FILE, 'wt'))) { throw new Exception("Unable to open " . OUTPUT_FILE); } } catch (Exception $e) { die($e->getMessage()); } $tldTree = array(); $license = true; fwrite($fp, ""); fclose($fp); ?>HTTP_Request2-2.2.1/data/public-suffix-list.php0000666000000000000000000041135412266024053016142 0ustar array( 'com' => true, 'edu' => true, 'gov' => true, 'net' => true, 'mil' => true, 'org' => true ), 'ad' => array( 'nom' => true ), 'ae' => array( 'co' => true, 'net' => true, 'org' => true, 'sch' => true, 'ac' => true, 'gov' => true, 'mil' => true ), 'aero' => array( 'accident-investigation' => true, 'accident-prevention' => true, 'aerobatic' => true, 'aeroclub' => true, 'aerodrome' => true, 'agents' => true, 'aircraft' => true, 'airline' => true, 'airport' => true, 'air-surveillance' => true, 'airtraffic' => true, 'air-traffic-control' => true, 'ambulance' => true, 'amusement' => true, 'association' => true, 'author' => true, 'ballooning' => true, 'broker' => true, 'caa' => true, 'cargo' => true, 'catering' => true, 'certification' => true, 'championship' => true, 'charter' => true, 'civilaviation' => true, 'club' => true, 'conference' => true, 'consultant' => true, 'consulting' => true, 'control' => true, 'council' => true, 'crew' => true, 'design' => true, 'dgca' => true, 'educator' => true, 'emergency' => true, 'engine' => true, 'engineer' => true, 'entertainment' => true, 'equipment' => true, 'exchange' => true, 'express' => true, 'federation' => true, 'flight' => true, 'freight' => true, 'fuel' => true, 'gliding' => true, 'government' => true, 'groundhandling' => true, 'group' => true, 'hanggliding' => true, 'homebuilt' => true, 'insurance' => true, 'journal' => true, 'journalist' => true, 'leasing' => true, 'logistics' => true, 'magazine' => true, 'maintenance' => true, 'marketplace' => true, 'media' => true, 'microlight' => true, 'modelling' => true, 'navigation' => true, 'parachuting' => true, 'paragliding' => true, 'passenger-association' => true, 'pilot' => true, 'press' => true, 'production' => true, 'recreation' => true, 'repbody' => true, 'res' => true, 'research' => true, 'rotorcraft' => true, 'safety' => true, 'scientist' => true, 'services' => true, 'show' => true, 'skydiving' => true, 'software' => true, 'student' => true, 'taxi' => true, 'trader' => true, 'trading' => true, 'trainer' => true, 'union' => true, 'workinggroup' => true, 'works' => true ), 'af' => array( 'gov' => true, 'com' => true, 'org' => true, 'net' => true, 'edu' => true ), 'ag' => array( 'com' => true, 'org' => true, 'net' => true, 'co' => true, 'nom' => true ), 'ai' => array( 'off' => true, 'com' => true, 'net' => true, 'org' => true ), 'al' => array( 'com' => true, 'edu' => true, 'gov' => true, 'mil' => true, 'net' => true, 'org' => true ), 'am' => true, 'an' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true ), 'ao' => array( 'ed' => true, 'gv' => true, 'og' => true, 'co' => true, 'pb' => true, 'it' => true ), 'aq' => true, 'ar' => array( 'com' => array( 'blogspot' => true ), 'edu' => true, 'gob' => true, 'int' => true, 'mil' => true, 'net' => true, 'org' => true, 'tur' => true ), 'arpa' => array( 'e164' => true, 'in-addr' => true, 'ip6' => true, 'iris' => true, 'uri' => true, 'urn' => true ), 'as' => array( 'gov' => true ), 'asia' => true, 'at' => array( 'ac' => true, 'co' => array( 'blogspot' => true ), 'gv' => true, 'or' => true, 'biz' => true, 'info' => true, 'priv' => true ), 'au' => array( 'com' => array( 'blogspot' => true ), 'net' => true, 'org' => true, 'edu' => array( 'act' => true, 'nsw' => true, 'nt' => true, 'qld' => true, 'sa' => true, 'tas' => true, 'vic' => true, 'wa' => true ), 'gov' => array( 'act' => true, 'qld' => true, 'sa' => true, 'tas' => true, 'vic' => true, 'wa' => true ), 'asn' => true, 'id' => true, 'csiro' => true, 'info' => true, 'conf' => true, 'oz' => true, 'act' => true, 'nsw' => true, 'nt' => true, 'qld' => true, 'sa' => true, 'tas' => true, 'vic' => true, 'wa' => true ), 'aw' => array( 'com' => true ), 'ax' => true, 'az' => array( 'com' => true, 'net' => true, 'int' => true, 'gov' => true, 'org' => true, 'edu' => true, 'info' => true, 'pp' => true, 'mil' => true, 'name' => true, 'pro' => true, 'biz' => true ), 'ba' => array( 'org' => true, 'net' => true, 'edu' => true, 'gov' => true, 'mil' => true, 'unsa' => true, 'unbi' => true, 'co' => true, 'com' => true, 'rs' => true ), 'bb' => array( 'biz' => true, 'com' => true, 'edu' => true, 'gov' => true, 'info' => true, 'net' => true, 'org' => true, 'store' => true ), 'bd' => array( '*' => true ), 'be' => array( 'ac' => true, 'blogspot' => true ), 'bf' => array( 'gov' => true ), 'bg' => array( 'a' => true, 'b' => true, 'c' => true, 'd' => true, 'e' => true, 'f' => true, 'g' => true, 'h' => true, 'i' => true, 'j' => true, 'k' => true, 'l' => true, 'm' => true, 'n' => true, 'o' => true, 'p' => true, 'q' => true, 'r' => true, 's' => true, 't' => true, 'u' => true, 'v' => true, 'w' => true, 'x' => true, 'y' => true, 'z' => true, '0' => true, '1' => true, '2' => true, '3' => true, '4' => true, '5' => true, '6' => true, '7' => true, '8' => true, '9' => true ), 'bh' => array( 'com' => true, 'edu' => true, 'net' => true, 'org' => true, 'gov' => true ), 'bi' => array( 'co' => true, 'com' => true, 'edu' => true, 'or' => true, 'org' => true ), 'biz' => array( 'dyndns' => true, 'for-better' => true, 'for-more' => true, 'for-some' => true, 'for-the' => true, 'selfip' => true, 'webhop' => true ), 'bj' => array( 'asso' => true, 'barreau' => true, 'gouv' => true, 'blogspot' => true ), 'bm' => array( 'com' => true, 'edu' => true, 'gov' => true, 'net' => true, 'org' => true ), 'bn' => array( '*' => true ), 'bo' => array( 'com' => true, 'edu' => true, 'gov' => true, 'gob' => true, 'int' => true, 'org' => true, 'net' => true, 'mil' => true, 'tv' => true ), 'br' => array( 'adm' => true, 'adv' => true, 'agr' => true, 'am' => true, 'arq' => true, 'art' => true, 'ato' => true, 'b' => true, 'bio' => true, 'blog' => true, 'bmd' => true, 'cim' => true, 'cng' => true, 'cnt' => true, 'com' => array( 'blogspot' => true ), 'coop' => true, 'ecn' => true, 'eco' => true, 'edu' => true, 'emp' => true, 'eng' => true, 'esp' => true, 'etc' => true, 'eti' => true, 'far' => true, 'flog' => true, 'fm' => true, 'fnd' => true, 'fot' => true, 'fst' => true, 'g12' => true, 'ggf' => true, 'gov' => true, 'imb' => true, 'ind' => true, 'inf' => true, 'jor' => true, 'jus' => true, 'leg' => true, 'lel' => true, 'mat' => true, 'med' => true, 'mil' => true, 'mus' => true, 'net' => true, 'nom' => true, 'not' => true, 'ntr' => true, 'odo' => true, 'org' => true, 'ppg' => true, 'pro' => true, 'psc' => true, 'psi' => true, 'qsl' => true, 'radio' => true, 'rec' => true, 'slg' => true, 'srv' => true, 'taxi' => true, 'teo' => true, 'tmp' => true, 'trd' => true, 'tur' => true, 'tv' => true, 'vet' => true, 'vlog' => true, 'wiki' => true, 'zlg' => true ), 'bs' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true, 'gov' => true ), 'bt' => array( 'com' => true, 'edu' => true, 'gov' => true, 'net' => true, 'org' => true ), 'bv' => true, 'bw' => array( 'co' => true, 'org' => true ), 'by' => array( 'gov' => true, 'mil' => true, 'com' => true, 'of' => true ), 'bz' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true, 'gov' => true ), 'ca' => array( 'ab' => true, 'bc' => true, 'mb' => true, 'nb' => true, 'nf' => true, 'nl' => true, 'ns' => true, 'nt' => true, 'nu' => true, 'on' => true, 'pe' => true, 'qc' => true, 'sk' => true, 'yk' => true, 'gc' => true, 'co' => true, 'blogspot' => true ), 'cat' => true, 'cc' => array( 'ftpaccess' => true, 'game-server' => true, 'myphotos' => true, 'scrapping' => true ), 'cd' => array( 'gov' => true ), 'cf' => array( 'blogspot' => true ), 'cg' => true, 'ch' => array( 'blogspot' => true ), 'ci' => array( 'org' => true, 'or' => true, 'com' => true, 'co' => true, 'edu' => true, 'ed' => true, 'ac' => true, 'net' => true, 'go' => true, 'asso' => true, 'aéroport' => true, 'int' => true, 'presse' => true, 'md' => true, 'gouv' => true ), 'ck' => array( '*' => true, '!www' => true ), 'cl' => array( 'gov' => true, 'gob' => true, 'co' => true, 'mil' => true ), 'cm' => array( 'gov' => true ), 'cn' => array( 'ac' => true, 'com' => true, 'edu' => true, 'gov' => true, 'net' => true, 'org' => true, 'mil' => true, '公司' => true, '网络' => true, '網絡' => true, 'ah' => true, 'bj' => true, 'cq' => true, 'fj' => true, 'gd' => true, 'gs' => true, 'gz' => true, 'gx' => true, 'ha' => true, 'hb' => true, 'he' => true, 'hi' => true, 'hl' => true, 'hn' => true, 'jl' => true, 'js' => true, 'jx' => true, 'ln' => true, 'nm' => true, 'nx' => true, 'qh' => true, 'sc' => true, 'sd' => true, 'sh' => true, 'sn' => true, 'sx' => true, 'tj' => true, 'xj' => true, 'xz' => true, 'yn' => true, 'zj' => true, 'hk' => true, 'mo' => true, 'tw' => true ), 'co' => array( 'arts' => true, 'com' => true, 'edu' => true, 'firm' => true, 'gov' => true, 'info' => true, 'int' => true, 'mil' => true, 'net' => true, 'nom' => true, 'org' => true, 'rec' => true, 'web' => true ), 'com' => array( 'amazonaws' => array( 'compute' => array( 'ap-northeast-1' => true, 'ap-southeast-1' => true, 'ap-southeast-2' => true, 'eu-west-1' => true, 'sa-east-1' => true, 'us-gov-west-1' => true, 'us-west-1' => true, 'us-west-2' => true ), 'us-east-1' => true, 'compute-1' => array( 'z-1' => true, 'z-2' => true ), 'elb' => true, 's3' => true, 's3-us-west-2' => true, 's3-us-west-1' => true, 's3-eu-west-1' => true, 's3-ap-southeast-1' => true, 's3-ap-southeast-2' => true, 's3-ap-northeast-1' => true, 's3-sa-east-1' => true, 's3-us-gov-west-1' => true, 's3-fips-us-gov-west-1' => true, 's3-website-us-east-1' => true, 's3-website-us-west-2' => true, 's3-website-us-west-1' => true, 's3-website-eu-west-1' => true, 's3-website-ap-southeast-1' => true, 's3-website-ap-southeast-2' => true, 's3-website-ap-northeast-1' => true, 's3-website-sa-east-1' => true, 's3-website-us-gov-west-1' => true ), 'elasticbeanstalk' => true, 'betainabox' => true, 'ar' => true, 'br' => true, 'cn' => true, 'de' => true, 'eu' => true, 'gb' => true, 'gr' => true, 'hu' => true, 'jpn' => true, 'kr' => true, 'no' => true, 'qc' => true, 'ru' => true, 'sa' => true, 'se' => true, 'uk' => true, 'us' => true, 'uy' => true, 'za' => true, 'cloudcontrolled' => true, 'cloudcontrolapp' => true, 'dreamhosters' => true, 'dyndns-at-home' => true, 'dyndns-at-work' => true, 'dyndns-blog' => true, 'dyndns-free' => true, 'dyndns-home' => true, 'dyndns-ip' => true, 'dyndns-mail' => true, 'dyndns-office' => true, 'dyndns-pics' => true, 'dyndns-remote' => true, 'dyndns-server' => true, 'dyndns-web' => true, 'dyndns-wiki' => true, 'dyndns-work' => true, 'blogdns' => true, 'cechire' => true, 'dnsalias' => true, 'dnsdojo' => true, 'doesntexist' => true, 'dontexist' => true, 'doomdns' => true, 'dyn-o-saur' => true, 'dynalias' => true, 'est-a-la-maison' => true, 'est-a-la-masion' => true, 'est-le-patron' => true, 'est-mon-blogueur' => true, 'from-ak' => true, 'from-al' => true, 'from-ar' => true, 'from-ca' => true, 'from-ct' => true, 'from-dc' => true, 'from-de' => true, 'from-fl' => true, 'from-ga' => true, 'from-hi' => true, 'from-ia' => true, 'from-id' => true, 'from-il' => true, 'from-in' => true, 'from-ks' => true, 'from-ky' => true, 'from-ma' => true, 'from-md' => true, 'from-mi' => true, 'from-mn' => true, 'from-mo' => true, 'from-ms' => true, 'from-mt' => true, 'from-nc' => true, 'from-nd' => true, 'from-ne' => true, 'from-nh' => true, 'from-nj' => true, 'from-nm' => true, 'from-nv' => true, 'from-oh' => true, 'from-ok' => true, 'from-or' => true, 'from-pa' => true, 'from-pr' => true, 'from-ri' => true, 'from-sc' => true, 'from-sd' => true, 'from-tn' => true, 'from-tx' => true, 'from-ut' => true, 'from-va' => true, 'from-vt' => true, 'from-wa' => true, 'from-wi' => true, 'from-wv' => true, 'from-wy' => true, 'getmyip' => true, 'gotdns' => true, 'hobby-site' => true, 'homelinux' => true, 'homeunix' => true, 'iamallama' => true, 'is-a-anarchist' => true, 'is-a-blogger' => true, 'is-a-bookkeeper' => true, 'is-a-bulls-fan' => true, 'is-a-caterer' => true, 'is-a-chef' => true, 'is-a-conservative' => true, 'is-a-cpa' => true, 'is-a-cubicle-slave' => true, 'is-a-democrat' => true, 'is-a-designer' => true, 'is-a-doctor' => true, 'is-a-financialadvisor' => true, 'is-a-geek' => true, 'is-a-green' => true, 'is-a-guru' => true, 'is-a-hard-worker' => true, 'is-a-hunter' => true, 'is-a-landscaper' => true, 'is-a-lawyer' => true, 'is-a-liberal' => true, 'is-a-libertarian' => true, 'is-a-llama' => true, 'is-a-musician' => true, 'is-a-nascarfan' => true, 'is-a-nurse' => true, 'is-a-painter' => true, 'is-a-personaltrainer' => true, 'is-a-photographer' => true, 'is-a-player' => true, 'is-a-republican' => true, 'is-a-rockstar' => true, 'is-a-socialist' => true, 'is-a-student' => true, 'is-a-teacher' => true, 'is-a-techie' => true, 'is-a-therapist' => true, 'is-an-accountant' => true, 'is-an-actor' => true, 'is-an-actress' => true, 'is-an-anarchist' => true, 'is-an-artist' => true, 'is-an-engineer' => true, 'is-an-entertainer' => true, 'is-certified' => true, 'is-gone' => true, 'is-into-anime' => true, 'is-into-cars' => true, 'is-into-cartoons' => true, 'is-into-games' => true, 'is-leet' => true, 'is-not-certified' => true, 'is-slick' => true, 'is-uberleet' => true, 'is-with-theband' => true, 'isa-geek' => true, 'isa-hockeynut' => true, 'issmarterthanyou' => true, 'likes-pie' => true, 'likescandy' => true, 'neat-url' => true, 'saves-the-whales' => true, 'selfip' => true, 'sells-for-less' => true, 'sells-for-u' => true, 'servebbs' => true, 'simple-url' => true, 'space-to-rent' => true, 'teaches-yoga' => true, 'writesthisblog' => true, 'ro' => true, 'appspot' => true, 'blogspot' => true, 'codespot' => true, 'googleapis' => true, 'googlecode' => true, 'herokuapp' => true, 'herokussl' => true, 'operaunite' => true, 'rhcloud' => true ), 'coop' => true, 'cr' => array( 'ac' => true, 'co' => true, 'ed' => true, 'fi' => true, 'go' => true, 'or' => true, 'sa' => true ), 'cu' => array( 'com' => true, 'edu' => true, 'org' => true, 'net' => true, 'gov' => true, 'inf' => true ), 'cv' => array( 'blogspot' => true ), 'cw' => array( 'com' => true, 'edu' => true, 'net' => true, 'org' => true ), 'cx' => array( 'gov' => true, 'ath' => true ), 'cy' => array( '*' => true ), 'cz' => array( 'blogspot' => true ), 'de' => array( 'com' => true, 'fuettertdasnetz' => true, 'isteingeek' => true, 'istmein' => true, 'lebtimnetz' => true, 'leitungsen' => true, 'traeumtgerade' => true, 'blogspot' => true ), 'dj' => true, 'dk' => array( 'blogspot' => true ), 'dm' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true, 'gov' => true ), 'do' => array( 'art' => true, 'com' => true, 'edu' => true, 'gob' => true, 'gov' => true, 'mil' => true, 'net' => true, 'org' => true, 'sld' => true, 'web' => true ), 'dz' => array( 'com' => true, 'org' => true, 'net' => true, 'gov' => true, 'edu' => true, 'asso' => true, 'pol' => true, 'art' => true ), 'ec' => array( 'com' => true, 'info' => true, 'net' => true, 'fin' => true, 'k12' => true, 'med' => true, 'pro' => true, 'org' => true, 'edu' => true, 'gov' => true, 'gob' => true, 'mil' => true ), 'edu' => true, 'ee' => array( 'edu' => true, 'gov' => true, 'riik' => true, 'lib' => true, 'med' => true, 'com' => true, 'pri' => true, 'aip' => true, 'org' => true, 'fie' => true ), 'eg' => array( 'com' => true, 'edu' => true, 'eun' => true, 'gov' => true, 'mil' => true, 'name' => true, 'net' => true, 'org' => true, 'sci' => true ), 'er' => array( '*' => true ), 'es' => array( 'com' => array( 'blogspot' => true ), 'nom' => true, 'org' => true, 'gob' => true, 'edu' => true ), 'et' => array( '*' => true ), 'eu' => true, 'fi' => array( 'aland' => true, 'blogspot' => true, 'iki' => true ), 'fj' => array( '*' => true ), 'fk' => array( '*' => true ), 'fm' => true, 'fo' => true, 'fr' => array( 'com' => true, 'asso' => true, 'nom' => true, 'prd' => true, 'presse' => true, 'tm' => true, 'aeroport' => true, 'assedic' => true, 'avocat' => true, 'avoues' => true, 'cci' => true, 'chambagri' => true, 'chirurgiens-dentistes' => true, 'experts-comptables' => true, 'geometre-expert' => true, 'gouv' => true, 'greta' => true, 'huissier-justice' => true, 'medecin' => true, 'notaires' => true, 'pharmacien' => true, 'port' => true, 'veterinaire' => true, 'blogspot' => true ), 'ga' => true, 'gb' => true, 'gd' => true, 'ge' => array( 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'mil' => true, 'net' => true, 'pvt' => true ), 'gf' => true, 'gg' => array( 'co' => true, 'net' => true, 'org' => true ), 'gh' => array( 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'mil' => true ), 'gi' => array( 'com' => true, 'ltd' => true, 'gov' => true, 'mod' => true, 'edu' => true, 'org' => true ), 'gl' => true, 'gm' => true, 'gn' => array( 'ac' => true, 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'net' => true ), 'gov' => true, 'gp' => array( 'com' => true, 'net' => true, 'mobi' => true, 'edu' => true, 'org' => true, 'asso' => true ), 'gq' => true, 'gr' => array( 'com' => true, 'edu' => true, 'net' => true, 'org' => true, 'gov' => true, 'blogspot' => true ), 'gs' => true, 'gt' => array( 'com' => true, 'edu' => true, 'gob' => true, 'ind' => true, 'mil' => true, 'net' => true, 'org' => true ), 'gu' => array( '*' => true ), 'gw' => true, 'gy' => array( 'co' => true, 'com' => true, 'net' => true ), 'hk' => array( 'com' => true, 'edu' => true, 'gov' => true, 'idv' => true, 'net' => true, 'org' => true, '公司' => true, '教育' => true, '敎育' => true, '政府' => true, '個人' => true, '个人' => true, '箇人' => true, '網络' => true, '网络' => true, '组織' => true, '網絡' => true, '网絡' => true, '组织' => true, '組織' => true, '組织' => true, 'blogspot' => true ), 'hm' => true, 'hn' => array( 'com' => true, 'edu' => true, 'org' => true, 'net' => true, 'mil' => true, 'gob' => true ), 'hr' => array( 'iz' => true, 'from' => true, 'name' => true, 'com' => true ), 'ht' => array( 'com' => true, 'shop' => true, 'firm' => true, 'info' => true, 'adult' => true, 'net' => true, 'pro' => true, 'org' => true, 'med' => true, 'art' => true, 'coop' => true, 'pol' => true, 'asso' => true, 'edu' => true, 'rel' => true, 'gouv' => true, 'perso' => true ), 'hu' => array( 'co' => true, 'info' => true, 'org' => true, 'priv' => true, 'sport' => true, 'tm' => true, '2000' => true, 'agrar' => true, 'bolt' => true, 'casino' => true, 'city' => true, 'erotica' => true, 'erotika' => true, 'film' => true, 'forum' => true, 'games' => true, 'hotel' => true, 'ingatlan' => true, 'jogasz' => true, 'konyvelo' => true, 'lakas' => true, 'media' => true, 'news' => true, 'reklam' => true, 'sex' => true, 'shop' => true, 'suli' => true, 'szex' => true, 'tozsde' => true, 'utazas' => true, 'video' => true, 'blogspot' => true ), 'id' => array( 'ac' => true, 'biz' => true, 'co' => true, 'go' => true, 'mil' => true, 'my' => true, 'net' => true, 'or' => true, 'sch' => true, 'web' => true ), 'ie' => array( 'gov' => true, 'blogspot' => true ), 'il' => array( '*' => true, 'co' => array( 'blogspot' => true ) ), 'im' => array( 'ac' => true, 'co' => array( 'ltd' => true, 'plc' => true ), 'com' => true, 'net' => true, 'org' => true, 'tt' => true, 'tv' => true ), 'in' => array( 'co' => true, 'firm' => true, 'net' => true, 'org' => true, 'gen' => true, 'ind' => true, 'nic' => true, 'ac' => true, 'edu' => true, 'res' => true, 'gov' => true, 'mil' => true, 'blogspot' => true ), 'info' => array( 'dyndns' => true, 'barrel-of-knowledge' => true, 'barrell-of-knowledge' => true, 'for-our' => true, 'groks-the' => true, 'groks-this' => true, 'here-for-more' => true, 'knowsitall' => true, 'selfip' => true, 'webhop' => true ), 'int' => array( 'eu' => true ), 'io' => array( 'com' => true, 'github' => true ), 'iq' => array( 'gov' => true, 'edu' => true, 'mil' => true, 'com' => true, 'org' => true, 'net' => true ), 'ir' => array( 'ac' => true, 'co' => true, 'gov' => true, 'id' => true, 'net' => true, 'org' => true, 'sch' => true, 'ایران' => true, 'ايران' => true ), 'is' => array( 'net' => true, 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'int' => true, 'cupcake' => true ), 'it' => array( 'gov' => true, 'edu' => true, 'agrigento' => true, 'ag' => true, 'alessandria' => true, 'al' => true, 'ancona' => true, 'an' => true, 'aosta' => true, 'aoste' => true, 'ao' => true, 'arezzo' => true, 'ar' => true, 'ascoli-piceno' => true, 'ascolipiceno' => true, 'ap' => true, 'asti' => true, 'at' => true, 'avellino' => true, 'av' => true, 'bari' => true, 'ba' => true, 'andria-barletta-trani' => true, 'andriabarlettatrani' => true, 'trani-barletta-andria' => true, 'tranibarlettaandria' => true, 'barletta-trani-andria' => true, 'barlettatraniandria' => true, 'andria-trani-barletta' => true, 'andriatranibarletta' => true, 'trani-andria-barletta' => true, 'traniandriabarletta' => true, 'bt' => true, 'belluno' => true, 'bl' => true, 'benevento' => true, 'bn' => true, 'bergamo' => true, 'bg' => true, 'biella' => true, 'bi' => true, 'bologna' => true, 'bo' => true, 'bolzano' => true, 'bozen' => true, 'balsan' => true, 'alto-adige' => true, 'altoadige' => true, 'suedtirol' => true, 'bz' => true, 'brescia' => true, 'bs' => true, 'brindisi' => true, 'br' => true, 'cagliari' => true, 'ca' => true, 'caltanissetta' => true, 'cl' => true, 'campobasso' => true, 'cb' => true, 'carboniaiglesias' => true, 'carbonia-iglesias' => true, 'iglesias-carbonia' => true, 'iglesiascarbonia' => true, 'ci' => true, 'caserta' => true, 'ce' => true, 'catania' => true, 'ct' => true, 'catanzaro' => true, 'cz' => true, 'chieti' => true, 'ch' => true, 'como' => true, 'co' => true, 'cosenza' => true, 'cs' => true, 'cremona' => true, 'cr' => true, 'crotone' => true, 'kr' => true, 'cuneo' => true, 'cn' => true, 'dell-ogliastra' => true, 'dellogliastra' => true, 'ogliastra' => true, 'og' => true, 'enna' => true, 'en' => true, 'ferrara' => true, 'fe' => true, 'fermo' => true, 'fm' => true, 'firenze' => true, 'florence' => true, 'fi' => true, 'foggia' => true, 'fg' => true, 'forli-cesena' => true, 'forlicesena' => true, 'cesena-forli' => true, 'cesenaforli' => true, 'fc' => true, 'frosinone' => true, 'fr' => true, 'genova' => true, 'genoa' => true, 'ge' => true, 'gorizia' => true, 'go' => true, 'grosseto' => true, 'gr' => true, 'imperia' => true, 'im' => true, 'isernia' => true, 'is' => true, 'laquila' => true, 'aquila' => true, 'aq' => true, 'la-spezia' => true, 'laspezia' => true, 'sp' => true, 'latina' => true, 'lt' => true, 'lecce' => true, 'le' => true, 'lecco' => true, 'lc' => true, 'livorno' => true, 'li' => true, 'lodi' => true, 'lo' => true, 'lucca' => true, 'lu' => true, 'macerata' => true, 'mc' => true, 'mantova' => true, 'mn' => true, 'massa-carrara' => true, 'massacarrara' => true, 'carrara-massa' => true, 'carraramassa' => true, 'ms' => true, 'matera' => true, 'mt' => true, 'medio-campidano' => true, 'mediocampidano' => true, 'campidano-medio' => true, 'campidanomedio' => true, 'vs' => true, 'messina' => true, 'me' => true, 'milano' => true, 'milan' => true, 'mi' => true, 'modena' => true, 'mo' => true, 'monza' => true, 'monza-brianza' => true, 'monzabrianza' => true, 'monzaebrianza' => true, 'monzaedellabrianza' => true, 'monza-e-della-brianza' => true, 'mb' => true, 'napoli' => true, 'naples' => true, 'na' => true, 'novara' => true, 'no' => true, 'nuoro' => true, 'nu' => true, 'oristano' => true, 'or' => true, 'padova' => true, 'padua' => true, 'pd' => true, 'palermo' => true, 'pa' => true, 'parma' => true, 'pr' => true, 'pavia' => true, 'pv' => true, 'perugia' => true, 'pg' => true, 'pescara' => true, 'pe' => true, 'pesaro-urbino' => true, 'pesarourbino' => true, 'urbino-pesaro' => true, 'urbinopesaro' => true, 'pu' => true, 'piacenza' => true, 'pc' => true, 'pisa' => true, 'pi' => true, 'pistoia' => true, 'pt' => true, 'pordenone' => true, 'pn' => true, 'potenza' => true, 'pz' => true, 'prato' => true, 'po' => true, 'ragusa' => true, 'rg' => true, 'ravenna' => true, 'ra' => true, 'reggio-calabria' => true, 'reggiocalabria' => true, 'rc' => true, 'reggio-emilia' => true, 'reggioemilia' => true, 're' => true, 'rieti' => true, 'ri' => true, 'rimini' => true, 'rn' => true, 'roma' => true, 'rome' => true, 'rm' => true, 'rovigo' => true, 'ro' => true, 'salerno' => true, 'sa' => true, 'sassari' => true, 'ss' => true, 'savona' => true, 'sv' => true, 'siena' => true, 'si' => true, 'siracusa' => true, 'sr' => true, 'sondrio' => true, 'so' => true, 'taranto' => true, 'ta' => true, 'tempio-olbia' => true, 'tempioolbia' => true, 'olbia-tempio' => true, 'olbiatempio' => true, 'ot' => true, 'teramo' => true, 'te' => true, 'terni' => true, 'tr' => true, 'torino' => true, 'turin' => true, 'to' => true, 'trapani' => true, 'tp' => true, 'trento' => true, 'trentino' => true, 'tn' => true, 'treviso' => true, 'tv' => true, 'trieste' => true, 'ts' => true, 'udine' => true, 'ud' => true, 'varese' => true, 'va' => true, 'venezia' => true, 'venice' => true, 've' => true, 'verbania' => true, 'vb' => true, 'vercelli' => true, 'vc' => true, 'verona' => true, 'vr' => true, 'vibo-valentia' => true, 'vibovalentia' => true, 'vv' => true, 'vicenza' => true, 'vi' => true, 'viterbo' => true, 'vt' => true, 'blogspot' => true ), 'je' => array( 'co' => true, 'net' => true, 'org' => true ), 'jm' => array( '*' => true ), 'jo' => array( 'com' => true, 'org' => true, 'net' => true, 'edu' => true, 'sch' => true, 'gov' => true, 'mil' => true, 'name' => true ), 'jobs' => true, 'jp' => array( 'ac' => true, 'ad' => true, 'co' => true, 'ed' => true, 'go' => true, 'gr' => true, 'lg' => true, 'ne' => true, 'or' => true, 'aichi' => array( 'aisai' => true, 'ama' => true, 'anjo' => true, 'asuke' => true, 'chiryu' => true, 'chita' => true, 'fuso' => true, 'gamagori' => true, 'handa' => true, 'hazu' => true, 'hekinan' => true, 'higashiura' => true, 'ichinomiya' => true, 'inazawa' => true, 'inuyama' => true, 'isshiki' => true, 'iwakura' => true, 'kanie' => true, 'kariya' => true, 'kasugai' => true, 'kira' => true, 'kiyosu' => true, 'komaki' => true, 'konan' => true, 'kota' => true, 'mihama' => true, 'miyoshi' => true, 'nagakute' => true, 'nishio' => true, 'nisshin' => true, 'obu' => true, 'oguchi' => true, 'oharu' => true, 'okazaki' => true, 'owariasahi' => true, 'seto' => true, 'shikatsu' => true, 'shinshiro' => true, 'shitara' => true, 'tahara' => true, 'takahama' => true, 'tobishima' => true, 'toei' => true, 'togo' => true, 'tokai' => true, 'tokoname' => true, 'toyoake' => true, 'toyohashi' => true, 'toyokawa' => true, 'toyone' => true, 'toyota' => true, 'tsushima' => true, 'yatomi' => true ), 'akita' => array( 'akita' => true, 'daisen' => true, 'fujisato' => true, 'gojome' => true, 'hachirogata' => true, 'happou' => true, 'higashinaruse' => true, 'honjo' => true, 'honjyo' => true, 'ikawa' => true, 'kamikoani' => true, 'kamioka' => true, 'katagami' => true, 'kazuno' => true, 'kitaakita' => true, 'kosaka' => true, 'kyowa' => true, 'misato' => true, 'mitane' => true, 'moriyoshi' => true, 'nikaho' => true, 'noshiro' => true, 'odate' => true, 'oga' => true, 'ogata' => true, 'semboku' => true, 'yokote' => true, 'yurihonjo' => true ), 'aomori' => array( 'aomori' => true, 'gonohe' => true, 'hachinohe' => true, 'hashikami' => true, 'hiranai' => true, 'hirosaki' => true, 'itayanagi' => true, 'kuroishi' => true, 'misawa' => true, 'mutsu' => true, 'nakadomari' => true, 'noheji' => true, 'oirase' => true, 'owani' => true, 'rokunohe' => true, 'sannohe' => true, 'shichinohe' => true, 'shingo' => true, 'takko' => true, 'towada' => true, 'tsugaru' => true, 'tsuruta' => true ), 'chiba' => array( 'abiko' => true, 'asahi' => true, 'chonan' => true, 'chosei' => true, 'choshi' => true, 'chuo' => true, 'funabashi' => true, 'futtsu' => true, 'hanamigawa' => true, 'ichihara' => true, 'ichikawa' => true, 'ichinomiya' => true, 'inzai' => true, 'isumi' => true, 'kamagaya' => true, 'kamogawa' => true, 'kashiwa' => true, 'katori' => true, 'katsuura' => true, 'kimitsu' => true, 'kisarazu' => true, 'kozaki' => true, 'kujukuri' => true, 'kyonan' => true, 'matsudo' => true, 'midori' => true, 'mihama' => true, 'minamiboso' => true, 'mobara' => true, 'mutsuzawa' => true, 'nagara' => true, 'nagareyama' => true, 'narashino' => true, 'narita' => true, 'noda' => true, 'oamishirasato' => true, 'omigawa' => true, 'onjuku' => true, 'otaki' => true, 'sakae' => true, 'sakura' => true, 'shimofusa' => true, 'shirako' => true, 'shiroi' => true, 'shisui' => true, 'sodegaura' => true, 'sosa' => true, 'tako' => true, 'tateyama' => true, 'togane' => true, 'tohnosho' => true, 'tomisato' => true, 'urayasu' => true, 'yachimata' => true, 'yachiyo' => true, 'yokaichiba' => true, 'yokoshibahikari' => true, 'yotsukaido' => true ), 'ehime' => array( 'ainan' => true, 'honai' => true, 'ikata' => true, 'imabari' => true, 'iyo' => true, 'kamijima' => true, 'kihoku' => true, 'kumakogen' => true, 'masaki' => true, 'matsuno' => true, 'matsuyama' => true, 'namikata' => true, 'niihama' => true, 'ozu' => true, 'saijo' => true, 'seiyo' => true, 'shikokuchuo' => true, 'tobe' => true, 'toon' => true, 'uchiko' => true, 'uwajima' => true, 'yawatahama' => true ), 'fukui' => array( 'echizen' => true, 'eiheiji' => true, 'fukui' => true, 'ikeda' => true, 'katsuyama' => true, 'mihama' => true, 'minamiechizen' => true, 'obama' => true, 'ohi' => true, 'ono' => true, 'sabae' => true, 'sakai' => true, 'takahama' => true, 'tsuruga' => true, 'wakasa' => true ), 'fukuoka' => array( 'ashiya' => true, 'buzen' => true, 'chikugo' => true, 'chikuho' => true, 'chikujo' => true, 'chikushino' => true, 'chikuzen' => true, 'chuo' => true, 'dazaifu' => true, 'fukuchi' => true, 'hakata' => true, 'higashi' => true, 'hirokawa' => true, 'hisayama' => true, 'iizuka' => true, 'inatsuki' => true, 'kaho' => true, 'kasuga' => true, 'kasuya' => true, 'kawara' => true, 'keisen' => true, 'koga' => true, 'kurate' => true, 'kurogi' => true, 'kurume' => true, 'minami' => true, 'miyako' => true, 'miyama' => true, 'miyawaka' => true, 'mizumaki' => true, 'munakata' => true, 'nakagawa' => true, 'nakama' => true, 'nishi' => true, 'nogata' => true, 'ogori' => true, 'okagaki' => true, 'okawa' => true, 'oki' => true, 'omuta' => true, 'onga' => true, 'onojo' => true, 'oto' => true, 'saigawa' => true, 'sasaguri' => true, 'shingu' => true, 'shinyoshitomi' => true, 'shonai' => true, 'soeda' => true, 'sue' => true, 'tachiarai' => true, 'tagawa' => true, 'takata' => true, 'toho' => true, 'toyotsu' => true, 'tsuiki' => true, 'ukiha' => true, 'umi' => true, 'usui' => true, 'yamada' => true, 'yame' => true, 'yanagawa' => true, 'yukuhashi' => true ), 'fukushima' => array( 'aizubange' => true, 'aizumisato' => true, 'aizuwakamatsu' => true, 'asakawa' => true, 'bandai' => true, 'date' => true, 'fukushima' => true, 'furudono' => true, 'futaba' => true, 'hanawa' => true, 'higashi' => true, 'hirata' => true, 'hirono' => true, 'iitate' => true, 'inawashiro' => true, 'ishikawa' => true, 'iwaki' => true, 'izumizaki' => true, 'kagamiishi' => true, 'kaneyama' => true, 'kawamata' => true, 'kitakata' => true, 'kitashiobara' => true, 'koori' => true, 'koriyama' => true, 'kunimi' => true, 'miharu' => true, 'mishima' => true, 'namie' => true, 'nango' => true, 'nishiaizu' => true, 'nishigo' => true, 'okuma' => true, 'omotego' => true, 'ono' => true, 'otama' => true, 'samegawa' => true, 'shimogo' => true, 'shirakawa' => true, 'showa' => true, 'soma' => true, 'sukagawa' => true, 'taishin' => true, 'tamakawa' => true, 'tanagura' => true, 'tenei' => true, 'yabuki' => true, 'yamato' => true, 'yamatsuri' => true, 'yanaizu' => true, 'yugawa' => true ), 'gifu' => array( 'anpachi' => true, 'ena' => true, 'gifu' => true, 'ginan' => true, 'godo' => true, 'gujo' => true, 'hashima' => true, 'hichiso' => true, 'hida' => true, 'higashishirakawa' => true, 'ibigawa' => true, 'ikeda' => true, 'kakamigahara' => true, 'kani' => true, 'kasahara' => true, 'kasamatsu' => true, 'kawaue' => true, 'kitagata' => true, 'mino' => true, 'minokamo' => true, 'mitake' => true, 'mizunami' => true, 'motosu' => true, 'nakatsugawa' => true, 'ogaki' => true, 'sakahogi' => true, 'seki' => true, 'sekigahara' => true, 'shirakawa' => true, 'tajimi' => true, 'takayama' => true, 'tarui' => true, 'toki' => true, 'tomika' => true, 'wanouchi' => true, 'yamagata' => true, 'yaotsu' => true, 'yoro' => true ), 'gunma' => array( 'annaka' => true, 'chiyoda' => true, 'fujioka' => true, 'higashiagatsuma' => true, 'isesaki' => true, 'itakura' => true, 'kanna' => true, 'kanra' => true, 'katashina' => true, 'kawaba' => true, 'kiryu' => true, 'kusatsu' => true, 'maebashi' => true, 'meiwa' => true, 'midori' => true, 'minakami' => true, 'naganohara' => true, 'nakanojo' => true, 'nanmoku' => true, 'numata' => true, 'oizumi' => true, 'ora' => true, 'ota' => true, 'shibukawa' => true, 'shimonita' => true, 'shinto' => true, 'showa' => true, 'takasaki' => true, 'takayama' => true, 'tamamura' => true, 'tatebayashi' => true, 'tomioka' => true, 'tsukiyono' => true, 'tsumagoi' => true, 'ueno' => true, 'yoshioka' => true ), 'hiroshima' => array( 'asaminami' => true, 'daiwa' => true, 'etajima' => true, 'fuchu' => true, 'fukuyama' => true, 'hatsukaichi' => true, 'higashihiroshima' => true, 'hongo' => true, 'jinsekikogen' => true, 'kaita' => true, 'kui' => true, 'kumano' => true, 'kure' => true, 'mihara' => true, 'miyoshi' => true, 'naka' => true, 'onomichi' => true, 'osakikamijima' => true, 'otake' => true, 'saka' => true, 'sera' => true, 'seranishi' => true, 'shinichi' => true, 'shobara' => true, 'takehara' => true ), 'hokkaido' => array( 'abashiri' => true, 'abira' => true, 'aibetsu' => true, 'akabira' => true, 'akkeshi' => true, 'asahikawa' => true, 'ashibetsu' => true, 'ashoro' => true, 'assabu' => true, 'atsuma' => true, 'bibai' => true, 'biei' => true, 'bifuka' => true, 'bihoro' => true, 'biratori' => true, 'chippubetsu' => true, 'chitose' => true, 'date' => true, 'ebetsu' => true, 'embetsu' => true, 'eniwa' => true, 'erimo' => true, 'esan' => true, 'esashi' => true, 'fukagawa' => true, 'fukushima' => true, 'furano' => true, 'furubira' => true, 'haboro' => true, 'hakodate' => true, 'hamatonbetsu' => true, 'hidaka' => true, 'higashikagura' => true, 'higashikawa' => true, 'hiroo' => true, 'hokuryu' => true, 'hokuto' => true, 'honbetsu' => true, 'horokanai' => true, 'horonobe' => true, 'ikeda' => true, 'imakane' => true, 'ishikari' => true, 'iwamizawa' => true, 'iwanai' => true, 'kamifurano' => true, 'kamikawa' => true, 'kamishihoro' => true, 'kamisunagawa' => true, 'kamoenai' => true, 'kayabe' => true, 'kembuchi' => true, 'kikonai' => true, 'kimobetsu' => true, 'kitahiroshima' => true, 'kitami' => true, 'kiyosato' => true, 'koshimizu' => true, 'kunneppu' => true, 'kuriyama' => true, 'kuromatsunai' => true, 'kushiro' => true, 'kutchan' => true, 'kyowa' => true, 'mashike' => true, 'matsumae' => true, 'mikasa' => true, 'minamifurano' => true, 'mombetsu' => true, 'moseushi' => true, 'mukawa' => true, 'muroran' => true, 'naie' => true, 'nakagawa' => true, 'nakasatsunai' => true, 'nakatombetsu' => true, 'nanae' => true, 'nanporo' => true, 'nayoro' => true, 'nemuro' => true, 'niikappu' => true, 'niki' => true, 'nishiokoppe' => true, 'noboribetsu' => true, 'numata' => true, 'obihiro' => true, 'obira' => true, 'oketo' => true, 'okoppe' => true, 'otaru' => true, 'otobe' => true, 'otofuke' => true, 'otoineppu' => true, 'oumu' => true, 'ozora' => true, 'pippu' => true, 'rankoshi' => true, 'rebun' => true, 'rikubetsu' => true, 'rishiri' => true, 'rishirifuji' => true, 'saroma' => true, 'sarufutsu' => true, 'shakotan' => true, 'shari' => true, 'shibecha' => true, 'shibetsu' => true, 'shikabe' => true, 'shikaoi' => true, 'shimamaki' => true, 'shimizu' => true, 'shimokawa' => true, 'shinshinotsu' => true, 'shintoku' => true, 'shiranuka' => true, 'shiraoi' => true, 'shiriuchi' => true, 'sobetsu' => true, 'sunagawa' => true, 'taiki' => true, 'takasu' => true, 'takikawa' => true, 'takinoue' => true, 'teshikaga' => true, 'tobetsu' => true, 'tohma' => true, 'tomakomai' => true, 'tomari' => true, 'toya' => true, 'toyako' => true, 'toyotomi' => true, 'toyoura' => true, 'tsubetsu' => true, 'tsukigata' => true, 'urakawa' => true, 'urausu' => true, 'uryu' => true, 'utashinai' => true, 'wakkanai' => true, 'wassamu' => true, 'yakumo' => true, 'yoichi' => true ), 'hyogo' => array( 'aioi' => true, 'akashi' => true, 'ako' => true, 'amagasaki' => true, 'aogaki' => true, 'asago' => true, 'ashiya' => true, 'awaji' => true, 'fukusaki' => true, 'goshiki' => true, 'harima' => true, 'himeji' => true, 'ichikawa' => true, 'inagawa' => true, 'itami' => true, 'kakogawa' => true, 'kamigori' => true, 'kamikawa' => true, 'kasai' => true, 'kasuga' => true, 'kawanishi' => true, 'miki' => true, 'minamiawaji' => true, 'nishinomiya' => true, 'nishiwaki' => true, 'ono' => true, 'sanda' => true, 'sannan' => true, 'sasayama' => true, 'sayo' => true, 'shingu' => true, 'shinonsen' => true, 'shiso' => true, 'sumoto' => true, 'taishi' => true, 'taka' => true, 'takarazuka' => true, 'takasago' => true, 'takino' => true, 'tamba' => true, 'tatsuno' => true, 'toyooka' => true, 'yabu' => true, 'yashiro' => true, 'yoka' => true, 'yokawa' => true ), 'ibaraki' => array( 'ami' => true, 'asahi' => true, 'bando' => true, 'chikusei' => true, 'daigo' => true, 'fujishiro' => true, 'hitachi' => true, 'hitachinaka' => true, 'hitachiomiya' => true, 'hitachiota' => true, 'ibaraki' => true, 'ina' => true, 'inashiki' => true, 'itako' => true, 'iwama' => true, 'joso' => true, 'kamisu' => true, 'kasama' => true, 'kashima' => true, 'kasumigaura' => true, 'koga' => true, 'miho' => true, 'mito' => true, 'moriya' => true, 'naka' => true, 'namegata' => true, 'oarai' => true, 'ogawa' => true, 'omitama' => true, 'ryugasaki' => true, 'sakai' => true, 'sakuragawa' => true, 'shimodate' => true, 'shimotsuma' => true, 'shirosato' => true, 'sowa' => true, 'suifu' => true, 'takahagi' => true, 'tamatsukuri' => true, 'tokai' => true, 'tomobe' => true, 'tone' => true, 'toride' => true, 'tsuchiura' => true, 'tsukuba' => true, 'uchihara' => true, 'ushiku' => true, 'yachiyo' => true, 'yamagata' => true, 'yawara' => true, 'yuki' => true ), 'ishikawa' => array( 'anamizu' => true, 'hakui' => true, 'hakusan' => true, 'kaga' => true, 'kahoku' => true, 'kanazawa' => true, 'kawakita' => true, 'komatsu' => true, 'nakanoto' => true, 'nanao' => true, 'nomi' => true, 'nonoichi' => true, 'noto' => true, 'shika' => true, 'suzu' => true, 'tsubata' => true, 'tsurugi' => true, 'uchinada' => true, 'wajima' => true ), 'iwate' => array( 'fudai' => true, 'fujisawa' => true, 'hanamaki' => true, 'hiraizumi' => true, 'hirono' => true, 'ichinohe' => true, 'ichinoseki' => true, 'iwaizumi' => true, 'iwate' => true, 'joboji' => true, 'kamaishi' => true, 'kanegasaki' => true, 'karumai' => true, 'kawai' => true, 'kitakami' => true, 'kuji' => true, 'kunohe' => true, 'kuzumaki' => true, 'miyako' => true, 'mizusawa' => true, 'morioka' => true, 'ninohe' => true, 'noda' => true, 'ofunato' => true, 'oshu' => true, 'otsuchi' => true, 'rikuzentakata' => true, 'shiwa' => true, 'shizukuishi' => true, 'sumita' => true, 'takizawa' => true, 'tanohata' => true, 'tono' => true, 'yahaba' => true, 'yamada' => true ), 'kagawa' => array( 'ayagawa' => true, 'higashikagawa' => true, 'kanonji' => true, 'kotohira' => true, 'manno' => true, 'marugame' => true, 'mitoyo' => true, 'naoshima' => true, 'sanuki' => true, 'tadotsu' => true, 'takamatsu' => true, 'tonosho' => true, 'uchinomi' => true, 'utazu' => true, 'zentsuji' => true ), 'kagoshima' => array( 'akune' => true, 'amami' => true, 'hioki' => true, 'isa' => true, 'isen' => true, 'izumi' => true, 'kagoshima' => true, 'kanoya' => true, 'kawanabe' => true, 'kinko' => true, 'kouyama' => true, 'makurazaki' => true, 'matsumoto' => true, 'minamitane' => true, 'nakatane' => true, 'nishinoomote' => true, 'satsumasendai' => true, 'soo' => true, 'tarumizu' => true, 'yusui' => true ), 'kanagawa' => array( 'aikawa' => true, 'atsugi' => true, 'ayase' => true, 'chigasaki' => true, 'ebina' => true, 'fujisawa' => true, 'hadano' => true, 'hakone' => true, 'hiratsuka' => true, 'isehara' => true, 'kaisei' => true, 'kamakura' => true, 'kiyokawa' => true, 'matsuda' => true, 'minamiashigara' => true, 'miura' => true, 'nakai' => true, 'ninomiya' => true, 'odawara' => true, 'oi' => true, 'oiso' => true, 'sagamihara' => true, 'samukawa' => true, 'tsukui' => true, 'yamakita' => true, 'yamato' => true, 'yokosuka' => true, 'yugawara' => true, 'zama' => true, 'zushi' => true ), 'kochi' => array( 'aki' => true, 'geisei' => true, 'hidaka' => true, 'higashitsuno' => true, 'ino' => true, 'kagami' => true, 'kami' => true, 'kitagawa' => true, 'kochi' => true, 'mihara' => true, 'motoyama' => true, 'muroto' => true, 'nahari' => true, 'nakamura' => true, 'nankoku' => true, 'nishitosa' => true, 'niyodogawa' => true, 'ochi' => true, 'okawa' => true, 'otoyo' => true, 'otsuki' => true, 'sakawa' => true, 'sukumo' => true, 'susaki' => true, 'tosa' => true, 'tosashimizu' => true, 'toyo' => true, 'tsuno' => true, 'umaji' => true, 'yasuda' => true, 'yusuhara' => true ), 'kumamoto' => array( 'amakusa' => true, 'arao' => true, 'aso' => true, 'choyo' => true, 'gyokuto' => true, 'hitoyoshi' => true, 'kamiamakusa' => true, 'kashima' => true, 'kikuchi' => true, 'kosa' => true, 'kumamoto' => true, 'mashiki' => true, 'mifune' => true, 'minamata' => true, 'minamioguni' => true, 'nagasu' => true, 'nishihara' => true, 'oguni' => true, 'ozu' => true, 'sumoto' => true, 'takamori' => true, 'uki' => true, 'uto' => true, 'yamaga' => true, 'yamato' => true, 'yatsushiro' => true ), 'kyoto' => array( 'ayabe' => true, 'fukuchiyama' => true, 'higashiyama' => true, 'ide' => true, 'ine' => true, 'joyo' => true, 'kameoka' => true, 'kamo' => true, 'kita' => true, 'kizu' => true, 'kumiyama' => true, 'kyotamba' => true, 'kyotanabe' => true, 'kyotango' => true, 'maizuru' => true, 'minami' => true, 'minamiyamashiro' => true, 'miyazu' => true, 'muko' => true, 'nagaokakyo' => true, 'nakagyo' => true, 'nantan' => true, 'oyamazaki' => true, 'sakyo' => true, 'seika' => true, 'tanabe' => true, 'uji' => true, 'ujitawara' => true, 'wazuka' => true, 'yamashina' => true, 'yawata' => true ), 'mie' => array( 'asahi' => true, 'inabe' => true, 'ise' => true, 'kameyama' => true, 'kawagoe' => true, 'kiho' => true, 'kisosaki' => true, 'kiwa' => true, 'komono' => true, 'kumano' => true, 'kuwana' => true, 'matsusaka' => true, 'meiwa' => true, 'mihama' => true, 'minamiise' => true, 'misugi' => true, 'miyama' => true, 'nabari' => true, 'shima' => true, 'suzuka' => true, 'tado' => true, 'taiki' => true, 'taki' => true, 'tamaki' => true, 'toba' => true, 'tsu' => true, 'udono' => true, 'ureshino' => true, 'watarai' => true, 'yokkaichi' => true ), 'miyagi' => array( 'furukawa' => true, 'higashimatsushima' => true, 'ishinomaki' => true, 'iwanuma' => true, 'kakuda' => true, 'kami' => true, 'kawasaki' => true, 'kesennuma' => true, 'marumori' => true, 'matsushima' => true, 'minamisanriku' => true, 'misato' => true, 'murata' => true, 'natori' => true, 'ogawara' => true, 'ohira' => true, 'onagawa' => true, 'osaki' => true, 'rifu' => true, 'semine' => true, 'shibata' => true, 'shichikashuku' => true, 'shikama' => true, 'shiogama' => true, 'shiroishi' => true, 'tagajo' => true, 'taiwa' => true, 'tome' => true, 'tomiya' => true, 'wakuya' => true, 'watari' => true, 'yamamoto' => true, 'zao' => true ), 'miyazaki' => array( 'aya' => true, 'ebino' => true, 'gokase' => true, 'hyuga' => true, 'kadogawa' => true, 'kawaminami' => true, 'kijo' => true, 'kitagawa' => true, 'kitakata' => true, 'kitaura' => true, 'kobayashi' => true, 'kunitomi' => true, 'kushima' => true, 'mimata' => true, 'miyakonojo' => true, 'miyazaki' => true, 'morotsuka' => true, 'nichinan' => true, 'nishimera' => true, 'nobeoka' => true, 'saito' => true, 'shiiba' => true, 'shintomi' => true, 'takaharu' => true, 'takanabe' => true, 'takazaki' => true, 'tsuno' => true ), 'nagano' => array( 'achi' => true, 'agematsu' => true, 'anan' => true, 'aoki' => true, 'asahi' => true, 'azumino' => true, 'chikuhoku' => true, 'chikuma' => true, 'chino' => true, 'fujimi' => true, 'hakuba' => true, 'hara' => true, 'hiraya' => true, 'iida' => true, 'iijima' => true, 'iiyama' => true, 'iizuna' => true, 'ikeda' => true, 'ikusaka' => true, 'ina' => true, 'karuizawa' => true, 'kawakami' => true, 'kiso' => true, 'kisofukushima' => true, 'kitaaiki' => true, 'komagane' => true, 'komoro' => true, 'matsukawa' => true, 'matsumoto' => true, 'miasa' => true, 'minamiaiki' => true, 'minamimaki' => true, 'minamiminowa' => true, 'minowa' => true, 'miyada' => true, 'miyota' => true, 'mochizuki' => true, 'nagano' => true, 'nagawa' => true, 'nagiso' => true, 'nakagawa' => true, 'nakano' => true, 'nozawaonsen' => true, 'obuse' => true, 'ogawa' => true, 'okaya' => true, 'omachi' => true, 'omi' => true, 'ookuwa' => true, 'ooshika' => true, 'otaki' => true, 'otari' => true, 'sakae' => true, 'sakaki' => true, 'saku' => true, 'sakuho' => true, 'shimosuwa' => true, 'shinanomachi' => true, 'shiojiri' => true, 'suwa' => true, 'suzaka' => true, 'takagi' => true, 'takamori' => true, 'takayama' => true, 'tateshina' => true, 'tatsuno' => true, 'togakushi' => true, 'togura' => true, 'tomi' => true, 'ueda' => true, 'wada' => true, 'yamagata' => true, 'yamanouchi' => true, 'yasaka' => true, 'yasuoka' => true ), 'nagasaki' => array( 'chijiwa' => true, 'futsu' => true, 'goto' => true, 'hasami' => true, 'hirado' => true, 'iki' => true, 'isahaya' => true, 'kawatana' => true, 'kuchinotsu' => true, 'matsuura' => true, 'nagasaki' => true, 'obama' => true, 'omura' => true, 'oseto' => true, 'saikai' => true, 'sasebo' => true, 'seihi' => true, 'shimabara' => true, 'shinkamigoto' => true, 'togitsu' => true, 'tsushima' => true, 'unzen' => true ), 'nara' => array( 'ando' => true, 'gose' => true, 'heguri' => true, 'higashiyoshino' => true, 'ikaruga' => true, 'ikoma' => true, 'kamikitayama' => true, 'kanmaki' => true, 'kashiba' => true, 'kashihara' => true, 'katsuragi' => true, 'kawai' => true, 'kawakami' => true, 'kawanishi' => true, 'koryo' => true, 'kurotaki' => true, 'mitsue' => true, 'miyake' => true, 'nara' => true, 'nosegawa' => true, 'oji' => true, 'ouda' => true, 'oyodo' => true, 'sakurai' => true, 'sango' => true, 'shimoichi' => true, 'shimokitayama' => true, 'shinjo' => true, 'soni' => true, 'takatori' => true, 'tawaramoto' => true, 'tenkawa' => true, 'tenri' => true, 'uda' => true, 'yamatokoriyama' => true, 'yamatotakada' => true, 'yamazoe' => true, 'yoshino' => true ), 'niigata' => array( 'aga' => true, 'agano' => true, 'gosen' => true, 'itoigawa' => true, 'izumozaki' => true, 'joetsu' => true, 'kamo' => true, 'kariwa' => true, 'kashiwazaki' => true, 'minamiuonuma' => true, 'mitsuke' => true, 'muika' => true, 'murakami' => true, 'myoko' => true, 'nagaoka' => true, 'niigata' => true, 'ojiya' => true, 'omi' => true, 'sado' => true, 'sanjo' => true, 'seiro' => true, 'seirou' => true, 'sekikawa' => true, 'shibata' => true, 'tagami' => true, 'tainai' => true, 'tochio' => true, 'tokamachi' => true, 'tsubame' => true, 'tsunan' => true, 'uonuma' => true, 'yahiko' => true, 'yoita' => true, 'yuzawa' => true ), 'oita' => array( 'beppu' => true, 'bungoono' => true, 'bungotakada' => true, 'hasama' => true, 'hiji' => true, 'himeshima' => true, 'hita' => true, 'kamitsue' => true, 'kokonoe' => true, 'kuju' => true, 'kunisaki' => true, 'kusu' => true, 'oita' => true, 'saiki' => true, 'taketa' => true, 'tsukumi' => true, 'usa' => true, 'usuki' => true, 'yufu' => true ), 'okayama' => array( 'akaiwa' => true, 'asakuchi' => true, 'bizen' => true, 'hayashima' => true, 'ibara' => true, 'kagamino' => true, 'kasaoka' => true, 'kibichuo' => true, 'kumenan' => true, 'kurashiki' => true, 'maniwa' => true, 'misaki' => true, 'nagi' => true, 'niimi' => true, 'nishiawakura' => true, 'okayama' => true, 'satosho' => true, 'setouchi' => true, 'shinjo' => true, 'shoo' => true, 'soja' => true, 'takahashi' => true, 'tamano' => true, 'tsuyama' => true, 'wake' => true, 'yakage' => true ), 'okinawa' => array( 'aguni' => true, 'ginowan' => true, 'ginoza' => true, 'gushikami' => true, 'haebaru' => true, 'higashi' => true, 'hirara' => true, 'iheya' => true, 'ishigaki' => true, 'ishikawa' => true, 'itoman' => true, 'izena' => true, 'kadena' => true, 'kin' => true, 'kitadaito' => true, 'kitanakagusuku' => true, 'kumejima' => true, 'kunigami' => true, 'minamidaito' => true, 'motobu' => true, 'nago' => true, 'naha' => true, 'nakagusuku' => true, 'nakijin' => true, 'nanjo' => true, 'nishihara' => true, 'ogimi' => true, 'okinawa' => true, 'onna' => true, 'shimoji' => true, 'taketomi' => true, 'tarama' => true, 'tokashiki' => true, 'tomigusuku' => true, 'tonaki' => true, 'urasoe' => true, 'uruma' => true, 'yaese' => true, 'yomitan' => true, 'yonabaru' => true, 'yonaguni' => true, 'zamami' => true ), 'osaka' => array( 'abeno' => true, 'chihayaakasaka' => true, 'chuo' => true, 'daito' => true, 'fujiidera' => true, 'habikino' => true, 'hannan' => true, 'higashiosaka' => true, 'higashisumiyoshi' => true, 'higashiyodogawa' => true, 'hirakata' => true, 'ibaraki' => true, 'ikeda' => true, 'izumi' => true, 'izumiotsu' => true, 'izumisano' => true, 'kadoma' => true, 'kaizuka' => true, 'kanan' => true, 'kashiwara' => true, 'katano' => true, 'kawachinagano' => true, 'kishiwada' => true, 'kita' => true, 'kumatori' => true, 'matsubara' => true, 'minato' => true, 'minoh' => true, 'misaki' => true, 'moriguchi' => true, 'neyagawa' => true, 'nishi' => true, 'nose' => true, 'osakasayama' => true, 'sakai' => true, 'sayama' => true, 'sennan' => true, 'settsu' => true, 'shijonawate' => true, 'shimamoto' => true, 'suita' => true, 'tadaoka' => true, 'taishi' => true, 'tajiri' => true, 'takaishi' => true, 'takatsuki' => true, 'tondabayashi' => true, 'toyonaka' => true, 'toyono' => true, 'yao' => true ), 'saga' => array( 'ariake' => true, 'arita' => true, 'fukudomi' => true, 'genkai' => true, 'hamatama' => true, 'hizen' => true, 'imari' => true, 'kamimine' => true, 'kanzaki' => true, 'karatsu' => true, 'kashima' => true, 'kitagata' => true, 'kitahata' => true, 'kiyama' => true, 'kouhoku' => true, 'kyuragi' => true, 'nishiarita' => true, 'ogi' => true, 'omachi' => true, 'ouchi' => true, 'saga' => true, 'shiroishi' => true, 'taku' => true, 'tara' => true, 'tosu' => true, 'yoshinogari' => true ), 'saitama' => array( 'arakawa' => true, 'asaka' => true, 'chichibu' => true, 'fujimi' => true, 'fujimino' => true, 'fukaya' => true, 'hanno' => true, 'hanyu' => true, 'hasuda' => true, 'hatogaya' => true, 'hatoyama' => true, 'hidaka' => true, 'higashichichibu' => true, 'higashimatsuyama' => true, 'honjo' => true, 'ina' => true, 'iruma' => true, 'iwatsuki' => true, 'kamiizumi' => true, 'kamikawa' => true, 'kamisato' => true, 'kasukabe' => true, 'kawagoe' => true, 'kawaguchi' => true, 'kawajima' => true, 'kazo' => true, 'kitamoto' => true, 'koshigaya' => true, 'kounosu' => true, 'kuki' => true, 'kumagaya' => true, 'matsubushi' => true, 'minano' => true, 'misato' => true, 'miyashiro' => true, 'miyoshi' => true, 'moroyama' => true, 'nagatoro' => true, 'namegawa' => true, 'niiza' => true, 'ogano' => true, 'ogawa' => true, 'ogose' => true, 'okegawa' => true, 'omiya' => true, 'otaki' => true, 'ranzan' => true, 'ryokami' => true, 'saitama' => true, 'sakado' => true, 'satte' => true, 'sayama' => true, 'shiki' => true, 'shiraoka' => true, 'soka' => true, 'sugito' => true, 'toda' => true, 'tokigawa' => true, 'tokorozawa' => true, 'tsurugashima' => true, 'urawa' => true, 'warabi' => true, 'yashio' => true, 'yokoze' => true, 'yono' => true, 'yorii' => true, 'yoshida' => true, 'yoshikawa' => true, 'yoshimi' => true ), 'shiga' => array( 'aisho' => true, 'gamo' => true, 'higashiomi' => true, 'hikone' => true, 'koka' => true, 'konan' => true, 'kosei' => true, 'koto' => true, 'kusatsu' => true, 'maibara' => true, 'moriyama' => true, 'nagahama' => true, 'nishiazai' => true, 'notogawa' => true, 'omihachiman' => true, 'otsu' => true, 'ritto' => true, 'ryuoh' => true, 'takashima' => true, 'takatsuki' => true, 'torahime' => true, 'toyosato' => true, 'yasu' => true ), 'shimane' => array( 'akagi' => true, 'ama' => true, 'gotsu' => true, 'hamada' => true, 'higashiizumo' => true, 'hikawa' => true, 'hikimi' => true, 'izumo' => true, 'kakinoki' => true, 'masuda' => true, 'matsue' => true, 'misato' => true, 'nishinoshima' => true, 'ohda' => true, 'okinoshima' => true, 'okuizumo' => true, 'shimane' => true, 'tamayu' => true, 'tsuwano' => true, 'unnan' => true, 'yakumo' => true, 'yasugi' => true, 'yatsuka' => true ), 'shizuoka' => array( 'arai' => true, 'atami' => true, 'fuji' => true, 'fujieda' => true, 'fujikawa' => true, 'fujinomiya' => true, 'fukuroi' => true, 'gotemba' => true, 'haibara' => true, 'hamamatsu' => true, 'higashiizu' => true, 'ito' => true, 'iwata' => true, 'izu' => true, 'izunokuni' => true, 'kakegawa' => true, 'kannami' => true, 'kawanehon' => true, 'kawazu' => true, 'kikugawa' => true, 'kosai' => true, 'makinohara' => true, 'matsuzaki' => true, 'minamiizu' => true, 'mishima' => true, 'morimachi' => true, 'nishiizu' => true, 'numazu' => true, 'omaezaki' => true, 'shimada' => true, 'shimizu' => true, 'shimoda' => true, 'shizuoka' => true, 'susono' => true, 'yaizu' => true, 'yoshida' => true ), 'tochigi' => array( 'ashikaga' => true, 'bato' => true, 'haga' => true, 'ichikai' => true, 'iwafune' => true, 'kaminokawa' => true, 'kanuma' => true, 'karasuyama' => true, 'kuroiso' => true, 'mashiko' => true, 'mibu' => true, 'moka' => true, 'motegi' => true, 'nasu' => true, 'nasushiobara' => true, 'nikko' => true, 'nishikata' => true, 'nogi' => true, 'ohira' => true, 'ohtawara' => true, 'oyama' => true, 'sakura' => true, 'sano' => true, 'shimotsuke' => true, 'shioya' => true, 'takanezawa' => true, 'tochigi' => true, 'tsuga' => true, 'ujiie' => true, 'utsunomiya' => true, 'yaita' => true ), 'tokushima' => array( 'aizumi' => true, 'anan' => true, 'ichiba' => true, 'itano' => true, 'kainan' => true, 'komatsushima' => true, 'matsushige' => true, 'mima' => true, 'minami' => true, 'miyoshi' => true, 'mugi' => true, 'nakagawa' => true, 'naruto' => true, 'sanagochi' => true, 'shishikui' => true, 'tokushima' => true, 'wajiki' => true ), 'tokyo' => array( 'adachi' => true, 'akiruno' => true, 'akishima' => true, 'aogashima' => true, 'arakawa' => true, 'bunkyo' => true, 'chiyoda' => true, 'chofu' => true, 'chuo' => true, 'edogawa' => true, 'fuchu' => true, 'fussa' => true, 'hachijo' => true, 'hachioji' => true, 'hamura' => true, 'higashikurume' => true, 'higashimurayama' => true, 'higashiyamato' => true, 'hino' => true, 'hinode' => true, 'hinohara' => true, 'inagi' => true, 'itabashi' => true, 'katsushika' => true, 'kita' => true, 'kiyose' => true, 'kodaira' => true, 'koganei' => true, 'kokubunji' => true, 'komae' => true, 'koto' => true, 'kouzushima' => true, 'kunitachi' => true, 'machida' => true, 'meguro' => true, 'minato' => true, 'mitaka' => true, 'mizuho' => true, 'musashimurayama' => true, 'musashino' => true, 'nakano' => true, 'nerima' => true, 'ogasawara' => true, 'okutama' => true, 'ome' => true, 'oshima' => true, 'ota' => true, 'setagaya' => true, 'shibuya' => true, 'shinagawa' => true, 'shinjuku' => true, 'suginami' => true, 'sumida' => true, 'tachikawa' => true, 'taito' => true, 'tama' => true, 'toshima' => true ), 'tottori' => array( 'chizu' => true, 'hino' => true, 'kawahara' => true, 'koge' => true, 'kotoura' => true, 'misasa' => true, 'nanbu' => true, 'nichinan' => true, 'sakaiminato' => true, 'tottori' => true, 'wakasa' => true, 'yazu' => true, 'yonago' => true ), 'toyama' => array( 'asahi' => true, 'fuchu' => true, 'fukumitsu' => true, 'funahashi' => true, 'himi' => true, 'imizu' => true, 'inami' => true, 'johana' => true, 'kamiichi' => true, 'kurobe' => true, 'nakaniikawa' => true, 'namerikawa' => true, 'nanto' => true, 'nyuzen' => true, 'oyabe' => true, 'taira' => true, 'takaoka' => true, 'tateyama' => true, 'toga' => true, 'tonami' => true, 'toyama' => true, 'unazuki' => true, 'uozu' => true, 'yamada' => true ), 'wakayama' => array( 'arida' => true, 'aridagawa' => true, 'gobo' => true, 'hashimoto' => true, 'hidaka' => true, 'hirogawa' => true, 'inami' => true, 'iwade' => true, 'kainan' => true, 'kamitonda' => true, 'katsuragi' => true, 'kimino' => true, 'kinokawa' => true, 'kitayama' => true, 'koya' => true, 'koza' => true, 'kozagawa' => true, 'kudoyama' => true, 'kushimoto' => true, 'mihama' => true, 'misato' => true, 'nachikatsuura' => true, 'shingu' => true, 'shirahama' => true, 'taiji' => true, 'tanabe' => true, 'wakayama' => true, 'yuasa' => true, 'yura' => true ), 'yamagata' => array( 'asahi' => true, 'funagata' => true, 'higashine' => true, 'iide' => true, 'kahoku' => true, 'kaminoyama' => true, 'kaneyama' => true, 'kawanishi' => true, 'mamurogawa' => true, 'mikawa' => true, 'murayama' => true, 'nagai' => true, 'nakayama' => true, 'nanyo' => true, 'nishikawa' => true, 'obanazawa' => true, 'oe' => true, 'oguni' => true, 'ohkura' => true, 'oishida' => true, 'sagae' => true, 'sakata' => true, 'sakegawa' => true, 'shinjo' => true, 'shirataka' => true, 'shonai' => true, 'takahata' => true, 'tendo' => true, 'tozawa' => true, 'tsuruoka' => true, 'yamagata' => true, 'yamanobe' => true, 'yonezawa' => true, 'yuza' => true ), 'yamaguchi' => array( 'abu' => true, 'hagi' => true, 'hikari' => true, 'hofu' => true, 'iwakuni' => true, 'kudamatsu' => true, 'mitou' => true, 'nagato' => true, 'oshima' => true, 'shimonoseki' => true, 'shunan' => true, 'tabuse' => true, 'tokuyama' => true, 'toyota' => true, 'ube' => true, 'yuu' => true ), 'yamanashi' => array( 'chuo' => true, 'doshi' => true, 'fuefuki' => true, 'fujikawa' => true, 'fujikawaguchiko' => true, 'fujiyoshida' => true, 'hayakawa' => true, 'hokuto' => true, 'ichikawamisato' => true, 'kai' => true, 'kofu' => true, 'koshu' => true, 'kosuge' => true, 'minami-alps' => true, 'minobu' => true, 'nakamichi' => true, 'nanbu' => true, 'narusawa' => true, 'nirasaki' => true, 'nishikatsura' => true, 'oshino' => true, 'otsuki' => true, 'showa' => true, 'tabayama' => true, 'tsuru' => true, 'uenohara' => true, 'yamanakako' => true, 'yamanashi' => true ), 'kawasaki' => array( '*' => true, '!city' => true ), 'kitakyushu' => array( '*' => true, '!city' => true ), 'kobe' => array( '*' => true, '!city' => true ), 'nagoya' => array( '*' => true, '!city' => true ), 'sapporo' => array( '*' => true, '!city' => true ), 'sendai' => array( '*' => true, '!city' => true ), 'yokohama' => array( '*' => true, '!city' => true ), 'blogspot' => true ), 'ke' => array( '*' => true ), 'kg' => array( 'org' => true, 'net' => true, 'com' => true, 'edu' => true, 'gov' => true, 'mil' => true ), 'kh' => array( '*' => true ), 'ki' => array( 'edu' => true, 'biz' => true, 'net' => true, 'org' => true, 'gov' => true, 'info' => true, 'com' => true ), 'km' => array( 'org' => true, 'nom' => true, 'gov' => true, 'prd' => true, 'tm' => true, 'edu' => true, 'mil' => true, 'ass' => true, 'com' => true, 'coop' => true, 'asso' => true, 'presse' => true, 'medecin' => true, 'notaires' => true, 'pharmaciens' => true, 'veterinaire' => true, 'gouv' => true ), 'kn' => array( 'net' => true, 'org' => true, 'edu' => true, 'gov' => true ), 'kp' => array( 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'rep' => true, 'tra' => true ), 'kr' => array( 'ac' => true, 'co' => true, 'es' => true, 'go' => true, 'hs' => true, 'kg' => true, 'mil' => true, 'ms' => true, 'ne' => true, 'or' => true, 'pe' => true, 're' => true, 'sc' => true, 'busan' => true, 'chungbuk' => true, 'chungnam' => true, 'daegu' => true, 'daejeon' => true, 'gangwon' => true, 'gwangju' => true, 'gyeongbuk' => true, 'gyeonggi' => true, 'gyeongnam' => true, 'incheon' => true, 'jeju' => true, 'jeonbuk' => true, 'jeonnam' => true, 'seoul' => true, 'ulsan' => true, 'blogspot' => true ), 'kw' => array( '*' => true ), 'ky' => array( 'edu' => true, 'gov' => true, 'com' => true, 'org' => true, 'net' => true ), 'kz' => array( 'org' => true, 'edu' => true, 'net' => true, 'gov' => true, 'mil' => true, 'com' => true ), 'la' => array( 'int' => true, 'net' => true, 'info' => true, 'edu' => true, 'gov' => true, 'per' => true, 'com' => true, 'org' => true, 'c' => true ), 'lb' => array( 'com' => true, 'edu' => true, 'gov' => true, 'net' => true, 'org' => true ), 'lc' => array( 'com' => true, 'net' => true, 'co' => true, 'org' => true, 'edu' => true, 'gov' => true ), 'li' => true, 'lk' => array( 'gov' => true, 'sch' => true, 'net' => true, 'int' => true, 'com' => true, 'org' => true, 'edu' => true, 'ngo' => true, 'soc' => true, 'web' => true, 'ltd' => true, 'assn' => true, 'grp' => true, 'hotel' => true ), 'lr' => array( 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'net' => true ), 'ls' => array( 'co' => true, 'org' => true ), 'lt' => array( 'gov' => true ), 'lu' => true, 'lv' => array( 'com' => true, 'edu' => true, 'gov' => true, 'org' => true, 'mil' => true, 'id' => true, 'net' => true, 'asn' => true, 'conf' => true ), 'ly' => array( 'com' => true, 'net' => true, 'gov' => true, 'plc' => true, 'edu' => true, 'sch' => true, 'med' => true, 'org' => true, 'id' => true ), 'ma' => array( 'co' => true, 'net' => true, 'gov' => true, 'org' => true, 'ac' => true, 'press' => true ), 'mc' => array( 'tm' => true, 'asso' => true ), 'md' => true, 'me' => array( 'co' => true, 'net' => true, 'org' => true, 'edu' => true, 'ac' => true, 'gov' => true, 'its' => true, 'priv' => true ), 'mg' => array( 'org' => true, 'nom' => true, 'gov' => true, 'prd' => true, 'tm' => true, 'edu' => true, 'mil' => true, 'com' => true ), 'mh' => true, 'mil' => true, 'mk' => array( 'com' => true, 'org' => true, 'net' => true, 'edu' => true, 'gov' => true, 'inf' => true, 'name' => true ), 'ml' => array( 'com' => true, 'edu' => true, 'gouv' => true, 'gov' => true, 'net' => true, 'org' => true, 'presse' => true ), 'mm' => array( '*' => true ), 'mn' => array( 'gov' => true, 'edu' => true, 'org' => true, 'nyc' => true ), 'mo' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true, 'gov' => true ), 'mobi' => true, 'mp' => true, 'mq' => true, 'mr' => array( 'gov' => true, 'blogspot' => true ), 'ms' => true, 'mt' => array( 'com' => true, 'edu' => true, 'net' => true, 'org' => true ), 'mu' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'ac' => true, 'co' => true, 'or' => true ), 'museum' => array( 'academy' => true, 'agriculture' => true, 'air' => true, 'airguard' => true, 'alabama' => true, 'alaska' => true, 'amber' => true, 'ambulance' => true, 'american' => true, 'americana' => true, 'americanantiques' => true, 'americanart' => true, 'amsterdam' => true, 'and' => true, 'annefrank' => true, 'anthro' => true, 'anthropology' => true, 'antiques' => true, 'aquarium' => true, 'arboretum' => true, 'archaeological' => true, 'archaeology' => true, 'architecture' => true, 'art' => true, 'artanddesign' => true, 'artcenter' => true, 'artdeco' => true, 'arteducation' => true, 'artgallery' => true, 'arts' => true, 'artsandcrafts' => true, 'asmatart' => true, 'assassination' => true, 'assisi' => true, 'association' => true, 'astronomy' => true, 'atlanta' => true, 'austin' => true, 'australia' => true, 'automotive' => true, 'aviation' => true, 'axis' => true, 'badajoz' => true, 'baghdad' => true, 'bahn' => true, 'bale' => true, 'baltimore' => true, 'barcelona' => true, 'baseball' => true, 'basel' => true, 'baths' => true, 'bauern' => true, 'beauxarts' => true, 'beeldengeluid' => true, 'bellevue' => true, 'bergbau' => true, 'berkeley' => true, 'berlin' => true, 'bern' => true, 'bible' => true, 'bilbao' => true, 'bill' => true, 'birdart' => true, 'birthplace' => true, 'bonn' => true, 'boston' => true, 'botanical' => true, 'botanicalgarden' => true, 'botanicgarden' => true, 'botany' => true, 'brandywinevalley' => true, 'brasil' => true, 'bristol' => true, 'british' => true, 'britishcolumbia' => true, 'broadcast' => true, 'brunel' => true, 'brussel' => true, 'brussels' => true, 'bruxelles' => true, 'building' => true, 'burghof' => true, 'bus' => true, 'bushey' => true, 'cadaques' => true, 'california' => true, 'cambridge' => true, 'can' => true, 'canada' => true, 'capebreton' => true, 'carrier' => true, 'cartoonart' => true, 'casadelamoneda' => true, 'castle' => true, 'castres' => true, 'celtic' => true, 'center' => true, 'chattanooga' => true, 'cheltenham' => true, 'chesapeakebay' => true, 'chicago' => true, 'children' => true, 'childrens' => true, 'childrensgarden' => true, 'chiropractic' => true, 'chocolate' => true, 'christiansburg' => true, 'cincinnati' => true, 'cinema' => true, 'circus' => true, 'civilisation' => true, 'civilization' => true, 'civilwar' => true, 'clinton' => true, 'clock' => true, 'coal' => true, 'coastaldefence' => true, 'cody' => true, 'coldwar' => true, 'collection' => true, 'colonialwilliamsburg' => true, 'coloradoplateau' => true, 'columbia' => true, 'columbus' => true, 'communication' => true, 'communications' => true, 'community' => true, 'computer' => true, 'computerhistory' => true, 'comunicações' => true, 'contemporary' => true, 'contemporaryart' => true, 'convent' => true, 'copenhagen' => true, 'corporation' => true, 'correios-e-telecomunicações' => true, 'corvette' => true, 'costume' => true, 'countryestate' => true, 'county' => true, 'crafts' => true, 'cranbrook' => true, 'creation' => true, 'cultural' => true, 'culturalcenter' => true, 'culture' => true, 'cyber' => true, 'cymru' => true, 'dali' => true, 'dallas' => true, 'database' => true, 'ddr' => true, 'decorativearts' => true, 'delaware' => true, 'delmenhorst' => true, 'denmark' => true, 'depot' => true, 'design' => true, 'detroit' => true, 'dinosaur' => true, 'discovery' => true, 'dolls' => true, 'donostia' => true, 'durham' => true, 'eastafrica' => true, 'eastcoast' => true, 'education' => true, 'educational' => true, 'egyptian' => true, 'eisenbahn' => true, 'elburg' => true, 'elvendrell' => true, 'embroidery' => true, 'encyclopedic' => true, 'england' => true, 'entomology' => true, 'environment' => true, 'environmentalconservation' => true, 'epilepsy' => true, 'essex' => true, 'estate' => true, 'ethnology' => true, 'exeter' => true, 'exhibition' => true, 'family' => true, 'farm' => true, 'farmequipment' => true, 'farmers' => true, 'farmstead' => true, 'field' => true, 'figueres' => true, 'filatelia' => true, 'film' => true, 'fineart' => true, 'finearts' => true, 'finland' => true, 'flanders' => true, 'florida' => true, 'force' => true, 'fortmissoula' => true, 'fortworth' => true, 'foundation' => true, 'francaise' => true, 'frankfurt' => true, 'franziskaner' => true, 'freemasonry' => true, 'freiburg' => true, 'fribourg' => true, 'frog' => true, 'fundacio' => true, 'furniture' => true, 'gallery' => true, 'garden' => true, 'gateway' => true, 'geelvinck' => true, 'gemological' => true, 'geology' => true, 'georgia' => true, 'giessen' => true, 'glas' => true, 'glass' => true, 'gorge' => true, 'grandrapids' => true, 'graz' => true, 'guernsey' => true, 'halloffame' => true, 'hamburg' => true, 'handson' => true, 'harvestcelebration' => true, 'hawaii' => true, 'health' => true, 'heimatunduhren' => true, 'hellas' => true, 'helsinki' => true, 'hembygdsforbund' => true, 'heritage' => true, 'histoire' => true, 'historical' => true, 'historicalsociety' => true, 'historichouses' => true, 'historisch' => true, 'historisches' => true, 'history' => true, 'historyofscience' => true, 'horology' => true, 'house' => true, 'humanities' => true, 'illustration' => true, 'imageandsound' => true, 'indian' => true, 'indiana' => true, 'indianapolis' => true, 'indianmarket' => true, 'intelligence' => true, 'interactive' => true, 'iraq' => true, 'iron' => true, 'isleofman' => true, 'jamison' => true, 'jefferson' => true, 'jerusalem' => true, 'jewelry' => true, 'jewish' => true, 'jewishart' => true, 'jfk' => true, 'journalism' => true, 'judaica' => true, 'judygarland' => true, 'juedisches' => true, 'juif' => true, 'karate' => true, 'karikatur' => true, 'kids' => true, 'koebenhavn' => true, 'koeln' => true, 'kunst' => true, 'kunstsammlung' => true, 'kunstunddesign' => true, 'labor' => true, 'labour' => true, 'lajolla' => true, 'lancashire' => true, 'landes' => true, 'lans' => true, 'läns' => true, 'larsson' => true, 'lewismiller' => true, 'lincoln' => true, 'linz' => true, 'living' => true, 'livinghistory' => true, 'localhistory' => true, 'london' => true, 'losangeles' => true, 'louvre' => true, 'loyalist' => true, 'lucerne' => true, 'luxembourg' => true, 'luzern' => true, 'mad' => true, 'madrid' => true, 'mallorca' => true, 'manchester' => true, 'mansion' => true, 'mansions' => true, 'manx' => true, 'marburg' => true, 'maritime' => true, 'maritimo' => true, 'maryland' => true, 'marylhurst' => true, 'media' => true, 'medical' => true, 'medizinhistorisches' => true, 'meeres' => true, 'memorial' => true, 'mesaverde' => true, 'michigan' => true, 'midatlantic' => true, 'military' => true, 'mill' => true, 'miners' => true, 'mining' => true, 'minnesota' => true, 'missile' => true, 'missoula' => true, 'modern' => true, 'moma' => true, 'money' => true, 'monmouth' => true, 'monticello' => true, 'montreal' => true, 'moscow' => true, 'motorcycle' => true, 'muenchen' => true, 'muenster' => true, 'mulhouse' => true, 'muncie' => true, 'museet' => true, 'museumcenter' => true, 'museumvereniging' => true, 'music' => true, 'national' => true, 'nationalfirearms' => true, 'nationalheritage' => true, 'nativeamerican' => true, 'naturalhistory' => true, 'naturalhistorymuseum' => true, 'naturalsciences' => true, 'nature' => true, 'naturhistorisches' => true, 'natuurwetenschappen' => true, 'naumburg' => true, 'naval' => true, 'nebraska' => true, 'neues' => true, 'newhampshire' => true, 'newjersey' => true, 'newmexico' => true, 'newport' => true, 'newspaper' => true, 'newyork' => true, 'niepce' => true, 'norfolk' => true, 'north' => true, 'nrw' => true, 'nuernberg' => true, 'nuremberg' => true, 'nyc' => true, 'nyny' => true, 'oceanographic' => true, 'oceanographique' => true, 'omaha' => true, 'online' => true, 'ontario' => true, 'openair' => true, 'oregon' => true, 'oregontrail' => true, 'otago' => true, 'oxford' => true, 'pacific' => true, 'paderborn' => true, 'palace' => true, 'paleo' => true, 'palmsprings' => true, 'panama' => true, 'paris' => true, 'pasadena' => true, 'pharmacy' => true, 'philadelphia' => true, 'philadelphiaarea' => true, 'philately' => true, 'phoenix' => true, 'photography' => true, 'pilots' => true, 'pittsburgh' => true, 'planetarium' => true, 'plantation' => true, 'plants' => true, 'plaza' => true, 'portal' => true, 'portland' => true, 'portlligat' => true, 'posts-and-telecommunications' => true, 'preservation' => true, 'presidio' => true, 'press' => true, 'project' => true, 'public' => true, 'pubol' => true, 'quebec' => true, 'railroad' => true, 'railway' => true, 'research' => true, 'resistance' => true, 'riodejaneiro' => true, 'rochester' => true, 'rockart' => true, 'roma' => true, 'russia' => true, 'saintlouis' => true, 'salem' => true, 'salvadordali' => true, 'salzburg' => true, 'sandiego' => true, 'sanfrancisco' => true, 'santabarbara' => true, 'santacruz' => true, 'santafe' => true, 'saskatchewan' => true, 'satx' => true, 'savannahga' => true, 'schlesisches' => true, 'schoenbrunn' => true, 'schokoladen' => true, 'school' => true, 'schweiz' => true, 'science' => true, 'scienceandhistory' => true, 'scienceandindustry' => true, 'sciencecenter' => true, 'sciencecenters' => true, 'science-fiction' => true, 'sciencehistory' => true, 'sciences' => true, 'sciencesnaturelles' => true, 'scotland' => true, 'seaport' => true, 'settlement' => true, 'settlers' => true, 'shell' => true, 'sherbrooke' => true, 'sibenik' => true, 'silk' => true, 'ski' => true, 'skole' => true, 'society' => true, 'sologne' => true, 'soundandvision' => true, 'southcarolina' => true, 'southwest' => true, 'space' => true, 'spy' => true, 'square' => true, 'stadt' => true, 'stalbans' => true, 'starnberg' => true, 'state' => true, 'stateofdelaware' => true, 'station' => true, 'steam' => true, 'steiermark' => true, 'stjohn' => true, 'stockholm' => true, 'stpetersburg' => true, 'stuttgart' => true, 'suisse' => true, 'surgeonshall' => true, 'surrey' => true, 'svizzera' => true, 'sweden' => true, 'sydney' => true, 'tank' => true, 'tcm' => true, 'technology' => true, 'telekommunikation' => true, 'television' => true, 'texas' => true, 'textile' => true, 'theater' => true, 'time' => true, 'timekeeping' => true, 'topology' => true, 'torino' => true, 'touch' => true, 'town' => true, 'transport' => true, 'tree' => true, 'trolley' => true, 'trust' => true, 'trustee' => true, 'uhren' => true, 'ulm' => true, 'undersea' => true, 'university' => true, 'usa' => true, 'usantiques' => true, 'usarts' => true, 'uscountryestate' => true, 'usculture' => true, 'usdecorativearts' => true, 'usgarden' => true, 'ushistory' => true, 'ushuaia' => true, 'uslivinghistory' => true, 'utah' => true, 'uvic' => true, 'valley' => true, 'vantaa' => true, 'versailles' => true, 'viking' => true, 'village' => true, 'virginia' => true, 'virtual' => true, 'virtuel' => true, 'vlaanderen' => true, 'volkenkunde' => true, 'wales' => true, 'wallonie' => true, 'war' => true, 'washingtondc' => true, 'watchandclock' => true, 'watch-and-clock' => true, 'western' => true, 'westfalen' => true, 'whaling' => true, 'wildlife' => true, 'williamsburg' => true, 'windmill' => true, 'workshop' => true, 'york' => true, 'yorkshire' => true, 'yosemite' => true, 'youth' => true, 'zoological' => true, 'zoology' => true, 'ירושלים' => true, 'иком' => true ), 'mv' => array( 'aero' => true, 'biz' => true, 'com' => true, 'coop' => true, 'edu' => true, 'gov' => true, 'info' => true, 'int' => true, 'mil' => true, 'museum' => true, 'name' => true, 'net' => true, 'org' => true, 'pro' => true ), 'mw' => array( 'ac' => true, 'biz' => true, 'co' => true, 'com' => true, 'coop' => true, 'edu' => true, 'gov' => true, 'int' => true, 'museum' => true, 'net' => true, 'org' => true ), 'mx' => array( 'com' => true, 'org' => true, 'gob' => true, 'edu' => true, 'net' => true, 'blogspot' => true ), 'my' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'edu' => true, 'mil' => true, 'name' => true ), 'mz' => array( '*' => true, '!teledata' => true ), 'na' => array( 'info' => true, 'pro' => true, 'name' => true, 'school' => true, 'or' => true, 'dr' => true, 'us' => true, 'mx' => true, 'ca' => true, 'in' => true, 'cc' => true, 'tv' => true, 'ws' => true, 'mobi' => true, 'co' => true, 'com' => true, 'org' => true ), 'name' => array( 'her' => array( 'forgot' => true ), 'his' => array( 'forgot' => true ) ), 'nc' => array( 'asso' => true ), 'ne' => true, 'net' => array( 'cloudfront' => true, 'gb' => true, 'hu' => true, 'jp' => true, 'se' => true, 'uk' => true, 'at-band-camp' => true, 'blogdns' => true, 'broke-it' => true, 'buyshouses' => true, 'dnsalias' => true, 'dnsdojo' => true, 'does-it' => true, 'dontexist' => true, 'dynalias' => true, 'dynathome' => true, 'endofinternet' => true, 'from-az' => true, 'from-co' => true, 'from-la' => true, 'from-ny' => true, 'gets-it' => true, 'ham-radio-op' => true, 'homeftp' => true, 'homeip' => true, 'homelinux' => true, 'homeunix' => true, 'in-the-band' => true, 'is-a-chef' => true, 'is-a-geek' => true, 'isa-geek' => true, 'kicks-ass' => true, 'office-on-the' => true, 'podzone' => true, 'scrapper-site' => true, 'selfip' => true, 'sells-it' => true, 'servebbs' => true, 'serveftp' => true, 'thruhere' => true, 'webhop' => true, 'fastly' => array( 'ssl' => array( 'a' => true, 'b' => true, 'global' => true ), 'prod' => array( 'a' => true, 'global' => true ) ), 'za' => true ), 'nf' => array( 'com' => true, 'net' => true, 'per' => true, 'rec' => true, 'web' => true, 'arts' => true, 'firm' => true, 'info' => true, 'other' => true, 'store' => true ), 'ng' => array( 'com' => true, 'edu' => true, 'name' => true, 'net' => true, 'org' => true, 'sch' => true, 'gov' => true, 'mil' => true, 'mobi' => true ), 'ni' => array( '*' => true ), 'nl' => array( 'bv' => true, 'co' => true, 'blogspot' => true ), 'no' => array( 'fhs' => true, 'vgs' => true, 'fylkesbibl' => true, 'folkebibl' => true, 'museum' => true, 'idrett' => true, 'priv' => true, 'mil' => true, 'stat' => true, 'dep' => true, 'kommune' => true, 'herad' => true, 'aa' => array( 'gs' => true ), 'ah' => array( 'gs' => true ), 'bu' => array( 'gs' => true ), 'fm' => array( 'gs' => true ), 'hl' => array( 'gs' => true ), 'hm' => array( 'gs' => true ), 'jan-mayen' => array( 'gs' => true ), 'mr' => array( 'gs' => true ), 'nl' => array( 'gs' => true ), 'nt' => array( 'gs' => true ), 'of' => array( 'gs' => true ), 'ol' => array( 'gs' => true ), 'oslo' => array( 'gs' => true ), 'rl' => array( 'gs' => true ), 'sf' => array( 'gs' => true ), 'st' => array( 'gs' => true ), 'svalbard' => array( 'gs' => true ), 'tm' => array( 'gs' => true ), 'tr' => array( 'gs' => true ), 'va' => array( 'gs' => true ), 'vf' => array( 'gs' => true ), 'akrehamn' => true, 'åkrehamn' => true, 'algard' => true, 'ålgård' => true, 'arna' => true, 'brumunddal' => true, 'bryne' => true, 'bronnoysund' => true, 'brønnøysund' => true, 'drobak' => true, 'drøbak' => true, 'egersund' => true, 'fetsund' => true, 'floro' => true, 'florø' => true, 'fredrikstad' => true, 'hokksund' => true, 'honefoss' => true, 'hønefoss' => true, 'jessheim' => true, 'jorpeland' => true, 'jørpeland' => true, 'kirkenes' => true, 'kopervik' => true, 'krokstadelva' => true, 'langevag' => true, 'langevåg' => true, 'leirvik' => true, 'mjondalen' => true, 'mjøndalen' => true, 'mo-i-rana' => true, 'mosjoen' => true, 'mosjøen' => true, 'nesoddtangen' => true, 'orkanger' => true, 'osoyro' => true, 'osøyro' => true, 'raholt' => true, 'råholt' => true, 'sandnessjoen' => true, 'sandnessjøen' => true, 'skedsmokorset' => true, 'slattum' => true, 'spjelkavik' => true, 'stathelle' => true, 'stavern' => true, 'stjordalshalsen' => true, 'stjørdalshalsen' => true, 'tananger' => true, 'tranby' => true, 'vossevangen' => true, 'afjord' => true, 'åfjord' => true, 'agdenes' => true, 'al' => true, 'ål' => true, 'alesund' => true, 'ålesund' => true, 'alstahaug' => true, 'alta' => true, 'áltá' => true, 'alaheadju' => true, 'álaheadju' => true, 'alvdal' => true, 'amli' => true, 'åmli' => true, 'amot' => true, 'åmot' => true, 'andebu' => true, 'andoy' => true, 'andøy' => true, 'andasuolo' => true, 'ardal' => true, 'årdal' => true, 'aremark' => true, 'arendal' => true, 'ås' => true, 'aseral' => true, 'åseral' => true, 'asker' => true, 'askim' => true, 'askvoll' => true, 'askoy' => true, 'askøy' => true, 'asnes' => true, 'åsnes' => true, 'audnedaln' => true, 'aukra' => true, 'aure' => true, 'aurland' => true, 'aurskog-holand' => true, 'aurskog-høland' => true, 'austevoll' => true, 'austrheim' => true, 'averoy' => true, 'averøy' => true, 'balestrand' => true, 'ballangen' => true, 'balat' => true, 'bálát' => true, 'balsfjord' => true, 'bahccavuotna' => true, 'báhccavuotna' => true, 'bamble' => true, 'bardu' => true, 'beardu' => true, 'beiarn' => true, 'bajddar' => true, 'bájddar' => true, 'baidar' => true, 'báidár' => true, 'berg' => true, 'bergen' => true, 'berlevag' => true, 'berlevåg' => true, 'bearalvahki' => true, 'bearalváhki' => true, 'bindal' => true, 'birkenes' => true, 'bjarkoy' => true, 'bjarkøy' => true, 'bjerkreim' => true, 'bjugn' => true, 'bodo' => true, 'bodø' => true, 'badaddja' => true, 'bådåddjå' => true, 'budejju' => true, 'bokn' => true, 'bremanger' => true, 'bronnoy' => true, 'brønnøy' => true, 'bygland' => true, 'bykle' => true, 'barum' => true, 'bærum' => true, 'telemark' => array( 'bo' => true, 'bø' => true ), 'nordland' => array( 'bo' => true, 'bø' => true, 'heroy' => true, 'herøy' => true ), 'bievat' => true, 'bievát' => true, 'bomlo' => true, 'bømlo' => true, 'batsfjord' => true, 'båtsfjord' => true, 'bahcavuotna' => true, 'báhcavuotna' => true, 'dovre' => true, 'drammen' => true, 'drangedal' => true, 'dyroy' => true, 'dyrøy' => true, 'donna' => true, 'dønna' => true, 'eid' => true, 'eidfjord' => true, 'eidsberg' => true, 'eidskog' => true, 'eidsvoll' => true, 'eigersund' => true, 'elverum' => true, 'enebakk' => true, 'engerdal' => true, 'etne' => true, 'etnedal' => true, 'evenes' => true, 'evenassi' => true, 'evenášši' => true, 'evje-og-hornnes' => true, 'farsund' => true, 'fauske' => true, 'fuossko' => true, 'fuoisku' => true, 'fedje' => true, 'fet' => true, 'finnoy' => true, 'finnøy' => true, 'fitjar' => true, 'fjaler' => true, 'fjell' => true, 'flakstad' => true, 'flatanger' => true, 'flekkefjord' => true, 'flesberg' => true, 'flora' => true, 'fla' => true, 'flå' => true, 'folldal' => true, 'forsand' => true, 'fosnes' => true, 'frei' => true, 'frogn' => true, 'froland' => true, 'frosta' => true, 'frana' => true, 'fræna' => true, 'froya' => true, 'frøya' => true, 'fusa' => true, 'fyresdal' => true, 'forde' => true, 'førde' => true, 'gamvik' => true, 'gangaviika' => true, 'gáŋgaviika' => true, 'gaular' => true, 'gausdal' => true, 'gildeskal' => true, 'gildeskål' => true, 'giske' => true, 'gjemnes' => true, 'gjerdrum' => true, 'gjerstad' => true, 'gjesdal' => true, 'gjovik' => true, 'gjøvik' => true, 'gloppen' => true, 'gol' => true, 'gran' => true, 'grane' => true, 'granvin' => true, 'gratangen' => true, 'grimstad' => true, 'grong' => true, 'kraanghke' => true, 'kråanghke' => true, 'grue' => true, 'gulen' => true, 'hadsel' => true, 'halden' => true, 'halsa' => true, 'hamar' => true, 'hamaroy' => true, 'habmer' => true, 'hábmer' => true, 'hapmir' => true, 'hápmir' => true, 'hammerfest' => true, 'hammarfeasta' => true, 'hámmárfeasta' => true, 'haram' => true, 'hareid' => true, 'harstad' => true, 'hasvik' => true, 'aknoluokta' => true, 'ákŋoluokta' => true, 'hattfjelldal' => true, 'aarborte' => true, 'haugesund' => true, 'hemne' => true, 'hemnes' => true, 'hemsedal' => true, 'more-og-romsdal' => array( 'heroy' => true, 'sande' => true ), 'møre-og-romsdal' => array( 'herøy' => true, 'sande' => true ), 'hitra' => true, 'hjartdal' => true, 'hjelmeland' => true, 'hobol' => true, 'hobøl' => true, 'hof' => true, 'hol' => true, 'hole' => true, 'holmestrand' => true, 'holtalen' => true, 'holtålen' => true, 'hornindal' => true, 'horten' => true, 'hurdal' => true, 'hurum' => true, 'hvaler' => true, 'hyllestad' => true, 'hagebostad' => true, 'hægebostad' => true, 'hoyanger' => true, 'høyanger' => true, 'hoylandet' => true, 'høylandet' => true, 'ha' => true, 'hå' => true, 'ibestad' => true, 'inderoy' => true, 'inderøy' => true, 'iveland' => true, 'jevnaker' => true, 'jondal' => true, 'jolster' => true, 'jølster' => true, 'karasjok' => true, 'karasjohka' => true, 'kárášjohka' => true, 'karlsoy' => true, 'galsa' => true, 'gálsá' => true, 'karmoy' => true, 'karmøy' => true, 'kautokeino' => true, 'guovdageaidnu' => true, 'klepp' => true, 'klabu' => true, 'klæbu' => true, 'kongsberg' => true, 'kongsvinger' => true, 'kragero' => true, 'kragerø' => true, 'kristiansand' => true, 'kristiansund' => true, 'krodsherad' => true, 'krødsherad' => true, 'kvalsund' => true, 'rahkkeravju' => true, 'ráhkkerávju' => true, 'kvam' => true, 'kvinesdal' => true, 'kvinnherad' => true, 'kviteseid' => true, 'kvitsoy' => true, 'kvitsøy' => true, 'kvafjord' => true, 'kvæfjord' => true, 'giehtavuoatna' => true, 'kvanangen' => true, 'kvænangen' => true, 'navuotna' => true, 'návuotna' => true, 'kafjord' => true, 'kåfjord' => true, 'gaivuotna' => true, 'gáivuotna' => true, 'larvik' => true, 'lavangen' => true, 'lavagis' => true, 'loabat' => true, 'loabát' => true, 'lebesby' => true, 'davvesiida' => true, 'leikanger' => true, 'leirfjord' => true, 'leka' => true, 'leksvik' => true, 'lenvik' => true, 'leangaviika' => true, 'leaŋgaviika' => true, 'lesja' => true, 'levanger' => true, 'lier' => true, 'lierne' => true, 'lillehammer' => true, 'lillesand' => true, 'lindesnes' => true, 'lindas' => true, 'lindås' => true, 'lom' => true, 'loppa' => true, 'lahppi' => true, 'láhppi' => true, 'lund' => true, 'lunner' => true, 'luroy' => true, 'lurøy' => true, 'luster' => true, 'lyngdal' => true, 'lyngen' => true, 'ivgu' => true, 'lardal' => true, 'lerdal' => true, 'lærdal' => true, 'lodingen' => true, 'lødingen' => true, 'lorenskog' => true, 'lørenskog' => true, 'loten' => true, 'løten' => true, 'malvik' => true, 'masoy' => true, 'måsøy' => true, 'muosat' => true, 'muosát' => true, 'mandal' => true, 'marker' => true, 'marnardal' => true, 'masfjorden' => true, 'meland' => true, 'meldal' => true, 'melhus' => true, 'meloy' => true, 'meløy' => true, 'meraker' => true, 'meråker' => true, 'moareke' => true, 'moåreke' => true, 'midsund' => true, 'midtre-gauldal' => true, 'modalen' => true, 'modum' => true, 'molde' => true, 'moskenes' => true, 'moss' => true, 'mosvik' => true, 'malselv' => true, 'målselv' => true, 'malatvuopmi' => true, 'málatvuopmi' => true, 'namdalseid' => true, 'aejrie' => true, 'namsos' => true, 'namsskogan' => true, 'naamesjevuemie' => true, 'nååmesjevuemie' => true, 'laakesvuemie' => true, 'nannestad' => true, 'narvik' => true, 'narviika' => true, 'naustdal' => true, 'nedre-eiker' => true, 'akershus' => array( 'nes' => true ), 'buskerud' => array( 'nes' => true ), 'nesna' => true, 'nesodden' => true, 'nesseby' => true, 'unjarga' => true, 'unjárga' => true, 'nesset' => true, 'nissedal' => true, 'nittedal' => true, 'nord-aurdal' => true, 'nord-fron' => true, 'nord-odal' => true, 'norddal' => true, 'nordkapp' => true, 'davvenjarga' => true, 'davvenjárga' => true, 'nordre-land' => true, 'nordreisa' => true, 'raisa' => true, 'ráisa' => true, 'nore-og-uvdal' => true, 'notodden' => true, 'naroy' => true, 'nærøy' => true, 'notteroy' => true, 'nøtterøy' => true, 'odda' => true, 'oksnes' => true, 'øksnes' => true, 'oppdal' => true, 'oppegard' => true, 'oppegård' => true, 'orkdal' => true, 'orland' => true, 'ørland' => true, 'orskog' => true, 'ørskog' => true, 'orsta' => true, 'ørsta' => true, 'hedmark' => array( 'os' => true, 'valer' => true, 'våler' => true ), 'hordaland' => array( 'os' => true ), 'osen' => true, 'osteroy' => true, 'osterøy' => true, 'ostre-toten' => true, 'østre-toten' => true, 'overhalla' => true, 'ovre-eiker' => true, 'øvre-eiker' => true, 'oyer' => true, 'øyer' => true, 'oygarden' => true, 'øygarden' => true, 'oystre-slidre' => true, 'øystre-slidre' => true, 'porsanger' => true, 'porsangu' => true, 'porsáŋgu' => true, 'porsgrunn' => true, 'radoy' => true, 'radøy' => true, 'rakkestad' => true, 'rana' => true, 'ruovat' => true, 'randaberg' => true, 'rauma' => true, 'rendalen' => true, 'rennebu' => true, 'rennesoy' => true, 'rennesøy' => true, 'rindal' => true, 'ringebu' => true, 'ringerike' => true, 'ringsaker' => true, 'rissa' => true, 'risor' => true, 'risør' => true, 'roan' => true, 'rollag' => true, 'rygge' => true, 'ralingen' => true, 'rælingen' => true, 'rodoy' => true, 'rødøy' => true, 'romskog' => true, 'rømskog' => true, 'roros' => true, 'røros' => true, 'rost' => true, 'røst' => true, 'royken' => true, 'røyken' => true, 'royrvik' => true, 'røyrvik' => true, 'rade' => true, 'råde' => true, 'salangen' => true, 'siellak' => true, 'saltdal' => true, 'salat' => true, 'sálát' => true, 'sálat' => true, 'samnanger' => true, 'vestfold' => array( 'sande' => true ), 'sandefjord' => true, 'sandnes' => true, 'sandoy' => true, 'sandøy' => true, 'sarpsborg' => true, 'sauda' => true, 'sauherad' => true, 'sel' => true, 'selbu' => true, 'selje' => true, 'seljord' => true, 'sigdal' => true, 'siljan' => true, 'sirdal' => true, 'skaun' => true, 'skedsmo' => true, 'ski' => true, 'skien' => true, 'skiptvet' => true, 'skjervoy' => true, 'skjervøy' => true, 'skierva' => true, 'skiervá' => true, 'skjak' => true, 'skjåk' => true, 'skodje' => true, 'skanland' => true, 'skånland' => true, 'skanit' => true, 'skánit' => true, 'smola' => true, 'smøla' => true, 'snillfjord' => true, 'snasa' => true, 'snåsa' => true, 'snoasa' => true, 'snaase' => true, 'snåase' => true, 'sogndal' => true, 'sokndal' => true, 'sola' => true, 'solund' => true, 'songdalen' => true, 'sortland' => true, 'spydeberg' => true, 'stange' => true, 'stavanger' => true, 'steigen' => true, 'steinkjer' => true, 'stjordal' => true, 'stjørdal' => true, 'stokke' => true, 'stor-elvdal' => true, 'stord' => true, 'stordal' => true, 'storfjord' => true, 'omasvuotna' => true, 'strand' => true, 'stranda' => true, 'stryn' => true, 'sula' => true, 'suldal' => true, 'sund' => true, 'sunndal' => true, 'surnadal' => true, 'sveio' => true, 'svelvik' => true, 'sykkylven' => true, 'sogne' => true, 'søgne' => true, 'somna' => true, 'sømna' => true, 'sondre-land' => true, 'søndre-land' => true, 'sor-aurdal' => true, 'sør-aurdal' => true, 'sor-fron' => true, 'sør-fron' => true, 'sor-odal' => true, 'sør-odal' => true, 'sor-varanger' => true, 'sør-varanger' => true, 'matta-varjjat' => true, 'mátta-várjjat' => true, 'sorfold' => true, 'sørfold' => true, 'sorreisa' => true, 'sørreisa' => true, 'sorum' => true, 'sørum' => true, 'tana' => true, 'deatnu' => true, 'time' => true, 'tingvoll' => true, 'tinn' => true, 'tjeldsund' => true, 'dielddanuorri' => true, 'tjome' => true, 'tjøme' => true, 'tokke' => true, 'tolga' => true, 'torsken' => true, 'tranoy' => true, 'tranøy' => true, 'tromso' => true, 'tromsø' => true, 'tromsa' => true, 'romsa' => true, 'trondheim' => true, 'troandin' => true, 'trysil' => true, 'trana' => true, 'træna' => true, 'trogstad' => true, 'trøgstad' => true, 'tvedestrand' => true, 'tydal' => true, 'tynset' => true, 'tysfjord' => true, 'divtasvuodna' => true, 'divttasvuotna' => true, 'tysnes' => true, 'tysvar' => true, 'tysvær' => true, 'tonsberg' => true, 'tønsberg' => true, 'ullensaker' => true, 'ullensvang' => true, 'ulvik' => true, 'utsira' => true, 'vadso' => true, 'vadsø' => true, 'cahcesuolo' => true, 'čáhcesuolo' => true, 'vaksdal' => true, 'valle' => true, 'vang' => true, 'vanylven' => true, 'vardo' => true, 'vardø' => true, 'varggat' => true, 'várggát' => true, 'vefsn' => true, 'vaapste' => true, 'vega' => true, 'vegarshei' => true, 'vegårshei' => true, 'vennesla' => true, 'verdal' => true, 'verran' => true, 'vestby' => true, 'vestnes' => true, 'vestre-slidre' => true, 'vestre-toten' => true, 'vestvagoy' => true, 'vestvågøy' => true, 'vevelstad' => true, 'vik' => true, 'vikna' => true, 'vindafjord' => true, 'volda' => true, 'voss' => true, 'varoy' => true, 'værøy' => true, 'vagan' => true, 'vågan' => true, 'voagat' => true, 'vagsoy' => true, 'vågsøy' => true, 'vaga' => true, 'vågå' => true, 'ostfold' => array( 'valer' => true ), 'østfold' => array( 'våler' => true ), 'co' => true, 'blogspot' => true ), 'np' => array( '*' => true ), 'nr' => array( 'biz' => true, 'info' => true, 'gov' => true, 'edu' => true, 'org' => true, 'net' => true, 'com' => true ), 'nu' => array( 'merseine' => true, 'mine' => true, 'shacknet' => true ), 'nz' => array( '*' => true, 'co' => array( 'blogspot' => true ) ), 'om' => array( 'co' => true, 'com' => true, 'edu' => true, 'gov' => true, 'med' => true, 'museum' => true, 'net' => true, 'org' => true, 'pro' => true ), 'org' => array( 'ae' => true, 'us' => true, 'dyndns' => array( 'go' => true, 'home' => true ), 'blogdns' => true, 'blogsite' => true, 'boldlygoingnowhere' => true, 'dnsalias' => true, 'dnsdojo' => true, 'doesntexist' => true, 'dontexist' => true, 'doomdns' => true, 'dvrdns' => true, 'dynalias' => true, 'endofinternet' => true, 'endoftheinternet' => true, 'from-me' => true, 'game-host' => true, 'gotdns' => true, 'hobby-site' => true, 'homedns' => true, 'homeftp' => true, 'homelinux' => true, 'homeunix' => true, 'is-a-bruinsfan' => true, 'is-a-candidate' => true, 'is-a-celticsfan' => true, 'is-a-chef' => true, 'is-a-geek' => true, 'is-a-knight' => true, 'is-a-linux-user' => true, 'is-a-patsfan' => true, 'is-a-soxfan' => true, 'is-found' => true, 'is-lost' => true, 'is-saved' => true, 'is-very-bad' => true, 'is-very-evil' => true, 'is-very-good' => true, 'is-very-nice' => true, 'is-very-sweet' => true, 'isa-geek' => true, 'kicks-ass' => true, 'misconfused' => true, 'podzone' => true, 'readmyblog' => true, 'selfip' => true, 'sellsyourhome' => true, 'servebbs' => true, 'serveftp' => true, 'servegame' => true, 'stuff-4-sale' => true, 'webhop' => true, 'za' => true ), 'pa' => array( 'ac' => true, 'gob' => true, 'com' => true, 'org' => true, 'sld' => true, 'edu' => true, 'net' => true, 'ing' => true, 'abo' => true, 'med' => true, 'nom' => true ), 'pe' => array( 'edu' => true, 'gob' => true, 'nom' => true, 'mil' => true, 'org' => true, 'com' => true, 'net' => true ), 'pf' => array( 'com' => true, 'org' => true, 'edu' => true ), 'pg' => array( '*' => true ), 'ph' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'edu' => true, 'ngo' => true, 'mil' => true, 'i' => true ), 'pk' => array( 'com' => true, 'net' => true, 'edu' => true, 'org' => true, 'fam' => true, 'biz' => true, 'web' => true, 'gov' => true, 'gob' => true, 'gok' => true, 'gon' => true, 'gop' => true, 'gos' => true, 'info' => true ), 'pl' => array( 'aid' => true, 'agro' => true, 'atm' => true, 'auto' => true, 'biz' => true, 'com' => true, 'edu' => true, 'gmina' => true, 'gsm' => true, 'info' => true, 'mail' => true, 'miasta' => true, 'media' => true, 'mil' => true, 'net' => true, 'nieruchomosci' => true, 'nom' => true, 'org' => true, 'pc' => true, 'powiat' => true, 'priv' => true, 'realestate' => true, 'rel' => true, 'sex' => true, 'shop' => true, 'sklep' => true, 'sos' => true, 'szkola' => true, 'targi' => true, 'tm' => true, 'tourism' => true, 'travel' => true, 'turystyka' => true, '6bone' => true, 'art' => true, 'mbone' => true, 'gov' => array( 'uw' => true, 'um' => true, 'ug' => true, 'upow' => true, 'starostwo' => true, 'so' => true, 'sr' => true, 'po' => true, 'pa' => true ), 'ngo' => true, 'irc' => true, 'usenet' => true, 'augustow' => true, 'babia-gora' => true, 'bedzin' => true, 'beskidy' => true, 'bialowieza' => true, 'bialystok' => true, 'bielawa' => true, 'bieszczady' => true, 'boleslawiec' => true, 'bydgoszcz' => true, 'bytom' => true, 'cieszyn' => true, 'czeladz' => true, 'czest' => true, 'dlugoleka' => true, 'elblag' => true, 'elk' => true, 'glogow' => true, 'gniezno' => true, 'gorlice' => true, 'grajewo' => true, 'ilawa' => true, 'jaworzno' => true, 'jelenia-gora' => true, 'jgora' => true, 'kalisz' => true, 'kazimierz-dolny' => true, 'karpacz' => true, 'kartuzy' => true, 'kaszuby' => true, 'katowice' => true, 'kepno' => true, 'ketrzyn' => true, 'klodzko' => true, 'kobierzyce' => true, 'kolobrzeg' => true, 'konin' => true, 'konskowola' => true, 'kutno' => true, 'lapy' => true, 'lebork' => true, 'legnica' => true, 'lezajsk' => true, 'limanowa' => true, 'lomza' => true, 'lowicz' => true, 'lubin' => true, 'lukow' => true, 'malbork' => true, 'malopolska' => true, 'mazowsze' => true, 'mazury' => true, 'mielec' => true, 'mielno' => true, 'mragowo' => true, 'naklo' => true, 'nowaruda' => true, 'nysa' => true, 'olawa' => true, 'olecko' => true, 'olkusz' => true, 'olsztyn' => true, 'opoczno' => true, 'opole' => true, 'ostroda' => true, 'ostroleka' => true, 'ostrowiec' => true, 'ostrowwlkp' => true, 'pila' => true, 'pisz' => true, 'podhale' => true, 'podlasie' => true, 'polkowice' => true, 'pomorze' => true, 'pomorskie' => true, 'prochowice' => true, 'pruszkow' => true, 'przeworsk' => true, 'pulawy' => true, 'radom' => true, 'rawa-maz' => true, 'rybnik' => true, 'rzeszow' => true, 'sanok' => true, 'sejny' => true, 'siedlce' => true, 'slask' => true, 'slupsk' => true, 'sosnowiec' => true, 'stalowa-wola' => true, 'skoczow' => true, 'starachowice' => true, 'stargard' => true, 'suwalki' => true, 'swidnica' => true, 'swiebodzin' => true, 'swinoujscie' => true, 'szczecin' => true, 'szczytno' => true, 'tarnobrzeg' => true, 'tgory' => true, 'turek' => true, 'tychy' => true, 'ustka' => true, 'walbrzych' => true, 'warmia' => true, 'warszawa' => true, 'waw' => true, 'wegrow' => true, 'wielun' => true, 'wlocl' => true, 'wloclawek' => true, 'wodzislaw' => true, 'wolomin' => true, 'wroclaw' => true, 'zachpomor' => true, 'zagan' => true, 'zarow' => true, 'zgora' => true, 'zgorzelec' => true, 'gda' => true, 'gdansk' => true, 'gdynia' => true, 'med' => true, 'sopot' => true, 'gliwice' => true, 'krakow' => true, 'poznan' => true, 'wroc' => true, 'zakopane' => true, 'co' => true ), 'pm' => true, 'pn' => array( 'gov' => true, 'co' => true, 'org' => true, 'edu' => true, 'net' => true ), 'post' => true, 'pr' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'edu' => true, 'isla' => true, 'pro' => true, 'biz' => true, 'info' => true, 'name' => true, 'est' => true, 'prof' => true, 'ac' => true ), 'pro' => array( 'aca' => true, 'bar' => true, 'cpa' => true, 'jur' => true, 'law' => true, 'med' => true, 'eng' => true ), 'ps' => array( 'edu' => true, 'gov' => true, 'sec' => true, 'plo' => true, 'com' => true, 'org' => true, 'net' => true ), 'pt' => array( 'net' => true, 'gov' => true, 'org' => true, 'edu' => true, 'int' => true, 'publ' => true, 'com' => true, 'nome' => true, 'blogspot' => true ), 'pw' => array( 'co' => true, 'ne' => true, 'or' => true, 'ed' => true, 'go' => true, 'belau' => true ), 'py' => array( 'com' => true, 'coop' => true, 'edu' => true, 'gov' => true, 'mil' => true, 'net' => true, 'org' => true ), 'qa' => array( 'com' => true, 'edu' => true, 'gov' => true, 'mil' => true, 'name' => true, 'net' => true, 'org' => true, 'sch' => true ), 're' => array( 'com' => true, 'asso' => true, 'nom' => true, 'blogspot' => true ), 'ro' => array( 'com' => true, 'org' => true, 'tm' => true, 'nt' => true, 'nom' => true, 'info' => true, 'rec' => true, 'arts' => true, 'firm' => true, 'store' => true, 'www' => true, 'blogspot' => true ), 'rs' => array( 'co' => true, 'org' => true, 'edu' => true, 'ac' => true, 'gov' => true, 'in' => true ), 'ru' => array( 'ac' => true, 'com' => true, 'edu' => true, 'int' => true, 'net' => true, 'org' => true, 'pp' => true, 'adygeya' => true, 'altai' => true, 'amur' => true, 'arkhangelsk' => true, 'astrakhan' => true, 'bashkiria' => true, 'belgorod' => true, 'bir' => true, 'bryansk' => true, 'buryatia' => true, 'cbg' => true, 'chel' => true, 'chelyabinsk' => true, 'chita' => true, 'chukotka' => true, 'chuvashia' => true, 'dagestan' => true, 'dudinka' => true, 'e-burg' => true, 'grozny' => true, 'irkutsk' => true, 'ivanovo' => true, 'izhevsk' => true, 'jar' => true, 'joshkar-ola' => true, 'kalmykia' => true, 'kaluga' => true, 'kamchatka' => true, 'karelia' => true, 'kazan' => true, 'kchr' => true, 'kemerovo' => true, 'khabarovsk' => true, 'khakassia' => true, 'khv' => true, 'kirov' => true, 'koenig' => true, 'komi' => true, 'kostroma' => true, 'krasnoyarsk' => true, 'kuban' => true, 'kurgan' => true, 'kursk' => true, 'lipetsk' => true, 'magadan' => true, 'mari' => true, 'mari-el' => true, 'marine' => true, 'mordovia' => true, 'mosreg' => true, 'msk' => true, 'murmansk' => true, 'nalchik' => true, 'nnov' => true, 'nov' => true, 'novosibirsk' => true, 'nsk' => true, 'omsk' => true, 'orenburg' => true, 'oryol' => true, 'palana' => true, 'penza' => true, 'perm' => true, 'pskov' => true, 'ptz' => true, 'rnd' => true, 'ryazan' => true, 'sakhalin' => true, 'samara' => true, 'saratov' => true, 'simbirsk' => true, 'smolensk' => true, 'spb' => true, 'stavropol' => true, 'stv' => true, 'surgut' => true, 'tambov' => true, 'tatarstan' => true, 'tom' => true, 'tomsk' => true, 'tsaritsyn' => true, 'tsk' => true, 'tula' => true, 'tuva' => true, 'tver' => true, 'tyumen' => true, 'udm' => true, 'udmurtia' => true, 'ulan-ude' => true, 'vladikavkaz' => true, 'vladimir' => true, 'vladivostok' => true, 'volgograd' => true, 'vologda' => true, 'voronezh' => true, 'vrn' => true, 'vyatka' => true, 'yakutia' => true, 'yamal' => true, 'yaroslavl' => true, 'yekaterinburg' => true, 'yuzhno-sakhalinsk' => true, 'amursk' => true, 'baikal' => true, 'cmw' => true, 'fareast' => true, 'jamal' => true, 'kms' => true, 'k-uralsk' => true, 'kustanai' => true, 'kuzbass' => true, 'magnitka' => true, 'mytis' => true, 'nakhodka' => true, 'nkz' => true, 'norilsk' => true, 'oskol' => true, 'pyatigorsk' => true, 'rubtsovsk' => true, 'snz' => true, 'syzran' => true, 'vdonsk' => true, 'zgrad' => true, 'gov' => true, 'mil' => true, 'test' => true ), 'rw' => array( 'gov' => true, 'net' => true, 'edu' => true, 'ac' => true, 'com' => true, 'co' => true, 'int' => true, 'mil' => true, 'gouv' => true ), 'sa' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'med' => true, 'pub' => true, 'edu' => true, 'sch' => true ), 'sb' => array( 'com' => true, 'edu' => true, 'gov' => true, 'net' => true, 'org' => true ), 'sc' => array( 'com' => true, 'gov' => true, 'net' => true, 'org' => true, 'edu' => true ), 'sd' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true, 'med' => true, 'tv' => true, 'gov' => true, 'info' => true ), 'se' => array( 'a' => true, 'ac' => true, 'b' => true, 'bd' => true, 'brand' => true, 'c' => true, 'd' => true, 'e' => true, 'f' => true, 'fh' => true, 'fhsk' => true, 'fhv' => true, 'g' => true, 'h' => true, 'i' => true, 'k' => true, 'komforb' => true, 'kommunalforbund' => true, 'komvux' => true, 'l' => true, 'lanbib' => true, 'm' => true, 'n' => true, 'naturbruksgymn' => true, 'o' => true, 'org' => true, 'p' => true, 'parti' => true, 'pp' => true, 'press' => true, 'r' => true, 's' => true, 'sshn' => true, 't' => true, 'tm' => true, 'u' => true, 'w' => true, 'x' => true, 'y' => true, 'z' => true, 'blogspot' => true ), 'sg' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'edu' => true, 'per' => true, 'blogspot' => true ), 'sh' => array( 'com' => true, 'net' => true, 'gov' => true, 'org' => true, 'mil' => true ), 'si' => true, 'sj' => true, 'sk' => array( 'blogspot' => true ), 'sl' => array( 'com' => true, 'net' => true, 'edu' => true, 'gov' => true, 'org' => true ), 'sm' => true, 'sn' => array( 'art' => true, 'com' => true, 'edu' => true, 'gouv' => true, 'org' => true, 'perso' => true, 'univ' => true ), 'so' => array( 'com' => true, 'net' => true, 'org' => true ), 'sr' => true, 'st' => array( 'co' => true, 'com' => true, 'consulado' => true, 'edu' => true, 'embaixada' => true, 'gov' => true, 'mil' => true, 'net' => true, 'org' => true, 'principe' => true, 'saotome' => true, 'store' => true ), 'su' => true, 'sv' => array( 'com' => true, 'edu' => true, 'gob' => true, 'org' => true, 'red' => true ), 'sx' => array( 'gov' => true ), 'sy' => array( 'edu' => true, 'gov' => true, 'net' => true, 'mil' => true, 'com' => true, 'org' => true ), 'sz' => array( 'co' => true, 'ac' => true, 'org' => true ), 'tc' => true, 'td' => array( 'blogspot' => true ), 'tel' => true, 'tf' => true, 'tg' => true, 'th' => array( 'ac' => true, 'co' => true, 'go' => true, 'in' => true, 'mi' => true, 'net' => true, 'or' => true ), 'tj' => array( 'ac' => true, 'biz' => true, 'co' => true, 'com' => true, 'edu' => true, 'go' => true, 'gov' => true, 'int' => true, 'mil' => true, 'name' => true, 'net' => true, 'nic' => true, 'org' => true, 'test' => true, 'web' => true ), 'tk' => true, 'tl' => array( 'gov' => true ), 'tm' => array( 'com' => true, 'co' => true, 'org' => true, 'net' => true, 'nom' => true, 'gov' => true, 'mil' => true, 'edu' => true ), 'tn' => array( 'com' => true, 'ens' => true, 'fin' => true, 'gov' => true, 'ind' => true, 'intl' => true, 'nat' => true, 'net' => true, 'org' => true, 'info' => true, 'perso' => true, 'tourism' => true, 'edunet' => true, 'rnrt' => true, 'rns' => true, 'rnu' => true, 'mincom' => true, 'agrinet' => true, 'defense' => true, 'turen' => true ), 'to' => array( 'com' => true, 'gov' => true, 'net' => true, 'org' => true, 'edu' => true, 'mil' => true ), 'tp' => true, 'tr' => array( '*' => true, '!nic' => true, 'nc' => array( 'gov' => true ) ), 'travel' => true, 'tt' => array( 'co' => true, 'com' => true, 'org' => true, 'net' => true, 'biz' => true, 'info' => true, 'pro' => true, 'int' => true, 'coop' => true, 'jobs' => true, 'mobi' => true, 'travel' => true, 'museum' => true, 'aero' => true, 'name' => true, 'gov' => true, 'edu' => true ), 'tv' => array( 'dyndns' => true, 'better-than' => true, 'on-the-web' => true, 'worse-than' => true ), 'tw' => array( 'edu' => true, 'gov' => true, 'mil' => true, 'com' => true, 'net' => true, 'org' => true, 'idv' => true, 'game' => true, 'ebiz' => true, 'club' => true, '網路' => true, '組織' => true, '商業' => true, 'blogspot' => true ), 'tz' => array( 'ac' => true, 'co' => true, 'go' => true, 'hotel' => true, 'info' => true, 'me' => true, 'mil' => true, 'mobi' => true, 'ne' => true, 'or' => true, 'sc' => true, 'tv' => true ), 'ua' => array( 'com' => true, 'edu' => true, 'gov' => true, 'in' => true, 'net' => true, 'org' => true, 'cherkassy' => true, 'cherkasy' => true, 'chernigov' => true, 'chernihiv' => true, 'chernivtsi' => true, 'chernovtsy' => true, 'ck' => true, 'cn' => true, 'cr' => true, 'crimea' => true, 'cv' => true, 'dn' => true, 'dnepropetrovsk' => true, 'dnipropetrovsk' => true, 'dominic' => true, 'donetsk' => true, 'dp' => true, 'if' => true, 'ivano-frankivsk' => true, 'kh' => true, 'kharkiv' => true, 'kharkov' => true, 'kherson' => true, 'khmelnitskiy' => true, 'khmelnytskyi' => true, 'kiev' => true, 'kirovograd' => true, 'km' => true, 'kr' => true, 'krym' => true, 'ks' => true, 'kv' => true, 'kyiv' => true, 'lg' => true, 'lt' => true, 'lugansk' => true, 'lutsk' => true, 'lv' => true, 'lviv' => true, 'mk' => true, 'mykolaiv' => true, 'nikolaev' => true, 'od' => true, 'odesa' => true, 'odessa' => true, 'pl' => true, 'poltava' => true, 'rivne' => true, 'rovno' => true, 'rv' => true, 'sb' => true, 'sebastopol' => true, 'sevastopol' => true, 'sm' => true, 'sumy' => true, 'te' => true, 'ternopil' => true, 'uz' => true, 'uzhgorod' => true, 'vinnica' => true, 'vinnytsia' => true, 'vn' => true, 'volyn' => true, 'yalta' => true, 'zaporizhzhe' => true, 'zaporizhzhia' => true, 'zhitomir' => true, 'zhytomyr' => true, 'zp' => true, 'zt' => true, 'co' => true, 'pp' => true ), 'ug' => array( 'co' => true, 'or' => true, 'ac' => true, 'sc' => true, 'go' => true, 'ne' => true, 'com' => true, 'org' => true ), 'uk' => array( '*' => true, 'sch' => array( '*' => true ), '!bl' => true, '!british-library' => true, '!jet' => true, '!mod' => true, '!national-library-scotland' => true, '!nel' => true, '!nic' => true, '!nls' => true, '!parliament' => true, 'co' => array( 'blogspot' => true ) ), 'us' => array( 'dni' => true, 'fed' => true, 'isa' => true, 'kids' => true, 'nsn' => true, 'ak' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'al' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ar' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'as' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'az' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ca' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'co' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ct' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'dc' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'de' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'fl' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ga' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'gu' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'hi' => array( 'cc' => true, 'lib' => true ), 'ia' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'id' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'il' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'in' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ks' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ky' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'la' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ma' => array( 'k12' => array( 'pvt' => true, 'chtr' => true, 'paroch' => true ), 'cc' => true, 'lib' => true ), 'md' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'me' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'mi' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'mn' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'mo' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ms' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'mt' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'nc' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'nd' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ne' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'nh' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'nj' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'nm' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'nv' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ny' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'oh' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ok' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'or' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'pa' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'pr' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ri' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'sc' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'sd' => array( 'cc' => true, 'lib' => true ), 'tn' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'tx' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'ut' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'vi' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'vt' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'va' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'wa' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'wi' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'wv' => array( 'cc' => true ), 'wy' => array( 'k12' => true, 'cc' => true, 'lib' => true ), 'is-by' => true, 'land-4-sale' => true, 'stuff-4-sale' => true ), 'uy' => array( 'com' => true, 'edu' => true, 'gub' => true, 'mil' => true, 'net' => true, 'org' => true ), 'uz' => array( 'co' => true, 'com' => true, 'net' => true, 'org' => true ), 'va' => true, 'vc' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'mil' => true, 'edu' => true ), 've' => array( 'co' => true, 'com' => true, 'e12' => true, 'edu' => true, 'gov' => true, 'info' => true, 'mil' => true, 'net' => true, 'org' => true, 'web' => true ), 'vg' => true, 'vi' => array( 'co' => true, 'com' => true, 'k12' => true, 'net' => true, 'org' => true ), 'vn' => array( 'com' => true, 'net' => true, 'org' => true, 'edu' => true, 'gov' => true, 'int' => true, 'ac' => true, 'biz' => true, 'info' => true, 'name' => true, 'pro' => true, 'health' => true ), 'vu' => true, 'wf' => true, 'ws' => array( 'com' => true, 'net' => true, 'org' => true, 'gov' => true, 'edu' => true, 'dyndns' => true, 'mypets' => true ), 'yt' => true, 'امارات' => true, 'বাংলা' => true, '中国' => true, '中國' => true, 'الجزائر' => true, 'مصر' => true, 'გე' => true, '香港' => true, 'भारत' => true, 'بھارت' => true, 'భారత్' => true, 'ભારત' => true, 'ਭਾਰਤ' => true, 'ভারত' => true, 'இந்தியா' => true, 'ایران' => true, 'ايران' => true, 'الاردن' => true, '한국' => true, 'қаз' => true, 'ලංකා' => true, 'இலங்கை' => true, 'المغرب' => true, 'мон' => true, 'مليسيا' => true, 'عمان' => true, 'فلسطين' => true, 'срб' => true, 'рф' => true, 'قطر' => true, 'السعودية' => true, 'السعودیة' => true, 'السعودیۃ' => true, 'السعوديه' => true, 'سورية' => true, 'سوريا' => true, '新加坡' => true, 'சிங்கப்பூர்' => true, 'ไทย' => true, 'تونس' => true, '台灣' => true, '台湾' => true, '臺灣' => true, 'укр' => true, 'اليمن' => true, 'xxx' => true, 'ye' => array( '*' => true ), 'za' => array( '*' => true ), 'zm' => array( '*' => true ), 'zw' => array( '*' => true ), 'онлайн' => true, 'сайт' => true, 'شبكة' => true, '游戏' => true, '企业' => true, 'camera' => true, 'clothing' => true, 'lighting' => true, 'singles' => true, 'ventures' => true, 'voyage' => true, 'guru' => true, 'holdings' => true, 'equipment' => true, 'bike' => true, 'estate' => true, 'tattoo' => true, '在线' => true, '中文网' => true, 'land' => true, 'plumbing' => true, 'contractors' => true, 'sexy' => true, 'menu' => true, '世界' => true, 'uno' => true, 'gallery' => true, 'technology' => true, '集团' => true, 'reviews' => true, 'guide' => true, '我爱你' => true, 'graphics' => true, 'construction' => true, 'onl' => true, 'みんな' => true, 'diamonds' => true, 'kiwi' => true, 'enterprises' => true, 'today' => true, 'futbol' => true, 'photography' => true, 'tips' => true, 'directory' => true, 'kitchen' => true, '移动' => true, 'kim' => true, '삼성' => true, 'monash' => true, 'wed' => true, 'pink' => true, 'ruhr' => true, 'buzz' => true, 'careers' => true, 'shoes' => true, 'موقع' => true, 'career' => true, 'otsuka' => true, '中信' => true, 'gift' => true, 'recipes' => true, 'coffee' => true, 'luxury' => true, 'domains' => true, 'photos' => true, 'limo' => true, 'viajes' => true, 'wang' => true, 'democrat' => true, 'mango' => true, 'cab' => true, 'support' => true, 'dance' => true, 'nagoya' => true, 'computer' => true, 'wien' => true, 'berlin' => true, 'codes' => true, 'email' => true, 'بازار' => true, 'repair' => true, 'holiday' => true, 'center' => true, 'systems' => true, 'wiki' => true, 'ceo' => true, 'international' => true, 'solar' => true, 'company' => true, 'education' => true, 'training' => true, 'academy' => true, 'marketing' => true, 'florist' => true, 'solutions' => true, 'build' => true, 'institute' => true, 'builders' => true, 'red' => true, 'blue' => true, 'ninja' => true, 'business' => true, 'gal' => true, 'social' => true, 'house' => true, 'camp' => true, 'immobilien' => true, 'moda' => true, 'glass' => true, 'management' => true, 'kaufen' => true, 'farm' => true, '公益' => true, '政务' => true, 'club' => true, 'voting' => true, 'TOKYO' => true, 'moe' => true ); ?>