--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/user_functions.php');
+require_once('lib/number_functions.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+require_once('lib/alias_functions.php');
+
+$config = get_config();
+
+$config['sql_link'] = @mysql_connect(
+ $config['sql_server'],
+ $config['sql_username'],
+ $config['sql_password']
+);
+if ( !$config['sql_link'] )
+{
+ print json_encode( array( 'response' => 'failed', 'cause' => 'error', 'detail' => 'Database connection failed.'));
+ exit;
+}
+
+//*************************************************************************************
+ switch ( $_SERVER['PATH_INFO'] )
+ {
+ case "/list":
+ break;
+ case "/add":
+ if ( array_key_exists( 'destination', $_GET)
+ //&& array_key_exists( 'domain', $_GET )
+ && array_key_exists( 'alias_username', $_GET )
+ && array_key_exists( 'alias_domain', $_GET ) )
+ {
+ $alias_username = $_GET['alias_username'];
+ $alias_domain = $_GET['alias_domain'];
+
+ if ( !verify_sipadress($_GET['destination']) )
+ {
+ // TODO: Provide a better response..
+ print json_encode ( array( 'response' => 'invalid', 'cause' => 'destination' ) );
+ break;
+ }
+ if ( !verify_sipadress( $alias_username . "@" . $alias_domain) )
+ {
+ // TODO: Provide a better response..
+ print json_encode ( array( 'response' => 'invalid', 'cause' => 'alias' ) );
+ break;
+ }
+
+ list ( $dest_username, $dest_domain ) = split_sipaddress( $_GET['destination']);
+
+ if ( (!$dest_username)||(!$dest_domain))
+ {
+ // TODO: Provide a better response..
+ print json_encode ( array( 'response' => 'invalid', 'cause' => 'alias' ) );
+ break;
+ }
+ if ( !is_kamailio_domain( $alias_domain ) )
+ {
+ print json_encode ( array( 'response' => 'invalid', 'cause' => 'nxdomain' ) );
+ break;
+ }
+ if ( is_kamailio_domain( $dest_domain) && ( !is_kamailio_subscriber($dest_username, $dest_domain) ) )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'Requesting a local alias, but there is no such user'));
+ break;
+ }
+ if ( verify_e164( $alias_username ) && is_kamailio_subscriber($dest_username, $dest_domain) )
+ {
+ $t = get_e164_alias( $dest_username, $dest_domain );
+ if ( $t )
+ {
+ print json_encode ( array(
+ 'response' => 'failed',
+ 'cause' => 'exists',
+ 'detail' => 'User already has E164 number alias',
+ 'alias' => $t ));
+ break;
+ }
+ }
+ if ( alias_exists ( $alias_username, $alias_domain ) )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'exists', 'detail' => 'The requested alias is already present.'));
+ break;
+ }
+ if ( add_alias( $alias_username, $alias_domain, $dest_username, $dest_domain ) )
+ {
+ print json_encode( array ('response' => 'ok',
+ 'alias' => $alias_username . "@" . $alias_domain,
+ 'destination' => $dest_username . "@" . $dest_domain ));
+ break;
+ }
+ print json_encode ( array ( 'response' => 'error' ));
+ break;
+ }
+ else
+ {
+ print json_encode ( array( 'response' => 'invalid') );
+ }
+ break;
+ case "/remove":
+ if ( array_key_exists( 'alias_username', $_GET )
+ && array_key_exists( 'alias_domain', $_GET ) )
+ {
+ $alias_username = $_GET['alias_username'];
+ $alias_domain = $_GET['alias_domain'];
+
+ if ( !verify_sipadress( $alias_username . "@" . $alias_domain) )
+ {
+ // TODO: Provide a better response..
+ print json_encode ( array( 'response' => 'invalid', 'cause' => 'address', 'detail' => 'Not a valid SIP address' ) );
+ break;
+ }
+ if ( ! alias_exists ( $alias_username, $alias_domain ) )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'The requested alias does not exist.'));
+ break;
+ }
+ if ( remove_alias( $alias_username, $alias_domain ) )
+ {
+ print json_encode( array ('response' => 'ok',
+ 'alias' => $alias_username . "@" . $alias_domain));
+ break;
+ }
+ print json_encode ( array ( 'response' => 'error' ));
+ break;
+ }
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+ default:
+ print json_encode ( array( 'response' => 'invalid') );
+ }
+mysql_close( $config['sql_link'] );
+?>
--- /dev/null
+All API nodes currently use GET requests for all parameters.
+All API nodes return JSON data, and all results will contain
+a 'response' element. The 'response' may be set to:
+ * 'ok' on success. Further contents depend on the node requested.
+ * 'invalid' on invalid requests. A 'cause' element should give a
+ keyword denoting what was invalid about the request...
+ * 'failed' on simple failure. A 'cause' element is supplied, with a
+ failure-keyword (e.g. 'nonexistant', 'inuse', 'dbfail').
+ A final 'detail' is commonly included, with a verbose desctiption.
+ * 'error' on critical failure. Regard this as a soft form of "500 server error"...
+
+List of API nodes:
+---------------------
+
+user/get?username=foo&domain=bar
+ Return User information, not including hardphone association.
+ Possible extension: allow user/get?address=foo@bar
+
+user/list
+user/list?search=foo@bar
+ Return a list of users in user@domain format.
+ If the search parameter is included, a globbing search is performed,
+ and only matches are listed.
+
+user/add_local?username=foo&domain=bar&displayname=baz&email=qux@zef.tld
+ Adds a user account to both Kamailio and provisioning,
+ if the username@domain is nonexistant, and the domain is local (handled by Kamailio).
+ The password for the user is auto-generated.
+ Returns a full user object, the same form as user/get
+
+user/add_remote?username=foo&domain=bar&password=S3cr3t&displayname=baz®istrar=sip.example.com
+ optionals: &r_port=5060&proxy=sipproxy.example.com&p_port=5060&authid=realfoo&dialplan=(.*)&linetext=lalala
+ Adds a provisioning user for a remote SIP account. This allows locally provisioned hardphones
+ to be associated with non-local SIP accounts.
+ Returns a full user object, the same form as user/get
+
+user/remove?username=foo&domain=bar
+ Removes user account from Kamailio, if present, and removes user from provisioning.
+ Will fail if user has associated hardphones, remove phones before removing user.
+ Returns 'ok' on success.
+
+user/gen_pw
+ Test-node, generates a random password on the same form as that used
+ by user/add_local. May be used with the to-be-implemented change_pw node.
+
+user/change_pw?username=foo&domain=bar&password=baz
+ Not implemented!
+
+user/update?username=foo&domain=bar
+ optionals: &displayname=baz&dialplan=(.*)&linetext=lalala
+ Not implemented! Will only be implemented for Local users.
+ Remote users will have to be removed, and re-added.
+
+phone/get?mac=f00ba2ba5c00
+phone/get?user=foo@bar.baz
+ Returns a list of associated elements. If MAC address is given as parameter,
+ a list of associated user@domain addresses is retruned. If user@domain is
+ given as parameter, a list of associated mac addresses is returned.
+
+phone/list?search=f00ba2
+ Returns the list of registered MAC addresses. If the (optional) search
+ parameter is included, the list is the result of a globbing search,
+ a subset matching on the MAC addresses.
+
+phone/add?mac=f00ba2ba5c00&username=foo&domain=bar.bz
+phone/add?mac=f00ba2ba5c00&user=foo@bar.bz
+ Adds a phone-to-user association to Provisioning. The user may
+ be provided as 'username' and 'domain' in separate parameters,
+ or as 'user' in sip-address form. The MAC address must be valid,
+ in plain-hex or colon-separated form, and the user must
+ match a valid user registration.
+ Returns the mac+user combinations on success.
+
+phone/remove?mac=f00ba2ba5c00&username=foo&domain=bar.bz
+phone/remove?mac=f00ba2ba5c00&user=foo@bar.bz
+ Deletes a phone-to-user association. Required parameters
+ and formats are identical to the phone/add node.
+ Returns 'ok' on success.
+
+
+numbers/list
+numbers/list?search=1234
+numbers/list?random=true
+ optional: limit=n
+ Gives a list of entries in the phone number pool.
+ With no parameters, the entire (ordered) list of available numbers is returned.
+ With 'search', the result of a pre-post globbing search is returned
+ With 'random', a list of entries is returned unordered (random order)
+ For all nodes, 'limit' may be a numeric limit of desired results.
+
+numbers/add_range?start=%2B471234&end=%2B47123456
+ The start and end must have identical lead-in, and start must be less than end.
+ Both numbers must be given in E164 format, remember to urlencode the plus (+ -> %2B)
+ The numeric component of both will be extracted, and all numbers in the range
+ will be iteratively added to the pool, including the ending number.
+ Returns 'ok' on success.
+
+numbers/add?number=%2B4761123456
+ Adds the given number to the pool, after verifying that the number is in
+ valid E164 format, and that the number does not already exist in the pool.
+ Returns 'response' 'ok' and "Added <number>" in 'detail' on success.
+
+numbers/remove?number=%2B4761123456
+ Removes/pulls a number from the pool.
+ The number must be a valid e164 number, and must be present in the pool.
+ Returns 'response' as 'ok' with 'number' set to the number that was pulled from the pool.
+
+numbers/inpool?number=%2B4761123456
+ A debug-node, may disappear at any time ^_^
+ Tests if the given E164 number is in the pool.
+ Returns 'response' = 'ok' with 'number' = '<number>' If the number is in the pool
+ Returns response = 'failed' with 'cause' = 'nonexistant' if not.
+
+
+alias/list
+ Currently not implemented.
+
+alias/user?destination=foo@bar.bz
+alias/user?destination=foo@bar.bz&e164=true
+ Currently not implemented
+
+alias/add?alias_username=foo&alias_domain=bar.bz&destination=bar@qux.zx
+ Add an alias specified by alias_username and alias_domain that
+ points to the destination SIP-adress.
+
+ On success, 'ok' is returned, with 'alias' and 'destination' set
+ to the resulting alias and destination adresses.
+
+ Returns 'invalid' with 'cause' = 'destination' or 'alias' if the
+ resulting alias-adress or destination are invalid SIP-adresses.
+ Returns 'failed' with 'cause' = 'nxdomain' if the given alias domain
+ is not a Kamailio domain.
+ Returns 'failed' with 'cause' = 'exists' if the alias is an E164 number
+ and the user already has an E164 alias registered.
+ Returns 'failed' with 'cause' = 'nonexistant' when an alias for a local
+ domain is requested, but the local subscriber does not exist.
+ Returns 'failed' with 'cause' = 'exists' for aliases that already exists,
+ (and aliases that overlaps SIP-accounts - not implemented).
+
+
+alias/remove?alias_username=foo&alias_domain=bar.bz
+ Removes the alias given by alias_username and alias_domain.
+
+ Returns 'ok' with the removed alias adress as 'alias' on success
+ Returns 'failed' with 'cause' = 'nonexistant' it the alias does not exists.
+ Returns 'invalid' with 'cause' = 'address' if the given alias is not a valid SIP adress.
+
+TODO list:
+---------------------
+Planned, but not implemented nodes/functions:
+ user/change_pw?username=foo&domain=bar&password=baz
+ user/update?username=foo&domain=bar
+ alias/list
+ alias/user?destination=foo@bar.bz
+
+Parameter updates:
+ Update nodes where username=foo&domain=bar.bz is used, to also support user=foo@bar.bz
+ user/get
+ user/add_local
+ user/add_remote
+ user/remove
+ user/change_pw
+ user/update
+ phone/get (reverse, needs username & domain param)
+ alias/add
+ alias/remove
+
+authentication-mechanism :)
--- /dev/null
+<?php
+
+require_once('lib/auth.php');
+function get_config()
+{
+ return array(
+ 'sql_server' => 'localhost',
+ 'sql_username' => 'dbusername',
+ 'sql_password' => 'dbuserpass',
+ 'kamailio_db' => 'kamailio',
+ 'kamailio_domain_table' => 'domain',
+ 'kamailio_subscriber_table' => 'subscriber',
+ 'kamailio_alias_table' => 'dbaliases',
+ 'provision_db' => 'provision',
+ 'provision_users_table' => 'users',
+ 'provision_phones_table' => 'phones',
+ 'provision_servers_table' => 'servers',
+ 'numbers_table' => 'number_pool',
+ 'standard_dialplan' => '(*xx*.|xxx.)',
+ 'default_domain' => 'hig.no',
+ 'permit_multiple_e164alias' => false,
+ );
+}
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+
+
+function get_destination ( $alias_username, $alias_domain )
+{
+ global $config;
+}
+function get_aliases ( $dest_username, $dest_domain )
+{
+ global $config;
+
+ $aliases = array();
+ $query = sprintf("SELECT CONCAT( alias_username, '@', alias_domain) AS alias FROM %s"
+ . " WHERE username = '%s' AND domain = '%s'",
+ $config['kamailio_alias_table'], $dest_username, $dest_domain);
+ $result = sql_dbquery( $config['kamailio_db'], $query);
+ // If result is empty, there was either an SQL error, or simply no results.
+ // At this point, no error checking is performed, instead the empty array is returned.
+ if ( ! $result ) return $aliases;
+
+ while ( $row = mysql_fetch_assoc( $result ) )
+ {
+ array_push( $aliases, $row['alias'] );
+ }
+ return $aliases;
+}
+
+function get_e164_alias( $dest_username, $dest_domain )
+{
+ global $config;
+ $cur_aliases = get_aliases( $dest_username, $dest_domain );
+ foreach ( $cur_aliases as $testalias )
+ {
+ list( $tau, $foo ) = split_sipaddress( $testalias );
+ if ( verify_e164 ( $tau ) ) return $testalias;
+ }
+ return false;
+
+}
+function alias_exists( $alias_username, $alias_domain )
+{
+ global $config;
+ $query = sprintf("SELECT COUNT(*) AS num FROM %s WHERE alias_username = '%s' AND alias_domain = '%s'",
+ $config['kamailio_alias_table'],
+ sql_clean($alias_username), sql_clean($alias_domain));
+ $result = sql_dbquery($config['kamailio_db'], $query);
+
+ if ( !$result ) return true; // This is an error. Better to fail claiming alias exists...
+ $row = mysql_fetch_row($result);
+ if ( !$row ) return true; // This is an error. Better to fail claiming alias exists...
+ $num_r = $row[0];
+ if ( $num_r == 1 )
+ {
+ $query = sprintf("SELECT CONCAT(username, '@', domain ) AS destination FROM %s WHERE alias_username = '%s' AND alias_domain = '%s'",
+ $config['kamailio_alias_table'],
+ sql_clean($alias_username), sql_clean($alias_domain));
+ $result = sql_dbquery($config['kamailio_db'], $query);
+ $row = mysql_fetch_row($result);
+ if ( is_string( $row[0] ) ) return $row[0];
+ return true; // Failure mode..
+ }
+
+ return false;
+}
+
+function add_alias( $alias_username, $alias_domain, $dest_username, $dest_domain )
+{
+ global $config;
+ // The following will, in normal cases, lead to a double-test, and double the number of queries...
+ // Defensive comment: better safe than sorry :P
+ if ( alias_exists( $alias_username, $alias_domain ) )
+ return false;
+
+ $query = sprintf("INSERT INTO %s ( alias_username, alias_domain, username, domain) VALUES ('%s','%s','%s','%s')",
+ $config['kamailio_alias_table'],
+ sql_clean($alias_username), sql_clean($alias_domain),
+ sql_clean($dest_username), sql_clean($dest_domain));
+
+ return sql_dbexec( $config['kamailio_db'], $query);
+
+}
+
+function remove_alias ( $alias_username, $alias_domain )
+{
+ global $config;
+ if (! alias_exists ( $alias_username, $alias_domain ) ) return false;
+ $query = sprintf ("DELETE FROM %s WHERE alias_username = '%s' AND alias_domain = '%s'",
+ $config['kamailio_alias_table'],
+ sql_clean( $alias_username ),
+ sql_clean( $alias_domain ));
+ return sql_dbexec( $config['kamailio_db'], $query);
+
+}
+
+?>
--- /dev/null
+<?php
+
+function token_auth( )
+{
+ return true;
+}
+
+if (! token_auth() )
+{
+ print json_encode( array( 'response' => 'failed', 'cause' => 'unauthorized', 'description' => 'Not authorized') );
+ exit;
+}
+?>
--- /dev/null
+<?php
+
+function check_email_address($email) {
+ // First, we check that there's one @ symbol,
+ // and that the lengths are right.
+ if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) {
+ // Email invalid because wrong number of characters
+ // in one section or wrong number of @ symbols.
+ return false;
+ }
+ // Split it into sections to make life easier
+ $email_array = explode("@", $email);
+ $local_array = explode(".", $email_array[0]);
+ for ($i = 0; $i < sizeof($local_array); $i++) {
+ if
+ (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{|}~-][A-Za-z0-9!#$%&'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$",
+ $local_array[$i])) {
+ return false;
+ }
+ }
+ // Check if domain is IP. If not,
+ // it should be valid domain name
+ if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) {
+ $domain_array = explode(".", $email_array[1]);
+ if (sizeof($domain_array) < 2) {
+ return false; // Not enough parts to domain
+ }
+ for ($i = 0; $i < sizeof($domain_array); $i++) {
+ if
+ (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])|([A-Za-z0-9]+))$",
+ $domain_array[$i])) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+require_once('lib/check_email.php');
+
+$config = get_config();
+
+function is_kamailio_domain ( $domain )
+{
+ global $config;
+ $query = sprintf("SELECT id FROM %s WHERE domain = '%s'",
+ $config['kamailio_domain_table'],
+ sql_clean( $domain )
+ );
+ $domain = sql_dbquery_single( $config['kamailio_db'], $query );
+ if ( !$domain ) return false;
+ return $domain['id'];
+
+}
+
+function get_servers( $domain )
+{
+ global $config;
+ $query = sprintf("SELECT registrar, r_port, proxy, p_port FROM %s WHERE domain = '%s'",
+ $config['provision_servers_table'],
+ sql_clean( $domain ));
+ $servers = sql_dbquery_single( $config['provision_db'], $query );
+ return $servers;
+}
+
+function verify_sipadress( $address ) //TODO: Improve/expand on the test!
+{
+ return check_email_address($address); // A SIP address follows the requirements for email addresses...
+ // Except that it permits the use of :portnumber notation!
+ // TODO: Fix this so it allows :NNNN syntax for portnumbers...
+}
+
+function split_sipaddress ( $address )
+{
+ if ( !verify_sipadress( $address ) ) return null;
+ $data = split('@', $address);
+ if ( count( $data ) != 2 ) return null;
+ return $data;
+}
+
+function clean_mac ( $input )
+{
+ $mac = strtolower( preg_replace('/:/', '', $input) );
+ if ( ! preg_match( '/^[a-f0-9]{1,}$/', $mac ) ) return null;
+ if (strlen( $mac ) != 12 ) return null;
+ return $mac;
+}
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/common_functions.php');
+
+function sql_dbquery( $db, $query )
+{
+ if ( ! mysql_select_db( $db ) ) return false;
+ $result = mysql_query( $query );
+ if ( !$result ) return false;
+ if (mysql_num_rows($result) == 0) return false;
+ return $result;
+}
+function sql_dbexec ( $db, $query )
+{
+ if ( ! mysql_select_db( $db ) ) return false;
+ $result = mysql_query( $query );
+ if ( !$result ) return false;
+ return $result;
+}
+function sql_dbquery_single( $db, $query )
+{
+ $result = sql_dbquery( $db, $query );
+ if ( ! $result ) return false;
+ if ( mysql_num_rows($result) != 1) return false;
+ $data = mysql_fetch_assoc( $result );
+ return $data;
+}
+function sql_dbtest_numrows ( $db, $query, $numrows )
+{
+ $result = sql_dbquery( $db, $query );
+
+ if ( ! $result ) return false;
+ if ( mysql_num_rows($result) == $numrows ) return true;
+ return false;
+}
+function sql_clean( $string )
+{
+ return addslashes( $string );
+}
+
+
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+
+function verify_e164 ( $input )
+{
+ // A really paranoid E164 test. Starts out with a regexp,
+ // where $arr[1] will be set to the numeric part of the
+ // e164 content, if valid. The rest is paranoid behaviour,
+ // if the regexp matches, the other tests can not fail...
+
+ // e164 format is: A plus (+) followed by at least four
+ // digits, and no more than 15 digits total.
+ if (! preg_match ( '/^\+(\d{4,15})$/', $input, $arr ) )
+ return 0;
+ $number = $arr[1];
+ if ( ! $number )
+ return 0;
+
+ if ( preg_match( '/\+/', $number ))
+ return 0;
+
+ if ( !is_numeric( $number ) )
+ return 0;
+
+ return $number;
+
+}
+
+function number_inpool( $number )
+{
+ global $config;
+ if (! verify_e164( $number ) )
+ {
+ return false;
+ }
+
+ $test = "SELECT COUNT(*) FROM " . $config['numbers_table'] . " WHERE number = '" . $number . "'";
+ $result = sql_dbquery($config['provision_db'], $test);
+ if ( !$result ) return false;
+ $row = mysql_fetch_row($result);
+ if ( !$row ) return false;
+ $num_r = $row[0];
+ if ( $num_r == 1 ) return true;
+
+ return false;
+}
+
+function get_random_numbers ( $limit = 0 )
+{
+ global $config;
+ $query = "SELECT number FROM " . $config['numbers_table'] . " ORDER BY RAND()";
+ if ( $limit && is_numeric( $limit ) )
+ $query .= " LIMIT " . $limit;
+ $result = sql_dbquery( $config['provision_db'], $query );
+ print mysql_error();
+ if ( !$result ) return null;
+ if (mysql_num_rows($result) < 1 ) return null;
+ $rows = array();
+ while ( $row = mysql_fetch_assoc( $result ) )
+ {
+ array_push( $rows, $row['number'] );
+ }
+ return $rows;
+
+}
+
+function get_numbers ( $search=null, $limit = 0 )
+{
+ global $config;
+ $query = "SELECT number FROM " . $config['numbers_table'];
+
+ if ( $search )
+ $query .= sprintf(" WHERE number LIKE '%%%s%%'", sql_clean($search));
+
+ $query .= " ORDER BY number ASC";
+
+ if ( $limit && is_numeric( $limit ) )
+ $query .= " LIMIT " . $limit;
+
+
+ $result = sql_dbquery( $config['provision_db'], $query );
+ print mysql_error();
+ if ( !$result ) return null;
+ if (mysql_num_rows($result) < 1 ) return null;
+ $rows = array();
+ while ( $row = mysql_fetch_assoc( $result ) )
+ {
+ array_push( $rows, $row['number'] );
+ }
+ return $rows;
+}
+
+function add_range( $start, $end )
+{
+ global $config;
+ $start_numeric = null;
+ $end_numeric = null;
+
+ // The start and end must be in the same e164 range. Because
+ // of this, they must hav identical length
+ if ( strlen( $start ) != strlen( $end ))
+ return "Start and end have different lengths.";
+
+ // The parameters MUST be in e164 format.
+ $start_numeric = verify_e164( $start );
+ $end_numeric = verify_e164( $end );
+ if (! $start_numeric )
+ return "Start of range is not a valid e164 number";
+ if (! $end_numeric )
+ return "End of range is not a valid e164 number";
+
+ // Significant, a simple sanity check.
+ if ( ! ($start < $end) )
+ return "Start of range is after end of range";
+
+ // Hard-coded paranoia: We expect ranges to be less than 10k numbers..
+ if ( ($end - $start ) > 9999 )
+ return "Range is larger than hard limit permits";
+
+ for ( $num = $start_numeric; $num <= $end_numeric; $num++ )
+ {
+ add_number( '+' . $num );
+ }
+ return 'ok';
+}
+
+function add_number( $number )
+{
+ global $config;
+ if (! verify_e164( $number ) )
+ {
+ return false;
+ }
+
+ if (number_inpool( $number ) ) return false;
+
+ $insert = "INSERT INTO " . $config['numbers_table'] . " (number) VALUES ('" . $number . "')";
+ return sql_dbexec( $config['provision_db'], $insert);
+}
+
+function remove_number ( $number )
+{
+ global $config;
+ if (! verify_e164( $number ) )
+ {
+ return false;
+ }
+ if ( !number_inpool( $number ) ) return false;
+ $query = "DELETE FROM " . $config['numbers_table'] . " WHERE number = '" . $number . "'";
+ return sql_dbexec( $config['provision_db'], $query);
+
+}
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+
+function get_user_phones ( $username, $domain )
+{
+ global $config;
+ $query = sprintf("SELECT id FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['provision_users_table'],
+ sql_clean( $username ),
+ sql_clean( $domain )
+ );
+
+ $user = sql_dbquery_single( $config['provision_db'], $query );
+ if ( ! $user ) return null;
+ $user_id = $user['id'];
+
+ $query = sprintf("SELECT mac FROM %s WHERE user_rel = %d",
+ $config['provision_phones_table'],
+ $user_id
+ );
+
+ $result = sql_dbquery( $config['provision_db'], $query );
+ if ( !$result ) return null;
+ if (mysql_num_rows($result) < 1 ) return null;
+ $rows = array();
+ while ( $row = mysql_fetch_assoc( $result ) )
+ array_push( $rows, $row['mac'] );
+ return $rows;
+}
+
+function get_phone_users ( $macaddress )
+{
+ global $config;
+ $ptbl = $config['provision_phones_table'];
+ $utbl = $config['provision_users_table'];
+ $query = "SELECT ".$ptbl.".mac as mac, CONCAT( ".$utbl.".username, '@', ".$utbl.".domain ) as user
+ FROM ".$ptbl."
+ INNER JOIN ".$utbl." ON ".$ptbl.".user_rel = ".$utbl.".id
+ WHERE ".$ptbl.".mac = '".sql_clean($macaddress ). "'";
+
+ $result = sql_dbquery( $config['provision_db'], $query );
+ if ( !$result ) return null;
+ if (mysql_num_rows($result) < 1 ) return null;
+ $rows = array();
+ while ( $row = mysql_fetch_assoc( $result ) )
+ {
+ array_push( $rows, $row['user'] );
+ }
+ return $rows;
+}
+
+function add_phone_user( $mac, $username, $domain )
+{
+ global $config;
+ // Get ID of user, for use with user_rel field..
+ $user_id = get_provision_userid( $username, $domain );
+ if ( !$user_id ) return false;
+
+ // Doublecheck :)
+ $mac = clean_mac($mac);
+ if (!$mac) return false;
+
+ // Triplecheck :)
+ $phones = get_user_phones ( $username, $domain);
+ if ( $phones && in_array( $mac, $phones ) )
+ return false;
+
+ // OK, so we have the User ID, a valid MAC, and no previous registration
+ // of that combination. Going to add.
+ $query = sprintf("INSERT INTO %s ( mac, user_rel ) VALUES ( '%s', %d )",
+ $config['provision_phones_table'], $mac, $user_id);
+ return sql_dbexec( $config['provision_db'], $query );
+}
+
+function delete_phone_user( $mac, $username, $domain )
+{
+ global $config;
+ // Get ID of user, for use with user_rel field..
+ $user_id = get_provision_userid( $username, $domain );
+ if ( !$user_id ) return false;
+
+ // Doublecheck :)
+ $mac = clean_mac($mac);
+ if (!$mac) return false;
+
+ // Triplecheck :)
+ $phones = get_user_phones ( $username, $domain);
+ if ( !$phones ) return false;
+ if ( ! in_array( $mac, $phones ) ) return false;
+
+ // OK, so we have the User ID, a valid MAC, and no previous registration
+ // of that combination. Going to remove.
+ $query = sprintf("DELETE FROM %s WHERE mac = '%s' AND user_rel = %d",
+ $config['provision_phones_table'], $mac, $user_id);
+ return sql_dbexec( $config['provision_db'], $query );
+}
+
+
+
+function list_phones ( $search = null )
+{
+ global $config;
+ $query = sprintf("SELECT mac FROM %s", $config['provision_phones_table']);
+ if ( $search )
+ $query .= sprintf(" WHERE mac LIKE '%s%%'", sql_clean($search));
+
+ $result = sql_dbquery( $config['provision_db'], $query );
+ if ( !$result ) return null;
+ if (mysql_num_rows($result) < 1 ) return null;
+ $rows = array();
+ while ( $row = mysql_fetch_assoc( $result ) )
+ {
+ array_push( $rows, $row['mac'] );
+ }
+ return $rows;
+}
+
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+
+$config = get_config();
+
+function generate_password( $length = 32 )
+{
+ $string = "";
+ while ( strlen( $string ) < $length )
+ $string .= substr(md5(rand().rand()), 0, $length);
+ return substr( $string, 0, $length );
+
+}
+
+
+function is_kamailio_subscriber ( $user, $domain )
+{
+ global $config;
+ $query = sprintf("SELECT username FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['kamailio_subscriber_table'],
+ sql_clean( $user ),
+ sql_clean( $domain )
+ );
+ return sql_dbtest_numrows( $config['kamailio_db'], $query, 1);
+}
+
+function is_provision_user ( $user, $domain )
+{
+ global $config;
+ $query = sprintf("SELECT username FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['provision_users_table'],
+ sql_clean( $user ),
+ sql_clean( $domain )
+ );
+ return sql_dbtest_numrows( $config['provision_db'], $query, 1);
+}
+
+function add_kamailio_subscriber( $username, $domain, $password, $email )
+{
+
+ global $config;
+
+ $ha1 = md5( $username . ":" . $domain . ":" . $password );
+ $ha1b = md5( $username . "@" . $domain . ":" . $domain . ":" . $password );
+
+ $query = sprintf( "INSERT INTO %s (username, domain, password, email_address, ha1, ha1b) VALUES ('%s','%s','%s', '%s', '%s', '%s')",
+ $config['kamailio_subscriber_table'],
+ sql_clean($username),
+ sql_clean($domain),
+ sql_clean($password),
+ sql_clean($email),
+ $ha1,
+ $ha1b
+ );
+ if ( ! sql_dbexec( $config['kamailio_db'], $query ) ) return false;
+ return true;
+}
+
+function delete_kamailio_subscriber( $username, $domain )
+{
+ global $config;
+
+
+ $query = sprintf("SELECT id FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['kamailio_subscriber_table'],
+ sql_clean($username),
+ sql_clean($domain)
+ );
+ $row = sql_dbquery_single( $config['kamailio_db'], $query );
+ if (!$row) return false;
+ $user_rowid = $row['id'];
+ if ( !$user_rowid ) return false;
+
+ $query = sprintf( "DELETE FROM %s WHERE id = %d AND username = '%s' AND domain = '%s'",
+ $config['kamailio_subscriber_table'],
+ $user_rowid,
+ sql_clean($username),
+ sql_clean($domain)
+ );
+ if ( ! sql_dbexec( $config['kamailio_db'], $query ) ) return false;
+ return true;
+}
+
+function add_provision_user( $username, $password, $domain, $authid, $registrar, $r_port, $proxy, $p_port, $displayname, $dialplan, $linetext )
+{ global $config;
+
+ if ( is_provision_user( $username, $password ) ) return false;
+ $query = sprintf ("INSERT INTO %s ( username, password, displayname, domain, registrar, r_port, proxy, p_port, dialplan, authid, linetext )
+ VALUES ('%s', '%s', '%s', '%s', '%s', %d, '%s', %d, '%s', '%s', '%s')",
+ $config['provision_users_table'],
+ sql_clean($username),
+ sql_clean($password),
+ sql_clean($displayname),
+ sql_clean($domain),
+ sql_clean($registrar),
+ $r_port,
+ sql_clean($proxy),
+ $p_port,
+ sql_clean($dialplan),
+ sql_clean($authid),
+ sql_clean($linetext)
+ );
+ if ( ! sql_dbexec( $config['provision_db'], $query ) ) return false;
+ return true;
+}
+function get_provision_userid ( $username, $domain )
+{
+ global $config;
+
+ $query = sprintf("SELECT id FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['provision_users_table'],
+ sql_clean($username),
+ sql_clean($domain)
+ );
+
+ $row = sql_dbquery_single( $config['provision_db'], $query );
+ if (!$row) return false;
+ $user_rowid = $row['id'];
+ return $user_rowid;
+}
+
+function delete_provision_user( $username, $domain )
+{
+ global $config;
+
+ $user_rowid = get_provision_userid( $username, $domain );
+ if ( !$user_rowid ) return false;
+
+ $query = sprintf( "DELETE FROM %s WHERE id = %d AND username = '%s' AND domain = '%s'",
+ $config['provision_users_table'],
+ $user_rowid,
+ sql_clean($username),
+ sql_clean($domain)
+ );
+ if ( ! sql_dbexec( $config['provision_db'], $query ) ) return false;
+ return true;
+}
+
+function list_users ( $search = null )
+{
+ global $config;
+ $query = sprintf("SELECT CONCAT(username, '@', domain) FROM %s ORDER BY username,domain", $config['provision_users_table'] );
+
+ if ( array_key_exists ( 'search', $_GET ) )
+ {
+ $search = $_GET['search']; // TODO: Add some sanitation and input validation!
+ $query = sprintf("SELECT CONCAT(username, '@', domain) FROM %s WHERE CONCAT(username, '@', domain) LIKE '%%%s%%' ORDER BY username,domain", $config['provision_users_table'], sql_clean( $search ) );
+ }
+
+ $result = sql_dbquery( $config['provision_db'], $query );
+ if ( !$result ) return null;
+ $list = array();
+ while ( $row = mysql_fetch_row( $result ) )
+ {
+ array_push( $list, $row[0] );
+ }
+ return $list;
+ print json_encode( array( 'response' => 'ok', 'list' => $list ));
+
+
+}
+function get_userdata( $username, $domain )
+{
+ global $config;
+ if ( is_kamailio_subscriber( $username, $domain ) // User must be present in both!
+ && is_provision_user( $username, $domain ) ) $type = 'local';
+ else if ( is_provision_user( $username, $domain ) ) $type = 'remote';
+ else return null;
+
+ $provision_data = null;
+ $kamailio_data = null;
+
+ $query_provision = sprintf ("SELECT id, username, password, displayname, domain, registrar, r_port, proxy, p_port, dialplan, authid, linetext FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['provision_users_table'],
+ sql_clean($username),
+ sql_clean($domain));
+
+ $provision_data = sql_dbquery_single( $config['provision_db'] , $query_provision );
+ if ( ! $provision_data ) return false;
+
+ if ( $type == 'local' )
+ {
+ // WARNING: Note the typo in the name of the 'permittedcalls' column!
+ $query_kamailio = sprintf ("SELECT id, username, domain, password, email_address, ha1, ha1b, rpid, permitedcalls FROM %s WHERE username = '%s' AND domain = '%s'",
+ $config['kamailio_subscriber_table'],
+ sql_clean($username),
+ sql_clean($domain));
+ $kamailio_data = sql_dbquery_single( $config['kamailio_db'] , $query_kamailio );
+ if ( ! $kamailio_data ) return false;
+ }
+ $user['type'] = $type;
+ $user['username'] = $provision_data['username'];
+ $user['password'] = $provision_data['password'];
+ $user['domain'] = $provision_data['domain'];
+ $user['authid'] = $provision_data['authid'];
+ $user['registrar'] = $provision_data['registrar'];
+ $user['r_port'] = $provision_data['r_port'];
+ $user['proxy'] = $provision_data['proxy'];
+ $user['p_port'] = $provision_data['p_port'];
+ $user['dialplan'] = $provision_data['dialplan'];
+ $user['displayname'] = $provision_data['displayname'];
+ $user['linetext'] = $provision_data['linetext'];
+ if ( $type == 'local' )
+ {
+ $user['email'] = $kamailio_data['email'];
+ $user['ha1'] = $kamailio_data['ha1'];
+ $user['ha1b'] = $kamailio_data['ha1b'];
+ $user['rpid'] = $kamailio_data['rpid'];
+ $user['permittedcalls'] = $kamailio_data['permitedcalls'];
+ }
+
+ return $user;
+}
+
+?>
--- /dev/null
+<?php
+require_once('config.php');
+//require_once('lib/user_functions.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+require_once('lib/number_functions.php');
+
+$config = get_config();
+
+$config['sql_link'] = @mysql_connect(
+ $config['sql_server'],
+ $config['sql_username'],
+ $config['sql_password']
+);
+if ( !$config['sql_link'] )
+{
+ print json_encode( array( 'response' => 'failed', 'cause' => 'error', 'detail' => 'Database connection failed.'));
+ exit;
+}
+
+//*************************************************************************************
+ switch ( $_SERVER['PATH_INFO'] )
+ {
+ case "/list":
+ // List all (distinct) phone MAC-adresses registered...
+ $limit = 0;
+ $random = false;
+ $search = null;
+ if ( array_key_exists('limit', $_GET ) && is_numeric( $_GET['limit']))
+ $limit = $_GET['limit'];
+
+ if ( array_key_exists('search', $_GET ) )
+ $search = $_GET['search'];
+ else if ( array_key_exists('random', $_GET ) && (strtolower( $_GET['random'] ) === 'true'))
+ $random = true;
+
+ $numbers = null;
+
+ if ( $random )
+ {
+ $numbers = get_random_numbers( $limit );
+ }
+ else
+ {
+ $numbers = get_numbers ( $search, $limit );
+ }
+ if ( $numbers )
+ {
+ print json_encode( array( 'response' => 'ok', 'list' => $numbers ));
+ break;
+ }
+
+ print json_encode( array( 'response' => 'failed', 'cause' => 'empty', 'detail' => 'Empty result.' ));
+ break;
+ case "/add_range":
+ if ( array_key_exists('start', $_GET) && array_key_exists('end', $_GET) )
+ {
+ $start = $_GET['start'];
+ $end = sql_clean( $_GET['end'] );
+ $result = add_range( $start, $end );
+ if ( $result === 'ok' )
+ {
+ print json_encode ( array( 'response' => 'ok') );
+ }
+ else
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'rejected', 'detail' => $result ) );
+ }
+ }
+ break;
+ case "/add":
+ // TODO: This should return better responses!
+ // Currently, it will fail with "invalid"
+ if ( array_key_exists('number', $_GET))
+ {
+ $number = $_GET['number'];
+
+ if (! verify_e164( $number ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'rejected', 'detail' => "Not a valid e164 number" ));
+ break;
+ }
+ if ( number_inpool( $number ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'exists', 'detail' => "Number is already in the pool" ));
+ break;
+ }
+
+ $result = add_number( $number );
+ if ( $result )
+ {
+ print json_encode ( array( 'response' => 'ok', 'detail' => 'Added ' . $number, 'number' => $number ) );
+ break;
+ }
+ else
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'rejected' ));
+ }
+ }
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+ case "/remove":
+ if ( array_key_exists('number', $_GET))
+ {
+ $number = $_GET['number'];
+
+ if (! verify_e164( $number ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'rejected', 'detail' => "Not a valid e164 number" ));
+ break;
+ }
+ if ( !number_inpool( $number ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => "Number not in pool" ));
+ break;
+ }
+ if ( !remove_number ( $number ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'dbfail', 'detail' => "Failed to remove number" ));
+ break;
+ }
+ print json_encode ( array( 'response' => 'ok', 'detail' => 'Removed ' . $number, 'number' => $number ) );
+ break;
+ }
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+ case "/inpool":
+ if ( array_key_exists('number', $_GET))
+ {
+ $number = $_GET['number'];
+ if ( number_inpool( $number ) )
+ print json_encode ( array( 'response' => 'ok', 'number' => $number ) );
+ else
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'nonexistant') );
+ break;
+ }
+ default:
+ print json_encode ( array( 'response' => 'invalid') );
+ }
+mysql_close( $config['sql_link'] );
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/user_functions.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+require_once('lib/phone_functions.php');
+
+$config = get_config();
+
+$config['sql_link'] = @mysql_connect(
+ $config['sql_server'],
+ $config['sql_username'],
+ $config['sql_password']
+);
+if ( !$config['sql_link'] )
+{
+ print json_encode( array( 'response' => 'failed', 'cause' => 'error', 'detail' => 'Database connection failed.'));
+ exit;
+}
+
+//*************************************************************************************
+ switch ( $_SERVER['PATH_INFO'] )
+ {
+ case "/get":
+ // Required GET parameters:
+ // user: authentication username, SIP-username without domain component
+ // domain: Domain/realm of the user. username + '@' + domain == SIP address.
+ if ( array_key_exists( 'mac', $_GET) )
+ {
+ $mac = $_GET['mac'];
+ $relations = get_phone_users ( $mac );
+ if ( $relations )
+ {
+ print json_encode( array( 'response' => 'ok', 'list' => $relations ));
+ }
+ else print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'No results.'));
+ }
+ else if ( array_key_exists( 'user', $_GET ) )
+ {
+ $user = split_sipaddress($_GET['user']);
+ if (! $user )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'Invalid SIP address') );
+ break;
+ }
+ list ( $username, $domain ) = $user;
+ $userdata = get_user_phones( $username, $domain );
+ if ( $userdata )
+ {
+ print json_encode( array( 'response' => 'ok', 'list' => $userdata ));
+ }
+ else print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'No results.'));
+
+ }
+ else
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+ case "/list":
+ // List all (distinct) phone MAC-adresses registered...
+ $search = null;
+ if ( array_key_exists('search', $_GET ) )
+ $search = $_GET['search'];
+
+ $phones = list_phones( $search );
+ print json_encode( array( 'response' => 'ok', 'list' => $phones ));
+ break;
+ case "/add":
+ // Add a MAC+user...
+ /*
+ Parameters:
+ mac The MAC-address of the phone to add an entry for
+ Either:
+ user A registered username on user@domain form (SIP address)
+ Or:
+ username A registered username, combines with:
+ domain A valid domain .. to form a registered user@domain combo :)
+
+ */
+ if ( array_key_exists('mac', $_GET ) &&
+ ( array_key_exists('user', $_GET) ||
+ ( array_key_exists('username', $_GET) && array_key_exists('domain', $_GET ))))
+ {
+ $username = "";
+ $domain = "";
+ if ( array_key_exists('username', $_GET) )
+ {
+ $username = $_GET['username'];
+ $domain = $_GET['domain'];
+ }
+ else
+ {
+ $user = split_sipaddress($_GET['user']);
+ if ( !$user )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'invalid', 'detail' => 'Invalid SIP address') );
+ break;
+ }
+ list ( $username, $domain ) = $user;
+ }
+ $mac = clean_mac($_GET['mac']);
+ if ( !$mac )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'invalid', 'detail' => 'No valid MAC address given.') );
+ break;
+ }
+
+ if ( !is_provision_user ( $username, $domain ) )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'User not registered.'));
+ break;
+ }
+ $phones = get_user_phones ( $username, $domain);
+ if ( $phones && in_array( $mac, $phones ) )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'exists', 'detail' => 'This phone and user combination is already configured..'));
+ break;
+ }
+ $res = add_phone_user ( $mac, $username, $domain );
+ if ( !$res )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' =>'dbfail', 'detail' => 'Failed to add phone to database.'));
+ break;
+ }
+ else
+ {
+ print json_encode( array ( 'response' => 'ok', 'data' => array (
+ 'mac' => $mac, 'username' => $username, 'domain' => $domain) ));
+ break;
+ }
+ break;
+ }
+ else
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+
+
+ case "/remove":
+ // Del a MAC+user...
+ /*
+ Parameters:
+ mac The MAC-address of the phone to add an entry for
+ Either:
+ user A registered username on user@domain form (SIP address)
+ Or:
+ username A registered username, combines with:
+ domain A valid domain .. to form a registered user@domain combo :)
+
+ */
+ if ( array_key_exists('mac', $_GET ) &&
+ ( array_key_exists('user', $_GET) ||
+ ( array_key_exists('username', $_GET) && array_key_exists('domain', $_GET ))))
+ {
+ $username = "";
+ $domain = "";
+ if ( array_key_exists('username', $_GET) )
+ {
+ $username = $_GET['username'];
+ $domain = $_GET['domain'];
+ }
+ else
+ {
+ $user = split_sipaddress($_GET['user']);
+ if ( !$user )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'invalid', 'detail' => 'Invalid SIP address') );
+ break;
+ }
+ list ( $username, $domain ) = $user;
+ }
+ $mac = clean_mac($_GET['mac']);
+ if ( !$mac )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'invalid', 'detail' => 'No valid MAC address given.') );
+ break;
+ }
+
+ $phones = get_user_phones ( $username, $domain);
+ if ( ! $phones || !in_array( $mac, $phones ) )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'Unable to locate requested combination'));
+ break;
+ }
+ $res = delete_phone_user ( $mac, $username, $domain );
+ if ( !$res )
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' =>'dbfail', 'detail' => 'Failed to remove phone from database.'));
+ break;
+ }
+ else
+ {
+ print json_encode( array ( 'response' => 'ok' ));
+ break;
+ }
+ break;
+ }
+ else
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+
+
+ default:
+ print json_encode ( array( 'response' => 'invalid') );
+ }
+mysql_close( $config['sql_link'] );
+?>
--- /dev/null
+<?php
+require_once('config.php');
+require_once('lib/user_functions.php');
+require_once('lib/common_functions.php');
+require_once('lib/db_functions.php');
+require_once('lib/phone_functions.php');
+
+$config = get_config();
+
+$config['sql_link'] = @mysql_connect(
+ $config['sql_server'],
+ $config['sql_username'],
+ $config['sql_password']
+);
+if ( !$config['sql_link'] )
+{
+ print json_encode( array( 'response' => 'failed', 'cause' => 'error', 'detail' => 'Database connection failed.'));
+ exit;
+}
+
+//*************************************************************************************
+ switch ( $_SERVER['PATH_INFO'] )
+ {
+ case "/get":
+ // Required GET parameters:
+ // user: authentication username, SIP-username without domain component
+ // domain: Domain/realm of the user. username + '@' + domain == SIP address.
+ if ( array_key_exists( 'username', $_GET) && array_key_exists( 'domain', $_GET ) )
+ {
+ $username = $_GET['username'];
+ $domain = $_GET['domain'];
+ // Now, do funky stuff.
+ /*
+ Test if user exists in both 'kamailio.subscribers' and 'provision.users'
+ * Return 'response' => 'ok', 'type' => 'local', 'user' => complete user object.
+ Test if user exists in 'provision.user' only
+ * Return 'response' => 'ok', 'type' => 'remote', 'user' => complete user object.
+ If user does is neither local nor remote
+ * Return 'response' => 'failed' with 'cause' => 'nonexistant'
+ On failure, return 'response' => 'failed' with 'cause' => 'error' (may set 'detail' => 'message')
+
+ */
+ // Dummy-response:
+ $userdata = get_userdata( $username, $domain );
+ if ( $userdata )
+ {
+ print json_encode( array( 'response' => 'ok', 'user' => $userdata ));
+ }
+ else
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'Request for user ' . $username . '@' . $domain . ' failed.'));
+ }
+ }
+ else
+ print json_encode ( array( 'response' => 'invalid') );
+ break;
+ case "/list":
+ /*
+ Simply list all users in user@domain format
+ Perform a search operation if 'search' exists as a GET-parameter
+ * The search should try to do a "smart search" on SIP-usernames:
+ * Try to search with names in in username@domain format
+ * Do the search with wildcards before and after input text.
+ * The search must be done in the provisioning tables, to be able
+ to match non-local users.
+ * SQL SELECT CONCAT() WHERE CONCAT() must be used *shrug*
+ */
+ $search = null;
+ if ( array_key_exists ( 'search', $_GET ) )
+ $search = $_GET['search']; // TODO: Add some sanitation and input validation!
+ $list = list_users( $search );
+ print json_encode( array( 'response' => 'ok', 'list' => $list ));
+ break;
+ case "/add_local":
+ /*
+ What to do??
+ Required parameters should be...
+ username
+ domain
+ displayname
+ email
+
+ Verify that domain is local (lookup in the 'kamailio.domain' table.
+ Verify that the username is available (nonexistant for domain in kamilio.subscribers (and provision.users?))
+ * Autocreate password
+ * Add username, domain, email and created password to the 'kamailio.subscriber' table
+ * Get the registrar+port, proxy+port from the 'provision.servers' table.
+ * standard dialplan from configuration.
+ * Add to the 'provision.users' table:
+ username -> username
+ password -> generated password
+ displayname -> displayname
+ domain -> domain
+ registrar -> provision.servers.registrar
+ r_port -> provision.servers.r_port
+ proxy -> provision.servers.proxy
+ p_port -> provision.servers.p_port
+ authid -> username
+ dialplan -> standard dialplan
+ linetext -> username
+ * Return 'response' => 'ok' with a full user object in JSON format.
+ If any of the tests fail, return 'response' => 'failed' with 'cause' => "description" on JSON format.
+
+ */
+ // Test required parameters:
+ if ( array_key_exists( 'username', $_GET)
+ && array_key_exists( 'domain', $_GET )
+ && array_key_exists( 'displayname', $_GET )
+ && array_key_exists( 'email', $_GET ) )
+ {
+ $username = $_GET['username'];
+ $domain = $_GET['domain'];
+ $password = generate_password();
+ $displayname = $_GET['displayname'];
+ $email = $_GET['email'];
+
+ if ( !is_kamailio_domain( $domain ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'nxdomain', 'detail' => 'The selected domain is not local' ));
+ break;
+ }
+
+ $servers = get_servers( $domain );
+ if ( !$servers )
+ {
+ print json_encode( array( 'response' => 'failed', 'cause' => 'servfail', 'detail' => 'Servers lookup failed for domain '. $domain ) );
+ break;
+ }
+ $registrar = $servers['registrar'];
+ $r_port = $servers['r_port'];
+ $proxy = $servers['proxy'];
+ $p_port = $servers['p_port'];
+ $authid = $username;
+ $linetext = $username;
+ $dialplan = $config['standard_dialplan'];
+
+ if ( is_provision_user ( $username, $domain ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'exists', 'detail' => 'User already exists in provisioning configuration' ));
+ break;
+ }
+ if ( is_kamailio_subscriber ( $username, $domain ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'exists', 'detail' => 'User already exists as a Kamailio subscriber' ));
+ break;
+ }
+ $kam_res = add_kamailio_subscriber( $username, $domain, $password, $email );
+ if ( !$kam_res )
+ {
+ print json_encode( array( 'response' => 'failed', 'cause' => 'dbfail', 'detail' => 'Failed to add kamailio subscriber.' ) );
+ break;
+ }
+ $pro_res = add_provision_user( $username, $password, $domain, $authid, $registrar, $r_port, $proxy, $p_port, $displayname, $dialplan, $linetext );
+ if ( !$pro_res )
+ {
+ // Rollback data added to Kamailio! Try to simulate atomicity, or atleast maintain integrity...
+ delete_kamailio_subscriber( $username, $domain );
+ // Give errormessage, and quit.
+ print json_encode( array( 'response' => 'failed', 'cause' => 'dbfail', 'detail' => 'Failed to add user for provisioning. Rolled back kamailio subscriber' ) );
+ break;
+ }
+ $userdata = get_userdata( $username, $domain );
+ if ( !$userdata )
+ {
+ // Rollback data added to Kamailio! Try to simulate atomicity, or atleast maintain integrity...
+ delete_kamailio_subscriber( $username, $domain );
+ delete_provision_user( $username, $domain );
+ // Give errormessage, and quit.
+ print json_encode( array( 'response' => 'failed', 'cause' => 'dbfail', 'detail' => 'Failed to read recently added data. Operations rolled back' ) );
+
+ }
+ print json_encode( array( 'response' => 'ok', 'user' => $userdata ));
+ }
+ else
+ print json_encode( array( 'response' => 'invalid', 'cause' => 'parameters' ) );
+ break;
+ case "/add_remote":
+ /*
+ Required parameters should be...
+ username
+ password
+ domain
+ displayname
+ registrar
+ Optional parameters
+ r_port
+ proxy
+ p_port
+ authid
+ dialplan
+ linetext
+
+ Verify that the username+domain is not already registered in 'provision.users'.
+ * If r_port is empty, set to 5060
+ * If proxy/port is empty, set to registrar/port
+ * If authid is empty, set to username
+ * If dialplan is empty, set to standard dialplan
+ * If linetext is empty, set to username@domain
+ * Add to the 'provision.users' table:
+ username -> username
+ password -> supplied password
+ displayname -> displayname
+ domain -> domain
+ registrar -> registrar
+ r_port -> r_port
+ proxy -> proxy
+ p_port -> p_port
+ authid -> authid
+ dialplan -> dialplan
+ linetext -> linetext
+ * Return 'response' => 'ok' with a full user object in JSON format.
+ If any of the tests fail, return 'response' => 'failed' with 'cause' => "description" in JSON format.
+ */
+
+
+ // Test required parameters:
+ if ( array_key_exists( 'username', $_GET)
+ && array_key_exists( 'password', $_GET )
+ && array_key_exists( 'displayname', $_GET )
+ && array_key_exists( 'domain', $_GET )
+ && array_key_exists( 'registrar', $_GET ) )
+ {
+ $username = $_GET['username'];
+ $password = $_GET['password'];
+ $domain = $_GET['domain'];
+ $displayname = $_GET['displayname'];
+ $registrar = $_GET['registrar'];
+ $r_port = ( array_key_exists('r_port', $_GET) ) ? $_GET['r_port'] : 5060;
+
+ $proxy = ( array_key_exists('proxy', $_GET) ) ? $_GET['proxy'] : $registrar;
+ $p_port = ( array_key_exists('p_port', $_GET) ) ? $_GET['p_port'] : $r_port;
+ $authid = ( array_key_exists('authid', $_GET) ) ? $_GET['authid'] : $username;
+ $dialplan = ( array_key_exists('dialplan', $_GET) ) ? $_GET['dialplan'] : $config['standard_dialplan'];
+ $linetext = ( array_key_exists('linetext', $_GET) ) ? $_GET['linetext'] : $username . '@' . $domain;
+
+ if ( is_provision_user ( $username, $domain ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'exists', 'detail' => 'User already exists in provisioning configuration' ));
+ break;
+ }
+ if ( is_kamailio_subscriber ( $username, $domain ) )
+ {
+ print json_encode ( array( 'response' => 'failed', 'cause' => 'exists', 'detail' => 'User already exists as a Kamailio subscriber' ));
+ break;
+ }
+
+ $pro_res = add_provision_user( $username, $password, $domain, $authid, $registrar, $r_port, $proxy, $p_port, $displayname, $dialplan, $linetext );
+ if ( !$pro_res )
+ {
+ // Give errormessage, and quit.
+ print json_encode( array( 'response' => 'failed', 'cause' => 'dbfail', 'detail' => 'Failed to add user for provisioning.' ) );
+ break;
+ }
+ $userdata = get_userdata( $username, $domain );
+ if ( !$userdata )
+ {
+ // Rollback data added!
+ delete_provision_user( $username, $domain );
+ // Give errormessage, and quit.
+ print json_encode( array( 'response' => 'failed', 'cause' => 'dbfail', 'detail' => 'Failed to read recently added data. Operations rolled back' ) );
+
+ }
+ print json_encode( array( 'response' => 'ok', 'user' => $userdata ));
+ }
+ else
+ print json_encode( array( 'response' => 'invalid', 'cause' => 'parameters' ) );
+ break;
+ case "/remove":
+ /*
+ Required parameters should be...
+ username
+ domain
+
+ * Verify that no associations/relations exist in 'provision.phones'
+ * Verify that the user exists in 'provision.users'
+ * Remove from 'provision.users'
+ * Test to see of user exists in 'kamailio.subscriber'.
+ * Remove from 'kamailio.subscribers'
+ * Return response' => 'ok', 'type' => 'local'
+ * If not in 'kamailio.subscribers'
+ * Return response' => 'ok', 'type' => 'remote'
+ * If associations exist, return 'response' => 'failed', 'cause' => 'inuse'
+ * If no such user exists, return 'response' => 'failed' with 'cause' => 'nonexistant'
+ * On other failures, return 'response' => 'failed' with 'cause' => 'error' (may set 'detail' => 'message')
+ */
+ if ( array_key_exists( 'username', $_GET) && array_key_exists( 'domain', $_GET ) )
+ {
+ $username = $_GET['username'];
+ $domain = $_GET['domain'];
+ if ( get_user_phones ( $username, $domain ) )
+ {
+ print json_encode( array( 'response' => 'failed', 'cause' => 'inuse', 'detail' => 'User has associated provisioning. Remove and retry.' ) );
+ break;
+ }
+ if ( is_provision_user( $username, $domain ) || is_kamailio_subscriber( $username, $domain ) )
+ {
+ delete_provision_user( $username, $domain );
+ delete_kamailio_subscriber( $username, $domain );
+ print json_encode( array ( 'response' => 'ok', 'detail' => 'User ' . $username . '@' . $domain . ' deleted.'));
+ break;
+ }
+ else
+ {
+ print json_encode( array ( 'response' => 'failed', 'cause' => 'nonexistant', 'detail' => 'Unable to remove nonexistant user.'));
+ break;
+ }
+
+ break;
+
+ }
+ break;
+ case "/gen_pw":
+ print generate_password();
+ break;
+ default:
+ print json_encode ( array( 'response' => 'invalid') );
+ }
+mysql_close( $config['sql_link'] );
+?>