]> git.mxchange.org Git - friendica.git/blob - src/Model/User.php
c272d4c6cea6e1c2cc0ebfdc7827d8c7bf8da3be
[friendica.git] / src / Model / User.php
1 <?php
2 /**
3  * @file src/Model/User.php
4  * @brief This file includes the User class with user related database functions
5  */
6 namespace Friendica\Model;
7
8 use DivineOmega\PasswordExposed\PasswordStatus;
9 use Exception;
10 use Friendica\Core\Addon;
11 use Friendica\Core\Config;
12 use Friendica\Core\L10n;
13 use Friendica\Core\PConfig;
14 use Friendica\Core\System;
15 use Friendica\Core\Worker;
16 use Friendica\Database\DBA;
17 use Friendica\Object\Image;
18 use Friendica\Util\Crypto;
19 use Friendica\Util\DateTimeFormat;
20 use Friendica\Util\Network;
21 use LightOpenID;
22 use function password_exposed;
23
24 require_once 'boot.php';
25 require_once 'include/dba.php';
26 require_once 'include/enotify.php';
27 require_once 'include/text.php';
28 /**
29  * @brief This class handles User related functions
30  */
31 class User
32 {
33         /**
34          * @brief Get owner data by user id
35          *
36          * @param int $uid
37          * @return boolean|array
38          */
39         public static function getOwnerDataById($uid) {
40                 $r = DBA::fetchFirst("SELECT
41                         `contact`.*,
42                         `user`.`prvkey` AS `uprvkey`,
43                         `user`.`timezone`,
44                         `user`.`nickname`,
45                         `user`.`sprvkey`,
46                         `user`.`spubkey`,
47                         `user`.`page-flags`,
48                         `user`.`account-type`,
49                         `user`.`prvnets`
50                         FROM `contact`
51                         INNER JOIN `user`
52                                 ON `user`.`uid` = `contact`.`uid`
53                         WHERE `contact`.`uid` = ?
54                         AND `contact`.`self`
55                         LIMIT 1",
56                         $uid
57                 );
58                 if (!DBA::isResult($r)) {
59                         return false;
60                 }
61                 return $r;
62         }
63
64         /**
65          * @brief Get owner data by nick name
66          *
67          * @param int $nick
68          * @return boolean|array
69          */
70         public static function getOwnerDataByNick($nick)
71         {
72                 $user = DBA::selectFirst('user', ['uid'], ['nickname' => $nick]);
73                 if (!DBA::isResult($user)) {
74                         return false;
75                 }
76                 return self::getOwnerDataById($user['uid']);
77         }
78
79         /**
80          * @brief Returns the default group for a given user and network
81          *
82          * @param int $uid User id
83          * @param string $network network name
84          *
85          * @return int group id
86          */
87         public static function getDefaultGroup($uid, $network = '')
88         {
89                 $default_group = 0;
90
91                 if ($network == NETWORK_OSTATUS) {
92                         $default_group = PConfig::get($uid, "ostatus", "default_group");
93                 }
94
95                 if ($default_group != 0) {
96                         return $default_group;
97                 }
98
99                 $user = DBA::selectFirst('user', ['def_gid'], ['uid' => $uid]);
100
101                 if (DBA::isResult($user)) {
102                         $default_group = $user["def_gid"];
103                 }
104
105                 return $default_group;
106         }
107
108
109         /**
110          * Authenticate a user with a clear text password
111          *
112          * @brief Authenticate a user with a clear text password
113          * @param mixed $user_info
114          * @param string $password
115          * @return int|boolean
116          * @deprecated since version 3.6
117          * @see User::getIdFromPasswordAuthentication()
118          */
119         public static function authenticate($user_info, $password)
120         {
121                 try {
122                         return self::getIdFromPasswordAuthentication($user_info, $password);
123                 } catch (Exception $ex) {
124                         return false;
125                 }
126         }
127
128         /**
129          * Returns the user id associated with a successful password authentication
130          *
131          * @brief Authenticate a user with a clear text password
132          * @param mixed $user_info
133          * @param string $password
134          * @return int User Id if authentication is successful
135          * @throws Exception
136          */
137         public static function getIdFromPasswordAuthentication($user_info, $password)
138         {
139                 $user = self::getAuthenticationInfo($user_info);
140
141                 if (strpos($user['password'], '$') === false) {
142                         //Legacy hash that has not been replaced by a new hash yet
143                         if (self::hashPasswordLegacy($password) === $user['password']) {
144                                 self::updatePassword($user['uid'], $password);
145
146                                 return $user['uid'];
147                         }
148                 } elseif (!empty($user['legacy_password'])) {
149                         //Legacy hash that has been double-hashed and not replaced by a new hash yet
150                         //Warning: `legacy_password` is not necessary in sync with the content of `password`
151                         if (password_verify(self::hashPasswordLegacy($password), $user['password'])) {
152                                 self::updatePassword($user['uid'], $password);
153
154                                 return $user['uid'];
155                         }
156                 } elseif (password_verify($password, $user['password'])) {
157                         //New password hash
158                         if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) {
159                                 self::updatePassword($user['uid'], $password);
160                         }
161
162                         return $user['uid'];
163                 }
164
165                 throw new Exception(L10n::t('Login failed'));
166         }
167
168         /**
169          * Returns authentication info from various parameters types
170          *
171          * User info can be any of the following:
172          * - User DB object
173          * - User Id
174          * - User email or username or nickname
175          * - User array with at least the uid and the hashed password
176          *
177          * @param mixed $user_info
178          * @return array
179          * @throws Exception
180          */
181         private static function getAuthenticationInfo($user_info)
182         {
183                 $user = null;
184
185                 if (is_object($user_info) || is_array($user_info)) {
186                         if (is_object($user_info)) {
187                                 $user = (array) $user_info;
188                         } else {
189                                 $user = $user_info;
190                         }
191
192                         if (!isset($user['uid'])
193                                 || !isset($user['password'])
194                                 || !isset($user['legacy_password'])
195                         ) {
196                                 throw new Exception(L10n::t('Not enough information to authenticate'));
197                         }
198                 } elseif (is_int($user_info) || is_string($user_info)) {
199                         if (is_int($user_info)) {
200                                 $user = DBA::selectFirst('user', ['uid', 'password', 'legacy_password'],
201                                         [
202                                                 'uid' => $user_info,
203                                                 'blocked' => 0,
204                                                 'account_expired' => 0,
205                                                 'account_removed' => 0,
206                                                 'verified' => 1
207                                         ]
208                                 );
209                         } else {
210                                 $fields = ['uid', 'password', 'legacy_password'];
211                                 $condition = ["(`email` = ? OR `username` = ? OR `nickname` = ?)
212                                         AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`",
213                                         $user_info, $user_info, $user_info];
214                                 $user = DBA::selectFirst('user', $fields, $condition);
215                         }
216
217                         if (!DBA::isResult($user)) {
218                                 throw new Exception(L10n::t('User not found'));
219                         }
220                 }
221
222                 return $user;
223         }
224
225         /**
226          * Generates a human-readable random password
227          *
228          * @return string
229          */
230         public static function generateNewPassword()
231         {
232                 return autoname(6) . mt_rand(100, 9999);
233         }
234
235         /**
236          * Checks if the provided plaintext password has been exposed or not
237          *
238          * @param string $password
239          * @return bool
240          */
241         public static function isPasswordExposed($password)
242         {
243                 return password_exposed($password) === PasswordStatus::EXPOSED;
244         }
245
246         /**
247          * Legacy hashing function, kept for password migration purposes
248          *
249          * @param string $password
250          * @return string
251          */
252         private static function hashPasswordLegacy($password)
253         {
254                 return hash('whirlpool', $password);
255         }
256
257         /**
258          * Global user password hashing function
259          *
260          * @param string $password
261          * @return string
262          */
263         public static function hashPassword($password)
264         {
265                 if (!trim($password)) {
266                         throw new Exception(L10n::t('Password can\'t be empty'));
267                 }
268
269                 return password_hash($password, PASSWORD_DEFAULT);
270         }
271
272         /**
273          * Updates a user row with a new plaintext password
274          *
275          * @param int    $uid
276          * @param string $password
277          * @return bool
278          */
279         public static function updatePassword($uid, $password)
280         {
281                 return self::updatePasswordHashed($uid, self::hashPassword($password));
282         }
283
284         /**
285          * Updates a user row with a new hashed password.
286          * Empties the password reset token field just in case.
287          *
288          * @param int    $uid
289          * @param string $pasword_hashed
290          * @return bool
291          */
292         private static function updatePasswordHashed($uid, $pasword_hashed)
293         {
294                 $fields = [
295                         'password' => $pasword_hashed,
296                         'pwdreset' => null,
297                         'pwdreset_time' => null,
298                         'legacy_password' => false
299                 ];
300                 return DBA::update('user', $fields, ['uid' => $uid]);
301         }
302
303         /**
304          * @brief Checks if a nickname is in the list of the forbidden nicknames
305          *
306          * Check if a nickname is forbidden from registration on the node by the
307          * admin. Forbidden nicknames (e.g. role namess) can be configured in the
308          * admin panel.
309          *
310          * @param string $nickname The nickname that should be checked
311          * @return boolean True is the nickname is blocked on the node
312          */
313         public static function isNicknameBlocked($nickname)
314         {
315                 $forbidden_nicknames = Config::get('system', 'forbidden_nicknames', '');
316
317                 // if the config variable is empty return false
318                 if (empty($forbidden_nicknames)) {
319                         return false;
320                 }
321
322                 // check if the nickname is in the list of blocked nicknames
323                 $forbidden = explode(',', $forbidden_nicknames);
324                 $forbidden = array_map('trim', $forbidden);
325                 if (in_array(strtolower($nickname), $forbidden)) {
326                         return true;
327                 }
328
329                 // else return false
330                 return false;
331         }
332
333         /**
334          * @brief Catch-all user creation function
335          *
336          * Creates a user from the provided data array, either form fields or OpenID.
337          * Required: { username, nickname, email } or { openid_url }
338          *
339          * Performs the following:
340          * - Sends to the OpenId auth URL (if relevant)
341          * - Creates new key pairs for crypto
342          * - Create self-contact
343          * - Create profile image
344          *
345          * @param array $data
346          * @return string
347          * @throw Exception
348          */
349         public static function create(array $data)
350         {
351                 $a = get_app();
352                 $return = ['user' => null, 'password' => ''];
353
354                 $using_invites = Config::get('system', 'invitation_only');
355                 $num_invites   = Config::get('system', 'number_invites');
356
357                 $invite_id  = !empty($data['invite_id'])  ? notags(trim($data['invite_id']))  : '';
358                 $username   = !empty($data['username'])   ? notags(trim($data['username']))   : '';
359                 $nickname   = !empty($data['nickname'])   ? notags(trim($data['nickname']))   : '';
360                 $email      = !empty($data['email'])      ? notags(trim($data['email']))      : '';
361                 $openid_url = !empty($data['openid_url']) ? notags(trim($data['openid_url'])) : '';
362                 $photo      = !empty($data['photo'])      ? notags(trim($data['photo']))      : '';
363                 $password   = !empty($data['password'])   ? trim($data['password'])           : '';
364                 $password1  = !empty($data['password1'])  ? trim($data['password1'])          : '';
365                 $confirm    = !empty($data['confirm'])    ? trim($data['confirm'])            : '';
366                 $blocked    = !empty($data['blocked'])    ? intval($data['blocked'])          : 0;
367                 $verified   = !empty($data['verified'])   ? intval($data['verified'])         : 0;
368                 $language   = !empty($data['language'])   ? notags(trim($data['language']))   : 'en';
369
370                 $publish = !empty($data['profile_publish_reg']) && intval($data['profile_publish_reg']) ? 1 : 0;
371                 $netpublish = strlen(Config::get('system', 'directory')) ? $publish : 0;
372
373                 if ($password1 != $confirm) {
374                         throw new Exception(L10n::t('Passwords do not match. Password unchanged.'));
375                 } elseif ($password1 != '') {
376                         $password = $password1;
377                 }
378
379                 if ($using_invites) {
380                         if (!$invite_id) {
381                                 throw new Exception(L10n::t('An invitation is required.'));
382                         }
383
384                         if (!DBA::exists('register', ['hash' => $invite_id])) {
385                                 throw new Exception(L10n::t('Invitation could not be verified.'));
386                         }
387                 }
388
389                 if (empty($username) || empty($email) || empty($nickname)) {
390                         if ($openid_url) {
391                                 if (!Network::isUrlValid($openid_url)) {
392                                         throw new Exception(L10n::t('Invalid OpenID url'));
393                                 }
394                                 $_SESSION['register'] = 1;
395                                 $_SESSION['openid'] = $openid_url;
396
397                                 $openid = new LightOpenID($a->get_hostname());
398                                 $openid->identity = $openid_url;
399                                 $openid->returnUrl = System::baseUrl() . '/openid';
400                                 $openid->required = ['namePerson/friendly', 'contact/email', 'namePerson'];
401                                 $openid->optional = ['namePerson/first', 'media/image/aspect11', 'media/image/default'];
402                                 try {
403                                         $authurl = $openid->authUrl();
404                                 } catch (Exception $e) {
405                                         throw new Exception(L10n::t('We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.') . EOL . EOL . L10n::t('The error message was:') . $e->getMessage(), 0, $e);
406                                 }
407                                 goaway($authurl);
408                                 // NOTREACHED
409                         }
410
411                         throw new Exception(L10n::t('Please enter the required information.'));
412                 }
413
414                 if (!Network::isUrlValid($openid_url)) {
415                         $openid_url = '';
416                 }
417
418                 $err = '';
419
420                 // collapse multiple spaces in name
421                 $username = preg_replace('/ +/', ' ', $username);
422
423                 if (mb_strlen($username) > 48) {
424                         throw new Exception(L10n::t('Please use a shorter name.'));
425                 }
426                 if (mb_strlen($username) < 3) {
427                         throw new Exception(L10n::t('Name too short.'));
428                 }
429
430                 // So now we are just looking for a space in the full name.
431                 $loose_reg = Config::get('system', 'no_regfullname');
432                 if (!$loose_reg) {
433                         $username = mb_convert_case($username, MB_CASE_TITLE, 'UTF-8');
434                         if (!strpos($username, ' ')) {
435                                 throw new Exception(L10n::t("That doesn't appear to be your full \x28First Last\x29 name."));
436                         }
437                 }
438
439                 if (!Network::isEmailDomainAllowed($email)) {
440                         throw new Exception(L10n::t('Your email domain is not among those allowed on this site.'));
441                 }
442
443                 if (!valid_email($email) || !Network::isEmailDomainValid($email)) {
444                         throw new Exception(L10n::t('Not a valid email address.'));
445                 }
446                 if (self::isNicknameBlocked($nickname)) {
447                         throw new Exception(L10n::t('The nickname was blocked from registration by the nodes admin.'));
448                 }
449
450                 if (Config::get('system', 'block_extended_register', false) && DBA::exists('user', ['email' => $email])) {
451                         throw new Exception(L10n::t('Cannot use that email.'));
452                 }
453
454                 // Disallow somebody creating an account using openid that uses the admin email address,
455                 // since openid bypasses email verification. We'll allow it if there is not yet an admin account.
456                 if (Config::get('config', 'admin_email') && strlen($openid_url)) {
457                         $adminlist = explode(',', str_replace(' ', '', strtolower(Config::get('config', 'admin_email'))));
458                         if (in_array(strtolower($email), $adminlist)) {
459                                 throw new Exception(L10n::t('Cannot use that email.'));
460                         }
461                 }
462
463                 $nickname = $data['nickname'] = strtolower($nickname);
464
465                 if (!preg_match('/^[a-z0-9][a-z0-9\_]*$/', $nickname)) {
466                         throw new Exception(L10n::t('Your nickname can only contain a-z, 0-9 and _.'));
467                 }
468
469                 // Check existing and deleted accounts for this nickname.
470                 if (DBA::exists('user', ['nickname' => $nickname])
471                         || DBA::exists('userd', ['username' => $nickname])
472                 ) {
473                         throw new Exception(L10n::t('Nickname is already registered. Please choose another.'));
474                 }
475
476                 $new_password = strlen($password) ? $password : User::generateNewPassword();
477                 $new_password_encoded = self::hashPassword($new_password);
478
479                 $return['password'] = $new_password;
480
481                 $keys = Crypto::newKeypair(4096);
482                 if ($keys === false) {
483                         throw new Exception(L10n::t('SERIOUS ERROR: Generation of security keys failed.'));
484                 }
485
486                 $prvkey = $keys['prvkey'];
487                 $pubkey = $keys['pubkey'];
488
489                 // Create another keypair for signing/verifying salmon protocol messages.
490                 $sres = Crypto::newKeypair(512);
491                 $sprvkey = $sres['prvkey'];
492                 $spubkey = $sres['pubkey'];
493
494                 $insert_result = DBA::insert('user', [
495                         'guid'     => generate_user_guid(),
496                         'username' => $username,
497                         'password' => $new_password_encoded,
498                         'email'    => $email,
499                         'openid'   => $openid_url,
500                         'nickname' => $nickname,
501                         'pubkey'   => $pubkey,
502                         'prvkey'   => $prvkey,
503                         'spubkey'  => $spubkey,
504                         'sprvkey'  => $sprvkey,
505                         'verified' => $verified,
506                         'blocked'  => $blocked,
507                         'language' => $language,
508                         'timezone' => 'UTC',
509                         'register_date' => DateTimeFormat::utcNow(),
510                         'default-location' => ''
511                 ]);
512
513                 if ($insert_result) {
514                         $uid = DBA::lastInsertId();
515                         $user = DBA::selectFirst('user', [], ['uid' => $uid]);
516                 } else {
517                         throw new Exception(L10n::t('An error occurred during registration. Please try again.'));
518                 }
519
520                 if (!$uid) {
521                         throw new Exception(L10n::t('An error occurred during registration. Please try again.'));
522                 }
523
524                 // if somebody clicked submit twice very quickly, they could end up with two accounts
525                 // due to race condition. Remove this one.
526                 $user_count = DBA::count('user', ['nickname' => $nickname]);
527                 if ($user_count > 1) {
528                         DBA::delete('user', ['uid' => $uid]);
529
530                         throw new Exception(L10n::t('Nickname is already registered. Please choose another.'));
531                 }
532
533                 $insert_result = DBA::insert('profile', [
534                         'uid' => $uid,
535                         'name' => $username,
536                         'photo' => System::baseUrl() . "/photo/profile/{$uid}.jpg",
537                         'thumb' => System::baseUrl() . "/photo/avatar/{$uid}.jpg",
538                         'publish' => $publish,
539                         'is-default' => 1,
540                         'net-publish' => $netpublish,
541                         'profile-name' => L10n::t('default')
542                 ]);
543                 if (!$insert_result) {
544                         DBA::delete('user', ['uid' => $uid]);
545
546                         throw new Exception(L10n::t('An error occurred creating your default profile. Please try again.'));
547                 }
548
549                 // Create the self contact
550                 if (!Contact::createSelfFromUserId($uid)) {
551                         DBA::delete('user', ['uid' => $uid]);
552
553                         throw new Exception(L10n::t('An error occurred creating your self contact. Please try again.'));
554                 }
555
556                 // Create a group with no members. This allows somebody to use it
557                 // right away as a default group for new contacts.
558                 $def_gid = Group::create($uid, L10n::t('Friends'));
559                 if (!$def_gid) {
560                         DBA::delete('user', ['uid' => $uid]);
561
562                         throw new Exception(L10n::t('An error occurred creating your default contact group. Please try again.'));
563                 }
564
565                 $fields = ['def_gid' => $def_gid];
566                 if (Config::get('system', 'newuser_private') && $def_gid) {
567                         $fields['allow_gid'] = '<' . $def_gid . '>';
568                 }
569
570                 DBA::update('user', $fields, ['uid' => $uid]);
571
572                 // if we have no OpenID photo try to look up an avatar
573                 if (!strlen($photo)) {
574                         $photo = Network::lookupAvatarByEmail($email);
575                 }
576
577                 // unless there is no avatar-addon loaded
578                 if (strlen($photo)) {
579                         $photo_failure = false;
580
581                         $filename = basename($photo);
582                         $img_str = Network::fetchUrl($photo, true);
583                         // guess mimetype from headers or filename
584                         $type = Image::guessType($photo, true);
585
586                         $Image = new Image($img_str, $type);
587                         if ($Image->isValid()) {
588                                 $Image->scaleToSquare(175);
589
590                                 $hash = Photo::newResource();
591
592                                 $r = Photo::store($Image, $uid, 0, $hash, $filename, L10n::t('Profile Photos'), 4);
593
594                                 if ($r === false) {
595                                         $photo_failure = true;
596                                 }
597
598                                 $Image->scaleDown(80);
599
600                                 $r = Photo::store($Image, $uid, 0, $hash, $filename, L10n::t('Profile Photos'), 5);
601
602                                 if ($r === false) {
603                                         $photo_failure = true;
604                                 }
605
606                                 $Image->scaleDown(48);
607
608                                 $r = Photo::store($Image, $uid, 0, $hash, $filename, L10n::t('Profile Photos'), 6);
609
610                                 if ($r === false) {
611                                         $photo_failure = true;
612                                 }
613
614                                 if (!$photo_failure) {
615                                         DBA::update('photo', ['profile' => 1], ['resource-id' => $hash]);
616                                 }
617                         }
618                 }
619
620                 Addon::callHooks('register_account', $uid);
621
622                 $return['user'] = $user;
623                 return $return;
624         }
625
626         /**
627          * @brief Sends pending registration confiƕmation email
628          *
629          * @param string $email
630          * @param string $sitename
631          * @param string $username
632          * @return NULL|boolean from notification() and email() inherited
633          */
634         public static function sendRegisterPendingEmail($email, $sitename, $username)
635         {
636                 $body = deindent(L10n::t('
637                         Dear %1$s,
638                                 Thank you for registering at %2$s. Your account is pending for approval by the administrator.
639                 '));
640
641                 $body = sprintf($body, $username, $sitename);
642
643                 return notification([
644                         'type' => SYSTEM_EMAIL,
645                         'to_email' => $email,
646                         'subject'=> L10n::t('Registration at %s', $sitename),
647                         'body' => $body]);
648         }
649
650         /**
651          * @brief Sends registration confirmation
652          *
653          * It's here as a function because the mail is sent from different parts
654          *
655          * @param string $email
656          * @param string $sitename
657          * @param string $siteurl
658          * @param string $username
659          * @param string $password
660          * @return NULL|boolean from notification() and email() inherited
661          */
662         public static function sendRegisterOpenEmail($email, $sitename, $siteurl, $username, $password)
663         {
664                 $preamble = deindent(L10n::t('
665                         Dear %1$s,
666                                 Thank you for registering at %2$s. Your account has been created.
667                 '));
668                 $body = deindent(L10n::t('
669                         The login details are as follows:
670
671                         Site Location:  %3$s
672                         Login Name:             %1$s
673                         Password:               %5$s
674
675                         You may change your password from your account "Settings" page after logging
676                         in.
677
678                         Please take a few moments to review the other account settings on that page.
679
680                         You may also wish to add some basic information to your default profile
681                         ' . "\x28" . 'on the "Profiles" page' . "\x29" . ' so that other people can easily find you.
682
683                         We recommend setting your full name, adding a profile photo,
684                         adding some profile "keywords" ' . "\x28" . 'very useful in making new friends' . "\x29" . ' - and
685                         perhaps what country you live in; if you do not wish to be more specific
686                         than that.
687
688                         We fully respect your right to privacy, and none of these items are necessary.
689                         If you are new and do not know anybody here, they may help
690                         you to make some new and interesting friends.
691
692                         If you ever want to delete your account, you can do so at %3$s/removeme
693
694                         Thank you and welcome to %2$s.'));
695
696                 $preamble = sprintf($preamble, $username, $sitename);
697                 $body = sprintf($body, $email, $sitename, $siteurl, $username, $password);
698
699                 return notification([
700                         'type' => SYSTEM_EMAIL,
701                         'to_email' => $email,
702                         'subject'=> L10n::t('Registration details for %s', $sitename),
703                         'preamble'=> $preamble,
704                         'body' => $body]);
705         }
706
707         /**
708          * @param object $uid user to remove
709          * @return void
710          */
711         public static function remove($uid)
712         {
713                 if (!$uid) {
714                         return;
715                 }
716
717                 logger('Removing user: ' . $uid);
718
719                 $user = DBA::selectFirst('user', [], ['uid' => $uid]);
720
721                 Addon::callHooks('remove_user', $user);
722
723                 // save username (actually the nickname as it is guaranteed
724                 // unique), so it cannot be re-registered in the future.
725                 DBA::insert('userd', ['username' => $user['nickname']]);
726
727                 // The user and related data will be deleted in "cron_expire_and_remove_users" (cronjobs.php)
728                 DBA::update('user', ['account_removed' => true, 'account_expires_on' => DateTimeFormat::utcNow()], ['uid' => $uid]);
729                 Worker::add(PRIORITY_HIGH, "Notifier", "removeme", $uid);
730
731                 // Send an update to the directory
732                 Worker::add(PRIORITY_LOW, "Directory", $user['url']);
733
734                 if ($uid == local_user()) {
735                         unset($_SESSION['authenticated']);
736                         unset($_SESSION['uid']);
737                         goaway(System::baseUrl());
738                 }
739         }
740 }