diff --git a/Routepermissions.class.php b/Routepermissions.class.php index 471b69e..519d952 100644 --- a/Routepermissions.class.php +++ b/Routepermissions.class.php @@ -1,4 +1,5 @@ . */ -if (!class_exists("RouterpermissionsLegacy")) { - require_once(dirname(__FILE__) . "/functions.inc.php"); -} - -class Routepermissions extends RoutepermissionsLegacy implements FreePBX\BMO +class Routepermissions extends \FreePBX\FreePBX_Helpers implements \FreePBX\BMO { + static $module = "routepermissions"; + /** Constructor code for version 13+ * @param Object $freepbx The FreePBX object * @return void @@ -99,12 +98,77 @@ public function search($query = null, &$results = null) public function install() { - return; + $columns = array( + "exten" => array("type"=>"integer"), + "routename" => array("type"=>"string", "length"=>25), + "allowed" => array("type"=>"string", "length"=>3, "default"=>"YES", "notnull"=>false), + "faildest" => array("type"=>"string", "length"=>255, "default"=>"", "notnull"=>false), + "prefix" => array("type"=>"string", "length"=>16, "default"=>"", "notnull"=>false), + ); + $indices = array( + "idx_exten" => array("type"=>"index", "cols"=>array("exten")), + "idx_route" => array("type"=>"index", "cols"=>array("routename")), + ); + try { + $table = $this->db->migrate("routepermissions"); + $table->modify($columns, $indices); + } catch (\Doctrine\DBAL\Exception $e) { + die_freepbx(sprintf(_("Error updating routepermissions table: %s"), $e->getMessage())); + } + + $stmt = $this->db->query("SELECT COUNT(exten) FROM routepermissions"); + if ($stmt->fetchColumn() > 0) { + out(_("Found data, using existing route permissions")); + try { + $this->db->exec("UPDATE routepermissions SET prefix=faildest, faildest='' WHERE faildest RLIKE '^[a-d0-9*#]+$'"); + } catch (\PDOException $e) { + die_freepbx(sprintf(_("Error updating routepermissions table: %s"), $e->getMessage())); + } + } else { + outn(_("New install, populating default allow permission… ")); + $extens = array(); + $devices = \FreePBX::Core()->getAllUsersByDeviceType(); + foreach($devices as $exten) { + if ($exten->id) { + $extens[] = $exten->id; + } + } + try { + $routes = \FreePBX::Core()->getAllRoutes(); + $query = "INSERT INTO routepermissions (exten, routename, allowed, faildest) VALUES (?, ?, 'YES', '')"; + $stmt = $this->db->prepare($query); + foreach($extens as $ext) { + foreach ($routes as $r) { + $this->db->execute($stmt, array($ext, $r["name"])); + } + } + } catch (\Exception $e) { + die_freepbx(sprintf(_("Error populating routepermissions table: %s"), $e->getMessage())); + } + out(_("complete")); + } } public function uninstall() { - return; + $amp_conf = \FreePBX\Freepbx_conf::create(); + + outn(_("Removing routepermissions database table… ")); + try { + $this->db->query("DROP TABLE routepermissions"); + out(_("complete")); + } catch (\PDOException $e) { + out(sprintf(_("Error removing routepermissions table: %s"), $result->getMessage())); + } + + outn(_("Removing AGI script… ")); + $agidir = $amp_conf->get("ASTAGIDIR"); + $result = unlink("$agidir/checkperms.agi"); + if (!$result) { + out(_("failed! File must be removed manually")); + } else { + out(_("complete")); + } } public function backup() @@ -127,8 +191,441 @@ public function writeConfig() return; } - public function doConfigPageInit($display) + /** + * Tell the system we want to hook into the dialplan + */ + public function myDialplanHooks() { - return; + // signal our intent to hook into the dialplan + return true; + } + + /** + * The actual dialplan hook + */ + public function doDialplanHook(&$ext, $engine, $pri) + { + if ($engine !== "asterisk") { + return false; + } + + foreach ($ext->_exts as $context=>$extensions) { + if (strncmp($context, "macro-dialout-", 14) === 0) { + $ext->splice($context, "s", 1, new \ext_agi("checkperms.agi")); + $ext->add($context, "barred", 1, new \ext_noop("Route administratively banned for this user.")); + $ext->add($context, "barred", 2, new \ext_hangup()); + $ext->add($context, "reroute", 1, new \ext_goto("1", "\${ARG2}", "from-internal")); + } + } + + // Insert the ROUTENAME into each route + foreach (\FreePBX::Core()->getAllRoutes() as $route) { + $context = "outrt-$route[route_id]"; + $routename = $route["name"]; + $routes = core_routing_getroutepatternsbyid($route["route_id"]); + foreach ($routes as $rt) { + $extension = $rt["match_pattern_prefix"] . $rt["match_pattern_pass"]; + // If there are any wildcards in there, add a _ to the start + if (preg_match("/\.|z|x|\[|\]/i", $extension)) { + $extension = "_".$extension; + } + if (!empty($rt['match_cid'])) { + $cid = (preg_match("/\.|z|x|\[|\]/i", $rt['match_cid'])) + ? '_'.$rt['match_cid'] + : $rt['match_cid']; + $extension = $extension.'/'.$cid; + } + $ext->splice($context, $extension, 1, new \ext_setvar("__ROUTENAME", $routename)); + } + } + } + + /** + * Tell the system which modules' pages we want to hook (hooked) + * + * @return array All the modules we want to hook into + */ + public static function myGuiHooks() { + // extensions page is part of core. + return array("core"); + } + + /** + * Perform our hook actions on page display + * + * @param Object $currentcomponent The ugly old page object + * @param string $module The module name + * @return boolean Returns false if the page is not modified + */ + public function doGuiHook(&$currentcomponent, $module) { + $pagename = isset($_REQUEST["display"]) ? $_REQUEST["display"] : ""; + $extdisplay = isset($_REQUEST["extdisplay"]) ? $_REQUEST["extdisplay"] : ""; + $action = isset($_REQUEST["action"]) ? $_REQUEST["action"] : ""; + $section = _("Outbound Route Permissions"); + $i = 0; + + if ( + $module !== "core" || $action === "del" || empty($extdisplay) || + ($pagename !== "extensions" && $pagename !== "users") + ) { + return false; + } + + $routes = $this->getRoutes(); + try { + $stmt = $this->db->prepare("SELECT allowed, faildest, prefix FROM routepermissions WHERE routename = ? AND exten = ?"); + } catch (\PDOException $e) { + return false; + } + foreach ($routes as $route) { + try { + $stmt->execute(array($route, $extdisplay)); + $res = $stmt->fetch(\PDO::FETCH_NUM); + } catch (\PDOException $e) { + continue; + } + if (is_array($res) && count($res) > 0) { + // a result was returned + list($allowed, $faildest, $prefix) = $res; + } else { + $allowed = "YES"; + $faildest = ""; + } + if ($allowed === "NO" && !empty($prefix)) { + $allowed = "REDIRECT"; + } + $route = htmlspecialchars($route); + $yes = _("Allow"); + $no = _("Deny"); + $redirect = _("Redirect w/prefix"); + $i += 10; + $js = '$("input[name=" + this.name.replace(/^routepermissions_perm_(\d+)-(.*)$/, "routepermissions_prefix_$1-$2") + "]").val("").prop("disabled", (this.value !== this.name + "=REDIRECT")).prop("required", (this.value === this.name + "=REDIRECT"));var id=$("select[name=" + $("#" + this.name.replace(/^routepermissions_perm_(\d+)-(.*)$/, "routepermissions_faildest_$1-$2")).val() + "]").val("").change().prop("disabled", (this.value !== this.name + "=NO")).data("id");$("select[data-id=" + id + "]").prop("disabled", (this.value !== this.name + "=NO"))'; + $radio = new \gui_radio( + "routepermissions_perm_$i-$route", //element name + array( + array("value"=>"YES", "text"=>$yes), + array("value"=>"NO", "text"=>$no), + array("value"=>"REDIRECT", "text"=>$redirect), + ), //radios + $allowed, //current value + sprintf(_("Allow access to %s"), $route), //group label text + "", //help text + false, //disable + htmlspecialchars($js), //onclick (13+) + "", //class (13+) + true //paired values; needed for 11 compatibility (13+) + ); + $currentcomponent->addguielem($section, $radio); + + $selects = new \gui_drawselects( + "routepermissions_faildest_$i-$route", //element name + $i, //index + $faildest, //current value + sprintf(_("Failure destination for %s"), $route), //label text + "", //help text + false, //can be empty + "", //fail validation message + _("Use default"), //empty message + ($allowed !== "NO"), //disable (13+) + "" //class (13+) + ); + $currentcomponent->addguielem($section, $selects); + + $input = new \gui_textbox( + "routepermissions_prefix_$i-$route", //element name + $prefix, //current value + sprintf(_("Redirect prefix for %s"), $route), //label text + "", //help text + "", //js validation + "", //validation failure msg + true, //can be empty + 0, //maxchars + ($allowed !== "REDIRECT"), // disable; no way to re-enable in 11/12 + false, //input group (13+) + "", //class (13+) + true //autocomplete (13+) + /* TODO: get rid of this */ + ); + $currentcomponent->addguielem($section, $input); + } + } + + /** + * Tell the system which pages' POSTs we want to hook + * + * @return array All the pages (not modules) we want to hook into + */ + public static function myConfigPageInits() { + return array("extensions", "users"); + } + + /** + * Perform our hook action on page POST + * + * @param string $module The module name + * @return boolean Returns false if there's nothing to do + */ + public function doConfigPageInit($module) + { + $pagename = isset($_REQUEST["display"]) ? $_REQUEST["display"] : "index"; + $extdisplay = isset($_REQUEST["extdisplay"]) ? $_REQUEST["extdisplay"] : null; + $action = isset($_REQUEST["action"]) ? $_REQUEST["action"] : null; + $route_perms = array(); + if (empty($extdisplay) || empty($action)) { + return false; + } + foreach ($_POST as $k=>$v) { + if (!preg_match("/routepermissions_(faildest|perm|prefix)_\d+-(.*)/", $k, $matches)) { + continue; + } + $route_name = $matches[2]; + if (!isset($route_perms[$route_name])) { + $route_perms[$route_name] = array("faildest"=>null, "perms"=>"YES", "prefix"=>null); + } + switch ($matches[1]) { + case "faildest": + $faildest_index = substr($v, 4); // remove "goto" from value + $faildest_type = isset($_POST[$v]) ? $_POST[$v] : null; + if ($faildest_type && isset($_POST["$faildest_type$faildest_index"])) { + $route_perms[$route_name]["faildest"] = $_POST["$faildest_type$faildest_index"]; + } + break; + case "perm": + list($foo, $perm) = explode("=", $v, 2); + $route_perms[$route_name]["perms"] = $perm; + break; + case "prefix": + $route_perms[$route_name]["prefix"] = $v; + break; + } + } + if (count($route_perms) === 0) { + return false; + } + + switch ($action) { + case "add": + case "edit": + try { + $stmt = $this->db->prepare("DELETE FROM routepermissions WHERE exten = ?"); + $stmt->execute(array($extdisplay)); + $stmt = $this->db->prepare("INSERT INTO routepermissions (exten, routename, allowed, faildest, prefix) VALUES(?, ?, ?, ?, ?)"); + } catch (\PDOException $e) { + return false; + } + foreach($route_perms as $route_name=>$data) { + if ($data["perms"] === "REDIRECT") { + $data["faildest"] = null; + $data["perms"] = "NO"; + if (empty($data["prefix"])) { + $data["prefix"] = null; + } + } elseif ($data["perms"] === "NO") { + $data["prefix"] = null; + } else { + $data["perms"] = "YES"; + $data["faildest"] = $data["prefix"] = null; + } + try { + $res = $stmt->execute(array( + $extdisplay, + $route_name, + $data["perms"], + $data["faildest"], + $data["prefix"], + )); + } catch (\PDOException $e) { + return false; + } + } + break; + case "del": + try { + $stmt = $this->db->prepare("DELETE FROM routepermissions WHERE exten = ?"); + $stmt->execute(array($extdisplay)); + } catch (\PDOException $e) { + return false; + } + break; + } + } + + /** + * Handle GET/POST requests to the page + */ + public function showPage($request = null) + { + $cwd = dirname(__FILE__); + $message = ""; + $errormessage = ""; + + if ($request !== null) { + foreach ($request as $k=>$perm) { + if (strncmp($k, "permission_", 11) === 0) { + $route = substr($k, 11); + $redir = ""; + $prefix = ""; + switch ($perm) { + case "YES": + break; + case "NO": + if (isset($request["goto_$route"])) { + $type = $request["goto_$route"]; + if (isset($request["${type}_$route"])) { + $redir = $request["${type}_$route"]; + } + } + break; + case "REDIRECT": + $perm = "NO"; + $prefix = trim($request["prefix_$route"]); + if (empty($prefix)) { + $errormessage .= sprintf( + _("Redirect selected but redirect prefix missing for route %s - no action taken"), + $route + ); + $errormessage .= "
"; + continue; + } + break; + default: + continue 2; + } + $range = $request["range_$route"]; + try { + $result = $this->setRangePermissions($route, $range, $perm, $redir, $prefix); + if ($prefix) { + $message .= sprintf( + _("Route %s set to %s for supplied range %s using redirect prefix %s"), + $route, + $perm, + $range, + $prefix + ); + $message .= "
"; + } else { + $message .= sprintf( + _("Route %s set to %s for supplied range %s"), + $route, + $perm, + $range + ); + $message .= "
"; + } + } catch (\PDOException $e) { + $errormessage .= sprintf( + _("Database error, couldn't set permissions for route %s: %s"), + $route, + $e->getMessage() + ); + $errormessage .= "
"; + } + } elseif ($k == "update_default") { + $dest_type = $request["gotofaildest"]; + $dest = $request[$dest_type . "faildest"]; + try { + $result = $this->updateDefaultDest($dest); + $message = _("Default destination changed"); + } catch (\PDOException $e) { + $errormessage = sprintf( + _("Database error, couldn't set default permissions: %s"), + $e->getMessage() + ); + } + } + } + } + + $viewdata = array( + "module"=>self::$module, + "message"=>$message, + "errormessage"=>$errormessage, + "rp"=>$this, + "routes"=>$this->getRoutes(), + ); + show_view("$cwd/views/settings13.php", $viewdata); + } + + public function getRoutes() + { + $sql = "SELECT DISTINCT name FROM outbound_routes JOIN outbound_route_sequence USING (route_id) ORDER BY seq"; + try { + $result = $this->db->query($sql); + return $result->fetchAll(\PDO::FETCH_COLUMN, 0); + } catch (\PDOException $e) { + return false; + } + } + + public function getDefaultDest() + { + $sql = "SELECT faildest FROM routepermissions where exten = -1 LIMIT 1"; + try { + $result = $this->db->query($sql); + return $result->fetchColumn(0); + } catch (\PDOException $e) { + return false; + } + } + + public function updateDefaultDest($dest) + { + try { + $sql = "DELETE FROM routepermissions WHERE exten = -1"; + $this->db->exec($sql); + if (!empty($dest)) { + $sql = "INSERT INTO routepermissions (exten, routename, faildest, prefix) VALUES ('-1', 'default', ?, '')"; + $stmt = $this->db->prepare($sql); + $stmt->execute(array($dest)); + } + return true; + } catch (\PDOException $e) { + throw $e; + } + } + + public function setRangePermissions($route, $range, $allowed, $faildest = "", $prefix = "") { + $allowed = (strtoupper($allowed) === "NO") ? "NO" : "YES"; + $extens = array_intersect( + $sys_ext = array_map(function($u) {return $u[0];}, core_users_list()), + $ext_range = (strtoupper($range) === strtoupper(_("All"))) ? $sys_ext : self::getRange($range) + ); + if (count($extens) === 0) { + return false; + } + try { + $sql = "DELETE FROM routepermissions WHERE exten=? AND routename=?"; + $stmt1 = $this->db->prepare($sql); + $sql = "INSERT INTO routepermissions (exten, routename, allowed, faildest, prefix) VALUES (?, ?, ?, ?, ?)"; + $stmt2 = $this->db->prepare($sql); + foreach ($extens as $ext) { + $stmt1->execute(array($ext, $route)); + $stmt2->execute(array($ext, $route, $allowed, $faildest, $prefix)); + } + return true; + } catch (\PDOException $e) { + throw $e; + } + } + + private static function getRange($range_str) { + $range_out = array(); + // Strip spaces + $ranges = explode(",", str_replace(" ", "", $range_str)); + + foreach($ranges as $range) { + if (is_numeric($range)) { + // Just a number; add it to the list. + $range_out[] = $range; + } elseif (strpos($range, "-")) { + list($start, $end) = explode("-", $range); + if (is_numeric($start) && is_numeric($end) && $start < $end) { + for ($i = $start; $i <= $end; $i++) { + $range_out[] = $i; + } + } + } + } + return array_unique($range_out, SORT_NUMERIC); } } \ No newline at end of file diff --git a/assets/.DS_Store b/assets/.DS_Store deleted file mode 100644 index 9f534e6..0000000 Binary files a/assets/.DS_Store and /dev/null differ diff --git a/assets/css/routepermissions.css b/assets/css/routepermissions.css index ce39474..fd54f15 100644 --- a/assets/css/routepermissions.css +++ b/assets/css/routepermissions.css @@ -17,18 +17,3 @@ Copyright 2016 Michael Newton You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ - -p.routepermissions_infobox { - width: 50%; - padding: 16px 16px 16px 80px; - border: 1px solid #ccc; - background-color: #ff9; - background-image: url('data:image/svg+xml,<%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FlinearGradient><%2FradialGradient><%2FlinearGradient><%2FlinearGradient><%2Fdefs><%2Fsvg>'); - background-repeat: no-repeat; - background-position: 16px; - text-align: justify; -} - -p.routepermissions_infobox.routepermissions_error { - background-image: url('data:image/svg+xml,<%3Fxml version%3D"1.0"%3F>%0A%0A%0A%0A%0A%0A<%2FradialGradient>%0A%0A%0A%0A<%2FlinearGradient>%0A%0A%0A%0A%0A<%2FlinearGradient>%0A<%2Fdefs>%0A%0A%0A%0A%0A%0A%0A<%2Fg>%0A%0A%0A<%2Fsvg>'); -} diff --git a/assets/js/routepermissions.js b/assets/js/routepermissions.js index 45aa263..49dcec4 100644 --- a/assets/js/routepermissions.js +++ b/assets/js/routepermissions.js @@ -32,5 +32,5 @@ $(function() { .change() .prop("disabled", (this.value !== "NO")); }); - // extensions page code is added per element: FUN!!! + // this script doesn't get included, so extensions page code has to be added per element: FUN!!! }); diff --git a/functions.inc.php b/functions.inc.php deleted file mode 100644 index fd8023c..0000000 --- a/functions.inc.php +++ /dev/null @@ -1,333 +0,0 @@ -. -*/ - -class RoutepermissionsLegacy { - public function __construct() - { - global $db; - $this->db = $db; - } - - public function getRoutes() - { - $sql = "SELECT DISTINCT name FROM outbound_routes JOIN outbound_route_sequence USING (route_id) ORDER BY seq"; - $result = $this->db->getCol($sql); - return $result; - } - - public function getDefaultDest() - { - $sql = "SELECT faildest FROM routepermissions where exten = -1 LIMIT 1"; - $result = $this->db->getOne($sql); - return $result; - } - - public function updateDefaultDest($dest) - { - $sql = "DELETE FROM routepermissions WHERE exten = -1"; - $this->db->query($sql); - if (DB::isError($res)) { - return false; - } - if (!empty($dest)) { - $sql = "INSERT INTO routepermissions (exten, routename, faildest, prefix) VALUES ('-1', 'default', ?, '')"; - $res = $this->db->query($sql, array($dest)); - if (DB::isError($res)) { - return false; - } - } - return true; - } - - public function setRangePermissions($route, $range, $allowed, $faildest = "", $prefix = "") { - $allowed = (strtoupper($allowed) === "NO") ? "NO" : "YES"; - $extens = array_intersect( - $sys_ext = array_map(function($u) {return $u[0];}, core_users_list()), - $ext_range = (strtoupper($range) === strtoupper(_("All"))) ? $sys_ext : self::getRange($range) - ); - if (count($extens) === 0) { - return false; - } - $sql = "DELETE FROM routepermissions WHERE exten=? AND routename=?"; - $stmt1 = $this->db->prepare($sql); - $sql = "INSERT INTO routepermissions (exten, routename, allowed, faildest, prefix) VALUES (?, ?, ?, ?, ?)"; - $stmt2 = $this->db->prepare($sql); - foreach ($extens as $ext) { - $res = $this->db->execute($stmt1, array($ext, $route)); - if (DB::isError($res) || $res === false) { - return $res; - } - $res = $this->db->execute($stmt2, array($ext, $route, $allowed, $faildest, $prefix)); - if (DB::isError($res) || $res === false) { - return $res; - } - } - return true; - } - - private static function getRange($range_str) { - $range_out = array(); - // Strip spaces - $ranges = explode(",", str_replace(" ", "", $range_str)); - - foreach($ranges as $range) { - if (is_numeric($range)) { - // Just a number; add it to the list. - $range_out[] = $range; - } elseif (strpos($range, "-")) { - list($start, $end) = explode("-", $range); - if (is_numeric($start) && is_numeric($end) && $start < $end) { - for ($i = $start; $i <= $end; $i++) { - $range_out[] = $i; - } - } - } - } - return array_unique($range_out, SORT_NUMERIC); - } -} - -/** - * Runs on reload to modify dialplan - */ -function routepermissions_hookGet_config($engine) -{ - global $ext; - global $version; - - if ($engine !== "asterisk") { - return false; - } - - foreach ($ext->_exts as $context=>$extensions) { - if (strncmp($context, "macro-dialout-", 14) === 0) { - $ext->splice($context, "s", 1, new ext_agi("checkperms.agi")); - $ext->add($context, "barred", 1, new ext_noop("Route administratively banned for this user.")); - $ext->add($context, "barred", 2, new ext_hangup()); - $ext->add($context, "reroute", 1, new ext_goto("1", "\${ARG2}", "from-internal")); - } - } - - // Insert the ROUTENAME into each route - $names = core_routing_list(); - foreach ($names as $name) { - $context = "outrt-$name[route_id]"; - $routename = $name["name"]; - $routes = core_routing_getroutepatternsbyid($name["route_id"]); - foreach ($routes as $rt) { - $extension = $rt["match_pattern_prefix"] . $rt["match_pattern_pass"]; - // If there are any wildcards in there, add a _ to the start - if (preg_match("/\.|z|x|\[|\]/i", $extension)) { - $extension = "_".$extension; - } - $ext->splice($context, $extension, 1, new ext_setvar("__ROUTENAME", $routename)); - } - } -} - -/** - * Runs when config.php loads a page up, used to modify extensions page - */ -function routepermissions_configpageinit($pagename) -{ - global $currentcomponent; - - if ($pagename === "extensions") { - $currentcomponent->addguifunc("routepermissions_configpageload"); - $currentcomponent->addprocessfunc("routepermissions_configpageprocess", 8); - } -} - -/** - * Adds some form inputs to the extension page - */ -function routepermissions_configpageload() -{ - global $db; - global $currentcomponent; - - $rp = new RoutepermissionsLegacy; - $pagename = isset($_REQUEST["display"]) ? $_REQUEST["display"] : "index"; - $extdisplay = isset($_REQUEST["extdisplay"]) ? $_REQUEST["extdisplay"] : null; - $i = 0; - - $section = _("Outbound Route Permissions"); - if ( - $pagename !== "extensions" || - (isset($_REQUEST["action"]) && $_REQUEST["action"] === "del") || - empty($extdisplay) - ) { - return false; - } - - $html = ""; - $routes = $rp->getRoutes(); - $stmt = $db->prepare("SELECT allowed, faildest, prefix FROM routepermissions WHERE routename = ? AND exten = ?"); - foreach ($routes as $route) { - // in 13 this is PDO, returns boolean; in 11 it's PEAR, returns a result object - $res = $db->execute($stmt, array($route, $extdisplay)); - if (DB::isError($res) || $res === false) { - continue; - } elseif ($res === true) { - list($allowed, $faildest, $prefix) = $stmt->fetch(PDO::FETCH_NUM); - } elseif ($res->numRows() === 1) { - list($allowed, $faildest, $prefix) = $res->fetchRow(); - } else { - $allowed = "YES"; - $faildest = ""; - } - if ($allowed === "NO" && !empty($prefix)) { - $allowed = "REDIRECT"; - } - $route = htmlspecialchars($route); - $yes = _("Allow"); - $no = _("Deny"); - $redirect = _("Redirect w/prefix"); - $i += 10; - $js = '$("input[name=" + this.name.replace(/^routepermissions_perm_(\d+)-(.*)$/, "routepermissions_prefix_$1-$2") + "]").val("").prop("disabled", (this.value !== this.name + "=REDIRECT")).prop("required", (this.value === this.name + "=REDIRECT"));var id=$("select[name=" + $("#" + this.name.replace(/^routepermissions_perm_(\d+)-(.*)$/, "routepermissions_faildest_$1-$2")).val() + "]").val("").change().prop("disabled", (this.value !== this.name + "=NO")).data("id");$("select[data-id=" + id + "]").prop("disabled", (this.value !== this.name + "=NO"))'; - $radio = new gui_radio( - "routepermissions_perm_$i-$route", //element name - array( - array("value"=>"YES", "text"=>$yes), - array("value"=>"NO", "text"=>$no), - array("value"=>"REDIRECT", "text"=>$redirect), - ), //radios - $allowed, //current value - sprintf(_("Allow access to %s"), $route), //group label text - "", //help text - false, //disable - htmlspecialchars($js), //onclick (13+) - "", //class (13+) - true //paired values; needed for 11 compatibility (13+) - ); - $currentcomponent->addguielem($section, $radio); - - $selects = new gui_drawselects( - "routepermissions_faildest_$i-$route", //element name - $i, //index - $faildest, //current value - sprintf(_("Failure destination for %s"), $route), //label text - "", //help text - false, //can be empty - "", //fail validation message - _("Use default"), //empty message - ($allowed !== "NO"), //disable (13+) - "" //class (13+) - ); - $currentcomponent->addguielem($section, $selects); - - $installed_ver = getVersion(); - $newer = version_compare_freepbx($installed_ver, "2.12","gt"); - - $input = new gui_textbox( - "routepermissions_prefix_$i-$route", //element name - $prefix, //current value - sprintf(_("Redirect prefix for %s"), $route), //label text - "", //help text - "", //js validation - "", //validation failure msg - true, //can be empty - 0, //maxchars - ($newer && $allowed !== "REDIRECT"), // disable; no way to re-enable in 11/12 - false, //input group (13+) - "", //class (13+) - true //autocomplete (13+) - ); - $currentcomponent->addguielem($section, $input); - } -} - -/** - * Runs when config.php is POSTed - */ -function routepermissions_configpageprocess() -{ - global $db; - - $action = isset($_POST['action']) ? $_POST['action'] : null; - $extdisplay = isset($_POST["extdisplay"]) ? $_POST["extdisplay"] : null; - $route_perms = array(); - if (empty($extdisplay) || empty($action)) { - return false; - } - - foreach ($_POST as $k=>$v) { - if (!preg_match("/routepermissions_(faildest|perm|prefix)_\d+-(.*)/", $k, $matches)) { - continue; - } - $route_name = $matches[2]; - if (!isset($route_perms[$route_name])) { - $route_perms[$route_name] = array("faildest"=>null, "perms"=>"YES", "prefix"=>null); - } - switch ($matches[1]) { - case "faildest": - $faildest_index = substr($v, 4); // remove "goto" from value - $faildest_type = isset($_POST[$v]) ? $_POST[$v] : null; - if ($faildest_type && isset($_POST["$faildest_type$faildest_index"])) { - $route_perms[$route_name]["faildest"] = $_POST["$faildest_type$faildest_index"]; - } - break; - case "perm": - list($foo, $perm) = explode("=", $v, 2); - $route_perms[$route_name]["perms"] = $perm; - break; - case "prefix": - $route_perms[$route_name]["prefix"] = $v; - break; - } - } - if (count($route_perms) === 0) { - return false; - } - - switch ($action) { - case "add": - case "edit": - $result = $db->query("DELETE FROM routepermissions WHERE exten = ?", $extdisplay); - $stmt = $db->prepare("INSERT INTO routepermissions (exten, routename, allowed, faildest, prefix) VALUES(?, ?, ?, ?, ?)"); - foreach($route_perms as $route_name=>$data) { - if ($data["perms"] === "REDIRECT") { - $data["faildest"] = null; - $data["perms"] = "NO"; - if (empty($data["prefix"])) { - $data["prefix"] = null; - } - } elseif ($data["perms"] === "NO") { - $data["prefix"] = null; - } else { - $data["perms"] = "YES"; - $data["faildest"] = $data["prefix"] = null; - } - $res = $db->execute($stmt, array( - $extdisplay, - $route_name, - $data["perms"], - $data["faildest"], - $data["prefix"], - )); - } - break; - case "del": - $db->query("DELETE FROM routepermissions WHERE exten = ?", $extdisplay); - break; - } -} - -?> diff --git a/install.php b/install.php deleted file mode 100644 index 8cff0b4..0000000 --- a/install.php +++ /dev/null @@ -1,117 +0,0 @@ -. -*/ - -global $db; - -// create the tables -$query = "CREATE TABLE IF NOT EXISTS routepermissions ( - exten INT(11) NOT NULL, - routename VARCHAR(25) NOT NULL, - allowed VARCHAR(3) DEFAULT 'YES', - faildest VARCHAR(255) DEFAULT '', - prefix VARCHAR(16) DEFAULT '', - INDEX idx_exten (exten), - INDEX idx_route (routename) -)"; - -$result = $db->query($query); -if (DB::IsError($result)) { - die_freepbx(sprintf(_("Error creating routepermissions table: %s"), $result->getMessage())); -} - -$result = $db->getRow("SELECT faildest FROM routepermissions LIMIT 1"); -if(DB::IsError($result)) { - // 0.3 - add 'faildest' - outn(_("Updating old database… ")); - $query = "ALTER TABLE routepermissions ADD faildest VARCHAR(255) DEFAULT ''"; - $result = $db->query($query); - if(DB::IsError($result)) { - die_freepbx(sprintf(_("Error updating routepermissions table: %s"), $result->getMessage())); - } - out(_("complete")); -} - -$result = $db->getRow("SELECT prefix FROM routepermissions LIMIT 1"); -if(DB::IsError($result)) { - // 1.0 - add 'prefix' and index on route name - outn(_("Updating old database… ")); - $query = "ALTER TABLE routepermissions ADD COLUMN prefix varchar(16)"; - $result = $db->query($query); - if(DB::IsError($result)) { - die_freepbx(sprintf(_("Error updating routepermissions table: %s"), $result->getMessage())); - } - $query = "UPDATE routepermissions SET prefix=faildest, faildest='' WHERE faildest RLIKE '^[a-d0-9*#]+$'"; - $result = $db->query($query); - if(DB::IsError($result)) { - die_freepbx(sprintf(_("Error updating routepermissions table: %s"), $result->getMessage())); - } - $query = "ALTER TABLE routepermissions ADD INDEX idx_route (routename)"; - $result = $db->query($query); - if(DB::IsError($result)) { - die_freepbx(sprintf(_("Error updating routepermissions table: %s"), $result->getMessage())); - } - out(_("complete")); -} - - -$query = "SELECT COUNT(exten) FROM routepermissions"; -$count = $db->getOne($query); -if ($count > 0) { - out(_("Found data, using existing route permissions")); -} else { - outn(_("New install, populating default allow permission… ")); - if (class_exists("FreePBX\\Modules\\Core")) { - $extens = array(); - $devices = \FreePBX::Core()->getAllUsersByDeviceType(); - foreach($devices as $exten) { - if ($exten->id) { - $extens[] = $exten->id; - } - } - } elseif (function_exists("core_devices_list")) { - $extens = array_map(function($u) {return $u[0];}, core_devices_list()); - } else { - die_freepbx(sprintf( - _("Error populating routepermissions table: %s"), - "no devices — tried core_devices_list and FreePBX::Core()->getAllUsersByDeviceType()" - )); - } - $query = "SELECT DISTINCT name FROM outbound_routes JOIN outbound_route_sequence USING (route_id) ORDER BY seq"; - $routes = $db->getCol($query); - $query = "INSERT INTO routepermissions (exten, routename, allowed, faildest) VALUES (?, ?, 'YES', '')"; - $stmt = $db->prepare($query); - if(!DB::IsError($routes) && !DB::IsError($stmt)) { - foreach($extens as $ext) { - foreach ($routes as $r) { - $result = $db->execute($stmt, array($ext, $r)); - if (DB::IsError($result)) { - die_freepbx(sprintf(_("Error populating routepermissions table: %s"), $result->getMessage())); - } - } - } - } else { - die_freepbx(sprintf( - _("Error populating routepermissions table: %s"), - DB::IsError($routes) ? $routes->getMessage() : $stmt->getMessage() - )); - } - out(_("complete")); -} -?> diff --git a/module.xml b/module.xml index 648723d..79aacf5 100644 --- a/module.xml +++ b/module.xml @@ -1,15 +1,15 @@ routepermissions Outbound Route Permissions - 1.0 + 1.5 Settings Outbound Permissions Point of Presence Allows you to block access to various routes, on a per-extension basis. - - *1.0* full rewrite for compatibility with 13 (tested working with 2.11 as well), store prefix in separate column, allow per-extension destinations and bulk destination changes + *1.5* remove all legacy code, version 13+ only + *1.0* full rewrite for compatibility with 13 (tested working with 11 as well), store prefix in separate column, allow per-extension destinations and bulk destination changes *0.4.0.0* #5220, updated to work with FreePBX 2.9 *0.3.2.5* add version dependency *0.3.2.4* Changed category to Third Party Addon, that name to be used by all modules in the Extended Repo (until they are brought into the main repo) @@ -23,10 +23,11 @@ *0.1.0.0* Original Release - GPL 3 - http://www.gnu.org/licenses/gpl-3.0.txt + AGPL 3 + http://www.gnu.org/licenses/agpl-3.0.txt - 2.11 + 13 asterisk ge 1.6.0 + 5.3 diff --git a/page.routepermissions.php b/page.routepermissions.php index ea745a4..b76a3b8 100644 --- a/page.routepermissions.php +++ b/page.routepermissions.php @@ -17,97 +17,8 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ - -$cwd = dirname(__FILE__); -$module = "routepermissions"; -$rp = new RoutepermissionsLegacy; -$message = ""; -$errormessage = ""; - -if(isset($_POST)) { - foreach ($_POST as $k=>$perm) { - if (strncmp($k, "permission_", 11) === 0) { - $route = substr($k, 11); - $redir = ""; - $prefix = ""; - switch ($perm) { - case "YES": - break; - case "NO": - if (isset($_POST["goto_$route"])) { - $type = $_POST["goto_$route"]; - if (isset($_POST["${type}_$route"])) { - $redir = $_POST["${type}_$route"]; - } - } - break; - case "REDIRECT": - $perm = "NO"; - $prefix = trim($_POST["prefix_$route"]); - if (empty($prefix)) { - $errormessage .= sprintf( - _("Redirect selected but redirect prefix missing for route %s - no action taken"), - $route - ); - $errormessage .= "
"; - continue; - } - break; - default: - continue 2; - } - $range = $_POST["range_$route"]; - $result = $rp->setRangePermissions($route, $range, $perm, $redir, $prefix); - if (DB::isError($result) || $result === false) { - $errormessage .= sprintf( - _("Database error, couldn't set permissions for route %s: %s"), - $route, - ($result === false) ? "" : $result->getMessage() - ); - $errormessage .= "
"; - } elseif ($prefix) { - $message .= sprintf( - _("Route %s set to %s for supplied range %s using redirect prefix %s"), - $route, - $perm, - $range, - $prefix - ); - $message .= "
"; - } else { - $message .= sprintf( - _("Route %s set to %s for supplied range %s"), - $route, - $perm, - $range - ); - $message .= "
"; - } - } elseif ($k == "update_default") { - $dest_type = $_POST["gotofaildest"]; - $dest = $_POST[$dest_type . "faildest"]; - $result = $rp->updateDefaultDest($dest); - if (DB::isError($result)) { - $errormessage = sprintf( - _("Database error, couldn't set default permissions: %s"), - $result->getMessage() - ); - } else { - $message = _("Default destination changed"); - } - } - } -} - -$viewdata = array( - "module"=>$module, - "message"=>$message, - "errormessage"=>$errormessage, - "rp"=>$rp, -); -if (interface_exists("BMO")) { - show_view("$cwd/views/settings13.php", $viewdata); -} else { - show_view("$cwd/views/settings.php", $viewdata); +if (!defined('FREEPBX_IS_AUTH')) { + die('No direct script access allowed'); } -?> \ No newline at end of file +$rp = \FreePBX::create()->Routepermissions; +$rp->showPage($_REQUEST); diff --git a/uninstall.php b/uninstall.php deleted file mode 100644 index 67e7ccf..0000000 --- a/uninstall.php +++ /dev/null @@ -1,36 +0,0 @@ -. -*/ - -outn(_("Removing routepermissions database table… ")); -$result = $db->query("DROP TABLE routepermissions"); -if (DB::IsError($result)) { - out(sprintf(_("Error removing routepermissions table: %s"), $result->getMessage())); -} else { - out(_("complete")); -} - -outn(_("Removing AGI script… ")); -$result = unlink($amp_conf["ASTAGIDIR"] . "/checkperms.agi"); -if (!$result) { - out(_("failed! File must be removed manually")); -} else { - out(_("complete")); -} -?> \ No newline at end of file diff --git a/views/settings.php b/views/settings.php deleted file mode 100644 index 5d7fc82..0000000 --- a/views/settings.php +++ /dev/null @@ -1,103 +0,0 @@ -. -*/ - -$html = "
"; -$html .= heading(_("Route Permissions"), 2); -if (!empty($message)) { - $html .= "

$message

"; -} -if (!empty($errormessage)) { - $html .= "

$errormessage

"; -} -$html .= "
"; -$html .= form_open("$_SERVER[PHP_SELF]?display=$module"); - -$html .= heading(_("Instructions"), 3); -$html .= "

"; -$html .= _("This module allows you to allow or deny access to certain routes from specified extensions. You can perform bulk changes on this page, and you can change an individual extension's access to routes on that extension's page."); -$html .= "

"; -$html .= "

"; -$html .= _("In addition to simple Allow/Deny rules, you can also deny access to a route and then redirect the call, allowing a different outbound route to match the call."); -$html .= "

"; -$html .= "

"; -$html .= _("For example, if you wanted to stop an extension from using Route A, selecting Deny would preclude the possibility of trying another route. Instead you could select Redirect with prefix and set the Redirect prefix to 9999; assuming you've created Route B with a prefix match of 9999 and not set a deny rule on it, the call can proceed."); -$html .= "

"; -$html .= "

"; -$html .= _("In addition, if you are denying access to a particular route and wish to use something other than the default destination, you can select Redirect with prefix, and create a Miscellaneous Application that matches the specified Redirect prefix. Using the previous example, a Miscellaneous Application with a feature code of _9999x. could be called if it existed on the system."); -$html .= "

"; - -$html .= heading(_("Bulk Changes"), 3); -$html .= "

"; -$html .= _("Select a route and select Allow or Deny to set permissions for the entered extensions. If you enter a Redirect prefix and click Redirect with prefix, the route will automatically be set to DENIED."); -$html .= _("You can enter one or more extensions or ranges separated by commas; a range is a start and end extension separated by a hyphen. For example 123,125,200-300 will select extensions 123 and 125 as well as any extensions between 200 and 300."); -$html .= "

"; -$html .= "

"; -$html .= _("Note that these changes take effect immediately and do not require a reload."); -$html .= "

"; - -$routes = $rp->getRoutes(); - -$table = new CI_Table; -$table->set_heading(array( - _("Route"), - _("Extensions"), - _("Permissions"), - _("Destination"), - _("Redirect Prefix"), -)); - -foreach ($routes as $r) { - $table->add_row(array( - array("data"=>$r, "id"=>"td_$r"), - form_input("range_$r", _("All"), "size=\"10\""), - "" . - form_radio("permission_$r", "", true, "id=\"permission_{$r}_SKIP\"") . - form_label(_("No change"), "permission_{$r}_SKIP") . - form_radio("permission_$r", "YES", false, "id=\"permission_{$r}_YES\"") . - form_label(_("Allow"), "permission_{$r}_YES") . - form_radio("permission_$r", "NO", false, "id=\"permission_{$r}_NO\"") . - form_label(_("Deny"), "permission_{$r}_NO") . - form_radio("permission_$r", "REDIRECT", false, "id=\"permission_{$r}_REDIRECT\"") . - form_label(_("Redirect w/prefix"), "permission_{$r}_REDIRECT") . - "", - drawselects("", "_$r", false, false, _("Use default")), - form_input("prefix_$r", "", sprintf("placeholder=\"%s\" size=\"10\"", _("Prefix"))), - )); -} -$table->add_row(array( - form_submit("update_permissions", _("Save Changes")) -)); -$html .= form_open("$_SERVER[PHP_SELF]?display=$module"); -$html .= $table->generate(); -$html .= form_close(); - -$html .= "

 

