]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - _darcs/pristine/actions/finishopenidlogin.php
eb52d73b1810d6c80060139a90f730c7d36c2328
[quix0rs-gnu-social.git] / _darcs / pristine / actions / finishopenidlogin.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/openid.php');
23
24 class FinishopenidloginAction extends Action {
25
26     function handle($args)
27     {
28         parent::handle($args);
29         if (common_logged_in()) {
30             common_user_error(_('Already logged in.'));
31         } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
32             $token = $this->trimmed('token');
33             if (!$token || $token != common_session_token()) {
34                 $this->show_form(_('There was a problem with your session token. Try again, please.'));
35                 return;
36             }
37             if ($this->arg('create')) {
38                 if (!$this->boolean('license')) {
39                     $this->show_form(_('You can\'t register if you don\'t agree to the license.'),
40                                      $this->trimmed('newname'));
41                     return;
42                 }
43                 $this->create_new_user();
44             } else if ($this->arg('connect')) {
45                 $this->connect_user();
46             } else {
47                 common_debug(print_r($this->args, true), __FILE__);
48                 $this->show_form(_('Something weird happened.'),
49                                  $this->trimmed('newname'));
50             }
51         } else {
52             $this->try_login();
53         }
54     }
55
56     function show_top($error=null)
57     {
58         if ($error) {
59             common_element('div', array('class' => 'error'), $error);
60         } else {
61             global $config;
62             common_element('div', 'instructions',
63                            sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name']));
64         }
65     }
66
67     function show_form($error=null, $username=null)
68     {
69         common_show_header(_('OpenID Account Setup'), null, $error,
70                            array($this, 'show_top'));
71
72         common_element_start('form', array('method' => 'post',
73                                            'id' => 'account_connect',
74                                            'action' => common_local_url('finishopenidlogin')));
75         common_hidden('token', common_session_token());
76         common_element('h2', null,
77                        _('Create new account'));
78         common_element('p', null,
79                        _('Create a new user with this nickname.'));
80         common_input('newname', _('New nickname'),
81                      ($username) ? $username : '',
82                      _('1-64 lowercase letters or numbers, no punctuation or spaces'));
83         common_element_start('p');
84         common_element('input', array('type' => 'checkbox',
85                                       'id' => 'license',
86                                       'name' => 'license',
87                                       'value' => 'true'));
88         common_text(_('My text and files are available under '));
89         common_element('a', array(href => common_config('license', 'url')),
90                        common_config('license', 'title'));
91         common_text(_(' except this private data: password, email address, IM address, phone number.'));
92         common_element_end('p');
93         common_submit('create', _('Create'));
94         common_element('h2', null,
95                        _('Connect existing account'));
96         common_element('p', null,
97                        _('If you already have an account, login with your username and password to connect it to your OpenID.'));
98         common_input('nickname', _('Existing nickname'));
99         common_password('password', _('Password'));
100         common_submit('connect', _('Connect'));
101         common_element_end('form');
102         common_show_footer();
103     }
104
105     function try_login()
106     {
107
108         $consumer = oid_consumer();
109
110         $response = $consumer->complete(common_local_url('finishopenidlogin'));
111
112         if ($response->status == Auth_OpenID_CANCEL) {
113             $this->message(_('OpenID authentication cancelled.'));
114             return;
115         } else if ($response->status == Auth_OpenID_FAILURE) {
116             // Authentication failed; display the error message.
117             $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message));
118         } else if ($response->status == Auth_OpenID_SUCCESS) {
119             // This means the authentication succeeded; extract the
120             // identity URL and Simple Registration data (if it was
121             // returned).
122             $display = $response->getDisplayIdentifier();
123             $canonical = ($response->endpoint->canonicalID) ?
124               $response->endpoint->canonicalID : $response->getDisplayIdentifier();
125
126             $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response);
127
128             if ($sreg_resp) {
129                 $sreg = $sreg_resp->contents();
130             }
131
132             $user = oid_get_user($canonical);
133
134             if ($user) {
135                 oid_set_last($display);
136                 # XXX: commented out at @edd's request until better
137                 # control over how data flows from OpenID provider.
138                 # oid_update_user($user, $sreg);
139                 common_set_user($user);
140                 common_real_login(true);
141                 if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
142                     common_rememberme($user);
143                 }
144                 unset($_SESSION['openid_rememberme']);
145                 $this->go_home($user->nickname);
146             } else {
147                 $this->save_values($display, $canonical, $sreg);
148                 $this->show_form(null, $this->best_new_nickname($display, $sreg));
149             }
150         }
151     }
152
153     function message($msg)
154     {
155         common_show_header(_('OpenID Login'));
156         common_element('p', null, $msg);
157         common_show_footer();
158     }
159
160     function save_values($display, $canonical, $sreg)
161     {
162         common_ensure_session();
163         $_SESSION['openid_display'] = $display;
164         $_SESSION['openid_canonical'] = $canonical;
165         $_SESSION['openid_sreg'] = $sreg;
166     }
167
168     function get_saved_values()
169     {
170         return array($_SESSION['openid_display'],
171                      $_SESSION['openid_canonical'],
172                      $_SESSION['openid_sreg']);
173     }
174
175     function create_new_user()
176     {
177
178         # FIXME: save invite code before redirect, and check here
179
180         if (common_config('site', 'closed') || common_config('site', 'inviteonly')) {
181             common_user_error(_('Registration not allowed.'));
182             return;
183         }
184
185         $nickname = $this->trimmed('newname');
186
187         if (!Validate::string($nickname, array('min_length' => 1,
188                                                'max_length' => 64,
189                                                'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
190             $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.'));
191             return;
192         }
193
194         if (!User::allowed_nickname($nickname)) {
195             $this->show_form(_('Nickname not allowed.'));
196             return;
197         }
198
199         if (User::staticGet('nickname', $nickname)) {
200             $this->show_form(_('Nickname already in use. Try another one.'));
201             return;
202         }
203
204         list($display, $canonical, $sreg) = $this->get_saved_values();
205
206         if (!$display || !$canonical) {
207             common_server_error(_('Stored OpenID not found.'));
208             return;
209         }
210
211         # Possible race condition... let's be paranoid
212
213         $other = oid_get_user($canonical);
214
215         if ($other) {
216             common_server_error(_('Creating new account for OpenID that already has a user.'));
217             return;
218         }
219
220         if ($sreg['country']) {
221             if ($sreg['postcode']) {
222                 # XXX: use postcode to get city and region
223                 # XXX: also, store postcode somewhere -- it's valuable!
224                 $location = $sreg['postcode'] . ', ' . $sreg['country'];
225             } else {
226                 $location = $sreg['country'];
227             }
228         }
229
230         if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) {
231             $fullname = $sreg['fullname'];
232         }
233
234         if ($sreg['email'] && Validate::email($sreg['email'], true)) {
235             $email = $sreg['email'];
236         }
237
238         # XXX: add language
239         # XXX: add timezone
240
241         $user = User::register(array('nickname' => $nickname,
242                                      'email' => $email,
243                                      'fullname' => $fullname,
244                                      'location' => $location));
245
246         $result = oid_link_user($user->id, $canonical, $display);
247
248         oid_set_last($display);
249         common_set_user($user);
250         common_real_login(true);
251         if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
252             common_rememberme($user);
253         }
254         unset($_SESSION['openid_rememberme']);
255         common_redirect(common_local_url('showstream', array('nickname' => $user->nickname)));
256     }
257
258     function connect_user()
259     {
260
261         $nickname = $this->trimmed('nickname');
262         $password = $this->trimmed('password');
263
264         if (!common_check_user($nickname, $password)) {
265             $this->show_form(_('Invalid username or password.'));
266             return;
267         }
268
269         # They're legit!
270
271         $user = User::staticGet('nickname', $nickname);
272
273         list($display, $canonical, $sreg) = $this->get_saved_values();
274
275         if (!$display || !$canonical) {
276             common_server_error(_('Stored OpenID not found.'));
277             return;
278         }
279
280         $result = oid_link_user($user->id, $canonical, $display);
281
282         if (!$result) {
283             common_server_error(_('Error connecting user to OpenID.'));
284             return;
285         }
286
287         oid_update_user($user, $sreg);
288         oid_set_last($display);
289         common_set_user($user);
290         common_real_login(true);
291         if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) {
292             common_rememberme($user);
293         }
294         unset($_SESSION['openid_rememberme']);
295         $this->go_home($user->nickname);
296     }
297
298     function go_home($nickname)
299     {
300         $url = common_get_returnto();
301         if ($url) {
302             # We don't have to return to it again
303             common_set_returnto(null);
304         } else {
305             $url = common_local_url('all',
306                                     array('nickname' =>
307                                           $nickname));
308         }
309         common_redirect($url);
310     }
311
312     function best_new_nickname($display, $sreg)
313     {
314
315         # Try the passed-in nickname
316
317         if ($sreg['nickname']) {
318             $nickname = $this->nicknamize($sreg['nickname']);
319             if ($this->is_new_nickname($nickname)) {
320                 return $nickname;
321             }
322         }
323
324         # Try the full name
325
326         if ($sreg['fullname']) {
327             $fullname = $this->nicknamize($sreg['fullname']);
328             if ($this->is_new_nickname($fullname)) {
329                 return $fullname;
330             }
331         }
332
333         # Try the URL
334
335         $from_url = $this->openid_to_nickname($display);
336
337         if ($from_url && $this->is_new_nickname($from_url)) {
338             return $from_url;
339         }
340
341         # XXX: others?
342
343         return null;
344     }
345
346     function is_new_nickname($str)
347     {
348         if (!Validate::string($str, array('min_length' => 1,
349                                           'max_length' => 64,
350                                           'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) {
351             return false;
352         }
353     if (!User::allowed_nickname($str)) {
354             return false;
355         }
356         if (User::staticGet('nickname', $str)) {
357             return false;
358         }
359         return true;
360     }
361
362     function openid_to_nickname($openid)
363     {
364         if (Auth_Yadis_identifierScheme($openid) == 'XRI') {
365             return $this->xri_to_nickname($openid);
366         } else {
367             return $this->url_to_nickname($openid);
368         }
369     }
370
371     # We try to use an OpenID URL as a legal Laconica user name in this order
372     # 1. Plain hostname, like http://evanp.myopenid.com/
373     # 2. One element in path, like http://profile.typekey.com/EvanProdromou/
374     #    or http://getopenid.com/evanprodromou
375
376     function url_to_nickname($openid)
377     {
378         static $bad = array('query', 'user', 'password', 'port', 'fragment');
379
380         $parts = parse_url($openid);
381
382         # If any of these parts exist, this won't work
383
384         foreach ($bad as $badpart) {
385             if (array_key_exists($badpart, $parts)) {
386                 return null;
387             }
388         }
389
390         # We just have host and/or path
391
392         # If it's just a host...
393         if (array_key_exists('host', $parts) &&
394             (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0))
395         {
396             $hostparts = explode('.', $parts['host']);
397
398             # Try to catch common idiom of nickname.service.tld
399
400             if ((count($hostparts) > 2) &&
401                 (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au
402                 (strcmp($hostparts[0], 'www') != 0))
403             {
404                 return $this->nicknamize($hostparts[0]);
405             } else {
406                 # Do the whole hostname
407                 return $this->nicknamize($parts['host']);
408             }
409         } else {
410             if (array_key_exists('path', $parts)) {
411                 # Strip starting, ending slashes
412                 $path = preg_replace('@/$@', '', $parts['path']);
413                 $path = preg_replace('@^/@', '', $path);
414                 if (strpos($path, '/') === false) {
415                     return $this->nicknamize($path);
416                 }
417             }
418         }
419
420         return null;
421     }
422
423     function xri_to_nickname($xri)
424     {
425         $base = $this->xri_base($xri);
426
427         if (!$base) {
428             return null;
429         } else {
430             # =evan.prodromou
431             # or @gratis*evan.prodromou
432             $parts = explode('*', substr($base, 1));
433             return $this->nicknamize(array_pop($parts));
434         }
435     }
436
437     function xri_base($xri)
438     {
439         if (substr($xri, 0, 6) == 'xri://') {
440             return substr($xri, 6);
441         } else {
442             return $xri;
443         }
444     }
445
446     # Given a string, try to make it work as a nickname
447
448     function nicknamize($str)
449     {
450         $str = preg_replace('/\W/', '', $str);
451         return strtolower($str);
452     }
453 }