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