"; - -$html .= form_open("$_SERVER[PHP_SELF]?display=$module"); -$html .= heading(_("Default Destination if Denied"), 3); -$html .= "

"; -$html .= _("Select the destination for calls when they are denied without specifying a destination."); -$html .= "

"; -$html .= drawselects($rp->getDefaultDest(), "faildest"); -$html .= "
"; -$html .= form_submit("update_default", "Change Destination"); -$html .= form_close(); - -echo $html; diff --git a/views/settings13.php b/views/settings13.php index 1566ef1..fc94841 100644 --- a/views/settings13.php +++ b/views/settings13.php @@ -4,79 +4,19 @@ // Extensive modifications by Michael Newton (miken32@gmail.com) // Copyright 2016 Michael Newton /* - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ - -$html = ""; - -$html .= heading(_("Bulk Changes"), 4); -$html .= "

"; -$html .= _("Select a route and select Allow or Deny to set permissions for the entered extensions. If you enter a Redirect prefix and click Redirect with prefix, the route will automatically be set to DENIED."); -$html .= " "; -$html .= _("You can enter one or more extensions or ranges separated by commas; a range is a start and end extension separated by a hyphen. For example 123,125,200-300 will select extensions 123 and 125 as well as any extensions between 200 and 300."); -$html .= "

