From 4395c2067941f9052eb2ccf009eed0adb8e05cf9 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 3 Dec 2017 22:15:31 -0500 Subject: [PATCH 1/4] Fix formatting and add documentation - mod/register - mod/regmod - Model/User - Worker/CronJobs --- mod/register.php | 4 +-- mod/regmod.php | 43 +++++++++++++++----------------- src/Model/User.php | 13 ++++++++++ src/Worker/CronJobs.php | 54 ++++++++++++++++++++++------------------- 4 files changed, 63 insertions(+), 51 deletions(-) diff --git a/mod/register.php b/mod/register.php index cd6385144c..8645ecb9ce 100644 --- a/mod/register.php +++ b/mod/register.php @@ -6,9 +6,9 @@ use Friendica\Core\PConfig; use Friendica\Core\System; use Friendica\Core\Worker; -require_once('include/enotify.php'); -require_once('include/bbcode.php'); require_once('include/user.php'); +require_once 'include/enotify.php'; +require_once 'include/bbcode.php'; if(! function_exists('register_post')) { function register_post(App $a) { diff --git a/mod/regmod.php b/mod/regmod.php index 6d76e7ea7e..7fce6914f2 100644 --- a/mod/regmod.php +++ b/mod/regmod.php @@ -6,11 +6,12 @@ use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBM; -require_once('include/enotify.php'); require_once('include/user.php'); -function user_allow($hash) { +require_once 'include/enotify.php'; +function user_allow($hash) +{ $a = get_app(); $register = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1", @@ -18,7 +19,7 @@ function user_allow($hash) { ); - if (! DBM::is_result($register)) { + if (!DBM::is_result($register)) { return false; } @@ -26,7 +27,7 @@ function user_allow($hash) { intval($register[0]['uid']) ); - if (! DBM::is_result($user)) { + if (!DBM::is_result($user)) { killme(); } @@ -44,7 +45,7 @@ function user_allow($hash) { ); if (DBM::is_result($r) && $r[0]['net-publish']) { $url = System::baseUrl() . '/profile/' . $user[0]['nickname']; - if ($url && strlen(Config::get('system','directory'))) { + if ($url && strlen(Config::get('system', 'directory'))) { Worker::add(PRIORITY_LOW, "Directory", $url); } } @@ -60,20 +61,17 @@ function user_allow($hash) { pop_lang(); - if($res) { - info( t('Account approved.') . EOL ); + if ($res) { + info(t('Account approved.') . EOL); return true; } - } - // This does not have to go through user_remove() and save the nickname // permanently against re-registration, as the person was not yet // allowed to have friends on this system - -function user_deny($hash) { - +function user_deny($hash) +{ $register = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1", dbesc($hash) ); @@ -91,23 +89,22 @@ function user_deny($hash) { notice(sprintf(t('Registration revoked for %s'), $user[0]['username']) . EOL); return true; - } -function regmod_content(App $a) { - +function regmod_content(App $a) +{ global $lang; $_SESSION['return_url'] = $a->cmd; - if (! local_user()) { - info( t('Please login.') . EOL); + if (!local_user()) { + info(t('Please login.') . EOL); $o .= '

' . login(($a->config['register_policy'] == REGISTER_CLOSED) ? 0 : 1); return $o; } - if ((!is_site_admin()) || (x($_SESSION,'submanage') && intval($_SESSION['submanage']))) { - notice( t('Permission denied.') . EOL); + if ((!is_site_admin()) || (x($_SESSION, 'submanage') && intval($_SESSION['submanage']))) { + notice(t('Permission denied.') . EOL); return ''; } @@ -115,20 +112,18 @@ function regmod_content(App $a) { killme(); } - $cmd = $a->argv[1]; + $cmd = $a->argv[1]; $hash = $a->argv[2]; - - if ($cmd === 'deny') { user_deny($hash); - goaway(System::baseUrl()."/admin/users/"); + goaway(System::baseUrl() . "/admin/users/"); killme(); } if ($cmd === 'allow') { user_allow($hash); - goaway(System::baseUrl()."/admin/users/"); + goaway(System::baseUrl() . "/admin/users/"); killme(); } } diff --git a/src/Model/User.php b/src/Model/User.php index b2beb8e19b..398cd19b42 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -20,6 +20,19 @@ require_once 'include/plugin.php'; */ class User { + /** + * @brief Authenticate a user with a clear text password + * + * User info can be any of the following: + * - User DB object + * - User Id + * - User email or username or nickname + * - User array with at least the uid and the hashed password + * + * @param mixed $user_info + * @param string $password + * @return boolean + */ public static function authenticate($user_info, $password) { if (is_object($user_info)) { diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index cbfa86ed88..a59cd4f9f1 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -1,4 +1,5 @@ '%s' @@ -120,9 +124,9 @@ class CronJobs { * * @param App $a */ - private static function clearCache(App $a) { - - $last = Config::get('system','cache_last_cleared'); + private static function clearCache(App $a) + { + $last = Config::get('system', 'cache_last_cleared'); if ($last) { $next = $last + (3600); // Once per hour @@ -142,16 +146,16 @@ class CronJobs { clear_cache(); // clear cache for photos - clear_cache($a->get_basepath(), $a->get_basepath()."/photo"); + clear_cache($a->get_basepath(), $a->get_basepath() . "/photo"); // clear smarty cache - clear_cache($a->get_basepath()."/view/smarty3/compiled", $a->get_basepath()."/view/smarty3/compiled"); + clear_cache($a->get_basepath() . "/view/smarty3/compiled", $a->get_basepath() . "/view/smarty3/compiled"); // clear cache for image proxy if (!Config::get("system", "proxy_disabled")) { - clear_cache($a->get_basepath(), $a->get_basepath()."/proxy"); + clear_cache($a->get_basepath(), $a->get_basepath() . "/proxy"); - $cachetime = Config::get('system','proxy_cache_time'); + $cachetime = Config::get('system', 'proxy_cache_time'); if (!$cachetime) { $cachetime = PROXY_DEFAULT_TIME; } @@ -166,13 +170,13 @@ class CronJobs { dba::delete('parsed_url', array("`created` < NOW() - INTERVAL 3 MONTH")); // Maximum table size in megabyte - $max_tablesize = intval(Config::get('system','optimize_max_tablesize')) * 1000000; + $max_tablesize = intval(Config::get('system', 'optimize_max_tablesize')) * 1000000; if ($max_tablesize == 0) { $max_tablesize = 100 * 1000000; // Default are 100 MB } if ($max_tablesize > 0) { // Minimum fragmentation level in percent - $fragmentation_level = intval(Config::get('system','optimize_fragmentation')) / 100; + $fragmentation_level = intval(Config::get('system', 'optimize_fragmentation')) / 100; if ($fragmentation_level == 0) { $fragmentation_level = 0.3; // Default value is 30% } @@ -194,7 +198,7 @@ class CronJobs { // Calculate fragmentation $fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]); - logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG); + logger("Table " . $table["Name"] . " - Fragmentation level: " . round($fragmentation * 100, 2), LOGGER_DEBUG); // Don't optimize tables that needn't to be optimized if ($fragmentation < $fragmentation_level) { @@ -202,12 +206,12 @@ class CronJobs { } // So optimize it - logger("Optimize Table ".$table["Name"], LOGGER_DEBUG); + logger("Optimize Table " . $table["Name"], LOGGER_DEBUG); q("OPTIMIZE TABLE `%s`", dbesc($table["Name"])); } } - Config::set('system','cache_last_cleared', time()); + Config::set('system', 'cache_last_cleared', time()); } /** @@ -215,8 +219,8 @@ class CronJobs { * * @param App $a */ - private static function repairDiaspora(App $a) { - + private static function repairDiaspora(App $a) + { $starttime = time(); $r = q("SELECT `id`, `url` FROM `contact` @@ -241,7 +245,7 @@ class CronJobs { continue; } - logger("Repair contact ".$contact["id"]." ".$contact["url"], LOGGER_DEBUG); + logger("Repair contact " . $contact["id"] . " " . $contact["url"], LOGGER_DEBUG); q("UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d", dbesc($data["batch"]), dbesc($data["notify"]), dbesc($data["poll"]), dbesc($data["pubkey"]), intval($contact["id"])); @@ -252,15 +256,15 @@ class CronJobs { * @brief Do some repairs in database entries * */ - private static function repairDatabase() { - + private static function repairDatabase() + { // Sometimes there seem to be issues where the "self" contact vanishes. // We haven't found the origin of the problem by now. $r = q("SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`)"); if (DBM::is_result($r)) { foreach ($r AS $user) { - logger('Create missing self contact for user '.$user['uid']); user_create_self_contact($user['uid']); + logger('Create missing self contact for user ' . $user['uid']); } } From b0dcfc2724188c309a70ce5281e94fdf6a733498 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 3 Dec 2017 22:27:49 -0500 Subject: [PATCH 2/4] Add class methods derived from include/user functions - Add User::create - Add User::sendRegisterPendingEmail - Add User::sendRegisterOpenEmail - Add Contact::createSelfFromUserId --- src/Model/User.php | 429 ++++++++++++++++++++++++++++++++++++++++- src/Object/Contact.php | 46 +++++ 2 files changed, 474 insertions(+), 1 deletion(-) diff --git a/src/Model/User.php b/src/Model/User.php index 398cd19b42..72e3aea939 100644 --- a/src/Model/User.php +++ b/src/Model/User.php @@ -7,14 +7,23 @@ namespace Friendica\Model; +use Friendica\Core\Config; use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBM; +use Friendica\Object\Contact; +use Friendica\Object\Photo; use dba; require_once 'boot.php'; +require_once 'include/crypto.php'; +require_once 'include/enotify.php'; +require_once 'include/group.php'; +require_once 'include/network.php'; +require_once 'library/openid.php'; +require_once 'include/pgettext.php'; require_once 'include/plugin.php'; - +require_once 'include/text.php'; /** * @brief This class handles User related functions */ @@ -79,6 +88,424 @@ class User return $user['uid']; } + /** + * @brief Catch-all user creation function + * + * Creates a user from the provided data array, either form fields or OpenID. + * Required: { username, nickname, email } or { openid_url } + * + * Performs the following: + * - Sends to the OpenId auth URL (if relevant) + * - Creates new key pairs for crypto + * - Create self-contact + * - Create profile image + * + * @param array $data + * @return string + */ + public static function create(array $data) + { + $a = get_app(); + $result = array('success' => false, 'user' => null, 'password' => '', 'message' => ''); + + $using_invites = Config::get('system', 'invitation_only'); + $num_invites = Config::get('system', 'number_invites'); + + $invite_id = x($data, 'invite_id') ? notags(trim($data['invite_id'])) : ''; + $username = x($data, 'username') ? notags(trim($data['username'])) : ''; + $nickname = x($data, 'nickname') ? notags(trim($data['nickname'])) : ''; + $email = x($data, 'email') ? notags(trim($data['email'])) : ''; + $openid_url = x($data, 'openid_url') ? notags(trim($data['openid_url'])) : ''; + $photo = x($data, 'photo') ? notags(trim($data['photo'])) : ''; + $password = x($data, 'password') ? trim($data['password']) : ''; + $password1 = x($data, 'password1') ? trim($data['password1']) : ''; + $confirm = x($data, 'confirm') ? trim($data['confirm']) : ''; + $blocked = x($data, 'blocked') ? intval($data['blocked']) : 0; + $verified = x($data, 'verified') ? intval($data['verified']) : 0; + + $publish = x($data, 'profile_publish_reg') && intval($data['profile_publish_reg']) ? 1 : 0; + $netpublish = strlen(Config::get('system', 'directory')) ? $publish : 0; + + if ($password1 != $confirm) { + $result['message'] .= t('Passwords do not match. Password unchanged.') . EOL; + return $result; + } elseif ($password1 != "") { + $password = $password1; + } + + $tmp_str = $openid_url; + + if ($using_invites) { + if (!$invite_id) { + $result['message'] .= t('An invitation is required.') . EOL; + return $result; + } + $r = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1", dbesc($invite_id)); + if (!results($r)) { + $result['message'] .= t('Invitation could not be verified.') . EOL; + return $result; + } + } + + if (!x($username) || !x($email) || !x($nickname)) { + if ($openid_url) { + if (!validate_url($tmp_str)) { + $result['message'] .= t('Invalid OpenID url') . EOL; + return $result; + } + $_SESSION['register'] = 1; + $_SESSION['openid'] = $openid_url; + + $openid = new LightOpenID; + $openid->identity = $openid_url; + $openid->returnUrl = System::baseUrl() . '/openid'; + $openid->required = array('namePerson/friendly', 'contact/email', 'namePerson'); + $openid->optional = array('namePerson/first', 'media/image/aspect11', 'media/image/default'); + try { + $authurl = $openid->authUrl(); + } catch (Exception $e) { + $result['message'] .= t("We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.") . EOL . EOL . t("The error message was:") . $e->getMessage() . EOL; + return $result; + } + goaway($authurl); + // NOTREACHED + } + + notice(t('Please enter the required information.') . EOL); + return; + } + + if (!validate_url($tmp_str)) { + $openid_url = ''; + } + + $err = ''; + + // collapse multiple spaces in name + $username = preg_replace('/ +/', ' ', $username); + + if (mb_strlen($username) > 48) { + $result['message'] .= t('Please use a shorter name.') . EOL; + } + if (mb_strlen($username) < 3) { + $result['message'] .= t('Name too short.') . EOL; + } + + // So now we are just looking for a space in the full name. + + $loose_reg = Config::get('system', 'no_regfullname'); + if (!$loose_reg) { + $username = mb_convert_case($username, MB_CASE_TITLE, 'UTF-8'); + if (!strpos($username, ' ')) { + $result['message'] .= t("That doesn't appear to be your full \x28First Last\x29 name.") . EOL; + } + } + + + if (!allowed_email($email)) { + $result['message'] .= t('Your email domain is not among those allowed on this site.') . EOL; + } + + if (!valid_email($email) || !validate_email($email)) { + $result['message'] .= t('Not a valid email address.') . EOL; + } + + // Disallow somebody creating an account using openid that uses the admin email address, + // since openid bypasses email verification. We'll allow it if there is not yet an admin account. + + $adminlist = explode(",", str_replace(" ", "", strtolower($a->config['admin_email']))); + + //if((x($a->config,'admin_email')) && (strcasecmp($email,$a->config['admin_email']) == 0) && strlen($openid_url)) { + if (x($a->config, 'admin_email') && in_array(strtolower($email), $adminlist) && strlen($openid_url)) { + $r = q("SELECT * FROM `user` WHERE `email` = '%s' LIMIT 1", + dbesc($email) + ); + if (DBM::is_result($r)) { + $result['message'] .= t('Cannot use that email.') . EOL; + } + } + + $nickname = $data['nickname'] = strtolower($nickname); + + if (!preg_match("/^[a-z0-9][a-z0-9\_]*$/", $nickname)) { + $result['message'] .= t('Your "nickname" can only contain "a-z", "0-9" and "_".') . EOL; + } + + $r = q("SELECT `uid` FROM `user` + WHERE `nickname` = '%s' LIMIT 1", + dbesc($nickname) + ); + if (DBM::is_result($r)) { + $result['message'] .= t('Nickname is already registered. Please choose another.') . EOL; + } + + // Check deleted accounts that had this nickname. Doesn't matter to us, + // but could be a security issue for federated platforms. + + $r = q("SELECT * FROM `userd` + WHERE `username` = '%s' LIMIT 1", + dbesc($nickname) + ); + if (DBM::is_result($r)) { + $result['message'] .= t('Nickname was once registered here and may not be re-used. Please choose another.') . EOL; + } + + if (strlen($result['message'])) { + return $result; + } + + $new_password = strlen($password) ? $password : autoname(6) . mt_rand(100, 9999); + $new_password_encoded = hash('whirlpool', $new_password); + + $result['password'] = $new_password; + + $keys = new_keypair(4096); + + if ($keys === false) { + $result['message'] .= t('SERIOUS ERROR: Generation of security keys failed.') . EOL; + return $result; + } + + $prvkey = $keys['prvkey']; + $pubkey = $keys['pubkey']; + + // Create another keypair for signing/verifying salmon protocol messages. + $sres = new_keypair(512); + $sprvkey = $sres['prvkey']; + $spubkey = $sres['pubkey']; + + $r = q("INSERT INTO `user` (`guid`, `username`, `password`, `email`, `openid`, `nickname`, + `pubkey`, `prvkey`, `spubkey`, `sprvkey`, `register_date`, `verified`, `blocked`, `timezone`, `default-location`) + VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, 'UTC', '')", + dbesc(generate_user_guid()), + dbesc($username), + dbesc($new_password_encoded), + dbesc($email), + dbesc($openid_url), + dbesc($nickname), + dbesc($pubkey), + dbesc($prvkey), + dbesc($spubkey), + dbesc($sprvkey), + dbesc(datetime_convert()), + intval($verified), + intval($blocked) + ); + + if ($r) { + $r = q("SELECT * FROM `user` + WHERE `username` = '%s' AND `password` = '%s' LIMIT 1", + dbesc($username), + dbesc($new_password_encoded) + ); + if (DBM::is_result($r)) { + $u = $r[0]; + $newuid = intval($r[0]['uid']); + } + } else { + $result['message'] .= t('An error occurred during registration. Please try again.') . EOL; + return $result; + } + + /** + * if somebody clicked submit twice very quickly, they could end up with two accounts + * due to race condition. Remove this one. + */ + $r = q("SELECT `uid` FROM `user` + WHERE `nickname` = '%s' ", + dbesc($nickname) + ); + if (DBM::is_result($r) && count($r) > 1 && $newuid) { + $result['message'] .= t('Nickname is already registered. Please choose another.') . EOL; + dba::delete('user', array('uid' => $newuid)); + return $result; + } + + if (x($newuid) !== false) { + $r = q("INSERT INTO `profile` ( `uid`, `profile-name`, `is-default`, `name`, `photo`, `thumb`, `publish`, `net-publish` ) + VALUES ( %d, '%s', %d, '%s', '%s', '%s', %d, %d ) ", + intval($newuid), + t('default'), + 1, + dbesc($username), + dbesc(System::baseUrl() . "/photo/profile/{$newuid}.jpg"), + dbesc(System::baseUrl() . "/photo/avatar/{$newuid}.jpg"), + intval($publish), + intval($netpublish) + ); + if ($r === false) { + $result['message'] .= t('An error occurred creating your default profile. Please try again.') . EOL; + // Start fresh next time. + dba::delete('user', array('uid' => $newuid)); + return $result; + } + + // Create the self contact + Contact::createSelfFromUserId($newuid); + + // Create a group with no members. This allows somebody to use it + // right away as a default group for new contacts. + + group_add($newuid, t('Friends')); + + $r = q("SELECT `id` FROM `group` WHERE `uid` = %d AND `name` = '%s'", + intval($newuid), + dbesc(t('Friends')) + ); + if (DBM::is_result($r)) { + $def_gid = $r[0]['id']; + + q("UPDATE `user` SET `def_gid` = %d WHERE `uid` = %d", + intval($r[0]['id']), + intval($newuid) + ); + } + + if (Config::get('system', 'newuser_private') && $def_gid) { + q("UPDATE `user` SET `allow_gid` = '%s' WHERE `uid` = %d", + dbesc("<" . $def_gid . ">"), + intval($newuid) + ); + } + } + + // if we have no OpenID photo try to look up an avatar + if (!strlen($photo)) { + $photo = avatar_img($email); + } + + // unless there is no avatar-plugin loaded + if (strlen($photo)) { + $photo_failure = false; + + $filename = basename($photo); + $img_str = fetch_url($photo, true); + // guess mimetype from headers or filename + $type = Photo::guessImageType($photo, true); + + + $img = new Photo($img_str, $type); + if ($img->isValid()) { + $img->scaleImageSquare(175); + + $hash = photo_new_resource(); + + $r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 4); + + if ($r === false) { + $photo_failure = true; + } + + $img->scaleImage(80); + + $r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 5); + + if ($r === false) { + $photo_failure = true; + } + + $img->scaleImage(48); + + $r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 6); + + if ($r === false) { + $photo_failure = true; + } + + if (!$photo_failure) { + q("UPDATE `photo` SET `profile` = 1 WHERE `resource-id` = '%s' ", + dbesc($hash) + ); + } + } + } + + call_hooks('register_account', $newuid); + + $result['success'] = true; + $result['user'] = $u; + return $result; + } + + /** + * @brief Sends pending registration confiƕmation email + * + * @param string $email + * @param string $sitename + * @param string $username + * @return NULL|boolean from notification() and email() inherited + */ + public static function sendRegisterPendingEmail($email, $sitename, $username) + { + $body = deindent(t(' + Dear %1$s, + Thank you for registering at %2$s. Your account is pending for approval by the administrator. + ')); + + $body = sprintf($body, $username, $sitename); + + return notification(array( + 'type' => SYSTEM_EMAIL, + 'to_email' => $email, + 'subject'=> sprintf( t('Registration at %s'), $sitename), + 'body' => $body)); + } + + /** + * @brief Sends registration confirmation + * + * It's here as a function because the mail is sent from different parts + * + * @param string $email + * @param string $sitename + * @param string $siteurl + * @param string $username + * @param string $password + * @return NULL|boolean from notification() and email() inherited + */ + public static function sendRegisterOpenEmail($email, $sitename, $siteurl, $username, $password) + { + $preamble = deindent(t(' + Dear %1$s, + Thank you for registering at %2$s. Your account has been created. + ')); + $body = deindent(t(' + The login details are as follows: + Site Location: %3$s + Login Name: %1$s + Password: %5$s + + You may change your password from your account "Settings" page after logging + in. + + Please take a few moments to review the other account settings on that page. + + You may also wish to add some basic information to your default profile + (on the "Profiles" page) so that other people can easily find you. + + We recommend setting your full name, adding a profile photo, + adding some profile "keywords" (very useful in making new friends) - and + perhaps what country you live in; if you do not wish to be more specific + than that. + + We fully respect your right to privacy, and none of these items are necessary. + If you are new and do not know anybody here, they may help + you to make some new and interesting friends. + + + Thank you and welcome to %2$s.')); + + $preamble = sprintf($preamble, $username, $sitename); + $body = sprintf($body, $email, $sitename, $siteurl, $username, $password); + + return notification(array( + 'type' => SYSTEM_EMAIL, + 'to_email' => $email, + 'subject'=> sprintf( t('Registration details for %s'), $sitename), + 'preamble'=> $preamble, + 'body' => $body)); + } + /** * @param object $uid user to remove * @return void diff --git a/src/Object/Contact.php b/src/Object/Contact.php index 34519401cb..fc1b26aba6 100644 --- a/src/Object/Contact.php +++ b/src/Object/Contact.php @@ -28,6 +28,52 @@ require_once 'include/text.php'; */ class Contact extends BaseObject { + /** + * Creates the self-contact for the provided user id + * + * @param int $uid + * @return bool Operation success + */ + public static function createSelfFromUserId($uid) + { + // Only create the entry if it doesn't exist yet + if (dba::exists('contact', ['uid' => intval($uid), 'self'])) { + return true; + } + + $user = dba::select('user', ['uid', 'username', 'nickname'], ['uid' => intval($uid)], ['limit' => 1]); + if (!DBM::is_result($user)) { + return false; + } + + $return = dba::insert('contact', [ + 'uid' => $user['uid'], + 'created' => datetime_convert(), + 'self' => 1, + 'name' => $user['username'], + 'nick' => $user['nickname'], + 'photo' => System::baseUrl() . '/photo/profile/' . $user['uid'] . '.jpg', + 'thumb' => System::baseUrl() . '/photo/avatar/' . $user['uid'] . '.jpg', + 'micro' => System::baseUrl() . '/photo/micro/' . $user['uid'] . '.jpg', + 'blocked' => 0, + 'pending' => 0, + 'url' => System::baseUrl() . '/profile/' . $user['nickname'], + 'nurl' => normalise_link(System::baseUrl() . '/profile/' . $user['nickname']), + 'addr' => $user['nickname'] . '@' . substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3), + 'request' => System::baseUrl() . '/dfrn_request/' . $user['nickname'], + 'notify' => System::baseUrl() . '/dfrn_notify/' . $user['nickname'], + 'poll' => System::baseUrl() . '/dfrn_poll/' . $user['nickname'], + 'confirm' => System::baseUrl() . '/dfrn_confirm/' . $user['nickname'], + 'poco' => System::baseUrl() . '/poco/' . $user['nickname'], + 'name-date' => datetime_convert(), + 'uri-date' => datetime_convert(), + 'avatar-date' => datetime_convert(), + 'closeness' => 0 + ]); + + return $return; + } + /** * @brief Marks a contact for removal * From 27212c7f3904d42308686bf82b9b300be3d8dc7c Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 3 Dec 2017 22:29:06 -0500 Subject: [PATCH 3/4] Update function calls to use Model\User and Object\Contact --- mod/admin.php | 4 +--- mod/register.php | 8 ++++---- mod/regmod.php | 4 +--- src/Worker/CronJobs.php | 3 ++- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/mod/admin.php b/mod/admin.php index d73ac23fd0..1b6975ab89 100644 --- a/mod/admin.php +++ b/mod/admin.php @@ -1466,9 +1466,7 @@ function admin_page_users_post(App $a) check_form_security_token_redirectOnErr('/admin/users', 'admin_users'); if (!($nu_name === "") && !($nu_email === "") && !($nu_nickname === "")) { - require_once 'include/user.php'; - - $result = create_user(array('username' => $nu_name, 'email' => $nu_email, + $result = User::create(array('username' => $nu_name, 'email' => $nu_email, 'nickname' => $nu_nickname, 'verified' => 1, 'language' => $nu_language)); if (!$result['success']) { notice($result['message']); diff --git a/mod/register.php b/mod/register.php index 8645ecb9ce..4f6dedd24b 100644 --- a/mod/register.php +++ b/mod/register.php @@ -5,8 +5,8 @@ use Friendica\Core\Config; use Friendica\Core\PConfig; use Friendica\Core\System; use Friendica\Core\Worker; +use Friendica\Model\User; -require_once('include/user.php'); require_once 'include/enotify.php'; require_once 'include/bbcode.php'; @@ -61,7 +61,7 @@ function register_post(App $a) { $arr['verified'] = $verified; $arr['language'] = get_browser_language(); - $result = create_user($arr); + $result = User::create($arr); if(! $result['success']) { notice($result['message']); @@ -89,7 +89,7 @@ function register_post(App $a) { // Only send a password mail when the password wasn't manually provided if (!x($_POST,'password1') || !x($_POST,'confirm')) { - $res = send_register_open_eml( + $res = User::sendRegisterOpenEmail( $user['email'], $a->config['sitename'], System::baseUrl(), @@ -159,7 +159,7 @@ function register_post(App $a) { )); } // send notification to the user, that the registration is pending - send_register_pending_eml( + User::sendRegisterPendingEmail( $user['email'], $a->config['sitename'], $user['username']); diff --git a/mod/regmod.php b/mod/regmod.php index 7fce6914f2..2c5d61059a 100644 --- a/mod/regmod.php +++ b/mod/regmod.php @@ -6,8 +6,6 @@ use Friendica\Core\System; use Friendica\Core\Worker; use Friendica\Database\DBM; -require_once('include/user.php'); - require_once 'include/enotify.php'; function user_allow($hash) @@ -52,7 +50,7 @@ function user_allow($hash) push_lang($register[0]['language']); - send_register_open_eml( + User::sendRegisterOpenEmail( $user[0]['email'], $a->config['sitename'], System::baseUrl(), diff --git a/src/Worker/CronJobs.php b/src/Worker/CronJobs.php index a59cd4f9f1..7df7d030cb 100644 --- a/src/Worker/CronJobs.php +++ b/src/Worker/CronJobs.php @@ -12,6 +12,7 @@ use Friendica\Core\Config; use Friendica\Database\DBM; use Friendica\Model\GlobalContact; use Friendica\Network\Probe; +use Friendica\Object\Contact; use Friendica\Protocol\PortableContact; use dba; @@ -263,8 +264,8 @@ class CronJobs $r = q("SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`)"); if (DBM::is_result($r)) { foreach ($r AS $user) { - user_create_self_contact($user['uid']); logger('Create missing self contact for user ' . $user['uid']); + Contact::createSelfFromUserId($user['uid']); } } From 85f6d6b7ed518a32fd939335fa646f959f0d636d Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 3 Dec 2017 22:29:13 -0500 Subject: [PATCH 4/4] Remove include/user --- include/user.php | 453 ----------------------------------------------- 1 file changed, 453 deletions(-) delete mode 100644 include/user.php diff --git a/include/user.php b/include/user.php deleted file mode 100644 index 356f10ad09..0000000000 --- a/include/user.php +++ /dev/null @@ -1,453 +0,0 @@ - false, 'user' => null, 'password' => '', 'message' => ''); - - $using_invites = Config::get('system','invitation_only'); - $num_invites = Config::get('system','number_invites'); - - - $invite_id = ((x($arr,'invite_id')) ? notags(trim($arr['invite_id'])) : ''); - $username = ((x($arr,'username')) ? notags(trim($arr['username'])) : ''); - $nickname = ((x($arr,'nickname')) ? notags(trim($arr['nickname'])) : ''); - $email = ((x($arr,'email')) ? notags(trim($arr['email'])) : ''); - $openid_url = ((x($arr,'openid_url')) ? notags(trim($arr['openid_url'])) : ''); - $photo = ((x($arr,'photo')) ? notags(trim($arr['photo'])) : ''); - $password = ((x($arr,'password')) ? trim($arr['password']) : ''); - $password1 = ((x($arr,'password1')) ? trim($arr['password1']) : ''); - $confirm = ((x($arr,'confirm')) ? trim($arr['confirm']) : ''); - $blocked = ((x($arr,'blocked')) ? intval($arr['blocked']) : 0); - $verified = ((x($arr,'verified')) ? intval($arr['verified']) : 0); - - $publish = ((x($arr,'profile_publish_reg') && intval($arr['profile_publish_reg'])) ? 1 : 0); - $netpublish = ((strlen(Config::get('system','directory'))) ? $publish : 0); - - if ($password1 != $confirm) { - $result['message'] .= t('Passwords do not match. Password unchanged.') . EOL; - return $result; - } elseif ($password1 != "") - $password = $password1; - - $tmp_str = $openid_url; - - if($using_invites) { - if(! $invite_id) { - $result['message'] .= t('An invitation is required.') . EOL; - return $result; - } - $r = q("SELECT * FROM `register` WHERE `hash` = '%s' LIMIT 1", dbesc($invite_id)); - if(! results($r)) { - $result['message'] .= t('Invitation could not be verified.') . EOL; - return $result; - } - } - - if((! x($username)) || (! x($email)) || (! x($nickname))) { - if($openid_url) { - if(! validate_url($tmp_str)) { - $result['message'] .= t('Invalid OpenID url') . EOL; - return $result; - } - $_SESSION['register'] = 1; - $_SESSION['openid'] = $openid_url; - require_once('library/openid.php'); - $openid = new LightOpenID; - $openid->identity = $openid_url; - $openid->returnUrl = System::baseUrl() . '/openid'; - $openid->required = array('namePerson/friendly', 'contact/email', 'namePerson'); - $openid->optional = array('namePerson/first','media/image/aspect11','media/image/default'); - try { - $authurl = $openid->authUrl(); - } catch (Exception $e){ - $result['message'] .= t("We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID."). EOL . EOL . t("The error message was:") . $e->getMessage() . EOL; - return $result; - } - goaway($authurl); - // NOTREACHED - } - - notice( t('Please enter the required information.') . EOL ); - return; - } - - if(! validate_url($tmp_str)) - $openid_url = ''; - - - $err = ''; - - // collapse multiple spaces in name - $username = preg_replace('/ +/',' ',$username); - - if(mb_strlen($username) > 48) - $result['message'] .= t('Please use a shorter name.') . EOL; - if(mb_strlen($username) < 3) - $result['message'] .= t('Name too short.') . EOL; - - // So now we are just looking for a space in the full name. - - $loose_reg = Config::get('system','no_regfullname'); - if(! $loose_reg) { - $username = mb_convert_case($username,MB_CASE_TITLE,'UTF-8'); - if(! strpos($username,' ')) - $result['message'] .= t("That doesn't appear to be your full \x28First Last\x29 name.") . EOL; - } - - - if(! allowed_email($email)) - $result['message'] .= t('Your email domain is not among those allowed on this site.') . EOL; - - if((! valid_email($email)) || (! validate_email($email))) - $result['message'] .= t('Not a valid email address.') . EOL; - - // Disallow somebody creating an account using openid that uses the admin email address, - // since openid bypasses email verification. We'll allow it if there is not yet an admin account. - - $adminlist = explode(",", str_replace(" ", "", strtolower($a->config['admin_email']))); - - //if((x($a->config,'admin_email')) && (strcasecmp($email,$a->config['admin_email']) == 0) && strlen($openid_url)) { - if((x($a->config,'admin_email')) && in_array(strtolower($email), $adminlist) && strlen($openid_url)) { - $r = q("SELECT * FROM `user` WHERE `email` = '%s' LIMIT 1", - dbesc($email) - ); - if (DBM::is_result($r)) - $result['message'] .= t('Cannot use that email.') . EOL; - } - - $nickname = $arr['nickname'] = strtolower($nickname); - - if(! preg_match("/^[a-z0-9][a-z0-9\_]*$/",$nickname)) - $result['message'] .= t('Your "nickname" can only contain "a-z", "0-9" and "_".') . EOL; - - $r = q("SELECT `uid` FROM `user` - WHERE `nickname` = '%s' LIMIT 1", - dbesc($nickname) - ); - if (DBM::is_result($r)) - $result['message'] .= t('Nickname is already registered. Please choose another.') . EOL; - - // Check deleted accounts that had this nickname. Doesn't matter to us, - // but could be a security issue for federated platforms. - - $r = q("SELECT * FROM `userd` - WHERE `username` = '%s' LIMIT 1", - dbesc($nickname) - ); - if (DBM::is_result($r)) - $result['message'] .= t('Nickname was once registered here and may not be re-used. Please choose another.') . EOL; - - if(strlen($result['message'])) { - return $result; - } - - $new_password = ((strlen($password)) ? $password : autoname(6) . mt_rand(100,9999)); - $new_password_encoded = hash('whirlpool',$new_password); - - $result['password'] = $new_password; - - require_once('include/crypto.php'); - - $keys = new_keypair(4096); - - if($keys === false) { - $result['message'] .= t('SERIOUS ERROR: Generation of security keys failed.') . EOL; - return $result; - } - - $prvkey = $keys['prvkey']; - $pubkey = $keys['pubkey']; - - // Create another keypair for signing/verifying salmon protocol messages. - $sres = new_keypair(512); - $sprvkey = $sres['prvkey']; - $spubkey = $sres['pubkey']; - - $r = q("INSERT INTO `user` (`guid`, `username`, `password`, `email`, `openid`, `nickname`, - `pubkey`, `prvkey`, `spubkey`, `sprvkey`, `register_date`, `verified`, `blocked`, `timezone`, `default-location`) - VALUES ('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %d, %d, 'UTC', '')", - dbesc(generate_user_guid()), - dbesc($username), - dbesc($new_password_encoded), - dbesc($email), - dbesc($openid_url), - dbesc($nickname), - dbesc($pubkey), - dbesc($prvkey), - dbesc($spubkey), - dbesc($sprvkey), - dbesc(datetime_convert()), - intval($verified), - intval($blocked) - ); - - if ($r) { - $r = q("SELECT * FROM `user` - WHERE `username` = '%s' AND `password` = '%s' LIMIT 1", - dbesc($username), - dbesc($new_password_encoded) - ); - if (DBM::is_result($r)) { - $u = $r[0]; - $newuid = intval($r[0]['uid']); - } - } - else { - $result['message'] .= t('An error occurred during registration. Please try again.') . EOL ; - return $result; - } - - /** - * if somebody clicked submit twice very quickly, they could end up with two accounts - * due to race condition. Remove this one. - */ - - $r = q("SELECT `uid` FROM `user` - WHERE `nickname` = '%s' ", - dbesc($nickname) - ); - if ((DBM::is_result($r)) && (count($r) > 1) && $newuid) { - $result['message'] .= t('Nickname is already registered. Please choose another.') . EOL; - dba::delete('user', array('uid' => $newuid)); - return $result; - } - - if(x($newuid) !== false) { - $r = q("INSERT INTO `profile` ( `uid`, `profile-name`, `is-default`, `name`, `photo`, `thumb`, `publish`, `net-publish` ) - VALUES ( %d, '%s', %d, '%s', '%s', '%s', %d, %d ) ", - intval($newuid), - t('default'), - 1, - dbesc($username), - dbesc(System::baseUrl() . "/photo/profile/{$newuid}.jpg"), - dbesc(System::baseUrl() . "/photo/avatar/{$newuid}.jpg"), - intval($publish), - intval($netpublish) - - ); - if ($r === false) { - $result['message'] .= t('An error occurred creating your default profile. Please try again.') . EOL; - // Start fresh next time. - dba::delete('user', array('uid' => $newuid)); - return $result; - } - - // Create the self contact - user_create_self_contact($newuid); - - // Create a group with no members. This allows somebody to use it - // right away as a default group for new contacts. - - require_once('include/group.php'); - group_add($newuid, t('Friends')); - - $r = q("SELECT `id` FROM `group` WHERE `uid` = %d AND `name` = '%s'", - intval($newuid), - dbesc(t('Friends')) - ); - if (DBM::is_result($r)) { - $def_gid = $r[0]['id']; - - q("UPDATE `user` SET `def_gid` = %d WHERE `uid` = %d", - intval($r[0]['id']), - intval($newuid) - ); - } - - if(Config::get('system', 'newuser_private') && $def_gid) { - q("UPDATE `user` SET `allow_gid` = '%s' WHERE `uid` = %d", - dbesc("<" . $def_gid . ">"), - intval($newuid) - ); - } - - } - - // if we have no OpenID photo try to look up an avatar - if(! strlen($photo)) - $photo = avatar_img($email); - - // unless there is no avatar-plugin loaded - if (strlen($photo)) { - $photo_failure = false; - - $filename = basename($photo); - $img_str = fetch_url($photo, true); - // guess mimetype from headers or filename - $type = Photo::guessImageType($photo, true); - - - $img = new Photo($img_str, $type); - if ($img->isValid()) { - $img->scaleImageSquare(175); - - $hash = photo_new_resource(); - - $r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 4); - - if ($r === false) { - $photo_failure = true; - } - - $img->scaleImage(80); - - $r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 5 ); - - if ($r === false) { - $photo_failure = true; - } - - $img->scaleImage(48); - - $r = $img->store($newuid, 0, $hash, $filename, t('Profile Photos'), 6 ); - - if ($r === false) { - $photo_failure = true; - } - - if (! $photo_failure) { - q("UPDATE `photo` SET `profile` = 1 WHERE `resource-id` = '%s' ", - dbesc($hash) - ); - } - } - } - - call_hooks('register_account', $newuid); - - $result['success'] = true; - $result['user'] = $u; - return $result; - -} - -/** - * @brief create the "self" contact from data from the user table - * - * @param integer $uid - */ -function user_create_self_contact($uid) { - - // Only create the entry if it doesn't exist yet - $r = q("SELECT `id` FROM `contact` WHERE `uid` = %d AND `self`", intval($uid)); - if (DBM::is_result($r)) { - return; - } - - $r = q("SELECT `uid`, `username`, `nickname` FROM `user` WHERE `uid` = %d", intval($uid)); - if (!DBM::is_result($r)) { - return; - } - - $user = $r[0]; - - q("INSERT INTO `contact` (`uid`, `created`, `self`, `name`, `nick`, `photo`, `thumb`, `micro`, `blocked`, `pending`, `url`, `nurl`, - `addr`, `request`, `notify`, `poll`, `confirm`, `poco`, `name-date`, `uri-date`, `avatar-date`, `closeness`) - VALUES (%d, '%s', 1, '%s', '%s', '%s', '%s', '%s', 0, 0, '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', 0)", - intval($user['uid']), - datetime_convert(), - dbesc($user['username']), - dbesc($user['nickname']), - dbesc(System::baseUrl()."/photo/profile/".$user['uid'].".jpg"), - dbesc(System::baseUrl()."/photo/avatar/".$user['uid'].".jpg"), - dbesc(System::baseUrl()."/photo/micro/".$user['uid'].".jpg"), - dbesc(System::baseUrl()."/profile/".$user['nickname']), - dbesc(normalise_link(System::baseUrl()."/profile/".$user['nickname'])), - dbesc($user['nickname'].'@'.substr(System::baseUrl(), strpos(System::baseUrl(),'://') + 3)), - dbesc(System::baseUrl()."/dfrn_request/".$user['nickname']), - dbesc(System::baseUrl()."/dfrn_notify/".$user['nickname']), - dbesc(System::baseUrl()."/dfrn_poll/".$user['nickname']), - dbesc(System::baseUrl()."/dfrn_confirm/".$user['nickname']), - dbesc(System::baseUrl()."/poco/".$user['nickname']), - dbesc(datetime_convert()), - dbesc(datetime_convert()), - dbesc(datetime_convert()) - ); -} - -/** - * @brief send registration confiƕmation with the intormation that reg is pending - * - * @param string $email - * @param string $sitename - * @param string $username - * @return NULL|boolean from notification() and email() inherited - */ -function send_register_pending_eml($email, $sitename, $username) { - $body = deindent(t(' - Dear %1$s, - Thank you for registering at %2$s. Your account is pending for approval by the administrator. - ')); - - $body = sprintf($body, $username, $sitename); - - return notification(array( - 'type' => SYSTEM_EMAIL, - 'to_email' => $email, - 'subject'=> sprintf( t('Registration at %s'), $sitename), - 'body' => $body)); -} - -/* - * send registration confirmation. - * It's here as a function because the mail is sent - * from different parts - */ -function send_register_open_eml($email, $sitename, $siteurl, $username, $password){ - $preamble = deindent(t(' - Dear %1$s, - Thank you for registering at %2$s. Your account has been created. - ')); - $body = deindent(t(' - The login details are as follows: - Site Location: %3$s - Login Name: %1$s - Password: %5$s - - You may change your password from your account "Settings" page after logging - in. - - Please take a few moments to review the other account settings on that page. - - You may also wish to add some basic information to your default profile - (on the "Profiles" page) so that other people can easily find you. - - We recommend setting your full name, adding a profile photo, - adding some profile "keywords" (very useful in making new friends) - and - perhaps what country you live in; if you do not wish to be more specific - than that. - - We fully respect your right to privacy, and none of these items are necessary. - If you are new and do not know anybody here, they may help - you to make some new and interesting friends. - - - Thank you and welcome to %2$s.')); - - $preamble = sprintf($preamble, $username, $sitename); - $body = sprintf($body, $email, $sitename, $siteurl, $username, $password); - - return notification(array( - 'type' => SYSTEM_EMAIL, - 'to_email' => $email, - 'subject'=> sprintf( t('Registration details for %s'), $sitename), - 'preamble'=> $preamble, - 'body' => $body)); -}