use DivineOmega\PasswordExposed;
use Exception;
-use Friendica\Core\Addon;
use Friendica\Core\Config;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
*/
class User
{
+ /**
+ * Page/profile types
+ *
+ * PAGE_FLAGS_NORMAL is a typical personal profile account
+ * PAGE_FLAGS_SOAPBOX automatically approves all friend requests as Contact::SHARING, (readonly)
+ * PAGE_FLAGS_COMMUNITY automatically approves all friend requests as Contact::SHARING, but with
+ * write access to wall and comments (no email and not included in page owner's ACL lists)
+ * PAGE_FLAGS_FREELOVE automatically approves all friend requests as full friends (Contact::FRIEND).
+ *
+ * @{
+ */
+ const PAGE_FLAGS_NORMAL = 0;
+ const PAGE_FLAGS_SOAPBOX = 1;
+ const PAGE_FLAGS_COMMUNITY = 2;
+ const PAGE_FLAGS_FREELOVE = 3;
+ const PAGE_FLAGS_BLOG = 4;
+ const PAGE_FLAGS_PRVGROUP = 5;
+ /**
+ * @}
+ */
+
+ /**
+ * Account types
+ *
+ * ACCOUNT_TYPE_PERSON - the account belongs to a person
+ * Associated page types: PAGE_FLAGS_NORMAL, PAGE_FLAGS_SOAPBOX, PAGE_FLAGS_FREELOVE
+ *
+ * ACCOUNT_TYPE_ORGANISATION - the account belongs to an organisation
+ * Associated page type: PAGE_FLAGS_SOAPBOX
+ *
+ * ACCOUNT_TYPE_NEWS - the account is a news reflector
+ * Associated page type: PAGE_FLAGS_SOAPBOX
+ *
+ * ACCOUNT_TYPE_COMMUNITY - the account is community forum
+ * Associated page types: PAGE_COMMUNITY, PAGE_FLAGS_PRVGROUP
+ *
+ * ACCOUNT_TYPE_RELAY - the account is a relay
+ * This will only be assigned to contacts, not to user accounts
+ * @{
+ */
+ const ACCOUNT_TYPE_PERSON = 0;
+ const ACCOUNT_TYPE_ORGANISATION = 1;
+ const ACCOUNT_TYPE_NEWS = 2;
+ const ACCOUNT_TYPE_COMMUNITY = 3;
+ const ACCOUNT_TYPE_RELAY = 4;
+ /**
+ * @}
+ */
+
/**
* Returns true if a user record exists with the provided id
*
* @param integer $uid
* @return boolean
+ * @throws Exception
*/
public static function exists($uid)
{
/**
* @param integer $uid
+ * @param array $fields
* @return array|boolean User record if it exists, false otherwise
+ * @throws Exception
+ */
+ public static function getById($uid, array $fields = [])
+ {
+ return DBA::selectFirst('user', $fields, ['uid' => $uid]);
+ }
+
+ /**
+ * @param string $nickname
+ * @param array $fields
+ * @return array|boolean User record if it exists, false otherwise
+ * @throws Exception
*/
- public static function getById($uid)
+ public static function getByNickname($nickname, array $fields = [])
{
- return DBA::selectFirst('user', [], ['uid' => $uid]);
+ return DBA::selectFirst('user', $fields, ['nickname' => $nickname]);
}
/**
* @param string $url
*
* @return integer user id
+ * @throws Exception
*/
public static function getIdForURL($url)
{
*
* @param int $uid
* @return boolean|array
+ * @throws Exception
*/
public static function getOwnerDataById($uid) {
$r = DBA::fetchFirst("SELECT
`user`.`spubkey`,
`user`.`page-flags`,
`user`.`account-type`,
- `user`.`prvnets`
+ `user`.`prvnets`,
+ `user`.`account_removed`
FROM `contact`
INNER JOIN `user`
ON `user`.`uid` = `contact`.`uid`
*
* @param int $nick
* @return boolean|array
+ * @throws Exception
*/
public static function getOwnerDataByNick($nick)
{
* @param string $network network name
*
* @return int group id
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function getDefaultGroup($uid, $network = '')
{
if (strpos($user['password'], '$') === false) {
//Legacy hash that has not been replaced by a new hash yet
if (self::hashPasswordLegacy($password) === $user['password']) {
- self::updatePassword($user['uid'], $password);
+ self::updatePasswordHashed($user['uid'], self::hashPassword($password));
return $user['uid'];
}
//Legacy hash that has been double-hashed and not replaced by a new hash yet
//Warning: `legacy_password` is not necessary in sync with the content of `password`
if (password_verify(self::hashPasswordLegacy($password), $user['password'])) {
- self::updatePassword($user['uid'], $password);
+ self::updatePasswordHashed($user['uid'], self::hashPassword($password));
return $user['uid'];
}
} elseif (password_verify($password, $user['password'])) {
//New password hash
if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) {
- self::updatePassword($user['uid'], $password);
+ self::updatePasswordHashed($user['uid'], self::hashPassword($password));
}
return $user['uid'];
*/
public static function generateNewPassword()
{
- return Strings::getRandomName(6) . mt_rand(100, 9999);
+ return ucfirst(Strings::getRandomName(8)) . mt_rand(1000, 9999);
}
/**
*
* @param string $password
* @return string
+ * @throws Exception
*/
public static function hashPassword($password)
{
* @param int $uid
* @param string $password
* @return bool
+ * @throws Exception
*/
public static function updatePassword($uid, $password)
{
+ $password = trim($password);
+
+ if (empty($password)) {
+ throw new Exception(L10n::t('Empty passwords are not allowed.'));
+ }
+
+ if (!Config::get('system', 'disable_password_exposed', false) && self::isPasswordExposed($password)) {
+ throw new Exception(L10n::t('The new password has been exposed in a public data dump, please choose another.'));
+ }
+
+ $allowed_characters = '!"#$%&\'()*+,-./;<=>?@[\]^_`{|}~';
+
+ if (!preg_match('/^[a-z0-9' . preg_quote($allowed_characters, '/') . ']+$/i', $password)) {
+ throw new Exception(L10n::t('The password can\'t contain accentuated letters, white spaces or colons (:)'));
+ }
+
return self::updatePasswordHashed($uid, self::hashPassword($password));
}
* @param int $uid
* @param string $pasword_hashed
* @return bool
+ * @throws Exception
*/
private static function updatePasswordHashed($uid, $pasword_hashed)
{
*
* @param string $nickname The nickname that should be checked
* @return boolean True is the nickname is blocked on the node
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function isNicknameBlocked($nickname)
{
* - Create self-contact
* - Create profile image
*
- * @param array $data
- * @return string
- * @throw Exception
+ * @param array $data
+ * @return array
+ * @throws \ErrorException
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+ * @throws \ImagickException
+ * @throws Exception
*/
public static function create(array $data)
{
- $a = get_app();
+ $a = \get_app();
$return = ['user' => null, 'password' => ''];
$using_invites = Config::get('system', 'invitation_only');
- $num_invites = Config::get('system', 'number_invites');
$invite_id = !empty($data['invite_id']) ? Strings::escapeTags(trim($data['invite_id'])) : '';
$username = !empty($data['username']) ? Strings::escapeTags(trim($data['username'])) : '';
$openid_url = '';
}
- $err = '';
-
// collapse multiple spaces in name
$username = preg_replace('/ +/', ' ', $username);
}
if (!$photo_failure) {
- DBA::update('photo', ['profile' => 1], ['resource-id' => $hash]);
+ Photo::update(['profile' => 1], ['resource-id' => $hash]);
}
}
}
- Addon::callHooks('register_account', $uid);
+ Hook::callAll('register_account', $uid);
$return['user'] = $user;
return $return;
* @param string $siteurl
* @param string $password Plaintext password
* @return NULL|boolean from notification() and email() inherited
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function sendRegisterPendingEmail($user, $sitename, $siteurl, $password)
{
* @param string $siteurl
* @param string $password Plaintext password
* @return NULL|boolean from notification() and email() inherited
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function sendRegisterOpenEmail($user, $sitename, $siteurl, $password)
{
/**
* @param object $uid user to remove
- * @return void
+ * @return bool
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public static function remove($uid)
{
return false;
}
- $a = get_app();
-
Logger::log('Removing user: ' . $uid);
$user = DBA::selectFirst('user', [], ['uid' => $uid]);
Worker::add(PRIORITY_LOW, 'Directory', $self['url']);
// Remove the user relevant data
- Worker::add(PRIORITY_LOW, 'RemoveUser', $uid);
+ Worker::add(PRIORITY_NEGLIGIBLE, 'RemoveUser', $uid);
return true;
}
* @return array All identities for this user
*
* Example for a return:
- * [
- * [
- * 'uid' => 1,
- * 'username' => 'maxmuster',
- * 'nickname' => 'Max Mustermann'
- * ],
- * [
- * 'uid' => 2,
- * 'username' => 'johndoe',
- * 'nickname' => 'John Doe'
- * ]
- * ]
+ * [
+ * [
+ * 'uid' => 1,
+ * 'username' => 'maxmuster',
+ * 'nickname' => 'Max Mustermann'
+ * ],
+ * [
+ * 'uid' => 2,
+ * 'username' => 'johndoe',
+ * 'nickname' => 'John Doe'
+ * ]
+ * ]
+ * @throws Exception
*/
public static function identities($uid)
{
return $identities;
}
+
+ /**
+ * Returns statistical information about the current users of this node
+ *
+ * @return array
+ *
+ * @throws Exception
+ */
+ public static function getStatistics()
+ {
+ $statistics = [
+ 'total_users' => 0,
+ 'active_users_halfyear' => 0,
+ 'active_users_monthly' => 0,
+ ];
+
+ $userStmt = DBA::p("SELECT `user`.`uid`, `user`.`login_date`, `contact`.`last-item`
+ FROM `user`
+ INNER JOIN `profile` ON `profile`.`uid` = `user`.`uid` AND `profile`.`is-default`
+ INNER JOIN `contact` ON `contact`.`uid` = `user`.`uid` AND `contact`.`self`
+ WHERE (`profile`.`publish` OR `profile`.`net-publish`) AND `user`.`verified`
+ AND NOT `user`.`blocked` AND NOT `user`.`account_removed`
+ AND NOT `user`.`account_expired`");
+
+ if (!DBA::isResult($userStmt)) {
+ return $statistics;
+ }
+
+ $halfyear = time() - (180 * 24 * 60 * 60);
+ $month = time() - (30 * 24 * 60 * 60);
+
+ while ($user = DBA::fetch($userStmt)) {
+ $statistics['total_users']++;
+
+ if ((strtotime($user['login_date']) > $halfyear) ||
+ (strtotime($user['last-item']) > $halfyear)) {
+ $statistics['active_users_halfyear']++;
+ }
+
+ if ((strtotime($user['login_date']) > $month) ||
+ (strtotime($user['last-item']) > $month)) {
+ $statistics['active_users_monthly']++;
+ }
+ }
+
+ return $statistics;
+ }
}