<?php
/////////////////////////////////////////////////
// PukiWiki - Yet another WikiWikiWeb clone.
//
// $Id: proxy.php,v 1.4 2003/10/01 05:38:47 arino Exp $
//

/*
 * http_request($url)
 *   HTTPリクエストを発行し、データを取得する
 * $url     : http://から始まるURL(http://user:pass@host:port/path?query)
 * $method  : GET, POST, HEADのいずれか(デフォルトはGET)
 * $headers : 任意の追加ヘッダ
 * $post    : POSTの時に送信するデータを格納した配列('変数名'=>'値')
 * $redirect_max : HTTP redirectの回数制限
*/

// リダイレクト回数制限の初期値
define('HTTP_REQUEST_URL_REDIRECT_MAX',10);

function http_request($url,$method='GET',$headers='',$post=array(),
    $redirect_max=HTTP_REQUEST_URL_REDIRECT_MAX)
{
    global $use_proxy,$proxy_host,$proxy_port;
    global $need_proxy_auth,$proxy_auth_user,$proxy_auth_pass;
    
    $rc = array();
    $arr = parse_url($url);
    
    $via_proxy = $use_proxy and via_proxy($arr['host']);
    
    // query
    $arr['query'] = isset($arr['query']) ? '?'.$arr['query'] : '';
    // port
    $arr['port'] = isset($arr['port']) ? $arr['port'] : 80;
    
    $url_base = $arr['scheme'].'://'.$arr['host'].':'.$arr['port'];
    $url_path = isset($arr['path']) ? $arr['path'] : '/';
    $url = ($via_proxy ? $url_base : '').$url_path.$arr['query'];
    
    $query = $method.' '.$url." HTTP/1.0\r\n";
    $query .= "Host: ".$arr['host']."\r\n";
    $query .= "User-Agent: PukiWiki/".S_VERSION."\r\n";

    // proxyのBasic認証
    if ($need_proxy_auth and isset($proxy_auth_user) and isset($proxy_auth_pass))
    {
        $query .= 'Proxy-Authorization: Basic '.
            base64_encode($proxy_auth_user.':'.$proxy_auth_pass)."\r\n";
    }
    // Basic 認証用
    if (isset($arr['user']) and isset($arr['pass']))
    {
        $query .= 'Authorization: Basic '.
            base64_encode($arr['user'].':'.$arr['pass'])."\r\n";
    }
    
    $query .= $headers;
    
    // POST 時は、urlencode したデータとする
    if (strtoupper($method) == 'POST')
    {
        $POST = array();
        foreach ($post as $name=>$val)
        {
            $POST[] = $name.'='.urlencode($val);
        }
        $data = join('&',$POST);
        $query .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $query .= 'Content-Length: '.strlen($data)."\r\n";
        $query .= "\r\n";
        $query .= $data;
    }
    else
    {
        $query .= "\r\n";
    }
    
    $fp = fsockopen(
        $via_proxy ? $proxy_host : $arr['host'],
        $via_proxy ? $proxy_port : $arr['port'],
        $errno,$errstr,30);
    if (!$fp)
    {
        return array(
            'query'  => $query, // Query String
            'rc'     => $errno, // エラー番号
            'header' => '',     // Header
            'data'   => $errstr // エラーメッセージ
        );
    }
    
    fputs($fp, $query);
    
    $response = '';
    while (!feof($fp))
    {
        $response .= fread($fp,4096);
    }
    fclose($fp);
    
    $resp = explode("\r\n\r\n",$response,2);
    $rccd = explode(' ',$resp[0],3); // array('HTTP/1.1','200','OK\r\n...')
    $rc = (integer)$rccd[1];
    
    // Redirect
    switch ($rc)
    {
        case 302: // Moved Temporarily
        case 301: // Moved Permanently
            if (preg_match('/^Location: (.+)$/m',$resp[0],$matches)
                and --$redirect_max > 0)
            {
                $url = trim($matches[1]);
                if (!preg_match('/^https?:\//',$url)) // no scheme
                {
                    if ($url{0} != '/') // Relative path
                    {
                        // to Absolute path
                        $url = substr($url_path,0,strrpos($url_path,'/')).'/'.$url;
                    }
                    // add sheme,host
                    $url = $url_base.$url;
                } 
                return http_request($url,$method,$headers,$post,$redirect_max);
            }
    }
    
    return array(
        'query'  => $query,   // Query String
        'rc'     => $rc,      // Response Code
        'header' => $resp[0], // Header
        'data'   => $resp[1]  // Data
    );
}
// プロキシを経由する必要があるかどうか判定
function via_proxy($host)
{
    global $use_proxy,$no_proxy;
    static $ip_pattern = '/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\/(.+))?$/';
    
    if (!$use_proxy)
    {
        return FALSE;
    }
    $ip = gethostbyname($host);
    $l_ip = ip2long($ip);
    $valid = (is_long($l_ip) and long2ip($l_ip) == $ip); // valid ip address
    
    foreach ($no_proxy as $network)
    {
        if ($valid and preg_match($ip_pattern,$network,$matches))
        {
            $l_net = ip2long($matches[1]);
            $mask = array_key_exists(2,$matches) ? $matches[2] : 32;
            $mask = is_numeric($mask) ?
                pow(2,32) - pow(2,32 - $mask) : // "10.0.0.0/8"
                ip2long($mask);                 // "10.0.0.0/255.0.0.0"
            if (($l_ip & $mask) == $l_net)
            {
                return FALSE;
            }
        }
        else
        {
            if (preg_match('/'.preg_quote($network,'/').'/',$host))
            {
                return FALSE;
            }
        }
    }
    return TRUE;
}
?>