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