From 060cab5506b0fcdd1956690a8832a74ef7269c4d Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 22 Aug 2019 14:32:55 +0200 Subject: [PATCH] Started changes for SHA-256 with MD5 compatibility --- src/misc/utilities.php | 10 +- src/objects/password.php | 28 +- src/xmlrpc/accounts.php | 766 ++-------------------------------- src/xmlrpc/accounts_email.php | 389 +++++++++++++++++ src/xmlrpc/accounts_phone.php | 420 +++++++++++++++++++ src/xmlrpc/compatibility.php | 4 +- src/xmlrpc/passwords.php | 215 ++++++++++ src/xmlrpc/provisioning.php | 2 +- src/xmlrpc/xmlrpc.php | 7 +- 9 files changed, 1095 insertions(+), 746 deletions(-) create mode 100644 src/xmlrpc/accounts_email.php create mode 100644 src/xmlrpc/accounts_phone.php create mode 100644 src/xmlrpc/passwords.php diff --git a/src/misc/utilities.php b/src/misc/utilities.php index 3156204..3fa6c41 100644 --- a/src/misc/utilities.php +++ b/src/misc/utilities.php @@ -29,6 +29,10 @@ if (SMS_API_ENABLED) { include_once __DIR__ . '/sms.php'; } +define('CLEAR', 'clrtxt'); +define('MD5', 'MD5'); +define('SHA256', 'SHA-256'); + function startswith($hay, $needle) { return substr($hay, 0, strlen($needle)) === $needle; } @@ -72,7 +76,7 @@ function get_algo($algo) { Logger::getInstance()->warning("Algo parameter wasn't found, assume " . DEFAULT_ALGORITHM); return DEFAULT_ALGORITHM; } - if ($algo == "MD5" || $algo == "SHA-256" || $algo == "clrtxt") { + if ($algo == MD5 || $algo == SHA256 || $algo == CLEAR) { return $algo; } Logger::getInstance()->error("Algo " . $algo . " is not supported"); @@ -101,8 +105,8 @@ function get_lang($param) { function hash_password($user, $password, $domain, $algo) { $hashed_password = $password; - if ($algo == "" || $algo == "MD5") $hashed_password = hash("md5", $user . ":" . $domain . ":" . $password); - else if ($algo == "SHA-256") $hashed_password = hash("sha256", $user . ":" . $domain . ":" . $password); + if ($algo == "" || $algo == MD5) $hashed_password = hash("md5", $user . ":" . $domain . ":" . $password); + else if ($algo == SHA256) $hashed_password = hash("sha256", $user . ":" . $domain . ":" . $password); else Logger::getInstance()->error("Algorithm not supported: " . $algo); return $hashed_password; } diff --git a/src/objects/password.php b/src/objects/password.php index 9b2b869..2e25a93 100644 --- a/src/objects/password.php +++ b/src/objects/password.php @@ -178,13 +178,33 @@ class Password { } function getOne() { - $query = "SELECT id, password, algorithm FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id = ? AND algorithm = ? LIMIT 0,1"; + $query = "SELECT id, password, algorithm FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id = ?"; - $stmt = $this->conn->prepare($query); $this->account_id = htmlspecialchars(strip_tags($this->account_id)); - $this->algorithm = htmlspecialchars(strip_tags($this->algorithm)); + if (!empty($this->algorithm)) { + $query = $query . " AND algorithm = ?"; + $this->algorithm = htmlspecialchars(strip_tags($this->algorithm)); + if (!empty($this->password)) { + $query = $query . " AND password = ?"; + $this->password = htmlspecialchars(strip_tags($this->password)); + } + } else if (!empty($this->password)) { + $query = $query . " AND password = ?"; + $this->password = htmlspecialchars(strip_tags($this->password)); + } + + $query = $query . " LIMIT 0,1"; + $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + $stmt = $this->conn->prepare($query); $stmt->bindParam(1, $this->account_id); - $stmt->bindParam(2, $this->algorithm); + if (!empty($this->algorithm)) { + $stmt->bindParam(1, $this->algorithm); + if (!empty($this->password)) { + $stmt->bindParam(2, $this->password); + } + } else if (!empty($this->password)) { + $stmt->bindParam(1, $this->password); + } Logger::getInstance()->debug("GetOne " . (string)$this); if ($stmt->execute()) { diff --git a/src/xmlrpc/accounts.php b/src/xmlrpc/accounts.php index 4bbe940..0bf503c 100644 --- a/src/xmlrpc/accounts.php +++ b/src/xmlrpc/accounts.php @@ -27,6 +27,9 @@ include_once __DIR__ . '/../objects/user_info.php'; include_once __DIR__ . '/../misc/utilities.php'; +include_once __DIR__ . '/accounts_email.php'; +include_once __DIR__ . '/accounts_phone.php'; + include_once __DIR__ . '/results_values.php'; // args = [user, pwd, [domain], [algo]] @@ -66,7 +69,7 @@ function xmlrpc_get_confirmation_key($method, $args) { return PASSWORD_NOT_FOUND; } - if ($algo == "clrtxt") { + if ($algo == CLEAR) { $hashed_password = $pwd; } else { $hashed_password = hash_password($user, $pwd, $domain, $algo); @@ -118,7 +121,7 @@ function xmlrpc_delete_account($method, $args) { return PASSWORD_NOT_FOUND; } - if ($algo == "clrtxt") { + if ($algo == CLEAR) { $hashed_password = $pwd; } else { $hashed_password = hash_password($user, $pwd, $domain, $algo); @@ -138,6 +141,8 @@ function xmlrpc_delete_account($method, $args) { return OK; } +/* ************************************************************************************************* */ + // args = [username, [domain]] function xmlrpc_is_account_used($method, $args) { $user = $args[0]; @@ -191,481 +196,6 @@ function xmlrpc_is_account_activated($method, $args) { return NOK; } -// args = [phone, [domain]] -function xmlrpc_is_phone_number_used($method, $args) { - $phone = $args[0]; - $domain = get_domain($args[1]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_is_phone_number_used(" . $phone . ", " . $domain . ")"); - - if (!check_parameter($phone, "phone")) { - return MISSING_PHONE_PARAM; - } else if (!startswith($phone, "+")) { - return PHONE_NOT_E164; - } - - $database = new Database(); - $db = $database->getConnection(); - - $alias = new Alias($db); - $alias->alias = $phone; - $alias->domain = $domain; - - if ($alias->getOne()) { - return OK_ALIAS; - } - - $account = new Account($db); - $account->username = $phone; - $account->domain = $domain; - - if ($account->getOne()) { - return OK_ACCOUNT; - } - - return NOK; -} - -// args = [phone, username, key, [domain], [algo]] -function xmlrpc_activate_phone_account($method, $args) { - $phone = $args[0]; - $user = $args[1]; - $key = $args[2]; - $domain = get_domain($args[3]); - $algo = get_algo($args[4]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_activate_phone_account(" . $user . ", " . $domain . ", " . $phone . ", " . $key . ", " . $algo . ")"); - - if (!check_parameter($phone, "phone")) { - return MISSING_PHONE_PARAM; - } else if (!check_parameter($user)) { - return MISSING_USERNAME_PARAM; - } else if (!startswith($phone, "+")) { - Logger::getInstance()->error("Phone doesn't start by +"); - return PHONE_NOT_E164; - } else if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $user; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - - if (!is_key_matching($key, $account)) { - return KEY_DOESNT_MATCH; - } - - // If this is a recovery, account is already activated, don't go through the following again - if (!is_activated($account->activated)) { - $expiration = NULL; - $account->activated = "1"; - $account->update(); - - $alias = new Alias($db); - $alias->account_id = $account->id; - $alias->alias = $phone; - $alias->domain = $account->domain; - $alias->create(); - - if (USE_IN_APP_PURCHASES) { - $expiration = get_trial_expiration_date(); - //db_inapp_add_account($user, $domain, $expiration); - //TODO - } - - if (CUSTOM_HOOKS) { - hook_on_account_activated($account); - } - } - - $password = new Password($db); - $password->account_id = $account->id; - $password->algorithm = $algo; - - if ($password->getOne()) { - return $password->password; - } - - return PASSWORD_NOT_FOUND; -} - -// args = [phone, [username], [password], useragent, [domain], [lang], [algo]] -function xmlrpc_create_phone_account($method, $args) { - $phone = $args[0]; - $user = $args[1]; - $hashed_password = $args[2]; - $user_agent = $args[3]; - $domain = get_domain($args[4]); - $lang = get_lang($args[5]); - $algo = get_algo($args[6]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_create_phone_account(" . $user . ", " . $domain . ", " . $phone . ", " . $lang . ", " . $algo . ")"); - - if (!check_parameter($phone, "phone")) { - return MISSING_PHONE_PARAM; - } else if (!startswith($phone, "+")) { - mylog("[ERROR] Phone doesn't start by +"); - return PHONE_NOT_E164; - } else if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - if (!check_parameter($user)) { - $user = $phone; - } - - $recover_params = array( - 0 => $phone, - 1 => $domain, - 2 => $lang, - ); - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $user; - $account->domain = $domain; - - $alias = new Alias($db); - $alias->alias = $phone; - $alias->domain = $domain; - - if ($account->getOne()) { - if (RECOVER_ACCOUNT_IF_EXISTS) { - $recovered_user = xmlrpc_recover_phone_account($method, $recover_params); - if ($recovered_user == $user) { - return OK; - } - - return ACCOUNT_RECOVERY_IMPOSSIBLE; - } - - return USERNAME_TAKEN; - } else if ($alias->getOne()) { - if (RECOVER_ACCOUNT_IF_EXISTS) { - $recovered_user = xmlrpc_recover_phone_account($method, $recover_params); - if ($recovered_user == $user) { - return OK; - } - - return ACCOUNT_RECOVERY_IMPOSSIBLE; - } - - return PHONE_TAKEN; - } - - $pwd = $hashed_password; - if (!check_parameter($hashed_password, "hashed password")) { - $pwd = generate_password(); - $hashed_password = hash_password($user, $pwd, $domain, $algo); - } - - $account->confirmation_key = generate_4_digits_code(); - $account->user_agent = $user_agent; - $account->ip_address = getIp(); - $account->activated = AUTO_ACTIVATE_ACCOUNT ? "1" : "0"; - $account->create(); - - $password = new Password($db); - $password->account_id = $account->id; - $password->password = $hashed_password; - $password->algorithm = $algo; - $password->create(); - - if ($user != $phone) { - $alias->account_id = $account->id; - $alias->create(); - } - - if (CUSTOM_HOOKS) { - hook_on_account_created($account); - } - - if (SEND_ACTIVATION_SMS) { - if (!SMS_API_ENABLED) { - // This is a hack to allow testing without sending SMS - return OK; - } - $ok = send_sms($phone, $account->confirmation_key, $lang); - return $ok; - } else if (AUTO_ACTIVATE_ACCOUNT) { - if (USE_IN_APP_PURCHASES) { - //TODO - /*$expiration = get_trial_expiration_date(); - db_inapp_add_account($user, $domain, $expiration);*/ - } - } - - return OK; -} - -// args = [username, key, [domain], [algo]] -function xmlrpc_activate_email_account($method, $args) { - $user = $args[0]; - $key = $args[1]; - $domain = get_domain($args[2]); - $algo = get_algo($args[3]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_activate_account(" . $user . ", " . $domain . ", " . $key . ", " . $algo . ")"); - - if (!check_parameter($user)) { - return MISSING_USERNAME_PARAM; - } else if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $user; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } else if ($account->activated != "0") { - return ACCOUNT_ALREADY_ACTIVATED; - } - - if (!is_key_matching($key, $account)) { - return KEY_DOESNT_MATCH; - } - - $account->activated = "1"; - $account->update(); - - $expiration = NULL; - // TODO - /*if (USE_IN_APP_PURCHASES) { - $expiration = get_trial_expiration_date(); - db_inapp_add_account($user, $domain, $expiration); - }*/ - - if (CUSTOM_HOOKS) { - hook_on_account_activated($account); - } - - $password = new Password($db); - $password->account_id = $account->id; - $password->algorithm = $algo; - - if ($password->getOne()) { - return $password->password; - } - - return PASSWORD_NOT_FOUND; -} - -// args = [username, email, [hash], useragent, [domain], [algo]] -function xmlrpc_create_email_account($method, $args) { - $user = $args[0]; - $email = $args[1]; - $hashed_password = $args[2]; - $user_agent = $args[3]; - $domain = get_domain($args[4]); - $algo = get_algo($args[5]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_create_email_account(" . $user . ", " . $domain . ", " . $email . ", " . $algo . ")"); - - if (!check_parameter($user)) { - return MISSING_USERNAME_PARAM; - } else if (!check_parameter($email, "email")) { - return MISSING_EMAIL_PARAM; - } else if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $user; - $account->domain = $domain; - - if ($account->getOne()) { - return USERNAME_TAKEN; - } - - if (!ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) { - $email_account = new Account($db); - $email_account->email = $email; - if ($email_account->getOne()) { - return EMAIL_TAKEN; - } - } - - if (GENERATE_PASSWORD_ENABLED) { - $hashed_password = hash_password($user, generate_password(), $domain, $algo); - } - - $account->confirmation_key = uniqid(); - $account->email = $email; - $account->user_agent = $user_agent; - $account->ip_address = getIp(); - $account->activated = AUTO_ACTIVATE_ACCOUNT ? "1" : "0"; - $account->create(); - - $password = new Password($db); - $password->account_id = $account->id; - $password->password = $hashed_password; - $password->algorithm = $algo; - $password->create(); - - if (CUSTOM_HOOKS) { - hook_on_account_created($account); - } - - if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { - send_email_with_activation_link($email, $account->confirmation_key, $account->username); - } else if (AUTO_ACTIVATE_ACCOUNT) { - //TODO - /*if (USE_IN_APP_PURCHASES) { - $expiration = get_trial_expiration_date(); - db_inapp_add_account($user, $domain, $expiration); - }*/ - } - - return OK; -} - -// args = [username, [domain]] -function xmlrpc_get_phone_number_for_account($method, $args) { - $user = $args[0]; - $domain = get_domain($args[1]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_get_phone_number_for_account(" . $user . ")"); - - if (!check_parameter($user)) { - return MISSING_USERNAME_PARAM; - } - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $user; - $account->domain = $domain; - - if (!$account->getOne()) { - $alias = new Alias($db); - $alias->alias = $user; - $alias->domain = $domain; - - if ($alias->getOne()) { - return $user; - } - - return ACCOUNT_NOT_FOUND; - } - - $phone = $account->alias; - if ($phone == NULL) { - return ALIAS_NOT_FOUND; - } - - if (RECOVER_ACCOUNT_IF_EXISTS) { - return ACCOUNT_NOT_FOUND; - } - - return $phone; -} - -// args = [phone, [domain], [lang]] -function xmlrpc_recover_phone_account($method, $args) { - - // Is this function overloaded - if (XMLRPC_RECOVER_PHONE_ACCOUNT_OVERLOAD === TRUE) { - return xmlrpc_recover_phone_account_overload($method, $args); - } - - $phone = $args[0]; - $domain = get_domain($args[1]); - $lang = get_lang($args[2]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_recover_phone_account(" . $phone . ", " . $domain . ", " . $lang . ")"); - - if (!check_parameter($phone, "phone")) { - return MISSING_PHONE_PARAM; - } else if (!startswith($phone, "+")) { - return PHONE_NOT_E164; - } - - $database = new Database(); - $db = $database->getConnection(); - - $account = new Account($db); - $account->username = $phone; - $account->domain = $domain; - - $alias = new Alias($db); - $alias->alias = $phone; - $alias->domain = $domain; - - if (!$account->getOne()) { - if ($alias->getOne()) { - $account->id = $alias->account_id; - // This time the search will be done on the id instead of couple username / domain - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - } else { - return ACCOUNT_NOT_FOUND; - } - } - - if (SEND_ACTIVATION_SMS) { - $account->confirmation_key = generate_4_digits_code(); - $account->update(); - - if (!SMS_API_ENABLED) { - // This is a hack to allow testing without sending SMS - return $account->username; - } - $ok = send_sms($phone, $account->confirmation_key, $lang); - if ($ok != OK) { - return $ok; - } - } - - return $account->username; -} - -// args = [username, email, [domain]] -function xmlrpc_recover_email_account($method, $args) { - $username = $args[0]; - $email = $args[1]; - $domain = get_domain($args[2]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_recover_email_account(" . $username . ", " . $email . ", " . $domain . ")"); - - $database = new Database(); - $db = $database->getConnection(); - - $account = new Account($db); - $account->username = $username; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - - if (strcasecmp($email, $account->email) != 0) { // Email case insensitive compare - return EMAIL_DOESNT_MATCH; - } - - $account->confirmation_key = uniqid(); - $account->update(); - - if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { - send_email_with_recover_key($email, $account->confirmation_key); - } - - return OK; -} - // args = [username, key, [domain], [algo]] function xmlrpc_recover_account_from_confirmation_key($method, $args) { $username = $args[0]; @@ -694,251 +224,33 @@ function xmlrpc_recover_account_from_confirmation_key($method, $args) { $password->algorithm = $algo; if ($password->getOne()) { - return $password->password; + $result = array( + "password" => $password->password, + "algorithm" => $password->algorithm + ); + return $result; + } + + if ($algo == SHA256) { + // When trying to log in with a phone account on an app that only supports SHA-256, create a new password for it if it doesn't exists + // This won't prevent already logged in users with MD5 password to use their account + $pwd = generate_password(); + $sha256_password = new Password($db); + $sha256_password->account_id = $account->id; + $sha256_password->password = hash_password($account->username, $pwd, $domain, SHA256); + $sha256_password->algorithm = SHA256; + $sha256_password->create(); + + $result = array( + "password" => $sha256_password->password, + "algorithm" => $sha256_password->algorithm + ); + return $result; } return PASSWORD_NOT_FOUND; } -function update_password($username, $domain, $hashed_old_password, $hashed_new_password, $algo) { - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $username; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - - $password = new Password($db); - $password->account_id = $account->id; - $password->algorithm = $algo; - - if (!$password->getOne()) { - return PASSWORD_NOT_FOUND; - } - - $db_hashed_password = $password->password; - if (!password_match($db_hashed_password, $hashed_old_password)) { - return PASSWORD_DOESNT_MATCH; - } - - $password->password = $hashed_new_password; - if ($password->update()) { - Logger::getInstance()->message("Password updated successfully"); - return OK; - } - - return NOK; -} - -// args = [username, old password, new password, [domain], [algo]] -function xmlrpc_update_password($method, $args) { - $username = $args[0]; - $old_password = $args[1]; - $new_password = $args[2]; - $domain = get_domain($args[3]); - $algo = get_algo($algo[4]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_update_password(" . $username . ", " . $domain . ", " . $algo . ")"); - - if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - $old_hash = hash_password($username, $old_password, $domain, $algo); - $new_hash = hash_password($username, $new_password, $domain, $algo); - - return update_password($username, $domain, $old_hash, $new_hash, $algo); -} - -// args = [username, old hash, new hash, [domain], [algo]] -function xmlrpc_update_hash($method, $args) { - $ususernameer = $args[0]; - $hashed_old_password = $args[1]; - $hashed_new_password = $args[2]; - $domain = get_domain($args[3]); - $algo = get_algo($args[4]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_update_hash(" . $username . ", " . $domain . ", " . $algo . ")"); - - if (!check_parameter($username)) { - return MISSING_USERNAME_PARAM; - } else if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - return update_password($username, $domain, $hashed_old_password, $hashed_new_password, $algo); -} - -// args = [username, password, new email, [domain], [algo]] -function xmlrpc_update_email($method, $args) { - $user = $args[0]; - $pwd = $args[1]; - $new_email = $args[2]; - $domain = get_domain($args[3]); - $algo = get_algo($args[4]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_update_email(" . $user . ", " . $domain . ", " . $new_email . ", " . $algo . ")"); - - if (!check_parameter($user)) { - return MISSING_USERNAME_PARAM; - } else if ($algo == NULL) { - return ALGO_NOT_SUPPORTED; - } - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $user; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - - $password = new Password($db); - $password->account_id = $account->id; - $password->algorithm = $algo; - - if (!$password->getOne()) { - return PASSWORD_NOT_FOUND; - } - - $hashed_old_password = hash_password($user, $pwd, $domain, $algo); - if (!password_match($password->password, $hashed_old_password)) { - return PASSWORD_DOESNT_MATCH; - } - - if ($account->email == $new_email) { - Logger::getInstance()->warning("New email same as previous one"); - return EMAIL_UNCHANGED; - } - - if (!ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) { - $email_account = new Account($db); - $email_account->email = $email; - if ($email_account->getOne()) { - return EMAIL_TAKEN; - } - } - - $account->email = $new_email; - if ($account->update()) { - Logger::getInstance()->message("Email updated successfully"); - return OK; - } - - return NOK; -} - -// args = [username, phone, ha1, [domain], [algo]] -function xmlrpc_delete_phone_account($method, $args) { - $username = $args[0]; - $phone = $args[1]; - $ha1 = $args[2]; - $domain = get_domain($args[3]); - $algo = get_algo($args[4]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_delete_phone_account(" . $username . ", " . $phone . ", " . $domain . ", " . $algo . ")"); - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $username; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - - if ($phone != $username && $phone != $account->alias) { - return ALIAS_DOESNT_MATCH; - } - - $password = new Password($db); - $password->account_id = $account->id; - $password->algorithm = $algo; - - if (!$password->getOne()) { - return PASSWORD_NOT_FOUND; - } - - if ($ha1 != $password->password) { - return PASSWORD_DOESNT_MATCH; - } - - if ($account->delete()) { - if ($password->delete()) { - $alias = new Alias($db); - $alias->account_id = $account->id; - $alias->delete(); - - $userinfo = new UserInfo($db); - $userinfo->account_id = $account->id; - $userinfo->delete(); - - return OK; - } - } - - return NOK; -} - -// args = [username, email, ha1, [domain], [algo]] -function xmlrpc_delete_email_account($method, $args) { - $username = $args[0]; - $email = $args[1]; - $ha1 = $args[2]; - $domain = get_domain($args[3]); - $algo = get_algo($args[4]); - - Logger::getInstance()->message("[XMLRPC] xmlrpc_delete_email_account(" . $username . ", " . $email . ", " . $domain . ", " . $algo . ")"); - - $database = new Database(); - $db = $database->getConnection(); - $account = new Account($db); - $account->username = $username; - $account->domain = $domain; - - if (!$account->getOne()) { - return ACCOUNT_NOT_FOUND; - } - - if ($email != $account->email) { - return EMAIL_DOESNT_MATCH; - } - - $password = new Password($db); - $password->account_id = $account->id; - $password->algorithm = $algo; - - if (!$password->getOne()) { - return PASSWORD_NOT_FOUND; - } - - if ($ha1 != $password->password) { - return PASSWORD_DOESNT_MATCH; - } - - if ($account->delete()) { - if ($password->delete()) { - $alias = new Alias($db); - $alias->account_id = $account->id; - $alias->delete(); - - $userinfo = new UserInfo($db); - $userinfo->account_id = $account->id; - $userinfo->delete(); - - return OK; - } - } - - return NOK; -} - // args = [] function xmlrpc_get_accounts_count($method, $args) { Logger::getInstance()->message("[XMLRPC] xmlrpc_get_accounts_count()"); @@ -958,26 +270,12 @@ function xmlrpc_accounts_register_methods($server) { xmlrpc_server_register_method($server, 'is_account_used', 'xmlrpc_is_account_used');// args = [username, [domain]], return OK or NOK xmlrpc_server_register_method($server, 'is_account_activated', 'xmlrpc_is_account_activated');// args = [username, [domain]], return OK or NOK - xmlrpc_server_register_method($server, 'is_phone_number_used', 'xmlrpc_is_phone_number_used');// args = [phone], return OK_ACCOUNT, OK_ALIAS or NOK - xmlrpc_server_register_method($server, 'get_phone_number_for_account', 'xmlrpc_get_phone_number_for_account');// args = [username, [domain]], return a phone number or an error - - xmlrpc_server_register_method($server, 'activate_phone_account', 'xmlrpc_activate_phone_account');// args = [phone, username, key, [domain], [algo]], return ha1_password - xmlrpc_server_register_method($server, 'create_phone_account', 'xmlrpc_create_phone_account');// args = [phone, [username], [password], useragent, [domain], [lang], [algo]], return OK - xmlrpc_server_register_method($server, 'activate_email_account', 'xmlrpc_activate_email_account');// args = [username, key, [domain], [algo]], return ha1_password - xmlrpc_server_register_method($server, 'create_email_account', 'xmlrpc_create_email_account');// args = [username, email, [hash], useragent, [domain], [algo]], return OK - - xmlrpc_server_register_method($server, 'recover_phone_account', 'xmlrpc_recover_phone_account');// args = [phone, [domain], [lang]], return username - xmlrpc_server_register_method($server, 'recover_email_account', 'xmlrpc_recover_email_account');// args = [username, email, [domain]], return OK xmlrpc_server_register_method($server, 'recover_account_from_confirmation_key', 'xmlrpc_recover_account_from_confirmation_key');// args = [username, key, [domain], [algo]] - xmlrpc_server_register_method($server, 'delete_phone_account', 'xmlrpc_delete_phone_account');// args = [username, phone, ha1, [domain], [algo]] - xmlrpc_server_register_method($server, 'delete_email_account', 'xmlrpc_delete_email_account');// args = [username, email, ha1, [domain], [algo]] - - xmlrpc_server_register_method($server, 'update_password', 'xmlrpc_update_password');// args = [username, old password, new password, [domain], [algo]], return OK - xmlrpc_server_register_method($server, 'update_hash', 'xmlrpc_update_hash');// args = [username, old hash, new hash, [domain], [algo]], return OK - xmlrpc_server_register_method($server, 'update_email', 'xmlrpc_update_email');// args = [username, password, new email, [domain], [algo]], return OK - xmlrpc_server_register_method($server, 'get_accounts_count', 'xmlrpc_get_accounts_count');//args = [] + + xmlrpc_accounts_email_register_methods($server); + xmlrpc_accounts_phone_register_methods($server); } ?> diff --git a/src/xmlrpc/accounts_email.php b/src/xmlrpc/accounts_email.php new file mode 100644 index 0000000..857ae69 --- /dev/null +++ b/src/xmlrpc/accounts_email.php @@ -0,0 +1,389 @@ +. +*/ + +include_once __DIR__ . '/../database/database.php'; + +include_once __DIR__ . '/../objects/account.php'; +include_once __DIR__ . '/../objects/password.php'; +include_once __DIR__ . '/../objects/alias.php'; +include_once __DIR__ . '/../objects/user_info.php'; + +include_once __DIR__ . '/../misc/utilities.php'; + +include_once __DIR__ . '/results_values.php'; + +// args = [username, email, [hash], useragent, [domain], [algo]] +function xmlrpc_create_email_account($method, $args) { + $user = $args[0]; + $email = $args[1]; + $hashed_password = $args[2]; + $user_agent = $args[3]; + $domain = get_domain($args[4]); + $algo = get_algo($args[5]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_create_email_account(" . $user . ", " . $domain . ", " . $email . ", " . $algo . ")"); + + if (!check_parameter($user)) { + return MISSING_USERNAME_PARAM; + } else if (!check_parameter($email, "email")) { + return MISSING_EMAIL_PARAM; + } else if ($algo == NULL) { + return ALGO_NOT_SUPPORTED; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if ($account->getOne()) { + return USERNAME_TAKEN; + } + + if (!ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) { + $email_account = new Account($db); + $email_account->email = $email; + if ($email_account->getOne()) { + return EMAIL_TAKEN; + } + } + + if (GENERATE_PASSWORD_ENABLED) { + $hashed_password = hash_password($user, generate_password(), $domain, $algo); + } + + $account->confirmation_key = uniqid(); + $account->email = $email; + $account->user_agent = $user_agent; + $account->ip_address = getIp(); + $account->activated = AUTO_ACTIVATE_ACCOUNT ? "1" : "0"; + $account->create(); + + $password = new Password($db); + $password->account_id = $account->id; + $password->password = $hashed_password; + $password->algorithm = $algo; + $password->create(); + + if (CUSTOM_HOOKS) { + hook_on_account_created($account); + } + + if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { + send_email_with_activation_link($email, $account->confirmation_key, $account->username); + } else if (AUTO_ACTIVATE_ACCOUNT) { + //TODO + /*if (USE_IN_APP_PURCHASES) { + $expiration = get_trial_expiration_date(); + db_inapp_add_account($user, $domain, $expiration); + }*/ + } + + return OK; +} + +// args = [username, email, password, useragent, [domain]], return OK +function xmlrpc_create_email_md5_sha256_account($method, $args) { + $user = $args[0]; + $email = $args[1]; + $pwd = $args[2]; + $user_agent = $args[3]; + $domain = get_domain($args[4]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_create_email_md5_sha256_account(" . $user . ", " . $domain . ", " . $email . ")"); + + if (!check_parameter($user)) { + return MISSING_USERNAME_PARAM; + } else if (!check_parameter($email, "email")) { + return MISSING_EMAIL_PARAM; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if ($account->getOne()) { + return USERNAME_TAKEN; + } + + if (!ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) { + $email_account = new Account($db); + $email_account->email = $email; + if ($email_account->getOne()) { + return EMAIL_TAKEN; + } + } + + if (GENERATE_PASSWORD_ENABLED) { + $pwd = generate_password(); + } + + $account->confirmation_key = uniqid(); + $account->email = $email; + $account->user_agent = $user_agent; + $account->ip_address = getIp(); + $account->activated = AUTO_ACTIVATE_ACCOUNT ? "1" : "0"; + $account->create(); + + $md5_password = new Password($db); + $md5_password->account_id = $account->id; + $md5_password->password = hash_password($user, $pwd, $domain, MD5); + $md5_password->algorithm = MD5; + $md5_password->create(); + + $sha256_password = new Password($db); + $sha256_password->account_id = $account->id; + $sha256_password->password = hash_password($user, $pwd, $domain, SHA256); + $sha256_password->algorithm = SHA256; + $sha256_password->create(); + + if (CUSTOM_HOOKS) { + hook_on_account_created($account); + } + + if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { + send_email_with_activation_link($email, $account->confirmation_key, $account->username); + } else if (AUTO_ACTIVATE_ACCOUNT) { + //TODO + /*if (USE_IN_APP_PURCHASES) { + $expiration = get_trial_expiration_date(); + db_inapp_add_account($user, $domain, $expiration); + }*/ + } + + return OK; +} + +// args = [username, key, [domain], [algo]] +function xmlrpc_activate_email_account($method, $args) { + $user = $args[0]; + $key = $args[1]; + $domain = get_domain($args[2]); + $algo = get_algo($args[3]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_activate_account(" . $user . ", " . $domain . ", " . $key . ", " . $algo . ")"); + + if (!check_parameter($user)) { + return MISSING_USERNAME_PARAM; + } else if ($algo == NULL) { + return ALGO_NOT_SUPPORTED; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } else if ($account->activated != "0") { + return ACCOUNT_ALREADY_ACTIVATED; + } + + if (!is_key_matching($key, $account)) { + return KEY_DOESNT_MATCH; + } + + $account->activated = "1"; + $account->update(); + + $expiration = NULL; + // TODO + /*if (USE_IN_APP_PURCHASES) { + $expiration = get_trial_expiration_date(); + db_inapp_add_account($user, $domain, $expiration); + }*/ + + if (CUSTOM_HOOKS) { + hook_on_account_activated($account); + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->algorithm = $algo; + + if ($password->getOne()) { + return $password->password; + } + + return PASSWORD_NOT_FOUND; +} + +// args = [username, email, [domain]] +function xmlrpc_recover_email_account($method, $args) { + $username = $args[0]; + $email = $args[1]; + $domain = get_domain($args[2]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_recover_email_account(" . $username . ", " . $email . ", " . $domain . ")"); + + $database = new Database(); + $db = $database->getConnection(); + + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + if (strcasecmp($email, $account->email) != 0) { // Email case insensitive compare + return EMAIL_DOESNT_MATCH; + } + + $account->confirmation_key = uniqid(); + $account->update(); + + if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { + send_email_with_recover_key($email, $account->confirmation_key); + } + + return OK; +} + +// args = [username, password, new email, [domain], [algo]] +function xmlrpc_update_email($method, $args) { + $user = $args[0]; + $pwd = $args[1]; + $new_email = $args[2]; + $domain = get_domain($args[3]); + $algo = get_algo($args[4]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_update_email(" . $user . ", " . $domain . ", " . $new_email . ", " . $algo . ")"); + + if (!check_parameter($user)) { + return MISSING_USERNAME_PARAM; + } else if ($algo == NULL) { + return ALGO_NOT_SUPPORTED; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->algorithm = $algo; + + if (!$password->getOne()) { + return PASSWORD_NOT_FOUND; + } + + $hashed_old_password = hash_password($user, $pwd, $domain, $algo); + if (!password_match($password->password, $hashed_old_password)) { + return PASSWORD_DOESNT_MATCH; + } + + if ($account->email == $new_email) { + Logger::getInstance()->warning("New email same as previous one"); + return EMAIL_UNCHANGED; + } + + if (!ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) { + $email_account = new Account($db); + $email_account->email = $email; + if ($email_account->getOne()) { + return EMAIL_TAKEN; + } + } + + $account->email = $new_email; + if ($account->update()) { + Logger::getInstance()->message("Email updated successfully"); + return OK; + } + + return NOK; +} + +// args = [username, email, ha1, [domain], [algo]] +function xmlrpc_delete_email_account($method, $args) { + $username = $args[0]; + $email = $args[1]; + $ha1 = $args[2]; + $domain = get_domain($args[3]); + $algo = get_algo($args[4]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_delete_email_account(" . $username . ", " . $email . ", " . $domain . ", " . $algo . ")"); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + if ($email != $account->email) { + return EMAIL_DOESNT_MATCH; + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->algorithm = $algo; + + if (!$password->getOne()) { + return PASSWORD_NOT_FOUND; + } + + if ($ha1 != $password->password) { + return PASSWORD_DOESNT_MATCH; + } + + if ($account->delete()) { + if ($password->delete()) { + $alias = new Alias($db); + $alias->account_id = $account->id; + $alias->delete(); + + $userinfo = new UserInfo($db); + $userinfo->account_id = $account->id; + $userinfo->delete(); + + return OK; + } + } + + return NOK; +} + +function xmlrpc_accounts_email_register_methods($server) { + xmlrpc_server_register_method($server, 'create_email_account', 'xmlrpc_create_email_account');// args = [username, email, [hash], useragent, [domain], [algo]], return OK + xmlrpc_server_register_method($server, 'create_email_md5_sha256_account', 'xmlrpc_create_email_md5_sha256_account');// args = [username, email, password, useragent, [domain]], return OK + xmlrpc_server_register_method($server, 'activate_email_account', 'xmlrpc_activate_email_account');// args = [username, key, [domain], [algo]], return ha1_password + xmlrpc_server_register_method($server, 'recover_email_account', 'xmlrpc_recover_email_account');// args = [username, email, [domain]], return OK + xmlrpc_server_register_method($server, 'update_email', 'xmlrpc_update_email');// args = [username, password, new email, [domain], [algo]], return OK + xmlrpc_server_register_method($server, 'delete_email_account', 'xmlrpc_delete_email_account');// args = [username, email, ha1, [domain], [algo]] +} + +?> \ No newline at end of file diff --git a/src/xmlrpc/accounts_phone.php b/src/xmlrpc/accounts_phone.php new file mode 100644 index 0000000..f462940 --- /dev/null +++ b/src/xmlrpc/accounts_phone.php @@ -0,0 +1,420 @@ +. +*/ + +include_once __DIR__ . '/../database/database.php'; + +include_once __DIR__ . '/../objects/account.php'; +include_once __DIR__ . '/../objects/password.php'; +include_once __DIR__ . '/../objects/alias.php'; +include_once __DIR__ . '/../objects/user_info.php'; + +include_once __DIR__ . '/../misc/utilities.php'; + +include_once __DIR__ . '/results_values.php'; + +// args = [phone, [username], [password], useragent, [domain], [lang], [algo]] +function xmlrpc_create_phone_account($method, $args) { + $phone = $args[0]; + $user = $args[1]; + $hashed_password = $args[2]; + $user_agent = $args[3]; + $domain = get_domain($args[4]); + $lang = get_lang($args[5]); + $algo = get_algo($args[6]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_create_phone_account(" . $user . ", " . $domain . ", " . $phone . ", " . $lang . ", " . $algo . ")"); + + if (!check_parameter($phone, "phone")) { + return MISSING_PHONE_PARAM; + } else if (!startswith($phone, "+")) { + mylog("[ERROR] Phone doesn't start by +"); + return PHONE_NOT_E164; + } else if ($algo == NULL) { + return ALGO_NOT_SUPPORTED; + } + + if (!check_parameter($user)) { + $user = $phone; + } + + $recover_params = array( + 0 => $phone, + 1 => $domain, + 2 => $lang, + ); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + $alias = new Alias($db); + $alias->alias = $phone; + $alias->domain = $domain; + + if ($account->getOne()) { + if (RECOVER_ACCOUNT_IF_EXISTS) { + $recovered_user = xmlrpc_recover_phone_account($method, $recover_params); + if ($recovered_user == $user) { + return OK; + } + + return ACCOUNT_RECOVERY_IMPOSSIBLE; + } + + return USERNAME_TAKEN; + } else if ($alias->getOne()) { + if (RECOVER_ACCOUNT_IF_EXISTS) { + $recovered_user = xmlrpc_recover_phone_account($method, $recover_params); + if ($recovered_user == $user) { + return OK; + } + + return ACCOUNT_RECOVERY_IMPOSSIBLE; + } + + return PHONE_TAKEN; + } + + $pwd = $hashed_password; + if (!check_parameter($hashed_password, "hashed password")) { + $pwd = generate_password(); + $hashed_password = hash_password($user, $pwd, $domain, $algo); + } + + $account->confirmation_key = generate_4_digits_code(); + $account->user_agent = $user_agent; + $account->ip_address = getIp(); + $account->activated = AUTO_ACTIVATE_ACCOUNT ? "1" : "0"; + $account->create(); + + $password = new Password($db); + $password->account_id = $account->id; + $password->password = $hashed_password; + $password->algorithm = $algo; + $password->create(); + + if ($user != $phone) { + $alias->account_id = $account->id; + $alias->create(); + } + + if (CUSTOM_HOOKS) { + hook_on_account_created($account); + } + + if (SEND_ACTIVATION_SMS) { + if (!SMS_API_ENABLED) { + // This is a hack to allow testing without sending SMS + return OK; + } + $ok = send_sms($phone, $account->confirmation_key, $lang); + return $ok; + } else if (AUTO_ACTIVATE_ACCOUNT) { + if (USE_IN_APP_PURCHASES) { + //TODO + /*$expiration = get_trial_expiration_date(); + db_inapp_add_account($user, $domain, $expiration);*/ + } + } + + return OK; +} + +// args = [phone, username, key, [domain], [algo]] +function xmlrpc_activate_phone_account($method, $args) { + $phone = $args[0]; + $user = $args[1]; + $key = $args[2]; + $domain = get_domain($args[3]); + $algo = get_algo($args[4]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_activate_phone_account(" . $user . ", " . $domain . ", " . $phone . ", " . $key . ", " . $algo . ")"); + + if (!check_parameter($phone, "phone")) { + return MISSING_PHONE_PARAM; + } else if (!check_parameter($user)) { + return MISSING_USERNAME_PARAM; + } else if (!startswith($phone, "+")) { + Logger::getInstance()->error("Phone doesn't start by +"); + return PHONE_NOT_E164; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + if (!is_key_matching($key, $account)) { + return KEY_DOESNT_MATCH; + } + + // If this is a recovery, account is already activated, don't go through the following again + if (!is_activated($account->activated)) { + $expiration = NULL; + $account->activated = "1"; + $account->update(); + + $alias = new Alias($db); + $alias->account_id = $account->id; + $alias->alias = $phone; + $alias->domain = $account->domain; + $alias->create(); + + if (USE_IN_APP_PURCHASES) { + $expiration = get_trial_expiration_date(); + //db_inapp_add_account($user, $domain, $expiration); + //TODO + } + + if (CUSTOM_HOOKS) { + hook_on_account_activated($account); + } + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->algorithm = $algo; + + if ($password->getOne()) { + return $password->password; + } + + if ($algo == SHA256) { + // When trying to log in with a phone account on an app that only supports SHA-256, create a new password for it if it doesn't exists + // This won't prevent already logged in users with MD5 password to use their account + $pwd = generate_password(); + $sha256_password = new Password($db); + $sha256_password->account_id = $account->id; + $sha256_password->password = hash_password($account->username, $pwd, $domain, SHA256); + $sha256_password->algorithm = SHA256; + $sha256_password->create(); + + return $sha256_password->password; + } + + return PASSWORD_NOT_FOUND; +} + +// args = [phone, [domain], [lang]] +function xmlrpc_recover_phone_account($method, $args) { + // Is this function overloaded + if (XMLRPC_RECOVER_PHONE_ACCOUNT_OVERLOAD === TRUE) { + return xmlrpc_recover_phone_account_overload($method, $args); + } + + $phone = $args[0]; + $domain = get_domain($args[1]); + $lang = get_lang($args[2]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_recover_phone_account(" . $phone . ", " . $domain . ", " . $lang . ")"); + + if (!check_parameter($phone, "phone")) { + return MISSING_PHONE_PARAM; + } else if (!startswith($phone, "+")) { + return PHONE_NOT_E164; + } + + $database = new Database(); + $db = $database->getConnection(); + + $account = new Account($db); + $account->username = $phone; + $account->domain = $domain; + + $alias = new Alias($db); + $alias->alias = $phone; + $alias->domain = $domain; + + if (!$account->getOne()) { + if ($alias->getOne()) { + $account->id = $alias->account_id; + // This time the search will be done on the id instead of couple username / domain + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + } else { + return ACCOUNT_NOT_FOUND; + } + } + + if (SEND_ACTIVATION_SMS) { + $account->confirmation_key = generate_4_digits_code(); + $account->update(); + + if (!SMS_API_ENABLED) { + // This is a hack to allow testing without sending SMS + return $account->username; + } + $ok = send_sms($phone, $account->confirmation_key, $lang); + if ($ok != OK) { + return $ok; + } + } + + return $account->username; +} + +// args = [username, phone, ha1, [domain], [algo]] +function xmlrpc_delete_phone_account($method, $args) { + $username = $args[0]; + $phone = $args[1]; + $ha1 = $args[2]; + $domain = get_domain($args[3]); + $algo = get_algo($args[4]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_delete_phone_account(" . $username . ", " . $phone . ", " . $domain . ", " . $algo . ")"); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + if ($phone != $username && $phone != $account->alias) { + return ALIAS_DOESNT_MATCH; + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->algorithm = $algo; + + if (!$password->getOne()) { + return PASSWORD_NOT_FOUND; + } + + if ($ha1 != $password->password) { + return PASSWORD_DOESNT_MATCH; + } + + if ($account->delete()) { + if ($password->delete()) { + $alias = new Alias($db); + $alias->account_id = $account->id; + $alias->delete(); + + $userinfo = new UserInfo($db); + $userinfo->account_id = $account->id; + $userinfo->delete(); + + return OK; + } + } + + return NOK; +} + +// args = [phone, [domain]] +function xmlrpc_is_phone_number_used($method, $args) { + $phone = $args[0]; + $domain = get_domain($args[1]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_is_phone_number_used(" . $phone . ", " . $domain . ")"); + + if (!check_parameter($phone, "phone")) { + return MISSING_PHONE_PARAM; + } else if (!startswith($phone, "+")) { + return PHONE_NOT_E164; + } + + $database = new Database(); + $db = $database->getConnection(); + + $alias = new Alias($db); + $alias->alias = $phone; + $alias->domain = $domain; + + if ($alias->getOne()) { + return OK_ALIAS; + } + + $account = new Account($db); + $account->username = $phone; + $account->domain = $domain; + + if ($account->getOne()) { + return OK_ACCOUNT; + } + + return NOK; +} + +// args = [username, [domain]] +function xmlrpc_get_phone_number_for_account($method, $args) { + $user = $args[0]; + $domain = get_domain($args[1]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_get_phone_number_for_account(" . $user . ")"); + + if (!check_parameter($user)) { + return MISSING_USERNAME_PARAM; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $user; + $account->domain = $domain; + + if (!$account->getOne()) { + $alias = new Alias($db); + $alias->alias = $user; + $alias->domain = $domain; + + if ($alias->getOne()) { + return $user; + } + + return ACCOUNT_NOT_FOUND; + } + + $phone = $account->alias; + if ($phone == NULL) { + return ALIAS_NOT_FOUND; + } + + if (RECOVER_ACCOUNT_IF_EXISTS) { + return ACCOUNT_NOT_FOUND; + } + + return $phone; +} + +function xmlrpc_accounts_phone_register_methods($server) { + xmlrpc_server_register_method($server, 'create_phone_account', 'xmlrpc_create_phone_account');// args = [phone, [username], [password], useragent, [domain], [lang], [algo]], return OK + xmlrpc_server_register_method($server, 'activate_phone_account', 'xmlrpc_activate_phone_account');// args = [phone, username, key, [domain], [algo]], return ha1_password + xmlrpc_server_register_method($server, 'recover_phone_account', 'xmlrpc_recover_phone_account');// args = [phone, [domain], [lang]], return username + xmlrpc_server_register_method($server, 'delete_phone_account', 'xmlrpc_delete_phone_account');// args = [username, phone, ha1, [domain], [algo]] + + xmlrpc_server_register_method($server, 'is_phone_number_used', 'xmlrpc_is_phone_number_used');// args = [phone], return OK_ACCOUNT, OK_ALIAS or NOK + xmlrpc_server_register_method($server, 'get_phone_number_for_account', 'xmlrpc_get_phone_number_for_account');// args = [username, [domain]], return a phone number or an error +} + +?> \ No newline at end of file diff --git a/src/xmlrpc/compatibility.php b/src/xmlrpc/compatibility.php index 2f95a9f..bb2725f 100644 --- a/src/xmlrpc/compatibility.php +++ b/src/xmlrpc/compatibility.php @@ -105,11 +105,11 @@ function xmlrpc_compatibility_create_account($method, $args) { } $account->create(); - $crypted_password = hash_password($login, $args[1], $domain, "MD5"); + $crypted_password = hash_password($login, $args[1], $domain, MD5); $password = new Password($db); $password->account_id = $account->id; $password->password = $crypted_password; - $password->algorithm = "MD5"; + $password->algorithm = MD5; $password->create(); if (SEND_ACTIVATION_EMAIL && EMAIL_ENABLED) { diff --git a/src/xmlrpc/passwords.php b/src/xmlrpc/passwords.php new file mode 100644 index 0000000..50dfaa5 --- /dev/null +++ b/src/xmlrpc/passwords.php @@ -0,0 +1,215 @@ +. +*/ + +include_once __DIR__ . '/../database/database.php'; + +include_once __DIR__ . '/../objects/account.php'; +include_once __DIR__ . '/../objects/password.php'; +include_once __DIR__ . '/../objects/alias.php'; +include_once __DIR__ . '/../objects/user_info.php'; + +include_once __DIR__ . '/../misc/utilities.php'; + +include_once __DIR__ . '/results_values.php'; + +// args = [username, old hash, new hash, [domain], [algo]] +function xmlrpc_update_hash($method, $args) { + $username = $args[0]; + $hashed_old_password = $args[1]; + $hashed_new_password = $args[2]; + $domain = get_domain($args[3]); + $algo = get_algo($args[4]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_update_hash(" . $username . ", " . $domain . ", " . $algo . ")"); + + if (!check_parameter($username)) { + return MISSING_USERNAME_PARAM; + } else if ($algo == NULL) { + return ALGO_NOT_SUPPORTED; + } + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->password = $hashed_old_password; + $password->algorithm = $algo; + + if (!$password->getOne()) { + return PASSWORD_DOESNT_MATCH; + } + + $password->password = $hashed_new_password; + if ($password->update()) { + Logger::getInstance()->message("Password updated successfully"); + return OK; + } + + return NOK; +} + +// args = [username, old hash, new password, [domain]] +function xmlrpc_upgrade_hash($method, $args) { + $username = $args[0]; + $hashed_password = $args[1]; + $new_password = $args[2]; + $domain = get_domain($args[3]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_upgrade_hash(" . $username . ", " . $domain . ")"); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->password = $hashed_password; + + if (!$password->getOne()) { + return PASSWORD_DOESNT_MATCH; + } + + // Old password is OK, now let's hash the new password for both MD5 and SHA-256 + + $md5_hashed_password = hash_password($username, $new_password, $domain, MD5); + if ($password->algorithm == MD5) { + $password->password = $md5_hashed_password; + $password->update(); + } else { + $md5_password = new Password($db); + $md5_password->account_id = $account->id; + $md5_password->password = $md5_hashed_password; + $md5_password->algorithm = MD5; + $md5_password->create(); + } + + $sha256_hashed_password = hash_password($username, $new_password, $domain, SHA256); + if ($password->algorithm == SHA256) { + $password->password = $sha256_hashed_password; + $password->update(); + } else { + $sha256_password = new Password($db); + $sha256_password->account_id = $account->id; + $sha256_password->password = $sha256_hashed_password; + $sha256_password->algorithm = SHA256; + $sha256_password->create(); + } + + return OK; +} + +// args = [username, hash, [domain]] +function xmlrpc_check_authentication($method, $args) { + $username = $args[0]; + $hashed_password = $args[1]; + $domain = get_domain($args[2]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_check_authentication(" . $username . ", " . $domain . ")"); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + $password = new Password($db); + $password->account_id = $account->id; + $password->password = $hashed_password; + + if (!$password->getOne()) { + return PASSWORD_DOESNT_MATCH; + } + + return OK; +} + +// args = [username, md5_hash, sha256_hash, [domain]] +function xmlrpc_check_authentication_and_upgrade_password($method, $args) { + $username = $args[0]; + $md5_hashed_password = $args[1]; + $sha256_hashed_password = $args[2]; + $domain = get_domain($args[3]); + + Logger::getInstance()->message("[XMLRPC] xmlrpc_check_authentication_and_upgrade_password(" . $username . ", " . $domain . ")"); + + $database = new Database(); + $db = $database->getConnection(); + $account = new Account($db); + $account->username = $username; + $account->domain = $domain; + + if (!$account->getOne()) { + return ACCOUNT_NOT_FOUND; + } + + $sha256_password = new Password($db); + $sha256_password->account_id = $account->id; + $sha256_password->password = $sha256_hashed_password; + $sha256_password->algorithm = SHA256; + + if (!$sha256_password->getOne()) { + // SHA-256 doesn't exists or doesn't match, let's try MD5 + $md5_password = new Password($db); + $md5_password->account_id = $account->id; + $md5_password->password = $md5_hashed_password; + $md5_password->algorithm = MD5; + + if (!$md5_password->getOne()) { + return PASSWORD_DOESNT_MATCH; + } + + if ($sha256_password->id > 0) { + // SHA-256 exists, let's update it + $sha256_password->update(); + } else { + $sha256_password->create(); + } + } + + return OK; +} + +function xmlrpc_passwords_register_methods($server) { + xmlrpc_server_register_method($server, 'update_hash', 'xmlrpc_update_hash');// args = [username, old hash, new hash, [domain], [algo]], return OK + xmlrpc_server_register_method($server, 'upgrade_hash', 'xmlrpc_upgrade_hash');// args = [username, old hash, new password, [domain]], return OK + + xmlrpc_server_register_method($server, 'check_authentication', 'xmlrpc_check_authentication');// args = [username, hash, [domain]] + xmlrpc_server_register_method($server, 'check_authentication_and_upgrade_password', 'xmlrpc_check_authentication_and_upgrade_password');// args = [username, md5_hash, sha256_hash, [domain]] +} + +?> \ No newline at end of file diff --git a/src/xmlrpc/provisioning.php b/src/xmlrpc/provisioning.php index c5ae502..19e5079 100644 --- a/src/xmlrpc/provisioning.php +++ b/src/xmlrpc/provisioning.php @@ -62,7 +62,7 @@ if (!empty($username)) { $xml = $xml . ''; $ha1 = isset($_GET['ha1']) ? $_GET['ha1'] : null; - $algo = isset($_GET['algorithm']) ? $_GET['algorithm'] : "MD5"; + $algo = isset($_GET['algorithm']) ? $_GET['algorithm'] : DEFAULT_ALGORITHM; if (!empty($ha1)) { $xml = $xml . '
'; diff --git a/src/xmlrpc/xmlrpc.php b/src/xmlrpc/xmlrpc.php index b178955..d49f665 100644 --- a/src/xmlrpc/xmlrpc.php +++ b/src/xmlrpc/xmlrpc.php @@ -26,6 +26,7 @@ include_once __DIR__ . '/authentication.php'; include_once __DIR__ . '/accounts.php'; include_once __DIR__ . '/aliases.php'; include_once __DIR__ . '/devices.php'; +include_once __DIR__ . '/passwords.php'; include_once __DIR__ . '/user_info.php'; include_once __DIR__ . '/compatibility.php'; @@ -99,11 +100,13 @@ if (USE_DIGEST_AUTH) { xmlrpc_accounts_register_methods($server); xmlrpc_aliases_register_methods($server); xmlrpc_devices_register_methods($server); +xmlrpc_passwords_register_methods($server); +xmlrpc_user_info_register_methods($server); +xmlrpc_compatibility_register_methods($server); + if (USE_IN_APP_PURCHASES) { xmlrpc_inapp_register_methods($server); } -xmlrpc_user_info_register_methods($server); -xmlrpc_compatibility_register_methods($server); if ($request) { $options = array('output_type' => 'xml', 'version' => 'auto');