]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/actions/pushhub.php
Merge branch '0.9.x' of git@gitorious.org:statusnet/mainline into 0.9.x
[quix0rs-gnu-social.git] / plugins / OStatus / actions / pushhub.php
1 <?php
2 /*
3  * StatusNet - the distributed open-source microblogging tool
4  * Copyright (C) 2010, StatusNet, Inc.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /**
21  * Integrated PuSH hub; lets us only ping them what need it.
22  * @package Hub
23  * @maintainer Brion Vibber <brion@status.net>
24  */
25
26 /**
27
28
29 Things to consider...
30 * should we purge incomplete subscriptions that never get a verification pingback?
31 * when can we send subscription renewal checks?
32     - at next send time probably ok
33 * when can we handle trimming of subscriptions?
34     - at next send time probably ok
35 * should we keep a fail count?
36
37 */
38
39
40 class PushHubAction extends Action
41 {
42     function arg($arg, $def=null)
43     {
44         // PHP converts '.'s in incoming var names to '_'s.
45         // It also merges multiple values, which'll break hub.verify and hub.topic for publishing
46         // @fixme handle multiple args
47         $arg = str_replace('.', '_', $arg);
48         return parent::arg($arg, $def);
49     }
50
51     function prepare($args)
52     {
53         StatusNet::setApi(true); // reduce exception reports to aid in debugging
54         return parent::prepare($args);
55     }
56
57     function handle()
58     {
59         $mode = $this->trimmed('hub.mode');
60         switch ($mode) {
61         case "subscribe":
62             $this->subscribe();
63             break;
64         case "unsubscribe":
65             $this->unsubscribe();
66             break;
67         case "publish":
68             throw new ServerException("Publishing outside feeds not supported.", 400);
69         default:
70             throw new ServerException("Unrecognized mode '$mode'.", 400);
71         }
72     }
73
74     /**
75      * Process a PuSH feed subscription request.
76      *
77      * HTTP return codes:
78      *   202 Accepted - request saved and awaiting verification
79      *   204 No Content - already subscribed
80      *   403 Forbidden - rejecting this (not specifically spec'd)
81      */
82     function subscribe()
83     {
84         $feed = $this->argUrl('hub.topic');
85         $callback = $this->argUrl('hub.callback');
86
87         common_log(LOG_DEBUG, __METHOD__ . ": checking sub'd to $feed $callback");
88         if ($this->getSub($feed, $callback)) {
89             // Already subscribed; return 204 per spec.
90             header('HTTP/1.1 204 No Content');
91             common_log(LOG_DEBUG, __METHOD__ . ': already subscribed');
92             return;
93         }
94
95         common_log(LOG_DEBUG, __METHOD__ . ': setting up');
96         $sub = new HubSub();
97         $sub->topic = $feed;
98         $sub->callback = $callback;
99         $sub->secret = $this->arg('hub.secret', null);
100         $sub->setLease(intval($this->arg('hub.lease_seconds')));
101
102         // @fixme check for feeds we don't manage
103         // @fixme check the verification mode, might want a return immediately?
104
105         common_log(LOG_DEBUG, __METHOD__ . ': inserting');
106         $ok = $sub->insert();
107         
108         if (!$ok) {
109             throw new ServerException("Failed to save subscription record", 500);
110         }
111
112         // @fixme check errors ;)
113
114         $data = array('sub' => $sub, 'mode' => 'subscribe');
115         $qm = QueueManager::get();
116         $qm->enqueue($data, 'hubverify');
117         
118         header('HTTP/1.1 202 Accepted');
119         common_log(LOG_DEBUG, __METHOD__ . ': done');
120     }
121
122     /**
123      * Process a PuSH feed unsubscription request.
124      *
125      * HTTP return codes:
126      *   202 Accepted - request saved and awaiting verification
127      *   204 No Content - already subscribed
128      *   400 Bad Request - invalid params or rejected feed
129      */
130     function unsubscribe()
131     {
132         $feed = $this->argUrl('hub.topic');
133         $callback = $this->argUrl('hub.callback');
134         $sub = $this->getSub($feed, $callback);
135         
136         if ($sub) {
137             if ($sub->verify('unsubscribe')) {
138                 $sub->delete();
139                 common_log(LOG_INFO, "PuSH unsubscribed $feed for $callback");
140             } else {
141                 throw new ServerException("Failed PuSH unsubscription: verification failed! $feed for $callback");
142             }
143         } else {
144             throw new ServerException("Failed PuSH unsubscription: not subscribed! $feed for $callback");
145         }
146     }
147
148     /**
149      * Grab and validate a URL from POST parameters.
150      * @throws ServerException for malformed or non-http/https URLs
151      */
152     protected function argUrl($arg)
153     {
154         $url = $this->arg($arg);
155         $params = array('domain_check' => false, // otherwise breaks my local tests :P
156                         'allowed_schemes' => array('http', 'https'));
157         if (Validate::uri($url, $params)) {
158             return $url;
159         } else {
160             throw new ServerException("Invalid URL passed for $arg: '$url'", 400);
161         }
162     }
163
164     /**
165      * Get HubSub subscription record for a given feed & subscriber.
166      *
167      * @param string $feed
168      * @param string $callback
169      * @return mixed HubSub or false
170      */
171     protected function getSub($feed, $callback)
172     {
173         return HubSub::staticGet($feed, $callback);
174     }
175 }
176