]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/RSSCloud/RSSCloudRequestNotify.php
Plugin now checks notify handlers before registering subscriptions
[quix0rs-gnu-social.git] / plugins / RSSCloud / RSSCloudRequestNotify.php
1 <?php
2
3 /**
4  * Action to let RSSCloud aggregators request update notification when
5  * user profile feeds change.
6  *
7  * PHP version 5
8  *
9  * @category Plugin
10  * @package  StatusNet
11  * @author   Zach Copley <zach@status.net>
12  * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
13  * @link     http://status.net/
14  *
15  * StatusNet - the distributed open-source microblogging tool
16  * Copyright (C) 2009, StatusNet, Inc.
17  *
18  * This program is free software: you can redistribute it and/or modify
19  * it under the terms of the GNU Affero General Public License as published by
20  * the Free Software Foundation, either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
26  * GNU Affero General Public License for more details.
27  *
28  * You should have received a copy of the GNU Affero General Public License
29  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
30  */
31
32 if (!defined('STATUSNET')) {
33     exit(1);
34 }
35
36 class RSSCloudRequestNotifyAction extends Action
37 {
38     /**
39      * Initialization.
40      *
41      * @param array $args Web and URL arguments
42      *
43      * @return boolean false if user doesn't exist
44      */
45     function prepare($args)
46     {
47         parent::prepare($args);
48
49         $this->ip        = $_SERVER['REMOTE_ADDR'];
50         $this->port      = $this->arg('port');
51         $this->path      = $this->arg('path');
52         $this->protocol  = $this->arg('protocol');
53         $this->procedure = $this->arg('notifyProcedure');
54         $this->domain    = $this->arg('domain');
55
56         $this->feeds     = $this->getFeeds();
57
58         return true;
59     }
60
61     function handle($args)
62     {
63         parent::handle($args);
64
65         if ($_SERVER['REQUEST_METHOD'] != 'POST') {
66             $this->showResult(false, 'Request must be POST.');
67             return;
68         }
69
70         $missing = array();
71
72         if (empty($this->port)) {
73             $missing[] = 'port';
74         }
75
76         $path = $this->arg('path');
77
78         if (empty($this->path)) {
79             $missing[] = 'path';
80         }
81
82         $protocol = $this->arg('protocol');
83
84         if (empty($this->protocol)) {
85             $missing[] = 'protocol';
86         }
87
88         if (!isset($this->procedure)) {
89             $missing[] = 'notifyProcedure';
90         }
91
92         if (!empty($missing)) {
93             $msg = 'The following parameters were missing from the request body: ' .
94                 implode(', ', $missing) . '.';
95             $this->showResult(false, $msg);
96             return;
97         }
98
99         if (empty($this->feeds)) {
100             $this->showResult(false,
101                               'You must provide at least one valid profile feed url (url1, url2, url3 ... urlN).');
102             return;
103         }
104
105         // We have to validate everything before saving anything.
106         // We only return one success or failure no matter how
107         // many feeds the subscriber is trying to subscribe to
108
109         foreach ($this->feeds as $feed) {
110
111             if (!$this->validateFeed($feed)) {
112                 $msg = 'Feed subscription failed - Not a valid feed.';
113                 $this->showResult(false, $msg);
114                 return;
115             }
116
117             if (!$this->testNotificationHandler($feed)) {
118                 $msg = 'Feed subscription failed - ' .
119                 'notification handler doesn\'t respond correctly.';
120                 $this->showResult(false, $msg);
121                 return;
122             }
123
124         }
125
126         foreach ($this->feeds as $feed) {
127             $this->saveSubscription($feed);
128         }
129
130         // XXX: What to do about deleting stale subscriptions?  25 hours seems harsh.
131         // WordPress doesn't ever remove subscriptions.
132
133         $msg = 'Thanks for the registration. It worked. When the feed(s) update(s) we\'ll notify you. ' .
134                ' Don\'t forget to re-register after 24 hours, your subscription will expire in 25.';
135
136         $this->showResult(true, $msg);
137     }
138
139     function validateFeed($feed)
140     {
141         $user = $this->userFromFeed($feed);
142
143         if (empty($user)) {
144             return false;
145         }
146
147         return true;
148     }
149
150     function getFeeds()
151     {
152         $feeds = array();
153
154         while (list($key, $feed) = each ($this->args)) {
155             if (preg_match('/^url\d*$/', $key)) {
156                 $feeds[] = $feed;
157             }
158         }
159
160         return $feeds;
161     }
162
163     function testNotificationHandler($feed)
164     {
165         common_debug("RSSCloudPlugin - testNotificationHandler()");
166
167         $notifier = new RSSCloudNotifier();
168
169         if (isset($this->domain)) {
170
171             // 'domain' param set, so we have to use GET and send a challenge
172
173             $endpoint = 'http://' . $this->domain . ':' . $this->port . '/' . $this->path;
174
175             common_log(LOG_INFO, 'Testing notification handler with challenge: ' .
176                        $endpoint);
177
178             return $notifier->challenge($endpoint, $feed);
179
180         } else {
181
182             $endpoint = 'http://' . $this->ip . ':' . $this->port . '/' . $this->path;
183
184             common_log(LOG_INFO, 'Testing notification handler: ' .
185                        $endpoint);
186
187             return $notifier->postUpdate($endpoint, $feed);
188         }
189
190     }
191
192     function userFromFeed($feed)
193     {
194         // We only do profile feeds
195
196         // XXX: Add cloud element to RSS 1.0 feeds
197
198         $path = common_path('api/statuses/user_timeline/');
199         $valid = '%^' . $path . '(?<nickname>.*)\.rss$%';
200
201         if (preg_match($valid, $feed, $matches)) {
202             $user = User::staticGet('nickname', $matches['nickname']);
203             if (!empty($user)) {
204                 return $user;
205             }
206         }
207
208         return false;
209     }
210
211     function saveSubscription($feed)
212     {
213         $user = $this->userFromFeed($feed);
214
215         $sub = RSSCloudSubscription::getSubscription($user->id, $this->url);
216
217         if ($sub) {
218             common_debug("Already subscribed to that!");
219         } else {
220
221             $sub = new RSSCloudSubscription();
222
223             $sub->subscribed = $user->id;
224             $sub->url        = $this->url;
225             $sub->created    = common_sql_now();
226
227             // auto timestamp doesn't seem to work for me
228
229             // $sub->modified   = common_sql_now();
230
231             if (!$sub->insert()) {
232                 common_log_db_error($sub, 'INSERT', __FILE__);
233                 return false;
234             }
235
236             DB_DataObject::debugLevel();
237         }
238
239         return true;
240     }
241
242     function showResult($success, $msg)
243     {
244         $this->startXML();
245         $this->elementStart('notifyResult', array('success' => ($success) ? 'true' : 'false',
246                                                   'msg'     => $msg));
247         $this->endXML();
248     }
249
250 }
251