]> git.mxchange.org Git - friendica-addons.git/blob - pumpio/oauth/oauth_client.php
Merge pull request #121 from annando/master
[friendica-addons.git] / pumpio / oauth / oauth_client.php
1 <?php
2 /*
3  * oauth_client.php
4  *
5  * @(#) $Id: oauth_client.php,v 1.58 2013/04/11 09:33:16 mlemos Exp $
6  *
7  */
8
9 /*
10 {metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?>
11 <class>
12
13         <package>net.manuellemos.oauth</package>
14
15         <version>@(#) $Id: oauth_client.php,v 1.58 2013/04/11 09:33:16 mlemos Exp $</version>
16         <copyright>Copyright © (C) Manuel Lemos 2012</copyright>
17         <title>OAuth client</title>
18         <author>Manuel Lemos</author>
19         <authoraddress>mlemos-at-acm.org</authoraddress>
20
21         <documentation>
22                 <idiom>en</idiom>
23                 <purpose>This class serves two main purposes:<paragraphbreak />
24                         1) Implement the OAuth protocol to retrieve a token from a server to
25                         authorize the access to an API on behalf of the current
26                         user.<paragraphbreak />
27                         2) Perform calls to a Web services API using a token previously
28                         obtained using this class or a token provided some other way by the
29                         Web services provider.</purpose>
30                 <usage>Regardless of your purposes, you always need to start calling
31                         the class <functionlink>Initialize</functionlink> function after
32                         initializing setup variables. After you are done with the class,
33                         always call the <functionlink>Finalize</functionlink> function at
34                         the end.<paragraphbreak />
35                         This class supports either OAuth protocol versions 1.0, 1.0a and
36                         2.0. It abstracts the differences between these protocol versions,
37                         so the class usage is the same independently of the OAuth
38                         version of the server.<paragraphbreak />
39                         The class also provides built-in support to several popular OAuth
40                         servers, so you do not have to manually configure all the details to
41                         access those servers. Just set the
42                         <variablelink>server</variablelink> variable to configure the class
43                         to access one of the built-in supported servers.<paragraphbreak />
44                         If you need to access one type of server that is not yet directly
45                         supported by the class, you need to configure it explicitly setting
46                         the variables: <variablelink>oauth_version</variablelink>,
47                         <variablelink>url_parameters</variablelink>,
48                         <variablelink>authorization_header</variablelink>,
49                         <variablelink>request_token_url</variablelink>,
50                         <variablelink>dialog_url</variablelink>,
51                         <variablelink>offline_dialog_url</variablelink>,
52                         <variablelink>append_state_to_redirect_uri</variablelink> and
53                         <variablelink>access_token_url</variablelink>.<paragraphbreak />
54                         Before proceeding to the actual OAuth authorization process, you
55                         need to have registered your application with the OAuth server. The
56                         registration provides you values to set the variables
57                         <variablelink>client_id</variablelink> and 
58                         <variablelink>client_secret</variablelink>.<paragraphbreak />
59                         You also need to set the variables
60                         <variablelink>redirect_uri</variablelink> and
61                         <variablelink>scope</variablelink> before calling the
62                         <functionlink>Process</functionlink> function to make the class
63                         perform the necessary interactions with the OAuth
64                         server.<paragraphbreak />
65                         The OAuth protocol involves multiple steps that include redirection
66                         to the OAuth server. There it asks permission to the current user to
67                         grant your application access to APIs on his/her behalf. When there
68                         is a redirection, the class will set the
69                         <variablelink>exit</variablelink> variable to
70                         <booleanvalue>1</booleanvalue>. Then your script should exit
71                         immediately without outputting anything.<paragraphbreak />
72                         When the OAuth access token is successfully obtained, the following
73                         variables are set by the class with the obtained values:
74                         <variablelink>access_token</variablelink>,
75                         <variablelink>access_token_secret</variablelink>,
76                         <variablelink>access_token_expiry</variablelink>,
77                         <variablelink>access_token_type</variablelink>. You may want to
78                         store these values to use them later when calling the server
79                         APIs.<paragraphbreak />
80                         If there was a problem during OAuth authorization process, check the
81                         variable <variablelink>authorization_error</variablelink> to
82                         determine the reason.<paragraphbreak />
83                         Once you get the access token, you can call the server APIs using
84                         the <functionlink>CallAPI</functionlink> function. Check the
85                         <variablelink>access_token_error</variablelink> variable to
86                         determine if there was an error when trying to to call the
87                         API.<paragraphbreak />
88                         If for some reason the user has revoked the access to your
89                         application, you need to ask the user to authorize your application
90                         again. First you may need to call the function
91                         <functionlink>ResetAccessToken</functionlink> to reset the value of
92                         the access token that may be cached in session variables.</usage>
93         </documentation>
94
95 {/metadocument}
96 */
97
98 class oauth_client_class
99 {
100 /*
101 {metadocument}
102         <variable>
103                 <name>error</name>
104                 <type>STRING</type>
105                 <value></value>
106                 <documentation>
107                         <purpose>Store the message that is returned when an error
108                                 occurs.</purpose>
109                         <usage>Check this variable to understand what happened when a call to
110                                 any of the class functions has failed.<paragraphbreak />
111                                 This class uses cumulative error handling. This means that if one
112                                 class functions that may fail is called and this variable was
113                                 already set to an error message due to a failure in a previous call
114                                 to the same or other function, the function will also fail and does
115                                 not do anything.<paragraphbreak />
116                                 This allows programs using this class to safely call several
117                                 functions that may fail and only check the failure condition after
118                                 the last function call.<paragraphbreak />
119                                 Just set this variable to an empty string to clear the error
120                                 condition.</usage>
121                 </documentation>
122         </variable>
123 {/metadocument}
124 */
125         var $error = '';
126
127 /*
128 {metadocument}
129         <variable>
130                 <name>debug</name>
131                 <type>BOOLEAN</type>
132                 <value>0</value>
133                 <documentation>
134                         <purpose>Control whether debug output is enabled</purpose>
135                         <usage>Set this variable to <booleanvalue>1</booleanvalue> if you
136                                 need to check what is going on during calls to the class. When
137                                 enabled, the debug output goes either to the variable
138                                 <variablelink>debug_output</variablelink> and the PHP error log.</usage>
139                 </documentation>
140         </variable>
141 {/metadocument}
142 */
143         var $debug = false;
144
145 /*
146 {metadocument}
147         <variable>
148                 <name>debug_http</name>
149                 <type>BOOLEAN</type>
150                 <value>0</value>
151                 <documentation>
152                         <purpose>Control whether the dialog with the remote Web server
153                                 should also be logged.</purpose>
154                         <usage>Set this variable to <booleanvalue>1</booleanvalue> if you
155                                 want to inspect the data exchange with the OAuth server.</usage>
156                 </documentation>
157         </variable>
158 {/metadocument}
159 */
160         var $debug_http = false;
161
162 /*
163 {metadocument}
164         <variable>
165                 <name>exit</name>
166                 <type>BOOLEAN</type>
167                 <value>0</value>
168                 <documentation>
169                         <purpose>Determine if the current script should be exited.</purpose>
170                         <usage>Check this variable after calling the
171                                 <functionlink>Process</functionlink> function and exit your script
172                                 immediately if the variable is set to
173                                 <booleanvalue>1</booleanvalue>.</usage>
174                 </documentation>
175         </variable>
176 {/metadocument}
177 */
178         var $exit = false;
179
180 /*
181 {metadocument}
182         <variable>
183                 <name>debug_output</name>
184                 <type>STRING</type>
185                 <value></value>
186                 <documentation>
187                         <purpose>Capture the debug output generated by the class</purpose>
188                         <usage>Inspect this variable if you need to see what happened during
189                                 the class function calls.</usage>
190                 </documentation>
191         </variable>
192 {/metadocument}
193 */
194         var $debug_output = '';
195
196 /*
197 {metadocument}
198         <variable>
199                 <name>debug_prefix</name>
200                 <type>STRING</type>
201                 <value>OAuth client: </value>
202                 <documentation>
203                         <purpose>Mark the lines of the debug output to identify actions
204                                 performed by this class.</purpose>
205                         <usage>Change this variable if you prefer the debug output lines to
206                                 be prefixed with a different text.</usage>
207                 </documentation>
208         </variable>
209 {/metadocument}
210 */
211         var $debug_prefix = 'OAuth client: ';
212
213 /*
214 {metadocument}
215         <variable>
216                 <name>server</name>
217                 <type>STRING</type>
218                 <value></value>
219                 <documentation>
220                         <purpose>Identify the type of OAuth server to access.</purpose>
221                         <usage>The class provides built-in support to several types of OAuth
222                                 servers. This means that the class can automatically initialize
223                                 several configuration variables just by setting this server
224                                 variable.<paragraphbreak />
225                                 Currently it supports the following servers:
226                                 <stringvalue>Bitbucket</stringvalue>,
227                                 <stringvalue>Box</stringvalue>,
228                                 <stringvalue>Dropbox</stringvalue>,
229                                 <stringvalue>Eventful</stringvalue>,
230                                 <stringvalue>Facebook</stringvalue>,
231                                 <stringvalue>Fitbit</stringvalue>,
232                                 <stringvalue>Flickr</stringvalue>,
233                                 <stringvalue>Foursquare</stringvalue>,
234                                 <stringvalue>github</stringvalue>,
235                                 <stringvalue>Google</stringvalue>,
236                                 <stringvalue>Instagram</stringvalue>,
237                                 <stringvalue>LinkedIn</stringvalue>,
238                                 <stringvalue>Microsoft</stringvalue>,
239                                 <stringvalue>Scoop.it</stringvalue>,
240                                 <stringvalue>StockTwits</stringvalue>,
241                                 <stringvalue>Tumblr</stringvalue>,
242                                 <stringvalue>Twitter</stringvalue>,
243                                 <stringvalue>XING</stringvalue> and
244                                 <stringvalue>Yahoo</stringvalue>. Please contact the author if you
245                                 would like to ask to add built-in support for other types of OAuth
246                                 servers.<paragraphbreak />
247                                 If you want to access other types of OAuth servers that are not
248                                 yet supported, set this variable to an empty string and configure
249                                 other variables with values specific to those servers.</usage>
250                 </documentation>
251         </variable>
252 {/metadocument}
253 */
254         var $server = '';
255
256 /*
257 {metadocument}
258         <variable>
259                 <name>request_token_url</name>
260                 <type>STRING</type>
261                 <value></value>
262                 <documentation>
263                         <purpose>URL of the OAuth server to request the initial token for
264                                 OAuth 1.0 and 1.0a servers.</purpose>
265                         <usage>Set this variable to the OAuth request token URL when you are
266                                 not accessing one of the built-in supported OAuth
267                                 servers.<paragraphbreak />
268                                 For OAuth 1.0 and 1.0a servers, the request token URL can have
269                                 certain marks that will act as template placeholders which will be
270                                 replaced with given values before requesting the authorization
271                                 token. Currently it supports the following placeholder
272                                 marks:<paragraphbreak />
273                                 {SCOPE} - scope of the requested permissions to the granted by the
274                                 OAuth server with the user permissions</usage>
275                 </documentation>
276         </variable>
277 {/metadocument}
278 */
279         var $request_token_url = '';
280
281 /*
282 {metadocument}
283         <variable>
284                 <name>dialog_url</name>
285                 <type>STRING</type>
286                 <value></value>
287                 <documentation>
288                         <purpose>URL of the OAuth server to redirect the browser so the user
289                                 can grant access to your application.</purpose>
290                         <usage>Set this variable to the OAuth request token URL when you are
291                                 not accessing one of the built-in supported OAuth servers.<paragraphbreak />
292                                 For certain servers, the dialog URL can have certain marks that
293                                 will act as template placeholders which will be replaced with
294                                 values defined before redirecting the users browser. Currently it
295                                 supports the following placeholder marks:<paragraphbreak />
296                                 {REDIRECT_URI} - URL to redirect when returning from the OAuth
297                                 server authorization page<paragraphbreak />
298                                 {CLIENT_ID} - client application identifier registered at the
299                                 server<paragraphbreak />
300                                 {SCOPE} - scope of the requested permissions to the granted by the
301                                 OAuth server with the user permissions<paragraphbreak />
302                                 {STATE} - identifier of the OAuth session state</usage>
303                 </documentation>
304         </variable>
305 {/metadocument}
306 */
307         var $dialog_url = '';
308
309 /*
310 {metadocument}
311         <variable>
312                 <name>offline_dialog_url</name>
313                 <type>STRING</type>
314                 <value></value>
315                 <documentation>
316                         <purpose>URL of the OAuth server to redirect the browser so the user
317                                 can grant access to your application when offline access is
318                                 requested.</purpose>
319                         <usage>Set this variable to the OAuth request token URL when you are
320                                 not accessing one of the built-in supported OAuth servers and the
321                                 OAuth server supports offline access.<paragraphbreak />
322                                 It should have the same format as the
323                                 <variablelink>dialog_url</variablelink> variable.</usage>
324                 </documentation>
325         </variable>
326 {/metadocument}
327 */
328         var $offline_dialog_url = '';
329
330 /*
331 {metadocument}
332         <variable>
333                 <name>append_state_to_redirect_uri</name>
334                 <type>STRING</type>
335                 <value></value>
336                 <documentation>
337                         <purpose>Pass the OAuth session state in a variable with a different
338                                 name to work around implementation bugs of certain OAuth
339                                 servers</purpose>
340                         <usage>Set this variable  when you are not accessing one of the
341                                 built-in supported OAuth servers if the OAuth server has a bug
342                                 that makes it not pass back the OAuth state identifier in a
343                                 request variable named state.</usage>
344                 </documentation>
345         </variable>
346 {/metadocument}
347 */
348         var $append_state_to_redirect_uri = '';
349
350 /*
351 {metadocument}
352         <variable>
353                 <name>access_token_url</name>
354                 <type>STRING</type>
355                 <value></value>
356                 <documentation>
357                         <purpose>OAuth server URL that will return the access token
358                                 URL.</purpose>
359                         <usage>Set this variable to the OAuth access token URL when you are
360                                 not accessing one of the built-in supported OAuth servers.</usage>
361                 </documentation>
362         </variable>
363 {/metadocument}
364 */
365         var $access_token_url = '';
366
367
368 /*
369 {metadocument}
370         <variable>
371                 <name>oauth_version</name>
372                 <type>STRING</type>
373                 <value>2.0</value>
374                 <documentation>
375                         <purpose>Version of the protocol version supported by the OAuth
376                                 server.</purpose>
377                         <usage>Set this variable to the OAuth server protocol version when
378                                 you are not accessing one of the built-in supported OAuth
379                                 servers.</usage>
380                 </documentation>
381         </variable>
382 {/metadocument}
383 */
384         var $oauth_version = '2.0';
385
386 /*
387 {metadocument}
388         <variable>
389                 <name>url_parameters</name>
390                 <type>BOOLEAN</type>
391                 <value>0</value>
392                 <documentation>
393                         <purpose>Determine if the API call parameters should be moved to the
394                                 call URL.</purpose>
395                         <usage>Set this variable to <booleanvalue>1</booleanvalue> if the
396                                 API you need to call requires that the call parameters always be
397                                 passed via the API URL.</usage>
398                 </documentation>
399         </variable>
400 {/metadocument}
401 */
402         var $url_parameters = false;
403
404 /*
405 {metadocument}
406         <variable>
407                 <name>authorization_header</name>
408                 <type>BOOLEAN</type>
409                 <value>1</value>
410                 <documentation>
411                         <purpose>Determine if the OAuth parameters should be passed via HTTP
412                                 Authorization request header.</purpose>
413                         <usage>Set this variable to <booleanvalue>1</booleanvalue> if the
414                                 OAuth server requires that the OAuth parameters be passed using
415                                 the HTTP Authorization instead of the request URI parameters.</usage>
416                 </documentation>
417         </variable>
418 {/metadocument}
419 */
420         var $authorization_header = true;
421
422 /*
423 {metadocument}
424         <variable>
425                 <name>token_request_method</name>
426                 <type>STRING</type>
427                 <value>GET</value>
428                 <documentation>
429                         <purpose>Define the HTTP method that should be used to request
430                                 tokens from the server.</purpose>
431                         <usage>Set this variable to <stringvalue>POST</stringvalue> if the
432                                 OAuth server does not support requesting tokens using the HTTP GET
433                                 method.</usage>
434                 </documentation>
435         </variable>
436 {/metadocument}
437 */
438         var $token_request_method = 'GET';
439
440 /*
441 {metadocument}
442         <variable>
443                 <name>signature_method</name>
444                 <type>STRING</type>
445                 <value>HMAC-SHA1</value>
446                 <documentation>
447                         <purpose>Define the method to generate the signature for API request
448                                 parameters values.</purpose>
449                         <usage>Currently it supports <stringvalue>PLAINTEXT</stringvalue>
450                                 and <stringvalue>HMAC-SHA1</stringvalue>.</usage>
451                 </documentation>
452         </variable>
453 {/metadocument}
454 */
455         var $signature_method = 'HMAC-SHA1';
456
457 /*
458 {metadocument}
459         <variable>
460                 <name>redirect_uri</name>
461                 <type>STRING</type>
462                 <value></value>
463                 <documentation>
464                         <purpose>URL of the current script page that is calling this
465                                 class</purpose>
466                         <usage>Set this variable to the current script page URL before
467                                 proceeding the the OAuth authorization process.</usage>
468                 </documentation>
469         </variable>
470 {/metadocument}
471 */
472         var $redirect_uri = '';
473
474 /*
475 {metadocument}
476         <variable>
477                 <name>client_id</name>
478                 <type>STRING</type>
479                 <value></value>
480                 <documentation>
481                         <purpose>Identifier of your application registered with the OAuth
482                                 server</purpose>
483                         <usage>Set this variable to the application identifier that is
484                                 provided by the OAuth server when you register the
485                                 application.</usage>
486                 </documentation>
487         </variable>
488 {/metadocument}
489 */
490         var $client_id = '';
491
492 /*
493 {metadocument}
494         <variable>
495                 <name>client_secret</name>
496                 <type>STRING</type>
497                 <value></value>
498                 <documentation>
499                         <purpose>Secret value assigned to your application when it is
500                                 registered with the OAuth server.</purpose>
501                         <usage>Set this variable to the application secret that is provided
502                                 by the OAuth server when you register the application.</usage>
503                 </documentation>
504         </variable>
505 {/metadocument}
506 */
507         var $client_secret = '';
508
509 /*
510 {metadocument}
511         <variable>
512                 <name>scope</name>
513                 <type>STRING</type>
514                 <value></value>
515                 <documentation>
516                         <purpose>Permissions that your application needs to call the OAuth
517                                 server APIs</purpose>
518                         <usage>Check the documentation of the APIs that your application
519                                 needs to call to set this variable with the identifiers of the
520                                 permissions that the user needs to grant to your application.</usage>
521                 </documentation>
522         </variable>
523 {/metadocument}
524 */
525         var $scope = '';
526
527 /*
528 {metadocument}
529         <variable>
530                 <name>offline</name>
531                 <type>BOOLEAN</type>
532                 <value>0</value>
533                 <documentation>
534                         <purpose>Specify whether it will be necessary to call the API when
535                                 the user is not present and the server supports renewing expired
536                                 access tokens using refresh tokens.</purpose>
537                         <usage>Set this variable to <booleanvalue>1</booleanvalue> if the
538                                 server supports renewing expired tokens automatically when the
539                                 user is not present.</usage>
540                 </documentation>
541         </variable>
542 {/metadocument}
543 */
544         var $offline = false;
545
546 /*
547 {metadocument}
548         <variable>
549                 <name>access_token</name>
550                 <type>STRING</type>
551                 <value></value>
552                 <documentation>
553                         <purpose>Access token obtained from the OAuth server</purpose>
554                         <usage>Check this variable to get the obtained access token upon
555                                 successful OAuth authorization.</usage>
556                 </documentation>
557         </variable>
558 {/metadocument}
559 */
560         var $access_token = '';
561
562 /*
563 {metadocument}
564         <variable>
565                 <name>access_token_secret</name>
566                 <type>STRING</type>
567                 <value></value>
568                 <documentation>
569                         <purpose>Access token secret obtained from the OAuth server</purpose>
570                         <usage>If the OAuth protocol version is 1.0 or 1.0a, check this
571                                 variable to get the obtained access token secret upon successful
572                                 OAuth authorization.</usage>
573                 </documentation>
574         </variable>
575 {/metadocument}
576 */
577         var $access_token_secret = '';
578
579 /*
580 {metadocument}
581         <variable>
582                 <name>access_token_expiry</name>
583                 <type>STRING</type>
584                 <value></value>
585                 <documentation>
586                         <purpose>Timestamp of the expiry of the access token obtained from
587                                 the OAuth server.</purpose>
588                         <usage>Check this variable to get the obtained access token expiry
589                                 time upon successful OAuth authorization. If this variable is
590                                 empty, that means no expiry time was set.</usage>
591                 </documentation>
592         </variable>
593 {/metadocument}
594 */
595         var $access_token_expiry = '';
596
597 /*
598 {metadocument}
599         <variable>
600                 <name>access_token_type</name>
601                 <type>STRING</type>
602                 <value></value>
603                 <documentation>
604                         <purpose>Type of access token obtained from the OAuth server.</purpose>
605                         <usage>Check this variable to get the obtained access token type
606                                 upon successful OAuth authorization.</usage>
607                 </documentation>
608         </variable>
609 {/metadocument}
610 */
611         var $access_token_type = '';
612
613 /*
614 {metadocument}
615         <variable>
616                 <name>refresh_token</name>
617                 <type>STRING</type>
618                 <value></value>
619                 <documentation>
620                         <purpose>Refresh token obtained from the OAuth server</purpose>
621                         <usage>Check this variable to get the obtained refresh token upon
622                                 successful OAuth authorization.</usage>
623                 </documentation>
624         </variable>
625 {/metadocument}
626 */
627         var $refresh_token = '';
628
629 /*
630 {metadocument}
631         <variable>
632                 <name>access_token_error</name>
633                 <type>STRING</type>
634                 <value></value>
635                 <documentation>
636                         <purpose>Error message returned when a call to the API fails.</purpose>
637                         <usage>Check this variable to determine if there was an error while
638                                 calling the Web services API when using the
639                                 <functionlink>CallAPI</functionlink> function.</usage>
640                 </documentation>
641         </variable>
642 {/metadocument}
643 */
644         var $access_token_error = '';
645
646 /*
647 {metadocument}
648         <variable>
649                 <name>authorization_error</name>
650                 <type>STRING</type>
651                 <value></value>
652                 <documentation>
653                         <purpose>Error message returned when it was not possible to obtain
654                                 an OAuth access token</purpose>
655                         <usage>Check this variable to determine if there was an error while
656                                 trying to obtain the OAuth access token.</usage>
657                 </documentation>
658         </variable>
659 {/metadocument}
660 */
661         var $authorization_error = '';
662
663 /*
664 {metadocument}
665         <variable>
666                 <name>response_status</name>
667                 <type>INTEGER</type>
668                 <value>0</value>
669                 <documentation>
670                         <purpose>HTTP response status returned by the server when calling an
671                                 API</purpose>
672                         <usage>Check this variable after calling the
673                                 <functionlink>CallAPI</functionlink> function if the API calls and you
674                                 need to process the error depending the response status.
675                                 <integervalue>200</integervalue> means no error. 
676                                 <integervalue>0</integervalue> means the server response was not
677                                 retrieved.</usage>
678                 </documentation>
679         </variable>
680 {/metadocument}
681 */
682         var $response_status = 0;
683
684         var $oauth_user_agent = 'PHP-OAuth-API (http://www.phpclasses.org/oauth-api $Revision: 1.58 $)';
685         var $session_started = false;
686
687         Function SetError($error)
688         {
689                 $this->error = $error;
690                 if($this->debug)
691                         $this->OutputDebug('Error: '.$error);
692                 return(false);
693         }
694
695         Function SetPHPError($error, &$php_error_message)
696         {
697                 if(IsSet($php_error_message)
698                 && strlen($php_error_message))
699                         $error.=": ".$php_error_message;
700                 return($this->SetError($error));
701         }
702
703         Function OutputDebug($message)
704         {
705                 if($this->debug)
706                 {
707                         $message = $this->debug_prefix.$message;
708                         $this->debug_output .= $message."\n";;
709                         error_log($message);
710                 }
711                 return(true);
712         }
713
714         Function GetRequestTokenURL(&$request_token_url)
715         {
716                 $request_token_url = $this->request_token_url;
717                 return(true);
718         }
719
720         Function GetDialogURL(&$url, $redirect_uri = '', $state = '')
721         {
722                 $url = (($this->offline && strlen($this->offline_dialog_url)) ? $this->offline_dialog_url : $this->dialog_url);
723                 if(strlen($url) === 0)
724                         return $this->SetError('the dialog URL '.($this->offline ? 'for offline access ' : '').'is not defined for this server');
725                 $url = str_replace(
726                         '{REDIRECT_URI}', UrlEncode($redirect_uri), str_replace(
727                         '{STATE}', UrlEncode($state), str_replace(
728                         '{CLIENT_ID}', UrlEncode($this->client_id), str_replace(
729                         '{SCOPE}', UrlEncode($this->scope),
730                         $url))));
731                 return(true);
732         }
733
734         Function GetAccessTokenURL(&$access_token_url)
735         {
736                 $access_token_url = $this->access_token_url;
737                 return(true);
738         }
739
740         Function GetStoredState(&$state)
741         {
742                 if(!$this->session_started)
743                 {
744                         if(!function_exists('session_start'))
745                                 return $this->SetError('Session variables are not accessible in this PHP environment');
746                 }
747                 if(IsSet($_SESSION['OAUTH_STATE']))
748                         $state = $_SESSION['OAUTH_STATE'];
749                 else
750                         $state = $_SESSION['OAUTH_STATE'] = time().'-'.substr(md5(rand().time()), 0, 6);
751                 return(true);
752         }
753
754         Function GetRequestState(&$state)
755         {
756                 $check = (strlen($this->append_state_to_redirect_uri) ? $this->append_state_to_redirect_uri : 'state');
757                 $state = (IsSet($_GET[$check]) ? $_GET[$check] : null);
758                 return(true);
759         }
760
761         Function GetRequestCode(&$code)
762         {
763                 $code = (IsSet($_GET['code']) ? $_GET['code'] : null);
764                 return(true);
765         }
766
767         Function GetRequestError(&$error)
768         {
769                 $error = (IsSet($_GET['error']) ? $_GET['error'] : null);
770                 return(true);
771         }
772
773         Function GetRequestDenied(&$denied)
774         {
775                 $denied = (IsSet($_GET['denied']) ? $_GET['denied'] : null);
776                 return(true);
777         }
778
779         Function GetRequestToken(&$token, &$verifier)
780         {
781                 $token = (IsSet($_GET['oauth_token']) ? $_GET['oauth_token'] : null);
782                 $verifier = (IsSet($_GET['oauth_verifier']) ? $_GET['oauth_verifier'] : null);
783                 return(true);
784         }
785
786         Function GetRedirectURI(&$redirect_uri)
787         {
788                 if(strlen($this->redirect_uri))
789                         $redirect_uri = $this->redirect_uri;
790                 else
791                         $redirect_uri = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
792                 return true;
793         }
794
795 /*
796 {metadocument}
797         <function>
798                 <name>StoreAccessToken</name>
799                 <type>BOOLEAN</type>
800                 <documentation>
801                         <purpose>Store the values of the access token when it is succefully
802                                 retrieved from the OAuth server.</purpose>
803                         <usage>This function is meant to be only be called from inside the
804                                 class. By default it stores access tokens in a session variable
805                                 named <stringvalue>OAUTH_ACCESS_TOKEN</stringvalue>.<paragraphbreak />
806                                 Actual implementations should create a sub-class and override this
807                                 function to make the access token values be stored in other types
808                                 of containers, like for instance databases.</usage>
809                         <returnvalue>This function should return
810                                 <booleanvalue>1</booleanvalue> if the access token was stored
811                                 successfully.</returnvalue>
812                 </documentation>
813                 <argument>
814                         <name>access_token</name>
815                         <type>HASH</type>
816                         <documentation>
817                                 <purpose>Associative array with properties of the access token. 
818                                         The array may have set the following
819                                         properties:<paragraphbreak />
820                                         <stringvalue>value</stringvalue>: string value of the access
821                                                 token<paragraphbreak />
822                                         <stringvalue>authorized</stringvalue>: boolean value that
823                                                 determines if the access token was obtained
824                                                 successfully<paragraphbreak />
825                                         <stringvalue>expiry</stringvalue>: (optional) timestamp in ISO
826                                                 format relative to UTC time zone of the access token expiry
827                                                 time<paragraphbreak />
828                                         <stringvalue>type</stringvalue>: (optional) type of OAuth token
829                                                 that may determine how it should be used when sending API call
830                                                 requests.<paragraphbreak />
831                                         <stringvalue>refresh</stringvalue>: (optional) token that some
832                                                 servers may set to allowing refreshing access tokens when they
833                                                 expire.</purpose>
834                         </documentation>
835                 </argument>
836                 <do>
837 {/metadocument}
838 */
839         Function StoreAccessToken($access_token)
840         {
841                 if(!$this->session_started)
842                 {
843                         if(!function_exists('session_start'))
844                                 return $this->SetError('Session variables are not accessible in this PHP environment');
845                 }
846                 $_SESSION['OAUTH_ACCESS_TOKEN'][$this->access_token_url] = $access_token;
847                 return true;
848         }
849 /*
850 {metadocument}
851                 </do>
852         </function>
853 {/metadocument}
854 */
855
856 /*
857 {metadocument}
858         <function>
859                 <name>GetAccessToken</name>
860                 <type>BOOLEAN</type>
861                 <documentation>
862                         <purpose>Retrieve the OAuth access token if it was already
863                                 previously stored by the
864                                 <functionlink>StoreAccessToken</functionlink> function.</purpose>
865                         <usage>This function is meant to be only be called from inside the
866                                 class. By default it retrieves access tokens stored in a session
867                                 variable named
868                                 <stringvalue>OAUTH_ACCESS_TOKEN</stringvalue>.<paragraphbreak />
869                                 Actual implementations should create a sub-class and override this
870                                 function to retrieve the access token values from other types of
871                                 containers, like for instance databases.</usage>
872                         <returnvalue>This function should return
873                                 <booleanvalue>1</booleanvalue> if the access token was retrieved
874                                 successfully.</returnvalue>
875                 </documentation>
876                 <argument>
877                         <name>access_token</name>
878                         <type>STRING</type>
879                         <out />
880                         <documentation>
881                                 <purpose>Return the properties of the access token in an
882                                         associative array. If the access token was not yet stored, it
883                                         returns an empty array. Otherwise, the properties it may return
884                                         are the same that may be passed to the
885                                         <functionlink>StoreAccessToken</functionlink>.</purpose>
886                         </documentation>
887                 </argument>
888                 <do>
889 {/metadocument}
890 */
891         Function GetAccessToken(&$access_token)
892         {
893                 if(!$this->session_started)
894                 {
895                         if(!function_exists('session_start'))
896                                 return $this->SetError('Session variables are not accessible in this PHP environment');
897                         if(!session_start())
898                                 return($this->SetPHPError('it was not possible to start the PHP session', $php_error_message));
899                         $this->session_started = true;
900                 }
901                 if(IsSet($_SESSION['OAUTH_ACCESS_TOKEN'][$this->access_token_url]))
902                         $access_token = $_SESSION['OAUTH_ACCESS_TOKEN'][$this->access_token_url];
903                 else
904                         $access_token = array();
905                 return true;
906         }
907 /*
908 {metadocument}
909                 </do>
910         </function>
911 {/metadocument}
912 */
913
914 /*
915 {metadocument}
916         <function>
917                 <name>ResetAccessToken</name>
918                 <type>BOOLEAN</type>
919                 <documentation>
920                         <purpose>Reset the access token to a state back when the user has
921                                 not yet authorized the access to the OAuth server API.</purpose>
922                         <usage>Call this function if for some reason the token to access
923                                 the API was revoked and you need to ask the user to authorize
924                                 the access again.<paragraphbreak />
925                                 By default the class stores and retrieves access tokens in a
926                                 session variable named
927                                 <stringvalue>OAUTH_ACCESS_TOKEN</stringvalue>.<paragraphbreak />
928                                 This function must be called when the user is accessing your site
929                                 pages, so it can reset the information stored in session variables
930                                 that cache the state of a previously retrieved access
931                                 token.<paragraphbreak />
932                                 Actual implementations should create a sub-class and override this
933                                 function to reset the access token state when it is stored in
934                                 other types of containers, like for instance databases.</usage>
935                         <returnvalue>This function should return
936                                 <booleanvalue>1</booleanvalue> if the access token was resetted
937                                 successfully.</returnvalue>
938                 </documentation>
939                 <do>
940 {/metadocument}
941 */
942         Function ResetAccessToken()
943         {
944                 if($this->debug)
945                         $this->OutputDebug('Resetting the access token status for OAuth server located at '.$this->access_token_url);
946                 if(!$this->session_started)
947                 {
948                         if(!function_exists('session_start'))
949                                 return $this->SetError('Session variables are not accessible in this PHP environment');
950                         if(!session_start())
951                                 return($this->SetPHPError('it was not possible to start the PHP session', $php_error_message));
952                 }
953                 $this->session_started = true;
954                 if(IsSet($_SESSION['OAUTH_ACCESS_TOKEN'][$this->access_token_url]))
955                         Unset($_SESSION['OAUTH_ACCESS_TOKEN'][$this->access_token_url]);
956                 return true;
957         }
958 /*
959 {metadocument}
960                 </do>
961         </function>
962 {/metadocument}
963 */
964
965         Function Encode($value)
966         {
967                 return(is_array($value) ? $this->EncodeArray($value) : str_replace('%7E', '~', str_replace('+',' ', RawURLEncode($value))));
968         }
969
970         Function EncodeArray($array)
971         {
972                 foreach($array as $key => $value)
973                         $array[$key] = $this->Encode($value);
974                 return $array;
975         }
976
977         Function HMAC($function, $data, $key)
978         {
979                 switch($function)
980                 {
981                         case 'sha1':
982                                 $pack = 'H40';
983                                 break;
984                         default:
985                                 if($this->debug)
986                                         $this->OutputDebug($function.' is not a supported an HMAC hash type');
987                                 return('');
988                 }
989                 if(strlen($key) > 64)
990                         $key = pack($pack, $function($key));
991                 if(strlen($key) < 64)
992                         $key = str_pad($key, 64, "\0");
993                 return(pack($pack, $function((str_repeat("\x5c", 64) ^ $key).pack($pack, $function((str_repeat("\x36", 64) ^ $key).$data)))));
994         }
995
996         Function SendAPIRequest($url, $method, $parameters, $oauth, $options, &$response)
997         {
998                 $this->response_status = 0;
999                 $http = new http_class;
1000                 $http->debug = ($this->debug && $this->debug_http);
1001                 $http->log_debug = true;
1002                 $http->sasl_authenticate = 0;
1003                 $http->user_agent = $this->oauth_user_agent;
1004                 $http->redirection_limit = (IsSet($options['FollowRedirection']) ? intval($options['FollowRedirection']) : 0);
1005                 $http->follow_redirect = ($http->redirection_limit != 0);
1006                 if($this->debug)
1007                         $this->OutputDebug('Accessing the '.$options['Resource'].' at '.$url);
1008                 $post_files = array();
1009                 $method = strtoupper($method);
1010                 $authorization = '';
1011                 $type = (IsSet($options['RequestContentType']) ? strtolower(trim(strtok($options['RequestContentType'], ';'))) : 'application/x-www-form-urlencoded');
1012                 if(IsSet($oauth))
1013                 {
1014                         $values = array(
1015                                 'oauth_consumer_key'=>$this->client_id,
1016                                 'oauth_nonce'=>md5(uniqid(rand(), true)),
1017                                 'oauth_signature_method'=>$this->signature_method,
1018                                 'oauth_timestamp'=>time(),
1019                                 'oauth_version'=>'1.0',
1020                         );
1021                         $files = (IsSet($options['Files']) ? $options['Files'] : array());
1022                         if(count($files))
1023                         {
1024                                 foreach($files as $name => $value)
1025                                 {
1026                                         if(!IsSet($parameters[$name]))
1027                                                 return($this->SetError('it was specified an file parameters named '.$name));
1028                                         $file = array();
1029                                         switch(IsSet($value['Type']) ? $value['Type'] : 'FileName')
1030                                         {
1031                                                 case 'FileName':
1032                                                         $file['FileName'] = $parameters[$name];
1033                                                         break;
1034                                                 case 'Data':
1035                                                         $file['Data'] = $parameters[$name];
1036                                                         break;
1037                                                 default:
1038                                                         return($this->SetError($value['Type'].' is not a valid type for file '.$name));
1039                                         }
1040                                         $file['ContentType'] = (IsSet($value['Content-Type']) ? $value['Content-Type'] : 'automatic/name');
1041                                         $post_files[$name] = $file;
1042                                 }
1043                                 UnSet($parameters[$name]);
1044                                 if($method !== 'POST')
1045                                 {
1046                                         $this->OutputDebug('For uploading files the method should be POST not '.$method);
1047                                         $method = 'POST';
1048                                 }
1049                                 if($type !== 'multipart/form-data')
1050                                 {
1051                                         if(IsSet($options['RequestContentType']))
1052                                                 return($this->SetError('the request content type for uploading files should be multipart/form-data'));
1053                                         $type = 'multipart/form-data';
1054                                 }
1055                                 $value_parameters = array();
1056                         }
1057                         else
1058                         {
1059                                 if($this->url_parameters
1060                                 && $type === 'application/x-www-form-urlencoded'
1061                                 && count($parameters))
1062                                 {
1063                                         $first = (strpos($url, '?') === false);
1064                                         foreach($parameters as $parameter => $value)
1065                                         {
1066                                                 $url .= ($first ? '?' : '&').UrlEncode($parameter).'='.UrlEncode($value);
1067                                                 $first = false;
1068                                         }
1069                                         $parameters = array();
1070                                 }
1071                                 $value_parameters = ($type !== 'application/x-www-form-urlencoded' ? array() : $parameters);
1072                         }
1073                         $values = array_merge($values, $oauth, $value_parameters);
1074                         $key = $this->Encode($this->client_secret).'&'.$this->Encode($this->access_token_secret);
1075                         switch($this->signature_method)
1076                         {
1077                                 case 'PLAINTEXT':
1078                                         $values['oauth_signature'] = $key;
1079                                         break;
1080                                 case 'HMAC-SHA1':
1081                                         $uri = strtok($url, '?');
1082                                         $sign = $method.'&'.$this->Encode($uri).'&';
1083                                         $first = true;
1084                                         $sign_values = $values;
1085                                         $u = parse_url($url);
1086                                         if(IsSet($u['query']))
1087                                         {
1088                                                 parse_str($u['query'], $q);
1089                                                 foreach($q as $parameter => $value)
1090                                                         $sign_values[$parameter] = $value;
1091                                         }
1092                                         KSort($sign_values);
1093                                         foreach($sign_values as $parameter => $value)
1094                                         {
1095                                                 $sign .= $this->Encode(($first ? '' : '&').$parameter.'='.$this->Encode($value));
1096                                                 $first = false;
1097                                         }
1098                                         $values['oauth_signature'] = base64_encode($this->HMAC('sha1', $sign, $key));
1099                                         break;
1100                                 default:
1101                                         return $this->SetError($this->signature_method.' signature method is not yet supported');
1102                         }
1103                         if($this->authorization_header)
1104                         {
1105                                 $authorization = 'OAuth';
1106                                 $first = true;
1107                                 foreach($values as $parameter => $value)
1108                                 {
1109                                         $authorization .= ($first ? ' ' : ',').$parameter.'="'.$this->Encode($value).'"';
1110                                         $first = false;
1111                                 }
1112                         }
1113                         else
1114                         {
1115                                 if($method === 'GET'
1116                                 || (IsSet($options['PostValuesInURI'])
1117                                 && $options['PostValuesInURI']))
1118                                 {
1119                                         $first = (strcspn($url, '?') == strlen($url));
1120                                         foreach($values as $parameter => $value)
1121                                         {
1122                                                 $url .= ($first ? '?' : '&').$parameter.'='.$this->Encode($value);
1123                                                 $first = false;
1124                                         }
1125                                         $post_values = array();
1126                                 }
1127                                 else
1128                                         $post_values = $values;
1129                         }
1130                 }
1131                 if(strlen($error = $http->GetRequestArguments($url, $arguments)))
1132                         return($this->SetError('it was not possible to open the '.$options['Resource'].' URL: '.$error));
1133                 if(strlen($error = $http->Open($arguments)))
1134                         return($this->SetError('it was not possible to open the '.$options['Resource'].' URL: '.$error));
1135                 if(count($post_files))
1136                         $arguments['PostFiles'] = $post_files;
1137                 $arguments['RequestMethod'] = $method;
1138                 switch($type)
1139                 {
1140                         case 'application/x-www-form-urlencoded':
1141                         case 'multipart/form-data':
1142                                 if(IsSet($options['RequestBody']))
1143                                         return($this->SetError('the request body is defined automatically from the parameters'));
1144                                 $arguments['PostValues'] = $parameters;
1145                                 break;
1146                         case 'application/json':
1147                                 $arguments['Headers']['Content-Type'] = $options['RequestContentType'];
1148                                 if(!IsSet($options['RequestBody']))
1149                                 {
1150                                         $arguments['Body'] = json_encode($parameters);
1151                                         break;
1152                                 }
1153                         default:
1154                                 if(!IsSet($options['RequestBody']))
1155                                         return($this->SetError('it was not specified the body value of the of the API call request'));
1156                                 $arguments['Headers']['Content-Type'] = $options['RequestContentType'];
1157                                 $arguments['Body'] = $options['RequestBody'];
1158                                 break;
1159                 }
1160                 $arguments['Headers']['Accept'] = (IsSet($options['Accept']) ? $options['Accept'] : '*/*');
1161                 if(strlen($authorization))
1162                         $arguments['Headers']['Authorization'] = $authorization;
1163                 if(strlen($error = $http->SendRequest($arguments))
1164                 || strlen($error = $http->ReadReplyHeaders($headers)))
1165                 {
1166                         $http->Close();
1167                         return($this->SetError('it was not possible to retrieve the '.$options['Resource'].': '.$error));
1168                 }
1169                 $error = $http->ReadWholeReplyBody($data);
1170                 $http->Close();
1171                 if(strlen($error))
1172                 {
1173                         return($this->SetError('it was not possible to access the '.$options['Resource'].': '.$error));
1174                 }
1175                 $this->response_status = intval($http->response_status);
1176                 $content_type = (IsSet($options['ResponseContentType']) ? $options['ResponseContentType'] : (IsSet($headers['content-type']) ? strtolower(trim(strtok($headers['content-type'], ';'))) : 'unspecified'));
1177                 switch($content_type)
1178                 {
1179                         case 'text/javascript':
1180                         case 'application/json':
1181                                 if(!function_exists('json_decode'))
1182                                         return($this->SetError('the JSON extension is not available in this PHP setup'));
1183                                 $object = json_decode($data);
1184                                 switch(GetType($object))
1185                                 {
1186                                         case 'object':
1187                                                 if(!IsSet($options['ConvertObjects'])
1188                                                 || !$options['ConvertObjects'])
1189                                                         $response = $object;
1190                                                 else
1191                                                 {
1192                                                         $response = array();
1193                                                         foreach($object as $property => $value)
1194                                                                 $response[$property] = $value;
1195                                                 }
1196                                                 break;
1197                                         case 'array':
1198                                                 $response = $object;
1199                                                 break;
1200                                         default:
1201                                                 if(!IsSet($object))
1202                                                         return($this->SetError('it was not returned a valid JSON definition of the '.$options['Resource'].' values'));
1203                                                 $response = $object;
1204                                                 break;
1205                                 }
1206                                 break;
1207                         case 'application/x-www-form-urlencoded':
1208                         case 'text/plain':
1209                         case 'text/html':
1210                                 parse_str($data, $response);
1211                                 break;
1212                         default:
1213                                 $response = $data;
1214                                 break;
1215                 }
1216                 if($this->response_status >= 200
1217                 && $this->response_status < 300)
1218                         $this->access_token_error = '';
1219                 else
1220                 {
1221                         $this->access_token_error = 'it was not possible to access the '.$options['Resource'].': it was returned an unexpected response status '.$http->response_status.' Response: '.$data;
1222                         if($this->debug)
1223                                 $this->OutputDebug('Could not retrieve the OAuth access. Error: '.$this->access_token_error);
1224                         if(IsSet($options['FailOnAccessError'])
1225                         && $options['FailOnAccessError'])
1226                         {
1227                                 $this->error = $this->access_token_error;
1228                                 return false;
1229                         }
1230                 }
1231                 return true;
1232         }
1233
1234         Function ProcessToken($code, $refresh)
1235         {
1236                 if($refresh)
1237                 {
1238                         $values = array(
1239                                 'client_id'=>$this->client_id,
1240                                 'client_secret'=>$this->client_secret,
1241                                 'refresh_token'=>$this->refresh_token,
1242                                 'grant_type'=>'refresh_token'
1243                         );
1244                 }
1245                 else
1246                 {
1247                         if(!$this->GetRedirectURI($redirect_uri))
1248                                 return false;
1249                         $values = array(
1250                                 'code'=>$code,
1251                                 'client_id'=>$this->client_id,
1252                                 'client_secret'=>$this->client_secret,
1253                                 'redirect_uri'=>$redirect_uri,
1254                                 'grant_type'=>'authorization_code'
1255                         );
1256                 }
1257                 if(!$this->GetAccessTokenURL($url))
1258                         return false;
1259                 if(!$this->SendAPIRequest($url, 'POST', $values, null, array('Resource'=>'OAuth '.($refresh ? 'refresh' : 'access').' token', 'ConvertObjects'=>true), $response))
1260                         return false;
1261                 if(strlen($this->access_token_error))
1262                 {
1263                         $this->authorization_error = $this->access_token_error;
1264                         return true;
1265                 }
1266                 if(!IsSet($response['access_token']))
1267                 {
1268                         if(IsSet($response['error']))
1269                         {
1270                                 $this->authorization_error = 'it was not possible to retrieve the access token: it was returned the error: '.$response['error'];
1271                                 return true;
1272                         }
1273                         return($this->SetError('OAuth server did not return the access token'));
1274                 }
1275                 $access_token = array(
1276                         'value'=>$this->access_token = $response['access_token'],
1277                         'authorized'=>true
1278                 );
1279                 if($this->debug)
1280                         $this->OutputDebug('Access token: '.$this->access_token);
1281                 if(IsSet($response['expires'])
1282                 || IsSet($response['expires_in']))
1283                 {
1284                         $expires = (IsSet($response['expires']) ? $response['expires'] : $response['expires_in']);
1285                         if(strval($expires) !== strval(intval($expires))
1286                         || $expires <= 0)
1287                                 return($this->SetError('OAuth server did not return a supported type of access token expiry time'));
1288                         $this->access_token_expiry = gmstrftime('%Y-%m-%d %H:%M:%S', time() + $expires);
1289                         if($this->debug)
1290                                 $this->OutputDebug('Access token expiry: '.$this->access_token_expiry.' UTC');
1291                         $access_token['expiry'] = $this->access_token_expiry;
1292                 }
1293                 else
1294                         $this->access_token_expiry = '';
1295                 if(IsSet($response['token_type']))
1296                 {
1297                         $this->access_token_type = $response['token_type'];
1298                         if($this->debug)
1299                                 $this->OutputDebug('Access token type: '.$this->access_token_type);
1300                         $access_token['type'] = $this->access_token_type;
1301                 }
1302                 else
1303                         $this->access_token_type = '';
1304                 if($refresh)
1305                         $response['refresh_token'] = $this->refresh_token;
1306                 elseif(IsSet($response['refresh_token']))
1307                 {
1308                         $this->refresh_token = $response['refresh_token'];
1309                         if($this->debug)
1310                                 $this->OutputDebug('Refresh token: '.$this->refresh_token);
1311                         $access_token['refresh'] = $this->refresh_token;
1312                 }
1313                 else
1314                         $this->refresh_token = '';
1315                 if(!$this->StoreAccessToken($access_token))
1316                         return false;
1317                 return true;
1318         }
1319
1320         Function RetrieveToken(&$valid)
1321         {
1322                 $valid = false;
1323                 if(!$this->GetAccessToken($access_token))
1324                         return false;
1325                 if(IsSet($access_token['value']))
1326                 {
1327                         $this->access_token_expiry = '';
1328                         if(IsSet($access_token['expiry'])
1329                         && strcmp($this->access_token_expiry = $access_token['expiry'], gmstrftime('%Y-%m-%d %H:%M:%S')) < 0)
1330                         {
1331                                 $this->access_token = '';
1332                                 if($this->debug)
1333                                         $this->OutputDebug('The OAuth access token expired in '.$this->access_token_expiry);
1334                         }
1335                         else
1336                         {
1337                                 $this->access_token = $access_token['value'];
1338                                 if(IsSet($access_token['type']))
1339                                         $this->access_token_type = $access_token['type'];
1340                                 else
1341                                         $this->access_token_type = '';
1342                                 if($this->debug)
1343                                         $this->OutputDebug('The OAuth access token '.$this->access_token.' is valid');
1344                                 if(strlen($this->access_token_type)
1345                                 && $this->debug)
1346                                         $this->OutputDebug('The OAuth access token is of type '.$this->access_token_type);
1347                                 if(IsSet($access_token['refresh']))
1348                                         $this->refresh_token = $access_token['refresh'];
1349                                 else
1350                                         $this->refresh_token = '';
1351                                 $valid = true;
1352                         }
1353                 }
1354                 return true;
1355         }
1356 /*
1357 {metadocument}
1358         <function>
1359                 <name>CallAPI</name>
1360                 <type>BOOLEAN</type>
1361                 <documentation>
1362                         <purpose>Send a HTTP request to the Web services API using a
1363                                 previously obtained authorization token via OAuth.</purpose>
1364                         <usage>This function can be used to call an API after having
1365                                 previously obtained an access token through the OAuth protocol
1366                                 using the <functionlink>Process</functionlink> function, or by
1367                                 directly setting the variables
1368                                 <variablelink>access_token</variablelink>, as well as
1369                                 <variablelink>access_token_secret</variablelink> in case of using
1370                                 OAuth 1.0 or 1.0a services.</usage>
1371                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
1372                                 the call was done successfully.</returnvalue>
1373                 </documentation>
1374                 <argument>
1375                         <name>url</name>
1376                         <type>STRING</type>
1377                         <documentation>
1378                                 <purpose>URL of the API where the HTTP request will be sent.</purpose>
1379                         </documentation>
1380                 </argument>
1381                 <argument>
1382                         <name>method</name>
1383                         <type>STRING</type>
1384                         <documentation>
1385                                 <purpose>HTTP method that will be used to send the request. It can
1386                                 be <stringvalue>GET</stringvalue>,
1387                                 <stringvalue>POST</stringvalue>,
1388                                 <stringvalue>DELETE</stringvalue>, <stringvalue>PUT</stringvalue>,
1389                                 etc..</purpose>
1390                         </documentation>
1391                 </argument>
1392                 <argument>
1393                         <name>parameters</name>
1394                         <type>HASH</type>
1395                         <documentation>
1396                                 <purpose>Associative array with the names and values of the API
1397                                         call request parameters.</purpose>
1398                         </documentation>
1399                 </argument>
1400                 <argument>
1401                         <name>options</name>
1402                         <type>HASH</type>
1403                         <documentation>
1404                                 <purpose>Associative array with additional options to configure
1405                                         the request. Currently it supports the following
1406                                         options:<paragraphbreak />
1407                                         <stringvalue>2Legged</stringvalue>: boolean option that
1408                                                 determines if the API request should be 2 legged. The default
1409                                                 value is <tt><booleanvalue>0</booleanvalue></tt>.<paragraphbreak />
1410                                         <stringvalue>Accept</stringvalue>: content type value of the
1411                                                 Accept HTTP header to be sent in the API call HTTP request.
1412                                                 Some APIs require that a certain value be sent to specify
1413                                                 which version of the API is being called. The default value is
1414                                                 <stringvalue>*&#47;*</stringvalue>.<paragraphbreak />
1415                                         <stringvalue>ConvertObjects</stringvalue>: boolean option that
1416                                                 determines if objects should be converted into arrays when the
1417                                                 response is returned in JSON format. The default value is
1418                                                 <booleanvalue>0</booleanvalue>.<paragraphbreak />
1419                                         <stringvalue>FailOnAccessError</stringvalue>: boolean option
1420                                                 that determines if this functions should fail when the server
1421                                                 response status is not between 200 and 299. The default value
1422                                                 is <booleanvalue>0</booleanvalue>.<paragraphbreak />
1423                                         <stringvalue>Files</stringvalue>: associative array with
1424                                                 details of the parameters that must be passed as file uploads.
1425                                                 The array indexes must have the same name of the parameters
1426                                                 to be sent as files. The respective array entry values must
1427                                                 also be associative arrays with the parameters for each file.
1428                                                 Currently it supports the following parameters:<paragraphbreak />
1429                                                 - <tt>Type</tt> - defines how the parameter value should be
1430                                                 treated. It can be <tt>'FileName'</tt> if the parameter value is
1431                                                 is the name of a local file to be uploaded. It may also be
1432                                                 <tt>'Data'</tt> if the parameter value is the actual data of
1433                                                 the file to be uploaded.<paragraphbreak />
1434                                                 - Default: <tt>'FileName'</tt><paragraphbreak />
1435                                                 - <tt>ContentType</tt> - MIME value of the content type of the
1436                                                 file. It can be <tt>'automatic/name'</tt> if the content type
1437                                                 should be determine from the file name extension.<paragraphbreak />
1438                                                 - Default: <tt>'automatic/name'</tt><paragraphbreak />
1439                                         <stringvalue>PostValuesInURI</stringvalue>: boolean option to
1440                                                 determine that a POST request should pass the request values
1441                                                 in the URI. The default value is
1442                                                 <booleanvalue>0</booleanvalue>.<paragraphbreak />
1443                                         <stringvalue>FollowRedirection</stringvalue>: limit number of
1444                                                 times that HTTP response redirects will be followed. If it is
1445                                                 set to <integervalue>0</integervalue>, redirection responses
1446                                                 fail in error. The default value is
1447                                                 <integervalue>0</integervalue>.<paragraphbreak />
1448                                         <stringvalue>RequestBody</stringvalue>: request body data of a
1449                                                 custom type. The <stringvalue>RequestContentType</stringvalue>
1450                                                 option must be specified, so the
1451                                                 <stringvalue>RequestBody</stringvalue> option is considered.<paragraphbreak />
1452                                         <stringvalue>RequestContentType</stringvalue>: content type that
1453                                                 should be used to send the request values. It can be either
1454                                                 <stringvalue>application/x-www-form-urlencoded</stringvalue>
1455                                                 for sending values like from Web forms, or
1456                                                 <stringvalue>application/json</stringvalue> for sending the
1457                                                 values encoded in JSON format. Other types are accepted if the
1458                                                 <stringvalue>RequestBody</stringvalue> option is specified.
1459                                                 The default value is
1460                                                 <stringvalue>application/x-www-form-urlencoded</stringvalue>.<paragraphbreak />
1461                                         <stringvalue>RequestBody</stringvalue>: request body data of a
1462                                                 custom type. The <stringvalue>RequestContentType</stringvalue>
1463                                                 option must be specified, so the
1464                                                 <stringvalue>RequestBody</stringvalue> option is considered.<paragraphbreak />
1465                                         <stringvalue>Resource</stringvalue>: string with a label that
1466                                                 will be used in the error messages and debug log entries to
1467                                                 identify what operation the request is performing. The default
1468                                                 value is <stringvalue>API call</stringvalue>.<paragraphbreak />
1469                                         <stringvalue>ResponseContentType</stringvalue>: content type
1470                                                 that should be considered when decoding the API request
1471                                                 response. This overrides the <tt>Content-Type</tt> header
1472                                                 returned by the server. If the content type is 
1473                                                 <stringvalue>application/x-www-form-urlencoded</stringvalue>
1474                                                 the function will parse the data returning an array of
1475                                                 key-value pairs. If the content type is 
1476                                                 <stringvalue>application/json</stringvalue> the response will
1477                                                 be decode as a JSON-encoded data type. Other content type
1478                                                 values will make the function return the original response
1479                                                 value as it was returned from the server. The default value
1480                                                 for this option is to use what the server returned in the
1481                                                 <tt>Content-Type</tt> header.</purpose>
1482                         </documentation>
1483                 </argument>
1484                 <argument>
1485                         <name>response</name>
1486                         <type>STRING</type>
1487                         <out />
1488                         <documentation>
1489                                 <purpose>Return the value of the API response. If the value is
1490                                         JSON encoded, this function will decode it and return the value
1491                                         converted to respective types. If the value is form encoded,
1492                                         this function will decode the response and return it as an
1493                                         array. Otherwise, the class will return the value as a
1494                                         string.</purpose>
1495                         </documentation>
1496                 </argument>
1497                 <do>
1498 {/metadocument}
1499 */
1500         Function CallAPI($url, $method, $parameters, $options, &$response)
1501         {
1502                 if(!IsSet($options['Resource']))
1503                         $options['Resource'] = 'API call';
1504                 if(!IsSet($options['ConvertObjects']))
1505                         $options['ConvertObjects'] = false;
1506                 if(strlen($this->access_token) === 0)
1507                 {
1508                         if(!$this->RetrieveToken($valid))
1509                                 return false;
1510                         if(!$valid)
1511                                 return $this->SetError('the access token is not set to a valid value');
1512                 }
1513                 switch(intval($this->oauth_version))
1514                 {
1515                         case 1:
1516                                 $oauth = array(
1517                                         'oauth_token'=>((IsSet($options['2Legged']) && $options['2Legged']) ? '' : $this->access_token)
1518                                 );
1519                                 break;
1520
1521                         case 2:
1522                                 if(strlen($this->access_token_expiry)
1523                                 && strcmp($this->access_token_expiry, gmstrftime('%Y-%m-%d %H:%M:%S')) <= 0)
1524                                 {
1525                                         if(strlen($this->refresh_token) === 0)
1526                                                 return($this->SetError('the access token expired and no refresh token is available'));
1527                                         if($this->debug)
1528                                         {
1529                                                 $this->OutputDebug('The access token expired on '.$this->access_token_expiry);
1530                                                 $this->OutputDebug('Refreshing the access token');
1531                                         }
1532                                         if(!$this->ProcessToken(null, true))
1533                                                 return false;
1534                                 }
1535                                 $oauth = null;
1536                                 $url .= (strcspn($url, '?') < strlen($url) ? '&' : '?').'access_token='.UrlEncode($this->access_token);
1537                                 break;
1538
1539                         default:
1540                                 return($this->SetError($this->oauth_version.' is not a supported version of the OAuth protocol'));
1541                 }
1542                 return($this->SendAPIRequest($url, $method, $parameters, $oauth, $options, $response));
1543         }
1544 /*
1545 {metadocument}
1546                 </do>
1547         </function>
1548 {/metadocument}
1549 */
1550
1551 /*
1552 {metadocument}
1553         <function>
1554                 <name>Initialize</name>
1555                 <type>BOOLEAN</type>
1556                 <documentation>
1557                         <purpose>Initialize the class variables and internal state. It must
1558                                 be called before calling other class functions.</purpose>
1559                         <usage>Set the <variablelink>server</variablelink> variable before
1560                                 calling this function to let it initialize the class variables to
1561                                 work with the specified server type. Alternatively, you can set
1562                                 other class variables manually to make it work with servers that
1563                                 are not yet built-in supported.</usage>
1564                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
1565                                 it was able to successfully initialize the class for the specified
1566                                 server type.</returnvalue>
1567                 </documentation>
1568                 <do>
1569 {/metadocument}
1570 */
1571         Function Initialize()
1572         {
1573                 if(strlen($this->server) === 0)
1574                         return true;
1575                 $this->request_token_url = '';
1576                 $this->append_state_to_redirect_uri = '';
1577                 $this->authorization_header = true;
1578                 $this->url_parameters = false;
1579                 $this->token_request_method = 'GET';
1580                 $this->signature_method = 'HMAC-SHA1';
1581                 switch($this->server)
1582                 {
1583                         case 'Bitbucket':
1584                                 $this->oauth_version = '1.0a';
1585                                 $this->request_token_url = 'https://bitbucket.org/!api/1.0/oauth/request_token';
1586                                 $this->dialog_url = 'https://bitbucket.org/!api/1.0/oauth/authenticate';
1587                                 $this->access_token_url = 'https://bitbucket.org/!api/1.0/oauth/access_token';
1588                                 $this->url_parameters = true;
1589                                 break;
1590
1591                         case 'Box':
1592                                 $this->oauth_version = '2.0';
1593                                 $this->dialog_url = 'https://www.box.com/api/oauth2/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&state={STATE}';
1594                                 $this->offline_dialog_url = 'https://www.box.com/api/oauth2/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&state={STATE}&access_type=offline&approval_prompt=force';
1595                                 $this->access_token_url = 'https://www.box.com/api/oauth2/token';
1596                                 break;
1597
1598                         case 'Dropbox':
1599                                 $this->oauth_version = '1.0';
1600                                 $this->request_token_url = 'https://api.dropbox.com/1/oauth/request_token';
1601                                 $this->dialog_url = 'https://www.dropbox.com/1/oauth/authorize';
1602                                 $this->access_token_url = 'https://api.dropbox.com/1/oauth/access_token';
1603                                 $this->authorization_header = false;
1604                                 break;
1605
1606                         case 'Eventful':
1607                                 $this->oauth_version = '1.0a';
1608                                 $this->request_token_url = 'http://eventful.com/oauth/request_token';
1609                                 $this->dialog_url = 'http://eventful.com/oauth/authorize';
1610                                 $this->access_token_url = 'http://eventful.com/oauth/access_token';
1611                                 $this->authorization_header = false;
1612                                 $this->url_parameters = true;
1613                                 $this->token_request_method = 'POST';
1614                                 break;
1615
1616                         case 'Evernote':
1617                                 $this->oauth_version = '1.0a';
1618                                 $this->request_token_url = 'https://sandbox.evernote.com/oauth';
1619                                 $this->dialog_url = 'https://sandbox.evernote.com/OAuth.action';
1620                                 $this->access_token_url = 'https://sandbox.evernote.com/oauth';
1621                                 $this->url_parameters = true;
1622                                 $this->authorization_header = false;
1623                                 break;
1624
1625                         case 'Facebook':
1626                                 $this->oauth_version = '2.0';
1627                                 $this->dialog_url = 'https://www.facebook.com/dialog/oauth?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}';
1628                                 $this->access_token_url = 'https://graph.facebook.com/oauth/access_token';
1629                                 break;
1630
1631                         case 'Fitbit':
1632                                 $this->oauth_version = '1.0a';
1633                                 $this->request_token_url = 'http://api.fitbit.com/oauth/request_token';
1634                                 $this->dialog_url = 'http://api.fitbit.com/oauth/authorize';
1635                                 $this->access_token_url = 'http://api.fitbit.com/oauth/access_token';
1636                                 break;
1637
1638                         case 'Flickr':
1639                                 $this->oauth_version = '1.0a';
1640                                 $this->request_token_url = 'http://www.flickr.com/services/oauth/request_token';
1641                                 $this->dialog_url = 'http://www.flickr.com/services/oauth/authorize?perms={SCOPE}';
1642                                 $this->access_token_url = 'http://www.flickr.com/services/oauth/access_token';
1643                                 $this->authorization_header = false;
1644                                 break;
1645
1646                         case 'Foursquare':
1647                                 $this->oauth_version = '2.0';
1648                                 $this->dialog_url = 'https://foursquare.com/oauth2/authorize?client_id={CLIENT_ID}&scope={SCOPE}&response_type=code&redirect_uri={REDIRECT_URI}&state={STATE}';
1649                                 $this->access_token_url = 'https://foursquare.com/oauth2/access_token';
1650                                 break;
1651
1652                         case 'github':
1653                                 $this->oauth_version = '2.0';
1654                                 $this->dialog_url = 'https://github.com/login/oauth/authorize?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}';
1655                                 $this->access_token_url = 'https://github.com/login/oauth/access_token';
1656                                 break;
1657
1658                         case 'Google':
1659                                 $this->oauth_version = '2.0';
1660                                 $this->dialog_url = 'https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}';
1661                                 $this->offline_dialog_url = 'https://accounts.google.com/o/oauth2/auth?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}&access_type=offline&approval_prompt=force';
1662                                 $this->access_token_url = 'https://accounts.google.com/o/oauth2/token';
1663                                 break;
1664
1665                         case 'Instagram':
1666                                 $this->oauth_version = '2.0';
1667                                 $this->dialog_url ='https://api.instagram.com/oauth/authorize/?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&response_type=code&state={STATE}';
1668                                 $this->access_token_url = 'https://api.instagram.com/oauth/access_token';
1669                                 break;
1670
1671                         case 'LinkedIn':
1672                                 $this->oauth_version = '1.0a';
1673                                 $this->request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken?scope={SCOPE}';
1674                                 $this->dialog_url = 'https://api.linkedin.com/uas/oauth/authenticate';
1675                                 $this->access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken';
1676                                 $this->url_parameters = true;
1677                                 break;
1678
1679                         case 'Microsoft':
1680                                 $this->oauth_version = '2.0';
1681                                 $this->dialog_url = 'https://login.live.com/oauth20_authorize.srf?client_id={CLIENT_ID}&scope={SCOPE}&response_type=code&redirect_uri={REDIRECT_URI}&state={STATE}';
1682                                 $this->access_token_url = 'https://login.live.com/oauth20_token.srf';
1683                                 break;
1684
1685                         case 'RightSignature':
1686                                 $this->oauth_version = '1.0a';
1687                                 $this->request_token_url = 'https://rightsignature.com/oauth/request_token';
1688                                 $this->dialog_url = 'https://rightsignature.com/oauth/authorize';
1689                                 $this->access_token_url = 'https://rightsignature.com/oauth/access_token';
1690                                 $this->authorization_header = false;
1691                                 break;
1692
1693                         case 'Scoop.it':
1694                                 $this->oauth_version = '1.0a';
1695                                 $this->request_token_url = 'https://www.scoop.it/oauth/request';
1696                                 $this->dialog_url = 'https://www.scoop.it/oauth/authorize';
1697                                 $this->access_token_url = 'https://www.scoop.it/oauth/access';
1698                                 $this->authorization_header = false;
1699                                 break;
1700
1701                         case 'StockTwits':
1702                                 $this->oauth_version = '2.0';
1703                                 $this->dialog_url = 'https://api.stocktwits.com/api/2/oauth/authorize?client_id={CLIENT_ID}&response_type=code&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}';
1704                                 $this->access_token_url = 'https://api.stocktwits.com/api/2/oauth/token';
1705                                 break;
1706
1707                         case 'Tumblr':
1708                                 $this->oauth_version = '1.0a';
1709                                 $this->request_token_url = 'http://www.tumblr.com/oauth/request_token';
1710                                 $this->dialog_url = 'http://www.tumblr.com/oauth/authorize';
1711                                 $this->access_token_url = 'http://www.tumblr.com/oauth/access_token';
1712                                 break;
1713
1714                         case 'Twitter':
1715                                 $this->oauth_version = '1.0a';
1716                                 $this->request_token_url = 'https://api.twitter.com/oauth/request_token';
1717                                 $this->dialog_url = 'https://api.twitter.com/oauth/authenticate';
1718                                 $this->access_token_url = 'https://api.twitter.com/oauth/access_token';
1719                                 $this->url_parameters = true;
1720                                 break;
1721
1722                         case 'XING':
1723                                 $this->oauth_version = '1.0a';
1724                                 $this->request_token_url = 'https://api.xing.com/v1/request_token';
1725                                 $this->dialog_url = 'https://api.xing.com/v1/authorize';
1726                                 $this->access_token_url = 'https://api.xing.com/v1/access_token';
1727                                 $this->authorization_header = false;
1728                                 break;
1729
1730                         case 'Yahoo':
1731                                 $this->oauth_version = '1.0a';
1732                                 $this->request_token_url = 'https://api.login.yahoo.com/oauth/v2/get_request_token';
1733                                 $this->dialog_url = 'https://api.login.yahoo.com/oauth/v2/request_auth';
1734                                 $this->access_token_url = 'https://api.login.yahoo.com/oauth/v2/get_token';
1735                                 $this->authorization_header = false;
1736                                 break;
1737
1738                         default:
1739                                 return($this->SetError($this->server.' is not yet a supported type of OAuth server. Please contact the author Manuel Lemos <mlemos@acm.org> to request adding built-in support to this type of OAuth server.'));
1740                 }
1741                 return(true);
1742         }
1743 /*
1744 {metadocument}
1745                 </do>
1746         </function>
1747 {/metadocument}
1748 */
1749
1750 /*
1751 {metadocument}
1752         <function>
1753                 <name>Process</name>
1754                 <type>BOOLEAN</type>
1755                 <documentation>
1756                         <purpose>Process the OAuth protocol interaction with the OAuth
1757                                 server.</purpose>
1758                         <usage>Call this function when you need to retrieve the OAuth access
1759                                 token. Check the <variablelink>access_token</variablelink> to
1760                                 determine if the access token was obtained successfully.</usage>
1761                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
1762                                 the OAuth protocol was processed without errors.</returnvalue>
1763                 </documentation>
1764                 <do>
1765 {/metadocument}
1766 */
1767         Function Process()
1768         {
1769                 switch(intval($this->oauth_version))
1770                 {
1771                         case 1:
1772                                 $one_a = ($this->oauth_version === '1.0a');
1773                                 if($this->debug)
1774                                         $this->OutputDebug('Checking the OAuth token authorization state');
1775                                 if(!$this->GetAccessToken($access_token))
1776                                         return false;
1777                                 if(IsSet($access_token['authorized'])
1778                                 && IsSet($access_token['value']))
1779                                 {
1780                                         $expired = (IsSet($access_token['expiry']) && strcmp($access_token['expiry'], gmstrftime('%Y-%m-%d %H:%M:%S')) <= 0);
1781                                         if(!$access_token['authorized']
1782                                         || $expired)
1783                                         {
1784                                                 if($this->debug)
1785                                                 {
1786                                                         if($expired)
1787                                                                 $this->OutputDebug('The OAuth token expired on '.$access_token['expiry'].'UTC');
1788                                                         else
1789                                                                 $this->OutputDebug('The OAuth token is not yet authorized');
1790                                                         $this->OutputDebug('Checking the OAuth token and verifier');
1791                                                 }
1792                                                 if(!$this->GetRequestToken($token, $verifier))
1793                                                         return false;
1794                                                 if(!IsSet($token)
1795                                                 || ($one_a
1796                                                 && !IsSet($verifier)))
1797                                                 {
1798                                                         if(!$this->GetRequestDenied($denied))
1799                                                                 return false;
1800                                                         if(IsSet($denied)
1801                                                         && $denied === $access_token['value'])
1802                                                         {
1803                                                                 if($this->debug)
1804                                                                         $this->OutputDebug('The authorization request was denied');
1805                                                                 $this->authorization_error = 'the request was denied';
1806                                                                 return true;
1807                                                         }
1808                                                         else
1809                                                         {
1810                                                                 if($this->debug)
1811                                                                         $this->OutputDebug('Reset the OAuth token state because token and verifier are not both set');
1812                                                                 $access_token = array();
1813                                                         }
1814                                                 }
1815                                                 elseif($token !== $access_token['value'])
1816                                                 {
1817                                                         if($this->debug)
1818                                                                 $this->OutputDebug('Reset the OAuth token state because token does not match what as previously retrieved');
1819                                                         $access_token = array();
1820                                                 }
1821                                                 else
1822                                                 {
1823                                                         if(!$this->GetAccessTokenURL($url))
1824                                                                 return false;
1825                                                         $oauth = array(
1826                                                                 'oauth_token'=>$token,
1827                                                         );
1828                                                         if($one_a)
1829                                                                 $oauth['oauth_verifier'] = $verifier;
1830                                                         $this->access_token_secret = $access_token['secret'];
1831                                                         $options = array('Resource'=>'OAuth access token');
1832                                                         $method = strtoupper($this->token_request_method);
1833                                                         switch($method)
1834                                                         {
1835                                                                 case 'GET':
1836                                                                         break;
1837                                                                 case 'POST':
1838                                                                         $options['PostValuesInURI'] = true;
1839                                                                         break;
1840                                                                 default:
1841                                                                         $this->error = $method.' is not a supported method to request tokens';
1842                                                                         break;
1843                                                         }
1844                                                         if(!$this->SendAPIRequest($url, $method, array(), $oauth, $options, $response))
1845                                                                 return false;
1846                                                         if(strlen($this->access_token_error))
1847                                                         {
1848                                                                 $this->authorization_error = $this->access_token_error;
1849                                                                 return true;
1850                                                         }
1851                                                         if(!IsSet($response['oauth_token'])
1852                                                         || !IsSet($response['oauth_token_secret']))
1853                                                         {
1854                                                                 $this->authorization_error= 'it was not returned the access token and secret';
1855                                                                 return true;
1856                                                         }
1857                                                         $access_token = array(
1858                                                                 'value'=>$response['oauth_token'],
1859                                                                 'secret'=>$response['oauth_token_secret'],
1860                                                                 'authorized'=>true
1861                                                         );
1862                                                         if(IsSet($response['oauth_expires_in']))
1863                                                         {
1864                                                                 $expires = $response['oauth_expires_in'];
1865                                                                 if(strval($expires) !== strval(intval($expires))
1866                                                                 || $expires <= 0)
1867                                                                         return($this->SetError('OAuth server did not return a supported type of access token expiry time'));
1868                                                                 $this->access_token_expiry = gmstrftime('%Y-%m-%d %H:%M:%S', time() + $expires);
1869                                                                 if($this->debug)
1870                                                                         $this->OutputDebug('Access token expiry: '.$this->access_token_expiry.' UTC');
1871                                                                 $access_token['expiry'] = $this->access_token_expiry;
1872                                                         }
1873                                                         else
1874                                                                 $this->access_token_expiry = '';
1875
1876                                                         if(!$this->StoreAccessToken($access_token))
1877                                                                 return false;
1878                                                         if($this->debug)
1879                                                                 $this->OutputDebug('The OAuth token was authorized');
1880                                                 }
1881                                         }
1882                                         elseif($this->debug)
1883                                                 $this->OutputDebug('The OAuth token was already authorized');
1884                                         if(IsSet($access_token['authorized'])
1885                                         && $access_token['authorized'])
1886                                         {
1887                                                 $this->access_token = $access_token['value'];
1888                                                 $this->access_token_secret = $access_token['secret'];
1889                                                 return true;
1890                                         }
1891                                 }
1892                                 else
1893                                 {
1894                                         if($this->debug)
1895                                                 $this->OutputDebug('The OAuth access token is not set');
1896                                         $access_token = array();
1897                                 }
1898                                 if(!IsSet($access_token['authorized']))
1899                                 {
1900                                         if($this->debug)
1901                                                 $this->OutputDebug('Requesting the unauthorized OAuth token');
1902                                         if(!$this->GetRequestTokenURL($url))
1903                                                 return false;
1904                                         $url = str_replace('{SCOPE}', UrlEncode($this->scope), $url); 
1905                                         if(!$this->GetRedirectURI($redirect_uri))
1906                                                 return false;
1907                                         $oauth = array(
1908                                                 'oauth_callback'=>$redirect_uri,
1909                                         );
1910                                         $options = array('Resource'=>'OAuth request token');
1911                                         $method = strtoupper($this->token_request_method);
1912                                         switch($method)
1913                                         {
1914                                                 case 'GET':
1915                                                         break;
1916                                                 case 'POST':
1917                                                         $options['PostValuesInURI'] = true;
1918                                                         break;
1919                                                 default:
1920                                                         $this->error = $method.' is not a supported method to request tokens';
1921                                                         break;
1922                                         }
1923                                         if(!$this->SendAPIRequest($url, $method, array(), $oauth, $options, $response))
1924                                                 return false;
1925                                         if(strlen($this->access_token_error))
1926                                         {
1927                                                 $this->authorization_error = $this->access_token_error;
1928                                                 return true;
1929                                         }
1930                                         if(!IsSet($response['oauth_token'])
1931                                         || !IsSet($response['oauth_token_secret']))
1932                                         {
1933                                                 $this->authorization_error = 'it was not returned the requested token';
1934                                                 return true;
1935                                         }
1936                                         $access_token = array(
1937                                                 'value'=>$response['oauth_token'],
1938                                                 'secret'=>$response['oauth_token_secret'],
1939                                                 'authorized'=>false
1940                                         );
1941                                         if(!$this->StoreAccessToken($access_token))
1942                                                 return false;
1943                                 }
1944                                 if(!$this->GetDialogURL($url))
1945                                         return false;
1946                                 $url .= (strpos($url, '?') === false ? '?' : '&').'oauth_token='.$access_token['value'];
1947                                 if(!$one_a)
1948                                 {
1949                                         if(!$this->GetRedirectURI($redirect_uri))
1950                                                 return false;
1951                                         $url .= '&oauth_callback='.UrlEncode($redirect_uri);
1952                                 }
1953                                 if($this->debug)
1954                                         $this->OutputDebug('Redirecting to OAuth authorize page '.$url);
1955                                 Header('HTTP/1.0 302 OAuth Redirection');
1956                                 Header('Location: '.$url);
1957                                 $this->exit = true;
1958                                 return true;
1959
1960                         case 2:
1961                                 if($this->debug)
1962                                         $this->OutputDebug('Checking if OAuth access token was already retrieved from '.$this->access_token_url);
1963                                 if(!$this->RetrieveToken($valid))
1964                                         return false;
1965                                 if($valid)
1966                                         return true;
1967                                 if($this->debug)
1968                                         $this->OutputDebug('Checking the authentication state in URI '.$_SERVER['REQUEST_URI']);
1969                                 if(!$this->GetStoredState($stored_state))
1970                                         return false;
1971                                 if(strlen($stored_state) == 0)
1972                                         return($this->SetError('it was not set the OAuth state'));
1973                                 if(!$this->GetRequestState($state))
1974                                         return false;
1975                                 if($state === $stored_state)
1976                                 {
1977                                         if($this->debug)
1978                                                 $this->OutputDebug('Checking the authentication code');
1979                                         if(!$this->GetRequestCode($code))
1980                                                 return false;
1981                                         if(strlen($code) == 0)
1982                                         {
1983                                                 if(!$this->GetRequestError($this->authorization_error))
1984                                                         return false;
1985                                                 if(IsSet($this->authorization_error))
1986                                                 {
1987                                                         if($this->debug)
1988                                                                 $this->OutputDebug('Authorization failed with error code '.$this->authorization_error);
1989                                                         switch($this->authorization_error)
1990                                                         {
1991                                                                 case 'invalid_request':
1992                                                                 case 'unauthorized_client':
1993                                                                 case 'access_denied':
1994                                                                 case 'unsupported_response_type':
1995                                                                 case 'invalid_scope':
1996                                                                 case 'server_error':
1997                                                                 case 'temporarily_unavailable':
1998                                                                 case 'user_denied':
1999                                                                         return true;
2000                                                                 default:
2001                                                                         return($this->SetError('it was returned an unknown OAuth error code'));
2002                                                         }
2003                                                 }
2004                                                 return($this->SetError('it was not returned the OAuth dialog code'));
2005                                         }
2006                                         if(!$this->ProcessToken($code, false))
2007                                                 return false;
2008                                 }
2009                                 else
2010                                 {
2011                                         if(!$this->GetRedirectURI($redirect_uri))
2012                                                 return false;
2013                                         if(strlen($this->append_state_to_redirect_uri))
2014                                                 $redirect_uri .= (strpos($redirect_uri, '?') === false ? '?' : '&').$this->append_state_to_redirect_uri.'='.$stored_state;
2015                                         if(!$this->GetDialogURL($url, $redirect_uri, $stored_state))
2016                                                 return false;
2017                                         if(strlen($url) == 0)
2018                                                 return($this->SetError('it was not set the OAuth dialog URL'));
2019                                         if($this->debug)
2020                                                 $this->OutputDebug('Redirecting to OAuth Dialog '.$url);
2021                                         Header('HTTP/1.0 302 OAuth Redirection');
2022                                         Header('Location: '.$url);
2023                                         $this->exit = true;
2024                                 }
2025                                 break;
2026
2027                         default:
2028                                 return($this->SetError($this->oauth_version.' is not a supported version of the OAuth protocol'));
2029                 }
2030                 return(true);
2031         }
2032 /*
2033 {metadocument}
2034                 </do>
2035         </function>
2036 {/metadocument}
2037 */
2038
2039 /*
2040 {metadocument}
2041         <function>
2042                 <name>Finalize</name>
2043                 <type>BOOLEAN</type>
2044                 <documentation>
2045                         <purpose>Cleanup any resources that may have been used during the
2046                                 OAuth protocol processing or execution of API calls.</purpose>
2047                         <usage>Always call this function as the last step after calling the
2048                                 functions <functionlink>Process</functionlink> or
2049                                 <functionlink>CallAPI</functionlink>.</usage>
2050                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
2051                                 the function cleaned up any resources successfully.</returnvalue>
2052                 </documentation>
2053                 <argument>
2054                         <name>success</name>
2055                         <type>BOOLEAN</type>
2056                         <documentation>
2057                                 <purpose>Pass the last success state returned by the class or any
2058                                         external code processing the class function results.</purpose>
2059                         </documentation>
2060                 </argument>
2061                 <do>
2062 {/metadocument}
2063 */
2064         Function Finalize($success)
2065         {
2066                 return($success);
2067         }
2068 /*
2069 {metadocument}
2070                 </do>
2071         </function>
2072 {/metadocument}
2073 */
2074
2075 /*
2076 {metadocument}
2077         <function>
2078                 <name>Output</name>
2079                 <type>VOID</type>
2080                 <documentation>
2081                         <purpose>Display the results of the OAuth protocol processing.</purpose>
2082                         <usage>Only call this function if you are debugging the OAuth
2083                                 authorization process and you need to view what was its
2084                                 results.</usage>
2085                 </documentation>
2086                 <do>
2087 {/metadocument}
2088 */
2089         Function Output()
2090         {
2091                 if(strlen($this->authorization_error)
2092                 || strlen($this->access_token_error)
2093                 || strlen($this->access_token))
2094                 {
2095 ?>
2096 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2097 <html>
2098 <head>
2099 <title>OAuth client result</title>
2100 </head>
2101 <body>
2102 <h1>OAuth client result</h1>
2103 <?php
2104                         if(strlen($this->authorization_error))
2105                         {
2106 ?>
2107 <p>It was not possible to authorize the application.<?php
2108                                 if($this->debug)
2109                                 {
2110 ?>
2111 <br>Authorization error: <?php echo HtmlSpecialChars($this->authorization_error);
2112                                 }
2113 ?></p>
2114 <?php
2115                         }
2116                         elseif(strlen($this->access_token_error))
2117                         {
2118 ?>
2119 <p>It was not possible to use the application access token.
2120 <?php
2121                                 if($this->debug)
2122                                 {
2123 ?>
2124 <br>Error: <?php echo HtmlSpecialChars($this->access_token_error);
2125                                 }
2126 ?></p>
2127 <?php
2128                         }
2129                         elseif(strlen($this->access_token))
2130                         {
2131 ?>
2132 <p>The application authorization was obtained successfully.
2133 <?php
2134                                 if($this->debug)
2135                                 {
2136 ?>
2137 <br>Access token: <?php echo HtmlSpecialChars($this->access_token);
2138                                         if(IsSet($this->access_token_secret))
2139                                         {
2140 ?>
2141 <br>Access token secret: <?php echo HtmlSpecialChars($this->access_token_secret);
2142                                         }
2143                                 }
2144 ?></p>
2145 <?php
2146                                 if(strlen($this->access_token_expiry))
2147                                 {
2148 ?>
2149 <p>Access token expiry: <?php echo $this->access_token_expiry; ?> UTC</p>
2150 <?php
2151                                 }
2152                         }
2153 ?>
2154 </body>
2155 </html>
2156 <?php
2157                 }
2158         }
2159 /*
2160 {metadocument}
2161                 </do>
2162         </function>
2163 {/metadocument}
2164 */
2165
2166 };
2167
2168 /*
2169
2170 {metadocument}
2171 </class>
2172 {/metadocument}
2173
2174 */
2175
2176 ?>