PageRenderTime 52ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/inc/Zend/Feed/Pubsubhubbub/Subscriber/Callback.php

https://github.com/silvanm/rss2twi.php
PHP | 320 lines | 152 code | 27 blank | 141 comment | 35 complexity | e921a1de2651b2f0d333d4399dc08348 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to padraic dot brady at yahoo dot com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Feed_Pubsubhubbub
  17. * @copyright Copyright (c) 2009 Padraic Brady
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. */
  20. /**
  21. * @see Zend_Feed_Pubsubhubbub
  22. */
  23. require_once 'Zend/Feed/Pubsubhubbub.php';
  24. /**
  25. * @see Zend_Feed_Pubsubhubbub
  26. */
  27. require_once 'Zend/Feed/Pubsubhubbub/CallbackAbstract.php';
  28. /**
  29. * @see Zend_Feed_Reader
  30. */
  31. require_once 'Zend/Feed/Reader.php';
  32. /**
  33. * @category Zend
  34. * @package Zend_Feed_Pubsubhubbub
  35. * @copyright Copyright (c) 2009 Padraic Brady
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class Zend_Feed_Pubsubhubbub_Subscriber_Callback
  39. extends Zend_Feed_Pubsubhubbub_CallbackAbstract
  40. {
  41. /**
  42. * Contains the content of any feeds sent as updates to the Callback URL
  43. *
  44. * @var string
  45. */
  46. protected $_feedUpdate = null;
  47. /**
  48. * Holds a manually set subscription key (i.e. identifies a unique
  49. * subscription) which is typical when it is not passed in the query string
  50. * but is part of the Callback URL path, requiring manual retrieval e.g.
  51. * using a route and the Zend_Controller_Action::_getParam() method.
  52. *
  53. * @var string
  54. */
  55. protected $_subscriptionKey = null;
  56. /**
  57. * After verification, this is set to the verified subscription's data.
  58. *
  59. * @var array
  60. */
  61. protected $_currentSubscriptionData = null;
  62. /**
  63. * Set a subscription key to use for the current callback request manually.
  64. * Required if usePathParameter is enabled for the Subscriber.
  65. *
  66. * @param string $key
  67. */
  68. public function setSubscriptionKey($key)
  69. {
  70. $this->_subscriptionKey = $key;
  71. }
  72. /**
  73. * Handle any callback from a Hub Server responding to a subscription or
  74. * unsubscription request. This should be the Hub Server confirming the
  75. * the request prior to taking action on it.
  76. *
  77. * @param array $httpGetData GET data if available and not in $_GET
  78. * @param bool $sendResponseNow Whether to send response now or when asked
  79. */
  80. public function handle(array $httpGetData = null, $sendResponseNow = false)
  81. {
  82. error_log(__METHOD__.":".__LINE__);
  83. if ($httpGetData === null) {
  84. $httpGetData = $_GET;
  85. }
  86. /**
  87. * Handle any feed updates (sorry for the mess :P)
  88. *
  89. * This DOES NOT attempt to process a feed update. Feed updates
  90. * SHOULD be validated/processed by an asynchronous process so as
  91. * to avoid holding up responses to the Hub.
  92. */
  93. if (strtolower($_SERVER['REQUEST_METHOD']) == 'post'
  94. && $this->_hasValidVerifyToken(null, false)
  95. && ($this->_getHeader('Content-Type') == 'application/atom+xml'
  96. || $this->_getHeader('Content-Type') == 'application/rss+xml'
  97. || $this->_getHeader('Content-Type') == 'application/rdf+xml')) {
  98. $this->setFeedUpdate($this->_getRawBody());
  99. $this->getHttpResponse()->setHeader('X-Hub-On-Behalf-Of',
  100. $this->getSubscriberCount());
  101. /**
  102. * Handle any (un)subscribe confirmation requests
  103. */
  104. } elseif ($this->isValidHubVerification($httpGetData)) {
  105. $data = $this->_currentSubscriptionData;
  106. $this->getHttpResponse()->setBody($httpGetData['hub_challenge']);
  107. $data['verified'] = 1;
  108. $this->getStorage()->setSubscription($data['id'], $data);
  109. /**
  110. * Hey, C'mon! We tried everything else!
  111. */
  112. } else {
  113. $this->getHttpResponse()->setHttpResponseCode(404);
  114. }
  115. if ($sendResponseNow) {
  116. $this->sendResponse();
  117. }
  118. }
  119. /**
  120. * Checks validity of the request simply by making a quick pass and
  121. * confirming the presence of all REQUIRED parameters.
  122. *
  123. * @param array $httpGetData
  124. * @return bool
  125. */
  126. public function isValidHubVerification(array $httpGetData)
  127. {
  128. /**
  129. * As per the specification, the hub.verify_token is OPTIONAL. This
  130. * implementation of Pubsubhubbub considers it REQUIRED and will
  131. * always send a hub.verify_token parameter to be echoed back
  132. * by the Hub Server. Therefore, its absence is considered invalid.
  133. */
  134. error_log(__METHOD__.":".__LINE__);
  135. if (strtolower($_SERVER['REQUEST_METHOD']) !== 'get') {
  136. return false;
  137. }
  138. error_log(__METHOD__.":".__LINE__);
  139. $required = array('hub_mode', 'hub_topic',
  140. 'hub_challenge', 'hub_verify_token');
  141. foreach ($required as $key) {
  142. if (!array_key_exists($key, $httpGetData)) {
  143. return false;
  144. }
  145. }
  146. error_log(__METHOD__.":".__LINE__);
  147. if ($httpGetData['hub_mode'] !== 'subscribe'
  148. && $httpGetData['hub_mode'] !== 'unsubscribe') {
  149. return false;
  150. }
  151. error_log(__METHOD__.":".__LINE__);
  152. if ($httpGetData['hub_mode'] == 'subscribe'
  153. && !array_key_exists('hub_lease_seconds', $httpGetData)) {
  154. return false;
  155. }
  156. error_log(__METHOD__.":".__LINE__);
  157. if (!Zend_Uri::check($httpGetData['hub_topic'])) {
  158. return false;
  159. }
  160. error_log(__METHOD__.":".__LINE__);
  161. /**
  162. * Attempt to retrieve any Verification Token Key attached to Callback
  163. * URL's path by our Subscriber implementation
  164. */
  165. if (!$this->_hasValidVerifyToken($httpGetData)) {
  166. return false;
  167. }
  168. return true;
  169. }
  170. /**
  171. * Sets a newly received feed (Atom/RSS) sent by a Hub as an update to a
  172. * Topic we've subscribed to.
  173. *
  174. * @param string $feed
  175. */
  176. public function setFeedUpdate($feed)
  177. {
  178. $this->_feedUpdate = $feed;
  179. }
  180. /**
  181. * Check if any newly received feed (Atom/RSS) update was received
  182. */
  183. public function hasFeedUpdate()
  184. {
  185. if (is_null($this->_feedUpdate)) {
  186. return false;
  187. }
  188. return true;
  189. }
  190. /**
  191. * Gets a newly received feed (Atom/RSS) sent by a Hub as an update to a
  192. * Topic we've subscribed to.
  193. *
  194. * @return string
  195. */
  196. public function getFeedUpdate()
  197. {
  198. return $this->_feedUpdate;
  199. }
  200. /**
  201. * Check for a valid verify_token. By default attempts to compare values
  202. * with that sent from Hub, otherwise merely ascertains its existence.
  203. *
  204. * @param array $httpGetData
  205. * @param bool $checkValue
  206. * @return bool
  207. */
  208. protected function _hasValidVerifyToken(array $httpGetData = null, $checkValue = true)
  209. {
  210. $verifyTokenKey = $this->_detectVerifyTokenKey($httpGetData);
  211. if (empty($verifyTokenKey)) {
  212. return false;
  213. }
  214. $verifyTokenExists = $this->getStorage()->hasSubscription($verifyTokenKey);
  215. if (!$verifyTokenExists) {
  216. return false;
  217. }
  218. if ($checkValue) {
  219. $data = $this->getStorage()->getSubscription($verifyTokenKey);
  220. $verifyToken = $data['verify_token'];
  221. if ($verifyToken !== hash('sha256', $httpGetData['hub_verify_token'])) {
  222. return false;
  223. }
  224. $this->_currentSubscriptionData = $data;
  225. return true;
  226. }
  227. return true;
  228. }
  229. /**
  230. * Attempt to detect the verification token key. This would be passed in
  231. * the Callback URL (which we are handling with this class!) as a URI
  232. * path part (the last part by convention).
  233. *
  234. * @return string
  235. */
  236. protected function _detectVerifyTokenKey(array $httpGetData = null)
  237. {
  238. /**
  239. * Available when sub keys encoding in Callback URL path
  240. */
  241. if (isset($this->_subscriptionKey)) {
  242. return $this->_subscriptionKey;
  243. }
  244. /**
  245. * Available only if allowed by PuSH 0.2 Hubs
  246. */
  247. if (isset($httpGetData['xhub_subscription'])) {
  248. return $httpGetData['xhub_subscription'];
  249. }
  250. /**
  251. * Available (possibly) if corrupted in transit and not part of $_GET
  252. */
  253. $params = $this->_parseQueryString();
  254. if (isset($params['xhub.subscription'])) {
  255. return rawurldecode($params['xhub.subscription']);
  256. }
  257. return false;
  258. }
  259. /**
  260. * Build an array of Query String parameters.
  261. * This bypasses $_GET which munges parameter names and cannot accept
  262. * multiple parameters with the same key.
  263. *
  264. * @return array|void
  265. */
  266. protected function _parseQueryString()
  267. {
  268. $params = array();
  269. $queryString = '';
  270. if (isset($_SERVER['QUERY_STRING'])) {
  271. $queryString = $_SERVER['QUERY_STRING'];
  272. }
  273. if (empty($queryString)) {
  274. return array();
  275. }
  276. $parts = explode('&', $queryString);
  277. foreach ($parts as $kvpair) {
  278. $pair = explode('=', $kvpair);
  279. $key = rawurldecode($pair[0]);
  280. $value = rawurldecode($pair[1]);
  281. if (isset($params[$key])) {
  282. if (is_array($params[$key])) {
  283. $params[$key][] = $value;
  284. } else {
  285. $params[$key] = array($params[$key], $value);
  286. }
  287. } else {
  288. $params[$key] = $value;
  289. }
  290. }
  291. return $params;
  292. }
  293. }