"; -$html .= "

"; -$html .= _("Note that these changes take effect immediately and do not require a reload."); -$html .= "

"; - -$routes = $rp->getRoutes(); - -$table = new CI_Table; -$table->set_heading(array( - _("Route"), - _("Extensions"), - _("Permissions"), - _("Destination"), - _("Redirect Prefix"), -)); - -foreach ($routes as $r) { - $table->add_row(array( - array("data"=>$r, "id"=>"td_$r"), - form_input("range_$r", _("All"), "size=\"10\""), - "" . - form_radio("permission_$r", "", true, "id=\"permission_{$r}_SKIP\"") . - form_label(_("No change"), "permission_{$r}_SKIP") . - form_radio("permission_$r", "YES", false, "id=\"permission_{$r}_YES\"") . - form_label(_("Allow"), "permission_{$r}_YES") . - form_radio("permission_$r", "NO", false, "id=\"permission_{$r}_NO\"") . - form_label(_("Deny"), "permission_{$r}_NO") . - form_radio("permission_$r", "REDIRECT", false, "id=\"permission_{$r}_REDIRECT\"") . - form_label(_("Redirect w/prefix"), "permission_{$r}_REDIRECT") . - "", - drawselects("", "_$r", false, false, _("Use default")), - form_input("prefix_$r", "", sprintf("placeholder=\"%s\" size=\"10\"", _("Prefix"))), - )); -} -$table->add_row(array( - form_submit("update_permissions", _("Save Changes")) -)); -$html .= form_open("$_SERVER[PHP_SELF]?display=$module"); -$html .= $table->generate(); -$html .= form_close(); - -$html .= "

 

