Skip to content
Newer
Older
100644 204 lines (167 sloc) 6.8 KB
4d41036 @michaelcurtis first commit
authored
1 <?php
2 require_once realpath(dirname(__FILE__)) . "/OAuth.php";
3
4 define('JSON11_ENDPOINT_URL', 'http://mail.yahooapis.com/ws/mail/v1.1/jsonrpc');
5 define('OAUTH2_ENDPOINT_URL', 'https://api.login.yahoo.com/oauth/v2');
6
7 class YMClient {
8
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
9 function __construct($oaConsumerKey, $oaConsumerSecret) {
4d41036 @michaelcurtis first commit
authored
10 $this->oaConsumerKey = $oaConsumerKey;
11 $this->oaConsumerSecret = $oaConsumerSecret;
12 $this->signature = new OAuthSignatureMethod_HMAC_SHA1();
13 }
14
15 function __call($method, $arguments) {
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
16 $this->oaRefreshedToken = null;
17
18 list ($params, $tok) = $arguments;
19
20 if(!$tok) {
21 throw new YMClientException("Missing oauth access token", 0, null);
22 }
23
4d41036 @michaelcurtis first commit
authored
24 $request = new stdclass();
25 $request->method = $method;
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
26 $request->params = $params;
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
27
4d41036 @michaelcurtis first commit
authored
28 // Create a loop around the cascade request in case
29 // the access token needs to be refreshed.
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
30 for($attemptNo = 0; $attemptNo < 2; $attemptNo++) {
4d41036 @michaelcurtis first commit
authored
31 $ch = curl_init(JSON11_ENDPOINT_URL);
32 curl_setopt($ch, CURLOPT_POST, 1);
33 curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request));
34 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
35 curl_setopt($ch, CURLOPT_HTTPHEADER, array(
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
36 'Content-Type: application/json',
37 'Accept: application/json',
38 $this->__build_oauth_header($tok)
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
39 ));
4d41036 @michaelcurtis first commit
authored
40 $rawresponse = curl_exec($ch);
41 $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
42 $responseContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
43 curl_close($ch);
44
45 if($responseContentType === "application/json") {
46 if($rawresponse == "") {
47 throw new YMClientException("Empty response", 0, "The Ymail webservice returned an empty response");
48 }
49
a6fa6f5 @michaelcurtis Added check for 'token_expired' message in 401 response from Cascade.
authored
50 $response = json_decode($rawresponse);
51
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
52 // Cascade returned an "unauthorized" response. Try to refresh the access token
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
53 if($responseCode == 401) {
a6fa6f5 @michaelcurtis Added check for 'token_expired' message in 401 response from Cascade.
authored
54
55 // The token expired, attempt to refresh it
56 if(isset($response->error->description) && preg_match("/token_expired/", $response->error->description)) {
57 $tok = $this->__oauth_refresh_access_token($tok);
58 $this->oaRefreshedToken = $tok;
59 }
60
61 // Some other error occured. Forward along info about it.
62 else {
63 throw new YMClientException("Ymail request failed", $responseCode, $response);
64 }
4d41036 @michaelcurtis first commit
authored
65 }
66
67 else {
68 return $response->result;
69 }
70 }
71
72 else {
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
73 // Cascade returned a malformed response. Forward along info about it.
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
74 throw new YMClientException("Ymail request failed", $responseCode, "Bad response from Ymail: HTTP $responseCode, Content-Type: $responseContentType");
4d41036 @michaelcurtis first commit
authored
75 }
76 }
77 }
78
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
79 function oauth_get_access_token($tok) {
4d41036 @michaelcurtis first commit
authored
80 $oaReqParams = array(
81 'oauth_nonce' => OAuthRequest::generate_nonce(),
82 'oauth_timestamp' => OAuthRequest::generate_timestamp(),
83 'oauth_consumer_key' => $this->oaConsumerKey,
84 'oauth_version' => '1.0',
85 'oauth_signature_method' => 'PLAINTEXT', //'HMAC-SHA1' //FIXME: Even needed??
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
86 'oauth_token' => $tok['oauth_token']
4d41036 @michaelcurtis first commit
authored
87 );
88
89 // If the passed token has a verifier add it to the OAuth parameters. This
90 // only happens when requesting a new access token (instead of doing a refresh
91 // on an existing access token which don't have verifiers)
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
92 if(isset($tok['oauth_verifier'])) {
93 $oaReqParams['oauth_verifier'] = $tok['oauth_verifier'];
4d41036 @michaelcurtis first commit
authored
94 }
95
96 // If the passed token has a session handle add it to the OAuth parameters.
97 // This happens when we are refreshing an access token.
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
98 if(isset($tok['oauth_session_handle'])) {
99 $oaReqParams['oauth_session_handle'] = $tok['oauth_session_handle'];
4d41036 @michaelcurtis first commit
authored
100 }
101
102 // Do the request
103 $request = new OAuthRequest('GET', (OAUTH2_ENDPOINT_URL . "/get_token"), $oaReqParams);
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
104 $url = $request->to_url() . '&oauth_signature=' . $this->oaConsumerSecret . '%26' . $tok['oauth_token_secret'];
4d41036 @michaelcurtis first commit
authored
105 $ch = curl_init();
106 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
107 curl_setopt($ch, CURLOPT_URL, $url);
108 $resp = curl_exec($ch);
109 curl_close($ch);
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
110 parse_str($resp, $newtok);
4d41036 @michaelcurtis first commit
authored
111
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
112 if(!$newtok['oauth_token'] || !$newtok['oauth_token_secret']) {
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
113 throw new YMClientException("Access token request failed", 0, $resp);
4d41036 @michaelcurtis first commit
authored
114 }
115
fcb1612 @michaelcurtis Fixed up expired access token handling.
authored
116 return $newtok;
4d41036 @michaelcurtis first commit
authored
117 }
118
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
119 function oauth_get_request_token($callbackURL) {
4d41036 @michaelcurtis first commit
authored
120 $request = new OAuthRequest('GET', (OAUTH2_ENDPOINT_URL . "/get_request_token"), array(
121 'oauth_nonce' => OAuthRequest::generate_nonce(),
122 'oauth_timestamp' => OAuthRequest::generate_timestamp(),
123 'oauth_version' => '1.0',
124 'oauth_signature_method' => 'HMAC-SHA1',
125 'oauth_consumer_key' => $this->oaConsumerKey,
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
126 'oauth_callback' => $callbackURL));
4d41036 @michaelcurtis first commit
authored
127
2c24894 @michaelcurtis added urlencoding of the oauth signature
authored
128 $url = $request->to_url() . "&oauth_signature=" . urlencode($this->signature->build_signature($request, new OAuthConsumer('', $this->oaConsumerSecret), NULL));
4d41036 @michaelcurtis first commit
authored
129
130 $ch = curl_init();
131 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
132 curl_setopt($ch, CURLOPT_URL, $url);
133 $resp = curl_exec($ch);
134 curl_close($ch);
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
135 parse_str($resp, $tok);
136 if(!$tok['oauth_token'] || !$tok['oauth_token_secret']) {
137 throw new YMClientException("Request token request failed", 0, $resp);
4d41036 @michaelcurtis first commit
authored
138 }
139
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
140 return array($tok, (OAUTH2_ENDPOINT_URL . "/request_auth?oauth_token=" . $tok['oauth_token']));
4d41036 @michaelcurtis first commit
authored
141 }
142
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
143 function oauth_get_refreshed_token() {
144 if($this->oaRefreshedToken) {
145 return $this->oaRefreshedToken;
4d41036 @michaelcurtis first commit
authored
146 }
147 }
148
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
149 public static function oauth_token_from_query_string($s) {
4d41036 @michaelcurtis first commit
authored
150 parse_str($s, $tokens);
151 return $tokens;
152 }
153
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
154 public static function oauth_token_to_query_string($tok) {
4d41036 @michaelcurtis first commit
authored
155 $a = array();
156 foreach($tok as $k => $v) {
157 array_push($a, ("$k=" . OAuthUtil::urlencodeRFC3986($v)));
158 }
159
160 return implode("&", $a);
161 }
162
8660b1b @michaelcurtis Refactored to separate app-specific cookie logic from the core library.
authored
163 private function __oauth_refresh_access_token($tok) {
164 if(!isset($tok['oauth_session_handle'])) {
165 throw new YMClientException("Failed to refresh access token without a session handle.", 0, null);
166 }
167
168 return $this->oauth_get_access_token($tok);
169 }
170
171 private function __build_oauth_header($tok) {
4d41036 @michaelcurtis first commit
authored
172 $request = new OAuthRequest('POST', JSON11_ENDPOINT_URL, array(
173 'oauth_nonce' => OAuthRequest::generate_nonce(),
174 'oauth_timestamp' => OAuthRequest::generate_timestamp(),
175 'oauth_version' => '1.0',
176 'oauth_signature_method' => 'HMAC-SHA1',
177 'oauth_consumer_key' => $this->oaConsumerKey,
178 'oauth_token' => $tok['oauth_token']
179 ));
180
181 $request->sign_request($this->signature, new OAuthConsumer('', $this->oaConsumerSecret), new OAuthToken('', $tok['oauth_token_secret']));
182 return $request->to_header();
183 }
184 }
185
186 class YMClientException extends Exception {
187 private $errorCode;
188 private $detail;
189
190 public function __construct($message, $code, $detail) {
191 parent::__construct($message);
192 $this->errorCode = $code;
193 $this->detail = $detail;
194 }
195
196 public function getErrorCode() {
197 return $this->errorCode;
198 }
199
200 public function getDetail() {
201 return $this->detail;
202 }
203 }
204 ?>
Something went wrong with that request. Please try again.