]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/finishremotesubscribe.php
change function headers to K&R style
[quix0rs-gnu-social.git] / actions / finishremotesubscribe.php
1 <?php
2 /*
3  * Laconica - a distributed open-source microblogging tool
4  * Copyright (C) 2008, Controlez-Vous, 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 if (!defined('LACONICA')) { exit(1); }
21
22 require_once(INSTALLDIR.'/lib/omb.php');
23
24 class FinishremotesubscribeAction extends Action {
25
26     function handle($args)
27     {
28
29         parent::handle($args);
30
31         if (common_logged_in()) {
32             common_user_error(_('You can use the local subscription!'));
33             return;
34         }
35
36         $omb = $_SESSION['oauth_authorization_request'];
37
38         if (!$omb) {
39             common_user_error(_('Not expecting this response!'));
40             return;
41         }
42
43         common_debug('stored request: '.print_r($omb,true), __FILE__);
44
45         common_remove_magic_from_request();
46         $req = OAuthRequest::from_request();
47
48         $token = $req->get_parameter('oauth_token');
49
50         # I think this is the success metric
51
52         if ($token != $omb['token']) {
53             common_user_error(_('Not authorized.'));
54             return;
55         }
56
57         $version = $req->get_parameter('omb_version');
58
59         if ($version != OMB_VERSION_01) {
60             common_user_error(_('Unknown version of OMB protocol.'));
61             return;
62         }
63
64         $nickname = $req->get_parameter('omb_listener_nickname');
65
66         if (!$nickname) {
67             common_user_error(_('No nickname provided by remote server.'));
68             return;
69         }
70
71         $profile_url = $req->get_parameter('omb_listener_profile');
72
73         if (!$profile_url) {
74             common_user_error(_('No profile URL returned by server.'));
75             return;
76         }
77
78         if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) {
79             common_user_error(_('Invalid profile URL returned by server.'));
80             return;
81         }
82
83         if ($profile_url == common_local_url('showstream', array('nickname' => $nickname))) {
84             common_user_error(_('You can use the local subscription!'));
85             return;
86         }
87
88         common_debug('listenee: "'.$omb['listenee'].'"', __FILE__);
89
90         $user = User::staticGet('nickname', $omb['listenee']);
91
92         if (!$user) {
93             common_user_error(_('User being listened to doesn\'t exist.'));
94             return;
95         }
96
97         $other = User::staticGet('uri', $omb['listener']);
98
99         if ($other) {
100             common_user_error(_('You can use the local subscription!'));
101             return;
102         }
103
104         $fullname = $req->get_parameter('omb_listener_fullname');
105         $homepage = $req->get_parameter('omb_listener_homepage');
106         $bio = $req->get_parameter('omb_listener_bio');
107         $location = $req->get_parameter('omb_listener_location');
108         $avatar_url = $req->get_parameter('omb_listener_avatar');
109
110         list($newtok, $newsecret) = $this->access_token($omb);
111
112         if (!$newtok || !$newsecret) {
113             common_user_error(_('Couldn\'t convert request tokens to access tokens.'));
114             return;
115         }
116
117         # XXX: possible attack point; subscribe and return someone else's profile URI
118
119         $remote = Remote_profile::staticGet('uri', $omb['listener']);
120
121         if ($remote) {
122             $exists = true;
123             $profile = Profile::staticGet($remote->id);
124             $orig_remote = clone($remote);
125             $orig_profile = clone($profile);
126             # XXX: compare current postNotice and updateProfile URLs to the ones
127             # stored in the DB to avoid (possibly...) above attack
128         } else {
129             $exists = false;
130             $remote = new Remote_profile();
131             $remote->uri = $omb['listener'];
132             $profile = new Profile();
133         }
134
135         $profile->nickname = $nickname;
136         $profile->profileurl = $profile_url;
137
138         if ($fullname) {
139             $profile->fullname = $fullname;
140         }
141         if ($homepage) {
142             $profile->homepage = $homepage;
143         }
144         if ($bio) {
145             $profile->bio = $bio;
146         }
147         if ($location) {
148             $profile->location = $location;
149         }
150
151         if ($exists) {
152             $profile->update($orig_profile);
153         } else {
154             $profile->created = DB_DataObject_Cast::dateTime(); # current time
155             $id = $profile->insert();
156             if (!$id) {
157                 common_server_error(_('Error inserting new profile'));
158                 return;
159             }
160             $remote->id = $id;
161         }
162
163         if ($avatar_url) {
164             if (!$this->add_avatar($profile, $avatar_url)) {
165                 common_server_error(_('Error inserting avatar'));
166                 return;
167             }
168         }
169
170         $remote->postnoticeurl = $omb['post_notice_url'];
171         $remote->updateprofileurl = $omb['update_profile_url'];
172
173         if ($exists) {
174             if (!$remote->update($orig_remote)) {
175                 common_server_error(_('Error updating remote profile'));
176                 return;
177             }
178         } else {
179             $remote->created = DB_DataObject_Cast::dateTime(); # current time
180             if (!$remote->insert()) {
181                 common_server_error(_('Error inserting remote profile'));
182                 return;
183             }
184         }
185
186         if ($user->hasBlocked($profile)) {
187             $this->client_error(_('That user has blocked you from subscribing.'));
188             return;
189         }
190
191         $sub = new Subscription();
192
193         $sub->subscriber = $remote->id;
194         $sub->subscribed = $user->id;
195
196         $sub_exists = false;
197
198         if ($sub->find(true)) {
199             $sub_exists = true;
200             $orig_sub = clone($sub);
201         } else {
202             $sub_exists = false;
203             $sub->created = DB_DataObject_Cast::dateTime(); # current time
204         }
205
206         $sub->token = $newtok;
207         $sub->secret = $newsecret;
208
209         if ($sub_exists) {
210             $result = $sub->update($orig_sub);
211         } else {
212             $result = $sub->insert();
213         }
214
215         if (!$result) {
216             common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__);
217             common_user_error(_('Couldn\'t insert new subscription.'));
218             return;
219         }
220
221         # Notify user, if necessary
222
223         mail_subscribe_notify_profile($user, $profile);
224
225         # Clear the data
226         unset($_SESSION['oauth_authorization_request']);
227
228         # If we show subscriptions in reverse chron order, this should
229         # show up close to the top of the page
230
231         common_redirect(common_local_url('subscribers', array('nickname' =>
232                                                              $user->nickname)));
233     }
234
235     function add_avatar($profile, $url)
236     {
237         $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
238         copy($url, $temp_filename);
239         return $profile->setOriginal($temp_filename);
240     }
241
242     function access_token($omb)
243     {
244
245         common_debug('starting request for access token', __FILE__);
246
247         $con = omb_oauth_consumer();
248         $tok = new OAuthToken($omb['token'], $omb['secret']);
249
250         common_debug('using request token "'.$tok.'"', __FILE__);
251
252         $url = $omb['access_token_url'];
253
254         common_debug('using access token url "'.$url.'"', __FILE__);
255
256         # XXX: Is this the right thing to do? Strip off GET params and make them
257         # POST params? Seems wrong to me.
258
259         $parsed = parse_url($url);
260         $params = array();
261         parse_str($parsed['query'], $params);
262
263         $req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params);
264
265         $req->set_parameter('omb_version', OMB_VERSION_01);
266
267         # XXX: test to see if endpoint accepts this signature method
268
269         $req->sign_request(omb_hmac_sha1(), $con, $tok);
270
271         # We re-use this tool's fetcher, since it's pretty good
272
273         common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__);
274         common_debug('posting request data "'.$req->to_postdata().'"', __FILE__);
275
276         $fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
277         $result = $fetcher->post($req->get_normalized_http_url(),
278                                  $req->to_postdata(),
279                                  array('User-Agent' => 'Laconica/' . LACONICA_VERSION));
280
281         common_debug('got result: "'.print_r($result,TRUE).'"', __FILE__);
282
283         if ($result->status != 200) {
284             return null;
285         }
286
287         parse_str($result->body, $return);
288
289         return array($return['oauth_token'], $return['oauth_token_secret']);
290     }
291 }