/library/Zend/Feed/Pubsubhubbub/Subscriber/Callback.php
PHP | 330 lines | 162 code | 20 blank | 148 comment | 36 complexity | 4073a3ebb939028729680c6279d9e539 MD5 | raw file
- <?php
- /**
- * Zend Framework
- *
- * LICENSE
- *
- * This source file is subject to the new BSD license that is bundled
- * with this package in the file LICENSE.txt.
- * It is also available through the world-wide-web at this URL:
- * http://framework.zend.com/license/new-bsd
- * If you did not receive a copy of the license and are unable to
- * obtain it through the world-wide-web, please send an email
- * to license@zend.com so we can send you a copy immediately.
- *
- * @category Zend
- * @package Zend_Feed_Pubsubhubbub
- * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- * @version $Id: Callback.php 23775 2011-03-01 17:25:24Z ralph $
- */
-
- /**
- * @see Zend_Feed_Pubsubhubbub
- */
- require_once 'Zend/Feed/Pubsubhubbub.php';
-
- /**
- * @see Zend_Feed_Pubsubhubbub
- */
- require_once 'Zend/Feed/Pubsubhubbub/CallbackAbstract.php';
-
- /**
- * @see Zend_Feed_Reader
- */
- require_once 'Zend/Feed/Reader.php';
-
- /**
- * @category Zend
- * @package Zend_Feed_Pubsubhubbub
- * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
- class Zend_Feed_Pubsubhubbub_Subscriber_Callback
- extends Zend_Feed_Pubsubhubbub_CallbackAbstract
- {
- /**
- * Contains the content of any feeds sent as updates to the Callback URL
- *
- * @var string
- */
- protected $_feedUpdate = null;
-
- /**
- * Holds a manually set subscription key (i.e. identifies a unique
- * subscription) which is typical when it is not passed in the query string
- * but is part of the Callback URL path, requiring manual retrieval e.g.
- * using a route and the Zend_Controller_Action::_getParam() method.
- *
- * @var string
- */
- protected $_subscriptionKey = null;
-
- /**
- * After verification, this is set to the verified subscription's data.
- *
- * @var array
- */
- protected $_currentSubscriptionData = null;
-
- /**
- * Set a subscription key to use for the current callback request manually.
- * Required if usePathParameter is enabled for the Subscriber.
- *
- * @param string $key
- * @return Zend_Feed_Pubsubhubbub_Subscriber_Callback
- */
- public function setSubscriptionKey($key)
- {
- $this->_subscriptionKey = $key;
- return $this;
- }
-
- /**
- * Handle any callback from a Hub Server responding to a subscription or
- * unsubscription request. This should be the Hub Server confirming the
- * the request prior to taking action on it.
- *
- * @param array $httpGetData GET data if available and not in $_GET
- * @param bool $sendResponseNow Whether to send response now or when asked
- * @return void
- */
- public function handle(array $httpGetData = null, $sendResponseNow = false)
- {
- if ($httpGetData === null) {
- $httpGetData = $_GET;
- }
-
- /**
- * Handle any feed updates (sorry for the mess :P)
- *
- * This DOES NOT attempt to process a feed update. Feed updates
- * SHOULD be validated/processed by an asynchronous process so as
- * to avoid holding up responses to the Hub.
- */
- $contentType = $this->_getHeader('Content-Type');
- if (strtolower($_SERVER['REQUEST_METHOD']) == 'post'
- && $this->_hasValidVerifyToken(null, false)
- && (stripos($contentType, 'application/atom+xml') === 0
- || stripos($contentType, 'application/rss+xml') === 0
- || stripos($contentType, 'application/xml') === 0
- || stripos($contentType, 'text/xml') === 0
- || stripos($contentType, 'application/rdf+xml') === 0)
- ) {
- $this->setFeedUpdate($this->_getRawBody());
- $this->getHttpResponse()
- ->setHeader('X-Hub-On-Behalf-Of', $this->getSubscriberCount());
- /**
- * Handle any (un)subscribe confirmation requests
- */
- } elseif ($this->isValidHubVerification($httpGetData)) {
- $data = $this->_currentSubscriptionData;
- $this->getHttpResponse()->setBody($httpGetData['hub_challenge']);
- $data['subscription_state'] = Zend_Feed_Pubsubhubbub::SUBSCRIPTION_VERIFIED;
- if (isset($httpGetData['hub_lease_seconds'])) {
- $data['lease_seconds'] = $httpGetData['hub_lease_seconds'];
- }
- $this->getStorage()->setSubscription($data);
- /**
- * Hey, C'mon! We tried everything else!
- */
- } else {
- $this->getHttpResponse()->setHttpResponseCode(404);
- }
- if ($sendResponseNow) {
- $this->sendResponse();
- }
- }
-
- /**
- * Checks validity of the request simply by making a quick pass and
- * confirming the presence of all REQUIRED parameters.
- *
- * @param array $httpGetData
- * @return bool
- */
- public function isValidHubVerification(array $httpGetData)
- {
- /**
- * As per the specification, the hub.verify_token is OPTIONAL. This
- * implementation of Pubsubhubbub considers it REQUIRED and will
- * always send a hub.verify_token parameter to be echoed back
- * by the Hub Server. Therefore, its absence is considered invalid.
- */
- if (strtolower($_SERVER['REQUEST_METHOD']) !== 'get') {
- return false;
- }
- $required = array(
- 'hub_mode',
- 'hub_topic',
- 'hub_challenge',
- 'hub_verify_token',
- );
- foreach ($required as $key) {
- if (!array_key_exists($key, $httpGetData)) {
- return false;
- }
- }
- if ($httpGetData['hub_mode'] !== 'subscribe'
- && $httpGetData['hub_mode'] !== 'unsubscribe'
- ) {
- return false;
- }
- if ($httpGetData['hub_mode'] == 'subscribe'
- && !array_key_exists('hub_lease_seconds', $httpGetData)
- ) {
- return false;
- }
- if (!Zend_Uri::check($httpGetData['hub_topic'])) {
- return false;
- }
-
- /**
- * Attempt to retrieve any Verification Token Key attached to Callback
- * URL's path by our Subscriber implementation
- */
- if (!$this->_hasValidVerifyToken($httpGetData)) {
- return false;
- }
- return true;
- }
-
- /**
- * Sets a newly received feed (Atom/RSS) sent by a Hub as an update to a
- * Topic we've subscribed to.
- *
- * @param string $feed
- * @return Zend_Feed_Pubsubhubbub_Subscriber_Callback
- */
- public function setFeedUpdate($feed)
- {
- $this->_feedUpdate = $feed;
- return $this;
- }
-
- /**
- * Check if any newly received feed (Atom/RSS) update was received
- *
- * @return bool
- */
- public function hasFeedUpdate()
- {
- if ($this->_feedUpdate === null) {
- return false;
- }
- return true;
- }
-
- /**
- * Gets a newly received feed (Atom/RSS) sent by a Hub as an update to a
- * Topic we've subscribed to.
- *
- * @return string
- */
- public function getFeedUpdate()
- {
- return $this->_feedUpdate;
- }
-
- /**
- * Check for a valid verify_token. By default attempts to compare values
- * with that sent from Hub, otherwise merely ascertains its existence.
- *
- * @param array $httpGetData
- * @param bool $checkValue
- * @return bool
- */
- protected function _hasValidVerifyToken(array $httpGetData = null, $checkValue = true)
- {
- $verifyTokenKey = $this->_detectVerifyTokenKey($httpGetData);
- if (empty($verifyTokenKey)) {
- return false;
- }
- $verifyTokenExists = $this->getStorage()->hasSubscription($verifyTokenKey);
- if (!$verifyTokenExists) {
- return false;
- }
- if ($checkValue) {
- $data = $this->getStorage()->getSubscription($verifyTokenKey);
- $verifyToken = $data['verify_token'];
- if ($verifyToken !== hash('sha256', $httpGetData['hub_verify_token'])) {
- return false;
- }
- $this->_currentSubscriptionData = $data;
- return true;
- }
- return true;
- }
-
- /**
- * Attempt to detect the verification token key. This would be passed in
- * the Callback URL (which we are handling with this class!) as a URI
- * path part (the last part by convention).
- *
- * @param null|array $httpGetData
- * @return false|string
- */
- protected function _detectVerifyTokenKey(array $httpGetData = null)
- {
- /**
- * Available when sub keys encoding in Callback URL path
- */
- if (isset($this->_subscriptionKey)) {
- return $this->_subscriptionKey;
- }
-
- /**
- * Available only if allowed by PuSH 0.2 Hubs
- */
- if (is_array($httpGetData)
- && isset($httpGetData['xhub_subscription'])
- ) {
- return $httpGetData['xhub_subscription'];
- }
-
- /**
- * Available (possibly) if corrupted in transit and not part of $_GET
- */
- $params = $this->_parseQueryString();
- if (isset($params['xhub.subscription'])) {
- return rawurldecode($params['xhub.subscription']);
- }
-
- return false;
- }
-
- /**
- * Build an array of Query String parameters.
- * This bypasses $_GET which munges parameter names and cannot accept
- * multiple parameters with the same key.
- *
- * @return array|void
- */
- protected function _parseQueryString()
- {
- $params = array();
- $queryString = '';
- if (isset($_SERVER['QUERY_STRING'])) {
- $queryString = $_SERVER['QUERY_STRING'];
- }
- if (empty($queryString)) {
- return array();
- }
- $parts = explode('&', $queryString);
- foreach ($parts as $kvpair) {
- $pair = explode('=', $kvpair);
- $key = rawurldecode($pair[0]);
- $value = rawurldecode($pair[1]);
- if (isset($params[$key])) {
- if (is_array($params[$key])) {
- $params[$key][] = $value;
- } else {
- $params[$key] = array($params[$key], $value);
- }
- } else {
- $params[$key] = $value;
- }
- }
- return $params;
- }
- }