"; - -$html .= form_open("$_SERVER[PHP_SELF]?display=$module"); -$html .= heading(_("Default Destination if Denied"), 4); -$html .= "

"; -$html .= _("Select the destination for calls when they are denied without specifying a destination."); -$html .= "

"; -$html .= drawselects($rp->getDefaultDest(), "faildest"); -$html .= form_submit("update_default", "Change Destination"); -$html .= form_close(); - ?>
@@ -84,27 +24,100 @@

-
-
-
-     -
-
-
-

-

-

Deny would preclude the possibility of trying another route. Instead you could select Redirect with prefix and set the Redirect prefix to 9999; assuming you've created Route B with a prefix match of 9999 and not set a deny rule on it, the call can proceed.");?>

-

Redirect with prefix, and create a Miscellaneous Application that matches the specified Redirect prefix. Using the previous example, a Miscellaneous Application with a feature code of _9999x. could be called if it existed on the system.");?>

-
+
+

+

+

Deny would preclude the possibility of trying another route. Instead you could select Redirect with prefix and set the Redirect prefix to 9999; assuming you've created Route B with a prefix match of 9999 and not set a deny rule on it, the call can proceed.");?>

+

Redirect with prefix, and create a Miscellaneous Application that matches the specified Redirect prefix. Using the previous example, a Miscellaneous Application with a feature code of _9999x. could be called if it existed on the system.");?>

- + - + +

+

+ Allow or Deny to set permissions for the entered extensions. If you enter a Redirect prefix and click Redirect with prefix, the route will automatically be set to DENIED.")?> + 123,125,200-300 will select extensions 123 and 125 as well as any extensions between 200 and 300.")?> +

+

+ immediately and do not require a reload.")?> +

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + class="form-control" type="text" size="10"> + + + + + + + + + + + + + + + " size="10"/> +
+ +
+
+

 

+
+

+

+ +

+

+ getDefaultDest(), "faildest")?> +

+

+ +

+
-