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