]> git.mxchange.org Git - friendica-addons.git/blob - pumpio/oauth/oauth_client.php
Pumpio: Timeout should now work
[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->timeout = 30;
1002                 $http->data_timeout = 60;
1003                 $http->log_debug = true;
1004                 $http->sasl_authenticate = 0;
1005                 $http->user_agent = $this->oauth_user_agent;
1006                 $http->redirection_limit = (IsSet($options['FollowRedirection']) ? intval($options['FollowRedirection']) : 0);
1007                 $http->follow_redirect = ($http->redirection_limit != 0);
1008                 if($this->debug)
1009                         $this->OutputDebug('Accessing the '.$options['Resource'].' at '.$url);
1010                 $post_files = array();
1011                 $method = strtoupper($method);
1012                 $authorization = '';
1013                 $type = (IsSet($options['RequestContentType']) ? strtolower(trim(strtok($options['RequestContentType'], ';'))) : 'application/x-www-form-urlencoded');
1014                 if(IsSet($oauth))
1015                 {
1016                         $values = array(
1017                                 'oauth_consumer_key'=>$this->client_id,
1018                                 //'oauth_nonce'=>md5(uniqid(rand(), true)),
1019                                 'oauth_nonce'=>sha1(uniqid(mt_rand(), true).uniqid(mt_rand(), true)),
1020                                 'oauth_signature_method'=>$this->signature_method,
1021                                 'oauth_timestamp'=>time(),
1022                                 'oauth_version'=>'1.0',
1023                         );
1024                         $files = (IsSet($options['Files']) ? $options['Files'] : array());
1025                         if(count($files))
1026                         {
1027                                 foreach($files as $name => $value)
1028                                 {
1029                                         if(!IsSet($parameters[$name]))
1030                                                 return($this->SetError('it was specified an file parameters named '.$name));
1031                                         $file = array();
1032                                         switch(IsSet($value['Type']) ? $value['Type'] : 'FileName')
1033                                         {
1034                                                 case 'FileName':
1035                                                         $file['FileName'] = $parameters[$name];
1036                                                         break;
1037                                                 case 'Data':
1038                                                         $file['Data'] = $parameters[$name];
1039                                                         break;
1040                                                 default:
1041                                                         return($this->SetError($value['Type'].' is not a valid type for file '.$name));
1042                                         }
1043                                         $file['ContentType'] = (IsSet($value['Content-Type']) ? $value['Content-Type'] : 'automatic/name');
1044                                         $post_files[$name] = $file;
1045                                 }
1046                                 UnSet($parameters[$name]);
1047                                 if($method !== 'POST')
1048                                 {
1049                                         $this->OutputDebug('For uploading files the method should be POST not '.$method);
1050                                         $method = 'POST';
1051                                 }
1052                                 if($type !== 'multipart/form-data')
1053                                 {
1054                                         if(IsSet($options['RequestContentType']))
1055                                                 return($this->SetError('the request content type for uploading files should be multipart/form-data'));
1056                                         $type = 'multipart/form-data';
1057                                 }
1058                                 $value_parameters = array();
1059                         }
1060                         else
1061                         {
1062                                 if($this->url_parameters
1063                                 && $type === 'application/x-www-form-urlencoded'
1064                                 && count($parameters))
1065                                 {
1066                                         $first = (strpos($url, '?') === false);
1067                                         foreach($parameters as $parameter => $value)
1068                                         {
1069                                                 $url .= ($first ? '?' : '&').UrlEncode($parameter).'='.UrlEncode($value);
1070                                                 $first = false;
1071                                         }
1072                                         $parameters = array();
1073                                 }
1074                                 $value_parameters = ($type !== 'application/x-www-form-urlencoded' ? array() : $parameters);
1075                         }
1076                         $values = array_merge($values, $oauth, $value_parameters);
1077                         $key = $this->Encode($this->client_secret).'&'.$this->Encode($this->access_token_secret);
1078                         switch($this->signature_method)
1079                         {
1080                                 case 'PLAINTEXT':
1081                                         $values['oauth_signature'] = $key;
1082                                         break;
1083                                 case 'HMAC-SHA1':
1084                                         $uri = strtok($url, '?');
1085                                         $sign = $method.'&'.$this->Encode($uri).'&';
1086                                         $first = true;
1087                                         $sign_values = $values;
1088                                         $u = parse_url($url);
1089                                         if(IsSet($u['query']))
1090                                         {
1091                                                 parse_str($u['query'], $q);
1092                                                 foreach($q as $parameter => $value)
1093                                                         $sign_values[$parameter] = $value;
1094                                         }
1095                                         KSort($sign_values);
1096                                         foreach($sign_values as $parameter => $value)
1097                                         {
1098                                                 $sign .= $this->Encode(($first ? '' : '&').$parameter.'='.$this->Encode($value));
1099                                                 $first = false;
1100                                         }
1101                                         $values['oauth_signature'] = base64_encode($this->HMAC('sha1', $sign, $key));
1102                                         break;
1103                                 default:
1104                                         return $this->SetError($this->signature_method.' signature method is not yet supported');
1105                         }
1106                         if($this->authorization_header)
1107                         {
1108                                 $authorization = 'OAuth';
1109                                 $first = true;
1110                                 foreach($values as $parameter => $value)
1111                                 {
1112                                         $authorization .= ($first ? ' ' : ',').$parameter.'="'.$this->Encode($value).'"';
1113                                         $first = false;
1114                                 }
1115                         }
1116                         else
1117                         {
1118                                 if($method === 'GET'
1119                                 || (IsSet($options['PostValuesInURI'])
1120                                 && $options['PostValuesInURI']))
1121                                 {
1122                                         $first = (strcspn($url, '?') == strlen($url));
1123                                         foreach($values as $parameter => $value)
1124                                         {
1125                                                 $url .= ($first ? '?' : '&').$parameter.'='.$this->Encode($value);
1126                                                 $first = false;
1127                                         }
1128                                         $post_values = array();
1129                                 }
1130                                 else
1131                                         $post_values = $values;
1132                         }
1133                 }
1134                 if(strlen($error = $http->GetRequestArguments($url, $arguments)))
1135                         return($this->SetError('it was not possible to open the '.$options['Resource'].' URL: '.$error));
1136                 if(strlen($error = $http->Open($arguments)))
1137                         return($this->SetError('it was not possible to open the '.$options['Resource'].' URL: '.$error));
1138                 if(count($post_files))
1139                         $arguments['PostFiles'] = $post_files;
1140                 $arguments['RequestMethod'] = $method;
1141                 switch($type)
1142                 {
1143                         case 'application/x-www-form-urlencoded':
1144                         case 'multipart/form-data':
1145                                 if(IsSet($options['RequestBody']))
1146                                         return($this->SetError('the request body is defined automatically from the parameters'));
1147                                 $arguments['PostValues'] = $parameters;
1148                                 break;
1149                         case 'application/json':
1150                                 $arguments['Headers']['Content-Type'] = $options['RequestContentType'];
1151                                 if(!IsSet($options['RequestBody']))
1152                                 {
1153                                         $arguments['Body'] = json_encode($parameters);
1154                                         break;
1155                                 }
1156                         default:
1157                                 if(!IsSet($options['RequestBody']))
1158                                         return($this->SetError('it was not specified the body value of the of the API call request'));
1159                                 $arguments['Headers']['Content-Type'] = $options['RequestContentType'];
1160                                 $arguments['Body'] = $options['RequestBody'];
1161                                 break;
1162                 }
1163                 $arguments['Headers']['Accept'] = (IsSet($options['Accept']) ? $options['Accept'] : '*/*');
1164                 if(strlen($authorization))
1165                         $arguments['Headers']['Authorization'] = $authorization;
1166                 if(strlen($error = $http->SendRequest($arguments))
1167                 || strlen($error = $http->ReadReplyHeaders($headers)))
1168                 {
1169                         $http->Close();
1170                         return($this->SetError('it was not possible to retrieve the '.$options['Resource'].': '.$error));
1171                 }
1172                 $error = $http->ReadWholeReplyBody($data);
1173                 $http->Close();
1174                 if(strlen($error))
1175                 {
1176                         return($this->SetError('it was not possible to access the '.$options['Resource'].': '.$error));
1177                 }
1178                 $this->response_status = intval($http->response_status);
1179                 $content_type = (IsSet($options['ResponseContentType']) ? $options['ResponseContentType'] : (IsSet($headers['content-type']) ? strtolower(trim(strtok($headers['content-type'], ';'))) : 'unspecified'));
1180                 switch($content_type)
1181                 {
1182                         case 'text/javascript':
1183                         case 'application/json':
1184                                 if(!function_exists('json_decode'))
1185                                         return($this->SetError('the JSON extension is not available in this PHP setup'));
1186                                 $object = json_decode($data);
1187                                 switch(GetType($object))
1188                                 {
1189                                         case 'object':
1190                                                 if(!IsSet($options['ConvertObjects'])
1191                                                 || !$options['ConvertObjects'])
1192                                                         $response = $object;
1193                                                 else
1194                                                 {
1195                                                         $response = array();
1196                                                         foreach($object as $property => $value)
1197                                                                 $response[$property] = $value;
1198                                                 }
1199                                                 break;
1200                                         case 'array':
1201                                                 $response = $object;
1202                                                 break;
1203                                         default:
1204                                                 if(!IsSet($object))
1205                                                         return($this->SetError('it was not returned a valid JSON definition of the '.$options['Resource'].' values'));
1206                                                 $response = $object;
1207                                                 break;
1208                                 }
1209                                 break;
1210                         case 'application/x-www-form-urlencoded':
1211                         case 'text/plain':
1212                         case 'text/html':
1213                                 parse_str($data, $response);
1214                                 break;
1215                         default:
1216                                 $response = $data;
1217                                 break;
1218                 }
1219                 if($this->response_status >= 200
1220                 && $this->response_status < 300)
1221                         $this->access_token_error = '';
1222                 else
1223                 {
1224                         $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;
1225                         if($this->debug)
1226                                 $this->OutputDebug('Could not retrieve the OAuth access. Error: '.$this->access_token_error);
1227                         if(IsSet($options['FailOnAccessError'])
1228                         && $options['FailOnAccessError'])
1229                         {
1230                                 $this->error = $this->access_token_error;
1231                                 return false;
1232                         }
1233                 }
1234                 return true;
1235         }
1236
1237         Function ProcessToken($code, $refresh)
1238         {
1239                 if($refresh)
1240                 {
1241                         $values = array(
1242                                 'client_id'=>$this->client_id,
1243                                 'client_secret'=>$this->client_secret,
1244                                 'refresh_token'=>$this->refresh_token,
1245                                 'grant_type'=>'refresh_token'
1246                         );
1247                 }
1248                 else
1249                 {
1250                         if(!$this->GetRedirectURI($redirect_uri))
1251                                 return false;
1252                         $values = array(
1253                                 'code'=>$code,
1254                                 'client_id'=>$this->client_id,
1255                                 'client_secret'=>$this->client_secret,
1256                                 'redirect_uri'=>$redirect_uri,
1257                                 'grant_type'=>'authorization_code'
1258                         );
1259                 }
1260                 if(!$this->GetAccessTokenURL($url))
1261                         return false;
1262                 if(!$this->SendAPIRequest($url, 'POST', $values, null, array('Resource'=>'OAuth '.($refresh ? 'refresh' : 'access').' token', 'ConvertObjects'=>true), $response))
1263                         return false;
1264                 if(strlen($this->access_token_error))
1265                 {
1266                         $this->authorization_error = $this->access_token_error;
1267                         return true;
1268                 }
1269                 if(!IsSet($response['access_token']))
1270                 {
1271                         if(IsSet($response['error']))
1272                         {
1273                                 $this->authorization_error = 'it was not possible to retrieve the access token: it was returned the error: '.$response['error'];
1274                                 return true;
1275                         }
1276                         return($this->SetError('OAuth server did not return the access token'));
1277                 }
1278                 $access_token = array(
1279                         'value'=>$this->access_token = $response['access_token'],
1280                         'authorized'=>true
1281                 );
1282                 if($this->debug)
1283                         $this->OutputDebug('Access token: '.$this->access_token);
1284                 if(IsSet($response['expires'])
1285                 || IsSet($response['expires_in']))
1286                 {
1287                         $expires = (IsSet($response['expires']) ? $response['expires'] : $response['expires_in']);
1288                         if(strval($expires) !== strval(intval($expires))
1289                         || $expires <= 0)
1290                                 return($this->SetError('OAuth server did not return a supported type of access token expiry time'));
1291                         $this->access_token_expiry = gmstrftime('%Y-%m-%d %H:%M:%S', time() + $expires);
1292                         if($this->debug)
1293                                 $this->OutputDebug('Access token expiry: '.$this->access_token_expiry.' UTC');
1294                         $access_token['expiry'] = $this->access_token_expiry;
1295                 }
1296                 else
1297                         $this->access_token_expiry = '';
1298                 if(IsSet($response['token_type']))
1299                 {
1300                         $this->access_token_type = $response['token_type'];
1301                         if($this->debug)
1302                                 $this->OutputDebug('Access token type: '.$this->access_token_type);
1303                         $access_token['type'] = $this->access_token_type;
1304                 }
1305                 else
1306                         $this->access_token_type = '';
1307                 if($refresh)
1308                         $response['refresh_token'] = $this->refresh_token;
1309                 elseif(IsSet($response['refresh_token']))
1310                 {
1311                         $this->refresh_token = $response['refresh_token'];
1312                         if($this->debug)
1313                                 $this->OutputDebug('Refresh token: '.$this->refresh_token);
1314                         $access_token['refresh'] = $this->refresh_token;
1315                 }
1316                 else
1317                         $this->refresh_token = '';
1318                 if(!$this->StoreAccessToken($access_token))
1319                         return false;
1320                 return true;
1321         }
1322
1323         Function RetrieveToken(&$valid)
1324         {
1325                 $valid = false;
1326                 if(!$this->GetAccessToken($access_token))
1327                         return false;
1328                 if(IsSet($access_token['value']))
1329                 {
1330                         $this->access_token_expiry = '';
1331                         if(IsSet($access_token['expiry'])
1332                         && strcmp($this->access_token_expiry = $access_token['expiry'], gmstrftime('%Y-%m-%d %H:%M:%S')) < 0)
1333                         {
1334                                 $this->access_token = '';
1335                                 if($this->debug)
1336                                         $this->OutputDebug('The OAuth access token expired in '.$this->access_token_expiry);
1337                         }
1338                         else
1339                         {
1340                                 $this->access_token = $access_token['value'];
1341                                 if(IsSet($access_token['type']))
1342                                         $this->access_token_type = $access_token['type'];
1343                                 else
1344                                         $this->access_token_type = '';
1345                                 if($this->debug)
1346                                         $this->OutputDebug('The OAuth access token '.$this->access_token.' is valid');
1347                                 if(strlen($this->access_token_type)
1348                                 && $this->debug)
1349                                         $this->OutputDebug('The OAuth access token is of type '.$this->access_token_type);
1350                                 if(IsSet($access_token['refresh']))
1351                                         $this->refresh_token = $access_token['refresh'];
1352                                 else
1353                                         $this->refresh_token = '';
1354                                 $valid = true;
1355                         }
1356                 }
1357                 return true;
1358         }
1359 /*
1360 {metadocument}
1361         <function>
1362                 <name>CallAPI</name>
1363                 <type>BOOLEAN</type>
1364                 <documentation>
1365                         <purpose>Send a HTTP request to the Web services API using a
1366                                 previously obtained authorization token via OAuth.</purpose>
1367                         <usage>This function can be used to call an API after having
1368                                 previously obtained an access token through the OAuth protocol
1369                                 using the <functionlink>Process</functionlink> function, or by
1370                                 directly setting the variables
1371                                 <variablelink>access_token</variablelink>, as well as
1372                                 <variablelink>access_token_secret</variablelink> in case of using
1373                                 OAuth 1.0 or 1.0a services.</usage>
1374                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
1375                                 the call was done successfully.</returnvalue>
1376                 </documentation>
1377                 <argument>
1378                         <name>url</name>
1379                         <type>STRING</type>
1380                         <documentation>
1381                                 <purpose>URL of the API where the HTTP request will be sent.</purpose>
1382                         </documentation>
1383                 </argument>
1384                 <argument>
1385                         <name>method</name>
1386                         <type>STRING</type>
1387                         <documentation>
1388                                 <purpose>HTTP method that will be used to send the request. It can
1389                                 be <stringvalue>GET</stringvalue>,
1390                                 <stringvalue>POST</stringvalue>,
1391                                 <stringvalue>DELETE</stringvalue>, <stringvalue>PUT</stringvalue>,
1392                                 etc..</purpose>
1393                         </documentation>
1394                 </argument>
1395                 <argument>
1396                         <name>parameters</name>
1397                         <type>HASH</type>
1398                         <documentation>
1399                                 <purpose>Associative array with the names and values of the API
1400                                         call request parameters.</purpose>
1401                         </documentation>
1402                 </argument>
1403                 <argument>
1404                         <name>options</name>
1405                         <type>HASH</type>
1406                         <documentation>
1407                                 <purpose>Associative array with additional options to configure
1408                                         the request. Currently it supports the following
1409                                         options:<paragraphbreak />
1410                                         <stringvalue>2Legged</stringvalue>: boolean option that
1411                                                 determines if the API request should be 2 legged. The default
1412                                                 value is <tt><booleanvalue>0</booleanvalue></tt>.<paragraphbreak />
1413                                         <stringvalue>Accept</stringvalue>: content type value of the
1414                                                 Accept HTTP header to be sent in the API call HTTP request.
1415                                                 Some APIs require that a certain value be sent to specify
1416                                                 which version of the API is being called. The default value is
1417                                                 <stringvalue>*&#47;*</stringvalue>.<paragraphbreak />
1418                                         <stringvalue>ConvertObjects</stringvalue>: boolean option that
1419                                                 determines if objects should be converted into arrays when the
1420                                                 response is returned in JSON format. The default value is
1421                                                 <booleanvalue>0</booleanvalue>.<paragraphbreak />
1422                                         <stringvalue>FailOnAccessError</stringvalue>: boolean option
1423                                                 that determines if this functions should fail when the server
1424                                                 response status is not between 200 and 299. The default value
1425                                                 is <booleanvalue>0</booleanvalue>.<paragraphbreak />
1426                                         <stringvalue>Files</stringvalue>: associative array with
1427                                                 details of the parameters that must be passed as file uploads.
1428                                                 The array indexes must have the same name of the parameters
1429                                                 to be sent as files. The respective array entry values must
1430                                                 also be associative arrays with the parameters for each file.
1431                                                 Currently it supports the following parameters:<paragraphbreak />
1432                                                 - <tt>Type</tt> - defines how the parameter value should be
1433                                                 treated. It can be <tt>'FileName'</tt> if the parameter value is
1434                                                 is the name of a local file to be uploaded. It may also be
1435                                                 <tt>'Data'</tt> if the parameter value is the actual data of
1436                                                 the file to be uploaded.<paragraphbreak />
1437                                                 - Default: <tt>'FileName'</tt><paragraphbreak />
1438                                                 - <tt>ContentType</tt> - MIME value of the content type of the
1439                                                 file. It can be <tt>'automatic/name'</tt> if the content type
1440                                                 should be determine from the file name extension.<paragraphbreak />
1441                                                 - Default: <tt>'automatic/name'</tt><paragraphbreak />
1442                                         <stringvalue>PostValuesInURI</stringvalue>: boolean option to
1443                                                 determine that a POST request should pass the request values
1444                                                 in the URI. The default value is
1445                                                 <booleanvalue>0</booleanvalue>.<paragraphbreak />
1446                                         <stringvalue>FollowRedirection</stringvalue>: limit number of
1447                                                 times that HTTP response redirects will be followed. If it is
1448                                                 set to <integervalue>0</integervalue>, redirection responses
1449                                                 fail in error. The default value is
1450                                                 <integervalue>0</integervalue>.<paragraphbreak />
1451                                         <stringvalue>RequestBody</stringvalue>: request body data of a
1452                                                 custom type. The <stringvalue>RequestContentType</stringvalue>
1453                                                 option must be specified, so the
1454                                                 <stringvalue>RequestBody</stringvalue> option is considered.<paragraphbreak />
1455                                         <stringvalue>RequestContentType</stringvalue>: content type that
1456                                                 should be used to send the request values. It can be either
1457                                                 <stringvalue>application/x-www-form-urlencoded</stringvalue>
1458                                                 for sending values like from Web forms, or
1459                                                 <stringvalue>application/json</stringvalue> for sending the
1460                                                 values encoded in JSON format. Other types are accepted if the
1461                                                 <stringvalue>RequestBody</stringvalue> option is specified.
1462                                                 The default value is
1463                                                 <stringvalue>application/x-www-form-urlencoded</stringvalue>.<paragraphbreak />
1464                                         <stringvalue>RequestBody</stringvalue>: request body data of a
1465                                                 custom type. The <stringvalue>RequestContentType</stringvalue>
1466                                                 option must be specified, so the
1467                                                 <stringvalue>RequestBody</stringvalue> option is considered.<paragraphbreak />
1468                                         <stringvalue>Resource</stringvalue>: string with a label that
1469                                                 will be used in the error messages and debug log entries to
1470                                                 identify what operation the request is performing. The default
1471                                                 value is <stringvalue>API call</stringvalue>.<paragraphbreak />
1472                                         <stringvalue>ResponseContentType</stringvalue>: content type
1473                                                 that should be considered when decoding the API request
1474                                                 response. This overrides the <tt>Content-Type</tt> header
1475                                                 returned by the server. If the content type is 
1476                                                 <stringvalue>application/x-www-form-urlencoded</stringvalue>
1477                                                 the function will parse the data returning an array of
1478                                                 key-value pairs. If the content type is 
1479                                                 <stringvalue>application/json</stringvalue> the response will
1480                                                 be decode as a JSON-encoded data type. Other content type
1481                                                 values will make the function return the original response
1482                                                 value as it was returned from the server. The default value
1483                                                 for this option is to use what the server returned in the
1484                                                 <tt>Content-Type</tt> header.</purpose>
1485                         </documentation>
1486                 </argument>
1487                 <argument>
1488                         <name>response</name>
1489                         <type>STRING</type>
1490                         <out />
1491                         <documentation>
1492                                 <purpose>Return the value of the API response. If the value is
1493                                         JSON encoded, this function will decode it and return the value
1494                                         converted to respective types. If the value is form encoded,
1495                                         this function will decode the response and return it as an
1496                                         array. Otherwise, the class will return the value as a
1497                                         string.</purpose>
1498                         </documentation>
1499                 </argument>
1500                 <do>
1501 {/metadocument}
1502 */
1503         Function CallAPI($url, $method, $parameters, $options, &$response)
1504         {
1505                 if(!IsSet($options['Resource']))
1506                         $options['Resource'] = 'API call';
1507                 if(!IsSet($options['ConvertObjects']))
1508                         $options['ConvertObjects'] = false;
1509                 if(strlen($this->access_token) === 0)
1510                 {
1511                         if(!$this->RetrieveToken($valid))
1512                                 return false;
1513                         if(!$valid)
1514                                 return $this->SetError('the access token is not set to a valid value');
1515                 }
1516                 switch(intval($this->oauth_version))
1517                 {
1518                         case 1:
1519                                 $oauth = array(
1520                                         'oauth_token'=>((IsSet($options['2Legged']) && $options['2Legged']) ? '' : $this->access_token)
1521                                 );
1522                                 break;
1523
1524                         case 2:
1525                                 if(strlen($this->access_token_expiry)
1526                                 && strcmp($this->access_token_expiry, gmstrftime('%Y-%m-%d %H:%M:%S')) <= 0)
1527                                 {
1528                                         if(strlen($this->refresh_token) === 0)
1529                                                 return($this->SetError('the access token expired and no refresh token is available'));
1530                                         if($this->debug)
1531                                         {
1532                                                 $this->OutputDebug('The access token expired on '.$this->access_token_expiry);
1533                                                 $this->OutputDebug('Refreshing the access token');
1534                                         }
1535                                         if(!$this->ProcessToken(null, true))
1536                                                 return false;
1537                                 }
1538                                 $oauth = null;
1539                                 $url .= (strcspn($url, '?') < strlen($url) ? '&' : '?').'access_token='.UrlEncode($this->access_token);
1540                                 break;
1541
1542                         default:
1543                                 return($this->SetError($this->oauth_version.' is not a supported version of the OAuth protocol'));
1544                 }
1545                 return($this->SendAPIRequest($url, $method, $parameters, $oauth, $options, $response));
1546         }
1547 /*
1548 {metadocument}
1549                 </do>
1550         </function>
1551 {/metadocument}
1552 */
1553
1554 /*
1555 {metadocument}
1556         <function>
1557                 <name>Initialize</name>
1558                 <type>BOOLEAN</type>
1559                 <documentation>
1560                         <purpose>Initialize the class variables and internal state. It must
1561                                 be called before calling other class functions.</purpose>
1562                         <usage>Set the <variablelink>server</variablelink> variable before
1563                                 calling this function to let it initialize the class variables to
1564                                 work with the specified server type. Alternatively, you can set
1565                                 other class variables manually to make it work with servers that
1566                                 are not yet built-in supported.</usage>
1567                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
1568                                 it was able to successfully initialize the class for the specified
1569                                 server type.</returnvalue>
1570                 </documentation>
1571                 <do>
1572 {/metadocument}
1573 */
1574         Function Initialize()
1575         {
1576                 if(strlen($this->server) === 0)
1577                         return true;
1578                 $this->request_token_url = '';
1579                 $this->append_state_to_redirect_uri = '';
1580                 $this->authorization_header = true;
1581                 $this->url_parameters = false;
1582                 $this->token_request_method = 'GET';
1583                 $this->signature_method = 'HMAC-SHA1';
1584                 switch($this->server)
1585                 {
1586                         case 'Bitbucket':
1587                                 $this->oauth_version = '1.0a';
1588                                 $this->request_token_url = 'https://bitbucket.org/!api/1.0/oauth/request_token';
1589                                 $this->dialog_url = 'https://bitbucket.org/!api/1.0/oauth/authenticate';
1590                                 $this->access_token_url = 'https://bitbucket.org/!api/1.0/oauth/access_token';
1591                                 $this->url_parameters = true;
1592                                 break;
1593
1594                         case 'Box':
1595                                 $this->oauth_version = '2.0';
1596                                 $this->dialog_url = 'https://www.box.com/api/oauth2/authorize?response_type=code&client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&state={STATE}';
1597                                 $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';
1598                                 $this->access_token_url = 'https://www.box.com/api/oauth2/token';
1599                                 break;
1600
1601                         case 'Dropbox':
1602                                 $this->oauth_version = '1.0';
1603                                 $this->request_token_url = 'https://api.dropbox.com/1/oauth/request_token';
1604                                 $this->dialog_url = 'https://www.dropbox.com/1/oauth/authorize';
1605                                 $this->access_token_url = 'https://api.dropbox.com/1/oauth/access_token';
1606                                 $this->authorization_header = false;
1607                                 break;
1608
1609                         case 'Eventful':
1610                                 $this->oauth_version = '1.0a';
1611                                 $this->request_token_url = 'http://eventful.com/oauth/request_token';
1612                                 $this->dialog_url = 'http://eventful.com/oauth/authorize';
1613                                 $this->access_token_url = 'http://eventful.com/oauth/access_token';
1614                                 $this->authorization_header = false;
1615                                 $this->url_parameters = true;
1616                                 $this->token_request_method = 'POST';
1617                                 break;
1618
1619                         case 'Evernote':
1620                                 $this->oauth_version = '1.0a';
1621                                 $this->request_token_url = 'https://sandbox.evernote.com/oauth';
1622                                 $this->dialog_url = 'https://sandbox.evernote.com/OAuth.action';
1623                                 $this->access_token_url = 'https://sandbox.evernote.com/oauth';
1624                                 $this->url_parameters = true;
1625                                 $this->authorization_header = false;
1626                                 break;
1627
1628                         case 'Facebook':
1629                                 $this->oauth_version = '2.0';
1630                                 $this->dialog_url = 'https://www.facebook.com/dialog/oauth?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}';
1631                                 $this->access_token_url = 'https://graph.facebook.com/oauth/access_token';
1632                                 break;
1633
1634                         case 'Fitbit':
1635                                 $this->oauth_version = '1.0a';
1636                                 $this->request_token_url = 'http://api.fitbit.com/oauth/request_token';
1637                                 $this->dialog_url = 'http://api.fitbit.com/oauth/authorize';
1638                                 $this->access_token_url = 'http://api.fitbit.com/oauth/access_token';
1639                                 break;
1640
1641                         case 'Flickr':
1642                                 $this->oauth_version = '1.0a';
1643                                 $this->request_token_url = 'http://www.flickr.com/services/oauth/request_token';
1644                                 $this->dialog_url = 'http://www.flickr.com/services/oauth/authorize?perms={SCOPE}';
1645                                 $this->access_token_url = 'http://www.flickr.com/services/oauth/access_token';
1646                                 $this->authorization_header = false;
1647                                 break;
1648
1649                         case 'Foursquare':
1650                                 $this->oauth_version = '2.0';
1651                                 $this->dialog_url = 'https://foursquare.com/oauth2/authorize?client_id={CLIENT_ID}&scope={SCOPE}&response_type=code&redirect_uri={REDIRECT_URI}&state={STATE}';
1652                                 $this->access_token_url = 'https://foursquare.com/oauth2/access_token';
1653                                 break;
1654
1655                         case 'github':
1656                                 $this->oauth_version = '2.0';
1657                                 $this->dialog_url = 'https://github.com/login/oauth/authorize?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&state={STATE}';
1658                                 $this->access_token_url = 'https://github.com/login/oauth/access_token';
1659                                 break;
1660
1661                         case 'Google':
1662                                 $this->oauth_version = '2.0';
1663                                 $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}';
1664                                 $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';
1665                                 $this->access_token_url = 'https://accounts.google.com/o/oauth2/token';
1666                                 break;
1667
1668                         case 'Instagram':
1669                                 $this->oauth_version = '2.0';
1670                                 $this->dialog_url ='https://api.instagram.com/oauth/authorize/?client_id={CLIENT_ID}&redirect_uri={REDIRECT_URI}&scope={SCOPE}&response_type=code&state={STATE}';
1671                                 $this->access_token_url = 'https://api.instagram.com/oauth/access_token';
1672                                 break;
1673
1674                         case 'LinkedIn':
1675                                 $this->oauth_version = '1.0a';
1676                                 $this->request_token_url = 'https://api.linkedin.com/uas/oauth/requestToken?scope={SCOPE}';
1677                                 $this->dialog_url = 'https://api.linkedin.com/uas/oauth/authenticate';
1678                                 $this->access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken';
1679                                 $this->url_parameters = true;
1680                                 break;
1681
1682                         case 'Microsoft':
1683                                 $this->oauth_version = '2.0';
1684                                 $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}';
1685                                 $this->access_token_url = 'https://login.live.com/oauth20_token.srf';
1686                                 break;
1687
1688                         case 'RightSignature':
1689                                 $this->oauth_version = '1.0a';
1690                                 $this->request_token_url = 'https://rightsignature.com/oauth/request_token';
1691                                 $this->dialog_url = 'https://rightsignature.com/oauth/authorize';
1692                                 $this->access_token_url = 'https://rightsignature.com/oauth/access_token';
1693                                 $this->authorization_header = false;
1694                                 break;
1695
1696                         case 'Scoop.it':
1697                                 $this->oauth_version = '1.0a';
1698                                 $this->request_token_url = 'https://www.scoop.it/oauth/request';
1699                                 $this->dialog_url = 'https://www.scoop.it/oauth/authorize';
1700                                 $this->access_token_url = 'https://www.scoop.it/oauth/access';
1701                                 $this->authorization_header = false;
1702                                 break;
1703
1704                         case 'StockTwits':
1705                                 $this->oauth_version = '2.0';
1706                                 $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}';
1707                                 $this->access_token_url = 'https://api.stocktwits.com/api/2/oauth/token';
1708                                 break;
1709
1710                         case 'Tumblr':
1711                                 $this->oauth_version = '1.0a';
1712                                 $this->request_token_url = 'http://www.tumblr.com/oauth/request_token';
1713                                 $this->dialog_url = 'http://www.tumblr.com/oauth/authorize';
1714                                 $this->access_token_url = 'http://www.tumblr.com/oauth/access_token';
1715                                 break;
1716
1717                         case 'Twitter':
1718                                 $this->oauth_version = '1.0a';
1719                                 $this->request_token_url = 'https://api.twitter.com/oauth/request_token';
1720                                 $this->dialog_url = 'https://api.twitter.com/oauth/authenticate';
1721                                 $this->access_token_url = 'https://api.twitter.com/oauth/access_token';
1722                                 $this->url_parameters = true;
1723                                 break;
1724
1725                         case 'XING':
1726                                 $this->oauth_version = '1.0a';
1727                                 $this->request_token_url = 'https://api.xing.com/v1/request_token';
1728                                 $this->dialog_url = 'https://api.xing.com/v1/authorize';
1729                                 $this->access_token_url = 'https://api.xing.com/v1/access_token';
1730                                 $this->authorization_header = false;
1731                                 break;
1732
1733                         case 'Yahoo':
1734                                 $this->oauth_version = '1.0a';
1735                                 $this->request_token_url = 'https://api.login.yahoo.com/oauth/v2/get_request_token';
1736                                 $this->dialog_url = 'https://api.login.yahoo.com/oauth/v2/request_auth';
1737                                 $this->access_token_url = 'https://api.login.yahoo.com/oauth/v2/get_token';
1738                                 $this->authorization_header = false;
1739                                 break;
1740
1741                         default:
1742                                 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.'));
1743                 }
1744                 return(true);
1745         }
1746 /*
1747 {metadocument}
1748                 </do>
1749         </function>
1750 {/metadocument}
1751 */
1752
1753 /*
1754 {metadocument}
1755         <function>
1756                 <name>Process</name>
1757                 <type>BOOLEAN</type>
1758                 <documentation>
1759                         <purpose>Process the OAuth protocol interaction with the OAuth
1760                                 server.</purpose>
1761                         <usage>Call this function when you need to retrieve the OAuth access
1762                                 token. Check the <variablelink>access_token</variablelink> to
1763                                 determine if the access token was obtained successfully.</usage>
1764                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
1765                                 the OAuth protocol was processed without errors.</returnvalue>
1766                 </documentation>
1767                 <do>
1768 {/metadocument}
1769 */
1770         Function Process()
1771         {
1772                 switch(intval($this->oauth_version))
1773                 {
1774                         case 1:
1775                                 $one_a = ($this->oauth_version === '1.0a');
1776                                 if($this->debug)
1777                                         $this->OutputDebug('Checking the OAuth token authorization state');
1778                                 if(!$this->GetAccessToken($access_token))
1779                                         return false;
1780                                 if(IsSet($access_token['authorized'])
1781                                 && IsSet($access_token['value']))
1782                                 {
1783                                         $expired = (IsSet($access_token['expiry']) && strcmp($access_token['expiry'], gmstrftime('%Y-%m-%d %H:%M:%S')) <= 0);
1784                                         if(!$access_token['authorized']
1785                                         || $expired)
1786                                         {
1787                                                 if($this->debug)
1788                                                 {
1789                                                         if($expired)
1790                                                                 $this->OutputDebug('The OAuth token expired on '.$access_token['expiry'].'UTC');
1791                                                         else
1792                                                                 $this->OutputDebug('The OAuth token is not yet authorized');
1793                                                         $this->OutputDebug('Checking the OAuth token and verifier');
1794                                                 }
1795                                                 if(!$this->GetRequestToken($token, $verifier))
1796                                                         return false;
1797                                                 if(!IsSet($token)
1798                                                 || ($one_a
1799                                                 && !IsSet($verifier)))
1800                                                 {
1801                                                         if(!$this->GetRequestDenied($denied))
1802                                                                 return false;
1803                                                         if(IsSet($denied)
1804                                                         && $denied === $access_token['value'])
1805                                                         {
1806                                                                 if($this->debug)
1807                                                                         $this->OutputDebug('The authorization request was denied');
1808                                                                 $this->authorization_error = 'the request was denied';
1809                                                                 return true;
1810                                                         }
1811                                                         else
1812                                                         {
1813                                                                 if($this->debug)
1814                                                                         $this->OutputDebug('Reset the OAuth token state because token and verifier are not both set');
1815                                                                 $access_token = array();
1816                                                         }
1817                                                 }
1818                                                 elseif($token !== $access_token['value'])
1819                                                 {
1820                                                         if($this->debug)
1821                                                                 $this->OutputDebug('Reset the OAuth token state because token does not match what as previously retrieved');
1822                                                         $access_token = array();
1823                                                 }
1824                                                 else
1825                                                 {
1826                                                         if(!$this->GetAccessTokenURL($url))
1827                                                                 return false;
1828                                                         $oauth = array(
1829                                                                 'oauth_token'=>$token,
1830                                                         );
1831                                                         if($one_a)
1832                                                                 $oauth['oauth_verifier'] = $verifier;
1833                                                         $this->access_token_secret = $access_token['secret'];
1834                                                         $options = array('Resource'=>'OAuth access token');
1835                                                         $method = strtoupper($this->token_request_method);
1836                                                         switch($method)
1837                                                         {
1838                                                                 case 'GET':
1839                                                                         break;
1840                                                                 case 'POST':
1841                                                                         $options['PostValuesInURI'] = true;
1842                                                                         break;
1843                                                                 default:
1844                                                                         $this->error = $method.' is not a supported method to request tokens';
1845                                                                         break;
1846                                                         }
1847                                                         if(!$this->SendAPIRequest($url, $method, array(), $oauth, $options, $response))
1848                                                                 return false;
1849                                                         if(strlen($this->access_token_error))
1850                                                         {
1851                                                                 $this->authorization_error = $this->access_token_error;
1852                                                                 return true;
1853                                                         }
1854                                                         if(!IsSet($response['oauth_token'])
1855                                                         || !IsSet($response['oauth_token_secret']))
1856                                                         {
1857                                                                 $this->authorization_error= 'it was not returned the access token and secret';
1858                                                                 return true;
1859                                                         }
1860                                                         $access_token = array(
1861                                                                 'value'=>$response['oauth_token'],
1862                                                                 'secret'=>$response['oauth_token_secret'],
1863                                                                 'authorized'=>true
1864                                                         );
1865                                                         if(IsSet($response['oauth_expires_in']))
1866                                                         {
1867                                                                 $expires = $response['oauth_expires_in'];
1868                                                                 if(strval($expires) !== strval(intval($expires))
1869                                                                 || $expires <= 0)
1870                                                                         return($this->SetError('OAuth server did not return a supported type of access token expiry time'));
1871                                                                 $this->access_token_expiry = gmstrftime('%Y-%m-%d %H:%M:%S', time() + $expires);
1872                                                                 if($this->debug)
1873                                                                         $this->OutputDebug('Access token expiry: '.$this->access_token_expiry.' UTC');
1874                                                                 $access_token['expiry'] = $this->access_token_expiry;
1875                                                         }
1876                                                         else
1877                                                                 $this->access_token_expiry = '';
1878
1879                                                         if(!$this->StoreAccessToken($access_token))
1880                                                                 return false;
1881                                                         if($this->debug)
1882                                                                 $this->OutputDebug('The OAuth token was authorized');
1883                                                 }
1884                                         }
1885                                         elseif($this->debug)
1886                                                 $this->OutputDebug('The OAuth token was already authorized');
1887                                         if(IsSet($access_token['authorized'])
1888                                         && $access_token['authorized'])
1889                                         {
1890                                                 $this->access_token = $access_token['value'];
1891                                                 $this->access_token_secret = $access_token['secret'];
1892                                                 return true;
1893                                         }
1894                                 }
1895                                 else
1896                                 {
1897                                         if($this->debug)
1898                                                 $this->OutputDebug('The OAuth access token is not set');
1899                                         $access_token = array();
1900                                 }
1901                                 if(!IsSet($access_token['authorized']))
1902                                 {
1903                                         if($this->debug)
1904                                                 $this->OutputDebug('Requesting the unauthorized OAuth token');
1905                                         if(!$this->GetRequestTokenURL($url))
1906                                                 return false;
1907                                         $url = str_replace('{SCOPE}', UrlEncode($this->scope), $url); 
1908                                         if(!$this->GetRedirectURI($redirect_uri))
1909                                                 return false;
1910                                         $oauth = array(
1911                                                 'oauth_callback'=>$redirect_uri,
1912                                         );
1913                                         $options = array('Resource'=>'OAuth request token');
1914                                         $method = strtoupper($this->token_request_method);
1915                                         switch($method)
1916                                         {
1917                                                 case 'GET':
1918                                                         break;
1919                                                 case 'POST':
1920                                                         $options['PostValuesInURI'] = true;
1921                                                         break;
1922                                                 default:
1923                                                         $this->error = $method.' is not a supported method to request tokens';
1924                                                         break;
1925                                         }
1926                                         if(!$this->SendAPIRequest($url, $method, array(), $oauth, $options, $response))
1927                                                 return false;
1928                                         if(strlen($this->access_token_error))
1929                                         {
1930                                                 $this->authorization_error = $this->access_token_error;
1931                                                 return true;
1932                                         }
1933                                         if(!IsSet($response['oauth_token'])
1934                                         || !IsSet($response['oauth_token_secret']))
1935                                         {
1936                                                 $this->authorization_error = 'it was not returned the requested token';
1937                                                 return true;
1938                                         }
1939                                         $access_token = array(
1940                                                 'value'=>$response['oauth_token'],
1941                                                 'secret'=>$response['oauth_token_secret'],
1942                                                 'authorized'=>false
1943                                         );
1944                                         if(!$this->StoreAccessToken($access_token))
1945                                                 return false;
1946                                 }
1947                                 if(!$this->GetDialogURL($url))
1948                                         return false;
1949                                 $url .= (strpos($url, '?') === false ? '?' : '&').'oauth_token='.$access_token['value'];
1950                                 if(!$one_a)
1951                                 {
1952                                         if(!$this->GetRedirectURI($redirect_uri))
1953                                                 return false;
1954                                         $url .= '&oauth_callback='.UrlEncode($redirect_uri);
1955                                 }
1956                                 if($this->debug)
1957                                         $this->OutputDebug('Redirecting to OAuth authorize page '.$url);
1958                                 Header('HTTP/1.0 302 OAuth Redirection');
1959                                 Header('Location: '.$url);
1960                                 $this->exit = true;
1961                                 return true;
1962
1963                         case 2:
1964                                 if($this->debug)
1965                                         $this->OutputDebug('Checking if OAuth access token was already retrieved from '.$this->access_token_url);
1966                                 if(!$this->RetrieveToken($valid))
1967                                         return false;
1968                                 if($valid)
1969                                         return true;
1970                                 if($this->debug)
1971                                         $this->OutputDebug('Checking the authentication state in URI '.$_SERVER['REQUEST_URI']);
1972                                 if(!$this->GetStoredState($stored_state))
1973                                         return false;
1974                                 if(strlen($stored_state) == 0)
1975                                         return($this->SetError('it was not set the OAuth state'));
1976                                 if(!$this->GetRequestState($state))
1977                                         return false;
1978                                 if($state === $stored_state)
1979                                 {
1980                                         if($this->debug)
1981                                                 $this->OutputDebug('Checking the authentication code');
1982                                         if(!$this->GetRequestCode($code))
1983                                                 return false;
1984                                         if(strlen($code) == 0)
1985                                         {
1986                                                 if(!$this->GetRequestError($this->authorization_error))
1987                                                         return false;
1988                                                 if(IsSet($this->authorization_error))
1989                                                 {
1990                                                         if($this->debug)
1991                                                                 $this->OutputDebug('Authorization failed with error code '.$this->authorization_error);
1992                                                         switch($this->authorization_error)
1993                                                         {
1994                                                                 case 'invalid_request':
1995                                                                 case 'unauthorized_client':
1996                                                                 case 'access_denied':
1997                                                                 case 'unsupported_response_type':
1998                                                                 case 'invalid_scope':
1999                                                                 case 'server_error':
2000                                                                 case 'temporarily_unavailable':
2001                                                                 case 'user_denied':
2002                                                                         return true;
2003                                                                 default:
2004                                                                         return($this->SetError('it was returned an unknown OAuth error code'));
2005                                                         }
2006                                                 }
2007                                                 return($this->SetError('it was not returned the OAuth dialog code'));
2008                                         }
2009                                         if(!$this->ProcessToken($code, false))
2010                                                 return false;
2011                                 }
2012                                 else
2013                                 {
2014                                         if(!$this->GetRedirectURI($redirect_uri))
2015                                                 return false;
2016                                         if(strlen($this->append_state_to_redirect_uri))
2017                                                 $redirect_uri .= (strpos($redirect_uri, '?') === false ? '?' : '&').$this->append_state_to_redirect_uri.'='.$stored_state;
2018                                         if(!$this->GetDialogURL($url, $redirect_uri, $stored_state))
2019                                                 return false;
2020                                         if(strlen($url) == 0)
2021                                                 return($this->SetError('it was not set the OAuth dialog URL'));
2022                                         if($this->debug)
2023                                                 $this->OutputDebug('Redirecting to OAuth Dialog '.$url);
2024                                         Header('HTTP/1.0 302 OAuth Redirection');
2025                                         Header('Location: '.$url);
2026                                         $this->exit = true;
2027                                 }
2028                                 break;
2029
2030                         default:
2031                                 return($this->SetError($this->oauth_version.' is not a supported version of the OAuth protocol'));
2032                 }
2033                 return(true);
2034         }
2035 /*
2036 {metadocument}
2037                 </do>
2038         </function>
2039 {/metadocument}
2040 */
2041
2042 /*
2043 {metadocument}
2044         <function>
2045                 <name>Finalize</name>
2046                 <type>BOOLEAN</type>
2047                 <documentation>
2048                         <purpose>Cleanup any resources that may have been used during the
2049                                 OAuth protocol processing or execution of API calls.</purpose>
2050                         <usage>Always call this function as the last step after calling the
2051                                 functions <functionlink>Process</functionlink> or
2052                                 <functionlink>CallAPI</functionlink>.</usage>
2053                         <returnvalue>This function returns <booleanvalue>1</booleanvalue> if
2054                                 the function cleaned up any resources successfully.</returnvalue>
2055                 </documentation>
2056                 <argument>
2057                         <name>success</name>
2058                         <type>BOOLEAN</type>
2059                         <documentation>
2060                                 <purpose>Pass the last success state returned by the class or any
2061                                         external code processing the class function results.</purpose>
2062                         </documentation>
2063                 </argument>
2064                 <do>
2065 {/metadocument}
2066 */
2067         Function Finalize($success)
2068         {
2069                 return($success);
2070         }
2071 /*
2072 {metadocument}
2073                 </do>
2074         </function>
2075 {/metadocument}
2076 */
2077
2078 /*
2079 {metadocument}
2080         <function>
2081                 <name>Output</name>
2082                 <type>VOID</type>
2083                 <documentation>
2084                         <purpose>Display the results of the OAuth protocol processing.</purpose>
2085                         <usage>Only call this function if you are debugging the OAuth
2086                                 authorization process and you need to view what was its
2087                                 results.</usage>
2088                 </documentation>
2089                 <do>
2090 {/metadocument}
2091 */
2092         Function Output()
2093         {
2094                 if(strlen($this->authorization_error)
2095                 || strlen($this->access_token_error)
2096                 || strlen($this->access_token))
2097                 {
2098 ?>
2099 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2100 <html>
2101 <head>
2102 <title>OAuth client result</title>
2103 </head>
2104 <body>
2105 <h1>OAuth client result</h1>
2106 <?php
2107                         if(strlen($this->authorization_error))
2108                         {
2109 ?>
2110 <p>It was not possible to authorize the application.<?php
2111                                 if($this->debug)
2112                                 {
2113 ?>
2114 <br>Authorization error: <?php echo HtmlSpecialChars($this->authorization_error);
2115                                 }
2116 ?></p>
2117 <?php
2118                         }
2119                         elseif(strlen($this->access_token_error))
2120                         {
2121 ?>
2122 <p>It was not possible to use the application access token.
2123 <?php
2124                                 if($this->debug)
2125                                 {
2126 ?>
2127 <br>Error: <?php echo HtmlSpecialChars($this->access_token_error);
2128                                 }
2129 ?></p>
2130 <?php
2131                         }
2132                         elseif(strlen($this->access_token))
2133                         {
2134 ?>
2135 <p>The application authorization was obtained successfully.
2136 <?php
2137                                 if($this->debug)
2138                                 {
2139 ?>
2140 <br>Access token: <?php echo HtmlSpecialChars($this->access_token);
2141                                         if(IsSet($this->access_token_secret))
2142                                         {
2143 ?>
2144 <br>Access token secret: <?php echo HtmlSpecialChars($this->access_token_secret);
2145                                         }
2146                                 }
2147 ?></p>
2148 <?php
2149                                 if(strlen($this->access_token_expiry))
2150                                 {
2151 ?>
2152 <p>Access token expiry: <?php echo $this->access_token_expiry; ?> UTC</p>
2153 <?php
2154                                 }
2155                         }
2156 ?>
2157 </body>
2158 </html>
2159 <?php
2160                 }
2161         }
2162 /*
2163 {metadocument}
2164                 </do>
2165         </function>
2166 {/metadocument}
2167 */
2168
2169 };
2170
2171 /*
2172
2173 {metadocument}
2174 </class>
2175 {/metadocument}
2176
2177 */
2178
2179 ?>