snyammerclient.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. /*
  3. * StatusNet - the distributed open-source microblogging tool
  4. * Copyright (C) 2010, StatusNet, Inc.
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU Affero General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU Affero General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Affero General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /**
  20. * Basic client class for Yammer's OAuth/JSON API.
  21. *
  22. * @package YammerImportPlugin
  23. * @author Brion Vibber <brion@status.net>
  24. */
  25. class SNYammerClient
  26. {
  27. protected $apiBase = "https://www.yammer.com";
  28. protected $consumerKey, $consumerSecret;
  29. protected $token, $tokenSecret, $verifier;
  30. public function __construct($consumerKey, $consumerSecret, $token=null, $tokenSecret=null)
  31. {
  32. $this->consumerKey = $consumerKey;
  33. $this->consumerSecret = $consumerSecret;
  34. $this->token = $token;
  35. $this->tokenSecret = $tokenSecret;
  36. }
  37. /**
  38. * Make an HTTP GET request with OAuth headers and return an HTTPResponse
  39. * with the returned body and codes.
  40. *
  41. * @param string $url
  42. * @return HTTPResponse
  43. *
  44. * @throws Exception on low-level network error
  45. */
  46. protected function httpGet($url)
  47. {
  48. $headers = array('Authorization: ' . $this->authHeader());
  49. $client = HTTPClient::start();
  50. return $client->get($url, $headers);
  51. }
  52. /**
  53. * Make an HTTP GET request with OAuth headers and return the response body
  54. * on success.
  55. *
  56. * @param string $url
  57. * @return string
  58. *
  59. * @throws Exception on low-level network or HTTP error
  60. */
  61. public function fetchUrl($url)
  62. {
  63. $response = $this->httpGet($url);
  64. if ($response->isOk()) {
  65. return $response->getBody();
  66. } else {
  67. // TRANS: Exeption thrown when an external Yammer system gives an error.
  68. // TRANS: %1$s is an HTTP error code, %2$s is the error message body.
  69. throw new Exception(sprintf(_m('Yammer API returned HTTP code %1$s: %2$s'),
  70. $response->getStatus(),
  71. $response->getBody()));
  72. }
  73. }
  74. /**
  75. * Make an HTTP hit with OAuth headers and return the response body on success.
  76. *
  77. * @param string $path URL chunk for the API method
  78. * @param array $params
  79. * @return string
  80. *
  81. * @throws Exception on low-level network or HTTP error
  82. */
  83. protected function fetchApi($path, $params=array())
  84. {
  85. $url = $this->apiBase . '/' . $path;
  86. if ($params) {
  87. $url .= '?' . http_build_query($params, null, '&');
  88. }
  89. return $this->fetchUrl($url);
  90. }
  91. /**
  92. * Hit the main Yammer API point and decode returned JSON data.
  93. *
  94. * @param string $method
  95. * @param array $params
  96. * @return array from JSON data
  97. *
  98. * @throws Exception for HTTP error or bad JSON return
  99. */
  100. public function api($method, $params=array())
  101. {
  102. $body = $this->fetchApi("api/v1/$method.json", $params);
  103. $data = json_decode($body, true);
  104. if ($data === null) {
  105. common_log(LOG_ERR, "Invalid JSON response from Yammer API: " . $body);
  106. // TRANS: Exeption thrown when an external Yammer system an invalid JSON response.
  107. throw new Exception(_m('Invalid JSON response from Yammer API.'));
  108. }
  109. return $data;
  110. }
  111. /**
  112. * Build an Authorization header value from the keys we have available.
  113. */
  114. protected function authHeader()
  115. {
  116. // token
  117. // token_secret
  118. $params = array('realm' => '',
  119. 'oauth_consumer_key' => $this->consumerKey,
  120. 'oauth_signature_method' => 'PLAINTEXT',
  121. 'oauth_timestamp' => time(),
  122. 'oauth_nonce' => time(),
  123. 'oauth_version' => '1.0');
  124. if ($this->token) {
  125. $params['oauth_token'] = $this->token;
  126. }
  127. if ($this->tokenSecret) {
  128. $params['oauth_signature'] = $this->consumerSecret . '&' . $this->tokenSecret;
  129. } else {
  130. $params['oauth_signature'] = $this->consumerSecret . '&';
  131. }
  132. if ($this->verifier) {
  133. $params['oauth_verifier'] = $this->verifier;
  134. }
  135. $parts = array_map(array($this, 'authHeaderChunk'), array_keys($params), array_values($params));
  136. return 'OAuth ' . implode(', ', $parts);
  137. }
  138. /**
  139. * Encode a key-value pair for use in an authentication header.
  140. *
  141. * @param string $key
  142. * @param string $val
  143. * @return string
  144. */
  145. protected function authHeaderChunk($key, $val)
  146. {
  147. return urlencode($key) . '="' . urlencode($val) . '"';
  148. }
  149. /**
  150. * Ask the Yammer server for a request token, which can be passed on
  151. * to authorizeUrl() for the user to start the authentication process.
  152. *
  153. * @return array of oauth return data; should contain nice things
  154. */
  155. public function requestToken()
  156. {
  157. if ($this->token || $this->tokenSecret) {
  158. // TRANS: Exeption thrown when a trust relationship has already been established.
  159. throw new Exception(_m('Requesting a token, but already set up with a token.'));
  160. }
  161. $data = $this->fetchApi('oauth/request_token');
  162. $arr = array();
  163. parse_str($data, $arr);
  164. return $arr;
  165. }
  166. /**
  167. * Get a final access token from the verifier/PIN code provided to
  168. * the user from Yammer's auth pages.
  169. *
  170. * @return array of oauth return data; should contain nice things
  171. */
  172. public function accessToken($verifier)
  173. {
  174. $this->verifier = $verifier;
  175. $data = $this->fetchApi('oauth/access_token');
  176. $this->verifier = null;
  177. $arr = array();
  178. parse_str($data, $arr);
  179. return $arr;
  180. }
  181. /**
  182. * Give the URL to send users to to authorize a new app setup.
  183. *
  184. * @param string $token as returned from accessToken()
  185. * @return string URL
  186. */
  187. public function authorizeUrl($token)
  188. {
  189. return $this->apiBase . '/oauth/authorize?oauth_token=' . urlencode($token);
  190. }
  191. /**
  192. * High-level API hit: fetch all messages in the network (up to 20 at a time).
  193. * Return data is the full JSON array returned, including meta and references
  194. * sections.
  195. *
  196. * The matching messages themselves will be in the 'messages' item within.
  197. *
  198. * @param array $options optional set of additional params for the request.
  199. * @return array
  200. *
  201. * @throws Exception on low-level or HTTP error
  202. */
  203. public function messages($params=array())
  204. {
  205. return $this->api('messages', $params);
  206. }
  207. /**
  208. * High-level API hit: fetch all users in the network (up to 50 at a time).
  209. * Return data is the full JSON array returned, listing user items.
  210. *
  211. * The matching messages themselves will be in the 'users' item within.
  212. *
  213. * @param array $options optional set of additional params for the request.
  214. * @return array of JSON-sourced user data arrays
  215. *
  216. * @throws Exception on low-level or HTTP error
  217. */
  218. public function users($params=array())
  219. {
  220. return $this->api('users', $params);
  221. }
  222. /**
  223. * High-level API hit: fetch all groups in the network (up to 20 at a time).
  224. * Return data is the full JSON array returned, listing user items.
  225. *
  226. * The matching messages themselves will be in the 'users' item within.
  227. *
  228. * @param array $options optional set of additional params for the request.
  229. * @return array of JSON-sourced user data arrays
  230. *
  231. * @throws Exception on low-level or HTTP error
  232. */
  233. public function groups($params=array())
  234. {
  235. return $this->api('groups', $params);
  236. }
  237. }