2 // $Id: proxy.php,v 1.6 2005/04/29 09:10:11 henoheno Exp $
3 // Copywrite (C) 2003-2005 PukiWiki Developers Team
4 // License: GPL v2 or (at your option) any later version
6 // HTTP-Proxy related functions
8 // Max number of 'track' redirection message with 301 or 302 response
9 define('PKWK_HTTP_REQUEST_URL_REDIRECT_MAX', 2);
11 // Separate IPv4 network-address and its netmask
12 define('PKWK_CIDR_NETWORK_REGEX', '/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\/([0-9.]+))?$/');
16 * Get / Send data via HTTP request
17 * $url : URI started with http:// (http://user:pass@host:port/path?query)
18 * $method : GET, POST, or HEAD
19 * $headers : Additional HTTP headers, ended with "\r\n"
20 * $post : An array of data to send via POST method ('key'=>'value')
21 * $redirect_max : Max number of HTTP redirect
22 * $content_charset : Content charset. Use '' or CONTENT_CHARSET
24 function http_request($url, $method = 'GET', $headers = '', $post = array(),
25 $redirect_max = PKWK_HTTP_REQUEST_URL_REDIRECT_MAX, $content_charset = '')
27 global $proxy_host, $proxy_port;
28 global $need_proxy_auth, $proxy_auth_user, $proxy_auth_pass;
31 $arr = parse_url($url);
33 $via_proxy = via_proxy($arr['host']);
36 $arr['query'] = isset($arr['query']) ? '?' . $arr['query'] : '';
38 $arr['port'] = isset($arr['port']) ? $arr['port'] : 80;
40 $url_base = $arr['scheme'] . '://' . $arr['host'] . ':' . $arr['port'];
41 $url_path = isset($arr['path']) ? $arr['path'] : '/';
42 $url = ($via_proxy ? $url_base : '') . $url_path . $arr['query'];
44 $query = $method . ' ' . $url . ' HTTP/1.0' . "\r\n";
45 $query .= 'Host: ' . $arr['host'] . "\r\n";
46 $query .= 'User-Agent: PukiWiki/' . S_VERSION . "\r\n";
48 // Basic-auth for HTTP proxy server
49 if ($need_proxy_auth && isset($proxy_auth_user) && isset($proxy_auth_pass))
50 $query .= 'Proxy-Authorization: Basic '.
51 base64_encode($proxy_auth_user . ':' . $proxy_auth_pass) . "\r\n";
53 // (Normal) Basic-auth for remote host
54 if (isset($arr['user']) && isset($arr['pass']))
55 $query .= 'Authorization: Basic '.
56 base64_encode($arr['user'] . ':' . $arr['pass']) . "\r\n";
60 if (strtoupper($method) == 'POST') {
61 // 'application/x-www-form-urlencoded', especially for TrackBack ping
63 foreach ($post as $name=>$val) $POST[] = $name . '=' . urlencode($val);
64 $data = join('&', $POST);
66 if (preg_match('/^[a-zA-Z0-9_-]+$/', $content_charset)) {
68 $query .= 'Content-Type: application/x-www-form-urlencoded' . "\r\n";
70 // With charset (NOTE: Some implementation may hate this)
71 $query .= 'Content-Type: application/x-www-form-urlencoded' .
72 '; charset=' . strtolower($content_charset) . "\r\n";
75 $query .= 'Content-Length: ' . strlen($data) . "\r\n";
85 $via_proxy ? $proxy_host : $arr['host'],
86 $via_proxy ? $proxy_port : $arr['port'],
90 'query' => $query, // Query string
91 'rc' => $errno, // Error number
92 'header' => '', // Header
93 'data' => $errstr // Error message
98 while (! feof($fp)) $response .= fread($fp, 4096);
101 $resp = explode("\r\n\r\n", $response, 2);
102 $rccd = explode(' ', $resp[0], 3); // array('HTTP/1.1', '200', 'OK\r\n...')
103 $rc = (integer)$rccd[1];
106 case 301: // Moved Permanently
107 case 302: // Moved Temporarily
109 if (preg_match('/^Location: (.+)$/m', $resp[0], $matches)
110 && --$redirect_max > 0)
112 $url = trim($matches[1]);
113 if (! preg_match('/^https?:\//', $url)) {
114 // Relative path to Absolute
116 $url = substr($url_path, 0, strrpos($url_path, '/')) . '/' . $url;
117 $url = $url_base . $url; // Add sheme, host
120 return http_request($url, $method, $headers, $post, $redirect_max);
124 'query' => $query, // Query String
125 'rc' => $rc, // Response Code
126 'header' => $resp[0], // Header
127 'data' => $resp[1] // Data
131 // Check HTTP proxy server is needed or not for the $host
132 function via_proxy($host)
134 global $use_proxy, $no_proxy;
136 if (! $use_proxy) return FALSE;
138 $ip = gethostbyname($host);
139 $l_ip = ip2long($ip);
140 $is_valid = (is_long($l_ip) && long2ip($l_ip) == $ip); // Valid IP address
143 foreach ($no_proxy as $network) {
144 if ($is_valid && preg_match(PKWK_CIDR_NETWORK_REGEX, $network, $matches)) {
145 // Sample: '10.0.0.0/8' or '10.0.0.0/255.0.0.0'
146 $l_net = ip2long($matches[1]); // '10.0.0.0'
147 $mask = isset($matches[2]) ? $matches[2] : 32; // '8' or '255.0.0.0'
148 $mask = is_numeric($mask) ?
149 pow(2, 32) - pow(2, 32 - $mask) : // '8' means '8-bit mask'
150 ip2long($mask); // '255.0.0.0' (the same)
152 if (($l_ip & $mask) == $l_net) return FALSE;
154 // Hostname, or a part of hostname
155 if (preg_match('/' . preg_quote($network, '/') . '$/', $host))
160 return TRUE; // Proxy needed