X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=plugins%2FRSSCloud%2FRSSCloudRequestNotify.php;h=e9c0eab5f881e6a4ed6046806e9fdcd5ce34c17c;hb=59119482ca34540bd7f0a2a1aa994de1d5328ea2;hp=1d4087334f206945012334a7a6aa77b2797b4284;hpb=391003c3c65572dd156244390d8eedd2dfd96f90;p=quix0rs-gnu-social.git diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index 1d4087334f..e9c0eab5f8 100644 --- a/plugins/RSSCloud/RSSCloudRequestNotify.php +++ b/plugins/RSSCloud/RSSCloudRequestNotify.php @@ -1,7 +1,7 @@ + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + **/ +class RSSCloudRequestNotifyAction extends Action { - /** * Initialization. * @@ -45,101 +53,278 @@ class RSSCloudRequestNotifyAction extends Action function prepare($args) { parent::prepare($args); + + $this->ip = $_SERVER['REMOTE_ADDR']; + $this->port = $this->arg('port'); + $this->path = $this->arg('path'); + + if ($this->path[0] != '/') { + $this->path = '/' . $this->path; + } + + $this->protocol = $this->arg('protocol'); + $this->procedure = $this->arg('notifyProcedure'); + $this->domain = $this->arg('domain'); + + $this->feeds = $this->getFeeds(); + return true; } + /** + * Handle the request + * + * Checks for all the required parameters for a subscription, + * validates that the feed being subscribed to is real, and then + * saves the subsctiption. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ function handle($args) { parent::handle($args); - + if ($_SERVER['REQUEST_METHOD'] != 'POST') { - showResult(false, 'Request must be POST.'); + $this->showResult(false, _m('Request must be POST.')); + return; } - - $ip = $_SERVER['REMOTE_ADDR']; + $missing = array(); - $port = $this->arg('port'); - + if (empty($this->port)) { $missing[] = 'port'; } - - $path = $this->arg('path'); if (empty($this->path)) { $missing[] = 'path'; } - - $protocol = $this->arg('protocol'); if (empty($this->protocol)) { $missing[] = 'protocol'; + } else if (strtolower($this->protocol) != 'http-post') { + $msg = _m('Only http-post notifications are supported at this time.'); + $this->showResult(false, $msg); + return; } - - if (empty($this->notifyProcedure)) { + + if (!isset($this->procedure)) { $missing[] = 'notifyProcedure'; } - + if (!empty($missing)) { - $msg = 'The following parameters were missing from the request body: ' . - implode(',', $missing) . '.'; + // TRANS: %s is a comma separated list of parameters. + $msg = sprintf(_m('The following parameters were missing from the request body: %s.'),implode(', ', $missing)); + $this->showResult(false, $msg); + return; + } + + if (empty($this->feeds)) { + $msg = _m('You must provide at least one valid profile feed url ' . + '(url1, url2, url3 ... urlN).'); $this->showResult(false, $msg); + return; } - - $feeds = $this->getFeeds(); - - if (empty($feeds)) { - $this->showResult(false, - 'You must provide at least one feed url (url1, url2, url3 ... urlN).'); - } - - $endpoint = $ip . ':' . $port . $path; - - foreach ($feeds as $feed) { - - } - - + + // We have to validate everything before saving anything. + // We only return one success or failure no matter how + // many feeds the subscriber is trying to subscribe to + foreach ($this->feeds as $feed) { + + if (!$this->validateFeed($feed)) { + + $nh = $this->getNotifyUrl(); + common_log(LOG_WARNING, + "RSSCloud plugin - $nh tried to subscribe to invalid feed: $feed"); + + $msg = _m('Feed subscription failed: Not a valid feed.'); + $this->showResult(false, $msg); + return; + } + + if (!$this->testNotificationHandler($feed)) { + $msg = _m('Feed subscription failed - ' . + 'notification handler doesn\'t respond correctly.'); + $this->showResult(false, $msg); + return; + } + } + + foreach ($this->feeds as $feed) { + $this->saveSubscription($feed); + } + + // XXX: What to do about deleting stale subscriptions? + // 25 hours seems harsh. WordPress doesn't ever remove + // subscriptions. + $msg = _m('Thanks for the subscription. ' . + 'When the feed(s) update(s), you will be notified.'); + + $this->showResult(true, $msg); } - - + + /** + * Validate that the requested feed is one we serve + * up via RSSCloud. + * + * @param string $feed the feed in question + * + * @return void + */ + function validateFeed($feed) + { + $user = $this->userFromFeed($feed); + + if (empty($user)) { + return false; + } + + return true; + } + + /** + * Pull all of the urls (url1, url2, url3...urlN) that + * the subscriber wants to subscribe to. + * + * @return array $feeds the list of feeds + */ function getFeeds() { $feeds = array(); - - foreach ($this->args as $key => $feed ) { - if (preg_match('|url\d+|', $key)) { - - // XXX: validate feeds somehow and kick bad ones out - + + while (list($key, $feed) = each($this->args)) { + if (preg_match('/^url\d*$/', $key)) { $feeds[] = $feed; } } - + return $feeds; } - - - function checkNotifyHandler() + + /** + * Test that a notification handler is there and is reponding + * correctly. This is called before adding a subscription. + * + * @param string $feed the feed to verify + * + * @return boolean success result + */ + function testNotificationHandler($feed) { - + $notifyUrl = $this->getNotifyUrl(); + + $notifier = new RSSCloudNotifier(); + + if (isset($this->domain)) { + // 'domain' param set, so we have to use GET and send a challenge + common_log(LOG_INFO, + 'RSSCloud plugin - Testing notification handler with challenge: ' . + $notifyUrl); + return $notifier->challenge($notifyUrl, $feed); + + } else { + common_log(LOG_INFO, 'RSSCloud plugin - Testing notification handler: ' . + $notifyUrl); + + return $notifier->postUpdate($notifyUrl, $feed); + } } - - function validateFeed() + + /** + * Build the URL for the notification handler based on the + * parameters passed in with the subscription request. + * + * @return string notification handler url + */ + function getNotifyUrl() { + if (isset($this->domain)) { + return 'http://' . $this->domain . ':' . $this->port . $this->path; + } else { + return 'http://' . $this->ip . ':' . $this->port . $this->path; + } } - - function showResult($success, $msg) + + /** + * Uses the nickname part of the subscribed feed URL to figure out + * whethere there's really a user with such a feed. Used to + * validate feeds before adding a subscription. + * + * @param string $feed the feed in question + * + * @return boolean success + */ + function userFromFeed($feed) { - $this->startXML(); - $this->elementStart('notifyResult', array('success' => ($success) ? 'true' : 'false', - 'msg' => $msg)); - $this->endXML(); - + // We only do canonical RSS2 profile feeds (specified by ID), e.g.: + // http://www.example.com/api/statuses/user_timeline/2.rss + $path = common_path('api/statuses/user_timeline/'); + $valid = '%^' . $path . '(?.*)\.rss$%'; + + if (preg_match($valid, $feed, $matches)) { + $user = User::staticGet('id', $matches['id']); + if (!empty($user)) { + return $user; + } + } + + return false; } - - -} + /** + * Save an RSSCloud subscription + * + * @param string $feed a valid profile feed + * + * @return boolean success result + */ + function saveSubscription($feed) + { + $user = $this->userFromFeed($feed); + + $notifyUrl = $this->getNotifyUrl(); + + $sub = RSSCloudSubscription::getSubscription($user->id, $notifyUrl); + + if ($sub) { + common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl refreshed subscription" . + " to user $user->nickname (id: $user->id)."); + } else { + + $sub = new RSSCloudSubscription(); + $sub->subscribed = $user->id; + $sub->url = $notifyUrl; + $sub->created = common_sql_now(); + if (!$sub->insert()) { + common_log_db_error($sub, 'INSERT', __FILE__); + return false; + } + + common_log(LOG_INFO, "RSSCloud plugin - $notifyUrl subscribed" . + " to user $user->nickname (id: $user->id)"); + } + + return true; + } + + /** + * Show an XML message indicating the subscription + * was successful or failed. + * + * @param boolean $success whether it was good or bad + * @param string $msg the message to output + * + * @return boolean success result + */ + function showResult($success, $msg) + { + $this->startXML(); + $this->elementStart('notifyResult', + array('success' => ($success) ? 'true' : 'false', + 'msg' => $msg)); + $this->endXML(); + } +}