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