1<?php
2/**
3 * Copyright 2007-2016 Horde LLC (http://www.horde.org/)
4 *
5 * @author   Chuck Hagenbuch <chuck@horde.org>
6 * @license  http://www.horde.org/licenses/bsd BSD
7 * @category Horde
8 * @package  Http
9 */
10
11/**
12 * @author   Chuck Hagenbuch <chuck@horde.org>
13 * @license  http://www.horde.org/licenses/bsd BSD
14 * @category Horde
15 * @package  Http
16 */
17class Horde_Http_Request_Curl extends Horde_Http_Request_Base
18{
19    /**
20     * Map of HTTP authentication schemes from Horde_Http constants to
21     * HTTP_AUTH constants.
22     *
23     * @var array
24     */
25    protected $_httpAuthSchemes = array(
26        Horde_Http::AUTH_ANY => CURLAUTH_ANY,
27        Horde_Http::AUTH_BASIC => CURLAUTH_BASIC,
28        Horde_Http::AUTH_DIGEST => CURLAUTH_DIGEST,
29        Horde_Http::AUTH_GSSNEGOTIATE => CURLAUTH_GSSNEGOTIATE,
30        Horde_Http::AUTH_NTLM => CURLAUTH_NTLM,
31    );
32
33    /**
34     * Constructor
35     *
36     * @throws Horde_Http_Exception
37     */
38    public function __construct($args = array())
39    {
40        if (!extension_loaded('curl')) {
41            throw new Horde_Http_Exception('The curl extension is not installed. See http://php.net/curl.installation');
42        }
43
44        parent::__construct($args);
45    }
46
47    /**
48     * Send this HTTP request
49     *
50     * @throws Horde_Http_Exception
51     * @return Horde_Http_Response_Base
52     */
53    public function send()
54    {
55        $curl = curl_init();
56        curl_setopt($curl, CURLOPT_URL, (string)$this->uri);
57        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
58        curl_setopt($curl, CURLOPT_HEADER, true);
59        curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->method);
60        curl_setopt($curl, CURLOPT_TIMEOUT, $this->timeout);
61        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifyPeer);
62        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyPeer ? 2 : 0);
63
64        // User-Agent
65        curl_setopt($curl, CURLOPT_USERAGENT, $this->userAgent);
66
67        // Redirects
68        if ($this->redirects) {
69            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
70            curl_setopt($curl, CURLOPT_MAXREDIRS, $this->redirects);
71        } else {
72            curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
73            curl_setopt($curl, CURLOPT_MAXREDIRS, 0);
74        }
75
76        $data = $this->data;
77        if (is_array($data)) {
78            // If we don't set POSTFIELDS to a string, and the first value
79            // begins with @, it will be treated as a filename, and the proper
80            // POST data isn't passed.
81            $data = http_build_query($data);
82        }
83        if ($data) {
84            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
85        }
86
87        // Proxy settings
88        if ($this->proxyServer) {
89            curl_setopt($curl, CURLOPT_PROXY, $this->proxyServer);
90            if ($this->proxyPort) {
91                curl_setopt($curl, CURLOPT_PROXYPORT, $this->proxyPort);
92            }
93            if ($this->proxyUsername && $this->proxyPassword) {
94                curl_setopt($curl, CURLOPT_PROXYUSERPWD, $this->proxyUsername . ':' . $this->proxyPassword);
95                curl_setopt($curl, CURLOPT_PROXYAUTH, $this->_httpAuthScheme($this->proxyAuthenticationScheme));
96            }
97            if ($this->proxyType == Horde_Http::PROXY_SOCKS5) {
98                curl_setopt($curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
99            } else if ($this->proxyType != Horde_Http::PROXY_HTTP) {
100                throw new Horde_Http_Exception(sprintf('Proxy type %s not supported by this request type!', $this->proxyType));
101            }
102        }
103
104        // Authentication settings
105        if ($this->username) {
106            curl_setopt($curl, CURLOPT_USERPWD, $this->username . ':' . $this->password);
107            curl_setopt($curl, CURLOPT_HTTPAUTH, $this->_httpAuthScheme($this->authenticationScheme));
108        }
109
110        // Concatenate the headers
111        $hdr = array();
112        $headers = $this->headers;
113        if (empty($headers['Expect'])) {
114            $headers['Expect'] = '';
115        }
116        foreach ($headers as $header => $value) {
117            $hdr[] = $header . ': ' . $value;
118        }
119
120        curl_setopt($curl, CURLOPT_HTTPHEADER, $hdr);
121
122        $result = curl_exec($curl);
123        if ($result === false) {
124            throw new Horde_Http_Exception(curl_error($curl), curl_errno($curl));
125        }
126        $info = curl_getinfo($curl);
127        return new Horde_Http_Response_Curl((string)$this->uri, $result, $info);
128    }
129
130    /**
131     * Translate a Horde_Http::AUTH_* constant to CURLAUTH_*
132     *
133     * @param const
134     * @throws Horde_Http_Exception
135     * @return const
136     */
137    protected function _httpAuthScheme($httpAuthScheme)
138    {
139        if (!isset($this->_httpAuthSchemes[$httpAuthScheme])) {
140            throw new Horde_Http_Exception('Unsupported authentication scheme (' . $httpAuthScheme . ')');
141        }
142        return $this->_httpAuthSchemes[$httpAuthScheme];
143    }
144}
145