Initial commit

This commit is contained in:
Johan Pascal 2019-06-26 17:18:50 +07:00
commit a3752621ac
21 changed files with 3039 additions and 0 deletions

19
Makefile Normal file
View file

@ -0,0 +1,19 @@
$(eval GIT_DESCRIBE = $(shell sh -c "git describe"))
OUTPUT_DIR = ${CURDIR}
rpm:
rm -rf $(OUTPUT_DIR)/flexisip-account-manager
mkdir $(OUTPUT_DIR)/flexisip-account-manager
mkdir -p $(OUTPUT_DIR)/rpmbuild/SPECS
mkdir -p $(OUTPUT_DIR)/rpmbuild/SOURCES
cp src/*.php $(OUTPUT_DIR)/flexisip-account-manager/
cp README.md $(OUTPUT_DIR)/flexisip-account-manager/
cp src/*.conf $(OUTPUT_DIR)/flexisip-account-manager/
mkdir -p $(OUTPUT_DIR)/flexisip-account-manager/httpd
cp httpd/flexisip-account-manager.conf $(OUTPUT_DIR)/flexisip-account-manager/httpd
cp flexisip-account-manager.spec $(OUTPUT_DIR)/rpmbuild/SPECS/
tar cvf flexisip-account-manager.tar.gz -C $(OUTPUT_DIR) flexisip-account-manager
mv flexisip-account-manager.tar.gz $(OUTPUT_DIR)/rpmbuild/SOURCES/flexisip-account-manager.tar.gz
rpmbuild -v -bb --define '_topdir $(OUTPUT_DIR)/rpmbuild' --define "_rpmdir $(OUTPUT_DIR)/rpmbuild" $(OUTPUT_DIR)/rpmbuild/SPECS/flexisip-account-manager.spec
rm -rf $(OUTPUT_DIR)/flexisip-account-manager
.PHONY: rpm

16
README.packaging.md Normal file
View file

@ -0,0 +1,16 @@
PHP Authenticated Lime server
==============================
Packaging
---------
To build a rpm package on centos7:
`make rpm`
To build a rpm package with docker:
`docker run -v $PWD:/home/bc -it gitlab.linphone.org:4567/bc/public/linphone-sdk/bc-dev-centos:7 make`
The lime-server rpm package can be found in rpmbuild/RPMS/x86_64/bc-lime-server*.rpm
Installation requires package centos-release-scl-rh to be installed for php7.1

View file

@ -0,0 +1,73 @@
# -*- rpm-spec -*-
#%define _prefix @CMAKE_INSTALL_PREFIX@
#%define pkg_prefix @BC_PACKAGE_NAME_PREFIX@
# re-define some directories for older RPMBuild versions which don't. This messes up the doc/ dir
# taken from https://fedoraproject.org/wiki/Packaging:RPMMacros?rd=Packaging/RPMMacros
#%define _datarootdir %{_prefix}/share
#%define _datadir %{_datarootdir}
#%define _docdir %{_datadir}/doc
%define build_number 1
#%if %{build_number}
#%define build_number_ext -%{build_number}
#%endif
Name: bc-flexisip-account-manager
Version: 1.0.2
Release: %{build_number}%{?dist}
Summary: SIP account management xml-rpc server, for use with flexisip server suite.
Group: Applications/Communications
License: GPL
URL: http://www.linphone.org
#Source0: %{name}-%{version}%{?build_number_ext}.tar.gz
Source0: flexisip-account-manager.tar.gz
#BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot
# dependencies
Requires: rh-php71-php rh-php71-php-xmlrpc rh-php71-php-mysqlnd rh-php71-php-mbstring
%description
PHP server for Linphone and Flexisip providing module for account creation.
%prep
%setup -n flexisip-account-manager
%install
rm -rf "$RPM_BUILD_ROOT"
mkdir -p "$RPM_BUILD_ROOT/opt/belledonne-communications/share/flexisip-account-manager"
cp -R *.php "$RPM_BUILD_ROOT/opt/belledonne-communications/share/flexisip-account-manager"
cp -R README* "$RPM_BUILD_ROOT/opt/belledonne-communications/share/flexisip-account-manager"
mkdir -p "$RPM_BUILD_ROOT/etc/flexisip-account-manager"
cp -R *.conf "$RPM_BUILD_ROOT/etc/flexisip-account-manager"
mkdir -p $RPM_BUILD_ROOT/opt/rh/httpd24/root/etc/httpd/conf.d
cp httpd/flexisip-account-manager.conf "$RPM_BUILD_ROOT/opt/rh/httpd24/root/etc/httpd/conf.d"
%post
if [ $1 -eq 1 ] ; then
mkdir -p /var/opt/belledonne-communications/log
touch /var/opt/belledonne-communications/log/account-manager.log
chown apache:apache /var/opt/belledonne-communications/log/account-manager.log
chcon -t httpd_sys_rw_content_t /var/opt/belledonne-communications/log/account-manager.log
setsebool httpd_can_network_connect_db on
fi
%files
/opt/belledonne-communications/share/flexisip-account-manager/*.php
/opt/belledonne-communications/share/flexisip-account-manager/README*
%config(noreplace) /etc/flexisip-account-manager/*.conf
%config(noreplace) /opt/rh/httpd24/root/etc/httpd/conf.d/flexisip-account-manager.conf
%clean
rm -rf $RPM_BUILD_ROOT
%changelog
* Fri Jun 28 2019 Johan Pascal <johan.pascal@belledonne-communications.com>
-
* Fri May 18 2018 Matthieu TANON <matthieu.tanon@belledonne-communications.com>
- Initial RPM release.

View file

@ -0,0 +1,7 @@
Alias /flexisip-account-manager /opt/belledonne-communications/share/flexisip-account-manager
<Directory /opt/belledonne-communications/share/flexisip-account-manager/>
Options FollowSymLinks MultiViews
AllowOverride None
Require all granted
</Directory>

78
src/admin.php Normal file
View file

@ -0,0 +1,78 @@
#! /bin/php
<?php
define("PATH_TO_CONFIG", "/etc/flexisip-account-manager/");
include PATH_TO_CONFIG . "xmlrpc.conf";
if (USE_MYSQLI) {
include "mysqli-db.php";
} else {
include "mysql-db.php";
}
include "logging.php";
include "utilities.php";
include "xmlrpc-accounts.php";
include "xmlrpc-aliases.php";
include "xmlrpc-sms.php";
date_default_timezone_set(DEFAULT_TIMEZONE);
mylog("[DEBUG] Timezone set to " . DEFAULT_TIMEZONE);
if ($argc >= 2) {
$action = $argv[1];
if ($action == "list_accounts") {
$accounts = db_get_accounts();
foreach ($accounts as $account) {
echo $account['username'] . '@' . $account['domain'] . ' activation status is ' . $account['activated'] . " (activation code is " . $account['activation_code'] . "): IP " . $account['ip_address'] . ", user-agent " . $account['user_agent'] . "\r\n";
}
} else if ($action == "delete_account") {
if ($argc >= 3) {
$login = $argv[2];
$domain = SIP_DOMAIN;
if ($argc >= 4) {
$domain = $argv[3];
}
if (!db_account_is_existing($login, $domain)) {
echo "Error: account " . $login . " on domain " . $domain . " doesn't exist." . "\r\n";
exit;
}
db_alias_delete($login, $domain);
db_account_delete($login, $domain);
if (startswith($login, "+")) {
db_delete_sms($login);
}
echo "Account " . $login . " successfuly deleted." . "\r\n";
} else {
echo "Proper way to use is php admin.php delete_account <login> [domain]" . "\r\n";
}
} else if ($action == "activate_account") {
if ($argc >= 3) {
$login = $argv[2];
$domain = SIP_DOMAIN;
if ($argc >= 4) {
$domain = $argv[3];
}
if (!db_account_is_existing($login, $domain)) {
echo "Error: account " . $login . " on domain " . $domain . " doesn't exist." . "\r\n";
exit;
}
db_account_super_activate($login, $domain);
echo "Account " . $login . " succesfuly super activated." . "\r\n";
} else {
echo "Proper way to use is php admin.php activate_account <login> [domain]" . "\r\n";
}
} else if ($action == "help") {
echo "Possible commands are:" . "\r\n";
echo "help" . "\r\n";
echo "list_accounts" . "\r\n";
echo "activate_account" . "\r\n";
echo "delete_account <login> [domain]" . "\r\n";
}
} else {
echo "Proper way to use is php admin.php action [params]" . "\r\n";
echo "Try php admin.php help to see all possible actions." . "\r\n";
exit;
}
?>

64
src/authentication.php Normal file
View file

@ -0,0 +1,64 @@
<?php
function request_authentication($realm = "sip.example.org") {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="' . $realm.
'",qop="auth",nonce="' . uniqid() . '",opaque="' . md5($realm) . '"');
exit();
}
function authenticate($auth_digest, $realm = "sip.example.org") {
// Parse the client authentication data
$default = array('nounce', 'nc', 'cnounce', 'qop', 'username', 'uri', 'response');
preg_match_all('~(\w+)="?([^",]+)"?~', $auth_digest, $matches); # $_SERVER['PHP_AUTH_DIGEST']
$data = array_combine($matches[1] + $default, $matches[2]);
// Get the password/hash from database
$conn = linphonedb_connect();
$query = "SELECT password FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $data['username']) . "');";
$result = linphonedb_query($query, $conn);
$row = linphonedb_fetch($result);
$password = $row[0];
// Get the corresponding algorithm if specified
$query = "SELECT algorithm FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $data['username']) . "');";
$result = linphonedb_query($query, $conn);
$row = linphonedb_fetch($result);
$algorithm = $row[0];
if (is_null($algorithm)) {
$algorithm = 'MD5';
}
// Close db connection
linphonedb_clean($result);
linphonedb_close($conn);
// Generate the valid response
switch ($algorithm) {
case 'CLRTXT':
$A1 = md5($data['username'].':'.$data['realm'].':'.$password);
$A2 = md5(getenv('REQUEST_METHOD').':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
break;
case 'MD5':
$A1 = $password; // username:realm:password
$A2 = md5(getenv('REQUEST_METHOD').':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
break;
case 'SHA256':
$A1 = $password; // username:realm:password
$A2 = hash('sha256', getenv('REQUEST_METHOD').':'.$data['uri']);
$valid_response = hash('sha256', $A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
break;
}
// Compare with the client response
if($data['response'] === $valid_response) {
return TRUE;
} else {
return FALSE;
}
}
?>

17
src/hooks.php Normal file
View file

@ -0,0 +1,17 @@
<?php
define("CUSTOM_HOOKS", True);
function hook_on_account_created($user, $domain, $hashed_password, $email) {
mylog("[HOOK] Account created");
}
function hook_on_account_activated($user, $domain, $expiration_date) {
mylog("[HOOK] Account activated");
}
function hook_on_expiration_date_updated($user, $domain, $expiration_date, $payloadJson, $os) {
mylog("[HOOK] Expiration updated");
}
?>

View file

@ -0,0 +1,7 @@
<?php
// For SMS, remember to stay under 160 characters.
$SMS_OVH_TEMPLATE = array (
'US' => 'Your Linphone validation code is #CODE#', // This one isn't required but if present it MUST be equal to SMS_OVH_US_TEMPLATE
'FR' => 'Votre code de validation Linphone est #CODE#',
);
?>

17
src/logging.php Normal file
View file

@ -0,0 +1,17 @@
<?php
function mylog($message) {
if (!LOGS_ENABLED) {
return;
}
$now = getdate();
$month = sprintf("%02d", $now["mon"]);
$day = sprintf("%02d", $now["mday"]);
$hours = sprintf("%02d", $now["hours"]);
$minutes = sprintf("%02d", $now["minutes"]);
$seconds = sprintf("%02d", $now["seconds"]);
error_log("[" . $day . "/" . $month . "/" . $now["year"] . " " . $hours . ":" . $minutes . ":" . $seconds . "] " . $message . "\r\n", 3, LOG_FILE);
}
?>

46
src/mysqli-db.php Normal file
View file

@ -0,0 +1,46 @@
<?php
function linphonedb_connect() {
if (USE_PERSISTENT_CONNECTIONS) {
$conn = mysqli_connect('p:' . DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
} else {
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
}
if ($conn->connect_errno) {
mylog("[ERROR][DB] Connection failed: " . $conn->connect_errno . " - " . $conn->connect_error);
}
return $conn;
}
function linphonedb_escape($conn, $param) {
return mysqli_real_escape_string($conn, $param);
}
function linphonedb_query($queryStr, $conn) {
$result = mysqli_query($conn, $queryStr);
if (! $result) {
mylog("[ERROR][DB] Invalid query: " . $conn->connect_errno . " - " . $conn->connect_error);
return "";
}
// cannot log result because fetch needed
// mylog("[DB] Query: " . $queryStr);
return $result;
}
function linphonedb_fetch($result) {
if ($result->num_rows === 0) {
mylog("[ERROR][DB] Result is empty...");
}
$row = mysqli_fetch_array($result, MYSQLI_NUM);
return $row;
}
function linphonedb_clean($result) {
mysqli_free_result($result);
}
function linphonedb_close($conn) {
mysqli_close($conn);
}
?>

26
src/provisioning.php Normal file
View file

@ -0,0 +1,26 @@
<?php
define("PATH_TO_CONFIG", "/etc/flexisip-account-manager/");
include PATH_TO_CONFIG . "xmlrpc.conf";
$rc_array = parse_ini_file("default.rc", true);
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
$xml = $xml . '<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">';
foreach ($rc_array as $section => $values) {
$xml = $xml . '<section name="' . $section . '">';
foreach ($values as $key => $value) {
if (REMOTE_PROVISIONING_OVERWRITE_ALL) {
$xml = $xml . '<entry name="' . $key . '" overwrite="true">' . $value . '</entry>';
} else {
$xml = $xml . '<entry name="' . $key . '">' . $value . '</entry>';
}
}
$xml = $xml . '</section>';
}
$xml = $xml . '</config>';
header('Content-type: text/xml');
echo $xml;
?>

30
src/sms-sender.php Normal file
View file

@ -0,0 +1,30 @@
<?php
define("PATH_TO_CONFIG", "/etc/flexisip-account-manager/");
include PATH_TO_CONFIG . "xmlrpc.conf";
if (USE_MYSQLI) {
include "mysqli-db.php";
} else {
include "mysql-db.php";
}
include "xmlrpc-sms.php";
include "xmlrpc-accounts.php";
include "xmlrpc-aliases.php";
include "xmlrpc-inapp.php";
include "xmlrpc-devices.php";
include "xmlrpc-compatibility.php";
include "utilities.php";
include "logging.php";
include "hooks.php";
if ($argc == 3) {
$phone = $argv[1];
$msg = $argv[2];
send_sms_ovh($phone, $msg, NULL);
} else {
echo "php ./sms_sender.php <phone number> <message>\r\n";
}
?>

158
src/utilities.php Normal file
View file

@ -0,0 +1,158 @@
<?php
function startswith($hay, $needle) {
return substr($hay, 0, strlen($needle)) === $needle;
}
function endswith($hay, $needle) {
return $needle === "" || (($temp = strlen($hay) - strlen($needle)) >= 0 and strpos($hay, $needle, $temp) !== FALSE);
}
function getIp() {
$ip = $_SERVER['REMOTE_ADDR'];
if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}
return $ip;
}
// Internationalization
function get_lang($param) {
if ($param == NULL || $param == "") {
mylog("[WARN] lang parameter wasn't found, use US");
return 'US';
} else if (strlen($param) > 2) {
$param = substr($param, 0, 2);
}
return strtoupper($param);
}
function get_sms_string_for_lang($lang) {
global $SMS_OVH_TEMPLATE;
if (isset($SMS_OVH_TEMPLATE[$lang])) {
return $SMS_OVH_TEMPLATE[$lang];
}
mylog("[WARN] SMS template not found for lang " . $lang . ", using US template");
return SMS_OVH_US_TEMPLATE;
}
// Password
function hash_password($user, $password, $domain, $algo) {
if(strcmp($algo,"")==0 || strcmp($algo,"MD5")==0) $hashed_password = hash("md5", $user . ":" . $domain . ":" . $password);
if(strcmp($algo,"SHA-256")==0) $hashed_password = hash("sha256", $user . ":" . $domain . ":" . $password);
return $hashed_password;
}
function generate_password() {
$generated_password = substr(str_shuffle(GENERATED_PASSWORD_CHARACTERS), 0, GENERATED_PASSWORD_LENGTH);
return $generated_password;
}
function generate_4_digits_code() {
$generated_password = substr(str_shuffle("0123456789"), 0, 4);
return $generated_password;
}
function get_trial_expiration_date() {
$expiration_date = new DateTime('now +' . TRIAL_DURATION_DAYS . ' days');
$expiration = $expiration_date->getTimestamp() * 1000;
return $expiration;
}
function check_parameter($param, $param_name = "username") {
if ($param == NULL || $param == "") {
mylog("[WARN] " . $param_name . " is missing");
return false;
}
return true;
}
function check_algo($algo) {
if (strcmp($algo,"")==0 || strcmp($algo,"MD5")==0 || strcmp($algo,"SHA-256")==0 || strcmp($algo,"clrtxt")==0){
return true;
}
mylog("[ERROR] Algo " . $algo . " is not supported");
return false;
}
function get_domain($param) {
if ($param == NULL || $param == "") {
mylog("[WARN] domain parameter wasn't found, use " . SIP_DOMAIN);
$param = SIP_DOMAIN;
}
return $param;
}
// Email
function send_email($email, $subject, $text, $html) {
$site = EMAIL_SITE;
$from = EMAIL_FROM_ADDR;
$name = EMAIL_FROM_NAME;
$to = $email;
$from = $name." <".$from.">";
$limite = "_----------=_parties_".md5(uniqid (rand()));
$headers = "Reply-to: ".$from."\n";
$headers .= "From: ".$from."\n";
$headers .= "Return-Path: ".$from."\n";
$headers .= "X-Sender: <".$site.">\n";
$headers .= "X-Mailer: PHP\n";
$headers .= "X-auth-smtp-user: ".$from." \n";
$headers .= "X-abuse-contact: ".$from." \n";
$headers .= "X-auth-smtp-user: ".$from." \n";
$headers .= "X-abuse-contact: ".$from." \n";
$headers .= "Date: ".date("D, j M Y G:i:s O")."\n";
$headers .= "MIME-Version: 1.0\n";
$headers .= "Content-Type: multipart/alternative; boundary=\"".$limite."\"";
$message = "";
$message .= "--".$limite."\n";
$message .= "Content-Type: text/plain; charset=\"utf-8\"\n";
$message .= "Content-Transfer-Encoding: 8bit\n\n";
$message .= $text;
$message .= "\n\n--".$limite."\n";
$message .= "Content-Type: text/html; charset=\"utf-8\"\n";
$message .= "Content-Transfer-Encoding: 8bit;\n\n";
$message .= $html;
$message .= "\n--".$limite."--";
$params = "-f" . EMAIL_FROM_ADDR . " -O DeliveryMode=b";
$result = mail($email, $subject, $message, $headers, $params);
if (!$result) {
mylog("[ERROR][EMAIL] Email delivery declined !");
}
}
function send_email_with_activation_link($email, $key) {
if( !EMAIL_ENABLED ){
mylog("[WARN] [EMAIL] Emails are disabled");
return "WARNING_EMAILS_DISABLED";
}
$pageURL = 'http';
if ($_SERVER["HTTPS"] == "on") {$pageURL .= "s";}
$pageURL .= "://";
$link = $pageURL . EMAIL_ACTIVATION_LINK;
$link = str_replace("%key%", $key, $link);
mylog("[EMAIL] Activation link is " . $link);
$body = str_replace("%link%", $link, EMAIL_ACTIVATION_BODY);
mylog("[EMAIL] Activation body is " . $body);
$body_html = str_replace("%link%", $link, EMAIL_ACTIVATION_BODY_HTML);
mylog("[EMAIL] Activation html body is " . $body_html);
send_email($email, EMAIL_ACTIVATION_SUBJECT, $body, $body_html);
mylog("[EMAIL] Email sent to email " . $email . " to activate the account");
}
?>

866
src/xmlrpc-accounts.php Normal file
View file

@ -0,0 +1,866 @@
<?php
function db_drop_accounts_table() {
$conn = linphonedb_connect();
$create_req = "DROP TABLE IF EXISTS " . ACCOUNTS_DB_TABLE;
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_create_accounts_table() {
$conn = linphonedb_connect();
$create_req = "CREATE TABLE IF NOT EXISTS " . ACCOUNTS_DB_TABLE . " (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
login VARCHAR(64) NOT NULL,
domain VARCHAR(64) NOT NULL,
email VARCHAR(64),
activated VARCHAR(1) NOT NULL DEFAULT '0',
confirmation_key VARCHAR(14) DEFAULT NULL,
ip_address VARCHAR(39) NOT NULL,
user_agent VARCHAR(256) NOT NULL,
expire DATETIME,
PRIMARY KEY (id), UNIQUE KEY login (login, domain))";
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_drop_accounts_algo_table() {
$conn = linphonedb_connect();
$create_req = "DROP TABLE IF EXISTS " . ACCOUNTS_ALGO_DB_TABLE;
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_create_accounts_algo_table($hash=true) {
$conn = linphonedb_connect();
$create_req = "CREATE TABLE IF NOT EXISTS " . ACCOUNTS_ALGO_DB_TABLE . " (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
account_id INTEGER(11) UNSIGNED NOT NULL,
password VARCHAR(255) default '',
algorithm VARCHAR(10) default '',
PRIMARY KEY (id), UNIQUE KEY login (account_id, algorithm)))";
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
if ($hash) {
$result = linphonedb_query(db_migrate_accounts_algo_table(), $conn);
linphonedb_clean($result);
}
linphonedb_close($conn);
}
function db_migrate_accounts_algo_table() {
return "UPDATE " . ACCOUNTS_ALGO_DB_TABLE . " AS a, " . ACCOUNTS_ALGO_DB_TABLE . " AS b SET a.password=b.password, a.algorithm=b.algorithm WHERE a.id=b.id;";
}
function db_account_delete($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("DELETE FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
linphonedb_clean($result);
$result = linphonedb_query("DELETE FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_create($user, $pwd, $domain, $email, $key, $ip, $ua, $algo) {
$conn = linphonedb_connect();
if (ALLOW_TEST_ACCOUNTS) {
$query = "INSERT INTO " . ACCOUNTS_DB_TABLE . "(login, activated, domain, email, confirmation_key, ip_address, user_agent, expire) VALUES('" . linphonedb_escape($conn, $user) . "','0','" . linphonedb_escape($conn, $domain) . "','" . linphonedb_escape($conn, $email) . "', '" . linphonedb_escape($conn, $key) . "','" . linphonedb_escape($conn, $ip) . "','" . linphonedb_escape($conn, $ua) . "', DATE_ADD(NOW(), INTERVAL " . EXPIRATION_DELAY . " MINUTE))";
mylog("[DEBUG] create account query = " . $query);
$result = linphonedb_query($query, $conn);
} else {
$result = linphonedb_query("INSERT INTO " . ACCOUNTS_DB_TABLE . "(login, activated, domain, email, confirmation_key, ip_address, user_agent) VALUES('" . linphonedb_escape($conn, $user) . "','0','" . linphonedb_escape($conn, $domain) . "','" . linphonedb_escape($conn, $email) . "', '" . linphonedb_escape($conn, $key) . "','" . linphonedb_escape($conn, $ip) . "','" . linphonedb_escape($conn, $ua) . "')", $conn);
}
linphonedb_clean($result);
if (!strcmp($algo,""))
$algo = "MD5";
$result = linphonedb_query("INSERT INTO " . ACCOUNTS_ALGO_DB_TABLE . " (account_id, password, algorithm) VALUES((SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'), '" . linphonedb_escape($conn, $pwd) . "', '" . linphonedb_escape($conn, $algo) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_activate($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . ACCOUNTS_DB_TABLE . " SET activated='1' WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_super_activate($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . ACCOUNTS_DB_TABLE . " SET activated='2' WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_update_confirmation_key($user, $domain, $key) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . ACCOUNTS_DB_TABLE . " SET confirmation_key='" . linphonedb_escape($conn, $key) . "' WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_update_email($user, $domain, $new_email) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . ACCOUNTS_DB_TABLE . " SET email='" . linphonedb_escape($conn, $new_email) . "' WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_update_password($user, $domain, $hashed_new_password, $algo) {
$conn = linphonedb_connect();
if(!strcmp($algo,""))
$algo = "MD5";
$result = "UPDATE " . ACCOUNTS_ALGO_DB_TABLE . " SET password='" . linphonedb_escape($conn, $hashed_new_password) . "' WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "') AND algorithm='" . linphonedb_escape($conn, $algo) . "'";
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_account_is_activated($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT activated FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
$row = linphonedb_fetch($result);
$is_activated = $row[0] == '1';
linphonedb_clean($result);
linphonedb_close($conn);
return $is_activated;
}
function db_account_is_existing($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count(*) FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$is_existing = $row[0] >= 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $is_existing;
}
function db_account_is_email_in_use($email) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count(*) FROM " . ACCOUNTS_DB_TABLE . " WHERE email='" . linphonedb_escape($conn, $email) . "'", $conn);
$row = linphonedb_fetch($result);
$is_in_use = $row[0] >= 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $is_in_use;
}
function db_account_is_email_or_login_in_use($user_or_email) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count(*) FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user_or_email) . "' OR email='" . linphonedb_escape($conn, $user_or_email) . "'", $conn);
$row = linphonedb_fetch($result);
$is_in_use = $row[0] >= 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $is_in_use;
}
function db_account_get_confirmation_key($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT confirmation_key FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
$row = linphonedb_fetch($result);
$key = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $key;
}
function db_account_get_password($user, $domain, $algo) {
$conn = linphonedb_connect();
if(!strcmp($algo,""))
$algo = "MD5";
$result = linphonedb_query("SELECT password FROM " . ACCOUNTS_ALGO_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "') AND algorithm='" . linphonedb_escape($conn, $algo) . "'", $conn);
$row = linphonedb_fetch($result);
$password = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $password;
}
function db_account_get_login_from_login_or_email($user_or_email) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT login FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user_or_email) . "' OR email='" . linphonedb_escape($conn, $user_or_email) . "'", $conn);
$row = linphonedb_fetch($result);
linphonedb_clean($result);
linphonedb_close($conn);
return $row[0];
}
function db_account_get_email_from_login_or_email($user_or_email) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT email FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user_or_email) . "' OR email='" . linphonedb_escape($conn, $user_or_email) . "'", $conn);
$row = linphonedb_fetch($result);
linphonedb_clean($result);
linphonedb_close($conn);
return $row[0];
}
function db_account_get_logins_from_email($email) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT login FROM " . ACCOUNTS_DB_TABLE . " WHERE email='" . linphonedb_escape($conn, $email) . "'", $conn);
$usernames = array();
$i = 0;
while ($row = linphonedb_fetch($result)) {
$usernames[$i] = $row[0];
$i = $i + 1;
}
linphonedb_clean($result);
linphonedb_close($conn);
return $usernames;
}
function db_get_accounts() {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT login, domain, activated, confirmation_key, ip_address, user_agent FROM " . ACCOUNTS_DB_TABLE, $conn);
$accounts = array();
$i = 0;
while ($row = linphonedb_fetch($result)) {
$account = array();
$account['username'] = $row[0];
$account['domain'] = $row[1];
$account['activated'] = $row[2];
$account['activation_code'] = $row[3];
$account['ip_address'] = $row[4];
$account['user_agent'] = $row[5];
$accounts[$i] = $account;
$i = $i + 1;
}
linphonedb_clean($result);
linphonedb_close($conn);
return $accounts;
}
// XMLRPC methods
// args == [email]
function xmlrpc_recover_usernames_from_email($method, $args) {
$email = $args[0];
mylog("[XMLRPC] xmlrpc_recover_usernames_from_email(" . $email . ")");
if (!check_parameter($email, "email")) {
return "ERROR_EMAIL_PARAMETER_NOT_FOUND";
}
if (db_account_is_email_in_use($email)) {
$usernames = db_account_get_logins_from_email($email);
//TODO: send email
return "OK";
} else {
mylog("[ERROR] email not found in database");
return "ERROR_EMAIL_DOESNT_EXIST";
}
}
// args = [username or email]
function xmlrpc_send_reset_password_email($method, $args) {
$user_or_email = $args[0];
mylog("[XMLRPC] xmlrpc_send_reset_password_email(" . $user_or_email . ")");
if (!check_parameter($user_or_email, "username or email")) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (db_account_is_email_or_login_in_use($user_or_email)) {
$user = db_account_get_login_from_login_or_email($user_or_email);
$email = db_account_get_email_from_login_or_email($user_or_email);
//TODO: send email
return "OK";
} else {
mylog("[ERROR] username or email not found in database");
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
}
function xmlrpc_send_activation_email($method, $args) {
$user_or_email = $args[0];
mylog("[XMLRPC]xmlrpc_send_activation_email(" . $user_or_email . ")" );
if (!check_parameter($user_or_email, "username or email")) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (db_account_is_email_or_login_in_use($user_or_email)) {
$user = db_account_get_login_from_login_or_email($user_or_email);
$email = db_account_get_email_from_login_or_email($user_or_email);
$hash = get_hash_from_email($email);
delete_link_in_database($hash);
//TODO: send email
return "OK";
} else {
mylog("[ERROR] username or email not found in database");
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
}
// args = [username, old hash, new hash, [domain], [algo]]
function xmlrpc_update_hash($method, $args) {
$user = $args[0];
$hashed_old_password = $args[1];
$hashed_new_password = $args[2];
$domain = get_domain($args[3]);
$algo = $args[4];
mylog("[XMLRPC] xmlrpc_update_hash(" . $user . ", " . $domain . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (db_account_is_existing($user, $domain)) {
$db_hashed_password = db_account_get_password($user, $domain, $algo);
if (strcmp($db_hashed_password, $hashed_old_password) != 0) {
mylog("[ERROR] old password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
} else {
db_account_update_password($user, $domain, $hashed_new_password, $algo);
mylog("Password updated successfully");
return "OK";
}
} else {
mylog("[ERROR] username not found in database");
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
}
// args = [username, old password, new password, [domain], [algo]]
function xmlrpc_update_password($method, $args) {
$user = $args[0];
$domain = get_domain($args[3]);
$algo = $algo[4];
mylog("[XMLRPC] xmlrpc_update_password(" . $user . ", " . $domain . ")");
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
$args[1] = hash_password($args[0], $args[1], $algo);
$args[2] = hash_password($args[0], $args[2], $algo);
return xmlrpc_update_hash("xmlrpc_update_password", $args);
}
// args = [username, password, new email, [domain], [algo]]
function xmlrpc_update_email($method, $args) {
$user = $args[0];
$password = $args[1];
$new_email = $args[2];
$domain = get_domain($args[3]);
$algo = $args[4];
mylog("[XMLRPC] xmlrpc_update_email(" . $user . ", " . $domain . ", " . $new_email . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (db_account_is_existing($user, $domain)) {
$db_hashed_password = db_account_get_password($user, $domain, $algo);
$hashed_old_password = hash_password($user, $password, $domain, $algo);
if (strcmp($db_hashed_password, $hashed_old_password) != 0 and strcmp($db_hashed_password, $password) != 0) {
mylog("[ERROR] old password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
} else {
$old_email = db_account_get_email_from_login_or_email($user);
if (strcmp($old_email, $new_email) == 0) {
mylog("New email same as previous one");
return "ERROR_EMAIL_NEW_SAME_AS_OLD";
} else {
if (db_account_is_email_in_use($new_email) && !ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) {
return "ERROR_EMAIL_ALREADY_IN_USE";
} else {
db_account_update_email($user, $domain, $new_email);
mylog("Email updated successfully");
return "OK";
}
}
}
} else {
mylog("[ERROR] username not found in database");
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
}
// args = [username, [domain]]
function xmlrpc_is_account_activated($method, $args) {
$user = $args[0];
$domain = get_domain($args[1]);
mylog("[XMLRPC] xmlrpc_is_account_activated(" . $user . ", " . $domain . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (db_account_is_activated($user, $domain)) {
mylog("User account " . $user . " / " . $domain . " is activated");
return "OK";
} else {
if (db_account_is_existing($user, $domain)) {
return "NOK";
} else {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
}
}
// args = [username, [domain]]
function xmlrpc_is_account_used($method, $args) {
$user = $args[0];
$domain = get_domain($args[1]);
mylog("[XMLRPC] xmlrpc_is_account_used(" . $user . ", " . $domain . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " exists");
return "OK";
} else {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "NOK";
}
}
// args = [email]
function xmlrpc_is_email_used($method, $args) {
$email = $args[0];
mylog("[XMLRPC] xmlrpc_is_email_used(" . $email . ")");
if (!check_parameter($email, "email")) {
return "ERROR_EMAIL_PARAMETER_NOT_FOUND";
}
if (db_account_is_email_in_use($email)) {
mylog("[ERROR] User email " . $email . " exists");
return "OK";
} else {
mylog("[ERROR] User email " . $email . " doesn't exist");
return "ERROR_EMAIL_DOESNT_EXIST";
}
}
// args = [username, key, [domain], [algo]]
function xmlrpc_activate_email_account($method, $args) {
$user = $args[0];
$key = $args[1];
$domain = get_domain($args[2]);
$algo = $args[3];
mylog("[XMLRPC] xmlrpc_activate_account(" . $user . ", " . $domain . ", " . $key . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else if (db_account_is_activated($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " is already activated");
return "ERROR_ACCOUNT_ALREADY_ACTIVATED";
}
$key_db = db_account_get_confirmation_key($user, $domain);
if (strcmp($key, "ERROR") == 0 or strcmp($key, $key_db) != 0) {
if (strcmp($key_db, "ERROR") != 0) {
db_account_update_confirmation_key($user, $domain, "ERROR");
}
mylog("[ERROR] Key doesn't match");
return "ERROR_KEY_DOESNT_MATCH";
}
$expiration = NULL;
db_account_activate($user, $domain);
if (USE_IN_APP_PURCHASES) {
$expiration = get_trial_expiration_date();
db_inapp_add_account($user, $domain, $expiration);
}
if (CUSTOM_HOOKS) {
hook_on_account_activated($user, $domain, $expiration);
}
$ha1 = db_account_get_password($user, $domain, $algo);
return $ha1;
}
// 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 = $args[4];
mylog("[XMLRPC] xmlrpc_activate_phone_account(" . $user . ", " . $domain . ", " . $key . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
mylog("[ERROR] Phone doesn't start by +");
return "ERROR_PHONE_ISNT_E164";
}
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
$key_db = db_account_get_confirmation_key($user, $domain);
if (strcmp($key, "ERROR") == 0 or strcmp($key, $key_db) != 0) {
if (strcmp($key_db, "ERROR") != 0) {
db_account_update_confirmation_key($user, $domain, "ERROR");
}
mylog("[ERROR] Key doesn't match");
return "ERROR_KEY_DOESNT_MATCH";
}
// If this is a recovery, account is already activated, don't go through the following again
if (!db_account_is_activated($user, $domain)) {
$expiration = NULL;
db_account_activate($user, $domain);
db_alias_add($phone, $user, $domain);
if (USE_IN_APP_PURCHASES) {
$expiration = get_trial_expiration_date();
db_inapp_add_account($user, $domain, $expiration);
}
if (CUSTOM_HOOKS) {
hook_on_account_activated($user, $domain, $expiration);
}
}
$ha1 = db_account_get_password($user, $domain, $algo);
return $ha1;
}
// args = [username, email, [hash], useragent, [domain], [algo]]
function xmlrpc_create_email_account($method, $args) {
$user = $args[0];
$email = $args[1];
$domain = get_domain($args[4]);
$algo = $args[5];
mylog("[XMLRPC] xmlrpc_create_account(" . $user . ", " . $domain . ", " . $email . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
} else if (db_account_is_existing($user, $domain)) {
mylog("[XMLRPC] account already in use");
return "ERROR_ACCOUNT_ALREADY_IN_USE";
}
if (!check_parameter($email, "email")) {
return "ERROR_EMAIL_PARAMETER_NOT_FOUND";
} else if (db_account_is_email_in_use($email) && !ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS) {
mylog("[XMLRPC] email already in use");
return "ERROR_EMAIL_ALREADY_IN_USE";
}
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (GENERATE_PASSWORD_ENABLED) {
$hashed_password = hash_password($user, generate_password(), $domain, $algo);
} else {
$hashed_password = $args[2];
}
$user_agent = $args[3];
$key = uniqid();
mylog("[XMLRPC] Create account " . $user);
db_account_create($user, $hashed_password, $domain, $email, $key, getIp(), $user_agent, $algo);
if (CUSTOM_HOOKS) {
hook_on_account_created($user, $domain, $hashed_password, $email);
}
if (SEND_ACTIVATION_EMAIL) {
send_email_with_activation_link($email, $key);
} else if (AUTO_ACTIVATE_ACCOUNT) {
db_account_activate($user, $domain);
if (USE_IN_APP_PURCHASES) {
$expiration = get_trial_expiration_date();
db_inapp_add_account($user, $domain, $expiration);
}
}
return "OK";
}
// 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];
$domain = get_domain($args[4]);
$lang = get_lang($args[5]);
$algo = $args[6];
mylog("[XMLRPC] xmlrpc_create_phone_account(" . $phone . ", " . $domain . ", " . $user . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
mylog("[ERROR] Phone doesn't start by +");
return "ERROR_PHONE_ISNT_E164";
}
if (!check_parameter($user)) {
$user = $phone;
}
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
$recover_params = array(
0 => $phone,
1 => $domain,
2 => $lang,
);
if (db_account_is_existing($user, $domain)) {
if (RECOVER_ACCOUNT_IF_EXISTS) {
$recovered_user = xmlrpc_recover_phone_account($method, $recover_params);
if ($recovered_user == $user) return "OK";
return "ERROR_CANT_RECOVER_ACCOUNT";
} else {
return "ERROR_ACCOUNT_ALREADY_IN_USE";
}
} else if (db_alias_is_in_use($phone, $domain)) {
if (RECOVER_ACCOUNT_IF_EXISTS) {
$recovered_user = xmlrpc_recover_phone_account($method, $recover_params);
if ($recovered_user == $user) return "OK";
return "ERROR_CANT_RECOVER_ACCOUNT";
} else {
return "ERROR_ALIAS_ALREADY_IN_USE";
}
}
$password = $hashed_password;
if (!check_parameter($hashed_password, "hashed password")) {
$password = generate_password();
$hashed_password = hash_password($user, $password, $domain, $algo);
}
$user_agent = $args[3];
$key = generate_4_digits_code();
db_account_create($user, $hashed_password, $domain, NULL, $key, getIp(), $user_agent, $algo);
if (CUSTOM_HOOKS) {
hook_on_account_created($user, $domain, $hashed_password, NULL);
}
if (SEND_ACTIVATION_SMS) {
if (!SMS_API_ENABLED) {
// This is a hack to allow testing without sending SMS
return "OK";
}
$ok = send_sms($phone, $key, $lang);
return $ok;
} else if (AUTO_ACTIVATE_ACCOUNT) {
db_account_activate($user, $domain);
if (USE_IN_APP_PURCHASES) {
$expiration = get_trial_expiration_date();
db_inapp_add_account($user, $domain, $expiration);
}
}
return "OK";
}
// args = [user, pwd, [domain], [algo]]
// /!\ This method must be used for tests purposes only /!\
function xmlrpc_get_confirmation_key($method, $args) {
$user = $args[0];
$password = $args[1];
$domain = get_domain($args[2]);
$algo = $args[3];
mylog("[XMLRPC] xmlrpc_get_confirmation_key(" . $user . ", " . $domain . ")");
if (!check_parameter($user)) {
mylog("[ERROR] Username parameter not found");
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
} else if (!ALLOW_TEST_ACCOUNTS) {
mylog("[ERROR] Non test account unauthorized");
return "ERROR_NON_TEST_ACCOUNTS_UNAUTHORIZED";
}
if (!check_algo($algo)) {
mylog("[ERROR] Algo not supported");
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else {
$hashed_password = hash_password($user, $password, $domain, $algo);
$db_hashed_password = db_account_get_password($user, $domain, $algo);
if (strcmp($hashed_password, $db_hashed_password) != 0 and strcmp($password, $db_hashed_password) != 0) {
mylog("[ERROR] Password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
}
$key = db_account_get_confirmation_key($user, $domain);
mylog("[XMLRPC] returning key = " . $key);
return $key;
}
// args = [user, pwd, [domain], [algo]]
// /!\ This method must be used for tests purposes only /!\
function xmlrpc_delete_account($method, $args) {
$user = $args[0];
$password = $args[1];
$domain = get_domain($args[2]);
$algo = $args[3];
mylog("[XMLRPC] xmlrpc_delete_account(" . $user . ", " . $domain . ")");
if (!check_algo($algo)) {
return "ERROR_ALGO_NOT_SUPPORTED";
}
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
} else if (!ALLOW_TEST_ACCOUNTS) {
return "ERROR_NON_TEST_ACCOUNTS_UNAUTHORIZED";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else {
$hashed_password = hash_password($user, $password, $domain, $algo);
$db_hashed_password = db_account_get_password($user, $domain, $algo);
if (strcmp($hashed_password, $db_hashed_password) != 0 and strcmp($password, $db_hashed_password) != 0) {
mylog("[ERROR] Password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
}
db_alias_delete($user, $domain);
db_account_delete($user, $domain);
return "OK";
}
// args = [phone, [domain]]
function xmlrpc_is_phone_number_used($method, $args) {
$phone = $args[0];
$domain = get_domain($args[1]);
mylog("[XMLRPC] xmlrpc_is_phone_number_used(" . $phone . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
return "ERROR_PHONE_ISNT_E164";
}
if (db_account_is_existing($phone, $domain)) {
return "OK_ACCOUNT";
} else if (db_alias_is_in_use($phone, $domain)) {
return "OK_ALIAS";
}
return "NOK";
}
// args = [username, [domain]]
function xmlrpc_get_phone_number_for_account($method, $args) {
$user = $args[0];
$domain = get_domain($args[1]);
mylog("[XMLRPC] xmlrpc_get_phone_number_for_account(" . $user . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!db_account_is_existing($user, $domain)) {
if (db_alias_is_in_use($user, $domain)) {
return $user;
}
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
$phone = db_alias_get_reverse($user, $domain);
if ($phone == NULL) {
return "ERROR_ALIAS_DOESNT_EXIST";
}
if (RECOVER_ACCOUNT_IF_EXISTS) {
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
return $phone;
}
// args = [phone, [domain], [lang]]
function xmlrpc_recover_phone_account($method, $args) {
$phone = $args[0];
$domain = get_domain($args[1]);
$lang = get_lang($args[2]);
mylog("[XMLRPC] xmlrpc_recover_phone_account(" . $phone . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
return "ERROR_PHONE_ISNT_E164";
}
$user = NULL;
if (db_alias_is_in_use($phone, $domain)) {
$user = db_alias_get($phone, $domain);
}
if ($user != NULL || db_account_is_existing($phone, $domain)) {
if ($user == NULL) {
$user = $phone;
}
if (SEND_ACTIVATION_SMS) {
$key = generate_4_digits_code();
db_account_update_confirmation_key($user, $domain, $key);
$ok = send_sms($phone, $key, $lang);
if ($ok != "OK") {
return $ok;
}
}
return $user;
}
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
function xmlrpc_accounts_register_methods($server) {
//TODO FIXME remove this methods later as it's a security risk
// /!\ This methods must be used for tests purposes only /!\
xmlrpc_server_register_method($server, 'get_confirmation_key', 'xmlrpc_get_confirmation_key');// args = [user, pwd, [domain]], return confirmation_key
xmlrpc_server_register_method($server, 'delete_account', 'xmlrpc_delete_account');// args = [user, pwd, [domain]]
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, 'activate_phone_account', 'xmlrpc_activate_phone_account');// args = [phone, username, key, [domain]], return ha1_password
xmlrpc_server_register_method($server, 'create_phone_account', 'xmlrpc_create_phone_account');// args = [phone, [username], [password], useragent, [domain], [lang]], return "OK"
xmlrpc_server_register_method($server, 'activate_email_account', 'xmlrpc_activate_email_account');// args = [username, key, [domain]], return ha1_password
xmlrpc_server_register_method($server, 'create_email_account', 'xmlrpc_create_email_account');// args = [username, email, [hash], useragent, [domain]], return "OK"
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, 'recover_phone_account', 'xmlrpc_recover_phone_account');// args = [phone, [domain], [lang]], return username
xmlrpc_server_register_method($server, 'update_password', 'xmlrpc_update_password');// args = [username, old password, new password, [domain]], return "OK"
xmlrpc_server_register_method($server, 'update_hash', 'xmlrpc_update_hash');// args = [username, old hash, new hash, [domain]], return "OK"
xmlrpc_server_register_method($server, 'update_email', 'xmlrpc_update_email');// args = [username, password, new email, [domain]], return "OK"
}
?>

203
src/xmlrpc-aliases.php Normal file
View file

@ -0,0 +1,203 @@
<?php
function db_drop_alias_table() {
$conn = linphonedb_connect();
$create_req = "DROP TABLE IF EXISTS " . ALIAS_DB_TABLE;
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_create_alias_table() {
$conn = linphonedb_connect();
$create_req = "CREATE TABLE IF NOT EXISTS " . ALIAS_DB_TABLE . " (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
account_id INTEGER(11) UNSIGNED NOT NULL,
alias VARCHAR(64),
domain VARCHAR(64) NOT NULL,
PRIMARY KEY (id), UNIQUE KEY login (alias, domain))";
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_alias_delete($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("DELETE FROM " . ALIAS_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_alias_add($alias, $user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("INSERT INTO " . ALIAS_DB_TABLE . "(account_id, alias, domain) VALUES((SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'),'" . linphonedb_escape($conn, $alias) . "','" . linphonedb_escape($conn, $domain) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_alias_update($alias, $user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . ALIAS_DB_TABLE . " SET account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "') WHERE alias='" . linphonedb_escape($conn, $alias) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_alias_is_in_use($alias, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count(*) FROM " . ALIAS_DB_TABLE . " WHERE alias='" . linphonedb_escape($conn, $alias) . "' AND domain='" . linphonedb_escape($conn, $domain) . "'", $conn);
$row = linphonedb_fetch($result);
$is_in_use = $row[0] == 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $is_in_use;
}
function db_alias_get($alias, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT login FROM " . ACCOUNTS_DB_TABLE . " WHERE id=(SELECT account_id FROM " . ALIAS_DB_TABLE . " WHERE alias='" . linphonedb_escape($conn, $alias) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$login = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $login;
}
function db_alias_get_reverse($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT alias FROM " . ALIAS_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$reverse = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $reverse;
}
// XMLRPC methods
// args = [phone, [domain]]
function xmlrpc_is_alias_used($method, $args) {
$phone = $args[0];
$domain = get_domain($args[1]);
mylog("[XMLRPC] xmlrpc_is_alias_used(" . $user . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
mylog("[ERROR] Phone doesn't start by +");
return "ERROR_PHONE_ISNT_E164";
} else if (!db_alias_is_in_use($phone, $domain)) {
return "ERROR_ALIAS_DOESNT_EXIST";
}
return "OK";
}
// args = [phone, account, [domain], [lang]]
function xmlrpc_link_phone_number_with_account($method, $args) {
$phone = $args[0];
$user = $args[1];
$domain = get_domain($args[2]);
$lang = get_lang($args[3]);
mylog("[XMLRPC] xmlrpc_link_phone_number_with_account(" . $user . ", " . $domain . ", " . $phone . ")");
if (!check_parameter($phone)) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
mylog("[ERROR] Phone doesn't start by +");
return "ERROR_PHONE_ISNT_E164";
/*} else if (db_alias_is_in_use($phone, $domain)) {
return "ERROR_ALIAS_ALREADY_IN_USE";*/
} else if (!db_account_is_existing($user, $domain)) {
return "ERROR_ACCOUNT_DOESNT_EXIST";
}
if (SEND_ACTIVATION_SMS) {
if (!SMS_API_ENABLED) {
// This is a hack to allow testing without sending SMS
return "OK";
}
$key = generate_4_digits_code();
db_account_update_confirmation_key($user, $domain, $key);
$ok = send_sms($phone, $key, $lang);
return $ok;
}
return "ERROR_SMS_API_DISABLED";
}
// args = [phone, user, key, ha1, [domain], [algo]]
function xmlrpc_activate_phone_number_link($method, $args) {
$phone = $args[0];
$user = $args[1];
$key = $args[2];
$ha1 = $args[3];
$domain = get_domain($args[4]);
mylog("[XMLRPC] xmlrpc_activate_phone_number_link(" . $user . ", " . $domain . ", " . $phone . ", " . $key . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
mylog("[ERROR] Phone doesn't start by +");
return "ERROR_PHONE_ISNT_E164";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else if (!db_account_is_activated($user, $domain)) {
mylog("[ERROR] User account " . $user . " is not activated");
return "ERROR_ACCOUNT_NOT_ACTIVATED";
}
$key_db = db_account_get_confirmation_key($user, $domain);
if (strcmp($key, $key_db) != 0) {
mylog("[ERROR] Key doesn't match");
return "ERROR_KEY_DOESNT_MATCH";
}
$db_hashed_password = db_account_get_password($user, $domain, $args[5]);
if (strcmp($db_hashed_password, $ha1) != 0) {
mylog("[ERROR] old password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
if (db_alias_is_in_use($phone, $domain)) {
db_alias_update($phone, $user, $domain);
} else {
db_alias_add($phone, $user, $domain);
}
return "OK";
}
// args = [phone, [domain]]
function xmlrpc_get_alias($method, $args) {
$phone = $args[0];
$domain = get_domain($args[1]);
mylog("[XMLRPC] xmlrpc_get_alias(" . $phone . ")");
if (!check_parameter($phone, "phone")) {
return "ERROR_PHONE_PARAMETER_NOT_FOUND";
} else if (!startswith($phone, "+")) {
mylog("[ERROR] Phone doesn't start by +");
return "ERROR_PHONE_ISNT_E164";
} else if (!db_alias_is_in_use($phone, $domain)) {
return "ERROR_ALIAS_DOESNT_EXIST";
}
return db_alias_get($phone);
}
function xmlrpc_aliases_register_methods($server) {
xmlrpc_server_register_method($server, 'is_alias_used', 'xmlrpc_is_alias_used');// args = [phone], return "OK"
xmlrpc_server_register_method($server, 'link_phone_number_with_account', 'xmlrpc_link_phone_number_with_account');// args = [phone, account, [domain], [lang]], return "OK"
xmlrpc_server_register_method($server, 'activate_phone_number_link', 'xmlrpc_activate_phone_number_link');// args = [phone, user, key, ha1, [domain]], return "OK"
xmlrpc_server_register_method($server, 'get_alias', 'xmlrpc_get_alias');// args = [phone], return username
}
?>

View file

@ -0,0 +1,75 @@
<?php
// args = [identity]
function xmlrpc_compatibility_check_account_validated($method, $args) {
// Return 1 if account is validated, else return 0
list($login, $domain) = explode("@", $args[0]);
if (startswith($login, "sip:")) {
list($sip, $login) = explode(":", $login);
}
if (db_account_is_activated($login, $domain)) {
return 1;
}
return 0;
}
// args = [identity]
function xmlrpc_compatibility_check_account($method, $args) {
// Return 1 if login is already used, else return 0
list($login, $domain) = explode("@", $args[0]);
if (startswith($login, "sip:")) {
list($sip, $login) = explode(":", $login);
}
if (db_account_is_existing($login, $domain)) {
return 1;
}
return 0;
}
// args = [identity, password, email, useragent]
function xmlrpc_compatibility_create_account_with_useragent($method, $args) {
$newargs = array($args[0], $args[1], $args[2], 0, $args[3]);
return xmlrpc_compatibility_create_account($method, $newargs);
}
// args = [identity, password, email, newsletter, useragent?]
function xmlrpc_compatibility_create_account($method, $args) {
// Return 0 if account successfully created, else return -1
list($login, $domain) = explode("@", $args[0]);
if (startswith($login, "sip:")) {
list($sip, $login) = explode(":", $login);
}
if (db_account_is_existing($login, $domain)) {
return -1;
}
$password = $args[1];
$email = $args[2];
$confirmation_key = uniqid();
$ip = getIp();
$crypted_password = hash_password($login, $password, $domain);
$useragent = 'linphone-wizard';
if (count($args) == 5) {
$useragent = $args[4];
}
db_account_create($login, $crypted_password, $domain, $email, $confirmation_key, $ip, $useragent);
send_email_with_activation_link($email, $confirmation_key);
return 0;
}
function xmlrpc_compatibility_register_methods($server) {
xmlrpc_server_register_method($server, 'check_account', 'xmlrpc_compatibility_check_account');
xmlrpc_server_register_method($server, 'create_account', 'xmlrpc_compatibility_create_account');
xmlrpc_server_register_method($server, 'check_account_validated', 'xmlrpc_compatibility_check_account_validated');
xmlrpc_server_register_method($server, 'create_account_with_useragent', 'xmlrpc_compatibility_create_account_with_useragent');
}
?>

52
src/xmlrpc-devices.php Normal file
View file

@ -0,0 +1,52 @@
<?php
function db_drop_devices_table() {
$conn = linphonedb_connect();
$create_req = "DROP TABLE IF EXISTS " . DEVICES_DB_TABLE;
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_create_devices_table() {
$conn = linphonedb_connect();
$create_req = "CREATE TABLE IF NOT EXISTS " . DEVICES_DB_TABLE . " (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
manufacturer VARCHAR(64) NOT NULL,
model VARCHAR(34) NOT NULL,
status VARCHAR(34) NOT NULL,
delay INTEGER(4) NOT NULL DEFAULT 0,
hardware_echo_canceller TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id))";
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_device_create($manufacturer, $model, $status, $delay, $hec) {
$conn = linphonedb_connect();
$result = linphonedb_query("INSERT INTO " . DEVICES_DB_TABLE . "(manufacturer, model, status, delay, hardware_echo_canceller) VALUES('" . linphonedb_escape($conn, $manufacturer) . "','" . linphonedb_escape($conn, $model) . "','" . linphonedb_escape($conn, $status) . "','" . linphonedb_escape($conn, $delay) . "','" . linphonedb_escape($conn, $hec) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
// args = [manufacturer, model, status, delay, hasHEC]
function xmlrpc_add_ec_calibration_result($method, $args) {
$manufacturer = $args[0];
$model = $args[1];
$status = $args[2];
$delay = $args[3];
$hasHEC = 0;
if (count($args) == 5) {
$hasHEC = (int)$args[4];
}
db_device_create($manufacturer, $model, $status, $delay, $hasHEC);
return "OK";
}
function xmlrpc_misc_register_methods($server) {
xmlrpc_server_register_method($server, 'add_ec_calibration_result', 'xmlrpc_add_ec_calibration_result');// args = [manufacturer, model, status, delay, hasHEC]
}
?>

476
src/xmlrpc-inapp.php Normal file
View file

@ -0,0 +1,476 @@
<?php
function db_drop_inapp_table() {
$conn = linphonedb_connect();
$create_req = "DROP TABLE IF EXISTS " . INAPP_DB_TABLE;
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_create_inapp_table() {
$conn = linphonedb_connect();
$create_req = "CREATE TABLE IF NOT EXISTS " . INAPP_DB_TABLE . " (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
account_id INTEGER(11) UNSIGNED NOT NULL,
expire BIGINT(15) UNSIGNED DEFAULT 0,
trial TINYINT(1) NOT NULL DEFAULT 1,
last_used BIGINT(15) UNSIGNED DEFAULT 0,
PRIMARY KEY (id), UNIQUE KEY login (account_id))";
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_inapp_add_account($user, $domain, $expiration_date) {
$conn = linphonedb_connect();
$result = linphonedb_query("INSERT INTO " . INAPP_DB_TABLE . "(account_id, expire) VALUES((SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')," . linphonedb_escape($conn, $expiration_date). ")", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_inapp_update_last_used_field($user, $domain, $last_used) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . INAPP_DB_TABLE . " SET last_used=" . linphonedb_escape($conn, $last_used) . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_inapp_update_expiration_date($user, $domain, $expiration_date) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . INAPP_DB_TABLE . " SET expire=" . linphonedb_escape($conn, $expiration_date) . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_inapp_update_trial($user, $domain, $trial) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . INAPP_DB_TABLE . " SET trial=" . linphonedb_escape($conn, $trial) . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_inapp_is_account($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count(*) FROM " . INAPP_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$is_account = $row[0] >= 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $is_account;
}
function db_inapp_is_account_trial($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT trial FROM " . INAPP_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$is_account_trial = $row[0] == 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $is_account_trial;
}
function db_inapp_get_last_used_field($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT last_used FROM " . INAPP_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$last_used_field = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $last_used_field;
}
function db_inapp_get_expiration_date($user, $domain) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT expire FROM " . INAPP_DB_TABLE . " WHERE account_id=(SELECT id FROM " . ACCOUNTS_DB_TABLE . " WHERE login='" . linphonedb_escape($conn, $user) . "' AND domain='" . linphonedb_escape($conn, $domain) . "')", $conn);
$row = linphonedb_fetch($result);
$expiration_date = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $expiration_date;
}
// Google/Android specifics
// Get an access token to access Google APIs
function get_google_access_token() {
$ch = curl_init(GOOGLE_API_OAUTH_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'client_id' => GOOGLE_PROJECT_ID,
'client_secret' => GOOGLE_PROJECT_PASSWORD,
'refresh_token' => GOOGLE_PROJECT_REFRESH_TOKEN,
'grant_type' => "refresh_token",
)));
$result = curl_exec($ch);
curl_close($ch);
$json = json_decode($result, true);
$token = $json["access_token"];
mylog("[GOOGLE] Access token is " . $token);
return $token;
}
// Query Google for the expiration time given the transaction token as described here: https://developers.google.com/android-publisher/api-ref/purchases/subscriptions/get
function get_expiration_for_android_token_and_subscription($token, $subscription) {
$google_access_token = get_google_access_token();
$url = "https://www.googleapis.com/androidpublisher/v2/applications/" . ANDROID_PACKAGE . "/purchases/subscriptions/" . $subscription . "/tokens/" . $token . "?access_token=" . $google_access_token;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$result = curl_exec($ch);
curl_close($ch);
$json = json_decode($result, true);
$expiration = $json["expiryTimeMillis"];
mylog("[GOOGLE] expire timestamp for token = " . $token . " and product id = " . $subscription . " is " . $expiration);
return $expiration . "";
}
// Returns 1 if the payload/signature has been issued by Google.
function check_google_signature($payload, $signature) {
$certFile = fopen(ANDROID_PUB_KEY_PATH, "r");
$cert = fread($certFile, 8192);
fclose($certFile);
$pubKeyId = openssl_get_publickey($cert);
$ok = openssl_verify($payload, base64_decode($signature), $pubKeyId, OPENSSL_ALGO_SHA1);
mylog("[GOOGLE] signature verification result is " . $ok);
return $ok;
}
// End of Google/Android specifics
// Apple/iOS specifics
function get_apple_receipt($payload) {
$ch = curl_init(APPLE_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array('receipt-data' => $payload, 'password' => APPLE_SECRET)));
$result = curl_exec($ch);
curl_close($ch);
mylog("[APPLE] decoded receipt is " . $result);
$json = json_decode($result, true);
$status_code = $json["status"];
if ($status_code == 21007) {
mylog("[APPLE] Error 21007 found, sending receipt to sandbox instead of production");
$ch = curl_init(APPLE_SANDBOX_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array('receipt-data' => $payload, 'password' => APPLE_SECRET)));
$result = curl_exec($ch);
curl_close($ch);
mylog("[APPLE] decoded receipt is " . $result);
$json = json_decode($result, true);
}
return $json;
}
// Returns 1 if the payload/signature has been signed by Apple, else will return the error code as described here: https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html#//apple_ref/doc/uid/TP40010573-CH104-SW1
function check_apple_signature($payload) {
$status = -1;
$status = $payload["status"];
mylog("[APPLE] Status in apple receipt is " . $status);
if ($status == 0) {
return 1;
}
return $status;
}
function parse_apple_receipt_get_expiration($user, $domain, $json) {
$last_used = db_inapp_get_last_used_field($user, $domain);
$days = 0;
$receipt = $json["receipt"];
$in_app = $receipt["in_app"];
foreach($in_app as $item => $value) {
if (array_key_exists("original_purchase_date_ms", $value) and array_key_exists("product_id", $value)) {
$purchase_date = $value["original_purchase_date_ms"];
$product_id = $value["product_id"];
if ($purchase_date > $last_used) {
$days_bought = 0;
if (endswith($product_id, "1_month")) {
$days_bought = 30;
} else if (endswith($product_id, "1_year")) {
$days_bought = 365;
} else {
mylog("[ERROR] Unknown duration for product ID " . $product_id);
continue;
}
if (startswith($product_id, "test.")) {
mylog("[APPLE] Test mode detected, time accelerated (1 month => 1 minute)");
$days_bought /= 43200;
}
if ($days_bought > 0) {
$days = $days + $days_bought;
db_inapp_update_last_used_field($user, $domain, $purchase_date);
}
}
}
}
if ($days <= 0) {
mylog("[WARN] [APPLE] Either no receipt or all receipts have already been consumed");
return 0;
}
$millis = 86400000 * $days;
$now = get_trial_expiration_date();
$expiration = db_inapp_get_expiration_date($user, $domain);
$max = max($now, $expiration);
$expiration_date = $max + $millis;
mylog("[APPLE] Adding " . $days . " days to current expiration date (= " . $millis . " ms). New expiration date is " . $expiration_date);
return $expiration_date;
}
// End of Apple/iOS specifics
// XMLRPC methods
// Returns 1 if the payload/signature has been signed by either Google or Apple, depending on $os.
function check_signature($os, $payload, $signature) {
if (strcmp($os, "google") == 0) {
return check_google_signature($payload, $signature);
} elseif (strcmp($os, "apple") == 0) {
return check_apple_signature($payload);
}
return -2;
}
// args = [username, ha1, [domain]]
function xmlrpc_is_account_trial($method, $args) {
$user = $args[0];
$password = $args[1];
$domain = get_domain($args[2]);
mylog("[XMLRPC] xmlrpc_is_account_trial(" . $user . ", " . $domain . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else {
$hashed_password = hash_password($user, $password, $domain);
$db_hashed_password = db_account_get_password($user, $domain);
if (strcmp($hashed_password, $db_hashed_password) != 0 and strcmp($password, $db_hashed_password) != 0) {
mylog("[ERROR] Password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
}
if (!USE_IN_APP_PURCHASES || !db_inapp_is_account($user, $domain)) {
return "ERROR_NO_EXPIRATION";
}
if (db_inapp_is_account_trial($user, $domain)) {
return "OK";
} else {
return "NOK";
}
}
// args = [username, ha1, [domain]]
function xmlrpc_is_account_expired($method, $args) {
$user = $args[0];
$password = $args[1];
$domain = get_domain($args[2]);
mylog("[XMLRPC] xmlrpc_is_account_expired(" . $user . ", " . $domain . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else {
$hashed_password = hash_password($user, $password, $domain);
$db_hashed_password = db_account_get_password($user, $domain);
if (strcmp($hashed_password, $db_hashed_password) != 0 and strcmp($password, $db_hashed_password) != 0) {
mylog("[ERROR] Password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
}
if (!USE_IN_APP_PURCHASES || !db_inapp_is_account($user, $domain)) {
return "ERROR_NO_EXPIRATION";
}
$expiration = db_inapp_get_expiration_date($user, $domain);
$now_date = new DateTime('now');
$now = $now_date->getTimestamp() * 1000;
if ($now > $expiration) {
return "OK";
} else {
return "NOK";
}
}
// args = [payload, signature]
function xmlrpc_check_payload_signature($method, $args) {
$payload = $args[0];
$signature = $args[1];
mylog("[XMLRPC] xmlrpc_check_payload_signature(payload, signature)");
$result = 0;
$os = "google";
$payloadJson = $payload;
if ($signature == "") {
$payloadJson = get_apple_receipt($payload);
$os = "apple";
}
$result = check_signature($os, $payloadJson, $signature);
if ($result == 1) {
return "OK";
}
return "NOK";
}
// args = [username, ha1, [domain], payload, signature=""]
function xmlrpc_update_expiration_date($method, $args) {
$user = $args[0];
$password = $args[1];
$payload = $args[3];
$signature = $args[4];
$domain = get_domain($args[2]);
mylog("[XMLRPC] xmlrpc_update_expiration_date(" . $user . ", " . $domain . ", payload, signature)");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else if (!db_account_is_activated($user, $domain)) {
mylog("[ERROR] User account " . $user . " / " . $domain . " isn't activated");
return "ERROR_ACCOUNT_NOT_ACTIVATED";
} else {
$hashed_password = hash_password($user, $password, $domain);
$db_hashed_password = db_account_get_password($user, $domain);
if (strcmp($hashed_password, $db_hashed_password) != 0 and strcmp($password, $db_hashed_password) != 0) {
mylog("[ERROR] Password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
}
if (!USE_IN_APP_PURCHASES || !db_inapp_is_account($user, $domain)) {
return "ERROR_NO_EXPIRATION";
}
$result = 0;
$os = "google";
$payloadJson = $payload;
if ($signature == "") {
$payloadJson = get_apple_receipt($payload);
$os = "apple";
}
$result = check_signature($os, $payloadJson, $signature);
if ($result == 1) {
$expiration_date = 0;
if (strcmp($os,"google") == 0) {
$json = json_decode($payload, true);
$token = $json["purchaseToken"];
$subscription = $json["productId"];
$expiration_date = get_expiration_for_android_token_and_subscription($token, $subscription);
} else if (strcmp($os, "apple") == 0) {
$expiration_date = parse_apple_receipt_get_expiration($user, $domain, $payloadJson);
}
if ($expiration_date >= 0) {
if ($expiration_date > 0) {
db_inapp_update_trial($user, $domain, 0);
db_inapp_update_expiration_date($user, $domain, $expiration_date);
if (CUSTOM_HOOKS) {
hook_on_expiration_date_updated($user, $domain, $expiration_date, $payloadJson, $os);
}
return $expiration_date . "";
} else {
return db_inapp_get_expiration_date($user, $domain) . "";
}
} else {
mylog("[ERROR] Expiration is " . $expiration_date);
}
}
mylog("[ERROR] Couldn't verify signature of payload...");
return "ERROR_SIGNATURE_VERIFICATION_FAILED";
}
// args = [username, ha1, [domain]]
function xmlrpc_get_account_expiration($method, $args) {
$user = $args[0];
$password = $args[1];
$domain = get_domain($args[2]);
mylog("[XMLRPC] xmlrpc_get_account_expiration(" . $user . ")");
if (!check_parameter($user)) {
return "ERROR_USERNAME_PARAMETER_NOT_FOUND";
}
if (!db_account_is_existing($user, $domain)) {
mylog("[ERROR] User account " . $user . " doesn't exist");
return "ERROR_ACCOUNT_DOESNT_EXIST";
} else if (!db_account_is_activated($user, $domain)) {
mylog("[ERROR] User account " . $user . " isn't activated");
return "ERROR_ACCOUNT_NOT_ACTIVATED";
} else {
$hashed_password = hash_password($user, $password, $domain);
$db_hashed_password = db_account_get_password($user, $domain);
if (strcmp($hashed_password, $db_hashed_password) != 0 and strcmp($password, $db_hashed_password) != 0) {
mylog("[ERROR] Password doesn't match");
return "ERROR_PASSWORD_DOESNT_MATCH";
}
}
if (!USE_IN_APP_PURCHASES || !db_inapp_is_account($user, $domain)) {
return "ERROR_NO_EXPIRATION";
}
$expiration = db_inapp_get_expiration_date($user, $domain);
return $expiration . "";
}
function xmlrpc_inapp_register_methods($server) {
xmlrpc_server_register_method($server, 'is_account_trial', 'xmlrpc_is_account_trial');// args = [username, ha1, [domain]]
xmlrpc_server_register_method($server, 'is_account_expired', 'xmlrpc_is_account_expired');// args = [username, ha1, [domain]]
xmlrpc_server_register_method($server, 'get_account_expiration', 'xmlrpc_get_account_expiration');// args = [username, ha1, [domain]]
xmlrpc_server_register_method($server, 'update_expiration_date', 'xmlrpc_update_expiration_date');// args = [username, ha1, [domain], payload, [signature]]
xmlrpc_server_register_method($server, 'check_payload_signature', 'xmlrpc_check_payload_signature');// args = [payload, signature]
}
?>

208
src/xmlrpc-sms.php Normal file
View file

@ -0,0 +1,208 @@
<?php
// The following can't be put inside a function...
// So comment it out if not using the OVH SMS API
require __DIR__ . '/vendor/autoload.php';
use \Ovh\Sms\SmsApi;
function db_drop_sms_table() {
$conn = linphonedb_connect();
$create_req = "DROP TABLE IF EXISTS " . SMS_DB_TABLE;
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_create_sms_table() {
$conn = linphonedb_connect();
$create_req = "CREATE TABLE IF NOT EXISTS " . SMS_DB_TABLE . " (
id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT,
phone VARCHAR(64),
last_sms BIGINT(15) UNSIGNED DEFAULT 0,
count TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id), UNIQUE KEY login (phone))";
$result = linphonedb_query($create_req, $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_has_sms_already_been_sent_to($phone) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count(*) FROM " . SMS_DB_TABLE . " WHERE phone='" . linphonedb_escape($conn, $phone) . "'", $conn);
$row = linphonedb_fetch($result);
$already_sent = $row[0] >= 1;
linphonedb_clean($result);
linphonedb_close($conn);
return $already_sent;
}
function db_insert_sms($phone, $time) {
$conn = linphonedb_connect();
$result = linphonedb_query("INSERT INTO " . SMS_DB_TABLE . "(phone, last_sms, count) VALUES('" . linphonedb_escape($conn, $phone) . "', " . linphonedb_escape($conn, $time) . ", 1)", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_get_sms_count($phone) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT count FROM " . SMS_DB_TABLE . " WHERE phone='" . linphonedb_escape($conn, $phone) . "'", $conn);
$row = linphonedb_fetch($result);
$sms_count = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $sms_count;
}
function db_get_last_sms($phone) {
$conn = linphonedb_connect();
$result = linphonedb_query("SELECT last_sms FROM " . SMS_DB_TABLE . " WHERE phone='" . linphonedb_escape($conn, $phone) . "'", $conn);
$row = linphonedb_fetch($result);
$last_sms = $row[0];
linphonedb_clean($result);
linphonedb_close($conn);
return $last_sms;
}
function db_update_sms($phone, $time, $count) {
$conn = linphonedb_connect();
$result = linphonedb_query("UPDATE " . SMS_DB_TABLE . " SET last_sms=" . linphonedb_escape($conn, $time) . ", count=" . linphonedb_escape($conn, $count) . " WHERE phone='" . linphonedb_escape($conn, $phone) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function db_delete_sms($phone) {
$conn = linphonedb_connect();
$result = linphonedb_query("DELETE FROM " . SMS_DB_TABLE . " WHERE phone='" . linphonedb_escape($conn, $phone) . "'", $conn);
linphonedb_clean($result);
linphonedb_close($conn);
}
function send_sms_ovh($phone, $key, $lang) {
if (!SMS_API_ENABLED) {
mylog("[WARN][SMS] SMS API disabled");
return "WARNING_SMS_API_DISABLED";
}
$sms = new SmsApi(SMS_OVH_API_KEY, SMS_OVH_API_SECRET, SMS_OVH_ENDPOINT, SMS_OVH_CONSUMER_KEY);
$accounts = $sms->getAccounts();
$sms->setAccount($accounts[0]);
if (SMS_USE_SENDER) {
$senders = $sms->getSenders();
/* The account must be validated in the OVH interface and by OVH itself */
if (count($senders) == 0) {
mylog("[WARN][SMS] No sender found, creating one " . SMS_OVH_SENDER . " / " . SMS_OVH_REASON . " : " . SMS_OVH_DESC);
$sms->addSender(SMS_OVH_SENDER, SMS_OVH_REASON, SMS_OVH_DESC);
$senders = $sms->getSenders();
}
}
$message = $sms->createMessage();
if (SMS_USE_SENDER && count($senders) > 0) {
foreach ($senders as $sender) {
if ($sender == SMS_OVH_SENDER) {
if ($sms->checkSender($sender)) {
// Check if sender exists and is valid, otherwise it will create an exception and sms won't be sent
mylog("[SMS] Found valid sender " . $sender . ", using it");
$message->setSender($sender);
break;
} else {
mylog("[ERROR][SMS] Found sender " . $sender . " but it is not valid");
}
}
}
}
$message->addReceiver($phone);
$message->setIsMarketing(FALSE);
$text = get_sms_string_for_lang($lang);
$text = str_replace("#CODE#", $key, $text);
$result = $message->send($text);
$credits_removed = $result['totalCreditsRemoved'];
mylog("[SMS] " . $credits_removed . " credit removed");
$invalid_receiver = $result['invalidReceivers'];
$valid_receiver = $result['validReceivers'];
if (count($invalid_receiver) > 0) {
mylog("[ERROR][SMS] phone number " . $phone . " seems invalid");
} else if (count($valid_receiver) > 0) {
mylog("[SMS] " . $text . " sent to " . $phone);
} else {
mylog("[WARN][SMS] Both valid and invalid receiver lists are empty...");
}
}
function send_sms_legacy($phone, $password) {
if (!SMS_API_ENABLED) {
mylog("[WARN][SMS] SMS API disabled");
return "WARNING_SMS_API_DISABLED";
}
$url = SMS_API_URL;
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FAILONERROR, false);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_USERPWD, SMS_API_USERNAME . ":" . SMS_API_PASSWORD);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(array(
'mobile' => $phone,
'password' => $password,
)));
$result = curl_exec($ch);
mylog("[SMS] SMS confirmation sent to " . $phone . " using password " . $password . ", request result is " . $result);
curl_close($ch);
}
function send_sms($phone, $key, $lang, $password) {
if (!SMS_API_ENABLED) {
mylog("[WARN][SMS] SMS API disabled");
return "WARNING_SMS_API_DISABLED";
}
if (startswith($phone, TESTS_PHONE_PREFIX)) {
mylog("[ERROR][SMS] Not sending sms to fake number used for tests purposes: " . $phone);
return "ERROR_NON_TEST_ACCOUNTS_UNAUTHORIZED";
}
$now_date = new DateTime('now');
$now = $now_date->getTimestamp() * 1000;
if (db_has_sms_already_been_sent_to($phone)) {
$count = db_get_sms_count($phone);
$time = db_get_last_sms($phone);
$diff = $now - $time;
if ($count >= SMS_COUNT_LIMIT_IN_PERIOD and $diff < SMS_TIME_PERIOD) {
mylog("[ERROR][SMS] Last sms was sent at " . $time . ", time elapsed since then is " . $diff . "ms which is less than the configured time period " . SMS_TIME_PERIOD);
return "ERROR_MAX_SMS_EXCEEDED";
} else if ($diff >= SMS_TIME_PERIOD) {
db_update_sms($phone, $now, 1);
} else {
$count = $count + 1;
db_update_sms($phone, $now, $count);
}
} else {
db_insert_sms($phone, $now);
}
if (SMS_OVH_API_KEY != NULL && SMS_OVH_API_KEY != "" && SMS_OVH_API_SECRET != NULL && SMS_OVH_API_SECRET != "" && SMS_OVH_CONSUMER_KEY != NULL && SMS_OVH_CONSUMER_KEY != "" && SMS_OVH_ENDPOINT != NULL && SMS_OVH_ENDPOINT != "") {
try {
send_sms_ovh($phone, $key, $lang);
return "OK";
} catch (Exception $e) {
mylog("[ERROR][OVH-SMS] Exception: " . $e->getMessage());
}
} else if (SMS_API_URL != NULL && SMS_API_URL != "" && SMS_API_USERNAME != NULL && SMS_API_USERNAME != "" && SMS_API_PASSWORD != NULL && SMS_API_PASSWORD != "") {
send_sms_legacy($phone, $password);
return "OK";
} else {
mylog("[ERROR][SMS] No SMS API configured, discarding sms...");
return "OK";
}
return "ERROR_CANT_SEND_SMS";
}
?>

466
src/xmlrpc.conf Normal file
View file

@ -0,0 +1,466 @@
<?php
include "internationalization.conf";
/* ### Global configuration ### */
/*
* If set to True, each line will be flagged as overwrite, otherwise none of them will be flagged.
*
* Default value: False
*/
define("REMOTE_PROVISIONING_OVERWRITE_ALL", False);
/*
* The SIP domain to use to hash passwords.
*
* Default value: sip.linphone.org
*/
define("SIP_DOMAIN", "sip.linphone.org");
/*
* The domain to use for digest auth.
*
* Default value: sip.linphone.org
*/
define("AUTH_REALM", "sip.linphone.org");
/*
* If true, when account is created, the password will be generated automatically (see below).
* Otherwise it has to be given as the last parameter of the create_account method call.
*
* Default value: False
*/
define("GENERATE_PASSWORD_ENABLED", False);
/*
* A string with each character allowed in the password generation.
*
* Default value: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789``-=~!@#$%^&*()_+,./<>?;:[]{}\|
*/
define("GENERATED_PASSWORD_CHARACTERS", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789``-=~!@#$%^&*()_+,./<>?;:[]{}\|");
/*
* The length of the passwords that will be generated.
*
* Default value: 8
*/
define("GENERATED_PASSWORD_LENGTH", 8);
/*
* If set to True, a created account will automatically be activated and it's expiration date set to now + TRIAL_DURATION_DAYS,
* otherwise expiration date for trial will be set when account is activated via a different xml rpc call.
*/
define('AUTO_ACTIVATE_ACCOUNT', False);
/*
* If set to True, a created account will be flagged as trial with an expiration date set in TRIAL_DURATION_DAYS days in the future.
*/
define ('USE_IN_APP_PURCHASES', False);
/*
* This value determines the number of days for trial starting when the account will be activated.
*
* Default value: 365
*/
define('TRIAL_DURATION_DAYS', 365);
/*
* This values sets the default timezone to use when computing the expiration dates when using inapp purchases
*
* Default value: 'Europe/Paris'
*/
define('DEFAULT_TIMEZONE', 'Europe/Paris');
/*
* Send an email to activate the account when it is created.
*/
define('SEND_ACTIVATION_EMAIL', True);
/*
* Send a sms to activate the phone account when it is created.
*/
define('SEND_ACTIVATION_SMS', True);
/*
* If false, creating an account with an email that is already used for another account will trigger an error
*/
define('ALLOW_SAME_EMAILS_ON_MULTILPLE_ACCOUNTS', True);
/*
* If true, when an account creation request is received for an existing number, assumes recover procedure
*/
define('RECOVER_ACCOUNT_IF_EXISTS', False);
/*
* If true, more features are available for test purposes
*
* Default value: False
*/
define('ALLOW_TEST_ACCOUNTS', False);
/* ### Logs configuration ### */
/*
* Whever or not to log each function called.
* Passwords are never logged.
*
* Default value: True
*/
define("LOGS_ENABLED", True);
/*
* The file in which to log XMLRPC calls.
*
* Default value: xmlrpc.log
*/
define("LOG_FILE", "xmlrpc.log");
/* ### Authentication configuration ### */
/*
* Realm used for digest authentication
*
* Default value: sip.example.org
*/
define("AUTH_REALM", "sip.example.org");
/* ### Database configuration ### */
/*
* If set to True, use Mysqli API instead of Mysql one.
*
* Default value: False
*/
define("USE_MYSQLI", False);
/*
* Whether or not use use persistent connections.
* ! It only works if USE_MYSQLI is set to True !
* See http://php.net/manual/fr/function.mysqli-connect.php and http://php.net/manual/fr/mysqli.quickstart.connections.php
*
* Default value: False
*/
define("USE_PERSISTENT_CONNECTIONS", False);
/*
* The host on which the database is located.
*
* Default value: localhost
*/
define("DB_HOST", "localhost");
/*
* The database username.
*
* Default value: flexisip
*/
define("DB_USER", "flexisip");
/*
* The database user's password.
*
* Default value:
*/
define("DB_PASSWORD", "");
/*
* The name of the database.
*
* Default value: flexisip
*/
define("DB_NAME", "flexisip");
/*
* The name of the accounts table.
*
* Default value: accounts
*/
define("ACCOUNTS_DB_TABLE", "accounts");
/*
* The name of the accounts_algo table.
*
* Default value: accounts_algo
*/
define("ACCOUNTS_ALGO_DB_TABLE", "accounts_algo");
/*
* The name of the inapp table.
* It is used to store informations about in-app purchases, accounts expiration, etc...
*
* Default value: inapp_purchases
*/
define("INAPP_DB_TABLE", "inapp_purchases");
/*
* The name of the aliases table.
* It is used to store links between an alias (phone number, facebook id, google email, ...) and a SIP address
*
* Default value: aliases
*/
define("ALIAS_DB_TABLE", "aliases");
/*
* The name of the devices table.
* It is used to store hardware information about devices running linphone
*
* Default value: devices
*/
define("DEVICES_DB_TABLE", "devices");
/*
* The name of the sms table.
* It is used to keep track of sent SMS
*
* Default value: sms
*/
define("SMS_DB_TABLE", "sms");
/*
* The delay in minutes before test account expiration.
* It is used to delete old test accounts from database;
*
* Default value: 180
*/
define("EXPIRATION_DELAY", 180);
/* ### Apple/ioS configuration ### */
/*
* The URL to use to validate an Apple in app purchase receipts.
*
* Default value: https://buy.itunes.apple.com/verifyReceipt
*/
define("APPLE_URL", "https://buy.itunes.apple.com/verifyReceipt");
/*
* The URL to use to validate an Apple in app purchase receipts while app is in development.
*
* Default value: https://buy.itunes.apple.com/verifyReceipt
*/
define("APPLE_SANDBOX_URL", "https://sandbox.itunes.apple.com/verifyReceipt");
/*
* The shared secret for your application.
* Used to validate in app purchase receipts.
*
* Default value:
*/
define("APPLE_SECRET", "");
/* ### Google/Android configuration ### */
/*
* The package name of your Android application.
* Used to validate in app purchase receipts.
*
* Default value: org.linphone
*/
define("ANDROID_PACKAGE", "org.linphone");
/*
* The path to the public key generated by the Android Play Store.
* See the documentation to know how to get it.
*
* Default value: google.pem
*/
define("ANDROID_PUB_KEY_PATH", "google.pem");
/*
* The URL to use to get the authentication token to make calls to Google API server.
* Used to validate in app purchase receipts.
*
* Default value: https://accounts.google.com/o/oauth2/token
*/
define("GOOGLE_API_OAUTH_URL", "https://accounts.google.com/o/oauth2/token");
/*
* The project ID with the access to the Android Developer Console API
* Used to validate in app purchase receipts.
*
* Default value:
*/
define("GOOGLE_PROJECT_ID", "");
/*
* The previous project ID's password
* Used to validate in app purchase receipts.
*
* Default value:
*/
define("GOOGLE_PROJECT_PASSWORD", "");
/*
* The refresh token generated by the Google Developer server.
* See documentation to know how to get it.
* Used to validate in app purchase receipts.
*
* Default value:
*/
define("GOOGLE_PROJECT_REFRESH_TOKEN", "");
/* ### Email configuration ### */
/*
* Whever or not enable the send email feature.
* Used to send link to generate random password if user forgot it, or the newly generated email once the link has been clicked.
*
* Default value: False
*/
define("EMAIL_ENABLED", False);
/*
* The website address to display in the email header.
*
* Default value: https://linphone.org
*/
define("EMAIL_SITE", "https://linphone.org");
/*
* The link to open when click on activation
*
* Default value: www.linphone.org
*/
define("EMAIL_ACTIVATION_LINK", "www.linphone.org");
/*
* The FROM address to set in the email header.
*
* Default value: no.reply@linphone.org
*/
define("EMAIL_FROM_ADDR", "no.reply@linphone.org");
/*
* The FROM display name to set in the email header.
*
* Default value: No reply at Linphone.org
*/
define("EMAIL_FROM_NAME", "No reply at Linphone.org");
/*
* The subject of the activation account email.
*/
define("EMAIL_ACTIVATION_SUBJECT", "Start your sip.linphone.org service");
/*
* The body (as text) of the activation account email.
*/
define("EMAIL_ACTIVATION_BODY", "Hello,\nActivation pending for using your Linphone account.\nPlease use the link bellow to activate your account :\n\n%link%\n\nRegards,\nThe Linphone team.\n");
/*
* The body (as html) of the activation account email.
*/
define("EMAIL_ACTIVATION_BODY_HTML", '<html><head><title>Start your sip.linphone.org service</title></head><body><p>Hello,</p><p>Activation pending for using your Linphone account.<br />Please use the link bellow to activate your account :</p><p><a href="%link%">%link%</a></p><p>&nbsp;</p><p>Regards,<br />The Linphone team.</p></body></html>');
/* ### SMS API configuration ### */
/*
* Whever or not enable the send SMS feature.
* Used to verify phone number when used as SIP username.
*
* Default value: False
*/
define("SMS_API_ENABLED", False);
/*
* The application key for OVH SMS platform
*
* Default value:
*/
define("SMS_OVH_API_KEY", "");
/*
* The application secret for OVH SMS platform
*
* Default value:
*/
define("SMS_OVH_API_SECRET", "");
/*
* The consumer key for OVH SMS platform
*
* Default value:
*/
define("SMS_OVH_CONSUMER_KEY", "");
/*
* The sender alias for OVH SMS
*
* Default value: "Linphone"
*/
define("SMS_OVH_SENDER", "Linphone");
/*
* Whever or not to use a sender to send the SMS.
* When using sender you can customize the name of the sender, otherwise it will be a phone number.
* To disable for clients using our own OVH SMS account.
*
* Default value: True
*/
define("SMS_USE_SENDER", True);
/*
* The sender reason for OVH SMS
*
* Default value: "created Linphone SMS sender"
*/
define("SMS_OVH_REASON", "created Linphone SMS sender");
/*
* The sender description for OVH SMS
*
* Default value: "Linphone SMS sender"
*/
define("SMS_OVH_DESC", "Linphone SMS sender");
/*
* The template to use to send SMS to the US
*
* Default value: "Your Linphone validation code is #CODE#"
*/
define("SMS_OVH_US_TEMPLATE", "Your Linphone validation code is #CODE#");
/*
* The OVH endpoint
*
* Default value: ovh-eu
*/
define("SMS_OVH_ENDPOINT", "ovh-eu");
/*
* The URL at which the SMS API is available.
*
* Default value:
*/
define("SMS_API_URL", "");
/*
* The username to authenticate to the SMS API if needed.
*
* Default value:
*/
define("SMS_API_USERNAME", "");
/*
* The username's password to authenticate to the SMS API if needed.
*
* Default value:
*/
define("SMS_API_PASSWORD", "");
/*
* The period of time (in milli seconds) over which we compute the number of sent sms
*
* Default value: 86400000 (24 hours)
*/
define("SMS_TIME_PERIOD", 86400000);
/*
* The maximum number of allowed SMS to be sent over the period
*
* Default value: 3
*/
define("SMS_COUNT_LIMIT_IN_PERIOD", 3);
?>

135
src/xmlrpc.php Normal file
View file

@ -0,0 +1,135 @@
<?php
define("PATH_TO_CONFIG", "/etc/flexisip-account-manager/");
include PATH_TO_CONFIG . "xmlrpc.conf";
include "logging.php";
include "utilities.php";
include "hooks.php";
include "authentication.php";
if (USE_MYSQLI) {
include "mysqli-db.php";
} else {
include "mysql-db.php";
}
if (SMS_API_ENABLED) {
include "xmlrpc-sms.php";
}
include "xmlrpc-accounts.php";
include "xmlrpc-aliases.php";
include "xmlrpc-inapp.php";
include "xmlrpc-devices.php";
include "xmlrpc-compatibility.php";
date_default_timezone_set(DEFAULT_TIMEZONE);
mylog("[DEBUG] Timezone set to " . DEFAULT_TIMEZONE);
$request = file_get_contents("php://input");
if (empty($request)) mylog("[DEBUG] request is empty");
$server = xmlrpc_server_create();
if (!$server) die("Couldn't create server");
if (!$request) {
if ($argc >= 2) {
$arg1 = $argv[1];
if (strcmp($arg1, "create_tables") == 0) {
db_create_inapp_table();
db_create_alias_table();
db_create_accounts_table();
db_create_accounts_algo_table();
db_create_devices_table();
db_create_sms_table();
echo "Tables have been created!\r\n";
} else if (strcmp($arg1, "delete_tables") == 0) {
db_drop_inapp_table();
db_drop_alias_table();
db_drop_accounts_table();
db_drop_accounts_algo_table();
db_drop_devices_table();
db_drop_sms_table();
echo "Tables have been deleted!\r\n";
} else if (strcmp($arg1, "create_algo_table") == 0) {
db_create_accounts_algo_table();
echo "Algo table has been created!\r\n";
} else if (strcmp($arg1, "drop_algo_table") == 0) {
db_drop_accounts_algo_table();
echo "Algo table has been deleted!\r\n";
}
exit;
}
mylog("[DEBUG] No request found");
exit;
}
// XMLRPC requests that do not require authentication
$unauthenticated_requests = array(
// account
0 => 'create_email_account',
1 => 'create_phone_account',
2 => 'get_confirmation_key',
3 => 'activate_email_account',
4 => 'activate_phone_account',
5 => 'recover_phone_account',
6 => 'get_phone_number_for_account',
7 => 'is_account_activated',
// aliases
8 => 'is_alias_used',
// inapp
9 => 'check_payload_signature',
// misc
10 => 'add_ec_calibration_result',
// compatibility
11 => 'create_account',
12 => 'create_account_with_useragent',
);
$headers = getallheaders();
$xml = simplexml_load_string($request);
$request_type = $xml->methodName;
// Get authentication header if there is one
if (!empty($headers['Auth-Digest'])) {
mylog("Auth-Digest = " . $headers['Auth-Digest']);
$authorization = $headers['Auth-Digest'];
} elseif (!empty($headers['Authorization'])) {
mylog("Authorization = " . $headers['Authorization']);
$authorization = $headers['Authorization'];
}
// Authentication
if (in_array($request_type, $unauthenticated_requests) == FALSE) {
if (!empty($authorization)) {
$authentication_status = authenticate(AUTH_REALM);
if ($authentication_status == TRUE) {
mylog("[DEBUG] Authentication successful for " . $headers['From']);
} else {
mylog("[DEBUG] Authentication failed for " . $headers['From']);
request_authentication(AUTH_REALM);
}
} else {
mylog("[DEBUG] No authentication header for " . $headers['From']);
request_authentication(AUTH_REALM);
}
}
xmlrpc_accounts_register_methods($server);
xmlrpc_aliases_register_methods($server);
xmlrpc_inapp_register_methods($server);
xmlrpc_misc_register_methods($server);
xmlrpc_compatibility_register_methods($server);
if ($request) {
$options = array('output_type' => 'xml', 'version' => 'auto');
echo xmlrpc_server_call_method($server, $request, null, $options);
}
?>