654b9de5701429eb0f9ac1971c795cb7c87d7ee4
[moodle.git] / lib / google / curlio.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * This file contains the class moodle_google_curlio.
19  *
20  * @package core_google
21  * @copyright 2013 Frédéric Massart
22  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
26 require_once($CFG->libdir . '/filelib.php');
27 require_once($CFG->libdir . '/google/io/Google_IO.php');
29 /**
30  * Class moodle_google_curlio.
31  *
32  * The initial purpose of this class is to add support for our
33  * class curl in Google_CurlIO.
34  *
35  * @package core_google
36  * @copyright 2013 Frédéric Massart
37  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38 */
39 class moodle_google_curlio extends Google_CurlIO {
41     /** @var array associate array of constant value and their name. */
42     private static $constants = null;
44     /**
45      * Private variables have to be redefined here.
46      */
47     private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null);
48     private static $HOP_BY_HOP = array(
49         'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',
50         'te', 'trailers', 'transfer-encoding', 'upgrade');
51     private $curlParams = array(
52         'CURLOPT_RETURNTRANSFER' => true,
53         'CURLOPT_FOLLOWLOCATION' => 0,
54         'CURLOPT_FAILONERROR' => false,
55         'CURLOPT_SSL_VERIFYPEER' => true,
56         'CURLOPT_HEADER' => true,
57         'CURLOPT_VERBOSE' => false
58     );
60     /**
61      * Send the request via our curl object.
62      *
63      * @param curl $curl prepared curl object.
64      * @param Google_HttpRequest $request The request.
65      * @return string result of the request.
66      */
67     private function do_request($curl, $request) {
68         $url = $request->getUrl();
69         $method = $request->getRequestMethod();
70         switch (strtoupper($method)) {
71             case 'POST':
72                 $ret = $curl->post($url, $request->getPostBody());
73                 break;
74             case 'GET':
75                 $ret = $curl->get($url);
76                 break;
77             case 'HEAD':
78                 $ret = $curl->head($url);
79                 break;
80             case 'PUT':
81                 $ret = $curl->put($url);
82                 break;
83             default:
84                 throw new coding_exception('Unknown request type: ' . $method);
85                 break;
86         }
87         return $ret;
88     }
90     /**
91      * Send an API request to Google.
92      *
93      * This method overwrite the parent one so that the Google SDK will use our class
94      * curl to proceed with the requests. This allows us to have control over the
95      * proxy parameters and other stuffs.
96      *
97      * Note that the caching support of the Google SDK has been removed from this function.
98      *
99      * @param Google_HttpRequest $request the http request to be executed
100      * @return Google_HttpRequest http request with the response http code, response
101      * headers and response body filled in
102      * @throws Google_IOException on curl or IO error
103      */
104     public function makeRequest(Google_HttpRequest $request) {
106         if (array_key_exists($request->getRequestMethod(), self::$ENTITY_HTTP_METHODS)) {
107             $request = $this->processEntityRequest($request);
108         }
110         $curl = new curl();
111         $curl->setopt($this->curlParams);
112         $curl->setopt(array('CURLOPT_URL' => $request->getUrl()));
114         $requestHeaders = $request->getRequestHeaders();
115         if ($requestHeaders && is_array($requestHeaders)) {
116             $parsed = array();
117             foreach ($requestHeaders as $k => $v) {
118                 $parsed[] = "$k: $v";
119             }
120             $curl->setHeader($parsed);
121         }
123         $curl->setopt(array(
124             'CURLOPT_CUSTOMREQUEST' => $request->getRequestMethod(),
125             'CURLOPT_USERAGENT' => $request->getUserAgent()
126         ));
128         $respdata = $this->do_request($curl, $request);
130         // Retry if certificates are missing.
131         if ($curl->get_errno() == CURLE_SSL_CACERT) {
132             error_log('SSL certificate problem, verify that the CA cert is OK.' .
133                     ' Retrying with the CA cert bundle from google-api-php-client.');
134             $curl->setopt(array('CURLOPT_CAINFO' => dirname(__FILE__) . '/io/cacerts.pem'));
135             $respdata = $this->do_request($curl, $request);
136         }
138         $infos = $curl->get_info();
139         $respheadersize = $infos['header_size'];
140         $resphttpcode = (int) $infos['http_code'];
141         $curlerrornum = $curl->get_errno();
142         $curlerror = $curl->error;
144         if ($curlerrornum != CURLE_OK) {
145           throw new Google_IOException("HTTP Error: ($resphttpcode) $curlerror");
146         }
148         // Parse out the raw response into usable bits.
149         list($responseHeaders, $responseBody) = self::parseHttpResponse($respdata, $respheadersize);
151         // Fill in the apiHttpRequest with the response values.
152         $request->setResponseHttpCode($resphttpcode);
153         $request->setResponseHeaders($responseHeaders);
154         $request->setResponseBody($responseBody);
156         return $request;
157     }
159     /**
160     * Set curl options.
161     *
162     * We overwrite this method to ensure that the data passed meets
163     * the requirement of our curl implementation and so that the keys
164     * are strings, and not curl constants.
165     *
166     * @param array $optCurlParams Multiple options used by a cURL session.
167     * @return void
168     */
169     public function setOptions($optCurlParams) {
170         $safeParams = array();
171         foreach ($optCurlParams as $name => $value) {
172             if (!is_string($name)) {
173                 $name = $this->get_option_name_from_constant($name);
174             }
175             $safeParams[$name] = $vale;
176         }
177         parent::setOptions($safeParams);
178     }
180     /**
181      * Return the name of an option based on the constant value.
182      *
183      * @param int $constant value of a CURL constant.
184      * @return string name of the constant if found, or throws exception.
185      * @throws coding_exception when the constant is not found.
186      * @since 2.5
187      */
188     public function get_option_name_from_constant($constant) {
189         if (is_null(self::$constants)) {
190             $constants = get_defined_constants(true);
191             $constants = isset($constants['curl']) ? $constants['curl'] : array();
192             $constants = array_flip($constants);
193             self::$constants = $constants;
194         }
195         if (isset(self::$constants[$constant])) {
196             return self::$constants[$constant];
197         }
198         throw new coding_exception('Unknown curl constant value: ' . $constant);
199     }