Google Analytics for WordPress by MonsterInsights - Version 5.2.4

Version Description

Release Date: December 15th, 2014

  • Enhancements:
    • Moved from OAuth1 to OAuth2 for Google Analytics API requests.
    • Open authentication and reauthentication for Google in a new window.
    • Force reloading of CSS and JS on version change of plugin.
    • Refactored fetching profiles from Google Analytics.
Download this release

Release Info

Developer joostdevalk
Plugin Icon 128x128 Google Analytics for WordPress by MonsterInsights
Version 5.2.4
Comparing to
See all releases

Code changes from version 5.2.3 to 5.2.4

Files changed (62) hide show
  1. admin/api-libs/google/Google_Client.php +471 -0
  2. admin/api-libs/google/auth/Google_AssertionCredentials.php +103 -0
  3. admin/api-libs/google/auth/Google_Auth.php +36 -0
  4. admin/api-libs/google/auth/Google_AuthNone.php +48 -0
  5. admin/api-libs/google/auth/Google_LoginTicket.php +63 -0
  6. admin/api-libs/google/auth/Google_OAuth2.php +455 -0
  7. admin/api-libs/google/auth/Google_P12Signer.php +70 -0
  8. admin/api-libs/google/auth/Google_PemVerifier.php +66 -0
  9. admin/api-libs/google/auth/Google_Signer.php +30 -0
  10. admin/api-libs/google/auth/Google_Verifier.php +31 -0
  11. admin/api-libs/google/cache/Google_ApcCache.php +98 -0
  12. admin/api-libs/google/cache/Google_Cache.php +55 -0
  13. admin/api-libs/google/cache/Google_FileCache.php +137 -0
  14. admin/api-libs/google/cache/Google_MemcacheCache.php +130 -0
  15. admin/api-libs/google/class-api-google.php +25 -0
  16. admin/api-libs/google/config.php +83 -0
  17. admin/api-libs/google/external/URITemplateParser.php +209 -0
  18. admin/api-libs/google/io/Google_CacheParser.php +173 -0
  19. admin/api-libs/google/io/Google_CurlIO.php +198 -0
  20. admin/api-libs/google/io/Google_HttpRequest.php +304 -0
  21. admin/api-libs/google/io/Google_HttpStreamIO.php +170 -0
  22. admin/api-libs/google/io/Google_IO.php +161 -0
  23. admin/api-libs/google/io/Google_REST.php +128 -0
  24. admin/api-libs/google/io/cacerts.pem +738 -0
  25. admin/api-libs/google/service/Google_BatchRequest.php +110 -0
  26. admin/api-libs/google/service/Google_MediaFileUpload.php +262 -0
  27. admin/api-libs/google/service/Google_Model.php +115 -0
  28. admin/api-libs/google/service/Google_Service.php +22 -0
  29. admin/api-libs/google/service/Google_ServiceResource.php +205 -0
  30. admin/api-libs/google/service/Google_Utils.php +117 -0
  31. admin/api-libs/googleanalytics/class-api-googleanalytics.php +2 -1
  32. admin/api-libs/googleanalytics/class-google-analytics-client.php +177 -0
  33. admin/api-libs/googleanalytics/class-googleanalytics-reporting.php +7 -66
  34. admin/api-libs/oauth/class-api-oauth.php +0 -41
  35. admin/api-libs/oauth/class-oauth-consumer.php +0 -22
  36. admin/api-libs/oauth/class-oauth-datastore.php +0 -32
  37. admin/api-libs/oauth/class-oauth-exception.php +0 -9
  38. admin/api-libs/oauth/class-oauth-request.php +0 -277
  39. admin/api-libs/oauth/class-oauth-server.php +0 -235
  40. admin/api-libs/oauth/class-oauth-signature-method-hmac-sha1.php +0 -35
  41. admin/api-libs/oauth/class-oauth-signature-method-plaintext.php +0 -40
  42. admin/api-libs/oauth/class-oauth-signature-method-rsa-sha1.php +0 -74
  43. admin/api-libs/oauth/class-oauth-signature-method.php +0 -63
  44. admin/api-libs/oauth/class-oauth-token.php +0 -37
  45. admin/api-libs/oauth/class-oauth-util.php +0 -169
  46. admin/class-admin-assets.php +11 -11
  47. admin/class-admin-form.php +273 -0
  48. admin/class-admin.php +25 -264
  49. admin/class-google-analytics.php +90 -313
  50. admin/dashboards/class-admin-dashboards-api-options.php +1 -1
  51. admin/dashboards/class-admin-dashboards-collector.php +22 -29
  52. admin/pages/dashboard.php +25 -0
  53. admin/pages/settings.php +32 -25
  54. admin/wp-gdata/index.php +0 -2
  55. admin/wp-gdata/wp-gdata.php +0 -108
  56. assets/css/yoast_ga_styles.css +23 -4
  57. assets/css/yoast_ga_styles.min.css +1 -1
  58. assets/js/yoast_ga_admin.js +7 -0
  59. assets/js/yoast_ga_admin.min.js +1 -1
  60. googleanalytics.php +2 -2
  61. includes/class-autoload.php +0 -2
  62. readme.txt +17 -7
admin/api-libs/google/Google_Client.php ADDED
@@ -0,0 +1,471 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.
20
+ if (! function_exists('json_decode')) {
21
+ throw new Exception('Google PHP API Client requires the JSON PHP extension');
22
+ }
23
+
24
+ if (! function_exists('http_build_query')) {
25
+ throw new Exception('Google PHP API Client requires http_build_query()');
26
+ }
27
+
28
+ if (! 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'
33
+ set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path());
34
+
35
+ require_once "config.php";
36
+ // If a local configuration file is found, merge it's values with the default configuration
37
+ if (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
44
+ require_once 'service/Google_Model.php';
45
+ require_once 'service/Google_Service.php';
46
+ require_once 'service/Google_ServiceResource.php';
47
+ require_once 'auth/Google_AssertionCredentials.php';
48
+ require_once 'auth/Google_Signer.php';
49
+ require_once 'auth/Google_P12Signer.php';
50
+ require_once 'service/Google_BatchRequest.php';
51
+ require_once 'external/URITemplateParser.php';
52
+ require_once 'auth/Google_Auth.php';
53
+ require_once 'cache/Google_Cache.php';
54
+ require_once 'io/Google_IO.php';
55
+ require_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
+ */
64
+ class 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
425
+ class Google_Exception extends Exception {}
426
+ class Google_AuthException extends Google_Exception {}
427
+ class Google_CacheException extends Google_Exception {}
428
+ class Google_IOException extends Google_Exception {}
429
+ class 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
+ }
admin/api-libs/google/auth/Google_AssertionCredentials.php ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2012 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
+ /**
19
+ * Credentials object used for OAuth 2.0 Signed JWT assertion grants.
20
+ *
21
+ * @author Chirag Shah <chirags@google.com>
22
+ */
23
+ class Google_AssertionCredentials {
24
+ const MAX_TOKEN_LIFETIME_SECS = 3600;
25
+
26
+ public $serviceAccountName;
27
+ public $scopes;
28
+ public $privateKey;
29
+ public $privateKeyPassword;
30
+ public $assertionType;
31
+ public $sub;
32
+ /**
33
+ * @deprecated
34
+ * @link http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06
35
+ */
36
+ public $prn;
37
+
38
+ /**
39
+ * @param $serviceAccountName
40
+ * @param $scopes array List of scopes
41
+ * @param $privateKey
42
+ * @param string $privateKeyPassword
43
+ * @param string $assertionType
44
+ * @param bool|string $sub The email address of the user for which the
45
+ * application is requesting delegated access.
46
+ */
47
+ public function __construct(
48
+ $serviceAccountName,
49
+ $scopes,
50
+ $privateKey,
51
+ $privateKeyPassword = 'notasecret',
52
+ $assertionType = 'http://oauth.net/grant_type/jwt/1.0/bearer',
53
+ $sub = false) {
54
+ $this->serviceAccountName = $serviceAccountName;
55
+ $this->scopes = is_string($scopes) ? $scopes : implode(' ', $scopes);
56
+ $this->privateKey = $privateKey;
57
+ $this->privateKeyPassword = $privateKeyPassword;
58
+ $this->assertionType = $assertionType;
59
+ $this->sub = $sub;
60
+ $this->prn = $sub;
61
+ }
62
+
63
+ public function generateAssertion() {
64
+ $now = time();
65
+
66
+ $jwtParams = array(
67
+ 'aud' => Google_OAuth2::OAUTH2_TOKEN_URI,
68
+ 'scope' => $this->scopes,
69
+ 'iat' => $now,
70
+ 'exp' => $now + self::MAX_TOKEN_LIFETIME_SECS,
71
+ 'iss' => $this->serviceAccountName,
72
+ );
73
+
74
+ if ($this->sub !== false) {
75
+ $jwtParams['sub'] = $this->sub;
76
+ } else if ($this->prn !== false) {
77
+ $jwtParams['prn'] = $this->prn;
78
+ }
79
+
80
+ return $this->makeSignedJwt($jwtParams);
81
+ }
82
+
83
+ /**
84
+ * Creates a signed JWT.
85
+ * @param array $payload
86
+ * @return string The signed JWT.
87
+ */
88
+ private function makeSignedJwt($payload) {
89
+ $header = array('typ' => 'JWT', 'alg' => 'RS256');
90
+
91
+ $segments = array(
92
+ Google_Utils::urlSafeB64Encode(json_encode($header)),
93
+ Google_Utils::urlSafeB64Encode(json_encode($payload))
94
+ );
95
+
96
+ $signingInput = implode('.', $segments);
97
+ $signer = new Google_P12Signer($this->privateKey, $this->privateKeyPassword);
98
+ $signature = $signer->sign($signingInput);
99
+ $segments[] = Google_Utils::urlSafeB64Encode($signature);
100
+
101
+ return implode(".", $segments);
102
+ }
103
+ }
admin/api-libs/google/auth/Google_Auth.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ require_once "Google_AuthNone.php";
19
+ require_once "Google_OAuth2.php";
20
+
21
+ /**
22
+ * Abstract class for the Authentication in the API client
23
+ * @author Chris Chabot <chabotc@google.com>
24
+ *
25
+ */
26
+ abstract class Google_Auth {
27
+ abstract public function authenticate($service);
28
+ abstract public function sign(Google_HttpRequest $request);
29
+ abstract public function createAuthUrl($scope);
30
+
31
+ abstract public function getAccessToken();
32
+ abstract public function setAccessToken($accessToken);
33
+ abstract public function setDeveloperKey($developerKey);
34
+ abstract public function refreshToken($refreshToken);
35
+ abstract public function revokeToken();
36
+ }
admin/api-libs/google/auth/Google_AuthNone.php ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
19
+ * Do-nothing authentication implementation, use this if you want to make un-authenticated calls
20
+ * @author Chris Chabot <chabotc@google.com>
21
+ * @author Chirag Shah <chirags@google.com>
22
+ */
23
+ class Google_AuthNone extends Google_Auth {
24
+ public $key = null;
25
+
26
+ public function __construct() {
27
+ global $apiConfig;
28
+ if (!empty($apiConfig['developer_key'])) {
29
+ $this->setDeveloperKey($apiConfig['developer_key']);
30
+ }
31
+ }
32
+
33
+ public function setDeveloperKey($key) {$this->key = $key;}
34
+ public function authenticate($service) {/*noop*/}
35
+ public function setAccessToken($accessToken) {/* noop*/}
36
+ public function getAccessToken() {return null;}
37
+ public function createAuthUrl($scope) {return null;}
38
+ public function refreshToken($refreshToken) {/* noop*/}
39
+ public function revokeToken() {/* noop*/}
40
+
41
+ public function sign(Google_HttpRequest $request) {
42
+ if ($this->key) {
43
+ $request->setUrl($request->getUrl() . ((strpos($request->getUrl(), '?') === false) ? '?' : '&')
44
+ . 'key='.urlencode($this->key));
45
+ }
46
+ return $request;
47
+ }
48
+ }
admin/api-libs/google/auth/Google_LoginTicket.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ /**
19
+ * Class to hold information about an authenticated login.
20
+ *
21
+ * @author Brian Eaton <beaton@google.com>
22
+ */
23
+ class Google_LoginTicket {
24
+ const USER_ATTR = "id";
25
+
26
+ // Information from id token envelope.
27
+ private $envelope;
28
+
29
+ // Information from id token payload.
30
+ private $payload;
31
+
32
+ /**
33
+ * Creates a user based on the supplied token.
34
+ *
35
+ * @param string $envelope Header from a verified authentication token.
36
+ * @param string $payload Information from a verified authentication token.
37
+ */
38
+ public function __construct($envelope, $payload) {
39
+ $this->envelope = $envelope;
40
+ $this->payload = $payload;
41
+ }
42
+
43
+ /**
44
+ * Returns the numeric identifier for the user.
45
+ * @throws Google_AuthException
46
+ * @return
47
+ */
48
+ public function getUserId() {
49
+ if (array_key_exists(self::USER_ATTR, $this->payload)) {
50
+ return $this->payload[self::USER_ATTR];
51
+ }
52
+ throw new Google_AuthException("No user_id in token");
53
+ }
54
+
55
+ /**
56
+ * Returns attributes from the login ticket. This can contain
57
+ * various information about the user session.
58
+ * @return array
59
+ */
60
+ public function getAttributes() {
61
+ return array("envelope" => $this->envelope, "payload" => $this->payload);
62
+ }
63
+ }
admin/api-libs/google/auth/Google_OAuth2.php ADDED
@@ -0,0 +1,455 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2008 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
+ require_once "Google_Verifier.php";
19
+ require_once "Google_LoginTicket.php";
20
+ require_once "service/Google_Utils.php";
21
+
22
+ /**
23
+ * Authentication class that deals with the OAuth 2 web-server authentication flow
24
+ *
25
+ * @author Chris Chabot <chabotc@google.com>
26
+ * @author Chirag Shah <chirags@google.com>
27
+ *
28
+ */
29
+ class Google_OAuth2 extends Google_Auth {
30
+ public $clientId;
31
+ public $clientSecret;
32
+ public $developerKey;
33
+ public $token;
34
+ public $redirectUri;
35
+ public $state;
36
+ public $accessType = 'offline';
37
+ public $approvalPrompt = 'force';
38
+ public $requestVisibleActions;
39
+
40
+ /** @var Google_AssertionCredentials $assertionCredentials */
41
+ public $assertionCredentials;
42
+
43
+ const OAUTH2_REVOKE_URI = 'https://accounts.google.com/o/oauth2/revoke';
44
+ const OAUTH2_TOKEN_URI = 'https://accounts.google.com/o/oauth2/token';
45
+ const OAUTH2_AUTH_URL = 'https://accounts.google.com/o/oauth2/auth';
46
+ const OAUTH2_FEDERATED_SIGNON_CERTS_URL = 'https://www.googleapis.com/oauth2/v1/certs';
47
+ const CLOCK_SKEW_SECS = 300; // five minutes in seconds
48
+ const AUTH_TOKEN_LIFETIME_SECS = 300; // five minutes in seconds
49
+ const MAX_TOKEN_LIFETIME_SECS = 86400; // one day in seconds
50
+
51
+ /**
52
+ * Instantiates the class, but does not initiate the login flow, leaving it
53
+ * to the discretion of the caller (which is done by calling authenticate()).
54
+ */
55
+ public function __construct() {
56
+ global $apiConfig;
57
+
58
+ if (! empty($apiConfig['developer_key'])) {
59
+ $this->developerKey = $apiConfig['developer_key'];
60
+ }
61
+
62
+ if (! empty($apiConfig['oauth2_client_id'])) {
63
+ $this->clientId = $apiConfig['oauth2_client_id'];
64
+ }
65
+
66
+ if (! empty($apiConfig['oauth2_client_secret'])) {
67
+ $this->clientSecret = $apiConfig['oauth2_client_secret'];
68
+ }
69
+
70
+ if (! empty($apiConfig['oauth2_redirect_uri'])) {
71
+ $this->redirectUri = $apiConfig['oauth2_redirect_uri'];
72
+ }
73
+
74
+ if (! empty($apiConfig['oauth2_access_type'])) {
75
+ $this->accessType = $apiConfig['oauth2_access_type'];
76
+ }
77
+
78
+ if (! empty($apiConfig['oauth2_approval_prompt'])) {
79
+ $this->approvalPrompt = $apiConfig['oauth2_approval_prompt'];
80
+ }
81
+
82
+ }
83
+
84
+ /**
85
+ * @param $service
86
+ * @param string|null $code
87
+ * @throws Google_AuthException
88
+ * @return string
89
+ */
90
+ public function authenticate($service, $code = null) {
91
+ if (!$code && isset($_GET['code'])) {
92
+ $code = $_GET['code'];
93
+ }
94
+
95
+
96
+ if ($code) {
97
+ // We got here from the redirect from a successful authorization grant, fetch the access token
98
+ $request = Google_Client::$io->makeRequest(new Google_HttpRequest(self::OAUTH2_TOKEN_URI, 'POST', array(), array(
99
+ 'code' => $code,
100
+ 'grant_type' => 'authorization_code',
101
+ 'redirect_uri' => $this->redirectUri,
102
+ 'client_id' => $this->clientId,
103
+ 'client_secret' => $this->clientSecret
104
+ )));
105
+
106
+
107
+ if ($request->getResponseHttpCode() == 200) {
108
+ $this->setAccessToken($request->getResponseBody());
109
+ $this->token['created'] = time();
110
+ return $this->getAccessToken();
111
+ } else {
112
+ $response = $request->getResponseBody();
113
+ $decodedResponse = json_decode($response, true);
114
+ if ($decodedResponse != null && $decodedResponse['error']) {
115
+ $response = $decodedResponse['error'];
116
+ }
117
+ throw new Google_AuthException("Error fetching OAuth2 access token, message: '$response'", $request->getResponseHttpCode());
118
+ }
119
+ }
120
+
121
+ $authUrl = $this->createAuthUrl($service['scope']);
122
+ header('Location: ' . $authUrl);
123
+ return true;
124
+ }
125
+
126
+ /**
127
+ * Create a URL to obtain user authorization.
128
+ * The authorization endpoint allows the user to first
129
+ * authenticate, and then grant/deny the access request.
130
+ * @param string $scope The scope is expressed as a list of space-delimited strings.
131
+ * @return string
132
+ */
133
+ public function createAuthUrl($scope) {
134
+ $params = array(
135
+ 'response_type=code',
136
+ 'redirect_uri=' . urlencode($this->redirectUri),
137
+ 'client_id=' . urlencode($this->clientId),
138
+ 'scope=' . urlencode($scope),
139
+ 'access_type=' . urlencode($this->accessType),
140
+ 'approval_prompt=' . urlencode($this->approvalPrompt),
141
+ );
142
+
143
+ // if the list of scopes contains plus.login, add request_visible_actions
144
+ // to auth URL
145
+ if(strpos($scope, 'plus.login') && count($this->requestVisibleActions) > 0) {
146
+ $params[] = 'request_visible_actions=' .
147
+ urlencode($this->requestVisibleActions);
148
+ }
149
+
150
+ if (isset($this->state)) {
151
+ $params[] = 'state=' . urlencode($this->state);
152
+ }
153
+ $params = implode('&', $params);
154
+ return self::OAUTH2_AUTH_URL . "?$params";
155
+ }
156
+
157
+ /**
158
+ * @param string $token
159
+ * @throws Google_AuthException
160
+ */
161
+ public function setAccessToken($token) {
162
+ $token = json_decode($token, true);
163
+ if ($token == null) {
164
+ throw new Google_AuthException('Could not json decode the token');
165
+ }
166
+ if (! isset($token['access_token'])) {
167
+ throw new Google_AuthException("Invalid token format");
168
+ }
169
+ $this->token = $token;
170
+ }
171
+
172
+ public function getAccessToken() {
173
+ return json_encode($this->token);
174
+ }
175
+
176
+ public function setDeveloperKey($developerKey) {
177
+ $this->developerKey = $developerKey;
178
+ }
179
+
180
+ public function setState($state) {
181
+ $this->state = $state;
182
+ }
183
+
184
+ public function setAccessType($accessType) {
185
+ $this->accessType = $accessType;
186
+ }
187
+
188
+ public function setApprovalPrompt($approvalPrompt) {
189
+ $this->approvalPrompt = $approvalPrompt;
190
+ }
191
+
192
+ public function setAssertionCredentials(Google_AssertionCredentials $creds) {
193
+ $this->assertionCredentials = $creds;
194
+ }
195
+
196
+ /**
197
+ * Include an accessToken in a given apiHttpRequest.
198
+ * @param Google_HttpRequest $request
199
+ * @return Google_HttpRequest
200
+ * @throws Google_AuthException
201
+ */
202
+ public function sign(Google_HttpRequest $request) {
203
+ // add the developer key to the request before signing it
204
+ if ($this->developerKey) {
205
+ $requestUrl = $request->getUrl();
206
+ $requestUrl .= (strpos($request->getUrl(), '?') === false) ? '?' : '&';
207
+ $requestUrl .= 'key=' . urlencode($this->developerKey);
208
+ $request->setUrl($requestUrl);
209
+ }
210
+
211
+ // Cannot sign the request without an OAuth access token.
212
+ if (null == $this->token && null == $this->assertionCredentials) {
213
+ return $request;
214
+ }
215
+
216
+ // Check if the token is set to expire in the next 30 seconds
217
+ // (or has already expired).
218
+ if ($this->isAccessTokenExpired()) {
219
+ if ($this->assertionCredentials) {
220
+ $this->refreshTokenWithAssertion();
221
+ } else {
222
+ if (! array_key_exists('refresh_token', $this->token)) {
223
+ throw new Google_AuthException("The OAuth 2.0 access token has expired, "
224
+ . "and a refresh token is not available. Refresh tokens are not "
225
+ . "returned for responses that were auto-approved.");
226
+ }
227
+ $this->refreshToken($this->token['refresh_token']);
228
+ }
229
+ }
230
+
231
+ // Add the OAuth2 header to the request
232
+ $request->setRequestHeaders(
233
+ array('Authorization' => 'Bearer ' . $this->token['access_token'])
234
+ );
235
+
236
+ return $request;
237
+ }
238
+
239
+ /**
240
+ * Fetches a fresh access token with the given refresh token.
241
+ * @param string $refreshToken
242
+ * @return void
243
+ */
244
+ public function refreshToken($refreshToken) {
245
+ $this->refreshTokenRequest(array(
246
+ 'client_id' => $this->clientId,
247
+ 'client_secret' => $this->clientSecret,
248
+ 'refresh_token' => $refreshToken,
249
+ 'grant_type' => 'refresh_token'
250
+ ));
251
+ }
252
+
253
+ /**
254
+ * Fetches a fresh access token with a given assertion token.
255
+ * @param Google_AssertionCredentials $assertionCredentials optional.
256
+ * @return void
257
+ */
258
+ public function refreshTokenWithAssertion($assertionCredentials = null) {
259
+ if (!$assertionCredentials) {
260
+ $assertionCredentials = $this->assertionCredentials;
261
+ }
262
+
263
+ $this->refreshTokenRequest(array(
264
+ 'grant_type' => 'assertion',
265
+ 'assertion_type' => $assertionCredentials->assertionType,
266
+ 'assertion' => $assertionCredentials->generateAssertion(),
267
+ ));
268
+ }
269
+
270
+ private function refreshTokenRequest($params) {
271
+ $http = new Google_HttpRequest(self::OAUTH2_TOKEN_URI, 'POST', array(), $params);
272
+ $request = Google_Client::$io->makeRequest($http);
273
+
274
+ $code = $request->getResponseHttpCode();
275
+ $body = $request->getResponseBody();
276
+ if (200 == $code) {
277
+ $token = json_decode($body, true);
278
+ if ($token == null) {
279
+ throw new Google_AuthException("Could not json decode the access token");
280
+ }
281
+
282
+ if (! isset($token['access_token']) || ! isset($token['expires_in'])) {
283
+ throw new Google_AuthException("Invalid token format");
284
+ }
285
+
286
+ $this->token['access_token'] = $token['access_token'];
287
+ $this->token['expires_in'] = $token['expires_in'];
288
+ $this->token['created'] = time();
289
+ } else {
290
+ throw new Google_AuthException("Error refreshing the OAuth2 token, message: '$body'", $code);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Revoke an OAuth2 access token or refresh token. This method will revoke the current access
296
+ * token, if a token isn't provided.
297
+ * @throws Google_AuthException
298
+ * @param string|null $token The token (access token or a refresh token) that should be revoked.
299
+ * @return boolean Returns True if the revocation was successful, otherwise False.
300
+ */
301
+ public function revokeToken($token = null) {
302
+ if (!$token) {
303
+ $token = $this->token['access_token'];
304
+ }
305
+ $request = new Google_HttpRequest(self::OAUTH2_REVOKE_URI, 'POST', array(), "token=$token");
306
+ $response = Google_Client::$io->makeRequest($request);
307
+ $code = $response->getResponseHttpCode();
308
+ if ($code == 200) {
309
+ $this->token = null;
310
+ return true;
311
+ }
312
+
313
+ return false;
314
+ }
315
+
316
+ /**
317
+ * Returns if the access_token is expired.
318
+ * @return bool Returns True if the access_token is expired.
319
+ */
320
+ public function isAccessTokenExpired() {
321
+ if (null == $this->token) {
322
+ return true;
323
+ }
324
+
325
+ // If the token is set to expire in the next 30 seconds.
326
+ $expired = ($this->token['created']
327
+ + ($this->token['expires_in'] - 30)) < time();
328
+
329
+ return $expired;
330
+ }
331
+
332
+ // Gets federated sign-on certificates to use for verifying identity tokens.
333
+ // Returns certs as array structure, where keys are key ids, and values
334
+ // are PEM encoded certificates.
335
+ private function getFederatedSignOnCerts() {
336
+ // This relies on makeRequest caching certificate responses.
337
+ $request = Google_Client::$io->makeRequest(new Google_HttpRequest(
338
+ self::OAUTH2_FEDERATED_SIGNON_CERTS_URL));
339
+ if ($request->getResponseHttpCode() == 200) {
340
+ $certs = json_decode($request->getResponseBody(), true);
341
+ if ($certs) {
342
+ return $certs;
343
+ }
344
+ }
345
+ throw new Google_AuthException(
346
+ "Failed to retrieve verification certificates: '" .
347
+ $request->getResponseBody() . "'.",
348
+ $request->getResponseHttpCode());
349
+ }
350
+
351
+ /**
352
+ * Verifies an id token and returns the authenticated apiLoginTicket.
353
+ * Throws an exception if the id token is not valid.
354
+ * The audience parameter can be used to control which id tokens are
355
+ * accepted. By default, the id token must have been issued to this OAuth2 client.
356
+ *
357
+ * @param $id_token
358
+ * @param $audience
359
+ * @return Google_LoginTicket
360
+ */
361
+ public function verifyIdToken($id_token = null, $audience = null) {
362
+ if (!$id_token) {
363
+ $id_token = $this->token['id_token'];
364
+ }
365
+
366
+ $certs = $this->getFederatedSignonCerts();
367
+ if (!$audience) {
368
+ $audience = $this->clientId;
369
+ }
370
+ return $this->verifySignedJwtWithCerts($id_token, $certs, $audience);
371
+ }
372
+
373
+ // Verifies the id token, returns the verified token contents.
374
+ // Visible for testing.
375
+ function verifySignedJwtWithCerts($jwt, $certs, $required_audience) {
376
+ $segments = explode(".", $jwt);
377
+ if (count($segments) != 3) {
378
+ throw new Google_AuthException("Wrong number of segments in token: $jwt");
379
+ }
380
+ $signed = $segments[0] . "." . $segments[1];
381
+ $signature = Google_Utils::urlSafeB64Decode($segments[2]);
382
+
383
+ // Parse envelope.
384
+ $envelope = json_decode(Google_Utils::urlSafeB64Decode($segments[0]), true);
385
+ if (!$envelope) {
386
+ throw new Google_AuthException("Can't parse token envelope: " . $segments[0]);
387
+ }
388
+
389
+ // Parse token
390
+ $json_body = Google_Utils::urlSafeB64Decode($segments[1]);
391
+ $payload = json_decode($json_body, true);
392
+ if (!$payload) {
393
+ throw new Google_AuthException("Can't parse token payload: " . $segments[1]);
394
+ }
395
+
396
+ // Check signature
397
+ $verified = false;
398
+ foreach ($certs as $keyName => $pem) {
399
+ $public_key = new Google_PemVerifier($pem);
400
+ if ($public_key->verify($signed, $signature)) {
401
+ $verified = true;
402
+ break;
403
+ }
404
+ }
405
+
406
+ if (!$verified) {
407
+ throw new Google_AuthException("Invalid token signature: $jwt");
408
+ }
409
+
410
+ // Check issued-at timestamp
411
+ $iat = 0;
412
+ if (array_key_exists("iat", $payload)) {
413
+ $iat = $payload["iat"];
414
+ }
415
+ if (!$iat) {
416
+ throw new Google_AuthException("No issue time in token: $json_body");
417
+ }
418
+ $earliest = $iat - self::CLOCK_SKEW_SECS;
419
+
420
+ // Check expiration timestamp
421
+ $now = time();
422
+ $exp = 0;
423
+ if (array_key_exists("exp", $payload)) {
424
+ $exp = $payload["exp"];
425
+ }
426
+ if (!$exp) {
427
+ throw new Google_AuthException("No expiration time in token: $json_body");
428
+ }
429
+ if ($exp >= $now + self::MAX_TOKEN_LIFETIME_SECS) {
430
+ throw new Google_AuthException(
431
+ "Expiration time too far in future: $json_body");
432
+ }
433
+
434
+ $latest = $exp + self::CLOCK_SKEW_SECS;
435
+ if ($now < $earliest) {
436
+ throw new Google_AuthException(
437
+ "Token used too early, $now < $earliest: $json_body");
438
+ }
439
+ if ($now > $latest) {
440
+ throw new Google_AuthException(
441
+ "Token used too late, $now > $latest: $json_body");
442
+ }
443
+
444
+ // TODO(beaton): check issuer field?
445
+
446
+ // Check audience
447
+ $aud = $payload["aud"];
448
+ if ($aud != $required_audience) {
449
+ throw new Google_AuthException("Wrong recipient, $aud != $required_audience: $json_body");
450
+ }
451
+
452
+ // All good.
453
+ return new Google_LoginTicket($envelope, $payload);
454
+ }
455
+ }
admin/api-libs/google/auth/Google_P12Signer.php ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ /**
19
+ * Signs data.
20
+ *
21
+ * Only used for testing.
22
+ *
23
+ * @author Brian Eaton <beaton@google.com>
24
+ */
25
+ class Google_P12Signer extends Google_Signer {
26
+ // OpenSSL private key resource
27
+ private $privateKey;
28
+
29
+ // Creates a new signer from a .p12 file.
30
+ function __construct($p12, $password) {
31
+ if (!function_exists('openssl_x509_read')) {
32
+ throw new Exception(
33
+ 'The Google PHP API library needs the openssl PHP extension');
34
+ }
35
+
36
+ // This throws on error
37
+ $certs = array();
38
+ if (!openssl_pkcs12_read($p12, $certs, $password)) {
39
+ throw new Google_AuthException("Unable to parse the p12 file. " .
40
+ "Is this a .p12 file? Is the password correct? OpenSSL error: " .
41
+ openssl_error_string());
42
+ }
43
+ // TODO(beaton): is this part of the contract for the openssl_pkcs12_read
44
+ // method? What happens if there are multiple private keys? Do we care?
45
+ if (!array_key_exists("pkey", $certs) || !$certs["pkey"]) {
46
+ throw new Google_AuthException("No private key found in p12 file.");
47
+ }
48
+ $this->privateKey = openssl_pkey_get_private($certs["pkey"]);
49
+ if (!$this->privateKey) {
50
+ throw new Google_AuthException("Unable to load private key in ");
51
+ }
52
+ }
53
+
54
+ function __destruct() {
55
+ if ($this->privateKey) {
56
+ openssl_pkey_free($this->privateKey);
57
+ }
58
+ }
59
+
60
+ function sign($data) {
61
+ if(version_compare(PHP_VERSION, '5.3.0') < 0) {
62
+ throw new Google_AuthException(
63
+ "PHP 5.3.0 or higher is required to use service accounts.");
64
+ }
65
+ if (!openssl_sign($data, $signature, $this->privateKey, "sha256")) {
66
+ throw new Google_AuthException("Unable to sign data");
67
+ }
68
+ return $signature;
69
+ }
70
+ }
admin/api-libs/google/auth/Google_PemVerifier.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ /**
19
+ * Verifies signatures using PEM encoded certificates.
20
+ *
21
+ * @author Brian Eaton <beaton@google.com>
22
+ */
23
+ class Google_PemVerifier extends Google_Verifier {
24
+ private $publicKey;
25
+
26
+ /**
27
+ * Constructs a verifier from the supplied PEM-encoded certificate.
28
+ *
29
+ * $pem: a PEM encoded certificate (not a file).
30
+ * @param $pem
31
+ * @throws Google_AuthException
32
+ * @throws Google_Exception
33
+ */
34
+ function __construct($pem) {
35
+ if (!function_exists('openssl_x509_read')) {
36
+ throw new Google_Exception('Google API PHP client needs the openssl PHP extension');
37
+ }
38
+ $this->publicKey = openssl_x509_read($pem);
39
+ if (!$this->publicKey) {
40
+ throw new Google_AuthException("Unable to parse PEM: $pem");
41
+ }
42
+ }
43
+
44
+ function __destruct() {
45
+ if ($this->publicKey) {
46
+ openssl_x509_free($this->publicKey);
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Verifies the signature on data.
52
+ *
53
+ * Returns true if the signature is valid, false otherwise.
54
+ * @param $data
55
+ * @param $signature
56
+ * @throws Google_AuthException
57
+ * @return bool
58
+ */
59
+ function verify($data, $signature) {
60
+ $status = openssl_verify($data, $signature, $this->publicKey, "sha256");
61
+ if ($status === -1) {
62
+ throw new Google_AuthException('Signature verification error: ' . openssl_error_string());
63
+ }
64
+ return $status === 1;
65
+ }
66
+ }
admin/api-libs/google/auth/Google_Signer.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ require_once "Google_P12Signer.php";
19
+
20
+ /**
21
+ * Signs data.
22
+ *
23
+ * @author Brian Eaton <beaton@google.com>
24
+ */
25
+ abstract class Google_Signer {
26
+ /**
27
+ * Signs data, returns the signature as binary data.
28
+ */
29
+ abstract public function sign($data);
30
+ }
admin/api-libs/google/auth/Google_Verifier.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ require_once "Google_PemVerifier.php";
19
+
20
+ /**
21
+ * Verifies signatures.
22
+ *
23
+ * @author Brian Eaton <beaton@google.com>
24
+ */
25
+ abstract class Google_Verifier {
26
+ /**
27
+ * Checks a signature, returns true if the signature is correct,
28
+ * false otherwise.
29
+ */
30
+ abstract public function verify($data, $signature);
31
+ }
admin/api-libs/google/cache/Google_ApcCache.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
19
+ * A persistent storage class based on the APC cache, which is not
20
+ * really very persistent, as soon as you restart your web server
21
+ * the storage will be wiped, however for debugging and/or speed
22
+ * it can be useful, kinda, and cache is a lot cheaper then storage.
23
+ *
24
+ * @author Chris Chabot <chabotc@google.com>
25
+ */
26
+ class Google_APCCache extends Google_Cache {
27
+
28
+ public function __construct() {
29
+ if (! function_exists('apc_add')) {
30
+ throw new Google_CacheException("Apc functions not available");
31
+ }
32
+ }
33
+
34
+ private function isLocked($key) {
35
+ if ((@apc_fetch($key . '.lock')) === false) {
36
+ return false;
37
+ }
38
+ return true;
39
+ }
40
+
41
+ private function createLock($key) {
42
+ // the interesting thing is that this could fail if the lock was created in the meantime..
43
+ // but we'll ignore that out of convenience
44
+ @apc_add($key . '.lock', '', 5);
45
+ }
46
+
47
+ private function removeLock($key) {
48
+ // suppress all warnings, if some other process removed it that's ok too
49
+ @apc_delete($key . '.lock');
50
+ }
51
+
52
+ private function waitForLock($key) {
53
+ // 20 x 250 = 5 seconds
54
+ $tries = 20;
55
+ $cnt = 0;
56
+ do {
57
+ // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
58
+ usleep(250);
59
+ $cnt ++;
60
+ } while ($cnt <= $tries && $this->isLocked($key));
61
+ if ($this->isLocked($key)) {
62
+ // 5 seconds passed, assume the owning process died off and remove it
63
+ $this->removeLock($key);
64
+ }
65
+ }
66
+
67
+ /**
68
+ * @inheritDoc
69
+ */
70
+ public function get($key, $expiration = false) {
71
+
72
+ if (($ret = @apc_fetch($key)) === false) {
73
+ return false;
74
+ }
75
+ if (!$expiration || (time() - $ret['time'] > $expiration)) {
76
+ $this->delete($key);
77
+ return false;
78
+ }
79
+ return unserialize($ret['data']);
80
+ }
81
+
82
+ /**
83
+ * @inheritDoc
84
+ */
85
+ public function set($key, $value) {
86
+ if (@apc_store($key, array('time' => time(), 'data' => serialize($value))) == false) {
87
+ throw new Google_CacheException("Couldn't store data");
88
+ }
89
+ }
90
+
91
+ /**
92
+ * @inheritDoc
93
+ * @param String $key
94
+ */
95
+ public function delete($key) {
96
+ @apc_delete($key);
97
+ }
98
+ }
admin/api-libs/google/cache/Google_Cache.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2008 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
+ require_once "Google_FileCache.php";
19
+ require_once "Google_MemcacheCache.php";
20
+
21
+ /**
22
+ * Abstract storage class
23
+ *
24
+ * @author Chris Chabot <chabotc@google.com>
25
+ */
26
+ abstract class Google_Cache {
27
+
28
+ /**
29
+ * Retrieves the data for the given key, or false if they
30
+ * key is unknown or expired
31
+ *
32
+ * @param String $key The key who's data to retrieve
33
+ * @param boolean|int $expiration Expiration time in seconds
34
+ *
35
+ */
36
+ abstract function get($key, $expiration = false);
37
+
38
+ /**
39
+ * Store the key => $value set. The $value is serialized
40
+ * by this function so can be of any type
41
+ *
42
+ * @param string $key Key of the data
43
+ * @param string $value data
44
+ */
45
+ abstract function set($key, $value);
46
+
47
+ /**
48
+ * Removes the key/data pair for the given $key
49
+ *
50
+ * @param String $key
51
+ */
52
+ abstract function delete($key);
53
+ }
54
+
55
+
admin/api-libs/google/cache/Google_FileCache.php ADDED
@@ -0,0 +1,137 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2008 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
+ /*
19
+ * This class implements a basic on disk storage. While that does
20
+ * work quite well it's not the most elegant and scalable solution.
21
+ * It will also get you into a heap of trouble when you try to run
22
+ * this in a clustered environment. In those cases please use the
23
+ * MySql back-end
24
+ *
25
+ * @author Chris Chabot <chabotc@google.com>
26
+ */
27
+ class Google_FileCache extends Google_Cache {
28
+ private $path;
29
+
30
+ public function __construct() {
31
+ global $apiConfig;
32
+ $this->path = $apiConfig['ioFileCache_directory'];
33
+ }
34
+
35
+ private function isLocked($storageFile) {
36
+ // our lock file convention is simple: /the/file/path.lock
37
+ return file_exists($storageFile . '.lock');
38
+ }
39
+
40
+ private function createLock($storageFile) {
41
+ $storageDir = dirname($storageFile);
42
+ if (! is_dir($storageDir)) {
43
+ // @codeCoverageIgnoreStart
44
+ if (! @mkdir($storageDir, 0755, true)) {
45
+ // make sure the failure isn't because of a concurrency issue
46
+ if (! is_dir($storageDir)) {
47
+ throw new Google_CacheException("Could not create storage directory: $storageDir");
48
+ }
49
+ }
50
+ // @codeCoverageIgnoreEnd
51
+ }
52
+ @touch($storageFile . '.lock');
53
+ }
54
+
55
+ private function removeLock($storageFile) {
56
+ // suppress all warnings, if some other process removed it that's ok too
57
+ @unlink($storageFile . '.lock');
58
+ }
59
+
60
+ private function waitForLock($storageFile) {
61
+ // 20 x 250 = 5 seconds
62
+ $tries = 20;
63
+ $cnt = 0;
64
+ do {
65
+ // make sure PHP picks up on file changes. This is an expensive action but really can't be avoided
66
+ clearstatcache();
67
+ // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
68
+ usleep(250);
69
+ $cnt ++;
70
+ } while ($cnt <= $tries && $this->isLocked($storageFile));
71
+ if ($this->isLocked($storageFile)) {
72
+ // 5 seconds passed, assume the owning process died off and remove it
73
+ $this->removeLock($storageFile);
74
+ }
75
+ }
76
+
77
+ private function getCacheDir($hash) {
78
+ // use the first 2 characters of the hash as a directory prefix
79
+ // this should prevent slowdowns due to huge directory listings
80
+ // and thus give some basic amount of scalability
81
+ return $this->path . '/' . substr($hash, 0, 2);
82
+ }
83
+
84
+ private function getCacheFile($hash) {
85
+ return $this->getCacheDir($hash) . '/' . $hash;
86
+ }
87
+
88
+ public function get($key, $expiration = false) {
89
+ $storageFile = $this->getCacheFile(md5($key));
90
+ // See if this storage file is locked, if so we wait up to 5 seconds for the lock owning process to
91
+ // complete it's work. If the lock is not released within that time frame, it's cleaned up.
92
+ // This should give us a fair amount of 'Cache Stampeding' protection
93
+ if ($this->isLocked($storageFile)) {
94
+ $this->waitForLock($storageFile);
95
+ }
96
+ if (file_exists($storageFile) && is_readable($storageFile)) {
97
+ $now = time();
98
+ if (! $expiration || (($mtime = @filemtime($storageFile)) !== false && ($now - $mtime) < $expiration)) {
99
+ if (($data = @file_get_contents($storageFile)) !== false) {
100
+ $data = unserialize($data);
101
+ return $data;
102
+ }
103
+ }
104
+ }
105
+ return false;
106
+ }
107
+
108
+ public function set($key, $value) {
109
+ $storageDir = $this->getCacheDir(md5($key));
110
+ $storageFile = $this->getCacheFile(md5($key));
111
+ if ($this->isLocked($storageFile)) {
112
+ // some other process is writing to this file too, wait until it's done to prevent hiccups
113
+ $this->waitForLock($storageFile);
114
+ }
115
+ if (! is_dir($storageDir)) {
116
+ if (! @mkdir($storageDir, 0755, true)) {
117
+ throw new Google_CacheException("Could not create storage directory: $storageDir");
118
+ }
119
+ }
120
+ // we serialize the whole request object, since we don't only want the
121
+ // responseContent but also the postBody used, headers, size, etc
122
+ $data = serialize($value);
123
+ $this->createLock($storageFile);
124
+ if (! @file_put_contents($storageFile, $data)) {
125
+ $this->removeLock($storageFile);
126
+ throw new Google_CacheException("Could not store data in the file");
127
+ }
128
+ $this->removeLock($storageFile);
129
+ }
130
+
131
+ public function delete($key) {
132
+ $file = $this->getCacheFile(md5($key));
133
+ if (! @unlink($file)) {
134
+ throw new Google_CacheException("Cache file could not be deleted");
135
+ }
136
+ }
137
+ }
admin/api-libs/google/cache/Google_MemcacheCache.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2008 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
+ /**
19
+ * A persistent storage class based on the memcache, which is not
20
+ * really very persistent, as soon as you restart your memcache daemon
21
+ * the storage will be wiped, however for debugging and/or speed
22
+ * it can be useful, kinda, and cache is a lot cheaper then storage.
23
+ *
24
+ * @author Chris Chabot <chabotc@google.com>
25
+ */
26
+ class Google_MemcacheCache extends Google_Cache {
27
+ private $connection = false;
28
+
29
+ public function __construct() {
30
+ global $apiConfig;
31
+ if (! function_exists('memcache_connect')) {
32
+ throw new Google_CacheException("Memcache functions not available");
33
+ }
34
+ $this->host = $apiConfig['ioMemCacheCache_host'];
35
+ $this->port = $apiConfig['ioMemCacheCache_port'];
36
+ if (empty($this->host) || empty($this->port)) {
37
+ throw new Google_CacheException("You need to supply a valid memcache host and port");
38
+ }
39
+ }
40
+
41
+ private function isLocked($key) {
42
+ $this->check();
43
+ if ((@memcache_get($this->connection, $key . '.lock')) === false) {
44
+ return false;
45
+ }
46
+ return true;
47
+ }
48
+
49
+ private function createLock($key) {
50
+ $this->check();
51
+ // the interesting thing is that this could fail if the lock was created in the meantime..
52
+ // but we'll ignore that out of convenience
53
+ @memcache_add($this->connection, $key . '.lock', '', 0, 5);
54
+ }
55
+
56
+ private function removeLock($key) {
57
+ $this->check();
58
+ // suppress all warnings, if some other process removed it that's ok too
59
+ @memcache_delete($this->connection, $key . '.lock');
60
+ }
61
+
62
+ private function waitForLock($key) {
63
+ $this->check();
64
+ // 20 x 250 = 5 seconds
65
+ $tries = 20;
66
+ $cnt = 0;
67
+ do {
68
+ // 250 ms is a long time to sleep, but it does stop the server from burning all resources on polling locks..
69
+ usleep(250);
70
+ $cnt ++;
71
+ } while ($cnt <= $tries && $this->isLocked($key));
72
+ if ($this->isLocked($key)) {
73
+ // 5 seconds passed, assume the owning process died off and remove it
74
+ $this->removeLock($key);
75
+ }
76
+ }
77
+
78
+ // I prefer lazy initialization since the cache isn't used every request
79
+ // so this potentially saves a lot of overhead
80
+ private function connect() {
81
+ if (! $this->connection = @memcache_pconnect($this->host, $this->port)) {
82
+ throw new Google_CacheException("Couldn't connect to memcache server");
83
+ }
84
+ }
85
+
86
+ private function check() {
87
+ if (! $this->connection) {
88
+ $this->connect();
89
+ }
90
+ }
91
+
92
+ /**
93
+ * @inheritDoc
94
+ */
95
+ public function get($key, $expiration = false) {
96
+ $this->check();
97
+ if (($ret = @memcache_get($this->connection, $key)) === false) {
98
+ return false;
99
+ }
100
+ if (is_numeric($expiration) && (time() - $ret['time'] > $expiration)) {
101
+ $this->delete($key);
102
+ return false;
103
+ }
104
+ return $ret['data'];
105
+ }
106
+
107
+ /**
108
+ * @inheritDoc
109
+ * @param string $key
110
+ * @param string $value
111
+ * @throws Google_CacheException
112
+ */
113
+ public function set($key, $value) {
114
+ $this->check();
115
+ // we store it with the cache_time default expiration so objects will at least get cleaned eventually.
116
+ if (@memcache_set($this->connection, $key, array('time' => time(),
117
+ 'data' => $value), false) == false) {
118
+ throw new Google_CacheException("Couldn't store data in cache");
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @inheritDoc
124
+ * @param String $key
125
+ */
126
+ public function delete($key) {
127
+ $this->check();
128
+ @memcache_delete($this->connection, $key);
129
+ }
130
+ }
admin/api-libs/google/class-api-google.php ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'Yoast_Api_Google' ) ) {
4
+
5
+ class Yoast_Api_Google{
6
+
7
+ public $options;
8
+
9
+ /**
10
+ * This class will be loaded when someone calls the API library with the Google analytics module
11
+ */
12
+ public function __construct() {
13
+ $this->load_api_google_files();
14
+ }
15
+
16
+ /**
17
+ * Autoload the Oauth classes
18
+ */
19
+ private function load_api_google_files() {
20
+ require_once( dirname( __FILE__ ) . '/Google_Client.php' );
21
+ }
22
+
23
+ }
24
+
25
+ }
admin/api-libs/google/config.php ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ global $apiConfig;
19
+ $apiConfig = array(
20
+ // True if objects should be returned by the service classes.
21
+ // False if associative arrays should be returned (default behavior).
22
+ 'use_objects' => false,
23
+
24
+ // The application_name is included in the User-Agent HTTP header.
25
+ 'application_name' => '',
26
+
27
+ // OAuth2 Settings, you can get these keys at https://code.google.com/apis/console
28
+ 'oauth2_client_id' => '',
29
+ 'oauth2_client_secret' => '',
30
+ 'oauth2_redirect_uri' => '',
31
+
32
+ // The developer key, you get this at https://code.google.com/apis/console
33
+ 'developer_key' => '',
34
+
35
+ // Site name to show in the Google's OAuth 1 authentication screen.
36
+ 'site_name' => 'www.example.org',
37
+
38
+ // Which Authentication, Storage and HTTP IO classes to use.
39
+ 'authClass' => 'Google_OAuth2',
40
+ 'ioClass' => 'Google_CurlIO',
41
+ 'cacheClass' => 'Google_FileCache',
42
+
43
+ // Don't change these unless you're working against a special development or testing environment.
44
+ 'basePath' => 'https://www.googleapis.com',
45
+
46
+ // IO Class dependent configuration, you only have to configure the values
47
+ // for the class that was configured as the ioClass above
48
+ 'ioFileCache_directory' =>
49
+ (function_exists('sys_get_temp_dir') ?
50
+ sys_get_temp_dir() . '/Google_Client' :
51
+ '/tmp/Google_Client'),
52
+
53
+ // Definition of service specific values like scopes, oauth token URLs, etc
54
+ /*
55
+ 'services' => array(
56
+ 'analytics' => array('scope' => 'https://www.googleapis.com/auth/analytics.readonly'),
57
+ 'calendar' => array(
58
+ 'scope' => array(
59
+ "https://www.googleapis.com/auth/calendar",
60
+ "https://www.googleapis.com/auth/calendar.readonly",
61
+ )
62
+ ),
63
+ 'books' => array('scope' => 'https://www.googleapis.com/auth/books'),
64
+ 'latitude' => array(
65
+ 'scope' => array(
66
+ 'https://www.googleapis.com/auth/latitude.all.best',
67
+ 'https://www.googleapis.com/auth/latitude.all.city',
68
+ )
69
+ ),
70
+ 'moderator' => array('scope' => 'https://www.googleapis.com/auth/moderator'),
71
+ 'oauth2' => array(
72
+ 'scope' => array(
73
+ 'https://www.googleapis.com/auth/userinfo.profile',
74
+ 'https://www.googleapis.com/auth/userinfo.email',
75
+ )
76
+ ),
77
+ 'plus' => array('scope' => 'https://www.googleapis.com/auth/plus.login'),
78
+ 'siteVerification' => array('scope' => 'https://www.googleapis.com/auth/siteverification'),
79
+ 'tasks' => array('scope' => 'https://www.googleapis.com/auth/tasks'),
80
+ 'urlshortener' => array('scope' => 'https://www.googleapis.com/auth/urlshortener')
81
+ )
82
+ */
83
+ );
admin/api-libs/google/external/URITemplateParser.php ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Copyright (c) 2010 Kevin M Burns Jr, http://kevburnsjr.com/
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+ */
24
+
25
+ /**
26
+ * A URI Template Parser which is used by the apiREST class to resolve the REST requests
27
+ * Blogpost: http://lab.kevburnsjr.com/php-uri-template-parser
28
+ * Source: http://github.com/KevBurnsJr/php-uri-template-parser
29
+ */
30
+ class URI_Template_Parser {
31
+
32
+ public static $operators = array('+', ';', '?', '/', '.');
33
+ public static $reserved_operators = array('|', '!', '@');
34
+ public static $explode_modifiers = array('+', '*');
35
+ public static $partial_modifiers = array(':', '^');
36
+
37
+ public static $gen_delims = array(':', '/', '?', '#', '[', ']', '@');
38
+ public static $gen_delims_pct = array('%3A', '%2F', '%3F', '%23', '%5B', '%5D', '%40');
39
+ public static $sub_delims = array('!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=');
40
+ public static $sub_delims_pct = array('%21', '%24', '%26', '%27', '%28', '%29', '%2A', '%2B', '%2C', '%3B', '%3D');
41
+ public static $reserved;
42
+ public static $reserved_pct;
43
+
44
+ public function __construct($template) {
45
+ self::$reserved = array_merge(self::$gen_delims, self::$sub_delims);
46
+ self::$reserved_pct = array_merge(self::$gen_delims_pct, self::$sub_delims_pct);
47
+ $this->template = $template;
48
+ }
49
+
50
+ public function expand($data) {
51
+ // Modification to make this a bit more performant (since gettype is very slow)
52
+ if (! is_array($data)) {
53
+ $data = (array)$data;
54
+ }
55
+ /*
56
+ // Original code, which uses a slow gettype() statement, kept in place for if the assumption that is_array always works here is incorrect
57
+ switch (gettype($data)) {
58
+ case "boolean":
59
+ case "integer":
60
+ case "double":
61
+ case "string":
62
+ case "object":
63
+ $data = (array)$data;
64
+ break;
65
+ }
66
+ */
67
+
68
+ // Resolve template vars
69
+ preg_match_all('/\{([^\}]*)\}/', $this->template, $em);
70
+
71
+ foreach ($em[1] as $i => $bare_expression) {
72
+ preg_match('/^([\+\;\?\/\.]{1})?(.*)$/', $bare_expression, $lm);
73
+ $exp = new StdClass();
74
+ $exp->expression = $em[0][$i];
75
+ $exp->operator = $lm[1];
76
+ $exp->variable_list = $lm[2];
77
+ $exp->varspecs = explode(',', $exp->variable_list);
78
+ $exp->vars = array();
79
+ foreach ($exp->varspecs as $varspec) {
80
+ preg_match('/^([a-zA-Z0-9_]+)([\*\+]{1})?([\:\^][0-9-]+)?(\=[^,]+)?$/', $varspec, $vm);
81
+ $var = new StdClass();
82
+ $var->name = $vm[1];
83
+ $var->modifier = isset($vm[2]) && $vm[2] ? $vm[2] : null;
84
+ $var->modifier = isset($vm[3]) && $vm[3] ? $vm[3] : $var->modifier;
85
+ $var->default = isset($vm[4]) ? substr($vm[4], 1) : null;
86
+ $exp->vars[] = $var;
87
+ }
88
+
89
+ // Add processing flags
90
+ $exp->reserved = false;
91
+ $exp->prefix = '';
92
+ $exp->delimiter = ',';
93
+ switch ($exp->operator) {
94
+ case '+':
95
+ $exp->reserved = 'true';
96
+ break;
97
+ case ';':
98
+ $exp->prefix = ';';
99
+ $exp->delimiter = ';';
100
+ break;
101
+ case '?':
102
+ $exp->prefix = '?';
103
+ $exp->delimiter = '&';
104
+ break;
105
+ case '/':
106
+ $exp->prefix = '/';
107
+ $exp->delimiter = '/';
108
+ break;
109
+ case '.':
110
+ $exp->prefix = '.';
111
+ $exp->delimiter = '.';
112
+ break;
113
+ }
114
+ $expressions[] = $exp;
115
+ }
116
+
117
+ // Expansion
118
+ $this->expansion = $this->template;
119
+
120
+ foreach ($expressions as $exp) {
121
+ $part = $exp->prefix;
122
+ $exp->one_var_defined = false;
123
+ foreach ($exp->vars as $var) {
124
+ $val = '';
125
+ if ($exp->one_var_defined && isset($data[$var->name])) {
126
+ $part .= $exp->delimiter;
127
+ }
128
+ // Variable present
129
+ if (isset($data[$var->name])) {
130
+ $exp->one_var_defined = true;
131
+ $var->data = $data[$var->name];
132
+
133
+ $val = self::val_from_var($var, $exp);
134
+
135
+ // Variable missing
136
+ } else {
137
+ if ($var->default) {
138
+ $exp->one_var_defined = true;
139
+ $val = $var->default;
140
+ }
141
+ }
142
+ $part .= $val;
143
+ }
144
+ if (! $exp->one_var_defined) $part = '';
145
+ $this->expansion = str_replace($exp->expression, $part, $this->expansion);
146
+ }
147
+
148
+ return $this->expansion;
149
+ }
150
+
151
+ private function val_from_var($var, $exp) {
152
+ $val = '';
153
+ if (is_array($var->data)) {
154
+ $i = 0;
155
+ if ($exp->operator == '?' && ! $var->modifier) {
156
+ $val .= $var->name . '=';
157
+ }
158
+ foreach ($var->data as $k => $v) {
159
+ $del = $var->modifier ? $exp->delimiter : ',';
160
+ $ek = rawurlencode($k);
161
+ $ev = rawurlencode($v);
162
+
163
+ // Array
164
+ if ($k !== $i) {
165
+ if ($var->modifier == '+') {
166
+ $val .= $var->name . '.';
167
+ }
168
+ if ($exp->operator == '?' && $var->modifier || $exp->operator == ';' && $var->modifier == '*' || $exp->operator == ';' && $var->modifier == '+') {
169
+ $val .= $ek . '=';
170
+ } else {
171
+ $val .= $ek . $del;
172
+ }
173
+
174
+ // List
175
+ } else {
176
+ if ($var->modifier == '+') {
177
+ if ($exp->operator == ';' && $var->modifier == '*' || $exp->operator == ';' && $var->modifier == '+' || $exp->operator == '?' && $var->modifier == '+') {
178
+ $val .= $var->name . '=';
179
+ } else {
180
+ $val .= $var->name . '.';
181
+ }
182
+ }
183
+ }
184
+ $val .= $ev . $del;
185
+ $i ++;
186
+ }
187
+ $val = trim($val, $del);
188
+
189
+ // Strings, numbers, etc.
190
+ } else {
191
+ if ($exp->operator == '?') {
192
+ $val = $var->name . (isset($var->data) ? '=' : '');
193
+ } else if ($exp->operator == ';') {
194
+ $val = $var->name . ($var->data ? '=' : '');
195
+ }
196
+ $val .= rawurlencode($var->data);
197
+ if ($exp->operator == '+') {
198
+ $val = str_replace(self::$reserved_pct, self::$reserved, $val);
199
+ }
200
+ }
201
+ return $val;
202
+ }
203
+
204
+ public function match($uri) {}
205
+
206
+ public function __toString() {
207
+ return $this->template;
208
+ }
209
+ }
admin/api-libs/google/io/Google_CacheParser.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2012 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
+ * Implement the caching directives specified in rfc2616. This
19
+ * implementation is guided by the guidance offered in rfc2616-sec13.
20
+ * @author Chirag Shah <chirags@google.com>
21
+ */
22
+ class Google_CacheParser {
23
+ public static $CACHEABLE_HTTP_METHODS = array('GET', 'HEAD');
24
+ public static $CACHEABLE_STATUS_CODES = array('200', '203', '300', '301');
25
+
26
+ private function __construct() {}
27
+
28
+ /**
29
+ * Check if an HTTP request can be cached by a private local cache.
30
+ *
31
+ * @static
32
+ * @param Google_HttpRequest $resp
33
+ * @return bool True if the request is cacheable.
34
+ * False if the request is uncacheable.
35
+ */
36
+ public static function isRequestCacheable (Google_HttpRequest $resp) {
37
+ $method = $resp->getRequestMethod();
38
+ if (! in_array($method, self::$CACHEABLE_HTTP_METHODS)) {
39
+ return false;
40
+ }
41
+
42
+ // Don't cache authorized requests/responses.
43
+ // [rfc2616-14.8] When a shared cache receives a request containing an
44
+ // Authorization field, it MUST NOT return the corresponding response
45
+ // as a reply to any other request...
46
+ if ($resp->getRequestHeader("authorization")) {
47
+ return false;
48
+ }
49
+
50
+ return true;
51
+ }
52
+
53
+ /**
54
+ * Check if an HTTP response can be cached by a private local cache.
55
+ *
56
+ * @static
57
+ * @param Google_HttpRequest $resp
58
+ * @return bool True if the response is cacheable.
59
+ * False if the response is un-cacheable.
60
+ */
61
+ public static function isResponseCacheable (Google_HttpRequest $resp) {
62
+ // First, check if the HTTP request was cacheable before inspecting the
63
+ // HTTP response.
64
+ if (false == self::isRequestCacheable($resp)) {
65
+ return false;
66
+ }
67
+
68
+ $code = $resp->getResponseHttpCode();
69
+ if (! in_array($code, self::$CACHEABLE_STATUS_CODES)) {
70
+ return false;
71
+ }
72
+
73
+ // The resource is uncacheable if the resource is already expired and
74
+ // the resource doesn't have an ETag for revalidation.
75
+ $etag = $resp->getResponseHeader("etag");
76
+ if (self::isExpired($resp) && $etag == false) {
77
+ return false;
78
+ }
79
+
80
+ // [rfc2616-14.9.2] If [no-store is] sent in a response, a cache MUST NOT
81
+ // store any part of either this response or the request that elicited it.
82
+ $cacheControl = $resp->getParsedCacheControl();
83
+ if (isset($cacheControl['no-store'])) {
84
+ return false;
85
+ }
86
+
87
+ // Pragma: no-cache is an http request directive, but is occasionally
88
+ // used as a response header incorrectly.
89
+ $pragma = $resp->getResponseHeader('pragma');
90
+ if ($pragma == 'no-cache' || strpos($pragma, 'no-cache') !== false) {
91
+ return false;
92
+ }
93
+
94
+ // [rfc2616-14.44] Vary: * is extremely difficult to cache. "It implies that
95
+ // a cache cannot determine from the request headers of a subsequent request
96
+ // whether this response is the appropriate representation."
97
+ // Given this, we deem responses with the Vary header as uncacheable.
98
+ $vary = $resp->getResponseHeader('vary');
99
+ if ($vary) {
100
+ return false;
101
+ }
102
+
103
+ return true;
104
+ }
105
+
106
+ /**
107
+ * @static
108
+ * @param Google_HttpRequest $resp
109
+ * @return bool True if the HTTP response is considered to be expired.
110
+ * False if it is considered to be fresh.
111
+ */
112
+ public static function isExpired(Google_HttpRequest $resp) {
113
+ // HTTP/1.1 clients and caches MUST treat other invalid date formats,
114
+ // especially including the value “0”, as in the past.
115
+ $parsedExpires = false;
116
+ $responseHeaders = $resp->getResponseHeaders();
117
+ if (isset($responseHeaders['expires'])) {
118
+ $rawExpires = $responseHeaders['expires'];
119
+ // Check for a malformed expires header first.
120
+ if (empty($rawExpires) || (is_numeric($rawExpires) && $rawExpires <= 0)) {
121
+ return true;
122
+ }
123
+
124
+ // See if we can parse the expires header.
125
+ $parsedExpires = strtotime($rawExpires);
126
+ if (false == $parsedExpires || $parsedExpires <= 0) {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ // Calculate the freshness of an http response.
132
+ $freshnessLifetime = false;
133
+ $cacheControl = $resp->getParsedCacheControl();
134
+ if (isset($cacheControl['max-age'])) {
135
+ $freshnessLifetime = $cacheControl['max-age'];
136
+ }
137
+
138
+ $rawDate = $resp->getResponseHeader('date');
139
+ $parsedDate = strtotime($rawDate);
140
+
141
+ if (empty($rawDate) || false == $parsedDate) {
142
+ $parsedDate = time();
143
+ }
144
+ if (false == $freshnessLifetime && isset($responseHeaders['expires'])) {
145
+ $freshnessLifetime = $parsedExpires - $parsedDate;
146
+ }
147
+
148
+ if (false == $freshnessLifetime) {
149
+ return true;
150
+ }
151
+
152
+ // Calculate the age of an http response.
153
+ $age = max(0, time() - $parsedDate);
154
+ if (isset($responseHeaders['age'])) {
155
+ $age = max($age, strtotime($responseHeaders['age']));
156
+ }
157
+
158
+ return $freshnessLifetime <= $age;
159
+ }
160
+
161
+ /**
162
+ * Determine if a cache entry should be revalidated with by the origin.
163
+ *
164
+ * @param Google_HttpRequest $response
165
+ * @return bool True if the entry is expired, else return false.
166
+ */
167
+ public static function mustRevalidate(Google_HttpRequest $response) {
168
+ // [13.3] When a cache has a stale entry that it would like to use as a
169
+ // response to a client's request, it first has to check with the origin
170
+ // server to see if its cached entry is still usable.
171
+ return self::isExpired($response);
172
+ }
173
+ }
admin/api-libs/google/io/Google_CurlIO.php ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
19
+ * Curl based implementation of apiIO.
20
+ *
21
+ * @author Chris Chabot <chabotc@google.com>
22
+ * @author Chirag Shah <chirags@google.com>
23
+ */
24
+
25
+ require_once 'Google_CacheParser.php';
26
+
27
+ class Google_CurlIO extends Google_IO {
28
+ private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null);
29
+ private static $HOP_BY_HOP = array(
30
+ 'connection', 'keep-alive', 'proxy-authenticate', 'proxy-authorization',
31
+ 'te', 'trailers', 'transfer-encoding', 'upgrade');
32
+
33
+ private $curlParams = array (
34
+ CURLOPT_RETURNTRANSFER => true,
35
+ CURLOPT_FOLLOWLOCATION => 0,
36
+ CURLOPT_FAILONERROR => false,
37
+ CURLOPT_SSL_VERIFYPEER => true,
38
+ CURLOPT_HEADER => true,
39
+ CURLOPT_VERBOSE => false,
40
+ );
41
+
42
+ /**
43
+ * Check for cURL availability.
44
+ */
45
+ public function __construct() {
46
+ if (! function_exists('curl_init')) {
47
+ throw new Exception(
48
+ 'Google CurlIO client requires the CURL PHP extension');
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Perform an authenticated / signed apiHttpRequest.
54
+ * This function takes the apiHttpRequest, calls apiAuth->sign on it
55
+ * (which can modify the request in what ever way fits the auth mechanism)
56
+ * and then calls apiCurlIO::makeRequest on the signed request
57
+ *
58
+ * @param Google_HttpRequest $request
59
+ * @return Google_HttpRequest The resulting HTTP response including the
60
+ * responseHttpCode, responseHeaders and responseBody.
61
+ */
62
+ public function authenticatedRequest(Google_HttpRequest $request) {
63
+ $request = Google_Client::$auth->sign($request);
64
+ return $this->makeRequest($request);
65
+ }
66
+
67
+ /**
68
+ * Execute a apiHttpRequest
69
+ *
70
+ * @param Google_HttpRequest $request the http request to be executed
71
+ * @return Google_HttpRequest http request with the response http code, response
72
+ * headers and response body filled in
73
+ * @throws Google_IOException on curl or IO error
74
+ */
75
+ public function makeRequest(Google_HttpRequest $request) {
76
+ // First, check to see if we have a valid cached version.
77
+ $cached = $this->getCachedRequest($request);
78
+ if ($cached !== false) {
79
+ if (!$this->checkMustRevaliadateCachedRequest($cached, $request)) {
80
+ return $cached;
81
+ }
82
+ }
83
+
84
+ if (array_key_exists($request->getRequestMethod(),
85
+ self::$ENTITY_HTTP_METHODS)) {
86
+ $request = $this->processEntityRequest($request);
87
+ }
88
+
89
+ $ch = curl_init();
90
+ curl_setopt_array($ch, $this->curlParams);
91
+ curl_setopt($ch, CURLOPT_URL, $request->getUrl());
92
+ if ($request->getPostBody()) {
93
+ curl_setopt($ch, CURLOPT_POSTFIELDS, $request->getPostBody());
94
+ }
95
+
96
+ $requestHeaders = $request->getRequestHeaders();
97
+ if ($requestHeaders && is_array($requestHeaders)) {
98
+ $parsed = array();
99
+ foreach ($requestHeaders as $k => $v) {
100
+ $parsed[] = "$k: $v";
101
+ }
102
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $parsed);
103
+ }
104
+
105
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request->getRequestMethod());
106
+ curl_setopt($ch, CURLOPT_USERAGENT, $request->getUserAgent());
107
+ $respData = curl_exec($ch);
108
+
109
+ // Retry if certificates are missing.
110
+ if (curl_errno($ch) == CURLE_SSL_CACERT) {
111
+ error_log('SSL certificate problem, verify that the CA cert is OK.'
112
+ . ' Retrying with the CA cert bundle from google-api-php-client.');
113
+ curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacerts.pem');
114
+ $respData = curl_exec($ch);
115
+ }
116
+
117
+ $respHeaderSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
118
+ $respHttpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
119
+ $curlErrorNum = curl_errno($ch);
120
+ $curlError = curl_error($ch);
121
+ curl_close($ch);
122
+ if ($curlErrorNum != CURLE_OK) {
123
+ throw new Google_IOException("HTTP Error: ($respHttpCode) $curlError");
124
+ }
125
+
126
+ // Parse out the raw response into usable bits
127
+ list($responseHeaders, $responseBody) =
128
+ self::parseHttpResponse($respData, $respHeaderSize);
129
+
130
+ if ($respHttpCode == 304 && $cached) {
131
+ // If the server responded NOT_MODIFIED, return the cached request.
132
+ $this->updateCachedRequest($cached, $responseHeaders);
133
+ return $cached;
134
+ }
135
+
136
+ // Fill in the apiHttpRequest with the response values
137
+ $request->setResponseHttpCode($respHttpCode);
138
+ $request->setResponseHeaders($responseHeaders);
139
+ $request->setResponseBody($responseBody);
140
+ // Store the request in cache (the function checks to see if the request
141
+ // can actually be cached)
142
+ $this->setCachedRequest($request);
143
+ // And finally return it
144
+ return $request;
145
+ }
146
+
147
+ /**
148
+ * Set options that update cURL's default behavior.
149
+ * The list of accepted options are:
150
+ * {@link http://php.net/manual/en/function.curl-setopt.php]
151
+ *
152
+ * @param array $optCurlParams Multiple options used by a cURL session.
153
+ */
154
+ public function setOptions($optCurlParams) {
155
+ foreach ($optCurlParams as $key => $val) {
156
+ $this->curlParams[$key] = $val;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * @param $respData
162
+ * @param $headerSize
163
+ * @return array
164
+ */
165
+ private static function parseHttpResponse($respData, $headerSize) {
166
+ if (stripos($respData, parent::CONNECTION_ESTABLISHED) !== false) {
167
+ $respData = str_ireplace(parent::CONNECTION_ESTABLISHED, '', $respData);
168
+ }
169
+
170
+ if ($headerSize) {
171
+ $responseBody = substr($respData, $headerSize);
172
+ $responseHeaders = substr($respData, 0, $headerSize);
173
+ } else {
174
+ list($responseHeaders, $responseBody) = explode("\r\n\r\n", $respData, 2);
175
+ }
176
+
177
+ $responseHeaders = self::parseResponseHeaders($responseHeaders);
178
+ return array($responseHeaders, $responseBody);
179
+ }
180
+
181
+ private static function parseResponseHeaders($rawHeaders) {
182
+ $responseHeaders = array();
183
+
184
+ $responseHeaderLines = explode("\r\n", $rawHeaders);
185
+ foreach ($responseHeaderLines as $headerLine) {
186
+ if ($headerLine && strpos($headerLine, ':') !== false) {
187
+ list($header, $value) = explode(': ', $headerLine, 2);
188
+ $header = strtolower($header);
189
+ if (isset($responseHeaders[$header])) {
190
+ $responseHeaders[$header] .= "\n" . $value;
191
+ } else {
192
+ $responseHeaders[$header] = $value;
193
+ }
194
+ }
195
+ }
196
+ return $responseHeaders;
197
+ }
198
+ }
admin/api-libs/google/io/Google_HttpRequest.php ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
19
+ * HTTP Request to be executed by apiIO classes. Upon execution, the
20
+ * responseHttpCode, responseHeaders and responseBody will be filled in.
21
+ *
22
+ * @author Chris Chabot <chabotc@google.com>
23
+ * @author Chirag Shah <chirags@google.com>
24
+ *
25
+ */
26
+ class Google_HttpRequest {
27
+ const USER_AGENT_SUFFIX = "google-api-php-client/0.6.5";
28
+ private $batchHeaders = array(
29
+ 'Content-Type' => 'application/http',
30
+ 'Content-Transfer-Encoding' => 'binary',
31
+ 'MIME-Version' => '1.0',
32
+ 'Content-Length' => ''
33
+ );
34
+
35
+ protected $url;
36
+ protected $requestMethod;
37
+ protected $requestHeaders;
38
+ protected $postBody;
39
+ protected $userAgent;
40
+
41
+ protected $responseHttpCode;
42
+ protected $responseHeaders;
43
+ protected $responseBody;
44
+
45
+ public $accessKey;
46
+
47
+ public function __construct($url, $method = 'GET', $headers = array(), $postBody = null) {
48
+ $this->setUrl($url);
49
+ $this->setRequestMethod($method);
50
+ $this->setRequestHeaders($headers);
51
+ $this->setPostBody($postBody);
52
+
53
+ global $apiConfig;
54
+ if (empty($apiConfig['application_name'])) {
55
+ $this->userAgent = self::USER_AGENT_SUFFIX;
56
+ } else {
57
+ $this->userAgent = $apiConfig['application_name'] . " " . self::USER_AGENT_SUFFIX;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Misc function that returns the base url component of the $url
63
+ * used by the OAuth signing class to calculate the base string
64
+ * @return string The base url component of the $url.
65
+ * @see http://oauth.net/core/1.0a/#anchor13
66
+ */
67
+ public function getBaseUrl() {
68
+ if ($pos = strpos($this->url, '?')) {
69
+ return substr($this->url, 0, $pos);
70
+ }
71
+ return $this->url;
72
+ }
73
+
74
+ /**
75
+ * Misc function that returns an array of the query parameters of the current
76
+ * url used by the OAuth signing class to calculate the signature
77
+ * @return array Query parameters in the query string.
78
+ */
79
+ public function getQueryParams() {
80
+ if ($pos = strpos($this->url, '?')) {
81
+ $queryStr = substr($this->url, $pos + 1);
82
+ $params = array();
83
+ parse_str($queryStr, $params);
84
+ return $params;
85
+ }
86
+ return array();
87
+ }
88
+
89
+ /**
90
+ * @return string HTTP Response Code.
91
+ */
92
+ public function getResponseHttpCode() {
93
+ return (int) $this->responseHttpCode;
94
+ }
95
+
96
+ /**
97
+ * @param int $responseHttpCode HTTP Response Code.
98
+ */
99
+ public function setResponseHttpCode($responseHttpCode) {
100
+ $this->responseHttpCode = $responseHttpCode;
101
+ }
102
+
103
+ /**
104
+ * @return $responseHeaders (array) HTTP Response Headers.
105
+ */
106
+ public function getResponseHeaders() {
107
+ return $this->responseHeaders;
108
+ }
109
+
110
+ /**
111
+ * @return string HTTP Response Body
112
+ */
113
+ public function getResponseBody() {
114
+ return $this->responseBody;
115
+ }
116
+
117
+ /**
118
+ * @param array $headers The HTTP response headers
119
+ * to be normalized.
120
+ */
121
+ public function setResponseHeaders($headers) {
122
+ $headers = Google_Utils::normalize($headers);
123
+ if ($this->responseHeaders) {
124
+ $headers = array_merge($this->responseHeaders, $headers);
125
+ }
126
+
127
+ $this->responseHeaders = $headers;
128
+ }
129
+
130
+ /**
131
+ * @param string $key
132
+ * @return array|boolean Returns the requested HTTP header or
133
+ * false if unavailable.
134
+ */
135
+ public function getResponseHeader($key) {
136
+ return isset($this->responseHeaders[$key])
137
+ ? $this->responseHeaders[$key]
138
+ : false;
139
+ }
140
+
141
+ /**
142
+ * @param string $responseBody The HTTP response body.
143
+ */
144
+ public function setResponseBody($responseBody) {
145
+ $this->responseBody = $responseBody;
146
+ }
147
+
148
+ /**
149
+ * @return string $url The request URL.
150
+ */
151
+
152
+ public function getUrl() {
153
+ return $this->url;
154
+ }
155
+
156
+ /**
157
+ * @return string $method HTTP Request Method.
158
+ */
159
+ public function getRequestMethod() {
160
+ return $this->requestMethod;
161
+ }
162
+
163
+ /**
164
+ * @return array $headers HTTP Request Headers.
165
+ */
166
+ public function getRequestHeaders() {
167
+ return $this->requestHeaders;
168
+ }
169
+
170
+ /**
171
+ * @param string $key
172
+ * @return array|boolean Returns the requested HTTP header or
173
+ * false if unavailable.
174
+ */
175
+ public function getRequestHeader($key) {
176
+ return isset($this->requestHeaders[$key])
177
+ ? $this->requestHeaders[$key]
178
+ : false;
179
+ }
180
+
181
+ /**
182
+ * @return string $postBody HTTP Request Body.
183
+ */
184
+ public function getPostBody() {
185
+ return $this->postBody;
186
+ }
187
+
188
+ /**
189
+ * @param string $url the url to set
190
+ */
191
+ public function setUrl($url) {
192
+ if (substr($url, 0, 4) == 'http') {
193
+ $this->url = $url;
194
+ } else {
195
+ // Force the path become relative.
196
+ if (substr($url, 0, 1) !== '/') {
197
+ $url = '/' . $url;
198
+ }
199
+ global $apiConfig;
200
+ $this->url = $apiConfig['basePath'] . $url;
201
+ }
202
+ }
203
+
204
+ /**
205
+ * @param string $method Set he HTTP Method and normalize
206
+ * it to upper-case, as required by HTTP.
207
+ *
208
+ */
209
+ public function setRequestMethod($method) {
210
+ $this->requestMethod = strtoupper($method);
211
+ }
212
+
213
+ /**
214
+ * @param array $headers The HTTP request headers
215
+ * to be set and normalized.
216
+ */
217
+ public function setRequestHeaders($headers) {
218
+ $headers = Google_Utils::normalize($headers);
219
+ if ($this->requestHeaders) {
220
+ $headers = array_merge($this->requestHeaders, $headers);
221
+ }
222
+ $this->requestHeaders = $headers;
223
+ }
224
+
225
+ /**
226
+ * @param string $postBody the postBody to set
227
+ */
228
+ public function setPostBody($postBody) {
229
+ $this->postBody = $postBody;
230
+ }
231
+
232
+ /**
233
+ * Set the User-Agent Header.
234
+ * @param string $userAgent The User-Agent.
235
+ */
236
+ public function setUserAgent($userAgent) {
237
+ $this->userAgent = $userAgent;
238
+ }
239
+
240
+ /**
241
+ * @return string The User-Agent.
242
+ */
243
+ public function getUserAgent() {
244
+ return $this->userAgent;
245
+ }
246
+
247
+ /**
248
+ * Returns a cache key depending on if this was an OAuth signed request
249
+ * in which case it will use the non-signed url and access key to make this
250
+ * cache key unique per authenticated user, else use the plain request url
251
+ * @return string The md5 hash of the request cache key.
252
+ */
253
+ public function getCacheKey() {
254
+ $key = $this->getUrl();
255
+
256
+ if (isset($this->accessKey)) {
257
+ $key .= $this->accessKey;
258
+ }
259
+
260
+ if (isset($this->requestHeaders['authorization'])) {
261
+ $key .= $this->requestHeaders['authorization'];
262
+ }
263
+
264
+ return md5($key);
265
+ }
266
+
267
+ public function getParsedCacheControl() {
268
+ $parsed = array();
269
+ $rawCacheControl = $this->getResponseHeader('cache-control');
270
+ if ($rawCacheControl) {
271
+ $rawCacheControl = str_replace(', ', '&', $rawCacheControl);
272
+ parse_str($rawCacheControl, $parsed);
273
+ }
274
+
275
+ return $parsed;
276
+ }
277
+
278
+ /**
279
+ * @param string $id
280
+ * @return string A string representation of the HTTP Request.
281
+ */
282
+ public function toBatchString($id) {
283
+ $str = '';
284
+ foreach($this->batchHeaders as $key => $val) {
285
+ $str .= $key . ': ' . $val . "\n";
286
+ }
287
+
288
+ $str .= "Content-ID: $id\n";
289
+ $str .= "\n";
290
+
291
+ $path = parse_url($this->getUrl(), PHP_URL_PATH);
292
+ $str .= $this->getRequestMethod() . ' ' . $path . " HTTP/1.1\n";
293
+ foreach($this->getRequestHeaders() as $key => $val) {
294
+ $str .= $key . ': ' . $val . "\n";
295
+ }
296
+
297
+ if ($this->getPostBody()) {
298
+ $str .= "\n";
299
+ $str .= $this->getPostBody();
300
+ }
301
+
302
+ return $str;
303
+ }
304
+ }
admin/api-libs/google/io/Google_HttpStreamIO.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2013 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
+ /**
19
+ * Http Streams based implementation of Google_IO.
20
+ *
21
+ * @author Stuart Langley <slangley@google.com>
22
+ */
23
+
24
+ require_once 'Google_CacheParser.php';
25
+
26
+ class Google_HttpStreamIO extends Google_IO {
27
+
28
+ private static $ENTITY_HTTP_METHODS = array("POST" => null, "PUT" => null);
29
+
30
+ private static $DEFAULT_HTTP_CONTEXT = array(
31
+ "follow_location" => 0,
32
+ "ignore_errors" => 1,
33
+ );
34
+
35
+ private static $DEFAULT_SSL_CONTEXT = array(
36
+ "verify_peer" => true,
37
+ );
38
+
39
+ /**
40
+ * Perform an authenticated / signed apiHttpRequest.
41
+ * This function takes the apiHttpRequest, calls apiAuth->sign on it
42
+ * (which can modify the request in what ever way fits the auth mechanism)
43
+ * and then calls Google_HttpStreamIO::makeRequest on the signed request
44
+ *
45
+ * @param Google_HttpRequest $request
46
+ * @return Google_HttpRequest The resulting HTTP response including the
47
+ * responseHttpCode, responseHeaders and responseBody.
48
+ */
49
+ public function authenticatedRequest(Google_HttpRequest $request) {
50
+ $request = Google_Client::$auth->sign($request);
51
+ return $this->makeRequest($request);
52
+ }
53
+
54
+ /**
55
+ * Execute a apiHttpRequest
56
+ *
57
+ * @param Google_HttpRequest $request the http request to be executed
58
+ * @return Google_HttpRequest http request with the response http code,
59
+ * response headers and response body filled in
60
+ * @throws Google_IOException on curl or IO error
61
+ */
62
+ public function makeRequest(Google_HttpRequest $request) {
63
+ // First, check to see if we have a valid cached version.
64
+ $cached = $this->getCachedRequest($request);
65
+ if ($cached !== false) {
66
+ if (!$this->checkMustRevaliadateCachedRequest($cached, $request)) {
67
+ return $cached;
68
+ }
69
+ }
70
+
71
+ $default_options = stream_context_get_options(stream_context_get_default());
72
+
73
+ $requestHttpContext = array_key_exists('http', $default_options) ?
74
+ $default_options['http'] : array();
75
+ if (array_key_exists($request->getRequestMethod(),
76
+ self::$ENTITY_HTTP_METHODS)) {
77
+ $request = $this->processEntityRequest($request);
78
+ }
79
+
80
+ if ($request->getPostBody()) {
81
+ $requestHttpContext["content"] = $request->getPostBody();
82
+ }
83
+
84
+ $requestHeaders = $request->getRequestHeaders();
85
+ if ($requestHeaders && is_array($requestHeaders)) {
86
+ $headers = "";
87
+ foreach($requestHeaders as $k => $v) {
88
+ $headers .= "$k: $v\n";
89
+ }
90
+ $requestHttpContext["header"] = $headers;
91
+ }
92
+
93
+ $requestHttpContext["method"] = $request->getRequestMethod();
94
+ $requestHttpContext["user_agent"] = $request->getUserAgent();
95
+
96
+ $requestSslContext = array_key_exists('ssl', $default_options) ?
97
+ $default_options['ssl'] : array();
98
+
99
+ if (!array_key_exists("cafile", $requestSslContext)) {
100
+ $requestSslContext["cafile"] = dirname(__FILE__) . '/cacerts.pem';
101
+ }
102
+
103
+ $options = array("http" => array_merge(self::$DEFAULT_HTTP_CONTEXT,
104
+ $requestHttpContext),
105
+ "ssl" => array_merge(self::$DEFAULT_SSL_CONTEXT,
106
+ $requestSslContext));
107
+
108
+ $context = stream_context_create($options);
109
+
110
+ $response_data = file_get_contents($request->getUrl(),
111
+ false,
112
+ $context);
113
+
114
+ if (false === $response_data) {
115
+ throw new Google_IOException("HTTP Error: Unable to connect");
116
+ }
117
+
118
+ $respHttpCode = $this->getHttpResponseCode($http_response_header);
119
+ $responseHeaders = $this->getHttpResponseHeaders($http_response_header);
120
+
121
+ if ($respHttpCode == 304 && $cached) {
122
+ // If the server responded NOT_MODIFIED, return the cached request.
123
+ $this->updateCachedRequest($cached, $responseHeaders);
124
+ return $cached;
125
+ }
126
+
127
+ $request->setResponseHttpCode($respHttpCode);
128
+ $request->setResponseHeaders($responseHeaders);
129
+ $request->setResponseBody($response_data);
130
+ // Store the request in cache (the function checks to see if the request
131
+ // can actually be cached)
132
+ $this->setCachedRequest($request);
133
+ return $request;
134
+ }
135
+
136
+ /**
137
+ * Set options that update the transport implementation's behavior.
138
+ * @param $options
139
+ */
140
+ public function setOptions($options) {
141
+ }
142
+
143
+ private function getHttpResponseCode($response_headers) {
144
+ $header_count = count($response_headers);
145
+
146
+ for ($i = 0; $i < $header_count; $i++) {
147
+ $header = $response_headers[$i];
148
+ if (strncasecmp("HTTP", $header, strlen("HTTP")) == 0) {
149
+ $response = explode(' ', $header);
150
+ return $response[1];
151
+ }
152
+ }
153
+ return 'UNKNOWN';
154
+ }
155
+
156
+ private function getHttpResponseHeaders($response_headers) {
157
+ $header_count = count($response_headers);
158
+ $headers = array();
159
+
160
+ for ($i = 0; $i < $header_count; $i++) {
161
+ $header = $response_headers[$i];
162
+ $header_parts = explode(':', $header);
163
+ if (count($header_parts) == 2) {
164
+ $headers[$header_parts[0]] = $header_parts[1];
165
+ }
166
+ }
167
+
168
+ return $headers;
169
+ }
170
+ }
admin/api-libs/google/io/Google_IO.php ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ require_once 'io/Google_HttpRequest.php';
19
+ require_once 'io/Google_HttpStreamIO.php';
20
+ require_once 'io/Google_CurlIO.php';
21
+ require_once 'io/Google_REST.php';
22
+
23
+ /**
24
+ * Abstract IO class
25
+ *
26
+ * @author Chris Chabot <chabotc@google.com>
27
+ */
28
+ abstract class Google_IO {
29
+ const CONNECTION_ESTABLISHED = "HTTP/1.0 200 Connection established\r\n\r\n";
30
+ const FORM_URLENCODED = 'application/x-www-form-urlencoded';
31
+ /**
32
+ * An utility function that first calls $this->auth->sign($request) and then executes makeRequest()
33
+ * on that signed request. Used for when a request should be authenticated
34
+ * @param Google_HttpRequest $request
35
+ * @return Google_HttpRequest $request
36
+ */
37
+ abstract function authenticatedRequest(Google_HttpRequest $request);
38
+
39
+ /**
40
+ * Executes a apIHttpRequest and returns the resulting populated httpRequest
41
+ * @param Google_HttpRequest $request
42
+ * @return Google_HttpRequest $request
43
+ */
44
+ abstract function makeRequest(Google_HttpRequest $request);
45
+
46
+ /**
47
+ * Set options that update the transport implementation's behavior.
48
+ * @param $options
49
+ */
50
+ abstract function setOptions($options);
51
+
52
+ /**
53
+ * @visible for testing.
54
+ * Cache the response to an HTTP request if it is cacheable.
55
+ * @param Google_HttpRequest $request
56
+ * @return bool Returns true if the insertion was successful.
57
+ * Otherwise, return false.
58
+ */
59
+ protected function setCachedRequest(Google_HttpRequest $request) {
60
+ // Determine if the request is cacheable.
61
+ if (Google_CacheParser::isResponseCacheable($request)) {
62
+ Google_Client::$cache->set($request->getCacheKey(), $request);
63
+ return true;
64
+ }
65
+
66
+ return false;
67
+ }
68
+
69
+ /**
70
+ * @visible for testing.
71
+ * @param Google_HttpRequest $request
72
+ * @return Google_HttpRequest|bool Returns the cached object or
73
+ * false if the operation was unsuccessful.
74
+ */
75
+ protected function getCachedRequest(Google_HttpRequest $request) {
76
+ if (false == Google_CacheParser::isRequestCacheable($request)) {
77
+ false;
78
+ }
79
+
80
+ return Google_Client::$cache->get($request->getCacheKey());
81
+ }
82
+
83
+ /**
84
+ * @visible for testing
85
+ * Process an http request that contains an enclosed entity.
86
+ * @param Google_HttpRequest $request
87
+ * @return Google_HttpRequest Processed request with the enclosed entity.
88
+ */
89
+ protected function processEntityRequest(Google_HttpRequest $request) {
90
+ $postBody = $request->getPostBody();
91
+ $contentType = $request->getRequestHeader("content-type");
92
+
93
+ // Set the default content-type as application/x-www-form-urlencoded.
94
+ if (false == $contentType) {
95
+ $contentType = self::FORM_URLENCODED;
96
+ $request->setRequestHeaders(array('content-type' => $contentType));
97
+ }
98
+
99
+ // Force the payload to match the content-type asserted in the header.
100
+ if ($contentType == self::FORM_URLENCODED && is_array($postBody)) {
101
+ $postBody = http_build_query($postBody, '', '&');
102
+ $request->setPostBody($postBody);
103
+ }
104
+
105
+ // Make sure the content-length header is set.
106
+ if (!$postBody || is_string($postBody)) {
107
+ $postsLength = strlen($postBody);
108
+ $request->setRequestHeaders(array('content-length' => $postsLength));
109
+ }
110
+
111
+ return $request;
112
+ }
113
+
114
+ /**
115
+ * Check if an already cached request must be revalidated, and if so update
116
+ * the request with the correct ETag headers.
117
+ * @param Google_HttpRequest $cached A previously cached response.
118
+ * @param Google_HttpRequest $request The outbound request.
119
+ * return bool If the cached object needs to be revalidated, false if it is
120
+ * still current and can be re-used.
121
+ */
122
+ protected function checkMustRevaliadateCachedRequest($cached, $request) {
123
+ if (Google_CacheParser::mustRevalidate($cached)) {
124
+ $addHeaders = array();
125
+ if ($cached->getResponseHeader('etag')) {
126
+ // [13.3.4] If an entity tag has been provided by the origin server,
127
+ // we must use that entity tag in any cache-conditional request.
128
+ $addHeaders['If-None-Match'] = $cached->getResponseHeader('etag');
129
+ } elseif ($cached->getResponseHeader('date')) {
130
+ $addHeaders['If-Modified-Since'] = $cached->getResponseHeader('date');
131
+ }
132
+
133
+ $request->setRequestHeaders($addHeaders);
134
+ return true;
135
+ } else {
136
+ return false;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Update a cached request, using the headers from the last response.
142
+ * @param Google_HttpRequest $cached A previously cached response.
143
+ * @param mixed Associative array of response headers from the last request.
144
+ */
145
+ protected function updateCachedRequest($cached, $responseHeaders) {
146
+ if (isset($responseHeaders['connection'])) {
147
+ $hopByHop = array_merge(
148
+ self::$HOP_BY_HOP,
149
+ explode(',', $responseHeaders['connection'])
150
+ );
151
+
152
+ $endToEnd = array();
153
+ foreach($hopByHop as $key) {
154
+ if (isset($responseHeaders[$key])) {
155
+ $endToEnd[$key] = $responseHeaders[$key];
156
+ }
157
+ }
158
+ $cached->setResponseHeaders($endToEnd);
159
+ }
160
+ }
161
+ }
admin/api-libs/google/io/Google_REST.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
19
+ * This class implements the RESTful transport of apiServiceRequest()'s
20
+ *
21
+ * @author Chris Chabot <chabotc@google.com>
22
+ * @author Chirag Shah <chirags@google.com>
23
+ */
24
+ class Google_REST {
25
+ /**
26
+ * Executes a apiServiceRequest using a RESTful call by transforming it into
27
+ * an apiHttpRequest, and executed via apiIO::authenticatedRequest().
28
+ *
29
+ * @param Google_HttpRequest $req
30
+ * @return array decoded result
31
+ * @throws Google_ServiceException on server side error (ie: not authenticated,
32
+ * invalid or malformed post body, invalid url)
33
+ */
34
+ static public function execute(Google_HttpRequest $req) {
35
+ $httpRequest = Google_Client::$io->makeRequest($req);
36
+ $decodedResponse = self::decodeHttpResponse($httpRequest);
37
+ $ret = isset($decodedResponse['data'])
38
+ ? $decodedResponse['data'] : $decodedResponse;
39
+ return $ret;
40
+ }
41
+
42
+
43
+ /**
44
+ * Decode an HTTP Response.
45
+ * @static
46
+ * @throws Google_ServiceException
47
+ * @param Google_HttpRequest $response The http response to be decoded.
48
+ * @return mixed|null
49
+ */
50
+ public static function decodeHttpResponse($response) {
51
+ $code = $response->getResponseHttpCode();
52
+ $body = $response->getResponseBody();
53
+ $decoded = null;
54
+
55
+ if ((intVal($code)) >= 300) {
56
+ $decoded = json_decode($body, true);
57
+ $err = 'Error calling ' . $response->getRequestMethod() . ' ' . $response->getUrl();
58
+ if ($decoded != null && isset($decoded['error']['message']) && isset($decoded['error']['code'])) {
59
+ // if we're getting a json encoded error definition, use that instead of the raw response
60
+ // body for improved readability
61
+ $err .= ": ({$decoded['error']['code']}) {$decoded['error']['message']}";
62
+ } else {
63
+ $err .= ": ($code) $body";
64
+ }
65
+
66
+ throw new Google_ServiceException($err, $code, null, $decoded['error']['errors']);
67
+ }
68
+
69
+ // Only attempt to decode the response, if the response code wasn't (204) 'no content'
70
+ if ($code != '204') {
71
+ $decoded = json_decode($body, true);
72
+ if ($decoded === null || $decoded === "") {
73
+ throw new Google_ServiceException("Invalid json in service response: $body");
74
+ }
75
+ }
76
+ return $decoded;
77
+ }
78
+
79
+ /**
80
+ * Parse/expand request parameters and create a fully qualified
81
+ * request uri.
82
+ * @static
83
+ * @param string $servicePath
84
+ * @param string $restPath
85
+ * @param array $params
86
+ * @return string $requestUrl
87
+ */
88
+ static function createRequestUri($servicePath, $restPath, $params) {
89
+ $requestUrl = $servicePath . $restPath;
90
+ $uriTemplateVars = array();
91
+ $queryVars = array();
92
+ foreach ($params as $paramName => $paramSpec) {
93
+ // Discovery v1.0 puts the canonical location under the 'location' field.
94
+ if (! isset($paramSpec['location'])) {
95
+ $paramSpec['location'] = $paramSpec['restParameterType'];
96
+ }
97
+
98
+ if ($paramSpec['type'] == 'boolean') {
99
+ $paramSpec['value'] = ($paramSpec['value']) ? 'true' : 'false';
100
+ }
101
+ if ($paramSpec['location'] == 'path') {
102
+ $uriTemplateVars[$paramName] = $paramSpec['value'];
103
+ } else {
104
+ if (isset($paramSpec['repeated']) && is_array($paramSpec['value'])) {
105
+ foreach ($paramSpec['value'] as $value) {
106
+ $queryVars[] = $paramName . '=' . rawurlencode($value);
107
+ }
108
+ } else {
109
+ $queryVars[] = $paramName . '=' . rawurlencode($paramSpec['value']);
110
+ }
111
+ }
112
+ }
113
+
114
+ if (count($uriTemplateVars)) {
115
+ $uriTemplateParser = new URI_Template_Parser($requestUrl);
116
+ $requestUrl = $uriTemplateParser->expand($uriTemplateVars);
117
+ }
118
+ //FIXME work around for the the uri template lib which url encodes
119
+ // the @'s & confuses our servers.
120
+ $requestUrl = str_replace('%40', '@', $requestUrl);
121
+
122
+ if (count($queryVars)) {
123
+ $requestUrl .= '?' . implode($queryVars, '&');
124
+ }
125
+
126
+ return $requestUrl;
127
+ }
128
+ }
admin/api-libs/google/io/cacerts.pem ADDED
@@ -0,0 +1,738 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Certifcate Authority certificates for validating SSL connections.
2
+ #
3
+ # This file contains PEM format certificates generated from
4
+ # http://mxr.mozilla.org/seamonkey/source/security/nss/lib/ckfw/builtins/certdata.txt
5
+ #
6
+ # ***** BEGIN LICENSE BLOCK *****
7
+ # Version: MPL 1.1/GPL 2.0/LGPL 2.1
8
+ #
9
+ # The contents of this file are subject to the Mozilla Public License Version
10
+ # 1.1 (the "License"); you may not use this file except in compliance with
11
+ # the License. You may obtain a copy of the License at
12
+ # http://www.mozilla.org/MPL/
13
+ #
14
+ # Software distributed under the License is distributed on an "AS IS" basis,
15
+ # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16
+ # for the specific language governing rights and limitations under the
17
+ # License.
18
+ #
19
+ # The Original Code is the Netscape security libraries.
20
+ #
21
+ # The Initial Developer of the Original Code is
22
+ # Netscape Communications Corporation.
23
+ # Portions created by the Initial Developer are Copyright (C) 1994-2000
24
+ # the Initial Developer. All Rights Reserved.
25
+ #
26
+ # Contributor(s):
27
+ #
28
+ # Alternatively, the contents of this file may be used under the terms of
29
+ # either the GNU General Public License Version 2 or later (the "GPL"), or
30
+ # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31
+ # in which case the provisions of the GPL or the LGPL are applicable instead
32
+ # of those above. If you wish to allow use of your version of this file only
33
+ # under the terms of either the GPL or the LGPL, and not to allow others to
34
+ # use your version of this file under the terms of the MPL, indicate your
35
+ # decision by deleting the provisions above and replace them with the notice
36
+ # and other provisions required by the GPL or the LGPL. If you do not delete
37
+ # the provisions above, a recipient may use your version of this file under
38
+ # the terms of any one of the MPL, the GPL or the LGPL.
39
+ #
40
+ # ***** END LICENSE BLOCK *****
41
+
42
+ Verisign/RSA Secure Server CA
43
+ =============================
44
+
45
+ -----BEGIN CERTIFICATE-----
46
+ MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzELMAkG
47
+ A1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
48
+ VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk0
49
+ MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UEBhMCVVMxIDAeBgNV
50
+ BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2Vy
51
+ dmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGbMA0GCSqGSIb3DQEBAQUAA4GJ
52
+ ADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6OLDfO6zV4ZFQD5YRAUcm/jwjiioII
53
+ 0haGN1XpsSECrXZogZoFokvJSyVmIlZsiAeP94FZbYQHZXATcXY+m3dM41CJVphI
54
+ uR2nKRoTLkoRWZweFdVJVCxzOmmCsZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZI
55
+ hvcNAQECBQADfgBl3X7hsuyw4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3
56
+ YQO2WxZpO8ZECAyIUwxrl0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc
57
+ 1/p3yjkWWW8O6tO1g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
58
+ -----END CERTIFICATE-----
59
+
60
+ Thawte Personal Basic CA
61
+ ========================
62
+
63
+ -----BEGIN CERTIFICATE-----
64
+ MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMCWkEx
65
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
66
+ VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
67
+ ZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBlcnNvbmFsIEJhc2lj
68
+ IENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNpY0B0aGF3dGUuY29tMB4X
69
+ DTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgcsxCzAJBgNVBAYTAlpBMRUw
70
+ EwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UE
71
+ ChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2Vy
72
+ dmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBD
73
+ QTEoMCYGCSqGSIb3DQEJARYZcGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzAN
74
+ BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53
75
+ dXLdjUmbllegeNTKP1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdK
76
+ wPQIcOk8RHtQfmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7
77
+ G1sY0b8jkyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQF
78
+ AAOBgQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
79
+ c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95B21P
80
+ 9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
81
+ -----END CERTIFICATE-----
82
+
83
+ Thawte Personal Premium CA
84
+ ==========================
85
+
86
+ -----BEGIN CERTIFICATE-----
87
+ MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMCWkEx
88
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
89
+ VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
90
+ ZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBlcnNvbmFsIFByZW1p
91
+ dW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXByZW1pdW1AdGhhd3RlLmNv
92
+ bTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5NTlaMIHPMQswCQYDVQQGEwJa
93
+ QTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIwEAYDVQQHEwlDYXBlIFRvd24xGjAY
94
+ BgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5nMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9u
95
+ IFNlcnZpY2VzIERpdmlzaW9uMSMwIQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJl
96
+ bWl1bSBDQTEqMCgGCSqGSIb3DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUu
97
+ Y29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0Vs
98
+ Bd/eJxZRNkERbGw77f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWI
99
+ Et12TfIa/G8jHnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYD
100
+ ZicRFTuqW/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqG
101
+ SIb3DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
102
+ b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVxeTBh
103
+ KXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1KzGJ
104
+ -----END CERTIFICATE-----
105
+
106
+ Thawte Personal Freemail CA
107
+ ===========================
108
+
109
+ -----BEGIN CERTIFICATE-----
110
+ MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMCWkEx
111
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMRowGAYD
112
+ VQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBT
113
+ ZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBlcnNvbmFsIEZyZWVt
114
+ YWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1mcmVlbWFpbEB0aGF3dGUu
115
+ Y29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgdExCzAJBgNVBAYT
116
+ AlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEa
117
+ MBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAmBgNVBAsTH0NlcnRpZmljYXRp
118
+ b24gU2VydmljZXMgRGl2aXNpb24xJDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBG
119
+ cmVlbWFpbCBDQTErMCkGCSqGSIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhh
120
+ d3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfY
121
+ DFG26nKRsIRefS0Nj3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5E
122
+ rHzmj+hND3EfQDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVq
123
+ uzgkCGqYx7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
124
+ BgkqhkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
125
+ MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgCneSa
126
+ /RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr5PjRznei
127
+ gQ==
128
+ -----END CERTIFICATE-----
129
+
130
+ Thawte Server CA
131
+ ================
132
+
133
+ -----BEGIN CERTIFICATE-----
134
+ MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMCWkEx
135
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
136
+ VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
137
+ biBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEm
138
+ MCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wHhcNOTYwODAx
139
+ MDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkGA1UEBhMCWkExFTATBgNVBAgT
140
+ DFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYDVQQKExRUaGF3
141
+ dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNl
142
+ cyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3
143
+ DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
144
+ gY0AMIGJAoGBANOkUG7I/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91
145
+ yekIYfUGbTBuFRkC6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCX
146
+ L+eQbcAoQpnXTEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGj
147
+ EzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG
148
+ 7oWDTSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
149
+ QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdniTCxZ
150
+ qdq5snUb9kLy78fyGPmJvKP/iiMucEc=
151
+ -----END CERTIFICATE-----
152
+
153
+ Thawte Premium Server CA
154
+ ========================
155
+
156
+ -----BEGIN CERTIFICATE-----
157
+ MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMCWkEx
158
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3duMR0wGwYD
159
+ VQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2VydGlmaWNhdGlv
160
+ biBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFByZW1pdW0gU2Vy
161
+ dmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNlcnZlckB0aGF3dGUuY29t
162
+ MB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVowgc4xCzAJBgNVBAYTAlpB
163
+ MRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNVBAcTCUNhcGUgVG93bjEdMBsG
164
+ A1UEChMUVGhhd3RlIENvbnN1bHRpbmcgY2MxKDAmBgNVBAsTH0NlcnRpZmljYXRp
165
+ b24gU2VydmljZXMgRGl2aXNpb24xITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNl
166
+ cnZlciBDQTEoMCYGCSqGSIb3DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNv
167
+ bTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkE
168
+ VdbQ7xwblRZH7xhINTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQ
169
+ ug2SBhRz1JPLlyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMR
170
+ uHM/qgeN9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG
171
+ 9w0BAQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
172
+ hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZa4JM
173
+ pAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcUQg==
174
+ -----END CERTIFICATE-----
175
+
176
+ Equifax Secure CA
177
+ =================
178
+
179
+ -----BEGIN CERTIFICATE-----
180
+ MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
181
+ UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy
182
+ dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1
183
+ MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx
184
+ dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B
185
+ AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f
186
+ BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A
187
+ cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC
188
+ AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ
189
+ MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm
190
+ aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw
191
+ ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj
192
+ IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF
193
+ MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
194
+ A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
195
+ 7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh
196
+ 1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
197
+ -----END CERTIFICATE-----
198
+
199
+ Verisign Class 1 Public Primary Certification Authority
200
+ =======================================================
201
+
202
+ -----BEGIN CERTIFICATE-----
203
+ MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8xCzAJ
204
+ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh
205
+ c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05
206
+ NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNVBAYTAlVTMRcwFQYD
207
+ VQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xhc3MgMSBQdWJsaWMgUHJp
208
+ bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOB
209
+ jQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3noaACpEO+jglr0aIguVzqKCbJF0N
210
+ H8xlbgyw0FaEGIeaBpsQoXPftFg5a27B9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR
211
+ 4k5FVmkfeAKA2txHkSm7NsljXMXg1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATAN
212
+ BgkqhkiG9w0BAQIFAAOBgQBMP7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZo
213
+ EWx8QszznC7EBz8UsA9P/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5
214
+ FvjqBUuUfx3CHMjjt/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89Fx
215
+ lA==
216
+ -----END CERTIFICATE-----
217
+
218
+ Verisign Class 2 Public Primary Certification Authority
219
+ =======================================================
220
+
221
+ -----BEGIN CERTIFICATE-----
222
+ MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzELMAkG
223
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
224
+ cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
225
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
226
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAyIFB1YmxpYyBQcmlt
227
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
228
+ ADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZMJaLtVRKXxaeAufqDwSCg+i8VDXyh
229
+ YGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvEerf4Zh+AVPy3wo5ZShRXRtGak75BkQO7
230
+ FYCTXOvnzAhsPz6zSvz/S2wj1VCCJkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0G
231
+ CSqGSIb3DQEBAgUAA4GBAIobK/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxg
232
+ J8pFUs4W7z8GZOeUaHxgMxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Nc
233
+ r6Pc5iaAIzy4RHT3Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY
234
+ -----END CERTIFICATE-----
235
+
236
+ Verisign Class 3 Public Primary Certification Authority
237
+ =======================================================
238
+
239
+ -----BEGIN CERTIFICATE-----
240
+ MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
241
+ A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
242
+ cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
243
+ MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
244
+ BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
245
+ YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
246
+ ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
247
+ BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
248
+ I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
249
+ CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
250
+ lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
251
+ AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
252
+ -----END CERTIFICATE-----
253
+
254
+ Verisign Class 1 Public Primary Certification Authority - G2
255
+ ============================================================
256
+
257
+ -----BEGIN CERTIFICATE-----
258
+ MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
259
+ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
260
+ c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
261
+ MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
262
+ emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
263
+ DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
264
+ FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMSBQdWJsaWMg
265
+ UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
266
+ YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
267
+ MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
268
+ AQUAA4GNADCBiQKBgQCq0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYK
269
+ VdPfQ4chEWWKfo+9Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSm
270
+ Fc/IReumXY6cPvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQID
271
+ AQABMA0GCSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0J
272
+ h9ZrbWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul
273
+ uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4iP/68
274
+ DzFc6PLZ
275
+ -----END CERTIFICATE-----
276
+
277
+ Verisign Class 2 Public Primary Certification Authority - G2
278
+ ============================================================
279
+
280
+ -----BEGIN CERTIFICATE-----
281
+ MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHBMQsw
282
+ CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0Ns
283
+ YXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH
284
+ MjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9y
285
+ aXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazAe
286
+ Fw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTlaMIHBMQswCQYDVQQGEwJVUzEX
287
+ MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNVBAsTM0NsYXNzIDIgUHVibGlj
288
+ IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjE6MDgGA1UECxMx
289
+ KGMpIDE5OTggVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s
290
+ eTEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0B
291
+ AQEFAAOBjQAwgYkCgYEAp4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjM
292
+ HiwSViy4AWkszJkfrbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjw
293
+ DqL7MWzJ5m+ZJwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cC
294
+ AwEAATANBgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9ji
295
+ nb3/7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
296
+ rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6xRnIn
297
+ jBJ7xUS0rg==
298
+ -----END CERTIFICATE-----
299
+
300
+ Verisign Class 3 Public Primary Certification Authority - G2
301
+ ============================================================
302
+
303
+ -----BEGIN CERTIFICATE-----
304
+ MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
305
+ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
306
+ c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
307
+ MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
308
+ emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
309
+ DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
310
+ FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
311
+ UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
312
+ YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
313
+ MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
314
+ AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
315
+ pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
316
+ 13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
317
+ AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
318
+ U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
319
+ F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
320
+ oJ2daZH9
321
+ -----END CERTIFICATE-----
322
+
323
+ Verisign Class 4 Public Primary Certification Authority - G2
324
+ ============================================================
325
+
326
+ -----BEGIN CERTIFICATE-----
327
+ MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
328
+ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
329
+ c3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
330
+ MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
331
+ emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
332
+ DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
333
+ FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgNCBQdWJsaWMg
334
+ UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
335
+ YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
336
+ MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
337
+ AQUAA4GNADCBiQKBgQC68OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDM
338
+ HO0oW369atyzkSTKQWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtK
339
+ qsGgtG7rL+VXxbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwID
340
+ AQABMA0GCSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwj
341
+ cSGIL4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y
342
+ cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckztImRP
343
+ T8qAkbYp
344
+ -----END CERTIFICATE-----
345
+
346
+ Verisign Class 1 Public Primary Certification Authority - G3
347
+ ============================================================
348
+
349
+ -----BEGIN CERTIFICATE-----
350
+ MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw
351
+ CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
352
+ cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
353
+ LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
354
+ aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
355
+ dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
356
+ VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
357
+ aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
358
+ bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
359
+ IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
360
+ LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4
361
+ nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO
362
+ 8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV
363
+ ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb
364
+ PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2
365
+ 6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr
366
+ n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a
367
+ qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4
368
+ wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3
369
+ ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs
370
+ pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4
371
+ E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g==
372
+ -----END CERTIFICATE-----
373
+
374
+ Verisign Class 2 Public Primary Certification Authority - G3
375
+ ============================================================
376
+
377
+ -----BEGIN CERTIFICATE-----
378
+ MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ
379
+ BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy
380
+ aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s
381
+ IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp
382
+ Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0
383
+ eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV
384
+ BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp
385
+ Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu
386
+ Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g
387
+ Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt
388
+ IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU
389
+ J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO
390
+ JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY
391
+ wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o
392
+ koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN
393
+ qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E
394
+ Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe
395
+ xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u
396
+ 7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU
397
+ sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI
398
+ sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP
399
+ cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q
400
+ -----END CERTIFICATE-----
401
+
402
+ Verisign Class 3 Public Primary Certification Authority - G3
403
+ ============================================================
404
+
405
+ -----BEGIN CERTIFICATE-----
406
+ MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
407
+ CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
408
+ cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
409
+ LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
410
+ aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
411
+ dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
412
+ VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
413
+ aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
414
+ bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
415
+ IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
416
+ LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b
417
+ N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t
418
+ KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu
419
+ kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm
420
+ CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
421
+ Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu
422
+ imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te
423
+ 2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe
424
+ DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC
425
+ /Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p
426
+ F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt
427
+ TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
428
+ -----END CERTIFICATE-----
429
+
430
+ Verisign Class 4 Public Primary Certification Authority - G3
431
+ ============================================================
432
+
433
+ -----BEGIN CERTIFICATE-----
434
+ MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHKMQsw
435
+ CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
436
+ cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu
437
+ LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT
438
+ aWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
439
+ dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD
440
+ VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT
441
+ aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ
442
+ bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu
443
+ IENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg
444
+ LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1
445
+ GQ9Wzsy1HyDkniYlS+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ
446
+ +mGuqPKljYXCKtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0Gbd
447
+ U6LM8BDcVHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLm
448
+ NxdLMEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
449
+ ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDDZq3/
450
+ ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1WrIhVZPMq1
451
+ CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXttmhwwjIDLk5Mq
452
+ g6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csKvE+MW8VLADsfKoKm
453
+ fjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluPQSjA1egtTaRezarZ7c7c
454
+ 2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kPmF6Z6PDQpLv1U70qzlmwr25/
455
+ bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr9Xgn2uf3ZkPznoM+IKrDNWCRzg==
456
+ -----END CERTIFICATE-----
457
+
458
+ Equifax Secure Global eBusiness CA
459
+ ==================================
460
+
461
+ -----BEGIN CERTIFICATE-----
462
+ MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJVUzEc
463
+ MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1aWZheCBT
464
+ ZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0MDAwMFoXDTIw
465
+ MDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE0VxdWlmYXggU2Vj
466
+ dXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJlIEdsb2JhbCBlQnVzaW5l
467
+ c3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuucXkAJlsTRVPEnC
468
+ UdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQytd4zjTov2/KaelpzmKNc6fuKcxtc
469
+ 58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORROhI8bIpaVIRw28HFkM9yRcuoWcDNM50/
470
+ o5brhTMhHD4ePmBudpxnhcXIw2ECAwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAH
471
+ MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1dr
472
+ aGwwHQYDVR0OBBYEFL6ooHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUA
473
+ A4GBADDiAVGqx+pf2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkA
474
+ Z70Br83gcfxaz2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv
475
+ 8qIYNMR1pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
476
+ -----END CERTIFICATE-----
477
+
478
+ Equifax Secure eBusiness CA 1
479
+ =============================
480
+
481
+ -----BEGIN CERTIFICATE-----
482
+ MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJVUzEc
483
+ MBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBT
484
+ ZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcNMjAwNjIxMDQw
485
+ MDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5j
486
+ LjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwgZ8wDQYJ
487
+ KoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fek6lfWg0XTzQaDJj0ItlZ1MRo
488
+ RvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5/VGcqiTZ9J2DKocKIdMSODRsjQBu
489
+ WqDZQu4aIZX5UkxVWsUPOE9G+m34LjXWHXzr4vCwdYDIqROsvojvOm6rXyo4YgKw
490
+ Env+j6YDAgMBAAGjZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTAD
491
+ AQH/MB8GA1UdIwQYMBaAFEp4MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRK
492
+ eDJSEdtZFjZe38EUNkBqR3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZM
493
+ zfmpTMANmvPMZWnmJXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+
494
+ WB5Hh1Q+WKG1tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN
495
+ /Bf+KpYrtWKmpj29f5JZzVoqgrI3eQ==
496
+ -----END CERTIFICATE-----
497
+
498
+ Equifax Secure eBusiness CA 2
499
+ =============================
500
+
501
+ -----BEGIN CERTIFICATE-----
502
+ MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV
503
+ UzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2Vj
504
+ dXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5MDYyMzEyMTQ0
505
+ NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXggU2VjdXJlMSYwJAYD
506
+ VQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0EtMjCBnzANBgkqhkiG9w0B
507
+ AQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF7Y6yEb3+6+e0dMKP/wXn2Z0G
508
+ vxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKDpkWNYmi7hRsgcDKqQM2mll/EcTc/
509
+ BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HMHMg3OrmXUqesxWoklE6ce8/AatbfIb0C
510
+ AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEX
511
+ MBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJl
512
+ IGVCdXNpbmVzcyBDQS0yMQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkw
513
+ NjIzMTIxNDQ1WjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBq
514
+ y/3YIHqngnYwHQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQF
515
+ MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA
516
+ A4GBAAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
517
+ 0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkty3D1
518
+ E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
519
+ -----END CERTIFICATE-----
520
+
521
+ Thawte Time Stamping CA
522
+ =======================
523
+
524
+ -----BEGIN CERTIFICATE-----
525
+ MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMCWkEx
526
+ FTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
527
+ BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAd
528
+ BgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcwMTAxMDAwMDAwWhcN
529
+ MjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4g
530
+ Q2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsG
531
+ A1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1l
532
+ c3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANYrWHhhRYZT
533
+ 6jR7UZztsOYuGA7+4F+oJ9O0yeB8WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQa
534
+ Wt9MevPZQx08EHp5JduQ/vBR5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL
535
+ 8vg7ij5FrHGSALSQQZj7X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMB
536
+ Af8wDQYJKoZIhvcNAQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC
537
+ 9RAIDb/LogWK0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQ
538
+ pgCed/r8zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZ
539
+ CayJSdM=
540
+ -----END CERTIFICATE-----
541
+
542
+ thawte Primary Root CA
543
+ ======================
544
+
545
+ -----BEGIN CERTIFICATE-----
546
+ MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB
547
+ qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf
548
+ Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw
549
+ MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV
550
+ BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw
551
+ NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j
552
+ LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG
553
+ A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl
554
+ IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG
555
+ SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs
556
+ W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta
557
+ 3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk
558
+ 6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6
559
+ Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J
560
+ NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA
561
+ MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP
562
+ r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU
563
+ DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz
564
+ YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX
565
+ xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2
566
+ /qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/
567
+ LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7
568
+ jVaMaA==
569
+ -----END CERTIFICATE-----
570
+
571
+ VeriSign Class 3 Public Primary Certification Authority - G5
572
+ ============================================================
573
+
574
+ -----BEGIN CERTIFICATE-----
575
+ MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB
576
+ yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
577
+ ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp
578
+ U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW
579
+ ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0
580
+ aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL
581
+ MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW
582
+ ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln
583
+ biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp
584
+ U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
585
+ aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1
586
+ nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex
587
+ t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz
588
+ SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG
589
+ BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+
590
+ rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/
591
+ NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E
592
+ BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH
593
+ BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy
594
+ aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv
595
+ MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE
596
+ p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y
597
+ 5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK
598
+ WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ
599
+ 4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N
600
+ hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq
601
+ -----END CERTIFICATE-----
602
+
603
+ Entrust.net Secure Server Certification Authority
604
+ =================================================
605
+
606
+ -----BEGIN CERTIFICATE-----
607
+ MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
608
+ VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
609
+ ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
610
+ KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
611
+ ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
612
+ MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
613
+ ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
614
+ b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
615
+ bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
616
+ U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
617
+ A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
618
+ I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
619
+ wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
620
+ AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
621
+ oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
622
+ BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
623
+ dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
624
+ MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
625
+ b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
626
+ dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
627
+ MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
628
+ E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
629
+ MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
630
+ hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
631
+ 95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
632
+ 2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
633
+ -----END CERTIFICATE-----
634
+
635
+ Go Daddy Certification Authority Root Certificate Bundle
636
+ ========================================================
637
+
638
+ -----BEGIN CERTIFICATE-----
639
+ MIIE3jCCA8agAwIBAgICAwEwDQYJKoZIhvcNAQEFBQAwYzELMAkGA1UEBhMCVVMx
640
+ ITAfBgNVBAoTGFRoZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28g
641
+ RGFkZHkgQ2xhc3MgMiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjExMTYw
642
+ MTU0MzdaFw0yNjExMTYwMTU0MzdaMIHKMQswCQYDVQQGEwJVUzEQMA4GA1UECBMH
643
+ QXJpem9uYTETMBEGA1UEBxMKU2NvdHRzZGFsZTEaMBgGA1UEChMRR29EYWRkeS5j
644
+ b20sIEluYy4xMzAxBgNVBAsTKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5j
645
+ b20vcmVwb3NpdG9yeTEwMC4GA1UEAxMnR28gRGFkZHkgU2VjdXJlIENlcnRpZmlj
646
+ YXRpb24gQXV0aG9yaXR5MREwDwYDVQQFEwgwNzk2OTI4NzCCASIwDQYJKoZIhvcN
647
+ AQEBBQADggEPADCCAQoCggEBAMQt1RWMnCZM7DI161+4WQFapmGBWTtwY6vj3D3H
648
+ KrjJM9N55DrtPDAjhI6zMBS2sofDPZVUBJ7fmd0LJR4h3mUpfjWoqVTr9vcyOdQm
649
+ VZWt7/v+WIbXnvQAjYwqDL1CBM6nPwT27oDyqu9SoWlm2r4arV3aLGbqGmu75RpR
650
+ SgAvSMeYddi5Kcju+GZtCpyz8/x4fKL4o/K1w/O5epHBp+YlLpyo7RJlbmr2EkRT
651
+ cDCVw5wrWCs9CHRK8r5RsL+H0EwnWGu1NcWdrxcx+AuP7q2BNgWJCJjPOq8lh8BJ
652
+ 6qf9Z/dFjpfMFDniNoW1fho3/Rb2cRGadDAW/hOUoz+EDU8CAwEAAaOCATIwggEu
653
+ MB0GA1UdDgQWBBT9rGEyk2xF1uLuhV+auud2mWjM5zAfBgNVHSMEGDAWgBTSxLDS
654
+ kdRMEXGzYcs9of7dqGrU4zASBgNVHRMBAf8ECDAGAQH/AgEAMDMGCCsGAQUFBwEB
655
+ BCcwJTAjBggrBgEFBQcwAYYXaHR0cDovL29jc3AuZ29kYWRkeS5jb20wRgYDVR0f
656
+ BD8wPTA7oDmgN4Y1aHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNvbS9yZXBv
657
+ c2l0b3J5L2dkcm9vdC5jcmwwSwYDVR0gBEQwQjBABgRVHSAAMDgwNgYIKwYBBQUH
658
+ AgEWKmh0dHA6Ly9jZXJ0aWZpY2F0ZXMuZ29kYWRkeS5jb20vcmVwb3NpdG9yeTAO
659
+ BgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBANKGwOy9+aG2Z+5mC6IG
660
+ OgRQjhVyrEp0lVPLN8tESe8HkGsz2ZbwlFalEzAFPIUyIXvJxwqoJKSQ3kbTJSMU
661
+ A2fCENZvD117esyfxVgqwcSeIaha86ykRvOe5GPLL5CkKSkB2XIsKd83ASe8T+5o
662
+ 0yGPwLPk9Qnt0hCqU7S+8MxZC9Y7lhyVJEnfzuz9p0iRFEUOOjZv2kWzRaJBydTX
663
+ RE4+uXR21aITVSzGh6O1mawGhId/dQb8vxRMDsxuxN89txJx9OjxUUAiKEngHUuH
664
+ qDTMBqLdElrRhjZkAzVvb3du6/KFUJheqwNTrZEjYx8WnM25sgVjOuH0aBsXBTWV
665
+ U+4=
666
+ -----END CERTIFICATE-----
667
+ -----BEGIN CERTIFICATE-----
668
+ MIIE+zCCBGSgAwIBAgICAQ0wDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1Zh
669
+ bGlDZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
670
+ Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
671
+ QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAe
672
+ BgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTA0MDYyOTE3MDYyMFoX
673
+ DTI0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRoZSBHbyBE
674
+ YWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3MgMiBDZXJ0
675
+ aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgC
676
+ ggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCAPVYYYwhv
677
+ 2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6wwdhFJ2+q
678
+ N1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXiEqITLdiO
679
+ r18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMYavx4A6lN
680
+ f4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+YihfukEH
681
+ U1jPEX44dMX4/7VpkI+EdOqXG68CAQOjggHhMIIB3TAdBgNVHQ4EFgQU0sSw0pHU
682
+ TBFxs2HLPaH+3ahq1OMwgdIGA1UdIwSByjCBx6GBwaSBvjCBuzEkMCIGA1UEBxMb
683
+ VmFsaUNlcnQgVmFsaWRhdGlvbiBOZXR3b3JrMRcwFQYDVQQKEw5WYWxpQ2VydCwg
684
+ SW5jLjE1MDMGA1UECxMsVmFsaUNlcnQgQ2xhc3MgMiBQb2xpY3kgVmFsaWRhdGlv
685
+ biBBdXRob3JpdHkxITAfBgNVBAMTGGh0dHA6Ly93d3cudmFsaWNlcnQuY29tLzEg
686
+ MB4GCSqGSIb3DQEJARYRaW5mb0B2YWxpY2VydC5jb22CAQEwDwYDVR0TAQH/BAUw
687
+ AwEB/zAzBggrBgEFBQcBAQQnMCUwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLmdv
688
+ ZGFkZHkuY29tMEQGA1UdHwQ9MDswOaA3oDWGM2h0dHA6Ly9jZXJ0aWZpY2F0ZXMu
689
+ Z29kYWRkeS5jb20vcmVwb3NpdG9yeS9yb290LmNybDBLBgNVHSAERDBCMEAGBFUd
690
+ IAAwODA2BggrBgEFBQcCARYqaHR0cDovL2NlcnRpZmljYXRlcy5nb2RhZGR5LmNv
691
+ bS9yZXBvc2l0b3J5MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOBgQC1
692
+ QPmnHfbq/qQaQlpE9xXUhUaJwL6e4+PrxeNYiY+Sn1eocSxI0YGyeR+sBjUZsE4O
693
+ WBsUs5iB0QQeyAfJg594RAoYC5jcdnplDQ1tgMQLARzLrUc+cb53S8wGd9D0Vmsf
694
+ SxOaFIqII6hR8INMqzW/Rn453HWkrugp++85j09VZw==
695
+ -----END CERTIFICATE-----
696
+ -----BEGIN CERTIFICATE-----
697
+ MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
698
+ IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAz
699
+ BgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9y
700
+ aXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG
701
+ 9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYyNjAwMTk1NFoXDTE5MDYy
702
+ NjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0IFZhbGlkYXRpb24gTmV0d29y
703
+ azEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENs
704
+ YXNzIDIgUG9saWN5IFZhbGlkYXRpb24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRw
705
+ Oi8vd3d3LnZhbGljZXJ0LmNvbS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNl
706
+ cnQuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vY
707
+ dA757tn2VUdZZUcOBVXc65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9
708
+ WlmpZdRJEy0kTRxQb7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QS
709
+ v4dk+NoS/zcnwbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9v
710
+ UJSZSWI4OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTu
711
+ IYEZoDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
712
+ W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
713
+ -----END CERTIFICATE-----
714
+
715
+ GeoTrust Global CA
716
+ ==================
717
+
718
+ -----BEGIN CERTIFICATE-----
719
+ MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
720
+ MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
721
+ aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
722
+ WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
723
+ AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
724
+ CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
725
+ OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
726
+ T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
727
+ JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
728
+ Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
729
+ PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
730
+ aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
731
+ TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
732
+ LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
733
+ BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
734
+ dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
735
+ AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
736
+ NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
737
+ b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
738
+ -----END CERTIFICATE-----
admin/api-libs/google/service/Google_BatchRequest.php ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2012 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
+ /**
19
+ * @author Chirag Shah <chirags@google.com>
20
+ */
21
+ class Google_BatchRequest {
22
+ /** @var string Multipart Boundary. */
23
+ private $boundary;
24
+
25
+ /** @var array service requests to be executed. */
26
+ private $requests = array();
27
+
28
+ public function __construct($boundary = false) {
29
+ $boundary = (false == $boundary) ? mt_rand() : $boundary;
30
+ $this->boundary = str_replace('"', '', $boundary);
31
+ }
32
+
33
+ public function add(Google_HttpRequest $request, $key = false) {
34
+ if (false == $key) {
35
+ $key = mt_rand();
36
+ }
37
+
38
+ $this->requests[$key] = $request;
39
+ }
40
+
41
+ public function execute() {
42
+ $body = '';
43
+
44
+ /** @var Google_HttpRequest $req */
45
+ foreach($this->requests as $key => $req) {
46
+ $body .= "--{$this->boundary}\n";
47
+ $body .= $req->toBatchString($key) . "\n";
48
+ }
49
+
50
+ $body = rtrim($body);
51
+ $body .= "\n--{$this->boundary}--";
52
+
53
+ global $apiConfig;
54
+ $url = $apiConfig['basePath'] . '/batch';
55
+ $httpRequest = new Google_HttpRequest($url, 'POST');
56
+ $httpRequest->setRequestHeaders(array(
57
+ 'Content-Type' => 'multipart/mixed; boundary=' . $this->boundary));
58
+
59
+ $httpRequest->setPostBody($body);
60
+ $response = Google_Client::$io->makeRequest($httpRequest);
61
+
62
+ $response = $this->parseResponse($response);
63
+ return $response;
64
+ }
65
+
66
+ public function parseResponse(Google_HttpRequest $response) {
67
+ $contentType = $response->getResponseHeader('content-type');
68
+ $contentType = explode(';', $contentType);
69
+ $boundary = false;
70
+ foreach($contentType as $part) {
71
+ $part = (explode('=', $part, 2));
72
+ if (isset($part[0]) && 'boundary' == trim($part[0])) {
73
+ $boundary = $part[1];
74
+ }
75
+ }
76
+
77
+ $body = $response->getResponseBody();
78
+ if ($body) {
79
+ $body = str_replace("--$boundary--", "--$boundary", $body);
80
+ $parts = explode("--$boundary", $body);
81
+ $responses = array();
82
+
83
+ foreach($parts as $part) {
84
+ $part = trim($part);
85
+ if (!empty($part)) {
86
+ list($metaHeaders, $part) = explode("\r\n\r\n", $part, 2);
87
+ $metaHeaders = Google_CurlIO::parseResponseHeaders($metaHeaders);
88
+
89
+ $status = substr($part, 0, strpos($part, "\n"));
90
+ $status = explode(" ", $status);
91
+ $status = $status[1];
92
+
93
+ list($partHeaders, $partBody) = Google_CurlIO::parseHttpResponse($part, false);
94
+ $response = new Google_HttpRequest("");
95
+ $response->setResponseHttpCode($status);
96
+ $response->setResponseHeaders($partHeaders);
97
+ $response->setResponseBody($partBody);
98
+ $response = Google_REST::decodeHttpResponse($response);
99
+
100
+ // Need content id.
101
+ $responses[$metaHeaders['content-id']] = $response;
102
+ }
103
+ }
104
+
105
+ return $responses;
106
+ }
107
+
108
+ return null;
109
+ }
110
+ }
admin/api-libs/google/service/Google_MediaFileUpload.php ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright 2012 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
+ /**
19
+ * @author Chirag Shah <chirags@google.com>
20
+ *
21
+ */
22
+ class Google_MediaFileUpload {
23
+ const UPLOAD_MEDIA_TYPE = 'media';
24
+ const UPLOAD_MULTIPART_TYPE = 'multipart';
25
+ const UPLOAD_RESUMABLE_TYPE = 'resumable';
26
+
27
+ /** @var string $mimeType */
28
+ public $mimeType;
29
+
30
+ /** @var string $data */
31
+ public $data;
32
+
33
+ /** @var bool $resumable */
34
+ public $resumable;
35
+
36
+ /** @var int $chunkSize */
37
+ public $chunkSize;
38
+
39
+ /** @var int $size */
40
+ public $size;
41
+
42
+ /** @var string $resumeUri */
43
+ public $resumeUri;
44
+
45
+ /** @var int $progress */
46
+ public $progress;
47
+
48
+ /**
49
+ * @param $mimeType string
50
+ * @param $data string The bytes you want to upload.
51
+ * @param $resumable bool
52
+ * @param bool $chunkSize File will be uploaded in chunks of this many bytes.
53
+ * only used if resumable=True
54
+ */
55
+ public function __construct($mimeType, $data, $resumable=false, $chunkSize=false) {
56
+ $this->mimeType = $mimeType;
57
+ $this->data = $data;
58
+ $this->size = strlen($this->data);
59
+ $this->resumable = $resumable;
60
+ if(!$chunkSize) {
61
+ $chunkSize = 256 * 1024;
62
+ }
63
+ $this->chunkSize = $chunkSize;
64
+ $this->progress = 0;
65
+ }
66
+
67
+ public function setFileSize($size) {
68
+ $this->size = $size;
69
+ }
70
+
71
+ /**
72
+ * @static
73
+ * @param $meta
74
+ * @param $params
75
+ * @return array|bool
76
+ */
77
+ public static function process($meta, &$params) {
78
+ $payload = array();
79
+ $meta = is_string($meta) ? json_decode($meta, true) : $meta;
80
+ $uploadType = self::getUploadType($meta, $payload, $params);
81
+ if (!$uploadType) {
82
+ // Process as a normal API request.
83
+ return false;
84
+ }
85
+
86
+ // Process as a media upload request.
87
+ $params['uploadType'] = array(
88
+ 'type' => 'string',
89
+ 'location' => 'query',
90
+ 'value' => $uploadType,
91
+ );
92
+
93
+ $mimeType = isset($params['mimeType'])
94
+ ? $params['mimeType']['value']
95
+ : false;
96
+ unset($params['mimeType']);
97
+
98
+ if (!$mimeType) {
99
+ $mimeType = $payload['content-type'];
100
+ }
101
+
102
+ if (isset($params['file'])) {
103
+ // This is a standard file upload with curl.
104
+ $file = $params['file']['value'];
105
+ unset($params['file']);
106
+ return self::processFileUpload($file, $mimeType);
107
+ }
108
+
109
+ $data = isset($params['data'])
110
+ ? $params['data']['value']
111
+ : false;
112
+ unset($params['data']);
113
+
114
+ if (self::UPLOAD_RESUMABLE_TYPE == $uploadType) {
115
+ $payload['content-type'] = $mimeType;
116
+ $payload['postBody'] = is_string($meta) ? $meta : json_encode($meta);
117
+
118
+ } elseif (self::UPLOAD_MEDIA_TYPE == $uploadType) {
119
+ // This is a simple media upload.
120
+ $payload['content-type'] = $mimeType;
121
+ $payload['postBody'] = $data;
122
+ }
123
+
124
+ elseif (self::UPLOAD_MULTIPART_TYPE == $uploadType) {
125
+ // This is a multipart/related upload.
126
+ $boundary = isset($params['boundary']['value']) ? $params['boundary']['value'] : mt_rand();
127
+ $boundary = str_replace('"', '', $boundary);
128
+ $payload['content-type'] = 'multipart/related; boundary=' . $boundary;
129
+ $related = "--$boundary\r\n";
130
+ $related .= "Content-Type: application/json; charset=UTF-8\r\n";
131
+ $related .= "\r\n" . json_encode($meta) . "\r\n";
132
+ $related .= "--$boundary\r\n";
133
+ $related .= "Content-Type: $mimeType\r\n";
134
+ $related .= "Content-Transfer-Encoding: base64\r\n";
135
+ $related .= "\r\n" . base64_encode($data) . "\r\n";
136
+ $related .= "--$boundary--";
137
+ $payload['postBody'] = $related;
138
+ }
139
+
140
+ return $payload;
141
+ }
142
+
143
+ /**
144
+ * Prepares a standard file upload via cURL.
145
+ * @param $file
146
+ * @param $mime
147
+ * @return array Includes the processed file name.
148
+ * @visible For testing.
149
+ */
150
+ public static function processFileUpload($file, $mime) {
151
+ if (!$file) return array();
152
+ if (substr($file, 0, 1) != '@') {
153
+ $file = '@' . $file;
154
+ }
155
+
156
+ // This is a standard file upload with curl.
157
+ $params = array('postBody' => array('file' => $file));
158
+ if ($mime) {
159
+ $params['content-type'] = $mime;
160
+ }
161
+
162
+ return $params;
163
+ }
164
+
165
+ /**
166
+ * Valid upload types:
167
+ * - resumable (UPLOAD_RESUMABLE_TYPE)
168
+ * - media (UPLOAD_MEDIA_TYPE)
169
+ * - multipart (UPLOAD_MULTIPART_TYPE)
170
+ * - none (false)
171
+ * @param $meta
172
+ * @param $payload
173
+ * @param $params
174
+ * @return bool|string
175
+ */
176
+ public static function getUploadType($meta, &$payload, &$params) {
177
+ if (isset($params['mediaUpload'])
178
+ && get_class($params['mediaUpload']['value']) == 'Google_MediaFileUpload') {
179
+ $upload = $params['mediaUpload']['value'];
180
+ unset($params['mediaUpload']);
181
+ $payload['content-type'] = $upload->mimeType;
182
+ if (isset($upload->resumable) && $upload->resumable) {
183
+ return self::UPLOAD_RESUMABLE_TYPE;
184
+ }
185
+ }
186
+
187
+ // Allow the developer to override the upload type.
188
+ if (isset($params['uploadType'])) {
189
+ return $params['uploadType']['value'];
190
+ }
191
+
192
+ $data = isset($params['data']['value'])
193
+ ? $params['data']['value'] : false;
194
+
195
+ if (false == $data && false == isset($params['file'])) {
196
+ // No upload data available.
197
+ return false;
198
+ }
199
+
200
+ if (isset($params['file'])) {
201
+ return self::UPLOAD_MEDIA_TYPE;
202
+ }
203
+
204
+ if (false == $meta) {
205
+ return self::UPLOAD_MEDIA_TYPE;
206
+ }
207
+
208
+ return self::UPLOAD_MULTIPART_TYPE;
209
+ }
210
+
211
+
212
+ public function nextChunk(Google_HttpRequest $req, $chunk=false) {
213
+ if (false == $this->resumeUri) {
214
+ $this->resumeUri = $this->getResumeUri($req);
215
+ }
216
+
217
+ if (false == $chunk) {
218
+ $chunk = substr($this->data, $this->progress, $this->chunkSize);
219
+ }
220
+
221
+ $lastBytePos = $this->progress + strlen($chunk) - 1;
222
+ $headers = array(
223
+ 'content-range' => "bytes $this->progress-$lastBytePos/$this->size",
224
+ 'content-type' => $req->getRequestHeader('content-type'),
225
+ 'content-length' => $this->chunkSize,
226
+ 'expect' => '',
227
+ );
228
+
229
+ $httpRequest = new Google_HttpRequest($this->resumeUri, 'PUT', $headers, $chunk);
230
+ $response = Google_Client::$io->authenticatedRequest($httpRequest);
231
+ $code = $response->getResponseHttpCode();
232
+ if (308 == $code) {
233
+ $range = explode('-', $response->getResponseHeader('range'));
234
+ $this->progress = $range[1] + 1;
235
+ return false;
236
+ } else {
237
+ return Google_REST::decodeHttpResponse($response);
238
+ }
239
+ }
240
+
241
+ private function getResumeUri(Google_HttpRequest $httpRequest) {
242
+ $result = null;
243
+ $body = $httpRequest->getPostBody();
244
+ if ($body) {
245
+ $httpRequest->setRequestHeaders(array(
246
+ 'content-type' => 'application/json; charset=UTF-8',
247
+ 'content-length' => Google_Utils::getStrLen($body),
248
+ 'x-upload-content-type' => $this->mimeType,
249
+ 'x-upload-content-length' => $this->size,
250
+ 'expect' => '',
251
+ ));
252
+ }
253
+
254
+ $response = Google_Client::$io->makeRequest($httpRequest);
255
+ $location = $response->getResponseHeader('location');
256
+ $code = $response->getResponseHttpCode();
257
+ if (200 == $code && true == $location) {
258
+ return $location;
259
+ }
260
+ throw new Google_Exception("Failed to start the resumable upload");
261
+ }
262
+ }
admin/api-libs/google/service/Google_Model.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ /**
19
+ * This class defines attributes, valid values, and usage which is generated from
20
+ * a given json schema. http://tools.ietf.org/html/draft-zyp-json-schema-03#section-5
21
+ *
22
+ * @author Chirag Shah <chirags@google.com>
23
+ *
24
+ */
25
+ class Google_Model {
26
+ public function __construct( /* polymorphic */ ) {
27
+ if (func_num_args() == 1 && is_array(func_get_arg(0))) {
28
+ // Initialize the model with the array's contents.
29
+ $array = func_get_arg(0);
30
+ $this->mapTypes($array);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Initialize this object's properties from an array.
36
+ *
37
+ * @param array $array Used to seed this object's properties.
38
+ * @return void
39
+ */
40
+ protected function mapTypes($array) {
41
+ foreach ($array as $key => $val) {
42
+ $this->$key = $val;
43
+
44
+ $keyTypeName = "__$key" . 'Type';
45
+ $keyDataType = "__$key" . 'DataType';
46
+ if ($this->useObjects() && property_exists($this, $keyTypeName)) {
47
+ if ($this->isAssociativeArray($val)) {
48
+ if (isset($this->$keyDataType) && 'map' == $this->$keyDataType) {
49
+ foreach($val as $arrayKey => $arrayItem) {
50
+ $val[$arrayKey] = $this->createObjectFromName($keyTypeName, $arrayItem);
51
+ }
52
+ $this->$key = $val;
53
+ } else {
54
+ $this->$key = $this->createObjectFromName($keyTypeName, $val);
55
+ }
56
+ } else if (is_array($val)) {
57
+ $arrayObject = array();
58
+ foreach ($val as $arrayIndex => $arrayItem) {
59
+ $arrayObject[$arrayIndex] = $this->createObjectFromName($keyTypeName, $arrayItem);
60
+ }
61
+ $this->$key = $arrayObject;
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Returns true only if the array is associative.
69
+ * @param array $array
70
+ * @return bool True if the array is associative.
71
+ */
72
+ protected function isAssociativeArray($array) {
73
+ if (!is_array($array)) {
74
+ return false;
75
+ }
76
+ $keys = array_keys($array);
77
+ foreach($keys as $key) {
78
+ if (is_string($key)) {
79
+ return true;
80
+ }
81
+ }
82
+ return false;
83
+ }
84
+
85
+ /**
86
+ * Given a variable name, discover its type.
87
+ *
88
+ * @param $name
89
+ * @param $item
90
+ * @return object The object from the item.
91
+ */
92
+ private function createObjectFromName($name, $item) {
93
+ $type = $this->$name;
94
+ return new $type($item);
95
+ }
96
+
97
+ protected function useObjects() {
98
+ global $apiConfig;
99
+ return (isset($apiConfig['use_objects']) && $apiConfig['use_objects']);
100
+ }
101
+
102
+ /**
103
+ * Verify if $obj is an array.
104
+ * @throws Google_Exception Thrown if $obj isn't an array.
105
+ * @param array $obj Items that should be validated.
106
+ * @param string $type Array items should be of this type.
107
+ * @param string $method Method expecting an array as an argument.
108
+ */
109
+ public function assertIsArray($obj, $type, $method) {
110
+ if ($obj && !is_array($obj)) {
111
+ throw new Google_Exception("Incorrect parameter type passed to $method(), expected an"
112
+ . " array containing items of type $type.");
113
+ }
114
+ }
115
+ }
admin/api-libs/google/service/Google_Service.php ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ class Google_Service {
19
+ public $version;
20
+ public $servicePath;
21
+ public $resource;
22
+ }
admin/api-libs/google/service/Google_ServiceResource.php ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ /**
19
+ * Implements the actual methods/resources of the discovered Google API using magic function
20
+ * calling overloading (__call()), which on call will see if the method name (plus.activities.list)
21
+ * is available in this service, and if so construct an apiHttpRequest representing it.
22
+ *
23
+ * @author Chris Chabot <chabotc@google.com>
24
+ * @author Chirag Shah <chirags@google.com>
25
+ *
26
+ */
27
+ class Google_ServiceResource {
28
+ // Valid query parameters that work, but don't appear in discovery.
29
+ private $stackParameters = array(
30
+ 'alt' => array('type' => 'string', 'location' => 'query'),
31
+ 'boundary' => array('type' => 'string', 'location' => 'query'),
32
+ 'fields' => array('type' => 'string', 'location' => 'query'),
33
+ 'trace' => array('type' => 'string', 'location' => 'query'),
34
+ 'userIp' => array('type' => 'string', 'location' => 'query'),
35
+ 'userip' => array('type' => 'string', 'location' => 'query'),
36
+ 'quotaUser' => array('type' => 'string', 'location' => 'query'),
37
+ 'file' => array('type' => 'complex', 'location' => 'body'),
38
+ 'data' => array('type' => 'string', 'location' => 'body'),
39
+ 'mimeType' => array('type' => 'string', 'location' => 'header'),
40
+ 'uploadType' => array('type' => 'string', 'location' => 'query'),
41
+ 'mediaUpload' => array('type' => 'complex', 'location' => 'query'),
42
+ );
43
+
44
+ /** @var Google_Service $service */
45
+ private $service;
46
+
47
+ /** @var string $serviceName */
48
+ private $serviceName;
49
+
50
+ /** @var string $resourceName */
51
+ private $resourceName;
52
+
53
+ /** @var array $methods */
54
+ private $methods;
55
+
56
+ public function __construct($service, $serviceName, $resourceName, $resource) {
57
+ $this->service = $service;
58
+ $this->serviceName = $serviceName;
59
+ $this->resourceName = $resourceName;
60
+ $this->methods = isset($resource['methods']) ? $resource['methods'] : array($resourceName => $resource);
61
+ }
62
+
63
+ /**
64
+ * @param $name
65
+ * @param $arguments
66
+ * @return Google_HttpRequest|array
67
+ * @throws Google_Exception
68
+ */
69
+ public function __call($name, $arguments) {
70
+ if (! isset($this->methods[$name])) {
71
+ throw new Google_Exception("Unknown function: {$this->serviceName}->{$this->resourceName}->{$name}()");
72
+ }
73
+ $method = $this->methods[$name];
74
+ $parameters = $arguments[0];
75
+
76
+ // postBody is a special case since it's not defined in the discovery document as parameter, but we abuse the param entry for storing it
77
+ $postBody = null;
78
+ if (isset($parameters['postBody'])) {
79
+ if (is_object($parameters['postBody'])) {
80
+ $this->stripNull($parameters['postBody']);
81
+ }
82
+
83
+ // Some APIs require the postBody to be set under the data key.
84
+ if (is_array($parameters['postBody']) && 'latitude' == $this->serviceName) {
85
+ if (!isset($parameters['postBody']['data'])) {
86
+ $rawBody = $parameters['postBody'];
87
+ unset($parameters['postBody']);
88
+ $parameters['postBody']['data'] = $rawBody;
89
+ }
90
+ }
91
+
92
+ $postBody = is_array($parameters['postBody']) || is_object($parameters['postBody'])
93
+ ? json_encode($parameters['postBody'])
94
+ : $parameters['postBody'];
95
+ unset($parameters['postBody']);
96
+
97
+ if (isset($parameters['optParams'])) {
98
+ $optParams = $parameters['optParams'];
99
+ unset($parameters['optParams']);
100
+ $parameters = array_merge($parameters, $optParams);
101
+ }
102
+ }
103
+
104
+ if (!isset($method['parameters'])) {
105
+ $method['parameters'] = array();
106
+ }
107
+
108
+ $method['parameters'] = array_merge($method['parameters'], $this->stackParameters);
109
+ foreach ($parameters as $key => $val) {
110
+ if ($key != 'postBody' && ! isset($method['parameters'][$key])) {
111
+ throw new Google_Exception("($name) unknown parameter: '$key'");
112
+ }
113
+ }
114
+ if (isset($method['parameters'])) {
115
+ foreach ($method['parameters'] as $paramName => $paramSpec) {
116
+ if (isset($paramSpec['required']) && $paramSpec['required'] && ! isset($parameters[$paramName])) {
117
+ throw new Google_Exception("($name) missing required param: '$paramName'");
118
+ }
119
+ if (isset($parameters[$paramName])) {
120
+ $value = $parameters[$paramName];
121
+ $parameters[$paramName] = $paramSpec;
122
+ $parameters[$paramName]['value'] = $value;
123
+ unset($parameters[$paramName]['required']);
124
+ } else {
125
+ unset($parameters[$paramName]);
126
+ }
127
+ }
128
+ }
129
+
130
+ // Discovery v1.0 puts the canonical method id under the 'id' field.
131
+ if (! isset($method['id'])) {
132
+ $method['id'] = $method['rpcMethod'];
133
+ }
134
+
135
+ // Discovery v1.0 puts the canonical path under the 'path' field.
136
+ if (! isset($method['path'])) {
137
+ $method['path'] = $method['restPath'];
138
+ }
139
+
140
+ $servicePath = $this->service->servicePath;
141
+
142
+ // Process Media Request
143
+ $contentType = false;
144
+ if (isset($method['mediaUpload'])) {
145
+ $media = Google_MediaFileUpload::process($postBody, $parameters);
146
+ if ($media) {
147
+ $contentType = isset($media['content-type']) ? $media['content-type']: null;
148
+ $postBody = isset($media['postBody']) ? $media['postBody'] : null;
149
+ $servicePath = $method['mediaUpload']['protocols']['simple']['path'];
150
+ $method['path'] = '';
151
+ }
152
+ }
153
+
154
+ $url = Google_REST::createRequestUri($servicePath, $method['path'], $parameters);
155
+ $httpRequest = new Google_HttpRequest($url, $method['httpMethod'], null, $postBody);
156
+ if ($postBody) {
157
+ $contentTypeHeader = array();
158
+ if (isset($contentType) && $contentType) {
159
+ $contentTypeHeader['content-type'] = $contentType;
160
+ } else {
161
+ $contentTypeHeader['content-type'] = 'application/json; charset=UTF-8';
162
+ $contentTypeHeader['content-length'] = Google_Utils::getStrLen($postBody);
163
+ }
164
+ $httpRequest->setRequestHeaders($contentTypeHeader);
165
+ }
166
+
167
+ $httpRequest = Google_Client::$auth->sign($httpRequest);
168
+ if (Google_Client::$useBatch) {
169
+ return $httpRequest;
170
+ }
171
+
172
+ // Terminate immediately if this is a resumable request.
173
+ if (isset($parameters['uploadType']['value'])
174
+ && Google_MediaFileUpload::UPLOAD_RESUMABLE_TYPE == $parameters['uploadType']['value']) {
175
+ $contentTypeHeader = array();
176
+ if (isset($contentType) && $contentType) {
177
+ $contentTypeHeader['content-type'] = $contentType;
178
+ }
179
+ $httpRequest->setRequestHeaders($contentTypeHeader);
180
+ if ($postBody) {
181
+ $httpRequest->setPostBody($postBody);
182
+ }
183
+ return $httpRequest;
184
+ }
185
+
186
+ return Google_REST::execute($httpRequest);
187
+ }
188
+
189
+ public function useObjects() {
190
+ global $apiConfig;
191
+ return (isset($apiConfig['use_objects']) && $apiConfig['use_objects']);
192
+ }
193
+
194
+ protected function stripNull(&$o) {
195
+ $o = (array) $o;
196
+ foreach ($o as $k => $v) {
197
+ if ($v === null || strstr($k, "\0*\0__")) {
198
+ unset($o[$k]);
199
+ }
200
+ elseif (is_object($v) || is_array($v)) {
201
+ $this->stripNull($o[$k]);
202
+ }
203
+ }
204
+ }
205
+ }
admin/api-libs/google/service/Google_Utils.php ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ * Copyright 2011 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
+ /**
19
+ * Collection of static utility methods used for convenience across
20
+ * the client library.
21
+ *
22
+ * @author Chirag Shah <chirags@google.com>
23
+ */
24
+ class Google_Utils {
25
+ public static function urlSafeB64Encode($data) {
26
+ $b64 = base64_encode($data);
27
+ $b64 = str_replace(array('+', '/', '\r', '\n', '='),
28
+ array('-', '_'),
29
+ $b64);
30
+ return $b64;
31
+ }
32
+
33
+ public static function urlSafeB64Decode($b64) {
34
+ $b64 = str_replace(array('-', '_'),
35
+ array('+', '/'),
36
+ $b64);
37
+ return base64_decode($b64);
38
+ }
39
+
40
+ /**
41
+ * Misc function used to count the number of bytes in a post body, in the world of multi-byte chars
42
+ * and the unpredictability of strlen/mb_strlen/sizeof, this is the only way to do that in a sane
43
+ * manner at the moment.
44
+ *
45
+ * This algorithm was originally developed for the
46
+ * Solar Framework by Paul M. Jones
47
+ *
48
+ * @link http://solarphp.com/
49
+ * @link http://svn.solarphp.com/core/trunk/Solar/Json.php
50
+ * @link http://framework.zend.com/svn/framework/standard/trunk/library/Zend/Json/Decoder.php
51
+ * @param string $str
52
+ * @return int The number of bytes in a string.
53
+ */
54
+ static public function getStrLen($str) {
55
+ $strlenVar = strlen($str);
56
+ $d = $ret = 0;
57
+ for ($count = 0; $count < $strlenVar; ++ $count) {
58
+ $ordinalValue = ord($str{$ret});
59
+ switch (true) {
60
+ case (($ordinalValue >= 0x20) && ($ordinalValue <= 0x7F)):
61
+ // characters U-00000000 - U-0000007F (same as ASCII)
62
+ $ret ++;
63
+ break;
64
+
65
+ case (($ordinalValue & 0xE0) == 0xC0):
66
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
67
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
68
+ $ret += 2;
69
+ break;
70
+
71
+ case (($ordinalValue & 0xF0) == 0xE0):
72
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
73
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
74
+ $ret += 3;
75
+ break;
76
+
77
+ case (($ordinalValue & 0xF8) == 0xF0):
78
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
79
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
80
+ $ret += 4;
81
+ break;
82
+
83
+ case (($ordinalValue & 0xFC) == 0xF8):
84
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
85
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
86
+ $ret += 5;
87
+ break;
88
+
89
+ case (($ordinalValue & 0xFE) == 0xFC):
90
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
91
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
92
+ $ret += 6;
93
+ break;
94
+ default:
95
+ $ret ++;
96
+ }
97
+ }
98
+ return $ret;
99
+ }
100
+
101
+ /**
102
+ * Normalize all keys in an array to lower-case.
103
+ * @param array $arr
104
+ * @return array Normalized array.
105
+ */
106
+ public static function normalize($arr) {
107
+ if (!is_array($arr)) {
108
+ return array();
109
+ }
110
+
111
+ $normalized = array();
112
+ foreach ($arr as $key => $val) {
113
+ $normalized[strtolower($key)] = $val;
114
+ }
115
+ return $normalized;
116
+ }
117
+ }
admin/api-libs/googleanalytics/class-api-googleanalytics.php CHANGED
@@ -18,7 +18,8 @@ if ( ! class_exists( 'Yoast_Api_Googleanalytics' ) ) {
18
  */
19
  private function load_api_oauth_files() {
20
  $oauth_files = array(
21
- 'yoast_api_googleanalytics_reporting' => 'class-googleanalytics-reporting',
 
22
  );
23
 
24
  foreach ( $oauth_files as $key => $name ) {
18
  */
19
  private function load_api_oauth_files() {
20
  $oauth_files = array(
21
+ 'yoast_api_googleanalytics_reporting' => 'class-googleanalytics-reporting',
22
+ 'yoast_google_analytics_client' => 'class-google-analytics-client',
23
  );
24
 
25
  foreach ( $oauth_files as $key => $name ) {
admin/api-libs/googleanalytics/class-google-analytics-client.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'Google_Client' ) ) {
4
+ require_once( WPSEO_PREMIUM_PATH . 'classes/google/Google_Client.php' );
5
+ }
6
+
7
+ class Yoast_Google_Analytics_Client extends Google_Client {
8
+
9
+ protected $http_response_code;
10
+
11
+ const OPTION_REFRESH_TOKEN = 'yoast-ga-refresh_token';
12
+
13
+ protected $default_config = array(
14
+ 'redirect_uri' => 'urn:ietf:wg:oauth:2.0:oob',
15
+ 'scopes' => array( 'https://www.googleapis.com/auth/analytics.readonly' ),
16
+ );
17
+
18
+ /**
19
+ * Initialize the config and refresh the token
20
+ * @param array $config
21
+ */
22
+ public function __construct( $config ) {
23
+
24
+ parent::__construct();
25
+
26
+ // Initialize the config to set all properties properly
27
+ $this->init_config( $config );
28
+
29
+ // Let's get an access token if we've got a refresh token
30
+ $this->refresh_tokens();
31
+
32
+ }
33
+
34
+ /**
35
+ * Initialize the config, will merge given config with default config to be sure all settings are available
36
+ *
37
+ * @param $config
38
+ */
39
+ protected function init_config( $config ) {
40
+ $config = array_merge( $config, $this->default_config );
41
+
42
+ if ( ! empty( $config['application_name'] ) ) {
43
+ $this->setApplicationName( $config['application_name'] );
44
+ } else {
45
+ // @todo: throw new error
46
+
47
+ }
48
+
49
+ if ( ! empty( $config['client_id'] ) ) {
50
+ $this->setClientId( $config['client_id'] );
51
+ } else {
52
+ // @todo: throw new error
53
+
54
+ }
55
+
56
+ if ( ! empty( $config['client_secret'] ) ) {
57
+ $this->setClientSecret( $config['client_secret'] );
58
+ } else {
59
+ // @todo: throw new error
60
+ }
61
+
62
+ // Set our settings
63
+ $this->setRedirectUri( $config['redirect_uri'] );
64
+ $this->setScopes( $config['scopes'] );
65
+ $this->setAccessType('offline');
66
+ }
67
+
68
+ /**
69
+ * Refeshing the tokens
70
+ */
71
+ protected function refresh_tokens() {
72
+ $refresh_token = $this->get_refresh_token();
73
+
74
+ if ( '' != $refresh_token ) {
75
+ try {
76
+ // Refresh the token
77
+ $response = $this->refreshToken( $refresh_token );
78
+
79
+
80
+ // Check response
81
+ if ( ! empty( $response ) ) {
82
+ $response = json_decode( $response );
83
+
84
+ // Check if there is an access_token
85
+ if ( isset( $response->access_token ) ) {
86
+ // Set access_token
87
+ $this->setAccessToken( $response->access_token );
88
+ }
89
+ }
90
+ } catch(Exception $e) {
91
+ $this->save_refresh_token('');
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Authenticate the client. If $authorization_code is empty it will lead the user through the validation process of
98
+ * Google. If set it will be get the access token for current session and save the refresh_token for future use
99
+ *
100
+ * @param mixed $authorization_code
101
+ *
102
+ * @return bool
103
+ */
104
+ public function authenticate_client( $authorization_code = null ) {
105
+
106
+ // Authenticate client
107
+ try {
108
+ $this->authenticate( $authorization_code );
109
+
110
+ // Get access response
111
+ $response = $this->getAccessToken();
112
+
113
+ // Check if there is a response body
114
+ if ( !empty( $response ) ) {
115
+ $response = json_decode( $response );
116
+
117
+ if ( is_object( $response ) ) {
118
+ // Save the refresh token
119
+ $this->save_refresh_token( $response->refresh_token );
120
+
121
+ return true;
122
+ }
123
+
124
+ }
125
+ } catch ( Google_AuthException $exception ) {
126
+ return false;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Doing a request to the API
132
+ *
133
+ * @param string $target_request_url
134
+ *
135
+ * @return Google_HttpRequest
136
+ */
137
+
138
+ public function do_request( $target_request_url ) {
139
+ // Do list sites request
140
+ $request = new Google_HttpRequest( $target_request_url );
141
+
142
+ // Get list sites response
143
+ $response = $this->getIo()->authenticatedRequest( $request );
144
+
145
+ $this->http_response_code = $response->getResponseHttpCode();
146
+
147
+ return $response;
148
+ }
149
+
150
+ /**
151
+ * Getting the response code, saved from latest request to Google
152
+ * @return mixed
153
+ */
154
+ public function get_http_response_code() {
155
+ return $this->http_response_code;
156
+ }
157
+
158
+
159
+ /**
160
+ * Save the refresh token
161
+ *
162
+ * @param $refresh_token
163
+ */
164
+ public function save_refresh_token( $refresh_token ) {
165
+ update_option( self::OPTION_REFRESH_TOKEN, $refresh_token );
166
+ }
167
+
168
+ /**
169
+ * Return refresh token
170
+ *
171
+ * @return string
172
+ */
173
+ public function get_refresh_token() {
174
+ return get_option( self::OPTION_REFRESH_TOKEN, '' );
175
+ }
176
+
177
+ }
admin/api-libs/googleanalytics/class-googleanalytics-reporting.php CHANGED
@@ -17,7 +17,7 @@ class Yoast_Googleanalytics_Reporting {
17
  *
18
  * @return null|Yoast_Google_Analytics
19
  */
20
- public static function instance() {
21
  if ( is_null( self::$instance ) ) {
22
  self::$instance = new self();
23
  }
@@ -25,65 +25,6 @@ class Yoast_Googleanalytics_Reporting {
25
  return self::$instance;
26
  }
27
 
28
- /**
29
- * Doing request to Google Analytics
30
- *
31
- * This method will do a request to google and get the response code and body from content
32
- *
33
- * @param string $target_url
34
- * @param string $scope
35
- * @param string $access_token
36
- * @param string secret
37
- * @param string table,datelist
38
- * @param int $start_date Unix timestamp
39
- * @param int $end_date Unix timestamp
40
- *
41
- * @return array|null
42
- */
43
- public function do_api_request( $target_url, $scope, $access_token, $secret, $store_as, $start_date, $end_date ) {
44
- $gdata = $this->get_gdata( $scope, $access_token, $secret );
45
- $response = $gdata->get( $target_url );
46
- $http_code = wp_remote_retrieve_response_code( $response );
47
- $response = wp_remote_retrieve_body( $response );
48
-
49
- if ( $http_code == 200 ) {
50
- return array(
51
- 'response' => array( 'code' => $http_code ),
52
- 'body_raw' => $response,
53
- 'body' => $this->parse_response( json_decode( $response ), $store_as, $start_date, $end_date ),
54
- );
55
- } else {
56
- return array(
57
- 'body_raw' => $response,
58
- 'response' => $response,
59
- 'http_code' => $http_code,
60
- 'gdata' => $gdata,
61
- );
62
- }
63
- }
64
-
65
- /**
66
- * Getting WP_GData object
67
- *
68
- * If not available include class file and create an instance of WP_GDAta
69
- *
70
- * @param string $scope
71
- * @param null $token
72
- * @param null $secret
73
- *
74
- * @return WP_GData
75
- */
76
- protected function get_gdata( $scope, $token = null, $secret = null ) {
77
- $args = array(
78
- 'scope' => $scope,
79
- 'xoauth_displayname' => 'Google Analytics by Yoast',
80
- );
81
-
82
- $gdata = new WP_GData( $args, $token, $secret );
83
-
84
- return $gdata;
85
- }
86
-
87
  /**
88
  * Format a response
89
  *
@@ -94,7 +35,7 @@ class Yoast_Googleanalytics_Reporting {
94
  *
95
  * @return array
96
  */
97
- private function parse_response( $raw_data, $store_as, $start_date, $end_date ) {
98
  $data = array();
99
 
100
  if ( $store_as == 'datelist' ) {
@@ -102,8 +43,8 @@ class Yoast_Googleanalytics_Reporting {
102
  $data = array_keys( $data_tmp );
103
  }
104
 
105
- if ( isset( $raw_data->rows ) && is_array( $raw_data->rows ) ) {
106
- foreach ( $raw_data->rows as $key => $item ) {
107
  if ( $store_as == 'datelist' ) {
108
  $data[(int) $this->format_ga_date( $item[0] )] = $this->parse_row( $item );
109
  } else {
@@ -127,9 +68,9 @@ class Yoast_Googleanalytics_Reporting {
127
  * @return array
128
  */
129
  private function check_validity_data( $data = array() ) {
130
- foreach( $data as $key => $value ){
131
- if(strlen($key)<=5){
132
- unset($data[$key]);
133
  }
134
  }
135
 
17
  *
18
  * @return null|Yoast_Google_Analytics
19
  */
20
+ public static function get_instance() {
21
  if ( is_null( self::$instance ) ) {
22
  self::$instance = new self();
23
  }
25
  return self::$instance;
26
  }
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  /**
29
  * Format a response
30
  *
35
  *
36
  * @return array
37
  */
38
+ public function parse_response( $raw_data, $store_as, $start_date, $end_date ) {
39
  $data = array();
40
 
41
  if ( $store_as == 'datelist' ) {
43
  $data = array_keys( $data_tmp );
44
  }
45
 
46
+ if ( isset( $raw_data['body']['rows'] ) && is_array( $raw_data['body']['rows'] ) ) {
47
+ foreach ( $raw_data['body']['rows'] as $key => $item ) {
48
  if ( $store_as == 'datelist' ) {
49
  $data[(int) $this->format_ga_date( $item[0] )] = $this->parse_row( $item );
50
  } else {
68
  * @return array
69
  */
70
  private function check_validity_data( $data = array() ) {
71
+ foreach ( $data as $key => $value ) {
72
+ if ( strlen( $key ) <= 5 ) {
73
+ unset( $data[$key] );
74
  }
75
  }
76
 
admin/api-libs/oauth/class-api-oauth.php DELETED
@@ -1,41 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_Api_Oauth' ) ) {
4
-
5
- class Yoast_Api_Oauth {
6
-
7
- /**
8
- * This class will be loaded when someone calls the API library with the Oauth module
9
- */
10
- public function __construct() {
11
- $this->load_api_oauth_files();
12
- }
13
-
14
- /**
15
- * Autoload the Oauth classes
16
- */
17
- private function load_api_oauth_files() {
18
- $oauth_files = array(
19
- 'yoast_oauthconsumer' => 'class-oauth-consumer',
20
- 'yoast_oauthdatastore' => 'class-oauth-datestore',
21
- 'yoast_oauthexception' => 'class-oauth-exception',
22
- 'yoast_oauthrequest' => 'class-oauth-request',
23
- 'yoast_oauthserver' => 'class-oauth-server',
24
- 'yoast_oauthsignaturemethod' => 'class-oauth-signature-method',
25
- 'yoast_oathsignaturemethod_hmac_sha1' => 'class-oauth-signature-method-hmac-sha1',
26
- 'yoast_oauthsignaturemethod_plaintext' => 'class-oauth-signature-method-plaintext',
27
- 'yoast_oauthsignaturemethod_rsa_sha1' => 'class-oauth-signature-method-rsa-sha1',
28
- 'yoast_oauthtoken' => 'class-oauth-token',
29
- 'yoast_oauthutil' => 'class-oauth-util',
30
- );
31
-
32
- foreach ( $oauth_files as $key => $name ) {
33
- if ( file_exists( dirname( __FILE__ ) . '/' . $name . '.php' ) ) {
34
- require_once( dirname( __FILE__ ) . '/' . $name . '.php' );
35
- }
36
- }
37
- }
38
-
39
- }
40
-
41
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-consumer.php DELETED
@@ -1,22 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_OAuthConsumer' ) ) {
4
-
5
- class Yoast_OAuthConsumer {
6
-
7
- public $key;
8
- public $secret;
9
-
10
- function __construct( $key, $secret, $callback_url = null ) {
11
- $this->key = $key;
12
- $this->secret = $secret;
13
- $this->callback_url = $callback_url;
14
- }
15
-
16
- function __toString() {
17
- return "Yoast_OAuthConsumer[key=$this->key,secret=$this->secret]";
18
- }
19
-
20
- }
21
-
22
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-datastore.php DELETED
@@ -1,32 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_OAuthDataStore' ) ) {
4
-
5
- class Yoast_OAuthDataStore {
6
-
7
- function lookup_consumer( $consumer_key ) {
8
- // implement me
9
- }
10
-
11
- function lookup_token( $consumer, $token_type, $token ) {
12
- // implement me
13
- }
14
-
15
- function lookup_nonce( $consumer, $token, $nonce, $timestamp ) {
16
- // implement me
17
- }
18
-
19
- function new_request_token( $consumer, $callback = null ) {
20
- // return a new token attached to this consumer
21
- }
22
-
23
- function new_access_token( $token, $consumer, $verifier = null ) {
24
- // return a new access token attached to this consumer
25
- // for the user associated with this token if the request token
26
- // is authorized
27
- // should also invalidate the request token
28
- }
29
-
30
- }
31
-
32
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-exception.php DELETED
@@ -1,9 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_OAuthException' ) ) {
4
-
5
- class Yoast_OAuthException extends Exception {
6
- // pass
7
- }
8
-
9
- }
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-request.php DELETED
@@ -1,277 +0,0 @@
1
- <?php
2
-
3
- if(!class_exists('Yoast_OAuthRequest')) {
4
-
5
- class Yoast_OAuthRequest {
6
-
7
- protected $parameters;
8
- protected $http_method;
9
- protected $http_url;
10
- // for debug purposes
11
- public $base_string;
12
- public static $version = '1.0';
13
- public static $POST_INPUT = 'php://input';
14
-
15
- function __construct( $http_method, $http_url, $parameters = null ) {
16
- $parameters = ( $parameters ) ? $parameters : array();
17
- $parameters = array_merge( Yoast_OAuthUtil::parse_parameters( parse_url( $http_url, PHP_URL_QUERY ) ), $parameters );
18
- $this->parameters = $parameters;
19
- $this->http_method = $http_method;
20
- $this->http_url = $http_url;
21
- }
22
-
23
-
24
- /**
25
- * attempt to build up a request from what was passed to the server
26
- */
27
- public static function from_request( $http_method = null, $http_url = null, $parameters = null ) {
28
- $scheme = ( ! isset( $_SERVER['HTTPS'] ) || $_SERVER['HTTPS'] != 'on' )
29
- ? 'http'
30
- : 'https';
31
- $http_url = ( $http_url ) ? $http_url : $scheme .
32
- '://' . $_SERVER['SERVER_NAME'] .
33
- ':' .
34
- $_SERVER['SERVER_PORT'] .
35
- $_SERVER['REQUEST_URI'];
36
- $http_method = ( $http_method ) ? $http_method : $_SERVER['REQUEST_METHOD'];
37
-
38
- // We weren't handed any parameters, so let's find the ones relevant to
39
- // this request.
40
- // If you run XML-RPC or similar you should use this to provide your own
41
- // parsed parameter-list
42
- if ( ! $parameters ) {
43
- // Find request headers
44
- $request_headers = Yoast_OAuthUtil::get_headers();
45
-
46
- // Parse the query-string to find GET parameters
47
- $parameters = Yoast_OAuthUtil::parse_parameters( $_SERVER['QUERY_STRING'] );
48
-
49
- // It's a POST request of the proper content-type, so parse POST
50
- // parameters and add those overriding any duplicates from GET
51
- if ( $http_method == 'POST'
52
- && isset( $request_headers['Content-Type'] )
53
- && strstr( $request_headers['Content-Type'],
54
- 'application/x-www-form-urlencoded' )
55
- ) {
56
- $post_data = Yoast_OAuthUtil::parse_parameters(
57
- file_get_contents( self::$POST_INPUT )
58
- );
59
- $parameters = array_merge( $parameters, $post_data );
60
- }
61
-
62
- // We have a Authorization-header with OAuth data. Parse the header
63
- // and add those overriding any duplicates from GET or POST
64
- if ( isset( $request_headers['Authorization'] ) && substr( $request_headers['Authorization'], 0, 6 ) == 'OAuth ' ) {
65
- $header_parameters = Yoast_OAuthUtil::split_header(
66
- $request_headers['Authorization']
67
- );
68
- $parameters = array_merge( $parameters, $header_parameters );
69
- }
70
- }
71
-
72
- return new Yoast_OAuthRequest( $http_method, $http_url, $parameters );
73
- }
74
-
75
- /**
76
- * pretty much a helper function to set up the request
77
- */
78
- public static function from_consumer_and_token( $consumer, $token, $http_method, $http_url, $parameters = null ) {
79
- $parameters = ( $parameters ) ? $parameters : array();
80
- $defaults = array(
81
- 'oauth_version' => Yoast_OAuthRequest::$version,
82
- 'oauth_nonce' => Yoast_OAuthRequest::generate_nonce(),
83
- 'oauth_timestamp' => Yoast_OAuthRequest::generate_timestamp(),
84
- 'oauth_consumer_key' => $consumer->key,
85
- );
86
- if ( $token ) {
87
- $defaults['oauth_token'] = $token->key;
88
- }
89
-
90
- $parameters = array_merge( $defaults, $parameters );
91
-
92
- return new Yoast_OAuthRequest( $http_method, $http_url, $parameters );
93
- }
94
-
95
- public function set_parameter( $name, $value, $allow_duplicates = true ) {
96
- if ( $allow_duplicates && isset( $this->parameters[$name] ) ) {
97
- // We have already added parameter(s) with this name, so add to the list
98
- if ( is_scalar( $this->parameters[$name] ) ) {
99
- // This is the first duplicate, so transform scalar (string)
100
- // into an array so we can add the duplicates
101
- $this->parameters[$name] = array( $this->parameters[$name] );
102
- }
103
-
104
- $this->parameters[$name][] = $value;
105
- } else {
106
- $this->parameters[$name] = $value;
107
- }
108
- }
109
-
110
- public function get_parameter( $name ) {
111
- return isset( $this->parameters[$name] ) ? $this->parameters[$name] : null;
112
- }
113
-
114
- public function get_parameters() {
115
- return $this->parameters;
116
- }
117
-
118
- public function unset_parameter( $name ) {
119
- unset( $this->parameters[$name] );
120
- }
121
-
122
- /**
123
- * The request parameters, sorted and concatenated into a normalized string.
124
- * @return string
125
- */
126
- public function get_signable_parameters() {
127
- // Grab all parameters
128
- $params = $this->parameters;
129
-
130
- // Remove oauth_signature if present
131
- // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
132
- if ( isset( $params['oauth_signature'] ) ) {
133
- unset( $params['oauth_signature'] );
134
- }
135
-
136
- return Yoast_OAuthUtil::build_http_query( $params );
137
- }
138
-
139
- /**
140
- * Returns the base string of this request
141
- *
142
- * The base string defined as the method, the url
143
- * and the parameters (normalized), each urlencoded
144
- * and the concated with &.
145
- */
146
- public function get_signature_base_string() {
147
- $parts = array(
148
- $this->get_normalized_http_method(),
149
- $this->get_normalized_http_url(),
150
- $this->get_signable_parameters(),
151
- );
152
-
153
- $parts = Yoast_OAuthUtil::urlencode_rfc3986( $parts );
154
-
155
- return implode( '&', $parts );
156
- }
157
-
158
- /**
159
- * just uppercases the http method
160
- */
161
- public function get_normalized_http_method() {
162
- return strtoupper( $this->http_method );
163
- }
164
-
165
- /**
166
- * parses the url and rebuilds it to be
167
- * scheme://host/path
168
- */
169
- public function get_normalized_http_url() {
170
- $parts = parse_url( $this->http_url );
171
-
172
- $scheme = ( isset( $parts['scheme'] ) ) ? $parts['scheme'] : 'http';
173
- $port = ( isset( $parts['port'] ) ) ? $parts['port'] : ( ( $scheme == 'https' ) ? '443' : '80' );
174
- $host = ( isset( $parts['host'] ) ) ? strtolower( $parts['host'] ) : '';
175
- $path = ( isset( $parts['path'] ) ) ? $parts['path'] : '';
176
-
177
- if ( ( $scheme == 'https' && $port != '443' )
178
- || ( $scheme == 'http' && $port != '80' )
179
- ) {
180
- $host = "$host:$port";
181
- }
182
-
183
- return "$scheme://$host$path";
184
- }
185
-
186
- /**
187
- * builds a url usable for a GET request
188
- */
189
- public function to_url() {
190
- $post_data = $this->to_postdata();
191
- $out = $this->get_normalized_http_url();
192
- if ( $post_data ) {
193
- $out .= '?' . $post_data;
194
- }
195
-
196
- return $out;
197
- }
198
-
199
- /**
200
- * builds the data one would send in a POST request
201
- */
202
- public function to_postdata() {
203
- return Yoast_OAuthUtil::build_http_query( $this->parameters );
204
- }
205
-
206
- /**
207
- * builds the Authorization: header
208
- */
209
- public function to_header( $realm = null ) {
210
- $first = true;
211
- if ( $realm ) {
212
- $out = 'Authorization: OAuth realm="' . Yoast_OAuthUtil::urlencode_rfc3986( $realm ) . '"';
213
- $first = false;
214
- } else {
215
- $out = 'Authorization: OAuth';
216
- }
217
-
218
- $total = array();
219
- foreach ( $this->parameters as $k => $v ) {
220
- if ( substr( $k, 0, 5 ) != 'oauth' ) {
221
- continue;
222
- }
223
- if ( is_array( $v ) ) {
224
- throw new Yoast_OAuthException( 'Arrays not supported in headers' );
225
- }
226
- $out .= ( $first ) ? ' ' : ',';
227
- $out .= Yoast_OAuthUtil::urlencode_rfc3986( $k ) .
228
- '="' .
229
- Yoast_OAuthUtil::urlencode_rfc3986( $v ) .
230
- '"';
231
- $first = false;
232
- }
233
-
234
- return $out;
235
- }
236
-
237
- public function __toString() {
238
- return $this->to_url();
239
- }
240
-
241
-
242
- public function sign_request( $signature_method, $consumer, $token ) {
243
- $this->set_parameter(
244
- 'oauth_signature_method',
245
- $signature_method->get_name(),
246
- false
247
- );
248
- $signature = $this->build_signature( $signature_method, $consumer, $token );
249
- $this->set_parameter( 'oauth_signature', $signature, false );
250
- }
251
-
252
- public function build_signature( $signature_method, $consumer, $token ) {
253
- $signature = $signature_method->build_signature( $this, $consumer, $token );
254
-
255
- return $signature;
256
- }
257
-
258
- /**
259
- * util function: current timestamp
260
- */
261
- private static function generate_timestamp() {
262
- return time();
263
- }
264
-
265
- /**
266
- * util function: current nonce
267
- */
268
- private static function generate_nonce() {
269
- $mt = microtime();
270
- $rand = mt_rand();
271
-
272
- return md5( $mt . $rand ); // md5s look nicer than numbers
273
- }
274
-
275
- }
276
-
277
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-server.php DELETED
@@ -1,235 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_OAuthServer' ) ) {
4
-
5
- class Yoast_OAuthServer {
6
-
7
- protected $timestamp_threshold = 300; // in seconds, five minutes
8
- protected $version = '1.0'; // hi blaine
9
- protected $signature_methods = array();
10
-
11
- protected $data_store;
12
-
13
- function __construct( $data_store ) {
14
- $this->data_store = $data_store;
15
- }
16
-
17
- public function add_signature_method( $signature_method ) {
18
- $this->signature_methods[$signature_method->get_name()] =
19
- $signature_method;
20
- }
21
-
22
- // high level functions
23
-
24
- /**
25
- * process a request_token request
26
- * returns the request token on success
27
- */
28
- public function fetch_request_token( &$request ) {
29
- $this->get_version( $request );
30
-
31
- $consumer = $this->get_consumer( $request );
32
-
33
- // no token required for the initial token request
34
- $token = null;
35
-
36
- $this->check_signature( $request, $consumer, $token );
37
-
38
- // Rev A change
39
- $callback = $request->get_parameter( 'oauth_callback' );
40
- $new_token = $this->data_store->new_request_token( $consumer, $callback );
41
-
42
- return $new_token;
43
- }
44
-
45
- /**
46
- * process an access_token request
47
- * returns the access token on success
48
- */
49
- public function fetch_access_token( &$request ) {
50
- $this->get_version( $request );
51
-
52
- $consumer = $this->get_consumer( $request );
53
-
54
- // requires authorized request token
55
- $token = $this->get_token( $request, $consumer, 'request' );
56
-
57
- $this->check_signature( $request, $consumer, $token );
58
-
59
- // Rev A change
60
- $verifier = $request->get_parameter( 'oauth_verifier' );
61
- $new_token = $this->data_store->new_access_token( $token, $consumer, $verifier );
62
-
63
- return $new_token;
64
- }
65
-
66
- /**
67
- * verify an api call, checks all the parameters
68
- */
69
- public function verify_request( &$request ) {
70
- $this->get_version( $request );
71
- $consumer = $this->get_consumer( $request );
72
- $token = $this->get_token( $request, $consumer, 'access' );
73
- $this->check_signature( $request, $consumer, $token );
74
-
75
- return array( $consumer, $token );
76
- }
77
-
78
- // Internals from here
79
- /**
80
- * version 1
81
- */
82
- private function get_version( &$request ) {
83
- $version = $request->get_parameter( 'oauth_version' );
84
- if ( ! $version ) {
85
- // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
86
- // Chapter 7.0 ("Accessing Protected Ressources")
87
- $version = '1.0';
88
- }
89
- if ( $version !== $this->version ) {
90
- throw new Yoast_OAuthException( 'OAuth version ' . $version . ' not supported' );
91
- }
92
-
93
- return $version;
94
- }
95
-
96
- /**
97
- * figure out the signature with some defaults
98
- */
99
- private function get_signature_method( $request ) {
100
- $signature_method = $request instanceof Yoast_OAuthRequest
101
- ? $request->get_parameter( 'oauth_signature_method' )
102
- : null;
103
-
104
- if ( ! $signature_method ) {
105
- // According to chapter 7 ("Accessing Protected Ressources") the signature-method
106
- // parameter is required, and we can't just fallback to PLAINTEXT
107
- throw new Yoast_OAuthException( 'No signature method parameter. This parameter is required' );
108
- }
109
-
110
- if ( ! in_array( $signature_method,
111
- array_keys( $this->signature_methods ) )
112
- ) {
113
- throw new Yoast_OAuthException(
114
- 'Signature method ' . $signature_method . ' not supported ' .
115
- 'try one of the following: ' .
116
- implode( ', ', array_keys( $this->signature_methods ) )
117
- );
118
- }
119
-
120
- return $this->signature_methods[$signature_method];
121
- }
122
-
123
- /**
124
- * try to find the consumer for the provided request's consumer key
125
- */
126
- private function get_consumer( $request ) {
127
- $consumer_key = $request instanceof Yoast_OAuthRequest
128
- ? $request->get_parameter( 'oauth_consumer_key' )
129
- : null;
130
-
131
- if ( ! $consumer_key ) {
132
- throw new Yoast_OAuthException( 'Invalid consumer key' );
133
- }
134
-
135
- $consumer = $this->data_store->lookup_consumer( $consumer_key );
136
- if ( ! $consumer ) {
137
- throw new Yoast_OAuthException( 'Invalid consumer' );
138
- }
139
-
140
- return $consumer;
141
- }
142
-
143
- /**
144
- * try to find the token for the provided request's token key
145
- */
146
- private function get_token( $request, $consumer, $token_type = 'access' ) {
147
- $token_field = $request instanceof Yoast_OAuthRequest
148
- ? $request->get_parameter( 'oauth_token' )
149
- : null;
150
-
151
- $token = $this->data_store->lookup_token(
152
- $consumer, $token_type, $token_field
153
- );
154
- if ( ! $token ) {
155
- throw new Yoast_OAuthException( "Invalid $token_type token: $token_field" );
156
- }
157
-
158
- return $token;
159
- }
160
-
161
- /**
162
- * all-in-one function to check the signature on a request
163
- * should guess the signature method appropriately
164
- */
165
- private function check_signature( $request, $consumer, $token ) {
166
- // this should probably be in a different method
167
- $timestamp = $request instanceof Yoast_OAuthRequest
168
- ? $request->get_parameter( 'oauth_timestamp' )
169
- : null;
170
- $nonce = $request instanceof Yoast_OAuthRequest
171
- ? $request->get_parameter( 'oauth_nonce' )
172
- : null;
173
-
174
- $this->check_timestamp( $timestamp );
175
- $this->check_nonce( $consumer, $token, $nonce, $timestamp );
176
-
177
- $signature_method = $this->get_signature_method( $request );
178
-
179
- $signature = $request->get_parameter( 'oauth_signature' );
180
- $valid_sig = $signature_method->check_signature(
181
- $request,
182
- $consumer,
183
- $token,
184
- $signature
185
- );
186
-
187
- if ( ! $valid_sig ) {
188
- throw new Yoast_OAuthException( 'Invalid signature' );
189
- }
190
- }
191
-
192
- /**
193
- * check that the timestamp is new enough
194
- */
195
- private function check_timestamp( $timestamp ) {
196
- if ( ! $timestamp ) {
197
- throw new Yoast_OAuthException(
198
- 'Missing timestamp parameter. The parameter is required'
199
- );
200
- }
201
-
202
- // verify that timestamp is recentish
203
- $now = time();
204
- if ( abs( $now - $timestamp ) > $this->timestamp_threshold ) {
205
- throw new Yoast_OAuthException(
206
- "Expired timestamp, yours $timestamp, ours $now"
207
- );
208
- }
209
- }
210
-
211
- /**
212
- * check that the nonce is not repeated
213
- */
214
- private function check_nonce( $consumer, $token, $nonce, $timestamp ) {
215
- if ( ! $nonce ) {
216
- throw new Yoast_OAuthException(
217
- 'Missing nonce parameter. The parameter is required'
218
- );
219
- }
220
-
221
- // verify that the nonce is uniqueish
222
- $found = $this->data_store->lookup_nonce(
223
- $consumer,
224
- $token,
225
- $nonce,
226
- $timestamp
227
- );
228
- if ( $found ) {
229
- throw new Yoast_OAuthException( "Nonce already used: $nonce" );
230
- }
231
- }
232
-
233
- }
234
-
235
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-signature-method-hmac-sha1.php DELETED
@@ -1,35 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_OAuthSignatureMethod_HMAC_SHA1' ) ) {
4
-
5
- /**
6
- * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
7
- * where the Signature Base String is the text and the key is the concatenated values (each first
8
- * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
9
- * character (ASCII code 38) even if empty.
10
- * - Chapter 9.2 ("HMAC-SHA1")
11
- */
12
- class Yoast_OAuthSignatureMethod_HMAC_SHA1 extends Yoast_OAuthSignatureMethod {
13
-
14
- function get_name() {
15
- return 'HMAC-SHA1';
16
- }
17
-
18
- public function build_signature( $request, $consumer, $token ) {
19
- $base_string = $request->get_signature_base_string();
20
- $request->base_string = $base_string;
21
-
22
- $key_parts = array(
23
- $consumer->secret,
24
- ( $token ) ? $token->secret : '',
25
- );
26
-
27
- $key_parts = Yoast_OAuthUtil::urlencode_rfc3986( $key_parts );
28
- $key = implode( '&', $key_parts );
29
-
30
- return base64_encode( hash_hmac( 'sha1', $base_string, $key, true ) );
31
- }
32
-
33
- }
34
-
35
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-signature-method-plaintext.php DELETED
@@ -1,40 +0,0 @@
1
- <?php
2
-
3
- if(!class_exists('Yoast_OAuthSignatureMethod_PLAINTEXT')) {
4
-
5
- /**
6
- * The PLAINTEXT method does not provide any security protection and SHOULD only be used
7
- * over a secure channel such as HTTPS. It does not use the Signature Base String.
8
- * - Chapter 9.4 ("PLAINTEXT")
9
- */
10
- class Yoast_OAuthSignatureMethod_PLAINTEXT extends Yoast_OAuthSignatureMethod {
11
-
12
- public function get_name() {
13
- return 'PLAINTEXT';
14
- }
15
-
16
- /**
17
- * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
18
- * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
19
- * empty. The result MUST be encoded again.
20
- * - Chapter 9.4.1 ("Generating Signatures")
21
- *
22
- * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
23
- * Yoast_OAuthRequest handles this!
24
- */
25
- public function build_signature( $request, $consumer, $token ) {
26
- $key_parts = array(
27
- $consumer->secret,
28
- ( $token ) ? $token->secret : '',
29
- );
30
-
31
- $key_parts = Yoast_OAuthUtil::urlencode_rfc3986( $key_parts );
32
- $key = implode( '&', $key_parts );
33
- $request->base_string = $key;
34
-
35
- return $key;
36
- }
37
-
38
- }
39
-
40
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-signature-method-rsa-sha1.php DELETED
@@ -1,74 +0,0 @@
1
- <?php
2
-
3
- if( !class_exists('Yoast_OAuthSignatureMethod_RSA_SHA1')) {
4
-
5
- /**
6
- * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
7
- * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
8
- * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
9
- * verified way to the Service Provider, in a manner which is beyond the scope of this
10
- * specification.
11
- * - Chapter 9.3 ("RSA-SHA1")
12
- */
13
- abstract class Yoast_OAuthSignatureMethod_RSA_SHA1 extends Yoast_OAuthSignatureMethod {
14
-
15
- public function get_name() {
16
- return 'RSA-SHA1';
17
- }
18
-
19
- // Up to the SP to implement this lookup of keys. Possible ideas are:
20
- // (1) do a lookup in a table of trusted certs keyed off of consumer
21
- // (2) fetch via http using a url provided by the requester
22
- // (3) some sort of specific discovery code based on request
23
- //
24
- // Either way should return a string representation of the certificate
25
- protected abstract function fetch_public_cert( &$request );
26
-
27
- // Up to the SP to implement this lookup of keys. Possible ideas are:
28
- // (1) do a lookup in a table of trusted certs keyed off of consumer
29
- //
30
- // Either way should return a string representation of the certificate
31
- protected abstract function fetch_private_cert( &$request );
32
-
33
- public function build_signature( $request, $consumer, $token ) {
34
- $base_string = $request->get_signature_base_string();
35
- $request->base_string = $base_string;
36
-
37
- // Fetch the private key cert based on the request
38
- $cert = $this->fetch_private_cert( $request );
39
-
40
- // Pull the private key ID from the certificate
41
- $privatekeyid = openssl_get_privatekey( $cert );
42
-
43
- // Sign using the key
44
- $ok = openssl_sign( $base_string, $signature, $privatekeyid );
45
-
46
- // Release the key resource
47
- openssl_free_key( $privatekeyid );
48
-
49
- return base64_encode( $signature );
50
- }
51
-
52
- public function check_signature( $request, $consumer, $token, $signature ) {
53
- $decoded_sig = base64_decode( $signature );
54
-
55
- $base_string = $request->get_signature_base_string();
56
-
57
- // Fetch the public key cert based on the request
58
- $cert = $this->fetch_public_cert( $request );
59
-
60
- // Pull the public key ID from the certificate
61
- $publickeyid = openssl_get_publickey( $cert );
62
-
63
- // Check the computed signature against the one passed in the query
64
- $ok = openssl_verify( $base_string, $decoded_sig, $publickeyid );
65
-
66
- // Release the key resource
67
- openssl_free_key( $publickeyid );
68
-
69
- return $ok == 1;
70
- }
71
-
72
- }
73
-
74
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-signature-method.php DELETED
@@ -1,63 +0,0 @@
1
- <?php
2
-
3
- if ( ! class_exists( 'Yoast_OAuthSignatureMethod' ) ) {
4
- /**
5
- * A class for implementing a Signature Method
6
- * See section 9 ("Signing Requests") in the spec
7
- */
8
- abstract class Yoast_OAuthSignatureMethod {
9
-
10
- /**
11
- * Needs to return the name of the Signature Method (ie HMAC-SHA1)
12
- * @return string
13
- */
14
- abstract public function get_name();
15
-
16
- /**
17
- * Build up the signature
18
- * NOTE: The output of this function MUST NOT be urlencoded.
19
- * the encoding is handled in Yoast_OAuthRequest when the final
20
- * request is serialized
21
- *
22
- * @param Yoast_OAuthRequest $request
23
- * @param Yoast_OAuthConsumer $consumer
24
- * @param Yoast_OAuthToken $token
25
- *
26
- * @return string
27
- */
28
- abstract public function build_signature( $request, $consumer, $token );
29
-
30
- /**
31
- * Verifies that a given signature is correct
32
- *
33
- * @param Yoast_OAuthRequest $request
34
- * @param Yoast_OAuthConsumer $consumer
35
- * @param Yoast_OAuthToken $token
36
- * @param string $signature
37
- *
38
- * @return bool
39
- */
40
- public function check_signature( $request, $consumer, $token, $signature ) {
41
- $built = $this->build_signature( $request, $consumer, $token );
42
-
43
- // Check for zero length, although unlikely here
44
- if ( strlen( $built ) == 0 || strlen( $signature ) == 0 ) {
45
- return false;
46
- }
47
-
48
- if ( strlen( $built ) != strlen( $signature ) ) {
49
- return false;
50
- }
51
-
52
- // Avoid a timing leak with a (hopefully) time insensitive compare
53
- $result = 0;
54
- for ( $i = 0; $i < strlen( $signature ); $i ++ ) {
55
- $result |= ord( $built{$i} ) ^ ord( $signature{$i} );
56
- }
57
-
58
- return $result == 0;
59
- }
60
-
61
- }
62
-
63
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-token.php DELETED
@@ -1,37 +0,0 @@
1
- <?php
2
-
3
- if(!class_exists('Yoast_OAuthToken')) {
4
-
5
- class Yoast_OAuthToken {
6
-
7
- // access tokens and request tokens
8
- public $key;
9
- public $secret;
10
-
11
- /**
12
- * key = the token
13
- * secret = the token secret
14
- */
15
- function __construct( $key, $secret ) {
16
- $this->key = $key;
17
- $this->secret = $secret;
18
- }
19
-
20
- /**
21
- * generates the basic string serialization of a token that a server
22
- * would respond to request_token and access_token calls with
23
- */
24
- function to_string() {
25
- return 'oauth_token=' .
26
- Yoast_OAuthUtil::urlencode_rfc3986( $this->key ) .
27
- '&oauth_token_secret=' .
28
- Yoast_OAuthUtil::urlencode_rfc3986( $this->secret );
29
- }
30
-
31
- function __toString() {
32
- return $this->to_string();
33
- }
34
-
35
- }
36
-
37
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/api-libs/oauth/class-oauth-util.php DELETED
@@ -1,169 +0,0 @@
1
- <?php
2
-
3
- if(!class_exists('Yoast_OAuthUtil')) {
4
-
5
- class Yoast_OAuthUtil {
6
-
7
- public static function urlencode_rfc3986( $input ) {
8
- if ( is_array( $input ) ) {
9
- return array_map( array( 'Yoast_OAuthUtil', 'urlencode_rfc3986' ), $input );
10
- } else {
11
- if ( is_scalar( $input ) ) {
12
- return str_replace(
13
- '+',
14
- ' ',
15
- str_replace( '%7E', '~', rawurlencode( $input ) )
16
- );
17
- } else {
18
- return '';
19
- }
20
- }
21
- }
22
-
23
-
24
- // This decode function isn't taking into consideration the above
25
- // modifications to the encoding process. However, this method doesn't
26
- // seem to be used anywhere so leaving it as is.
27
- public static function urldecode_rfc3986( $string ) {
28
- return urldecode( $string );
29
- }
30
-
31
- // Utility function for turning the Authorization: header into
32
- // parameters, has to do some unescaping
33
- // Can filter out any non-oauth parameters if needed (default behaviour)
34
- // May 28th, 2010 - method updated to tjerk.meesters for a speed improvement.
35
- // see http://code.google.com/p/oauth/issues/detail?id=163
36
- public static function split_header( $header, $only_allow_oauth_parameters = true ) {
37
- $params = array();
38
- if ( preg_match_all( '/(' . ( $only_allow_oauth_parameters ? 'oauth_' : '' ) . '[a-z_-]*)=(:?"([^"]*)"|([^,]*))/', $header, $matches ) ) {
39
- foreach ( $matches[1] as $i => $h ) {
40
- $params[$h] = Yoast_OAuthUtil::urldecode_rfc3986( empty( $matches[3][$i] ) ? $matches[4][$i] : $matches[3][$i] );
41
- }
42
- if ( isset( $params['realm'] ) ) {
43
- unset( $params['realm'] );
44
- }
45
- }
46
-
47
- return $params;
48
- }
49
-
50
- // helper to try to sort out headers for people who aren't running apache
51
- public static function get_headers() {
52
- if ( function_exists( 'apache_request_headers' ) ) {
53
- // we need this to get the actual Authorization: header
54
- // because apache tends to tell us it doesn't exist
55
- $headers = apache_request_headers();
56
-
57
- // sanitize the output of apache_request_headers because
58
- // we always want the keys to be Cased-Like-This and arh()
59
- // returns the headers in the same case as they are in the
60
- // request
61
- $out = array();
62
- foreach ( $headers AS $key => $value ) {
63
- $key = str_replace(
64
- ' ',
65
- '-',
66
- ucwords( strtolower( str_replace( '-', ' ', $key ) ) )
67
- );
68
- $out[$key] = $value;
69
- }
70
- } else {
71
- // otherwise we don't have apache and are just going to have to hope
72
- // that $_SERVER actually contains what we need
73
- $out = array();
74
- if ( isset( $_SERVER['CONTENT_TYPE'] ) ) {
75
- $out['Content-Type'] = $_SERVER['CONTENT_TYPE'];
76
- }
77
- if ( isset( $_ENV['CONTENT_TYPE'] ) ) {
78
- $out['Content-Type'] = $_ENV['CONTENT_TYPE'];
79
- }
80
-
81
- foreach ( $_SERVER as $key => $value ) {
82
- if ( substr( $key, 0, 5 ) == 'HTTP_' ) {
83
- // this is chaos, basically it is just there to capitalize the first
84
- // letter of every word that is not an initial HTTP and strip HTTP
85
- // code from przemek
86
- $key = str_replace(
87
- ' ',
88
- '-',
89
- ucwords( strtolower( str_replace( '_', ' ', substr( $key, 5 ) ) ) )
90
- );
91
- $out[$key] = $value;
92
- }
93
- }
94
- }
95
-
96
- return $out;
97
- }
98
-
99
- // This function takes a input like a=b&a=c&d=e and returns the parsed
100
- // parameters like this
101
- // array('a' => array('b','c'), 'd' => 'e')
102
- public static function parse_parameters( $input ) {
103
- if ( ! isset( $input ) || ! $input ) {
104
- return array();
105
- }
106
-
107
- $pairs = explode( '&', $input );
108
-
109
- $parsed_parameters = array();
110
- foreach ( $pairs as $pair ) {
111
- $split = explode( '=', $pair, 2 );
112
- $parameter = Yoast_OAuthUtil::urldecode_rfc3986( $split[0] );
113
- $value = isset( $split[1] ) ? Yoast_OAuthUtil::urldecode_rfc3986( $split[1] ) : '';
114
-
115
- if ( isset( $parsed_parameters[$parameter] ) ) {
116
- // We have already recieved parameter(s) with this name, so add to the list
117
- // of parameters with this name
118
-
119
- if ( is_scalar( $parsed_parameters[$parameter] ) ) {
120
- // This is the first duplicate, so transform scalar (string) into an array
121
- // so we can add the duplicates
122
- $parsed_parameters[$parameter] = array( $parsed_parameters[$parameter] );
123
- }
124
-
125
- $parsed_parameters[$parameter][] = $value;
126
- } else {
127
- $parsed_parameters[$parameter] = $value;
128
- }
129
- }
130
-
131
- return $parsed_parameters;
132
- }
133
-
134
- public static function build_http_query( $params ) {
135
- if ( ! $params ) {
136
- return '';
137
- }
138
-
139
- // Urlencode both keys and values
140
- $keys = Yoast_OAuthUtil::urlencode_rfc3986( array_keys( $params ) );
141
- $values = Yoast_OAuthUtil::urlencode_rfc3986( array_values( $params ) );
142
- $params = array_combine( $keys, $values );
143
-
144
- // Parameters are sorted by name, using lexicographical byte value ordering.
145
- // Ref: Spec: 9.1.1 (1)
146
- uksort( $params, 'strcmp' );
147
-
148
- $pairs = array();
149
- foreach ( $params as $parameter => $value ) {
150
- if ( is_array( $value ) ) {
151
- // If two or more parameters share the same name, they are sorted by their value
152
- // Ref: Spec: 9.1.1 (1)
153
- // June 12th, 2010 - changed to sort because of issue 164 by hidetaka
154
- sort( $value, SORT_STRING );
155
- foreach ( $value as $duplicate_value ) {
156
- $pairs[] = $parameter . '=' . $duplicate_value;
157
- }
158
- } else {
159
- $pairs[] = $parameter . '=' . $value;
160
- }
161
- }
162
- // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61)
163
- // Each name-value pair is separated by an '&' character (ASCII code 38)
164
- return implode( '&', $pairs );
165
- }
166
-
167
- }
168
-
169
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
admin/class-admin-assets.php CHANGED
@@ -11,20 +11,20 @@ if ( ! class_exists( 'Yoast_GA_Admin_Assets' ) ) {
11
  * Add the scripts to the admin head
12
  */
13
  public static function enqueue_scripts() {
14
- wp_enqueue_script( 'yoast_ga_admin', self::get_asset_path( 'assets/js/yoast_ga_admin' ) . self::file_ext( '.js' ) );
15
 
16
  // Enqueue the qtip js file
17
  wp_enqueue_script( 'jquery-qtip', self::get_asset_path( 'assets/dependencies/qtip/jquery.qtip.min.js' ) , array( 'jquery' ), '1.0.0-RC3', true );
18
 
19
  // Enqueue the chosen js file
20
- wp_enqueue_script( 'chosen_js', self::get_asset_path( 'assets/dependencies/chosen/chosen.jquery.min.js' ), array(), false, true );
21
  }
22
 
23
  /**
24
  * Add the styles in the admin head
25
  */
26
  public static function enqueue_styles() {
27
- wp_enqueue_style( 'yoast_ga_styles', self::get_asset_path( 'assets/css/yoast_ga_styles' ) . self::file_ext( '.css' ) );
28
  }
29
 
30
  /**
@@ -32,7 +32,7 @@ if ( ! class_exists( 'Yoast_GA_Admin_Assets' ) ) {
32
  */
33
  public static function enqueue_settings_styles() {
34
  // Enqueue the chosen css file
35
- wp_enqueue_style( 'chosen_css', self::get_asset_path( 'assets/dependencies/chosen/chosen' ) . self::file_ext( '.css' ) );
36
  }
37
 
38
  /**
@@ -41,23 +41,23 @@ if ( ! class_exists( 'Yoast_GA_Admin_Assets' ) ) {
41
  public static function enqueue_dashboard_assets() {
42
 
43
  //
44
- wp_enqueue_script('ga-admin-dashboard', self::get_asset_path( 'assets/js/yoast_ga_admin_dashboard' ) . self::file_ext( '.js' ) );
45
- wp_enqueue_style('ga-admin-dashboard-css', self::get_asset_path( 'assets/css/yoast_ga_admin_dashboard' ). self::file_ext( '.css' ) ) ;
46
 
47
  // Enqueue the d3 js file
48
- wp_enqueue_script( 'd3_js', self::get_asset_path( 'assets/dependencies/rickshaw/d3.v3.min.js' ), array(), false, true );
49
 
50
  // Enqueue the ricksaw js file
51
- wp_enqueue_script( 'rickshaw_js', self::get_asset_path( 'assets/dependencies/rickshaw/rickshaw.min.js' ), array(), false, true );
52
 
53
  // Enqueue the rickshaw css
54
- wp_enqueue_style( 'rickshaw_css', self::get_asset_path( 'assets/dependencies/rickshaw/rickshaw.min.css' ) );
55
 
56
  // Enqueue the datatables js file
57
- wp_enqueue_script( 'datatables_js', self::get_asset_path( 'assets/dependencies/datatables/js/jquery.dataTables.min.js' ), array(), false, true );
58
 
59
  // Enqueue the datatables css
60
- wp_enqueue_style( 'datatables_css', self::get_asset_path( 'assets/dependencies/datatables/css/jquery.dataTables.min.css' ) );
61
 
62
  Yoast_GA_Dashboards::get_instance()->add_dashboard_js_translations();
63
  }
11
  * Add the scripts to the admin head
12
  */
13
  public static function enqueue_scripts() {
14
+ wp_enqueue_script( 'yoast_ga_admin', self::get_asset_path( 'assets/js/yoast_ga_admin' ) . self::file_ext( '.js' ), array( 'jquery' ), GAWP_VERSION );
15
 
16
  // Enqueue the qtip js file
17
  wp_enqueue_script( 'jquery-qtip', self::get_asset_path( 'assets/dependencies/qtip/jquery.qtip.min.js' ) , array( 'jquery' ), '1.0.0-RC3', true );
18
 
19
  // Enqueue the chosen js file
20
+ wp_enqueue_script( 'chosen_js', self::get_asset_path( 'assets/dependencies/chosen/chosen.jquery.min.js' ), array(), GAWP_VERSION, true );
21
  }
22
 
23
  /**
24
  * Add the styles in the admin head
25
  */
26
  public static function enqueue_styles() {
27
+ wp_enqueue_style( 'yoast_ga_styles', self::get_asset_path( 'assets/css/yoast_ga_styles' ) . self::file_ext( '.css' ), array(), GAWP_VERSION );
28
  }
29
 
30
  /**
32
  */
33
  public static function enqueue_settings_styles() {
34
  // Enqueue the chosen css file
35
+ wp_enqueue_style( 'chosen_css', self::get_asset_path( 'assets/dependencies/chosen/chosen' ) . self::file_ext( '.css' ), array(), GAWP_VERSION );
36
  }
37
 
38
  /**
41
  public static function enqueue_dashboard_assets() {
42
 
43
  //
44
+ wp_enqueue_script('ga-admin-dashboard', self::get_asset_path( 'assets/js/yoast_ga_admin_dashboard' ) . self::file_ext( '.js' ), array(), GAWP_VERSION );
45
+ wp_enqueue_style('ga-admin-dashboard-css', self::get_asset_path( 'assets/css/yoast_ga_admin_dashboard' ). self::file_ext( '.css' ), array(), GAWP_VERSION ) ;
46
 
47
  // Enqueue the d3 js file
48
+ wp_enqueue_script( 'd3_js', self::get_asset_path( 'assets/dependencies/rickshaw/d3.v3.min.js' ), array(), GAWP_VERSION, true );
49
 
50
  // Enqueue the ricksaw js file
51
+ wp_enqueue_script( 'rickshaw_js', self::get_asset_path( 'assets/dependencies/rickshaw/rickshaw.min.js' ), array(), GAWP_VERSION, true );
52
 
53
  // Enqueue the rickshaw css
54
+ wp_enqueue_style( 'rickshaw_css', self::get_asset_path( 'assets/dependencies/rickshaw/rickshaw.min.css' ), array(), GAWP_VERSION );
55
 
56
  // Enqueue the datatables js file
57
+ wp_enqueue_script( 'datatables_js', self::get_asset_path( 'assets/dependencies/datatables/js/jquery.dataTables.min.js' ), array(), GAWP_VERSION, true );
58
 
59
  // Enqueue the datatables css
60
+ wp_enqueue_style( 'datatables_css', self::get_asset_path( 'assets/dependencies/datatables/css/jquery.dataTables.min.css' ), array(), GAWP_VERSION );
61
 
62
  Yoast_GA_Dashboards::get_instance()->add_dashboard_js_translations();
63
  }
admin/class-admin-form.php CHANGED
@@ -7,6 +7,215 @@ if ( ! class_exists( 'Yoast_GA_Admin_Form' ) ) {
7
 
8
  class Yoast_GA_Admin_Form {
9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  /**
11
  * Show a question mark with help
12
  *
@@ -21,6 +230,70 @@ if ( ! class_exists( 'Yoast_GA_Admin_Form' ) ) {
21
  return $help;
22
  }
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  }
25
 
26
  }
7
 
8
  class Yoast_GA_Admin_Form {
9
 
10
+ private static $form_namespace;
11
+
12
+ /**
13
+ * Create a form element to init a form
14
+ *
15
+ * @param string $namespace
16
+ *
17
+ * @return string
18
+ */
19
+ public static function create_form( $namespace ) {
20
+ self::$form_namespace = $namespace;
21
+
22
+ $action = admin_url( 'admin.php' );
23
+ if ( isset( $_GET['page'] ) ) {
24
+ $action .= '?page=' . $_GET['page'];
25
+ }
26
+
27
+ return '<form action="' . $action . '" method="post" id="yoast-ga-form-' . self::$form_namespace . '" class="yoast_ga_form">' . wp_nonce_field( 'save_settings', 'yoast_ga_nonce', null, false );
28
+ }
29
+
30
+
31
+ /**
32
+ * Return the form end tag and the submit button
33
+ *
34
+ * @param string $button_label
35
+ * @param string $name
36
+ *
37
+ * @return null|string
38
+ */
39
+ public static function end_form( $button_label = 'Save changes', $name = 'submit' ) {
40
+ $output = null;
41
+ $output .= '<div class="ga-form ga-form-input">';
42
+ $output .= '<input type="submit" name="ga-form-' . $name . '" value="' . $button_label . '" class="button button-primary ga-form-submit" id="yoast-ga-form-submit-' . self::$form_namespace . '">';
43
+ $output .= '</div></form>';
44
+
45
+ return $output;
46
+ }
47
+
48
+
49
+ /**
50
+ * Create a input form element with our labels and wrap them
51
+ *
52
+ * @param string $type
53
+ * @param null|string $title
54
+ * @param null|string $name
55
+ * @param null|string $text_label
56
+ * @param null|string $description
57
+ *
58
+ * @return null|string
59
+ */
60
+ public static function input( $type = 'text', $title = null, $name = null, $text_label = null, $description = null ) {
61
+ $input = null;
62
+ $id = str_replace( '[', '-', $name );
63
+ $id = str_replace( ']', '', $id );
64
+
65
+ $input_value = self::get_formfield_from_options( $name );
66
+
67
+ $input .= '<div class="ga-form ga-form-input">';
68
+ if ( ! is_null( $title ) ) {
69
+ $input .= self::label( $id, $title, $type );
70
+ }
71
+
72
+ $attributes = array(
73
+ 'type' => $type,
74
+ 'id' => 'yoast-ga-form-' . $type . '-' . self::$form_namespace . '-' . $id . '',
75
+ 'name' => $name,
76
+ 'class' => 'ga-form ga-form-' . $type . ' ',
77
+ );
78
+
79
+ if ( $type == 'checkbox' ) {
80
+ $attributes['value'] = '1';
81
+
82
+ if ( $input_value == 1 ) {
83
+ $attributes['checked'] = 'checked';
84
+ }
85
+ } else {
86
+ $attributes['value'] = stripslashes( $input_value );
87
+ }
88
+
89
+ $input .= '<input ' . self::parse_attributes( $attributes ) . ' />';
90
+
91
+ if ( ! is_null( $text_label ) ) {
92
+ $input .= '<label class="ga-form ga-form-' . $type . '-label" id="yoast-ga-form-label-' . $type . '-textlabel-' . self::$form_namespace . '-' . $id . '" for="yoast-ga-form-' . $type . '-' . self::$form_namespace . '-' . $id . '" />' . $text_label . '</label>';
93
+ }
94
+
95
+ // If we get a description, append it to this select field in a new row
96
+ if ( ! is_null( $description ) ) {
97
+ $input .= self::show_help( $id, $description );
98
+ }
99
+
100
+ $input .= '</div>';
101
+
102
+ return $input;
103
+ }
104
+
105
+ /**
106
+ * Generate a select box
107
+ *
108
+ * @param string $title
109
+ * @param string $name
110
+ * @param array $values
111
+ * @param null|string $description
112
+ * @param bool $multiple
113
+ * @param string $empty_text
114
+ *
115
+ * @return null|string
116
+ */
117
+ public static function select( $title, $name, $values, $description = null, $multiple = false, $empty_text = null ) {
118
+ $select = null;
119
+ $id = str_replace( '[', '-', $name );
120
+ $id = str_replace( ']', '', $id );
121
+
122
+ $select .= '<div class="ga-form ga-form-input">';
123
+ if ( ! is_null( $title ) ) {
124
+ $select .= self::label( $id, $title, 'select' ); //'<label class="ga-form ga-form-select-label ga-form-label-left" id="yoast-ga-form-label-select-' . self::$form_namespace . '-' . $id . '" />' . $title . ':</label>';
125
+ }
126
+
127
+ if ( $multiple ) {
128
+ $select .= '<select multiple name="' . $name . '[]" id="yoast-ga-form-select-' . self::$form_namespace . '-' . $id . '" class="ga-multiple">';
129
+ } else {
130
+ $select .= '<select data-placeholder="' . $empty_text . '" name="' . $name . '" id="yoast-ga-form-select-' . self::$form_namespace . '-' . $id . '">';
131
+ if ( ! is_null( $empty_text ) ) {
132
+ $select .= '<option></option>';
133
+ }
134
+ }
135
+ if ( count( $values ) >= 1 ) {
136
+ $select_value = self::get_formfield_from_options( $name );
137
+
138
+ foreach ( $values as $optgroup => $value ) {
139
+ if ( ! empty( $value['options'] ) ) {
140
+ $select .= '<optgroup label="' . $optgroup . '">';
141
+
142
+ foreach ( $value['options'] as $option ) {
143
+ $select .= self::option( $select_value, $option );
144
+ }
145
+
146
+ $select .= '</optgroup>';
147
+
148
+ } else {
149
+ $select .= self::option( $select_value, $value );
150
+ }
151
+ }
152
+ }
153
+ $select .= '</select>';
154
+
155
+ if ( ! is_null( $description ) ) {
156
+ $select .= self::show_help( $id, $description );
157
+ }
158
+
159
+ $select .= '</div>';
160
+
161
+ return $select;
162
+ }
163
+
164
+
165
+ /**
166
+ * Generate a textarea field
167
+ *
168
+ * @param string $title
169
+ * @param string $name
170
+ * @param null|string $description
171
+ *
172
+ * @return null|string
173
+ */
174
+ public static function textarea( $title, $name, $description = null ) {
175
+ $text = null;
176
+ $id = Yoast_GA_Options::instance()->option_prefix . '_' . $name;
177
+
178
+ $textarea_value = self::get_formfield_from_options( $name );
179
+
180
+ $text .= '<div class="ga-form ga-form-input">';
181
+
182
+ if ( ! is_null( $title ) ) {
183
+ $text .= '<label class="ga-form ga-form-select-label ga-form-label-left" id="yoast-ga-form-label-select-' . self::$form_namespace . '-' . $id . '" />' . __( $title, 'google-analytics-for-wordpress' ) . ':</label>';
184
+ }
185
+
186
+ $text .= '<textarea rows="5" cols="60" name="' . $name . '" id="yoast-ga-form-textarea-' . self::$form_namespace . '-' . $id . '">' . stripslashes( $textarea_value ) . '</textarea>';
187
+
188
+ if ( ! is_null( $description ) ) {
189
+ $text .= self::show_help( $id, $description );
190
+ }
191
+
192
+ $text .= '</div>';
193
+
194
+ return $text;
195
+ }
196
+
197
+ /**
198
+ * Parsing a option string for select
199
+ *
200
+ * @param string $select_value
201
+ * @param string $value
202
+ *
203
+ * @return string
204
+ */
205
+ private static function option( $select_value, $value ) {
206
+
207
+ if ( is_array( $select_value ) ) {
208
+ if ( in_array( $value['id'], $select_value ) ) {
209
+ return '<option value="' . $value['id'] . '" selected="selected">' . stripslashes( $value['name'] ) . '</option>';
210
+ } else {
211
+ return '<option value="' . $value['id'] . '">' . stripslashes( $value['name'] ) . '</option>';
212
+ }
213
+ } else {
214
+ return '<option value="' . $value['id'] . '" ' . selected( $select_value, $value['id'], false ) . '>' . stripslashes( $value['name'] ) . '</option>';
215
+ }
216
+ }
217
+
218
+
219
  /**
220
  * Show a question mark with help
221
  *
230
  return $help;
231
  }
232
 
233
+
234
+ /**
235
+ * Will parse the optgroups.
236
+ *
237
+ * @param array $values
238
+ *
239
+ * @return array
240
+ */
241
+ public static function parse_optgroups( $values ) {
242
+ $optgroups = array();
243
+ foreach ( $values as $key => $value ) {
244
+ $optgroups[$value['parent_name']]['options'] = $value['profiles'];
245
+ }
246
+
247
+ return $optgroups;
248
+ }
249
+
250
+ /**
251
+ * Creates a label
252
+ *
253
+ * @param $id
254
+ * @param $title
255
+ * @param $type
256
+ *
257
+ * @return string
258
+ */
259
+ private static function label( $id, $title, $type ) {
260
+ return '<label class="ga-form ga-form-' . $type . '-label ga-form-label-left" id="yoast-ga-form-label-' . $type . '-' . self::$form_namespace . '-' . $id . '" />' . $title . ':</label>';
261
+ }
262
+
263
+ /**
264
+ * Getting the value from the option, if it doesn't exist return empty string
265
+ *
266
+ * @param string $name
267
+ *
268
+ * @return string
269
+ */
270
+ private static function get_formfield_from_options( $name ) {
271
+ static $options;
272
+
273
+ if ( $options === null ) {
274
+ $options = Yoast_GA_Options::instance()->get_options();
275
+ }
276
+
277
+ // Catch a notice if the option doesn't exist, yet
278
+ return ( isset( $options[$name] ) ) ? $options[$name] : '';
279
+ }
280
+
281
+ /**
282
+ * Parsing given array with attributes as an attribute string
283
+ *
284
+ * @param array $attributes_to_parse
285
+ *
286
+ * @return string
287
+ */
288
+ private static function parse_attributes( $attributes_to_parse ) {
289
+ $parsed_attributes = '';
290
+ foreach ( $attributes_to_parse as $attribute_name => $attribute_value ) {
291
+ $parsed_attributes .= $attribute_name . '="' . $attribute_value . '" ';
292
+ }
293
+
294
+ return trim( $parsed_attributes );
295
+ }
296
+
297
  }
298
 
299
  }
admin/class-admin.php CHANGED
@@ -7,8 +7,6 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
7
 
8
  class Yoast_GA_Admin extends Yoast_GA_Options {
9
 
10
- private $form_namespace;
11
-
12
  /**
13
  * Store the API instance
14
  *
@@ -39,7 +37,11 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
39
  */
40
  public function init_settings() {
41
  $this->options = $this->get_options();
42
- $this->api = Yoast_Api_Libs::load_api_libraries( array( 'oauth', 'googleanalytics' ) );
 
 
 
 
43
 
44
  if ( is_null( $this->get_tracking_code() ) ) {
45
  add_action( 'admin_notices', array( $this, 'config_warning' ) );
@@ -66,8 +68,6 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
66
  */
67
  $this->show_notification( 'ga_notifications' );
68
 
69
- $this->connect_with_google_analytics();
70
-
71
  // Load the Google Analytics Dashboards functionality
72
  $dashboards = Yoast_GA_Dashboards::get_instance();
73
  $dashboards->init_dashboards( $this->get_current_profile() );
@@ -86,6 +86,9 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
86
  * @param $data
87
  */
88
  public function save_settings( $data ) {
 
 
 
89
  foreach ( $data as $key => $value ) {
90
  if ( $key != 'return_tab' ) {
91
  if ( $key != 'custom_code' && is_string( $value ) ) {
@@ -140,8 +143,10 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
140
  $ua_code = null;
141
 
142
  foreach ( $profiles as $profile ) {
143
- if ( isset( $profile['id'] ) && $profile['id'] == $profile_id ) {
144
- $ua_code = $profile['ua_code'];
 
 
145
  }
146
  }
147
 
@@ -171,11 +176,13 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
171
  * Adds some promo text for the premium plugin on the custom dimensions tab.
172
  */
173
  public function premium_promo() {
 
174
  echo '<p>';
175
  printf( __( 'If you want to track custom dimensions, to for instance track page views per author or post type, you should upgrade to the %1$spremium version of Google Analytics by Yoast%2$s.', 'google-analytics-for-wordpress' ), '<a href="https://yoast.com/wordpress/plugins/google-analytics/#utm_medium=text-link&utm_source=gawp-config&utm_campaign=wpgaplugin&utm_content=custom_dimensions_tab">', '</a>' );
176
  echo ' ';
177
  _e( 'This will also give you email access to the support team at Yoast, who will provide support on the plugin 24/7.', 'google-analytics-for-wordpress' );
178
  echo '</p>';
 
179
  }
180
 
181
  /**
@@ -233,252 +240,13 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
233
  }
234
  }
235
 
236
- /**
237
- * Create a form element to init a form
238
- *
239
- * @param string $namespace
240
- *
241
- * @return string
242
- */
243
- public function create_form( $namespace ) {
244
- $this->form_namespace = $namespace;
245
-
246
- $action = admin_url( 'admin.php' );
247
- if ( isset( $_GET['page'] ) ) {
248
- $action .= '?page=' . $_GET['page'];
249
- }
250
-
251
- return '<form action="' . $action . '" method="post" id="yoast-ga-form-' . $this->form_namespace . '" class="yoast_ga_form">' . wp_nonce_field( 'save_settings', 'yoast_ga_nonce', null, false );
252
- }
253
-
254
- /**
255
- * Return the form end tag and the submit button
256
- *
257
- * @param string $button_label
258
- * @param string $name
259
- *
260
- * @return null|string
261
- */
262
- public function end_form( $button_label = 'Save changes', $name = 'submit' ) {
263
- $output = null;
264
- $output .= '<div class="ga-form ga-form-input">';
265
- $output .= '<input type="submit" name="ga-form-' . $name . '" value="' . $button_label . '" class="button button-primary ga-form-submit" id="yoast-ga-form-submit-' . $this->form_namespace . '">';
266
- $output .= '</div></form>';
267
-
268
- return $output;
269
- }
270
-
271
- /**
272
- * Create a input form element with our labels and wrap them
273
- *
274
- * @param string $type
275
- * @param null|string $title
276
- * @param null|string $name
277
- * @param null|string $text_label
278
- * @param null|string $description
279
- *
280
- * @return null|string
281
- */
282
- public function input( $type = 'text', $title = null, $name = null, $text_label = null, $description = null ) {
283
- $input = null;
284
- $id = str_replace( '[', '-', $name );
285
- $id = str_replace( ']', '', $id );
286
-
287
- // Catch a notice if the option doesn't exist, yet
288
- if ( ! isset( $this->options[$name] ) ) {
289
- $this->options[$name] = '';
290
- }
291
-
292
- $input .= '<div class="ga-form ga-form-input">';
293
- if ( ! is_null( $title ) ) {
294
- $input .= '<label class="ga-form ga-form-' . $type . '-label ga-form-label-left" id="yoast-ga-form-label-' . $type . '-' . $this->form_namespace . '-' . $id . '" />' . $title . ':</label>';
295
- }
296
-
297
- if ( $type == 'checkbox' && $this->options[$name] == 1 ) {
298
- $input .= '<input type="' . $type . '" class="ga-form ga-form-checkbox" id="yoast-ga-form-' . $type . '-' . $this->form_namespace . '-' . $id . '" name="' . $name . '" value="1" checked="checked" />';
299
- } elseif ( $type == 'checkbox' ) {
300
- $input .= '<input type="' . $type . '" class="ga-form ga-form-checkbox" id="yoast-ga-form-' . $type . '-' . $this->form_namespace . '-' . $id . '" name="' . $name . '" value="1" />';
301
- } else {
302
- $input .= '<input type="' . $type . '" class="ga-form ga-form-' . $type . '" id="yoast-ga-form-' . $type . '-' . $this->form_namespace . '-' . $id . '" name="' . $name . '" value="' . stripslashes( $this->options[$name] ) . '" />';
303
- }
304
-
305
- if ( ! is_null( $text_label ) ) {
306
- $input .= '<label class="ga-form ga-form-' . $type . '-label" id="yoast-ga-form-label-' . $type . '-textlabel-' . $this->form_namespace . '-' . $id . '" for="yoast-ga-form-' . $type . '-' . $this->form_namespace . '-' . $id . '" />' . $text_label . '</label>';
307
- }
308
-
309
- // If we get a description, append it to this select field in a new row
310
- if ( ! is_null( $description ) ) {
311
- $input .= $this->show_help( $id, $description );
312
- }
313
-
314
- $input .= '</div>';
315
-
316
- return $input;
317
- }
318
-
319
- /**
320
- * Show a question mark with help
321
- *
322
- * @param string $id
323
- * @param string $description
324
- *
325
- * @return string
326
- */
327
- private function show_help( $id, $description ) {
328
- $help = '<img src="' . plugins_url( 'assets/img/question-mark.png', GAWP_FILE ) . '" class="alignleft yoast_help" id="' . esc_attr( $id . 'help' ) . '" alt="' . esc_attr( $description ) . '" />';
329
-
330
- return $help;
331
- }
332
-
333
- /**
334
- * Generate a select box
335
- *
336
- * @param string $title
337
- * @param string $name
338
- * @param array $values
339
- * @param null|string $description
340
- * @param bool $multiple
341
- * @param string $empty_text
342
- *
343
- * @return null|string
344
- */
345
- public function select( $title, $name, $values, $description = null, $multiple = false, $empty_text = null ) {
346
- $select = null;
347
- $id = str_replace( '[', '-', $name );
348
- $id = str_replace( ']', '', $id );
349
-
350
- // Catch a notice if the option doesn't exist, yet
351
- if ( ! isset( $this->options[$name] ) ) {
352
- $this->options[$name] = '';
353
- }
354
-
355
- $select .= '<div class="ga-form ga-form-input">';
356
- if ( ! is_null( $title ) ) {
357
- $select .= '<label class="ga-form ga-form-select-label ga-form-label-left" id="yoast-ga-form-label-select-' . $this->form_namespace . '-' . $id . '" />' . $title . ':</label>';
358
- }
359
-
360
- if ( $multiple ) {
361
- $select .= '<select multiple name="' . $name . '[]" id="yoast-ga-form-select-' . $this->form_namespace . '-' . $id . '" class="ga-multiple">';
362
- } else {
363
- $select .= '<select data-placeholder="' . $empty_text . '" name="' . $name . '" id="yoast-ga-form-select-' . $this->form_namespace . '-' . $id . '">';
364
- if ( !is_null( $empty_text ) ) {
365
- $select .= '<option></option>';
366
- }
367
- }
368
- if ( count( $values ) >= 1 ) {
369
-
370
- foreach ( $values as $optgroup => $value ) {
371
-
372
- if( !empty($value['options'])) {
373
- $select .= '<optgroup label="' . $optgroup . '">';
374
-
375
- foreach($value['options'] AS $option) {
376
- $select .= $this->option($name, $option);
377
- }
378
-
379
- $select .= '</optgroup>';
380
-
381
- } else {
382
- $select .= $this->option($name, $value);
383
- }
384
- }
385
- }
386
- $select .= '</select>';
387
-
388
- if ( ! is_null( $description ) ) {
389
- $select .= $this->show_help( $id, $description );
390
- }
391
-
392
- $select .= '</div>';
393
-
394
- return $select;
395
- }
396
-
397
- /**
398
- * Parsing a option string for select
399
- *
400
- * @param string $name
401
- * @param string $value
402
- *
403
- * @return string
404
- */
405
- private function option($name, $value) {
406
- if ( is_array( $this->options[$name] ) ) {
407
- if ( in_array( $value['id'], $this->options[$name] ) ) {
408
- return '<option value="' . $value['id'] . '" selected="selected">' . stripslashes( $value['name'] ) . '</option>';
409
- } else {
410
- return '<option value="' . $value['id'] . '">' . stripslashes( $value['name'] ) . '</option>';
411
- }
412
- } else {
413
- return '<option value="' . $value['id'] . '" ' . selected( $this->options[$name], $value['id'], false ) . '>' . stripslashes( $value['name'] ) . '</option>';
414
- }
415
- }
416
-
417
- /**
418
- * Will parse the optgroups.
419
- *
420
- * @param array $values
421
- *
422
- * @return array
423
- */
424
- public function parse_optgroups($values) {
425
-
426
- $optgroups = array();
427
- foreach($values AS $key => $value) {
428
- if(empty($value['parent_name'])) {
429
- $current = $value;
430
- } else {
431
- $optgroups[$value['parent_name']]['options'][] = $current;
432
- }
433
- }
434
-
435
- return $optgroups;
436
- }
437
-
438
- /**
439
- * Generate a textarea field
440
- *
441
- * @param string $title
442
- * @param string $name
443
- * @param null|string $description
444
- *
445
- * @return null|string
446
- */
447
- public function textarea( $title, $name, $description = null ) {
448
- $text = null;
449
- $id = $this->option_prefix . '_' . $name;
450
-
451
- // Catch a notice if the option doesn't exist, yet
452
- if ( ! isset( $this->options[$name] ) ) {
453
- $this->options[$name] = '';
454
- }
455
-
456
- $text .= '<div class="ga-form ga-form-input">';
457
- if ( ! is_null( $title ) ) {
458
- $text .= '<label class="ga-form ga-form-select-label ga-form-label-left" id="yoast-ga-form-label-select-' . $this->form_namespace . '-' . $id . '" />' . __( $title, 'google-analytics-for-wordpress' ) . ':</label>';
459
- }
460
- $text .= '<textarea rows="5" cols="60" name="' . $name . '" id="yoast-ga-form-textarea-' . $this->form_namespace . '-' . $id . '">' . stripslashes( $this->options[$name] ) . '</textarea>';
461
-
462
- if ( ! is_null( $description ) ) {
463
- $text .= $this->show_help( $id, $description );
464
- }
465
-
466
- $text .= '</div>';
467
-
468
- return $text;
469
- }
470
-
471
  /**
472
  * Get the Google Analytics profiles which are in this google account
473
  *
474
  * @return array
475
  */
476
  public function get_profiles() {
477
- $return = array();
478
- $google_analytics = Yoast_Google_Analytics::instance();
479
- if ( $google_analytics->has_token() ) {
480
- $return = $google_analytics->get_profiles();
481
- }
482
 
483
  return $return;
484
  }
@@ -487,25 +255,21 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
487
  /**
488
  * Checks if there is a callback or reauth to get token from Google Analytics api
489
  */
490
- private function connect_with_google_analytics() {
491
-
492
- if ( isset( $_REQUEST['ga_oauth_callback'] ) ) {
493
 
494
- Yoast_Google_Analytics::instance()->authenticate( $_REQUEST['oauth_token'], $_REQUEST['oauth_verifier'] );
495
-
496
- wp_redirect( menu_page_url( 'yst_ga_settings', false ) );
497
- exit;
498
  }
499
 
 
500
  if ( ! empty ( $_GET['reauth'] ) ) {
501
- $authorize_url = Yoast_Google_Analytics::instance()->authenticate();
502
 
503
  delete_option( 'yst_ga_accounts' );
504
  delete_option( 'yst_ga_response' );
505
 
506
- wp_redirect( $authorize_url );
507
- exit;
508
  }
 
509
  }
510
 
511
  /**
@@ -514,14 +278,11 @@ if ( ! class_exists( 'Yoast_GA_Admin' ) ) {
514
  * @return null
515
  */
516
  private function get_current_profile() {
517
- $current_profile = null;
518
- foreach ( $this->get_profiles() as $profile ) {
519
- if ( ! empty( $profile['id'] ) && $profile['id'] == $this->options['analytics_profile'] ) {
520
- $current_profile = $profile['profile_id'];
521
- }
522
  }
523
-
524
- return $current_profile;
525
  }
526
 
527
  /**
7
 
8
  class Yoast_GA_Admin extends Yoast_GA_Options {
9
 
 
 
10
  /**
11
  * Store the API instance
12
  *
37
  */
38
  public function init_settings() {
39
  $this->options = $this->get_options();
40
+ $this->api = Yoast_Api_Libs::load_api_libraries( array( 'google', 'googleanalytics' ) );
41
+
42
+
43
+ // Listener for reconnecting with google analytics
44
+ $this->google_analytics_listener();
45
 
46
  if ( is_null( $this->get_tracking_code() ) ) {
47
  add_action( 'admin_notices', array( $this, 'config_warning' ) );
68
  */
69
  $this->show_notification( 'ga_notifications' );
70
 
 
 
71
  // Load the Google Analytics Dashboards functionality
72
  $dashboards = Yoast_GA_Dashboards::get_instance();
73
  $dashboards->init_dashboards( $this->get_current_profile() );
86
  * @param $data
87
  */
88
  public function save_settings( $data ) {
89
+
90
+ unset( $data['google_auth_code'] );
91
+
92
  foreach ( $data as $key => $value ) {
93
  if ( $key != 'return_tab' ) {
94
  if ( $key != 'custom_code' && is_string( $value ) ) {
143
  $ua_code = null;
144
 
145
  foreach ( $profiles as $profile ) {
146
+ foreach( $profile['profiles'] as $subprofile ) {
147
+ if ( isset( $subprofile['id'] ) && $subprofile['id'] == $profile_id ) {
148
+ $ua_code = $subprofile['ua_code'];
149
+ }
150
  }
151
  }
152
 
176
  * Adds some promo text for the premium plugin on the custom dimensions tab.
177
  */
178
  public function premium_promo() {
179
+ echo '<div class="ga-promote">';
180
  echo '<p>';
181
  printf( __( 'If you want to track custom dimensions, to for instance track page views per author or post type, you should upgrade to the %1$spremium version of Google Analytics by Yoast%2$s.', 'google-analytics-for-wordpress' ), '<a href="https://yoast.com/wordpress/plugins/google-analytics/#utm_medium=text-link&utm_source=gawp-config&utm_campaign=wpgaplugin&utm_content=custom_dimensions_tab">', '</a>' );
182
  echo ' ';
183
  _e( 'This will also give you email access to the support team at Yoast, who will provide support on the plugin 24/7.', 'google-analytics-for-wordpress' );
184
  echo '</p>';
185
+ echo '</div>';
186
  }
187
 
188
  /**
240
  }
241
  }
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  /**
244
  * Get the Google Analytics profiles which are in this google account
245
  *
246
  * @return array
247
  */
248
  public function get_profiles() {
249
+ $return = Yoast_Google_Analytics::get_instance()->get_profiles();
 
 
 
 
250
 
251
  return $return;
252
  }
255
  /**
256
  * Checks if there is a callback or reauth to get token from Google Analytics api
257
  */
258
+ private function google_analytics_listener() {
 
 
259
 
260
+ if ( ! empty( $_POST['google_auth_code'] ) ) {
261
+ Yoast_Google_Analytics::get_instance()->authenticate( $_POST['google_auth_code'] );
 
 
262
  }
263
 
264
+
265
  if ( ! empty ( $_GET['reauth'] ) ) {
 
266
 
267
  delete_option( 'yst_ga_accounts' );
268
  delete_option( 'yst_ga_response' );
269
 
270
+ Yoast_Google_Analytics::get_instance()->authenticate();
 
271
  }
272
+
273
  }
274
 
275
  /**
278
  * @return null
279
  */
280
  private function get_current_profile() {
281
+ if ( ! empty( $this->options['analytics_profile'] ) ) {
282
+ return $this->options['analytics_profile'];
283
+ } else {
284
+ return null;
 
285
  }
 
 
286
  }
287
 
288
  /**
admin/class-google-analytics.php CHANGED
@@ -5,15 +5,31 @@ if ( ! class_exists( 'Yoast_Google_Analytics', false ) ) {
5
 
6
  class Yoast_Google_Analytics {
7
 
8
- private $access_token;
9
- private $secret;
10
-
11
  private $option_name = 'yst_ga_api';
 
 
 
 
12
  private $options = array();
13
 
 
 
 
14
  private static $instance = null;
15
 
16
- public function __construct() {
 
 
 
 
 
 
 
 
 
17
 
18
  if ( is_null( self::$instance ) ) {
19
  self::$instance = $this;
@@ -21,10 +37,8 @@ if ( ! class_exists( 'Yoast_Google_Analytics', false ) ) {
21
 
22
  $this->options = $this->get_options();
23
 
24
- if ( $this->has_token() ) {
25
- $this->set_access_token();
26
- $this->set_secret();
27
- }
28
  }
29
 
30
  /**
@@ -35,7 +49,7 @@ if ( ! class_exists( 'Yoast_Google_Analytics', false ) ) {
35
  *
36
  * @return null|Yoast_Google_Analytics
37
  */
38
- public static function instance() {
39
  if ( is_null( self::$instance ) ) {
40
  self::$instance = new self();
41
  }
@@ -44,36 +58,12 @@ if ( ! class_exists( 'Yoast_Google_Analytics', false ) ) {
44
  }
45
 
46
  /**
47
- * Is there a token set
48
- *
49
- * Checks in options whether there is a token set or not. Will return true if token is set
50
- *
51
- * @return bool
52
- */
53
- public function has_token() {
54
- return ! empty( $this->options['ga_token'] );
55
- }
56
-
57
- /**
58
- * Connect with google analytics
59
  *
60
- * @param bool $token
61
- * @param bool $verifier
62
- *
63
- * @return string
64
  */
65
- public function authenticate( $token = false, $verifier = false ) {
66
-
67
- if ( ! empty( $token ) && ! empty ( $verifier ) ) {
68
- if ( isset( $this->options['ga_oauth']['oauth_token'] ) && $this->options['ga_oauth']['oauth_token'] == $token ) {
69
- $this->get_access_token( $verifier );
70
- }
71
- } else {
72
- $authorize_url = $this->get_authorize_url();
73
-
74
- return $authorize_url;
75
- }
76
-
77
  }
78
 
79
  /**
@@ -85,346 +75,133 @@ if ( ! class_exists( 'Yoast_Google_Analytics', false ) ) {
85
  * @return array
86
  */
87
  public function get_profiles() {
88
- $return = array();
89
- $result = array();
90
-
91
- $result['accounts'] = $this->get_option_api( 'yst_ga_accounts' );
92
- $result['response'] = $this->get_option_api( 'yst_ga_response' );
93
-
94
- if ( $result['accounts'] === false || $result['response'] === false ) {
95
- $result = $this->fetch_api_profiles();
96
- }
97
 
98
- if ( $result['response'] ) {
99
- $this->save_profile_response( $result['response'], $result['accounts'] );
100
 
101
- $return = $this->parse_profile_response( $result['response'] );
102
  }
103
 
104
- return $return;
105
- }
106
-
107
- /**
108
- * Get the transient of an API call
109
- *
110
- * @param $name
111
- *
112
- * @return string
113
- */
114
- private function get_option_api( $name ) {
115
- return get_option( $name );
116
- }
117
-
118
- /**
119
- * Save the option API
120
- *
121
- * @param $name
122
- * @param $value
123
- *
124
- * @return string
125
- */
126
- private function save_option_api( $name, $value ) {
127
- return update_option( $name, $value );
128
  }
129
 
130
  /**
131
- * Fetch the API profiles and store them
132
  *
133
- * @return array
134
- */
135
- private function fetch_api_profiles() {
136
- $accounts = $this->format_accounts_call( $this->do_request( 'https://www.googleapis.com/analytics/v3/management/accounts/~all/webproperties', 'https://www.googleapis.com/auth/analytics.readonly' ) );
137
- $response = $this->do_request( 'https://www.googleapis.com/analytics/v2.4/management/accounts/~all/webproperties/~all/profiles', 'https://www.googleapis.com/auth/analytics.readonly' );
138
-
139
- // Save the accounts and response results in the new transient
140
- $this->save_option_api( 'yst_ga_accounts', $accounts );
141
- $this->save_option_api( 'yst_ga_response', $response );
142
-
143
- return array(
144
- 'accounts' => $accounts,
145
- 'response' => $response,
146
- );
147
- }
148
-
149
- /**
150
- * Format the accounts request
151
  *
152
- * @param $response
153
  *
154
- * @return array
155
  */
156
- private function format_accounts_call( $response ) {
157
- if ( isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) {
158
- $body = json_decode( $response['body'] );
159
 
160
- if ( is_array( $body->items ) ) {
161
- $accounts = array();
162
 
163
- foreach ( $body->items as $item ) {
164
- $accounts[(string) $item->id] = (string) $item->name;
165
- }
166
-
167
- return $accounts;
168
- }
169
  }
170
 
171
- return array();
172
  }
173
 
174
- /**
175
- * Getting a access token
176
- *
177
- * With this token a reconnection to Google Analytics is possible
178
- *
179
- * @param string $verifier
180
- */
181
- protected function get_access_token( $verifier ) {
182
- $gdata = $this->get_gdata(
183
- 'https://www.google.com/analytics/feeds/',
184
- $this->options['ga_oauth']['oauth_token'],
185
- $this->options['ga_oauth']['oauth_token_secret']
186
- );
187
-
188
- $access_token = $gdata->get_access_token( $verifier );
189
-
190
- $this->options['ga_oauth']['access_token'] = $access_token;
191
- $this->options['ga_token'] = $access_token['oauth_token'];
192
-
193
- unset( $this->options['ga_oauth']['oauth_token'] );
194
- unset( $this->options['ga_oauth']['oauth_token_secret'] );
195
-
196
- $this->update_options();
197
- }
198
 
199
  /**
200
- * Getting the URL to authenticate the use
201
- *
202
- * @return string
203
  */
204
- protected function get_authorize_url() {
205
- $gdata = $this->get_gdata( 'https://www.google.com/analytics/feeds/' );
206
- $request_token = $this->get_request_token( $gdata );
207
-
208
- if ( is_array( $this->options ) ) {
209
- unset( $this->options['ga_token'] );
210
- if ( is_array( $this->options['ga_oauth'] ) ) {
211
- unset( $this->options['ga_oauth']['access_token'] );
212
- }
213
- }
214
-
215
- $this->options['ga_oauth']['oauth_token'] = $request_token['oauth_token'];
216
- $this->options['ga_oauth']['oauth_token_secret'] = $request_token['oauth_token_secret'];
217
-
218
- $this->update_options();
219
-
220
- return $gdata->get_authorize_url( $request_token );
221
  }
222
 
223
  /**
224
- * Get the request token from Google Analytics
225
- *
226
- * @param WP_Gdata $gdata
227
  *
228
- * @return array
229
  */
230
- protected function get_request_token( $gdata ) {
231
- $oauth_callback = add_query_arg( array( 'ga_oauth_callback' => 1 ), menu_page_url( 'yst_ga_settings', false ) );
232
- $request_token = $gdata->get_request_token( $oauth_callback );
233
-
234
- return $request_token;
235
  }
236
 
237
  /**
238
- * Doing request to Google Analytics
239
- *
240
- * This method will do a request to google and get the response code and body from content
241
- *
242
- * @param string $target_url
243
- * @param string $scope
244
- *
245
- * @return array|null
246
  */
247
- protected function do_request( $target_url, $scope ) {
248
- $gdata = $this->get_gdata( $scope, $this->access_token, $this->secret );
249
- $response = $gdata->get( $target_url );
250
- $http_code = wp_remote_retrieve_response_code( $response );
251
- $response = wp_remote_retrieve_body( $response );
252
-
253
- if ( $http_code == 200 ) {
254
- return array(
255
- 'response' => array( 'code' => $http_code ),
256
- 'body' => $response,
257
- );
258
- }
259
  }
260
 
261
  /**
262
- * Getting WP_GData object
263
- *
264
- * If not available include class file and create an instance of WP_GDAta
265
- *
266
- * @param string $scope
267
- * @param null $token
268
- * @param null $secret
269
  *
270
- * @return WP_GData
271
  */
272
- protected function get_gdata( $scope, $token = null, $secret = null ) {
273
- $args = array(
274
- 'scope' => $scope,
275
- 'xoauth_displayname' => 'Google Analytics by Yoast',
 
276
  );
277
 
278
- $gdata = new WP_GData( $args, $token, $secret );
279
 
280
- return $gdata;
281
  }
282
 
283
  /**
284
  * Saving profile response in options
285
  *
286
- * @param $response
287
  */
288
- protected function save_profile_response( $response, $accounts ) {
289
- $this->options['ga_api_response'] = $response;
290
  $this->options['ga_api_response_accounts'] = $accounts;
291
 
292
  $this->update_options();
293
  }
294
 
295
  /**
296
- * Parsing the profile response
297
  *
298
- * Create XML_Reader for the response. Check if there are entries available. Check which link is used and parsing the entries.
299
- * If there are entries parse, then sort them and rebuild array
300
  *
301
- * @return array
302
  */
303
- protected function parse_profile_response() {
304
- $return = array();
305
-
306
- try {
307
- $xml_reader = new SimpleXMLElement( $this->options['ga_api_response']['body'] );
308
 
309
- if ( ! empty( $xml_reader->entry ) ) {
 
 
310
 
311
- $ga_accounts = array();
312
 
313
- // Check whether the feed output is the new one, first set, or the old one, second set.
314
- if ( $xml_reader->link['href'] == 'https://www.googleapis.com/analytics/v2.4/management/accounts/~all/webproperties/~all/profiles' ) {
315
- $ga_accounts = $this->parse_entries( $xml_reader->entry, 1, 2 );
316
- } elseif ( $xml_reader->link['href'] == 'https://www.google.com/analytics/feeds/accounts/default' ) {
317
- $ga_accounts = $this->parse_entries( $xml_reader->entry, 3, 2 );
318
- }
319
 
320
- if ( is_array( $ga_accounts ) ) {
321
- usort( $ga_accounts, array( $this, 'sort_profiles' ) );
322
- }
323
 
324
- foreach ( $ga_accounts as $key => $ga_account ) {
325
- $return[] = array(
326
- 'id' => $ga_account['profile_id'],
327
- 'profile_id' => $ga_account['profile_id'],
328
- 'ua_code' => $ga_account['ua'],
329
- 'name' => $ga_account['title'] . ' (' . $ga_account['ua'] . ')',
330
  );
331
 
332
- if ( isset( $this->options['ga_api_response_accounts'][$ga_account['ua']] ) ) {
333
- $tmp_array['parent_name'] = $this->options['ga_api_response_accounts'][$ga_account['ua']];
334
- }
335
-
336
- $return[] = $tmp_array;
337
  }
338
- }
339
- } catch ( Exception $e ) {
340
-
341
- }
342
-
343
- return $return;
344
-
345
- }
346
-
347
- /**
348
- * Sorting the array in alphabetic order
349
- *
350
- * @param string $a
351
- * @param string $b
352
- *
353
- * @return int
354
- */
355
- protected function sort_profiles( $a, $b ) {
356
- return strcmp( $a['title'], $b['title'] );
357
- }
358
-
359
-
360
- /**
361
- * Parses the entries
362
- *
363
- * The keys can be different for some types of responses, so there are two params which defines the target keys
364
- *
365
- * @param SimpleXMLElement $entries
366
- * @param integer $ua_key
367
- * @param integer $title_key
368
- *
369
- * @return array
370
- */
371
- protected function parse_entries( $entries, $ua_key, $title_key ) {
372
- $return = array();
373
-
374
- foreach ( $entries AS $entry ) {
375
- $ns = $entry->getNamespaces( true );
376
- $properties = $entry->children( $ns['dxp'] )->property;
377
-
378
- $profile_id = (int) $properties[3]->attributes()->value; // ga:profileId
379
 
380
- if ( isset ( $properties[$ua_key]->attributes()->value ) ) {
381
- $ua = (string) trim( $properties[$ua_key]->attributes()->value );
382
- }
383
-
384
- if ( isset ( $properties[$title_key]->attributes()->value ) ) {
385
- $title = (string) trim( $properties[$title_key]->attributes()->value );
386
- }
387
-
388
- if ( ! empty( $ua ) && ! empty( $title ) ) {
389
- $return[] = array(
390
- 'ua' => $ua,
391
- 'profile_id' => $profile_id,
392
- 'title' => $title,
393
- );
394
  }
395
  }
396
 
397
- return $return;
398
- }
399
-
400
- /**
401
- * Setting the token for Google Analytics api
402
- */
403
- protected function set_access_token() {
404
- $this->access_token = $this->options['ga_oauth']['access_token']['oauth_token'];
405
- }
406
-
407
- /**
408
- * Setting the token secret for Google Analytics API
409
- */
410
- protected function set_secret() {
411
- $this->secret = $this->options['ga_oauth']['access_token']['oauth_token_secret'];
412
- }
413
-
414
- /**
415
- * Getting the options bases on $this->option_name from the database
416
- *
417
- * @return mixed
418
- */
419
- public function get_options() {
420
- return get_option( $this->option_name );
421
- }
422
-
423
- /**
424
- * Updating the options based on $this->option_name and the internal property $this->options
425
- */
426
- protected function update_options() {
427
- update_option( $this->option_name, $this->options );
428
  }
429
 
430
  }
5
 
6
  class Yoast_Google_Analytics {
7
 
8
+ /**
9
+ * @var string
10
+ */
11
  private $option_name = 'yst_ga_api';
12
+
13
+ /**
14
+ * @var array|mixed
15
+ */
16
  private $options = array();
17
 
18
+ /**
19
+ * @var null|Yoast_Google_Analytics
20
+ */
21
  private static $instance = null;
22
 
23
+ /**
24
+ * @var The api client object holder
25
+ */
26
+ private $client;
27
+
28
+ /**
29
+ * Singleton
30
+ *
31
+ */
32
+ protected function __construct() {
33
 
34
  if ( is_null( self::$instance ) ) {
35
  self::$instance = $this;
37
 
38
  $this->options = $this->get_options();
39
 
40
+ // Setting the client
41
+ $this->set_client();
 
 
42
  }
43
 
44
  /**
49
  *
50
  * @return null|Yoast_Google_Analytics
51
  */
52
+ public static function get_instance() {
53
  if ( is_null( self::$instance ) ) {
54
  self::$instance = new self();
55
  }
58
  }
59
 
60
  /**
61
+ * Wrapper for authenticate the client. If authentication code is send it will get and check an access token.
 
 
 
 
 
 
 
 
 
 
 
62
  *
63
+ * @param mixed $authentication_code
 
 
 
64
  */
65
+ public function authenticate( $authentication_code = null ) {
66
+ $this->client->authenticate_client( $authentication_code );
 
 
 
 
 
 
 
 
 
 
67
  }
68
 
69
  /**
75
  * @return array
76
  */
77
  public function get_profiles() {
78
+ $accounts = $this->format_profile_call(
79
+ $this->do_request( 'https://www.googleapis.com/analytics/v3/management/accountSummaries' )
80
+ );
 
 
 
 
 
 
81
 
82
+ if ( is_array( $accounts ) ) {
83
+ $this->save_profile_response( $accounts );
84
 
85
+ return $accounts;
86
  }
87
 
88
+ return array();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  }
90
 
91
  /**
92
+ * Doing request to Google Analytics
93
  *
94
+ * This method will do a request to google and get the response code and body from content
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  *
96
+ * @param string $target_request_url
97
  *
98
+ * @return array|null
99
  */
100
+ public function do_request( $target_request_url ) {
 
 
101
 
102
+ $response = $this->client->do_request( $target_request_url );
 
103
 
104
+ if ( ! empty( $response ) ) {
105
+ return array(
106
+ 'response' => array( 'code' => $this->client->get_http_response_code() ),
107
+ 'body' => json_decode( $response->getResponseBody(), true ),
108
+ );
 
109
  }
110
 
 
111
  }
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  /**
115
+ * Check if client has a refresh token
116
+ * @return bool
 
117
  */
118
+ public function has_refresh_token() {
119
+ return ($this->client->get_refresh_token() != '');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  }
121
 
122
  /**
123
+ * Getting the options bases on $this->option_name from the database
 
 
124
  *
125
+ * @return mixed
126
  */
127
+ public function get_options() {
128
+ return get_option( $this->option_name );
 
 
 
129
  }
130
 
131
  /**
132
+ * Updating the options based on $this->option_name and the internal property $this->options
 
 
 
 
 
 
 
133
  */
134
+ protected function update_options() {
135
+ update_option( $this->option_name, $this->options );
 
 
 
 
 
 
 
 
 
 
136
  }
137
 
138
  /**
139
+ * Setting the client
 
 
 
 
 
 
140
  *
141
+ * The filter is a hook to override the configuration/
142
  */
143
+ protected function set_client() {
144
+ $config = array(
145
+ 'application_name' => 'Google Analytics by Yoast',
146
+ 'client_id' => '346753076522-21smrc6aq0hq8oij8001s57dfoo8igf5.apps.googleusercontent.com',
147
+ 'client_secret' => '5oWaEGFgp-bSrY6vWBmdPfIF',
148
  );
149
 
150
+ $config = apply_filters( 'yst-ga-filter-ga-config', $config );
151
 
152
+ $this->client = new Yoast_Google_Analytics_Client( $config );
153
  }
154
 
155
  /**
156
  * Saving profile response in options
157
  *
158
+ * @param array $accounts
159
  */
160
+ protected function save_profile_response( $accounts ) {
 
161
  $this->options['ga_api_response_accounts'] = $accounts;
162
 
163
  $this->update_options();
164
  }
165
 
166
  /**
167
+ * Format the accounts request
168
  *
169
+ * @param $response
 
170
  *
171
+ * @return mixed
172
  */
173
+ private function format_profile_call( $response ) {
 
 
 
 
174
 
175
+ if ( isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) {
176
+ if ( ! empty( $response['body']['items'] ) && is_array( $response['body']['items'] ) ) {
177
+ $accounts = array();
178
 
179
+ foreach ( $response['body']['items'] as $item ) {
180
 
181
+ $profiles = array();
182
+ foreach ( $item['webProperties'] AS $property ) {
183
+ foreach ( $property['profiles'] AS $key => $profile ) {
184
+ $property['profiles'][$key]['name'] = $profile['name'] . ' (' . $property['id'] . ')';
185
+ $property['profiles'][$key]['ua_code'] = $property['id'];
186
+ }
187
 
188
+ $profiles = array_merge( $profiles, $property['profiles'] );
189
+ }
 
190
 
191
+ $accounts[$item['id']] = array(
192
+ 'id' => $item['id'],
193
+ 'ua_code' => $property['id'],
194
+ 'parent_name' => $item['name'],
195
+ 'profiles' => $profiles,
 
196
  );
197
 
 
 
 
 
 
198
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
+ return $accounts;
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  }
202
  }
203
 
204
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  }
206
 
207
  }
admin/dashboards/class-admin-dashboards-api-options.php CHANGED
@@ -49,7 +49,7 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Api_Options' ) ) {
49
  * Set the API options
50
  */
51
  public function set_options() {
52
- $this->options = Yoast_Google_Analytics::instance()->get_options();
53
 
54
  $this->set_access_token();
55
  }
49
  * Set the API options
50
  */
51
  public function set_options() {
52
+ $this->options = Yoast_Google_Analytics::get_instance()->get_options();
53
 
54
  $this->set_access_token();
55
  }
admin/dashboards/class-admin-dashboards-collector.php CHANGED
@@ -63,23 +63,21 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
63
  * Fetch the data from Google Analytics and store it
64
  */
65
  public function aggregate_data() {
66
- $access_tokens = $this->options->get_access_token();
67
-
68
- if ( $access_tokens != false && is_array( $access_tokens ) ) {
69
- // Access tokens are set, continue
70
 
71
  /**
72
  * Implement the metric data first
73
  */
74
  if ( is_array( $this->active_metrics ) && count( $this->active_metrics ) >= 1 ) {
75
- $this->aggregate_metrics( $access_tokens, $this->active_metrics );
76
  }
77
 
78
  /**
79
  * Now implement the dimensions that are set
80
  */
81
  if ( is_array( $this->dimensions ) && count( $this->dimensions ) >= 1 ) {
82
- $this->aggregate_dimensions( $access_tokens, $this->dimensions );
83
  }
84
 
85
  } else {
@@ -147,7 +145,7 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
147
  private function hours_between( $last_run, $now ) {
148
  $seconds = max( ( $now - $last_run ), 1 );
149
  $hours = $seconds / 3600;
150
-
151
  return floor( $hours );
152
  }
153
 
@@ -221,31 +219,29 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
221
  /**
222
  * Aggregate metrics from GA. This function should be called in the shutdown function.
223
  *
224
- * @param $access_tokens
225
  * @param $metrics
226
  */
227
- private function aggregate_metrics( $access_tokens, $metrics ) {
228
  foreach ( $metrics as $metric ) {
229
- $this->execute_call( $access_tokens, $metric, date( 'Y-m-d', strtotime( '-6 weeks' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ) );
230
  }
231
  }
232
 
233
  /**
234
  * Aggregate dimensions from GA. This function should be called in the shutdown function.
235
  *
236
- * @param $access_tokens
237
  * @param $dimensions
238
  */
239
- private function aggregate_dimensions( $access_tokens, $dimensions ) {
240
  foreach ( $dimensions as $dimension ) {
241
  if ( ( isset( $dimension['id'] ) || isset( $dimension['dimension'] ) ) && isset( $dimension['metric'] ) ) {
242
  if ( isset( $dimension['id'] ) ) {
243
- $this->execute_call( $access_tokens, $dimension['metric'], date( 'Y-m-d', strtotime( '-1 month' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ), 'ga:dimension' . $dimension['id'] );
244
  } elseif ( isset( $dimension['dimension'] ) ) {
245
  if ( isset( $dimension['storage_name'] ) ) {
246
- $this->execute_call( $access_tokens, $dimension['metric'], date( 'Y-m-d', strtotime( '-1 month' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ), 'ga:' . $dimension['dimension'], $dimension['storage_name'] );
247
  } else {
248
- $this->execute_call( $access_tokens, $dimension['metric'], date( 'Y-m-d', strtotime( '-1 month' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ), 'ga:' . $dimension['dimension'] );
249
  }
250
  }
251
  }
@@ -255,7 +251,6 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
255
  /**
256
  * Execute an API call to Google Analytics and store the data in the dashboards data class
257
  *
258
- * @param $access_tokens
259
  * @param $metric
260
  * @param $start_date 2014-10-16
261
  * @param $end_date 2014-11-20
@@ -264,20 +259,18 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
264
  *
265
  * @return bool
266
  */
267
- private function execute_call( $access_tokens, $metric, $start_date, $end_date, $dimensions = 'ga:date', $storage_name = 'auto' ) {
268
  $dimensions = $this->prepare_dimensions( $dimensions, $metric );
269
  $params = $this->build_params_for_call( $start_date, $end_date, $dimensions, $metric );
270
  $storage_type = $this->get_storage_type( $dimensions );
271
 
272
- $response = Yoast_Googleanalytics_Reporting::instance()->do_api_request(
273
- 'https://www.googleapis.com/analytics/v3/data/ga?' . $params,
274
- 'https://www.googleapis.com/analytics/v3/data/ga',
275
- $access_tokens['oauth_token'],
276
- $access_tokens['oauth_token_secret'],
277
- $storage_type,
278
- $start_date,
279
- $end_date
280
- );
281
 
282
  if ( strpos( 'ga:date', $dimensions ) !== false ) {
283
  return $this->handle_response( $response, $metric, $dimensions, $start_date, $end_date, 'datelist', $storage_name );
@@ -386,7 +379,7 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
386
  * @return bool
387
  */
388
  private function handle_response( $response, $metric, $dimensions, $start_date, $end_date, $store_as = 'table', $storage_name = 'auto' ) {
389
- if ( is_array( $response ) && isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) {
390
  // Success, store this data
391
  $filter_metrics = $this->get_filter_metrics();
392
  $extracted = str_replace( 'ga:', '', str_replace( 'ga:date,', '', $dimensions ) );
@@ -414,10 +407,10 @@ if ( ! class_exists( 'Yoast_GA_Dashboards_Collector' ) ) {
414
  set_transient( 'yst_ga_last_wp_run', date( 'Y-m-d' ), 48 * HOUR_IN_SECONDS );
415
  }
416
 
417
- return Yoast_GA_Dashboards_Data::set( $name, $response['body'], strtotime( $start_date ), strtotime( $end_date ), $store_as );
418
  } else {
419
  // Failure on API call try to log it
420
- $this->log_error( print_r( $response['body_raw'], true ) );
421
 
422
  return false;
423
  }
63
  * Fetch the data from Google Analytics and store it
64
  */
65
  public function aggregate_data() {
66
+ if ( is_numeric( $this->ga_profile_id ) ) {
67
+ // ProfileID is set
 
 
68
 
69
  /**
70
  * Implement the metric data first
71
  */
72
  if ( is_array( $this->active_metrics ) && count( $this->active_metrics ) >= 1 ) {
73
+ $this->aggregate_metrics( $this->active_metrics );
74
  }
75
 
76
  /**
77
  * Now implement the dimensions that are set
78
  */
79
  if ( is_array( $this->dimensions ) && count( $this->dimensions ) >= 1 ) {
80
+ $this->aggregate_dimensions( $this->dimensions );
81
  }
82
 
83
  } else {
145
  private function hours_between( $last_run, $now ) {
146
  $seconds = max( ( $now - $last_run ), 1 );
147
  $hours = $seconds / 3600;
148
+
149
  return floor( $hours );
150
  }
151
 
219
  /**
220
  * Aggregate metrics from GA. This function should be called in the shutdown function.
221
  *
 
222
  * @param $metrics
223
  */
224
+ private function aggregate_metrics( $metrics ) {
225
  foreach ( $metrics as $metric ) {
226
+ $this->execute_call( $metric, date( 'Y-m-d', strtotime( '-6 weeks' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ) );
227
  }
228
  }
229
 
230
  /**
231
  * Aggregate dimensions from GA. This function should be called in the shutdown function.
232
  *
 
233
  * @param $dimensions
234
  */
235
+ private function aggregate_dimensions( $dimensions ) {
236
  foreach ( $dimensions as $dimension ) {
237
  if ( ( isset( $dimension['id'] ) || isset( $dimension['dimension'] ) ) && isset( $dimension['metric'] ) ) {
238
  if ( isset( $dimension['id'] ) ) {
239
+ $this->execute_call( $dimension['metric'], date( 'Y-m-d', strtotime( '-1 month' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ), 'ga:dimension' . $dimension['id'] );
240
  } elseif ( isset( $dimension['dimension'] ) ) {
241
  if ( isset( $dimension['storage_name'] ) ) {
242
+ $this->execute_call( $dimension['metric'], date( 'Y-m-d', strtotime( '-1 month' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ), 'ga:' . $dimension['dimension'], $dimension['storage_name'] );
243
  } else {
244
+ $this->execute_call( $dimension['metric'], date( 'Y-m-d', strtotime( '-1 month' ) ), date( 'Y-m-d', strtotime( 'yesterday' ) ), 'ga:' . $dimension['dimension'] );
245
  }
246
  }
247
  }
251
  /**
252
  * Execute an API call to Google Analytics and store the data in the dashboards data class
253
  *
 
254
  * @param $metric
255
  * @param $start_date 2014-10-16
256
  * @param $end_date 2014-11-20
259
  *
260
  * @return bool
261
  */
262
+ private function execute_call( $metric, $start_date, $end_date, $dimensions = 'ga:date', $storage_name = 'auto' ) {
263
  $dimensions = $this->prepare_dimensions( $dimensions, $metric );
264
  $params = $this->build_params_for_call( $start_date, $end_date, $dimensions, $metric );
265
  $storage_type = $this->get_storage_type( $dimensions );
266
 
267
+ $response = Yoast_Google_Analytics::get_instance()->do_request( 'https://www.googleapis.com/analytics/v3/data/ga?' . $params );
268
+
269
+ if ( isset( $response['response']['code'] ) && $response['response']['code'] == 200 ) {
270
+ $response = Yoast_Googleanalytics_Reporting::get_instance()->parse_response( $response, $storage_type, $start_date, $end_date );
271
+ } else {
272
+ return false;
273
+ }
 
 
274
 
275
  if ( strpos( 'ga:date', $dimensions ) !== false ) {
276
  return $this->handle_response( $response, $metric, $dimensions, $start_date, $end_date, 'datelist', $storage_name );
379
  * @return bool
380
  */
381
  private function handle_response( $response, $metric, $dimensions, $start_date, $end_date, $store_as = 'table', $storage_name = 'auto' ) {
382
+ if ( is_array( $response ) ) {
383
  // Success, store this data
384
  $filter_metrics = $this->get_filter_metrics();
385
  $extracted = str_replace( 'ga:', '', str_replace( 'ga:date,', '', $dimensions ) );
407
  set_transient( 'yst_ga_last_wp_run', date( 'Y-m-d' ), 48 * HOUR_IN_SECONDS );
408
  }
409
 
410
+ return Yoast_GA_Dashboards_Data::set( $name, $response, strtotime( $start_date ), strtotime( $end_date ), $store_as );
411
  } else {
412
  // Failure on API call try to log it
413
+ $this->log_error( print_r( $response, true ) );
414
 
415
  return false;
416
  }
admin/pages/dashboard.php CHANGED
@@ -34,20 +34,32 @@ echo $yoast_ga_admin->content_head();
34
  <?php
35
  if ( $tracking_code !== '' ) {
36
  if ( empty( $options['analytics_profile'] ) ) {
 
37
  echo sprintf(
38
  __( 'We need you to authenticate with Google Analytics to use this functionality. If you set your UA-code manually, this won\'t work. You can %sauthenticate your Google Analytics profile here%s to enable dashboards.', 'google-analytics-for-wordpress' ),
39
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
40
  '</a>'
41
  );
 
 
 
 
 
 
 
 
 
42
  } else {
43
  Yoast_GA_Dashboards_Display::get_instance()->display( 'general' );
44
  }
45
  } else {
 
46
  echo sprintf(
47
  __( 'You have not yet finished setting up Google Analytics for Wordpress by Yoast. Please %sadd your Analytics profile here%s to enable tracking.', 'google-analytics-for-wordpress' ),
48
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
49
  '</a>'
50
  );
 
51
  }
52
  ?>
53
  </div>
@@ -55,13 +67,24 @@ echo $yoast_ga_admin->content_head();
55
 
56
  <div id="dimensions" class="wpseotab gatab">
57
  <?php
 
58
  if ( $tracking_code !== '' ) {
59
  if ( empty( $options['analytics_profile'] ) ) {
 
60
  echo sprintf(
61
  __( 'We need you to authenticate with Google Analytics to use this functionality. If you set your UA-code manually, this won\'t work. You can %sauthenticate your Google Analytics profile here%s to enable dashboards.', 'google-analytics-for-wordpress' ),
62
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
63
  '</a>'
64
  );
 
 
 
 
 
 
 
 
 
65
  } else {
66
  ?>
67
  <div class="ga-form ga-form-input">
@@ -73,11 +96,13 @@ echo $yoast_ga_admin->content_head();
73
  Yoast_GA_Dashboards_Display::get_instance()->display( 'dimensions' );
74
  }
75
  } else {
 
76
  echo sprintf(
77
  __( 'You have not yet finished setting up Google Analytics for Wordpress by Yoast. Please %sadd your Analytics profile here%s to enable tracking.', 'google-analytics-for-wordpress' ),
78
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
79
  '</a>'
80
  );
 
81
  }
82
  ?>
83
  </div>
34
  <?php
35
  if ( $tracking_code !== '' ) {
36
  if ( empty( $options['analytics_profile'] ) ) {
37
+ echo '<div class="ga-promote"><p>';
38
  echo sprintf(
39
  __( 'We need you to authenticate with Google Analytics to use this functionality. If you set your UA-code manually, this won\'t work. You can %sauthenticate your Google Analytics profile here%s to enable dashboards.', 'google-analytics-for-wordpress' ),
40
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
41
  '</a>'
42
  );
43
+ echo '</p></div>';
44
+ } else if( ! Yoast_Google_Analytics::get_instance()->has_refresh_token() ) {
45
+ echo '<div class="ga-promote"><p>';
46
+ echo sprintf(
47
+ __( 'Because we\'ve switched to a newer version of the Google Analytics API, you\'ll need to re-authenticate with Google Analytics. We\'re sorry for the inconvenience. You can %sre-authenticate your Google Analytics profile here%s.', 'google-analytics-for-wordpress' ),
48
+ '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
49
+ '</a>'
50
+ );
51
+ echo '</p></div>';
52
  } else {
53
  Yoast_GA_Dashboards_Display::get_instance()->display( 'general' );
54
  }
55
  } else {
56
+ echo '<div class="ga-promote"><p>';
57
  echo sprintf(
58
  __( 'You have not yet finished setting up Google Analytics for Wordpress by Yoast. Please %sadd your Analytics profile here%s to enable tracking.', 'google-analytics-for-wordpress' ),
59
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
60
  '</a>'
61
  );
62
+ echo '</p></div>';
63
  }
64
  ?>
65
  </div>
67
 
68
  <div id="dimensions" class="wpseotab gatab">
69
  <?php
70
+
71
  if ( $tracking_code !== '' ) {
72
  if ( empty( $options['analytics_profile'] ) ) {
73
+ echo '<div class="ga-promote"><p>';
74
  echo sprintf(
75
  __( 'We need you to authenticate with Google Analytics to use this functionality. If you set your UA-code manually, this won\'t work. You can %sauthenticate your Google Analytics profile here%s to enable dashboards.', 'google-analytics-for-wordpress' ),
76
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
77
  '</a>'
78
  );
79
+ echo '</p></div>';
80
+ } else if( ! Yoast_Google_Analytics::get_instance()->has_refresh_token() ) {
81
+ echo '<div class="ga-promote"><p>';
82
+ echo sprintf(
83
+ __( 'Because we\'ve switched to a newer version of the Google Analytics API, you\'ll need to re-authenticate with Google Analytics. We\'re sorry for the inconvenience. You can %sre-authenticate your Google Analytics profile here%s.', 'google-analytics-for-wordpress' ),
84
+ '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
85
+ '</a>'
86
+ );
87
+ echo '</p></div>';
88
  } else {
89
  ?>
90
  <div class="ga-form ga-form-input">
96
  Yoast_GA_Dashboards_Display::get_instance()->display( 'dimensions' );
97
  }
98
  } else {
99
+ echo '<div class="ga-promote"><p>';
100
  echo sprintf(
101
  __( 'You have not yet finished setting up Google Analytics for Wordpress by Yoast. Please %sadd your Analytics profile here%s to enable tracking.', 'google-analytics-for-wordpress' ),
102
  '<a href=" ' . admin_url( 'admin.php?page=yst_ga_settings#top#general' ) . '">',
103
  '</a>'
104
  );
105
+ echo '</p></div>';
106
  }
107
  ?>
108
  </div>
admin/pages/settings.php CHANGED
@@ -19,7 +19,7 @@ settings_errors( 'yoast_google_analytics' );
19
  </h2>
20
 
21
  <?php
22
- echo $yoast_ga_admin->create_form( 'settings' );
23
  ?>
24
  <input type="hidden" name="return_tab" id="return_tab" value="general" />
25
  <div class="tabwrapper">
@@ -28,7 +28,7 @@ echo $yoast_ga_admin->create_form( 'settings' );
28
  echo '<h2>' . __( 'General settings', 'google-analytics-for-wordpress' ) . '</h2>';
29
  echo '<div id="ga-promote">';
30
 
31
- $profiles = $yoast_ga_admin->parse_optgroups( $yoast_ga_admin->get_profiles() );
32
  $ga_url = $_SERVER['PHP_SELF'];
33
  if ( isset( $_GET['page'] ) ) {
34
  $ga_url .= '?page=' . $_GET['page'];
@@ -39,61 +39,68 @@ echo $yoast_ga_admin->create_form( 'settings' );
39
  if ( count( $profiles ) == 0 ) {
40
  echo '<div class="ga-form ga-form-input">';
41
  echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />' . __( 'Google profile', 'google-analytics-for-wordpress' ) . ':</label>';
42
- echo '<a id="yst_ga_authenticate" class="button" href="' . $ga_url . '">' . __( 'Authenticate with your Google account', 'google-analytics-for-wordpress' ) . '</a>';
43
  echo '</div>';
44
  echo '<div class="ga-form ga-form-input">';
45
  echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />' . __( 'Current UA-profile', 'google-analytics-for-wordpress' ) . '</label>';
46
  echo $yoast_ga_admin->get_tracking_code();
47
  echo '</div>';
48
  } else {
49
- echo $yoast_ga_admin->select( __('Analytics profile', 'google-analytics-for-wordpress' ), 'analytics_profile', $profiles, null, false, __( 'Select a profile', 'google-analytics-for-wordpress' ) );
50
 
51
  echo '<div class="ga-form ga-form-input">';
52
  echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />&nbsp;</label>';
53
- echo '<a id="yst_ga_authenticate" class="button" href="' . $ga_url . '">' . __( 'Re-authenticate with your Google account', 'google-analytics-for-wordpress' ) . '</a>';
54
  echo '</div>';
55
  }
 
 
 
 
 
 
 
56
  echo '</div>';
57
 
58
  echo '<label class="ga-form ga-form-checkbox-label ga-form-label-left">';
59
- echo $yoast_ga_admin->input( 'checkbox', null, 'manual_ua_code', __( 'Manually enter your UA code', 'google-analytics-for-wordpress' ) );
60
  echo '</label>';
61
  echo '<div id="enter_ua">';
62
- echo $yoast_ga_admin->input( 'text', null, 'manual_ua_code_field' );
63
  echo '<p><strong>' . __('Warning: If you use a manual UA code, you won\'t be able to use the dashboards.', 'google-analytics-for-wordpress') . '</strong></p>';
64
  echo '</div>';
65
  echo '<div class="clear"></div></div>';
66
  ?>
67
  <div class="clear"><br /></div>
68
  <?php
69
- echo $yoast_ga_admin->input( 'checkbox', __( 'Track outbound click & downloads', 'google-analytics-for-wordpress' ), 'track_outbound', null, __( 'Clicks &amp; downloads will be tracked as events, you can find these under Content &raquo; Event Tracking in your Google Analytics reports.', 'google-analytics-for-wordpress' ) );
70
- echo $yoast_ga_admin->input( 'checkbox', __( 'Allow tracking of anonymous data', 'google-analytics-for-wordpress' ), 'anonymous_data', null, __( 'By allowing us to track anonymous data we can better help you, because we know with which WordPress configurations, themes and plugins we should test. No personal data will be submitted.', 'google-analytics-for-wordpress' ) );
71
- echo $yoast_ga_admin->input( 'checkbox', __( 'Anonymize IP\'s', 'google-analytics-for-wordpress' ), 'anonymize_ips', null, sprintf( __( 'This adds <code>%1$s _anonymizeIp%2$s</code>, telling Google Analytics to anonymize the information sent by the tracker objects by removing the last octet of the IP address prior to its storage.', 'google-analytics-for-wordpress' ), '<a href="http://code.google.com/apis/analytics/docs/gaJS/gaJSApi_gat.html#_gat._anonymizeIp" target="_blank">', '</a>' ) );
72
- echo $yoast_ga_admin->select( 'Ignore users', 'ignore_users', $yoast_ga_admin->get_userroles(), __( 'Users of the role you select will be ignored, so if you select Editor, all Editors will be ignored.', 'google-analytics-for-wordpress' ), true );
73
  ?>
74
  </div>
75
  <div id="universal" class="gatab">
76
  <?php
77
  echo '<h2>' . __( 'Universal settings', 'google-analytics-for-wordpress' ) . '</h2>';
78
- echo $yoast_ga_admin->input( 'checkbox', __( 'Enable Universal tracking', 'google-analytics-for-wordpress' ), 'enable_universal', null, sprintf( __( 'First enable Universal tracking in your Google Analytics account. How to do that, please read %1$sthis guide%2$s to learn how to do that.', 'google-analytics-for-wordpress' ), '<a href="http://kb.yoast.com/article/125-universal-analytics#utm_medium=kb-link&utm_source=gawp-config&utm_campaign=wpgaplugin" target="_blank">', '</a>' ) );
79
- echo $yoast_ga_admin->input( 'checkbox', __( 'Enable Demographics and Interest Reports', 'google-analytics-for-wordpress' ), 'demographics', null, sprintf( __( 'You have to enable the Demographics in Google Analytics before you can see the tracking data. We have a doc in our %1$sknowlegde base%2$s about this feature.', 'google-analytics-for-wordpress' ), '<a href="http://kb.yoast.com/article/154-enable-demographics-and-interests-report-in-google-analytics/#utm_medium=kb-link&utm_source=gawp-config&utm_campaign=wpgaplugin" target="_blank">', '</a>' ) );
80
  ?>
81
  </div>
82
  <div id="advanced" class="gatab">
83
  <?php
84
  echo '<h2>' . __( 'Advanced settings', 'google-analytics-for-wordpress' ) . '</h2>';
85
- echo $yoast_ga_admin->select( __( 'Track downloads as', 'google-analytics-for-wordpress' ), 'track_download_as', $yoast_ga_admin->track_download_types(), __( 'Not recommended, as this would skew your statistics, but it does make it possible to track downloads as goals.', 'google-analytics-for-wordpress' ) );
86
- echo $yoast_ga_admin->input( 'text', __( 'Extensions of files to track as downloads', 'google-analytics-for-wordpress' ), 'extensions_of_files', null, 'Please separate extensions using commas' );
87
- echo $yoast_ga_admin->select( __( 'Track full URL of outbound clicks or just the domain', 'google-analytics-for-wordpress' ), 'track_full_url', $yoast_ga_admin->get_track_full_url() );
88
- echo $yoast_ga_admin->input( 'text', __( 'Subdomain tracking', 'google-analytics-for-wordpress' ), 'subdomain_tracking', null, __( 'This allows you to set the domain that\'s set by <code>setDomainName</code> for tracking subdomains, if empty this will not be set.', 'google-analytics-for-wordpress' ) );
89
 
90
- echo $yoast_ga_admin->input( 'text', __( 'Set path for internal links to track as outbound links', 'google-analytics-for-wordpress' ), 'track_internal_as_outbound', null, 'If you want to track all internal links that begin with <code>/out/</code>, enter <code>/out/</code> in the box above. If you have multiple prefixes you can separate them with comma\'s: <code>/out/,/recommends/</code>' );
91
- echo $yoast_ga_admin->input( 'text', __( 'Label for those links', 'google-analytics-for-wordpress' ), 'track_internal_as_label', null, 'The label to use for these links, this will be added to where the click came from, so if the label is "aff", the label for a click from the content of an article becomes "outbound-article-aff".' );
92
 
93
- echo $yoast_ga_admin->input( 'checkbox', __( 'Tag links in RSS feed with campaign variables', 'google-analytics-for-wordpress' ), 'tag_links_in_rss', null, __( 'Do not use this feature if you use FeedBurner, as FeedBurner can do this automatically, and better than this plugin can. Check <a href="https://support.google.com/feedburner/answer/165769?hl=en&amp;ref_topic=13075" target="_blank">this help page</a> for info on how to enable this feature in FeedBurner.', 'google-analytics-for-wordpress' ) );
94
- echo $yoast_ga_admin->input( 'checkbox', __( 'Allow anchor', 'google-analytics-for-wordpress' ), 'allow_anchor', null, sprintf( __( 'This adds a <code>%1$s_setAllowAnchor%2$s</code> call to your tracking code, and makes RSS link tagging use a # as well.', 'google-analytics-for-wordpress' ), '<a href="http://code.google.com/apis/analytics/docs/gaJSApiCampaignTracking.html#_gat.GA_Tracker_._setAllowAnchor" target="_blank">', '</a>' ) );
95
- echo $yoast_ga_admin->input( 'checkbox', __( 'Add <code>_setAllowLinker</code>', 'google-analytics-for-wordpress' ), 'add_allow_linker', null, sprintf( __( 'This adds a <code>%1$s_setAllowLinker%2$s</code> call to your tracking code, allowing you to use <code>_link</code> and related functions.', 'google-analytics-for-wordpress' ), '<a href="http://code.google.com/apis/analytics/docs/gaJS/gaJSApiDomainDirectory.html#_gat.GA_Tracker_._setAllowLinker" target="_blank">', '</a>' ) );
96
- echo $yoast_ga_admin->textarea( 'Custom code', 'custom_code', __( 'Not for the average user: this allows you to add a line of code, to be added before the <code>trackPageview</code> call.', 'google-analytics-for-wordpress' ) );
97
 
98
  do_action( 'yst_ga_advanced-tab' );
99
  ?>
@@ -113,12 +120,12 @@ echo $yoast_ga_admin->create_form( 'settings' );
113
  echo '<p class="ga-topdescription">' . __( 'If you want to confirm that tracking on your blog is working as it should, enable this option and check the console of your browser. Be absolutely sure to disable debugging afterwards, as it is slower than normal tracking.', 'google-analytics-for-wordpress' ) . '</p>';
114
  echo '<p class="ga-topdescription"><strong>' . __( 'Note', 'google-analytics-for-wordpress' ) . ':</strong> ' . __( 'the debugging scripts is only loaded for admins.', 'google-analytics-for-wordpress' ) . '</p>';
115
  echo '</div>';
116
- echo $yoast_ga_admin->input( 'checkbox', __( 'Enable debug mode', 'google-analytics-for-wordpress' ), 'debug_mode' );
117
  ?>
118
  </div>
119
  </div>
120
  <?php
121
- echo $yoast_ga_admin->end_form( 'Save changes', 'settings' );
122
  echo $yoast_ga_admin->content_footer();
123
  ?>
124
  <script type="text/javascript">
19
  </h2>
20
 
21
  <?php
22
+ echo Yoast_GA_Admin_Form::create_form( 'settings' );
23
  ?>
24
  <input type="hidden" name="return_tab" id="return_tab" value="general" />
25
  <div class="tabwrapper">
28
  echo '<h2>' . __( 'General settings', 'google-analytics-for-wordpress' ) . '</h2>';
29
  echo '<div id="ga-promote">';
30
 
31
+ $profiles = Yoast_GA_Admin_Form::parse_optgroups( $yoast_ga_admin->get_profiles() );
32
  $ga_url = $_SERVER['PHP_SELF'];
33
  if ( isset( $_GET['page'] ) ) {
34
  $ga_url .= '?page=' . $_GET['page'];
39
  if ( count( $profiles ) == 0 ) {
40
  echo '<div class="ga-form ga-form-input">';
41
  echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />' . __( 'Google profile', 'google-analytics-for-wordpress' ) . ':</label>';
42
+ echo '<a id="yst_ga_authenticate" class="button" href="' . $ga_url . '" target="_blank">' . __( 'Authenticate with your Google account', 'google-analytics-for-wordpress' ) . '</a>';
43
  echo '</div>';
44
  echo '<div class="ga-form ga-form-input">';
45
  echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />' . __( 'Current UA-profile', 'google-analytics-for-wordpress' ) . '</label>';
46
  echo $yoast_ga_admin->get_tracking_code();
47
  echo '</div>';
48
  } else {
49
+ echo Yoast_GA_Admin_Form::select( __('Analytics profile', 'google-analytics-for-wordpress' ), 'analytics_profile', $profiles, null, false, __( 'Select a profile', 'google-analytics-for-wordpress' ) );
50
 
51
  echo '<div class="ga-form ga-form-input">';
52
  echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />&nbsp;</label>';
53
+ echo '<a id="yst_ga_authenticate" class="button" href="' . $ga_url . '" target="_blank">' . __( 'Re-authenticate with your Google account', 'google-analytics-for-wordpress' ) . '</a>';
54
  echo '</div>';
55
  }
56
+
57
+ echo '<div id="oauth_code" class="ga-form ga-form-input">';
58
+ echo '<label class="ga-form ga-form-text-label ga-form-label-left" id="yoast-ga-form-label-text-ga-authwithgoogle" />' . __( 'Paste your Google code here', 'google-analytics-for-wordpress' ) . ':</label>';
59
+ echo Yoast_GA_Admin_Form::input( 'text', null, 'google_auth_code', null, __('Please leave this field empty if everything is all right for you', 'google-analytics-for-wordpress') );
60
+ echo '</label>';
61
+
62
+ echo '</div>';
63
  echo '</div>';
64
 
65
  echo '<label class="ga-form ga-form-checkbox-label ga-form-label-left">';
66
+ echo Yoast_GA_Admin_Form::input( 'checkbox', null, 'manual_ua_code', __( 'Manually enter your UA code', 'google-analytics-for-wordpress' ) );
67
  echo '</label>';
68
  echo '<div id="enter_ua">';
69
+ echo Yoast_GA_Admin_Form::input( 'text', null, 'manual_ua_code_field' );
70
  echo '<p><strong>' . __('Warning: If you use a manual UA code, you won\'t be able to use the dashboards.', 'google-analytics-for-wordpress') . '</strong></p>';
71
  echo '</div>';
72
  echo '<div class="clear"></div></div>';
73
  ?>
74
  <div class="clear"><br /></div>
75
  <?php
76
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Track outbound click & downloads', 'google-analytics-for-wordpress' ), 'track_outbound', null, __( 'Clicks &amp; downloads will be tracked as events, you can find these under Content &raquo; Event Tracking in your Google Analytics reports.', 'google-analytics-for-wordpress' ) );
77
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Allow tracking of anonymous data', 'google-analytics-for-wordpress' ), 'anonymous_data', null, __( 'By allowing us to track anonymous data we can better help you, because we know with which WordPress configurations, themes and plugins we should test. No personal data will be submitted.', 'google-analytics-for-wordpress' ) );
78
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Anonymize IP\'s', 'google-analytics-for-wordpress' ), 'anonymize_ips', null, sprintf( __( 'This adds <code>%1$s _anonymizeIp%2$s</code>, telling Google Analytics to anonymize the information sent by the tracker objects by removing the last octet of the IP address prior to its storage.', 'google-analytics-for-wordpress' ), '<a href="http://code.google.com/apis/analytics/docs/gaJS/gaJSApi_gat.html#_gat._anonymizeIp" target="_blank">', '</a>' ) );
79
+ echo Yoast_GA_Admin_Form::select( 'Ignore users', 'ignore_users', $yoast_ga_admin->get_userroles(), __( 'Users of the role you select will be ignored, so if you select Editor, all Editors will be ignored.', 'google-analytics-for-wordpress' ), true );
80
  ?>
81
  </div>
82
  <div id="universal" class="gatab">
83
  <?php
84
  echo '<h2>' . __( 'Universal settings', 'google-analytics-for-wordpress' ) . '</h2>';
85
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Enable Universal tracking', 'google-analytics-for-wordpress' ), 'enable_universal', null, sprintf( __( 'First enable Universal tracking in your Google Analytics account. How to do that, please read %1$sthis guide%2$s to learn how to do that.', 'google-analytics-for-wordpress' ), '<a href="http://kb.yoast.com/article/125-universal-analytics#utm_medium=kb-link&utm_source=gawp-config&utm_campaign=wpgaplugin" target="_blank">', '</a>' ) );
86
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Enable Demographics and Interest Reports', 'google-analytics-for-wordpress' ), 'demographics', null, sprintf( __( 'You have to enable the Demographics in Google Analytics before you can see the tracking data. We have a doc in our %1$sknowlegde base%2$s about this feature.', 'google-analytics-for-wordpress' ), '<a href="http://kb.yoast.com/article/154-enable-demographics-and-interests-report-in-google-analytics/#utm_medium=kb-link&utm_source=gawp-config&utm_campaign=wpgaplugin" target="_blank">', '</a>' ) );
87
  ?>
88
  </div>
89
  <div id="advanced" class="gatab">
90
  <?php
91
  echo '<h2>' . __( 'Advanced settings', 'google-analytics-for-wordpress' ) . '</h2>';
92
+ echo Yoast_GA_Admin_Form::select( __( 'Track downloads as', 'google-analytics-for-wordpress' ), 'track_download_as', $yoast_ga_admin->track_download_types(), __( 'Not recommended, as this would skew your statistics, but it does make it possible to track downloads as goals.', 'google-analytics-for-wordpress' ) );
93
+ echo Yoast_GA_Admin_Form::input( 'text', __( 'Extensions of files to track as downloads', 'google-analytics-for-wordpress' ), 'extensions_of_files', null, 'Please separate extensions using commas' );
94
+ echo Yoast_GA_Admin_Form::select( __( 'Track full URL of outbound clicks or just the domain', 'google-analytics-for-wordpress' ), 'track_full_url', $yoast_ga_admin->get_track_full_url() );
95
+ echo Yoast_GA_Admin_Form::input( 'text', __( 'Subdomain tracking', 'google-analytics-for-wordpress' ), 'subdomain_tracking', null, __( 'This allows you to set the domain that\'s set by <code>setDomainName</code> for tracking subdomains, if empty this will not be set.', 'google-analytics-for-wordpress' ) );
96
 
97
+ echo Yoast_GA_Admin_Form::input( 'text', __( 'Set path for internal links to track as outbound links', 'google-analytics-for-wordpress' ), 'track_internal_as_outbound', null, 'If you want to track all internal links that begin with <code>/out/</code>, enter <code>/out/</code> in the box above. If you have multiple prefixes you can separate them with comma\'s: <code>/out/,/recommends/</code>' );
98
+ echo Yoast_GA_Admin_Form::input( 'text', __( 'Label for those links', 'google-analytics-for-wordpress' ), 'track_internal_as_label', null, 'The label to use for these links, this will be added to where the click came from, so if the label is "aff", the label for a click from the content of an article becomes "outbound-article-aff".' );
99
 
100
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Tag links in RSS feed with campaign variables', 'google-analytics-for-wordpress' ), 'tag_links_in_rss', null, __( 'Do not use this feature if you use FeedBurner, as FeedBurner can do this automatically, and better than this plugin can. Check <a href="https://support.google.com/feedburner/answer/165769?hl=en&amp;ref_topic=13075" target="_blank">this help page</a> for info on how to enable this feature in FeedBurner.', 'google-analytics-for-wordpress' ) );
101
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Allow anchor', 'google-analytics-for-wordpress' ), 'allow_anchor', null, sprintf( __( 'This adds a <code>%1$s_setAllowAnchor%2$s</code> call to your tracking code, and makes RSS link tagging use a # as well.', 'google-analytics-for-wordpress' ), '<a href="http://code.google.com/apis/analytics/docs/gaJSApiCampaignTracking.html#_gat.GA_Tracker_._setAllowAnchor" target="_blank">', '</a>' ) );
102
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Add <code>_setAllowLinker</code>', 'google-analytics-for-wordpress' ), 'add_allow_linker', null, sprintf( __( 'This adds a <code>%1$s_setAllowLinker%2$s</code> call to your tracking code, allowing you to use <code>_link</code> and related functions.', 'google-analytics-for-wordpress' ), '<a href="http://code.google.com/apis/analytics/docs/gaJS/gaJSApiDomainDirectory.html#_gat.GA_Tracker_._setAllowLinker" target="_blank">', '</a>' ) );
103
+ echo Yoast_GA_Admin_Form::textarea( 'Custom code', 'custom_code', __( 'Not for the average user: this allows you to add a line of code, to be added before the <code>trackPageview</code> call.', 'google-analytics-for-wordpress' ) );
104
 
105
  do_action( 'yst_ga_advanced-tab' );
106
  ?>
120
  echo '<p class="ga-topdescription">' . __( 'If you want to confirm that tracking on your blog is working as it should, enable this option and check the console of your browser. Be absolutely sure to disable debugging afterwards, as it is slower than normal tracking.', 'google-analytics-for-wordpress' ) . '</p>';
121
  echo '<p class="ga-topdescription"><strong>' . __( 'Note', 'google-analytics-for-wordpress' ) . ':</strong> ' . __( 'the debugging scripts is only loaded for admins.', 'google-analytics-for-wordpress' ) . '</p>';
122
  echo '</div>';
123
+ echo Yoast_GA_Admin_Form::input( 'checkbox', __( 'Enable debug mode', 'google-analytics-for-wordpress' ), 'debug_mode' );
124
  ?>
125
  </div>
126
  </div>
127
  <?php
128
+ echo Yoast_GA_Admin_Form::end_form( 'Save changes', 'settings' );
129
  echo $yoast_ga_admin->content_footer();
130
  ?>
131
  <script type="text/javascript">
admin/wp-gdata/index.php DELETED
@@ -1,2 +0,0 @@
1
- <?php
2
- //Nothing to see here
 
 
admin/wp-gdata/wp-gdata.php DELETED
@@ -1,108 +0,0 @@
1
- <?php
2
- /**
3
- * WP_GData - WordPress Google Data API Library
4
- *
5
- * @author Pete Mall
6
- */
7
-
8
- class WP_GData {
9
- /* Contains the last HTTP status code returned. */
10
- public $http_code;
11
-
12
- const request_token_url = 'https://www.google.com/accounts/OAuthGetRequestToken';
13
- const authorize_url = 'https://www.google.com/accounts/OAuthAuthorizeToken';
14
- const access_token_url = 'https://www.google.com/accounts/OAuthGetAccessToken';
15
-
16
- function __construct( $parameters = array(), $oauth_token = null, $oauth_token_secret = null ) {
17
- $this->parameters = $parameters;
18
- $this->signature_method = new Yoast_OAuthSignatureMethod_HMAC_SHA1();
19
- $this->consumer = new Yoast_OAuthConsumer( 'anonymous', 'anonymous' );
20
-
21
- if ( ! empty( $oauth_token ) && ! empty( $oauth_token_secret ) ) {
22
- $this->token = new Yoast_OAuthConsumer( $oauth_token, $oauth_token_secret );
23
- } else {
24
- $this->token = null;
25
- }
26
- }
27
-
28
- function get_request_token( $oauth_callback = null ) {
29
- $parameters = $this->parameters;
30
- if ( ! empty( $oauth_callback ) ) {
31
- $parameters['oauth_callback'] = $oauth_callback;
32
- }
33
-
34
- $request = $this->oauth_request( self::request_token_url, 'GET', $parameters );
35
- $token = Yoast_OAuthUtil::parse_parameters( wp_remote_retrieve_body( $request ) );
36
- $this->token = new Yoast_OAuthConsumer( $token['oauth_token'], $token['oauth_token_secret'] );
37
-
38
- return $token;
39
- }
40
-
41
- /**
42
- * Format and sign an OAuth / API request
43
- */
44
- private function oauth_request( $url, $method, $parameters ) {
45
- $request = Yoast_OAuthRequest::from_consumer_and_token( $this->consumer, $this->token, $method, $url, $parameters );
46
- $request->sign_request( $this->signature_method, $this->consumer, $this->token );
47
-
48
- if ( 'GET' == $method ) {
49
- return wp_remote_get( $request->to_url() );
50
- } else {
51
- return wp_remote_post( $request->to_url(), $request->to_postdata() );
52
- }
53
- }
54
-
55
- function get_authorize_url( $token ) {
56
- if ( is_array( $token ) ) {
57
- $token = $token['oauth_token'];
58
- }
59
-
60
- return self::authorize_url . "?oauth_token={$token}";
61
- }
62
-
63
- /**
64
- * Exchange request token and secret for an access token and
65
- * secret, to sign API calls.
66
- *
67
- * @returns array( 'oauth_token' => 'the-access-token',
68
- * 'oauth_token_secret' => 'the-access-secret' )
69
- */
70
- function get_access_token( $oauth_verifier = '' ) {
71
- $parameters = array();
72
- if ( ! empty( $oauth_verifier ) ) {
73
- $parameters['oauth_verifier'] = $oauth_verifier;
74
- }
75
-
76
- $request = $this->oauth_request( self::access_token_url, 'GET', $parameters );
77
- $token = Yoast_OAuthUtil::parse_parameters( wp_remote_retrieve_body( $request ) );
78
- $this->token = new Yoast_OAuthConsumer( $token['oauth_token'], $token['oauth_token_secret'] );
79
-
80
- return $token;
81
- }
82
-
83
- /**
84
- * GET wrapper for oAuthRequest.
85
- */
86
- public function get( $url, $parameters = array() ) {
87
- return $this->oauth_request( $url, 'GET', $parameters );
88
- }
89
-
90
- /**
91
- * POST wrapper for oAuthRequest.
92
- */
93
- public function post( $url, $parameters = array() ) {
94
- $defaults = array(
95
- 'method' => 'POST',
96
- 'timeout' => 30,
97
- 'redirection' => 5,
98
- 'httpversion' => '1.0',
99
- 'blocking' => true,
100
- 'body' => array(),
101
- 'headers' => array(),
102
- 'cookies' => array()
103
- );
104
- $parameters = array_merge( $defaults, $parameters );
105
-
106
- return $this->oauth_request( $url, 'POST', $parameters );
107
- }
108
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/css/yoast_ga_styles.css CHANGED
@@ -96,17 +96,36 @@ p.ga-topdescription {
96
  max-width: 600px;
97
  }
98
 
99
- #ga-promote {
 
100
  max-width: 600px;
101
  padding-left: 10px;
102
  border: 1px solid #ccc;
103
  background-color: #fff;
104
  }
105
 
 
 
 
 
 
 
 
 
 
 
106
  #yoast-ga-form-settings input[type="text"] {
107
  width: 325px;
108
  }
109
 
 
 
 
 
 
 
 
 
110
  .ga-form .chosen-search input[type="text"] {
111
  width: 100% !important;
112
  }
@@ -189,6 +208,6 @@ select.ga-multiple {
189
  }
190
 
191
  #enter_ua div {
192
- display: inline-block;
193
- margin: 0;
194
- }
96
  max-width: 600px;
97
  }
98
 
99
+ #ga-promote,
100
+ .ga-promote {
101
  max-width: 600px;
102
  padding-left: 10px;
103
  border: 1px solid #ccc;
104
  background-color: #fff;
105
  }
106
 
107
+ .ga-promote {
108
+ margin-bottom: 10px;
109
+ }
110
+
111
+ #ga-promote p,
112
+ .ga-promote p {
113
+ margin: 0;
114
+ padding: 1em 1em 1em 0;
115
+ }
116
+
117
  #yoast-ga-form-settings input[type="text"] {
118
  width: 325px;
119
  }
120
 
121
+ #yoast-ga-form-settings #oauth_code div {
122
+ margin: 0 0 0 5px;
123
+ }
124
+
125
+ #yoast-ga-form-settings #oauth_code input[type="text"] {
126
+ width: 275px;
127
+ }
128
+
129
  .ga-form .chosen-search input[type="text"] {
130
  width: 100% !important;
131
  }
208
  }
209
 
210
  #enter_ua div {
211
+ display: inline-block;
212
+ margin: 0;
213
+ }
assets/css/yoast_ga_styles.min.css CHANGED
@@ -1 +1 @@
1
- h2#yoast_ga_title{padding:9px 15px 4px 0;font-size:23px;font-weight:400;line-height:29px}.nav-tab-wrapper{margin-bottom:20px}.gatab{display:none}.gatab.active{display:block;padding:.5em .9em;border:1px solid #ddd;border-radius:0 3px 3px;background-color:#fff}.tabwrapper .gatab{padding:0;border:none;background:0 0}.tabwrapper .gatab h2{padding:9px 15px 4px 0;font-size:23px;font-weight:400}.ga-form-submit{margin-top:20px}.ga-form-label-left{float:left;width:250px;margin:0 0 0 5px;font-size:14px;font-weight:700;cursor:default}.ga-form-input{margin-top:10px}#yoast-ga-wrapper{display:table;width:auto;max-width:1150px}.yoast-ga-content{display:table-cell;min-width:850px;height:500px;margin:0;padding:0;vertical-align:top}.yoast-ga-banners{display:table-cell;width:261px;height:500px;margin:0;padding:20px;vertical-align:top}.ga-form-input{display:table;margin-bottom:20px}.ga-form-table{display:table-cell}.ga-form-description{display:block;float:right;max-width:325px;color:#999}p.ga-topdescription{max-width:600px}#ga-promote{max-width:600px;padding-left:10px;border:1px solid #ccc;background-color:#fff}#yoast-ga-form-settings input[type=text]{width:325px}.ga-form .chosen-search input[type=text]{width:100%!important}.yoast-ga-content .chosen-container{width:325px!important}#enter_ua{display:none;margin-top:10px;padding-top:10px}#yoast-ga-form-text-settings-ga_general-manual_ua_code_field{margin-top:-30px}select.ga-multiple{width:300px;height:150px!important}.wpseotab .extension{float:left;box-sizing:border-box;width:350px;height:230px;margin:10px 20px 10px 0;border:1px solid #ccc}.wpseotab .extension p{margin:0;padding:10px}.wpseotab .extension h3{box-sizing:border-box;height:110px;margin:0;padding:20px 10px 0 120px;border-bottom:1px solid #ccc;background:left 10px/130px 100px no-repeat #fff}.wpseotab .extension button.installed{border-color:#00a000;background-color:#00a000;cursor:default}.wpseotab .extension .button-primary.activate-link{border-color:#f06000;background-color:#f18500}.extension a{text-decoration:none}.ecommerce h3{background-image:url(../img/eComm_130x100.png)!important}.ga_premium h3{background-image:url(../img/ga-premium-banner.png)!important}#ga-debug-info h3{height:30px;margin-top:10px;padding-left:16px;font-size:14px}.yoast_help{margin:2px 5px 0}
1
+ h2#yoast_ga_title{padding:9px 15px 4px 0;font-size:23px;font-weight:400;line-height:29px}.nav-tab-wrapper{margin-bottom:20px}.gatab{display:none}.gatab.active{display:block;padding:.5em .9em;border:1px solid #ddd;border-radius:0 3px 3px;background-color:#fff}.tabwrapper .gatab{padding:0;border:none;background:0 0}.tabwrapper .gatab h2{padding:9px 15px 4px 0;font-size:23px;font-weight:400}.ga-form-submit{margin-top:20px}.ga-form-label-left{float:left;width:250px;margin:0 0 0 5px;font-size:14px;font-weight:700;cursor:default}.ga-form-input{margin-top:10px}#yoast-ga-wrapper{display:table;width:auto;max-width:1150px}.yoast-ga-content{display:table-cell;min-width:850px;height:500px;margin:0;padding:0;vertical-align:top}.yoast-ga-banners{display:table-cell;width:261px;height:500px;margin:0;padding:20px;vertical-align:top}.ga-form-input{display:table;margin-bottom:20px}.ga-form-table{display:table-cell}.ga-form-description{display:block;float:right;max-width:325px;color:#999}p.ga-topdescription{max-width:600px}#ga-promote,.ga-promote{max-width:600px;padding-left:10px;border:1px solid #ccc;background-color:#fff}.ga-promote{margin-bottom:10px}#ga-promote p,.ga-promote p{margin:0;padding:1em 1em 1em 0}#yoast-ga-form-settings input[type=text]{width:325px}#yoast-ga-form-settings #oauth_code div{margin:0 0 0 5px}#yoast-ga-form-settings #oauth_code input[type=text]{width:275px}.ga-form .chosen-search input[type=text]{width:100%!important}.yoast-ga-content .chosen-container{width:325px!important}#enter_ua{display:none;margin-top:10px;padding-top:10px}#yoast-ga-form-text-settings-ga_general-manual_ua_code_field{margin-top:-30px}select.ga-multiple{width:300px;height:150px!important}.wpseotab .extension{float:left;box-sizing:border-box;width:350px;height:230px;margin:10px 20px 10px 0;border:1px solid #ccc}.wpseotab .extension p{margin:0;padding:10px}.wpseotab .extension h3{box-sizing:border-box;height:110px;margin:0;padding:20px 10px 0 120px;border-bottom:1px solid #ccc;background:left 10px/130px 100px no-repeat #fff}.wpseotab .extension button.installed{border-color:#00a000;background-color:#00a000;cursor:default}.wpseotab .extension .button-primary.activate-link{border-color:#f06000;background-color:#f18500}.extension a{text-decoration:none}.ecommerce h3{background-image:url(../img/eComm_130x100.png)!important}.ga_premium h3{background-image:url(../img/ga-premium-banner.png)!important}#ga-debug-info h3{height:30px;margin-top:10px;padding-left:16px;font-size:14px}.yoast_help{margin:2px 5px 0}#enter_ua div{display:inline-block;margin:0}
assets/js/yoast_ga_admin.js CHANGED
@@ -32,11 +32,13 @@ jQuery(document).ready(function() {
32
  jQuery('#enter_ua').show();
33
  jQuery("#yoast-ga-form-select-settings-analytics_profile").prop('disabled', true).trigger("chosen:updated");
34
  jQuery("#yst_ga_authenticate").attr('disabled', true);
 
35
  } else {
36
  jQuery('#enter_ua').hide();
37
  jQuery('#yoast-ga-form-text-settings-manual_ua_code_field').attr('value', '');
38
  jQuery("#yoast-ga-form-select-settings-analytics_profile").prop('disabled', false).trigger("chosen:updated");
39
  jQuery("#yst_ga_authenticate").attr('disabled', false);
 
40
  }
41
  }
42
 
@@ -44,6 +46,11 @@ jQuery(document).ready(function() {
44
  jQuery('#yoast-ga-form-checkbox-settings-manual_ua_code').click( function() { yst_ga_switch_manual(); } );
45
  yst_ga_switch_manual();
46
 
 
 
 
 
 
47
  jQuery('.nav-tab-active').click();
48
 
49
  jQuery('.yoast_help').qtip({
32
  jQuery('#enter_ua').show();
33
  jQuery("#yoast-ga-form-select-settings-analytics_profile").prop('disabled', true).trigger("chosen:updated");
34
  jQuery("#yst_ga_authenticate").attr('disabled', true);
35
+ jQuery('#oauth_code').hide();
36
  } else {
37
  jQuery('#enter_ua').hide();
38
  jQuery('#yoast-ga-form-text-settings-manual_ua_code_field').attr('value', '');
39
  jQuery("#yoast-ga-form-select-settings-analytics_profile").prop('disabled', false).trigger("chosen:updated");
40
  jQuery("#yst_ga_authenticate").attr('disabled', false);
41
+ jQuery('#oauth_code').show();
42
  }
43
  }
44
 
46
  jQuery('#yoast-ga-form-checkbox-settings-manual_ua_code').click( function() { yst_ga_switch_manual(); } );
47
  yst_ga_switch_manual();
48
 
49
+ jQuery('#oauth_code').hide();
50
+ jQuery("#yst_ga_authenticate").click( function() {
51
+ jQuery('#oauth_code').show().focus();
52
+ });
53
+
54
  jQuery('.nav-tab-active').click();
55
 
56
  jQuery('.yoast_help').qtip({
assets/js/yoast_ga_admin.min.js CHANGED
@@ -1 +1 @@
1
- jQuery(document).ready(function(){function a(){jQuery("#yoast-ga-form-checkbox-settings-manual_ua_code").is(":checked")?(jQuery("#enter_ua").show(),jQuery("#yoast-ga-form-select-settings-analytics_profile").prop("disabled",!0).trigger("chosen:updated"),jQuery("#yst_ga_authenticate").attr("disabled",!0)):(jQuery("#enter_ua").hide(),jQuery("#yoast-ga-form-text-settings-manual_ua_code_field").attr("value",""),jQuery("#yoast-ga-form-select-settings-analytics_profile").prop("disabled",!1).trigger("chosen:updated"),jQuery("#yst_ga_authenticate").attr("disabled",!1))}jQuery("#ga-tabs").find("a").click(function(){jQuery("#ga-tabs").find("a").removeClass("nav-tab-active"),jQuery(".gatab").removeClass("active");var a=jQuery(this).attr("id").replace("-tab","");jQuery("#"+a).addClass("active"),jQuery(this).addClass("nav-tab-active"),jQuery("#return_tab").val(a)}),jQuery("a.activate-link").click(function(){jQuery("#extensions.wpseotab").removeClass("active"),jQuery("#extensions-tab").removeClass("nav-tab-active"),jQuery("#licenses.wpseotab").addClass("active"),jQuery("#licenses-tab").addClass("nav-tab-active")});var b=window.location.hash.replace("#top#","");(""===b||"#_=_"===b)&&(b=jQuery(".gatab").attr("id")),jQuery("#"+b).addClass("active"),jQuery("#"+b+"-tab").addClass("nav-tab-active"),jQuery("#yoast-ga-form-checkbox-settings-manual_ua_code").click(function(){a()}),a(),jQuery(".nav-tab-active").click(),jQuery(".yoast_help").qtip({position:{corner:{target:"topMiddle",tooltip:"bottomLeft"}},show:{when:{event:"mouseover"}},hide:{fixed:!0,when:{event:"mouseout"}},style:{tip:"bottomLeft",name:"blue"}})});
1
+ jQuery(document).ready(function(){function a(){jQuery("#yoast-ga-form-checkbox-settings-manual_ua_code").is(":checked")?(jQuery("#enter_ua").show(),jQuery("#yoast-ga-form-select-settings-analytics_profile").prop("disabled",!0).trigger("chosen:updated"),jQuery("#yst_ga_authenticate").attr("disabled",!0),jQuery("#oauth_code").hide()):(jQuery("#enter_ua").hide(),jQuery("#yoast-ga-form-text-settings-manual_ua_code_field").attr("value",""),jQuery("#yoast-ga-form-select-settings-analytics_profile").prop("disabled",!1).trigger("chosen:updated"),jQuery("#yst_ga_authenticate").attr("disabled",!1),jQuery("#oauth_code").show())}jQuery("#ga-tabs").find("a").click(function(){jQuery("#ga-tabs").find("a").removeClass("nav-tab-active"),jQuery(".gatab").removeClass("active");var a=jQuery(this).attr("id").replace("-tab","");jQuery("#"+a).addClass("active"),jQuery(this).addClass("nav-tab-active"),jQuery("#return_tab").val(a)}),jQuery("a.activate-link").click(function(){jQuery("#extensions.wpseotab").removeClass("active"),jQuery("#extensions-tab").removeClass("nav-tab-active"),jQuery("#licenses.wpseotab").addClass("active"),jQuery("#licenses-tab").addClass("nav-tab-active")});var b=window.location.hash.replace("#top#","");(""===b||"#_=_"===b)&&(b=jQuery(".gatab").attr("id")),jQuery("#"+b).addClass("active"),jQuery("#"+b+"-tab").addClass("nav-tab-active"),jQuery("#yoast-ga-form-checkbox-settings-manual_ua_code").click(function(){a()}),a(),jQuery("#oauth_code").hide(),jQuery("#yst_ga_authenticate").click(function(){jQuery("#oauth_code").show().focus()}),jQuery(".nav-tab-active").click(),jQuery(".yoast_help").qtip({position:{corner:{target:"topMiddle",tooltip:"bottomLeft"}},show:{when:{event:"mouseover"}},hide:{fixed:!0,when:{event:"mouseout"}},style:{tip:"bottomLeft",name:"blue"}})});
googleanalytics.php CHANGED
@@ -4,7 +4,7 @@ Plugin Name: Google Analytics by Yoast
4
  Plugin URI: https://yoast.com/wordpress/plugins/google-analytics/#utm_source=wordpress&utm_medium=plugin&utm_campaign=wpgaplugin&utm_content=v504
5
  Description: This plugin makes it simple to add Google Analytics to your WordPress blog, adding lots of features, eg. error page, search result and automatic clickout and download tracking.
6
  Author: Team Yoast
7
- Version: 5.2.3
8
  Requires at least: 3.8
9
  Author URI: https://yoast.com/
10
  License: GPL v3
@@ -30,7 +30,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
30
 
31
  // This plugin was originally based on Rich Boakes' Analytics plugin: http://boakes.org/analytics, but has since been rewritten and refactored multiple times.
32
 
33
- define( 'GAWP_VERSION', '5.2.3' );
34
 
35
  define( 'GAWP_FILE', __FILE__ );
36
 
4
  Plugin URI: https://yoast.com/wordpress/plugins/google-analytics/#utm_source=wordpress&utm_medium=plugin&utm_campaign=wpgaplugin&utm_content=v504
5
  Description: This plugin makes it simple to add Google Analytics to your WordPress blog, adding lots of features, eg. error page, search result and automatic clickout and download tracking.
6
  Author: Team Yoast
7
+ Version: 5.2.4
8
  Requires at least: 3.8
9
  Author URI: https://yoast.com/
10
  License: GPL v3
30
 
31
  // This plugin was originally based on Rich Boakes' Analytics plugin: http://boakes.org/analytics, but has since been rewritten and refactored multiple times.
32
 
33
+ define( 'GAWP_VERSION', '5.2.4' );
34
 
35
  define( 'GAWP_FILE', __FILE__ );
36
 
includes/class-autoload.php CHANGED
@@ -30,8 +30,6 @@ if ( ! class_exists( 'Yoast_GA_Autoload' ) ) {
30
  'yoast_ga_admin_assets' => 'admin/class-admin-assets',
31
  'yoast_ga_admin_form' => 'admin/class-admin-form',
32
 
33
- 'wp_gdata' => 'admin/wp-gdata/wp-gdata',
34
-
35
  // Dashboards
36
  'yoast_ga_dashboards_api_options' => 'admin/dashboards/class-admin-dashboards-api-options',
37
  'yoast_ga_dashboards' => 'admin/dashboards/class-admin-dashboards',
30
  'yoast_ga_admin_assets' => 'admin/class-admin-assets',
31
  'yoast_ga_admin_form' => 'admin/class-admin-form',
32
 
 
 
33
  // Dashboards
34
  'yoast_ga_dashboards_api_options' => 'admin/dashboards/class-admin-dashboards-api-options',
35
  'yoast_ga_dashboards' => 'admin/dashboards/class-admin-dashboards',
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: joostdevalk,PvW_NL
3
  Donate link: https://yoast.com/donate/
4
  Tags: analytics, google analytics, statistics, tracking, stats, google, yoast
5
  Requires at least: 3.8
6
- Tested up to: 4.0
7
- Stable tag: 5.2.3
8
 
9
  Track your WordPress site easily with the latest tracking codes and lots added data for search result pages and error pages.
10
 
@@ -49,15 +49,25 @@ This section describes how to install the plugin and get it working.
49
 
50
  == Changelog ==
51
 
 
 
 
 
 
 
 
 
 
 
52
  = 5.2.3 =
53
 
54
  Release Date: December 11th, 2014
55
 
56
- Enhancements:
57
  * improved visual look and data representation for dashboard graphs, props [Andrey Savchenko](https://github.com/Rarst/)
58
  * when manual UA-code is selected, the profile selection gets disabled to clarify that the user is choosing an alternative to regular profile selection.
59
 
60
- Bugfixes:
61
  * Fixes a bug where GA profile was fetched with every admin request, resulting in a pile of unnecessary API requests.
62
  * Fixes a problem where GA started throttling API requests done by our plugin because there were too many. We now fetch the data only once a day from GA. For realtime data, users should go to Google Analytics.
63
 
@@ -65,7 +75,7 @@ Bugfixes:
65
 
66
  Release Date: December 10th, 2014
67
 
68
- Bugfixes:
69
  * Fixes a bug where it was no longer possible to uncheck checkboxes in the settings.
70
  * Fixes a bug where jQuery Chosen component was not rendered correctly on a hidden tab.
71
 
@@ -73,11 +83,11 @@ Bugfixes:
73
 
74
  Release Date: December 9th, 2014
75
 
76
- Enhancements:
77
  * Replaced jQuery Chosen library with latest version to enable optgroup search.
78
  * Shows a warning when configuring a manual UA-code that this will not work together with the dashboards functionality.
79
 
80
- Bugfixes:
81
  * Fixes a 'headers already sent' warning.
82
  * Fixes a bug where nothing was shown on the dashboards for setups with a manual UA-code. It now shows you a message that you need to authenticate with Google Analytics if you wish to make use of the dashboards feature.
83
 
3
  Donate link: https://yoast.com/donate/
4
  Tags: analytics, google analytics, statistics, tracking, stats, google, yoast
5
  Requires at least: 3.8
6
+ Tested up to: 4.1
7
+ Stable tag: 5.2.4
8
 
9
  Track your WordPress site easily with the latest tracking codes and lots added data for search result pages and error pages.
10
 
49
 
50
  == Changelog ==
51
 
52
+ = 5.2.4 =
53
+
54
+ Release Date: December 15th, 2014
55
+
56
+ * Enhancements:
57
+ * Moved from OAuth1 to OAuth2 for Google Analytics API requests.
58
+ * Open authentication and reauthentication for Google in a new window.
59
+ * Force reloading of CSS and JS on version change of plugin.
60
+ * Refactored fetching profiles from Google Analytics.
61
+
62
  = 5.2.3 =
63
 
64
  Release Date: December 11th, 2014
65
 
66
+ * Enhancements:
67
  * improved visual look and data representation for dashboard graphs, props [Andrey Savchenko](https://github.com/Rarst/)
68
  * when manual UA-code is selected, the profile selection gets disabled to clarify that the user is choosing an alternative to regular profile selection.
69
 
70
+ * Bugfixes:
71
  * Fixes a bug where GA profile was fetched with every admin request, resulting in a pile of unnecessary API requests.
72
  * Fixes a problem where GA started throttling API requests done by our plugin because there were too many. We now fetch the data only once a day from GA. For realtime data, users should go to Google Analytics.
73
 
75
 
76
  Release Date: December 10th, 2014
77
 
78
+ * Bugfixes:
79
  * Fixes a bug where it was no longer possible to uncheck checkboxes in the settings.
80
  * Fixes a bug where jQuery Chosen component was not rendered correctly on a hidden tab.
81
 
83
 
84
  Release Date: December 9th, 2014
85
 
86
+ * Enhancements:
87
  * Replaced jQuery Chosen library with latest version to enable optgroup search.
88
  * Shows a warning when configuring a manual UA-code that this will not work together with the dashboards functionality.
89
 
90
+ * Bugfixes:
91
  * Fixes a 'headers already sent' warning.
92
  * Fixes a bug where nothing was shown on the dashboards for setups with a manual UA-code. It now shows you a message that you need to authenticate with Google Analytics if you wish to make use of the dashboards feature.
93