MDL-42808 library: Google API was resetting the timezone
[moodle.git] / lib / google / Google_Client.php
1 <?php
2 /*
3  * Copyright 2010 Google Inc.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
18 // Check for the required json and curl extensions, the Google APIs PHP Client
19 // won't function without them.
20 if (! function_exists('curl_init')) {
21   throw new Exception('Google PHP API Client requires the CURL PHP extension');
22 }
24 if (! function_exists('json_decode')) {
25   throw new Exception('Google PHP API Client requires the JSON PHP extension');
26 }
28 if (! function_exists('http_build_query')) {
29   throw new Exception('Google PHP API Client requires http_build_query()');
30 }
32 if (! ini_get('date.timezone') && function_exists('date_default_timezone_set')) {
33   // Library hack. See MDL-42808.
34   // date_default_timezone_set('UTC');
35 }
37 // hack around with the include paths a bit so the library 'just works'
38 set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path());
40 require_once "config.php";
41 // If a local configuration file is found, merge it's values with the default configuration
42 if (file_exists(dirname(__FILE__)  . '/local_config.php')) {
43   $defaultConfig = $apiConfig;
44   require_once (dirname(__FILE__)  . '/local_config.php');
45   $apiConfig = array_merge($defaultConfig, $apiConfig);
46 }
48 // Include the top level classes, they each include their own dependencies
49 require_once 'service/Google_Model.php';
50 require_once 'service/Google_Service.php';
51 require_once 'service/Google_ServiceResource.php';
52 require_once 'auth/Google_AssertionCredentials.php';
53 require_once 'auth/Google_Signer.php';
54 require_once 'auth/Google_P12Signer.php';
55 require_once 'service/Google_BatchRequest.php';
56 require_once 'external/URITemplateParser.php';
57 require_once 'auth/Google_Auth.php';
58 require_once 'cache/Google_Cache.php';
59 require_once 'io/Google_IO.php';
60 require_once('service/Google_MediaFileUpload.php');
62 /**
63  * The Google API Client
64  * http://code.google.com/p/google-api-php-client/
65  *
66  * @author Chris Chabot <chabotc@google.com>
67  * @author Chirag Shah <chirags@google.com>
68  */
69 class Google_Client {
70   /**
71    * @static
72    * @var Google_Auth $auth
73    */
74   static $auth;
76   /**
77    * @static
78    * @var Google_IO $io
79    */
80   static $io;
82   /**
83    * @static
84    * @var Google_Cache $cache
85    */
86   static $cache;
88   /**
89    * @static
90    * @var boolean $useBatch
91    */
92   static $useBatch = false;
94   /** @var array $scopes */
95   protected $scopes = array();
97   /** @var bool $useObjects */
98   protected $useObjects = false;
100   // definitions of services that are discovered.
101   protected $services = array();
103   // Used to track authenticated state, can't discover services after doing authenticate()
104   private $authenticated = false;
106   public function __construct($config = array()) {
107     global $apiConfig;
108     $apiConfig = array_merge($apiConfig, $config);
109     self::$cache = new $apiConfig['cacheClass']();
110     self::$auth = new $apiConfig['authClass']();
111     self::$io = new $apiConfig['ioClass']();
112   }
114   /**
115    * Add a service
116    */
117   public function addService($service, $version = false) {
118     global $apiConfig;
119     if ($this->authenticated) {
120       throw new Google_Exception('Cant add services after having authenticated');
121     }
122     $this->services[$service] = array();
123     if (isset($apiConfig['services'][$service])) {
124       // Merge the service descriptor with the default values
125       $this->services[$service] = array_merge($this->services[$service], $apiConfig['services'][$service]);
126     }
127   }
129   public function authenticate($code = null) {
130     $service = $this->prepareService();
131     $this->authenticated = true;
132     return self::$auth->authenticate($service, $code);
133   }
135   /**
136    * @return array
137    * @visible For Testing
138    */
139   public function prepareService() {
140     $service = array();
141     $scopes = array();
142     if ($this->scopes) {
143       $scopes = $this->scopes;
144     } else {
145       foreach ($this->services as $key => $val) {
146         if (isset($val['scope'])) {
147           if (is_array($val['scope'])) {
148             $scopes = array_merge($val['scope'], $scopes);
149           } else {
150             $scopes[] = $val['scope'];
151           }
152         } else {
153           $scopes[] = 'https://www.googleapis.com/auth/' . $key;
154         }
155         unset($val['discoveryURI']);
156         unset($val['scope']);
157         $service = array_merge($service, $val);
158       }
159     }
160     $service['scope'] = implode(' ', $scopes);
161     return $service;
162   }
164   /**
165    * Set the OAuth 2.0 access token using the string that resulted from calling authenticate()
166    * or Google_Client#getAccessToken().
167    * @param string $accessToken JSON encoded string containing in the following format:
168    * {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer",
169    *  "expires_in":3600, "id_token":"TOKEN", "created":1320790426}
170    */
171   public function setAccessToken($accessToken) {
172     if ($accessToken == null || 'null' == $accessToken) {
173       $accessToken = null;
174     }
175     self::$auth->setAccessToken($accessToken);
176   }
178   /**
179    * Set the type of Auth class the client should use.
180    * @param string $authClassName
181    */
182   public function setAuthClass($authClassName) {
183     self::$auth = new $authClassName();
184   }
186   /**
187    * Construct the OAuth 2.0 authorization request URI.
188    * @return string
189    */
190   public function createAuthUrl() {
191     $service = $this->prepareService();
192     return self::$auth->createAuthUrl($service['scope']);
193   }
195   /**
196    * Get the OAuth 2.0 access token.
197    * @return string $accessToken JSON encoded string in the following format:
198    * {"access_token":"TOKEN", "refresh_token":"TOKEN", "token_type":"Bearer",
199    *  "expires_in":3600,"id_token":"TOKEN", "created":1320790426}
200    */
201   public function getAccessToken() {
202     $token = self::$auth->getAccessToken();
203     return (null == $token || 'null' == $token) ? null : $token;
204   }
206   /**
207    * Returns if the access_token is expired.
208    * @return bool Returns True if the access_token is expired.
209    */
210   public function isAccessTokenExpired() {
211     return self::$auth->isAccessTokenExpired();
212   }
214   /**
215    * Set the developer key to use, these are obtained through the API Console.
216    * @see http://code.google.com/apis/console-help/#generatingdevkeys
217    * @param string $developerKey
218    */
219   public function setDeveloperKey($developerKey) {
220     self::$auth->setDeveloperKey($developerKey);
221   }
223   /**
224    * Set OAuth 2.0 "state" parameter to achieve per-request customization.
225    * @see http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.1.2.2
226    * @param string $state
227    */
228   public function setState($state) {
229     self::$auth->setState($state);
230   }
232   /**
233    * @param string $accessType Possible values for access_type include:
234    *  {@code "offline"} to request offline access from the user. (This is the default value)
235    *  {@code "online"} to request online access from the user.
236    */
237   public function setAccessType($accessType) {
238     self::$auth->setAccessType($accessType);
239   }
241   /**
242    * @param string $approvalPrompt Possible values for approval_prompt include:
243    *  {@code "force"} to force the approval UI to appear. (This is the default value)
244    *  {@code "auto"} to request auto-approval when possible.
245    */
246   public function setApprovalPrompt($approvalPrompt) {
247     self::$auth->setApprovalPrompt($approvalPrompt);
248   }
250   /**
251    * Set the application name, this is included in the User-Agent HTTP header.
252    * @param string $applicationName
253    */
254   public function setApplicationName($applicationName) {
255     global $apiConfig;
256     $apiConfig['application_name'] = $applicationName;
257   }
259   /**
260    * Set the OAuth 2.0 Client ID.
261    * @param string $clientId
262    */
263   public function setClientId($clientId) {
264     global $apiConfig;
265     $apiConfig['oauth2_client_id'] = $clientId;
266     self::$auth->clientId = $clientId;
267   }
269   /**
270    * Get the OAuth 2.0 Client ID.
271    */
272   public function getClientId() {
273     return self::$auth->clientId;
274   }
275   
276   /**
277    * Set the OAuth 2.0 Client Secret.
278    * @param string $clientSecret
279    */
280   public function setClientSecret($clientSecret) {
281     global $apiConfig;
282     $apiConfig['oauth2_client_secret'] = $clientSecret;
283     self::$auth->clientSecret = $clientSecret;
284   }
286   /**
287    * Get the OAuth 2.0 Client Secret.
288    */
289   public function getClientSecret() {
290     return self::$auth->clientSecret;
291   }
293   /**
294    * Set the OAuth 2.0 Redirect URI.
295    * @param string $redirectUri
296    */
297   public function setRedirectUri($redirectUri) {
298     global $apiConfig;
299     $apiConfig['oauth2_redirect_uri'] = $redirectUri;
300     self::$auth->redirectUri = $redirectUri;
301   }
303   /**
304    * Get the OAuth 2.0 Redirect URI.
305    */
306   public function getRedirectUri() {
307     return self::$auth->redirectUri;
308   }
310   /**
311    * Fetches a fresh OAuth 2.0 access token with the given refresh token.
312    * @param string $refreshToken
313    * @return void
314    */
315   public function refreshToken($refreshToken) {
316     self::$auth->refreshToken($refreshToken);
317   }
319   /**
320    * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
321    * token, if a token isn't provided.
322    * @throws Google_AuthException
323    * @param string|null $token The token (access token or a refresh token) that should be revoked.
324    * @return boolean Returns True if the revocation was successful, otherwise False.
325    */
326   public function revokeToken($token = null) {
327     self::$auth->revokeToken($token);
328   }
330   /**
331    * Verify an id_token. This method will verify the current id_token, if one
332    * isn't provided.
333    * @throws Google_AuthException
334    * @param string|null $token The token (id_token) that should be verified.
335    * @return Google_LoginTicket Returns an apiLoginTicket if the verification was
336    * successful.
337    */
338   public function verifyIdToken($token = null) {
339     return self::$auth->verifyIdToken($token);
340   }
342   /**
343    * @param Google_AssertionCredentials $creds
344    * @return void
345    */
346   public function setAssertionCredentials(Google_AssertionCredentials $creds) {
347     self::$auth->setAssertionCredentials($creds);
348   }
350   /**
351    * This function allows you to overrule the automatically generated scopes,
352    * so that you can ask for more or less permission in the auth flow
353    * Set this before you call authenticate() though!
354    * @param array $scopes, ie: array('https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/moderator')
355    */
356   public function setScopes($scopes) {
357     $this->scopes = is_string($scopes) ? explode(" ", $scopes) : $scopes;
358   }
360   /**
361    * Declare if objects should be returned by the api service classes.
362    *
363    * @param boolean $useObjects True if objects should be returned by the service classes.
364    * False if associative arrays should be returned (default behavior).
365    * @experimental
366    */
367   public function setUseObjects($useObjects) {
368     global $apiConfig;
369     $apiConfig['use_objects'] = $useObjects;
370   }
372   /**
373    * Declare if objects should be returned by the api service classes.
374    *
375    * @param boolean $useBatch True if the experimental batch support should
376    * be enabled. Defaults to False.
377    * @experimental
378    */
379   public function setUseBatch($useBatch) {
380     self::$useBatch = $useBatch;
381   }
383   /**
384    * @static
385    * @return Google_Auth the implementation of apiAuth.
386    */
387   public static function getAuth() {
388     return Google_Client::$auth;
389   }
391   /**
392    * @static
393    * @return Google_IO the implementation of apiIo.
394    */
395   public static function getIo() {
396     return Google_Client::$io;
397   }
399   /**
400    * @return Google_Cache the implementation of apiCache.
401    */
402   public function getCache() {
403     return Google_Client::$cache;
404   }
407 // Exceptions that the Google PHP API Library can throw
408 class Google_Exception extends Exception {}
409 class Google_AuthException extends Google_Exception {}
410 class Google_CacheException extends Google_Exception {}
411 class Google_IOException extends Google_Exception {}
412 class Google_ServiceException extends Google_Exception {
413   /**
414    * Optional list of errors returned in a JSON body of an HTTP error response.
415    */
416   protected $errors = array();
418   /**
419    * Override default constructor to add ability to set $errors.
420    *
421    * @param string $message
422    * @param int $code
423    * @param Exception|null $previous
424    * @param [{string, string}] errors List of errors returned in an HTTP
425    * response.  Defaults to [].
426    */
427   public function __construct($message, $code = 0, Exception $previous = null,
428                               $errors = array()) {
429     if(version_compare(PHP_VERSION, '5.3.0') >= 0) {
430       parent::__construct($message, $code, $previous);
431     } else {
432       parent::__construct($message, $code);
433     }
434     
435     $this->errors = $errors;
436   }
438   /**
439    * An example of the possible errors returned.
440    *
441    * {
442    *   "domain": "global",
443    *   "reason": "authError",
444    *   "message": "Invalid Credentials",
445    *   "locationType": "header",
446    *   "location": "Authorization",
447    * }
448    *
449    * @return [{string, string}] List of errors return in an HTTP response or [].
450    */
451   public function getErrors() {
452     return $this->errors;
453   }