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