PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/zendframework/zend-feed/src/PubSubHubbub/Subscriber/Callback.php

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