3 * Let the user authorize a remote subscription request
9 * @author Evan Prodromou <evan@status.net>
10 * @author Robin Millette <millette@status.net>
11 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
12 * @link http://status.net/
14 * StatusNet - the distributed open-source microblogging tool
15 * Copyright (C) 2008, 2009, StatusNet, Inc.
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU Affero General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Affero General Public License for more details.
27 * You should have received a copy of the GNU Affero General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
33 require_once INSTALLDIR.'/lib/omb.php';
34 require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
35 require_once INSTALLDIR.'/extlib/libomb/profile.php';
36 define('TIMESTAMP_THRESHOLD', 300);
38 class UserauthorizationAction extends Action
43 function handle($args)
45 parent::handle($args);
47 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
48 /* Use a session token for CSRF protection. */
49 $token = $this->trimmed('token');
50 if (!$token || $token != common_session_token()) {
51 $srv = $this->getStoredParams();
52 $this->showForm($srv->getRemoteUser(), _('There was a problem ' .
53 'with your session token. Try again, ' .
57 /* We've shown the form, now post user's choice. */
58 $this->sendAuthorization();
60 if (!common_logged_in()) {
61 /* Go log in, and then come back. */
62 common_set_returnto($_SERVER['REQUEST_URI']);
64 common_redirect(common_local_url('login'));
68 $user = common_current_user();
69 $profile = $user->getProfile();
71 common_log_db_error($user, 'SELECT', __FILE__);
72 $this->serverError(_('User without matching profile.'));
76 /* TODO: If no token is passed the user should get a prompt to enter
77 it according to OAuth Core 1.0. */
80 $srv = new OMB_Service_Provider(
81 profile_to_omb_profile($user->uri, $profile),
82 omb_oauth_datastore());
84 $remote_user = $srv->handleUserAuth();
85 } catch (Exception $e) {
87 $this->clientError($e->getMessage());
91 $this->storeParams($srv);
92 $this->showForm($remote_user);
96 function showForm($params, $error=null)
98 $this->params = $params;
99 $this->error = $error;
105 return _('Authorize subscription');
108 function showPageNotice()
110 $this->element('p', null, _('Please check these details to make sure '.
111 'that you want to subscribe to this ' .
112 'user’s notices. If you didn’t just ask ' .
113 'to subscribe to someone’s notices, '.
117 function showContent()
119 $params = $this->params;
121 $nickname = $params->getNickname();
122 $profile = $params->getProfileURL();
123 $license = $params->getLicenseURL();
124 $fullname = $params->getFullname();
125 $homepage = $params->getHomepage();
126 $bio = $params->getBio();
127 $location = $params->getLocation();
128 $avatar = $params->getAvatarURL();
130 $this->elementStart('div', 'entity_profile vcard');
131 $this->elementStart('dl', 'entity_depiction');
132 $this->element('dt', null, _('Photo'));
133 $this->elementStart('dd');
135 $this->element('img', array('src' => $avatar,
136 'class' => 'photo avatar',
137 'width' => AVATAR_PROFILE_SIZE,
138 'height' => AVATAR_PROFILE_SIZE,
139 'alt' => $nickname));
141 $this->elementEnd('dd');
142 $this->elementEnd('dl');
144 $this->elementStart('dl', 'entity_nickname');
145 $this->element('dt', null, _('Nickname'));
146 $this->elementStart('dd');
147 $hasFN = ($fullname !== '') ? 'nickname' : 'fn nickname';
148 $this->elementStart('a', array('href' => $profile,
149 'class' => 'url '.$hasFN));
150 $this->raw($nickname);
151 $this->elementEnd('a');
152 $this->elementEnd('dd');
153 $this->elementEnd('dl');
155 if (!is_null($fullname)) {
156 $this->elementStart('dl', 'entity_fn');
157 $this->elementStart('dd');
158 $this->elementStart('span', 'fn');
159 $this->raw($fullname);
160 $this->elementEnd('span');
161 $this->elementEnd('dd');
162 $this->elementEnd('dl');
164 if (!is_null($location)) {
165 $this->elementStart('dl', 'entity_location');
166 $this->element('dt', null, _('Location'));
167 $this->elementStart('dd', 'label');
168 $this->raw($location);
169 $this->elementEnd('dd');
170 $this->elementEnd('dl');
173 if (!is_null($homepage)) {
174 $this->elementStart('dl', 'entity_url');
175 $this->element('dt', null, _('URL'));
176 $this->elementStart('dd');
177 $this->elementStart('a', array('href' => $homepage,
179 $this->raw($homepage);
180 $this->elementEnd('a');
181 $this->elementEnd('dd');
182 $this->elementEnd('dl');
185 if (!is_null($bio)) {
186 $this->elementStart('dl', 'entity_note');
187 $this->element('dt', null, _('Note'));
188 $this->elementStart('dd', 'note');
190 $this->elementEnd('dd');
191 $this->elementEnd('dl');
194 if (!is_null($license)) {
195 $this->elementStart('dl', 'entity_license');
196 $this->element('dt', null, _('License'));
197 $this->elementStart('dd', 'license');
198 $this->element('a', array('href' => $license,
199 'class' => 'license'),
201 $this->elementEnd('dd');
202 $this->elementEnd('dl');
204 $this->elementEnd('div');
206 $this->elementStart('div', 'entity_actions');
207 $this->elementStart('ul');
208 $this->elementStart('li', 'entity_subscribe');
209 $this->elementStart('form', array('method' => 'post',
210 'id' => 'userauthorization',
211 'class' => 'form_user_authorization',
212 'name' => 'userauthorization',
213 'action' => common_local_url(
214 'userauthorization')));
215 $this->hidden('token', common_session_token());
217 $this->submit('accept', _('Accept'), 'submit accept', null,
218 _('Subscribe to this user'));
219 $this->submit('reject', _('Reject'), 'submit reject', null,
220 _('Reject this subscription'));
221 $this->elementEnd('form');
222 $this->elementEnd('li');
223 $this->elementEnd('ul');
224 $this->elementEnd('div');
227 function sendAuthorization()
229 $srv = $this->getStoredParams();
232 $this->clientError(_('No authorization request!'));
236 $accepted = $this->arg('accept');
238 list($val, $token) = $srv->continueUserAuth($accepted);
239 } catch (Exception $e) {
240 $this->clientError($e->getMessage());
243 if ($val !== false) {
244 common_redirect($val, 303);
245 } elseif ($accepted) {
246 $this->showAcceptMessage($token);
248 $this->showRejectMessage();
252 function showAcceptMessage($tok)
254 common_show_header(_('Subscription authorized'));
255 $this->element('p', null,
256 _('The subscription has been authorized, but no '.
257 'callback URL was passed. Check with the site’s ' .
258 'instructions for details on how to authorize the ' .
259 'subscription. Your subscription token is:'));
260 $this->element('blockquote', 'token', $tok);
261 common_show_footer();
264 function showRejectMessage()
266 common_show_header(_('Subscription rejected'));
267 $this->element('p', null,
268 _('The subscription has been rejected, but no '.
269 'callback URL was passed. Check with the site’s ' .
270 'instructions for details on how to fully reject ' .
271 'the subscription.'));
272 common_show_footer();
275 function storeParams($params)
277 common_ensure_session();
278 $_SESSION['userauthorizationparams'] = serialize($params);
281 function clearParams()
283 common_ensure_session();
284 unset($_SESSION['userauthorizationparams']);
287 function getStoredParams()
289 common_ensure_session();
290 $params = unserialize($_SESSION['userauthorizationparams']);
294 function validateOmb()
296 $listener = $_GET['omb_listener'];
297 $listenee = $_GET['omb_listenee'];
298 $nickname = $_GET['omb_listenee_nickname'];
299 $profile = $_GET['omb_listenee_profile'];
301 $user = User::staticGet('uri', $listener);
303 throw new Exception(sprintf(_('Listener URI ‘%s’ not found here.'),
307 if (strlen($listenee) > 255) {
308 throw new Exception(sprintf(_('Listenee URI ‘%s’ is too long.'),
312 $other = User::staticGet('uri', $listenee);
314 throw new Exception(sprintf(_('Listenee URI ‘%s’ is a local user.'),
318 $remote = Remote_profile::staticGet('uri', $listenee);
320 $sub = new Subscription();
321 $sub->subscriber = $user->id;
322 $sub->subscribed = $remote->id;
323 if ($sub->find(true)) {
324 throw new Exception('You are already subscribed to this user.');
328 if ($profile == common_profile_url($nickname)) {
329 throw new Exception(sprintf(_('Profile URL ‘%s’ is for a local user.'),
334 $license = $_GET['omb_listenee_license'];
335 $site_license = common_config('license', 'url');
336 if (!common_compatible_license($license, $site_license)) {
337 throw new Exception(sprintf(_('Listenee stream license ‘%1$s’ is not ' .
338 'compatible with site license ‘%2$s’.'),
339 $license, $site_license));
342 $avatar = $_GET['omb_listenee_avatar'];
344 if (!common_valid_http_url($avatar) || strlen($avatar) > 255) {
345 throw new Exception(sprintf(_('Avatar URL ‘%s’ is not valid.'),
348 $size = @getimagesize($avatar);
350 throw new Exception(sprintf(_('Can’t read avatar URL ‘%s’.'),
353 if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG,
355 throw new Exception(sprintf(_('Wrong image type for avatar URL '.