--- /dev/null
+<?xml version="1.0"?>\r
+<configuration>\r
+ <appSettings>\r
+ <add key="connectionString" value="DSN=easyroute;User=root;Password=;" />\r
+ <add key="defaultProfile" value="sofia/default"/>\r
+ <add key="defaultGateway" value="192.168.1.1"/>\r
+ <!-- customQuery can be defined. Fields must be in the same order, and the number parameter must be %number% -->\r
+ <!-- <add key="customQuery" value="SELECT gateways.gateway_ip, gateways.group, gateways.limit, gateways.techprofile, numbers.acctcode, numbers.translated from gateways, numbers where numbers.number = %number% and numbers.gateway_id = gateways.gateway_id;" /> -->\r
+ </appSettings>\r
+</configuration>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <PropertyGroup>\r
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>\r
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>\r
+ <ProductVersion>8.0.30703</ProductVersion>\r
+ <SchemaVersion>2.0</SchemaVersion>\r
+ <ProjectGuid>{7b5259d4-eaa0-4bcc-9eac-8dc054a4a092}</ProjectGuid>\r
+ <OutputType>Library</OutputType>\r
+ <RootNamespace>fs_easyroute</RootNamespace>\r
+ <AssemblyName>easyroute</AssemblyName>\r
+ <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>\r
+ <Name>EasyRoute</Name>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">\r
+ <DebugSymbols>true</DebugSymbols>\r
+ <DebugType>full</DebugType>\r
+ <Optimize>false</Optimize>\r
+ <Tailcalls>false</Tailcalls>\r
+ <OutputPath>bin\Debug\</OutputPath>\r
+ <DefineConstants>DEBUG;TRACE</DefineConstants>\r
+ <WarningLevel>3</WarningLevel>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">\r
+ <DebugType>pdbonly</DebugType>\r
+ <Optimize>true</Optimize>\r
+ <Tailcalls>true</Tailcalls>\r
+ <OutputPath>bin\Release\</OutputPath>\r
+ <DefineConstants>TRACE</DefineConstants>\r
+ <WarningLevel>3</WarningLevel>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">\r
+ <Tailcalls>false</Tailcalls>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">\r
+ <OtherFlags>\r
+ </OtherFlags>\r
+ <WarningLevel>4</WarningLevel>\r
+ </PropertyGroup>\r
+ <ItemGroup>\r
+ <Compile Include="easyroute.fs" />\r
+ <Content Include="App.config" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <Reference Include="FreeSWITCH.Managed, Version=1.0.4.0, Culture=neutral, PublicKeyToken=null">\r
+ <Name>FreeSWITCH.Managed</Name>\r
+ <AssemblyName>FreeSWITCH.Managed.dll</AssemblyName>\r
+ <SpecificVersion>False</SpecificVersion>\r
+ <HintPath>..\..\FreeSWITCH.Managed.dll</HintPath>\r
+ <Private>True</Private>\r
+ </Reference>\r
+ <Reference Include="FSharp.PowerPack, Version=1.9.6.16, Culture=neutral, PublicKeyToken=a19089b1c74d0809">\r
+ <Name>FSharp.PowerPack</Name>\r
+ <AssemblyName>FSharp.PowerPack.dll</AssemblyName>\r
+ <SpecificVersion>False</SpecificVersion>\r
+ </Reference>\r
+ <Reference Include="mscorlib" />\r
+ <Reference Include="FSharp.Core" />\r
+ <Reference Include="System" />\r
+ <Reference Include="System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">\r
+ <Name>System.Configuration</Name>\r
+ <AssemblyName>System.configuration.dll</AssemblyName>\r
+ <SpecificVersion>False</SpecificVersion>\r
+ </Reference>\r
+ <Reference Include="System.Core">\r
+ <RequiredTargetFramework>3.5</RequiredTargetFramework>\r
+ </Reference>\r
+ <Reference Include="System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">\r
+ <Name>System.Data</Name>\r
+ <AssemblyName>System.Data.dll</AssemblyName>\r
+ <SpecificVersion>False</SpecificVersion>\r
+ </Reference>\r
+ <Reference Include="System.Transactions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">\r
+ <Name>System.Transactions</Name>\r
+ <AssemblyName>System.Transactions.dll</AssemblyName>\r
+ <SpecificVersion>False</SpecificVersion>\r
+ </Reference>\r
+ </ItemGroup>\r
+ <Import Project="$(MSBuildExtensionsPath)\FSharp\1.0\Microsoft.FSharp.Targets" />\r
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r
+ Other similar extension points exist, see Microsoft.Common.targets.\r
+ <Target Name="BeforeBuild">\r
+ </Target>\r
+ <Target Name="AfterBuild">\r
+ </Target>\r
+ -->\r
+</Project>
\ No newline at end of file
--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 10.00\r
+# Visual Studio 2008\r
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "EasyRoute", "EasyRoute.fsproj", "{7B5259D4-EAA0-4BCC-9EAC-8DC054A4A092}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|Any CPU = Debug|Any CPU\r
+ Release|Any CPU = Release|Any CPU\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {7B5259D4-EAA0-4BCC-9EAC-8DC054A4A092}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\r
+ {7B5259D4-EAA0-4BCC-9EAC-8DC054A4A092}.Debug|Any CPU.Build.0 = Debug|Any CPU\r
+ {7B5259D4-EAA0-4BCC-9EAC-8DC054A4A092}.Release|Any CPU.ActiveCfg = Release|Any CPU\r
+ {7B5259D4-EAA0-4BCC-9EAC-8DC054A4A092}.Release|Any CPU.Build.0 = Release|Any CPU\r
+ EndGlobalSection\r
+ GlobalSection(SolutionProperties) = preSolution\r
+ HideSolutionNode = FALSE\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+<?xml version="1.0"?>\r
+<configuration>\r
+ <appSettings>\r
+ <add key="connectionString" value="DSN=easyroute;User=root;Password=;" />\r
+ <add key="defaultProfile" value="sofia/default"/>\r
+ <add key="defaultGateway" value="192.168.1.1"/>\r
+ <!-- customQuery can be defined. Fields must be in the same order, and the number parameter must be %number% -->\r
+ <!-- <add key="customQuery" value="SELECT gateways.gateway_ip, gateways.group, gateways.limit, gateways.techprofile, numbers.acctcode, numbers.translated from gateways, numbers where numbers.number = %number% and numbers.gateway_id = gateways.gateway_id;" /> -->\r
+ </appSettings>\r
+</configuration>
\ No newline at end of file
--- /dev/null
+// To get unixodbc to work on Mono Linux, you may need to create an odbc32.dll link: ln -s /lib64/libodbc.so /lib64/odbc32.dll\r
+open System\r
+open System.Data\r
+open FreeSWITCH\r
+\r
+type QueryResult = { dialstring: string; group: string; acctcode: string; limit: int; translated: string }\r
+\r
+module easyroute =\r
+ let getAppSetting (name:string) = match Configuration.ConfigurationManager.AppSettings.Get name with null -> "" | x -> x\r
+ let connString = getAppSetting "connectionString"\r
+ let defaultProfile = getAppSetting "defaultProfile"\r
+ let defaultGateway = getAppSetting "defaultGateway"\r
+ let query = match getAppSetting "customQuery" with\r
+ | "" -> "SELECT gateways.gateway_ip, gateways.group, gateways.limit, gateways.techprofile, numbers.acctcode, numbers.translated from gateways, numbers where numbers.number = %number% and numbers.gateway_id = gateways.gateway_id;"\r
+ | x -> x \r
+ let configOk = [ connString; defaultProfile; defaultGateway; query; ] |> List.forall (String.IsNullOrEmpty >> not)\r
+ \r
+ let formatDialstring number gateway profile separator = \r
+ match separator with \r
+ | None -> sprintf "%s/%s%s" profile number gateway\r
+ | Some s -> sprintf "%s/%s%s%s" profile number s gateway\r
+\r
+ let getDefaultResult number sep = { \r
+ dialstring = formatDialstring number defaultGateway defaultProfile sep;\r
+ limit = 9999; group = ""; acctcode = ""; translated = number; }\r
+ \r
+ let readResult (r: IDataReader) number sep =\r
+ let defString def = function null | "" -> def | s -> s\r
+ let gw = defString defaultGateway <| r.GetString(0)\r
+ let group = r.GetString(1)\r
+ let limit = match r.GetInt32(2) with 0 -> 9999 | x -> x\r
+ let profile = defString defaultProfile <| r.GetString(3)\r
+ let acctcode = r.GetString(4)\r
+ let translated = r.GetString(5)\r
+ let dialstring = formatDialstring number gw profile sep\r
+ { dialstring = dialstring; limit = limit; group = group; acctcode = acctcode; translated = translated; }\r
+\r
+ let lookup (number: string) sep =\r
+ try\r
+ let query = query.Replace("%number%", sprintf "'%s'" (number.Replace(@"\'", "'").Replace("'", "''"))) // Don't use params cause some odbc drivers are awesome\r
+ Log.WriteLine(LogLevel.Debug, "EasyRoute query prepared: {0}", query)\r
+ use conn = new Odbc.OdbcConnection(connString)\r
+ use comm = new Odbc.OdbcCommand(query, conn)\r
+ conn.Open()\r
+ use reader = comm.ExecuteReader CommandBehavior.SingleRow\r
+ match reader.Read() with\r
+ | true -> readResult reader number sep\r
+ | false -> Log.WriteLine(LogLevel.Error, "No records for {0}; setting default route.", number)\r
+ getDefaultResult number sep\r
+ with ex -> Log.WriteLine(LogLevel.Error, "Exception getting route for {0}. Setting default route. Exception: {1}", number, ex.ToString())\r
+ getDefaultResult number sep\r
+\r
+ // Returns tuple: number * separator option * field option\r
+ let parseArgs args = \r
+ let args = String.split [' '] args\r
+ let num = List.hd args\r
+ let opt = Map.of_list (List.tl args |> List.map (fun x -> match x.Split([|'='|], 2) with \r
+ | [|n;v|] -> n, Some v \r
+ | arr -> arr.[0], None))\r
+ (num, defaultArg (opt.TryFind "separator") (Some "@"), defaultArg (opt.TryFind "field") (None))\r
+open easyroute\r
+\r
+type EasyRoute() =\r
+ interface ILoadNotificationPlugin with\r
+ member x.Load() = \r
+ if not configOk then Log.WriteLine(LogLevel.Alert, "EasyRoute configuration is missing values.")\r
+ configOk\r
+ \r
+ interface IApiPlugin with\r
+ member x.ExecuteBackground ctx = \r
+ Log.WriteLine(LogLevel.Error, "Background execution not supported for EasyRoute.")\r
+ member x.Execute ctx = \r
+ let num, sep, field = parseArgs ctx.Arguments\r
+ let res = lookup num sep\r
+ let sw = ctx.Stream.Write \r
+ match field with\r
+ | None -> sw "Number \tLimit \tGroup \tAcctCode \tDialstring\n"\r
+ sw (sprintf "%-10s\t%-10d\t%-10s\t%-10s\t%s\n" res.translated res.limit res.group res.acctcode res.dialstring)\r
+ | Some "dialstring" -> sw res.dialstring\r
+ | Some "translated" -> sw res.translated\r
+ | Some "limit" -> sw (string res.limit)\r
+ | Some "group" -> sw res.group\r
+ | Some "acctcode" -> sw res.acctcode \r
+ | _ -> sw "Invalid input!\n"\r
+ \r
+ interface IAppPlugin with\r
+ member x.Run ctx = \r
+ let num, sep, field = parseArgs ctx.Arguments\r
+ let res = lookup num sep\r
+ [ "easy_destnum", res.translated; "easy_dialstring", res.dialstring; "easy_group", res.group; "easy_limit", string res.limit; "easy_acctcode", res.acctcode]\r
+ |> List.iter ctx.Session.SetVariable\r