<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[Freeswitch-trunk][17448] </title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<div id="header">FreeSWITCH Subversion</div>
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://fisheye.freeswitch.org/changelog/FreeSWITCH?cs=17448">17448</a></dd>
<dt>Author</dt> <dd>brian</dd>
<dt>Date</dt> <dd>2010-04-28 22:22:28 -0500 (Wed, 28 Apr 2010)</dd>
</dl>

<h3>Log Message</h3>
<pre>Check in mod_osp from transnexus thank you.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#freeswitchtrunkbuildmodulesconfin">freeswitch/trunk/build/modules.conf.in</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#freeswitchtrunkconfautoload_configsospconfxml">freeswitch/trunk/conf/autoload_configs/osp.conf.xml</a></li>
<li>freeswitch/trunk/src/mod/applications/mod_osp/</li>
<li><a href="#freeswitchtrunksrcmodapplicationsmod_ospMakefileam">freeswitch/trunk/src/mod/applications/mod_osp/Makefile.am</a></li>
<li><a href="#freeswitchtrunksrcmodapplicationsmod_ospmod_ospc">freeswitch/trunk/src/mod/applications/mod_osp/mod_osp.c</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="freeswitchtrunkbuildmodulesconfin"></a>
<div class="modfile"><h4>Modified: freeswitch/trunk/build/modules.conf.in (17447 => 17448)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/build/modules.conf.in        2010-04-28 22:24:03 UTC (rev 17447)
+++ freeswitch/trunk/build/modules.conf.in        2010-04-29 03:22:28 UTC (rev 17448)
</span><span class="lines">@@ -6,6 +6,7 @@
</span><span class="cx"> applications/mod_conference
</span><span class="cx"> applications/mod_dptools
</span><span class="cx"> applications/mod_enum
</span><ins>+#applications/mod_osp
</ins><span class="cx"> applications/mod_fifo
</span><span class="cx"> #applications/mod_fax
</span><span class="cx"> #applications/mod_curl
</span></span></pre></div>
<a id="freeswitchtrunkconfautoload_configsospconfxml"></a>
<div class="addfile"><h4>Added: freeswitch/trunk/conf/autoload_configs/osp.conf.xml (0 => 17448)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/conf/autoload_configs/osp.conf.xml                                (rev 0)
+++ freeswitch/trunk/conf/autoload_configs/osp.conf.xml        2010-04-29 03:22:28 UTC (rev 17448)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+&lt;configuration name=&quot;osp.conf&quot; description=&quot;OSP Module Configuration&quot;&gt;
+        &lt;settings&gt;
+                &lt;!-- Debug info flag --&gt;
+                &lt;param name=&quot;debug-info&quot; value=&quot;disabled&quot;/&gt;
+                &lt;!-- Log level for debug info --&gt;
+                &lt;param name=&quot;log-level&quot; value=&quot;info&quot;/&gt;
+                &lt;!-- Crypto hareware accelerate is disabled by default --&gt;
+                &lt;param name=&quot;crypto-hardware&quot; value=&quot;disabled&quot;/&gt;
+                &lt;!-- SIP settings --&gt;
+                &lt;param name=&quot;sip&quot; module=&quot;sofia&quot; profile=&quot;external&quot;/&gt;
+                &lt;!-- H.323 settings --&gt;
+                &lt;!-- &lt;param name=&quot;h323&quot; module=&quot;h323&quot; profile=&quot;external&quot;/&gt; --&gt;
+                &lt;!-- IAX settings --&gt;
+                &lt;!-- &lt;param name=&quot;iax&quot; module=&quot;iax&quot; profile=&quot;external&quot;/&gt; --&gt;
+                &lt;!-- Skype settings --&gt;
+                &lt;!-- &lt;param name=&quot;skype&quot; module=&quot;skypopen&quot; profile=&quot;external&quot;/&gt; --&gt;
+                &lt;!-- Default destination protocol --&gt;
+                &lt;param name=&quot;default-protocol&quot; value=&quot;sip&quot;/&gt;
+        &lt;/settings&gt;
+
+        &lt;profiles&gt;
+        &lt;!-- Default OSP provider profile --&gt;
+                &lt;profile name=&quot;default&quot;&gt;
+                        &lt;!-- Service point URLs, up to 8 allowed --&gt;
+                        &lt;!-- &lt;param name=&quot;service-point-url&quot; value=&quot;http://osptestserver.transnexus.com:1080/osp&quot;/&gt; --&gt;
+                        &lt;!-- &lt;param name=&quot;service-point-url&quot; value=&quot;https://127.0.0.1:1443/osp&quot;/&gt; --&gt;
+                        &lt;param name=&quot;service-point-url&quot; value=&quot;http://127.0.0.1:1080/osp&quot;/&gt;
+
+                        &lt;!-- FreeSWITCH IP address for OSP --&gt;
+                        &lt;param name=&quot;device-ip&quot; value=&quot;127.0.0.1:5080&quot;/&gt;
+
+                        &lt;!-- SSL lifetime in seconds --&gt;
+                        &lt;param name=&quot;ssl-lifetime&quot; value=&quot;300&quot;/&gt;
+                        &lt;!-- HTTP max connections, 1~1000 --&gt;
+                        &lt;param name=&quot;http-max-connections&quot; value=&quot;20&quot;/&gt;
+                        &lt;!-- HTTP persistence in seconds --&gt;
+                        &lt;param name=&quot;http-persistence&quot; value=&quot;60&quot;/&gt;
+                        &lt;!-- HTTP retry delay in seconds, 0~10 --&gt;
+                        &lt;param name=&quot;http-retry-delay&quot; value=&quot;0&quot;/&gt;
+                        &lt;!-- HTTP retry limit, 0~100 --&gt;
+                        &lt;param name=&quot;http-retry-limit&quot; value=&quot;2&quot;/&gt;
+                        &lt;!-- HTTP timeout in milliseconds, 200~60000 --&gt;
+                        &lt;param name=&quot;http-timeout&quot; value=&quot;10000&quot;/&gt;
+
+                        &lt;!-- OSP service type, voice or npquery --&gt;
+                        &lt;param name=&quot;service-type&quot; value=&quot;voice&quot;/&gt;
+                        &lt;!-- Max number of destinations --&gt;
+                        &lt;param name=&quot;max-destinations&quot; value=&quot;5&quot;/&gt;
+
+                        &lt;!-- SIP features --&gt;
+                        &lt;!-- Add &quot;user=phone&quot; URI parameter in outbound SIP messages --&gt;
+                        &lt;param name=&quot;user-phone&quot; value=&quot;disabled&quot;/&gt;
+                &lt;/profile&gt;
+        &lt;/profiles&gt;
+&lt;/configuration&gt;
</ins></span></pre></div>
<a id="freeswitchtrunksrcmodapplicationsmod_ospMakefileam"></a>
<div class="addfile"><h4>Added: freeswitch/trunk/src/mod/applications/mod_osp/Makefile.am (0 => 17448)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/src/mod/applications/mod_osp/Makefile.am                                (rev 0)
+++ freeswitch/trunk/src/mod/applications/mod_osp/Makefile.am        2010-04-29 03:22:28 UTC (rev 17448)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+include $(top_srcdir)/build/modmake.rulesam
+MODNAME=mod_osp
+
+mod_LTLIBRARIES = mod_osp.la
+mod_osp_la_SOURCES  = mod_osp.c 
+mod_osp_la_CFLAGS   = $(AM_CFLAGS)
+mod_osp_la_LDFLAGS  = -avoid-version -module -no-undefined -shared -losptk -lssl -lcrypto -lpthread -lm
+
</ins></span></pre></div>
<a id="freeswitchtrunksrcmodapplicationsmod_ospmod_ospc"></a>
<div class="addfile"><h4>Added: freeswitch/trunk/src/mod/applications/mod_osp/mod_osp.c (0 => 17448)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/src/mod/applications/mod_osp/mod_osp.c                                (rev 0)
+++ freeswitch/trunk/src/mod/applications/mod_osp/mod_osp.c        2010-04-29 03:22:28 UTC (rev 17448)
</span><span class="lines">@@ -0,0 +1,2197 @@
</span><ins>+/*
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005-2009, Anthony Minessale II &lt;anthm@freeswitch.org&gt;
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the &quot;License&quot;); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an &quot;AS IS&quot; basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II &lt;anthm@freeswitch.org&gt;
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Di-Shi Sun &lt;di-shi@transnexus.com&gt;
+ *
+ * mod_osp.c -- Open Settlement Protocol (OSP) Module
+ *
+ */
+
+/*
+ * TODO:
+ * 1. NID -&gt; outbound messages
+ */
+
+#include &lt;switch.h&gt;
+#include &lt;osp/osp.h&gt;
+#include &lt;osp/ospb64.h&gt;
+#include &lt;osp/osptrans.h&gt;
+
+/* OSP Buffer Size Constants */
+#define OSP_SIZE_NORSTR                256                /* OSP normal string buffer size */
+#define OSP_SIZE_KEYSTR                1024        /* OSP certificate string buffer size */
+#define OSP_SIZE_ROUSTR                1024        /* OSP route buffer size */
+#define OSP_SIZE_TOKSTR                4096        /* OSP token string buffer size */
+
+/* OSP Settings Constants */
+#define OSP_MAX_SP                        8                        /* Max number of OSP service points */
+#define OSP_AUDIT_URL                &quot;localhost&quot;        /* OSP default Audit URL */
+#define OSP_LOCAL_VALID                1                        /* OSP token validating method, locally */
+#define OSP_DEF_LIFETIME        300                        /* OSP default SSL lifetime */
+#define OSP_DEF_MAXCONN                20                        /* OSP default max connections */
+#define OSP_MIN_MAXCONN                1                        /* OSP min max connections */
+#define OSP_MAX_MAXCONN                1000                /* OSP max max connections */
+#define OSP_DEF_PERSIST                60                        /* OSP default HTTP persistence in seconds */
+#define OSP_DEF_RETRYDELAY        0                        /* OSP default retry delay in seconds */
+#define OSP_MIN_RETRYDELAY        0                        /* OSP min retry delay */
+#define OSP_MAX_RETRYDELAY        10                        /* OSP max retry delay */
+#define OSP_DEF_RETRYLIMIT        2                        /* OSP default retry times */
+#define OSP_MIN_RETRYLIMIT        0                        /* OSP min retry times */
+#define OSP_MAX_RETRYLIMIT        100                        /* OSP max retry times */
+#define OSP_DEF_TIMEOUT                10000                /* OSP default timeout in ms */
+#define OSP_MIN_TIMEOUT                200                        /* OSP min timeout in ms */
+#define OSP_MAX_TIMEOUT                60000                /* OSP max timeout in ms */
+#define OSP_CUSTOMER_ID                &quot;&quot;                        /* OSP customer ID */
+#define OSP_DEVICE_ID                &quot;&quot;                        /* OSP device ID */
+#define OSP_DEF_MAXDEST                5                        /* OSP default max destinations */
+#define OSP_MIN_MAXDEST                1                        /* OSP min max destinations */
+#define OSP_MAX_MAXDEST                10                        /* OSP max max destinations */
+#define OSP_DEF_PROFILE                &quot;default&quot;        /* OSP default profile name */
+#define OSP_DEF_STRING                &quot;&quot;                        /* OSP default empty string */
+#define OSP_DEF_CALLID                &quot;UNDEFINED&quot;        /* OSP default Call-ID */
+#define OSP_DEF_STATS                -1                        /* OSP default statistics */
+#define OSP_URI_DELIM                '@'                        /* URI delimit */
+#define OSP_USER_DELIM                &quot;;:&quot;                /* URI userinfo delimit */
+#define OSP_HOST_DELIM                &quot;;&gt;&quot;                /* URI hostport delimit */
+#define OSP_MAX_CINFO                8                        /* Max number of custom info */
+
+/* OSP Handle Constant */
+#define OSP_INVALID_HANDLE        -1        /* Invalid OSP handle, provider, transaction etc. */
+
+/* OSP Supported Destination Protocols for Default Protocol */
+#define OSP_PROTOCOL_SIP        &quot;sip&quot;                        /* SIP protocol name */
+#define OSP_PROTOCOL_H323        &quot;h323&quot;                        /* H.323 protocol name */
+#define OSP_PROTOCOL_IAX        &quot;iax&quot;                        /* IAX protocol name */
+#define OSP_PROTOCOL_SKYPE        &quot;skype&quot;                        /* Skype protocol name */
+#define OSP_PROTOCOL_UNKNO        &quot;unknown&quot;                /* Unknown protocol */
+#define OSP_PROTOCOL_UNDEF        &quot;undefined&quot;                /* Undefined protocol */
+#define OSP_PROTOCOL_UNSUP        &quot;unsupported&quot;        /* Unsupported protocol */
+
+/* OSP Supported Destination Protocols for Destination Protocol Usage */
+#define OSP_MODULE_SIP                &quot;mod_sofia&quot;                /* FreeSWITCH SIP module name */
+#define OSP_MODULE_H323                &quot;mod_h323&quot;                /* FreeSWITCH H.323 module name */
+#define OSP_MODULE_IAX                &quot;mod_iax&quot;                /* FreeSWITCH IAX module name */
+#define OSP_MODULE_SKYPE        &quot;mod_skypopen&quot;        /* FreeSWITCH Skype module name */
+
+/* OSP Variables Name */
+#define OSP_VAR_PROFILE                &quot;osp_profile&quot;                        /* Provider name, in OSP cookie */
+#define OSP_VAR_TRANSID                &quot;osp_transaction_id&quot;        /* Transaction ID, in OSP cookie */
+#define OSP_VAR_START                &quot;osp_start_time&quot;                /* Inbound Call start time, in OSP cookie */
+#define OSP_VAR_DESTCOUNT        &quot;osp_destination_count&quot;        /* Destination count, in OSP cookie */
+#define OSP_VAR_SRCNID                &quot;osp_source_nid&quot;                /* Source network ID, inbound and in OSP cookie */
+#define OSP_VAR_DESTIP                &quot;osp_destination_ip&quot;        /* Destination IP, in OSP cookie */
+#define OSP_VAR_DESTNID                &quot;osp_destination_nid&quot;        /* Destination network ID, in OSP cookie */
+#define OSP_VAR_CUSTOMINFO        &quot;osp_custom_info_&quot;                /* Custom info */
+#define OSP_VAR_ROUTECOUNT        &quot;osp_route_count&quot;                /* Number of destinations */
+#define OSP_VAR_ROUTEPRE        &quot;osp_route_&quot;                        /* Destination prefix */
+#define OSP_VAR_AUTOROUTE        &quot;osp_auto_route&quot;                /* Bridge route string */
+
+/* OSP Use Variable Name */
+#define OSP_FS_TOHOST                &quot;sip_to_host&quot;                                                /* Inbound SIP To host */
+#define OSP_FS_TOPORT                &quot;sip_to_port&quot;                                                /* Inbound SIP To port */
+#define OSP_FS_DIVERSION        &quot;sip_h_Diversion&quot;                                        /* Inbound SIP Diversion header */
+#define OSP_FS_OUTCALLID        &quot;sip_call_id&quot;                                                /* Outbound SIP Call-ID */
+#define OSP_FS_OUTCALLING        &quot;origination_caller_id_number&quot;                /* Outbound calling number */
+#define OSP_FS_SIPRELEASE        &quot;sip_hangup_disposition&quot;                        /* SIP release source */
+#define OSP_FS_DOWNCODEC        &quot;write_codec&quot;                                                /* Downstream codec */
+#define OSP_FS_UPCODEC                &quot;read_codec&quot;                                                /* Upstream codec */
+#define OSP_FS_RTPDOWNOCTS        &quot;rtp_audio_out_media_bytes&quot;                        /* Downstream octets */
+#define OSP_FS_RTPUPOCTS        &quot;rtp_audio_in_media_bytes&quot;                        /* Upstream octets */
+#define OSP_FS_RTPDOWNPKTS        &quot;rtp_audio_out_media_packet_count&quot;        /* Downstream packets */
+#define OSP_FS_RTPUPPKTS        &quot;rtp_audio_in_media_packet_count&quot;        /* Upstream packets */
+
+typedef struct osp_settings {
+        switch_bool_t debug;                                        /* OSP module debug info flag */
+        switch_log_level_t loglevel;                        /* Log level for debug info */
+        switch_bool_t hardware;                                        /* Crypto hardware flag */
+        const char *module[OSPC_DPROT_NUMBER];        /* Endpoint names */
+        const char *profile[OSPC_DPROT_NUMBER];        /* Profile names */
+        OSPE_DEST_PROTOCOL protocol;                        /* Default destination protocol */
+        switch_bool_t shutdown;                                        /* OSP module status */
+        switch_memory_pool_t *pool;                                /* OSP module memory pool */
+} osp_settings_t;
+
+/* OSP Service Types */
+typedef enum osp_srvtype {
+        OSP_SRV_VOICE = 0,        /* Normal voice service */
+        OSP_SRV_NPQUERY                /* Number portability query service */
+} osp_srvtype_t;
+
+typedef struct osp_provider {
+        const char *name;                                /* OSP provider profile name */
+        int spnum;                                                /* Number of OSP service points */
+        const char *spurls[OSP_MAX_SP];        /* OSP provider service point URLs */
+        const char *device;                                /* OSP source IP */
+        int lifetime;                                        /* SSL life time */
+        int maxconnect;                                        /* Max number of HTTP connections */
+        int persistence;                                /* HTTP persistence in seconds */
+        int retrydelay;                                        /* HTTP retry delay in seconds */
+        int retrylimit;                                        /* HTTP retry times */
+        int timeout;                                        /* HTTP timeout in ms */
+        osp_srvtype_t srvtype;                        /* OSP service type */
+        int maxdest;                                        /* Max destinations */
+        switch_bool_t userphone;                /* Add &quot;user=phone&quot; URI parameter */
+        OSPTPROVHANDLE handle;                        /* OSP provider handle */
+        struct osp_provider *next;                /* Next OSP provider */
+} osp_provider_t;
+
+typedef struct osp_inbound {
+        const char *srcdev;                                        /* Source device IP address */
+        const char *calling;                                /* Inbound calling number */
+        char called[OSP_SIZE_NORSTR];                /* Inbound called number */
+        char nprn[OSP_SIZE_NORSTR];                        /* Inbound NP routing number */
+        char npcic[OSP_SIZE_NORSTR];                /* Inbound NP carrier identification code */
+        int npdi;                                                        /* Inbound NP database dip indicator */
+        const char *tohost;                                        /* Inbound host of To URI */
+        const char *toport;                                        /* Inbound port of To URI */
+        char divuser[OSP_SIZE_NORSTR];                /* Inbound user of SIP Diversion header */
+        char divhost[OSP_SIZE_NORSTR];                /* Inbound hostport of SIP Diversion header */
+        const char *srcnid;                                        /* Inbound source network ID */
+        switch_time_t start;                                /* Call start time */
+        const char *cinfo[OSP_MAX_CINFO];        /* Custom info */
+} osp_inbound_t;
+
+typedef struct osp_destination {
+        unsigned int timelimit;                                                                /* Outbound duration limit */
+        char dest[OSP_SIZE_NORSTR];                                                        /* Destination IP address */
+        char calling[OSP_SIZE_NORSTR];                                                /* Outbound calling number, may be translated */
+        char called[OSP_SIZE_NORSTR];                                                /* Outbound called number, may be translated */
+        char destnid[OSP_SIZE_NORSTR];                                                /* Destination network ID */
+        char nprn[OSP_SIZE_NORSTR];                                                        /* Outbound NP routing number */
+        char npcic[OSP_SIZE_NORSTR];                                                /* Outbound NP carrier identification code */
+        int npdi;                                                                                        /* Outbound NP database dip indicator */
+        char opname[OSPC_OPNAME_NUMBER][OSP_SIZE_NORSTR];        /* Outbound Operator names */
+        OSPE_DEST_PROTOCOL protocol;                                                /* Destination protocol */
+        switch_bool_t supported;                                                        /* Supported by FreeRADIUS OSP module */
+        switch_bool_t userphone;                                                        /* Add &quot;user=phone&quot; parameter */
+} osp_destination_t;
+
+typedef struct osp_results {
+        const char *profile;                                        /* Provider name */
+        unsigned long long transid;                                /* Transaction ID */
+        switch_time_t start;                                        /* Call start time */
+        char called[OSP_SIZE_NORSTR];                        /* Original called number */
+        const char *srcnid;                                                /* Source network ID */
+        int numdest;                                                        /* Number of destinations */
+        osp_destination_t dests[OSP_MAX_SP];        /* Destinations */
+} osp_results_t;
+
+typedef struct osp_cookie {
+        const char *profile;                /* Provider name */
+        unsigned long long transid;        /* Transaction ID */
+        switch_time_t start;                /* Call start time */
+        int destcount;                                /* Destination count */
+        const char *dest;                        /* Destination IP */
+        const char *srcnid;                        /* Source network ID */
+        const char *destnid;                /* Destination network ID */
+} osp_cookie_t;
+
+typedef struct osp_usage {
+        const char *callid;                                /* Call-ID */
+        const char *calling;                        /* Calling number */
+        char called[OSP_SIZE_NORSTR];        /* Called number */
+        const char *srcdev;                                /* Source device IP */
+        OSPE_DEST_PROTOCOL protocol;        /* Destination protocol */
+        int release;                                        /* Release source */
+        switch_call_cause_t cause;                /* Termination cause */
+        switch_time_t alert;                        /* Call alert time */
+        switch_time_t connect;                        /* Call answer time */
+        switch_time_t end;                                /* Call end time */
+        switch_time_t duration;                        /* Call duration */
+        switch_time_t pdd;                                /* Post dial delay */
+        const char *fcodec;                                /* Forward codec */
+        const char *rcodec;                                /* Reverse codec */
+        int rtpdownoctets;                                /* RTP downstream bytes */
+        int rtpupoctets;                                /* RTP upstream bytes */
+        int rtpdownpackets;                                /* RTP downstream packets */
+        int rtpuppackets;                                /* RTP upstream packets */
+} osp_usage_t;
+
+typedef struct osp_threadarg {
+        OSPTTRANHANDLE handle;                /* Transaction handle */
+        unsigned long long transid;        /* Transaction ID */
+        switch_call_cause_t cause;        /* Release code */
+        time_t start;                                /* Call start time */
+        time_t alert;                                /* Call alert time */
+        time_t connect;                                /* Call connect time */
+        time_t end;                                        /* Call end time */
+        int duration;                                /* Call duration */
+        int pdd;                                        /* Post dial delay */
+        int release;                                /* EP that released the call */
+} osp_threadarg_t;
+
+/* OSP module global settings */
+static osp_settings_t osp_globals;
+
+/* OSP module providers */
+static osp_provider_t *osp_providers = NULL;
+
+/* switch_status_t mod_osp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)  */
+SWITCH_MODULE_LOAD_FUNCTION(mod_osp_load);
+/* switch_status_t mod_osp_shutdown(void) */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_osp_shutdown);
+/* SWITCH_MODULE_DEFINITION(name, load, shutdown, runtime) */
+SWITCH_MODULE_DEFINITION(mod_osp, mod_osp_load, mod_osp_shutdown, NULL);
+
+/* Macro to prevent NULL string */
+#define osp_filter_null(_str)                                switch_strlen_zero(_str) ? OSP_DEF_STRING : _str
+#define osp_adjust_len(_head, _size, _len)        { _len = strlen(_head); _head += _len; _size -= _len; }
+
+/*
+ * Find OSP provider by name
+ * param name OSP provider name
+ * param provider OSP provider, NULL is allowed
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_find_provider(
+        const char *name,
+        osp_provider_t **provider)
+{
+        osp_provider_t *p;
+        switch_status_t status = SWITCH_STATUS_FALSE;
+
+        if (name) {
+                if (provider) {
+                        *provider = NULL;
+                }
+
+                for (p = osp_providers; p; p = p-&gt;next) {
+                        if (!strcasecmp(p-&gt;name, name)) {
+                                if (provider) {
+                                        *provider = p;
+                                }
+                                status = SWITCH_STATUS_SUCCESS;
+                                break;
+                        }
+                }
+        }
+
+        return status;
+}
+
+/*
+ * Load OSP module configuration
+ * param pool OSP module memory pool
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed, SWITCH_STATUS_MEMERR Memory Error.
+ */
+static switch_status_t osp_load_settings(
+        switch_memory_pool_t *pool)
+{
+        char *cf = &quot;osp.conf&quot;;
+        switch_xml_t cfg, xml = NULL, param, settings, profile, profiles;
+        const char *name;
+        const char *value;
+        const char *module;
+        const char *context;
+        osp_provider_t *provider;
+        int number;
+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+        if (!(xml = switch_xml_open_cfg(cf, &amp;cfg, NULL))) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to open '%s'\n&quot;, cf);
+                status = SWITCH_STATUS_FALSE;
+                return status;
+        }
+
+        memset(&amp;osp_globals, 0, sizeof(osp_globals));
+        osp_globals.loglevel = SWITCH_LOG_DEBUG;
+        osp_globals.pool = pool;
+        osp_globals.protocol = OSPC_DPROT_SIP;
+
+        if ((settings = switch_xml_child(cfg, &quot;settings&quot;))) {
+                for (param = switch_xml_child(settings, &quot;param&quot;); param; param = param-&gt;next) {
+                        name = switch_xml_attr_soft(param, &quot;name&quot;);
+                        value = switch_xml_attr_soft(param, &quot;value&quot;);
+                        module = switch_xml_attr_soft(param, &quot;module&quot;);
+                        context = switch_xml_attr_soft(param, &quot;profile&quot;);
+                        if (switch_strlen_zero(name)) {
+                                continue;
+                        }
+                        if (!strcasecmp(name, &quot;debug-info&quot;)) {
+                                if (!switch_strlen_zero(value)) {
+                                        osp_globals.debug = switch_true(value);
+                                }
+                        } else if (!strcasecmp(name, &quot;log-level&quot;)) {
+                                if (switch_strlen_zero(value)) {
+                                        continue;
+                                } else if (!strcasecmp(value, &quot;console&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_CONSOLE;
+                                } else if (!strcasecmp(value, &quot;alert&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_ALERT;
+                                } else if (!strcasecmp(value, &quot;crit&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_CRIT;
+                                } else if (!strcasecmp(value, &quot;error&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_ERROR;
+                                } else if (!strcasecmp(value, &quot;warning&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_WARNING;
+                                } else if (!strcasecmp(value, &quot;notice&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_NOTICE;
+                                } else if (!strcasecmp(value, &quot;info&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_INFO;
+                                } else if (!strcasecmp(value, &quot;debug&quot;)) {
+                                        osp_globals.loglevel = SWITCH_LOG_DEBUG;
+                                }
+                        } else if (!strcasecmp(name, &quot;crypto-hardware&quot;)) {
+                                if (!switch_strlen_zero(value)) {
+                                        osp_globals.hardware = switch_true(value);
+                                }
+                        } else if (!strcasecmp(name, &quot;default-protocol&quot;)) {
+                                if (switch_strlen_zero(value)) {
+                                        continue;
+                                } else if (!strcasecmp(value, OSP_PROTOCOL_SIP)) {
+                                        osp_globals.protocol = OSPC_DPROT_SIP;
+                                } else if (!strcasecmp(value, OSP_PROTOCOL_H323)) {
+                                        osp_globals.protocol = OSPC_DPROT_Q931;
+                                } else if (!strcasecmp(value, OSP_PROTOCOL_IAX)) {
+                                        osp_globals.protocol = OSPC_DPROT_IAX;
+                                } else if (!strcasecmp(value, OSP_PROTOCOL_SKYPE)) {
+                                        osp_globals.protocol = OSPC_DPROT_SKYPE;
+                                } else {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unsupported default protocol '%s'\n&quot;, value);
+                                }
+                        } else if (!strcasecmp(name, &quot;sip&quot;)) {
+                                if (!switch_strlen_zero(module)) {
+                                        if (!(osp_globals.module[OSPC_DPROT_SIP] = switch_core_strdup(osp_globals.pool, module))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate SIP module name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                                if (!switch_strlen_zero(context)) {
+                                        if (!(osp_globals.profile[OSPC_DPROT_SIP] = switch_core_strdup(osp_globals.pool, context))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate SIP profile name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                        } else if (!strcasecmp(name, &quot;h323&quot;)) {
+                                if (!switch_strlen_zero(module)) {
+                                        if (!(osp_globals.module[OSPC_DPROT_Q931] = switch_core_strdup(osp_globals.pool, module))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate H.323 module name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                                if (!switch_strlen_zero(context)) {
+                                        if (!(osp_globals.profile[OSPC_DPROT_Q931] = switch_core_strdup(osp_globals.pool, context))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate H.323 profile name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                        } else if (!strcasecmp(name, &quot;iax&quot;)) {
+                                if (!switch_strlen_zero(module)) {
+                                        if (!(osp_globals.module[OSPC_DPROT_IAX] = switch_core_strdup(osp_globals.pool, module))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate IAX module name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                                if (!switch_strlen_zero(context)) {
+                                        if (!(osp_globals.profile[OSPC_DPROT_IAX] = switch_core_strdup(osp_globals.pool, context))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate IAX profile name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                        } else if (!strcasecmp(name, &quot;skype&quot;)) {
+                                if (!switch_strlen_zero(module)) {
+                                        if (!(osp_globals.module[OSPC_DPROT_SKYPE] = switch_core_strdup(osp_globals.pool, module))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate Skype module name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                        }
+                                if (!switch_strlen_zero(context)) {
+                                        if (!(osp_globals.profile[OSPC_DPROT_SKYPE] = switch_core_strdup(osp_globals.pool, context))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate Skype profile name\n&quot;);
+                                                status = SWITCH_STATUS_MEMERR;
+                                                break;
+                                        }
+                                }
+                        } else {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unknown parameter '%s'\n&quot;, name);
+                        }
+                }
+        }
+
+        if (status != SWITCH_STATUS_SUCCESS) {
+                switch_xml_free(xml);
+                return status;
+        }
+
+        if ((profiles = switch_xml_child(cfg, &quot;profiles&quot;))) {
+                for (profile = switch_xml_child(profiles, &quot;profile&quot;); profile; profile = profile-&gt;next) {
+                        name = switch_xml_attr_soft(profile, &quot;name&quot;);
+                        if (switch_strlen_zero(name)) {
+                                name = OSP_DEF_PROFILE;
+                        }
+                        if (osp_find_provider(name, NULL) == SWITCH_STATUS_SUCCESS) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Ignored duplicate profile '%s'\n&quot;, name);
+                                continue;
+                        }
+                        if (!(provider = switch_core_alloc(osp_globals.pool, sizeof(*provider)))) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to alloc provider\n&quot;);
+                                status = SWITCH_STATUS_MEMERR;
+                                break;
+                        }
+                        if (!(provider-&gt;name = switch_core_strdup(osp_globals.pool, name))) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate provider name\n&quot;);
+                                status = SWITCH_STATUS_MEMERR;
+                                /* &quot;provider&quot; cannot free to pool in FreeSWITCH */
+                                break;
+                        }
+
+                        /* Provider has been set to 0 by switch_core_alloc */
+                        provider-&gt;lifetime = OSP_DEF_LIFETIME;
+                        provider-&gt;maxconnect = OSP_DEF_MAXCONN;
+                        provider-&gt;persistence = OSP_DEF_PERSIST;
+                        provider-&gt;retrydelay = OSP_DEF_RETRYDELAY;
+                        provider-&gt;retrylimit = OSP_DEF_RETRYLIMIT;
+                        provider-&gt;timeout = OSP_DEF_TIMEOUT;
+                        provider-&gt;maxdest = OSP_DEF_MAXDEST;
+                        provider-&gt;handle = OSP_INVALID_HANDLE;
+
+                        for (param = switch_xml_child(profile, &quot;param&quot;); param; param = param-&gt;next) {
+                                name = switch_xml_attr_soft(param, &quot;name&quot;);
+                                value = switch_xml_attr_soft(param, &quot;value&quot;);
+                                if (switch_strlen_zero(name) || switch_strlen_zero(value)) {
+                                        continue;
+                                }
+                                if (!strcasecmp(name, &quot;service-point-url&quot;)) {
+                                        if (provider-&gt;spnum &lt; OSP_MAX_SP) {
+                                                provider-&gt;spurls[provider-&gt;spnum] = switch_core_strdup(osp_globals.pool, value);
+                                                provider-&gt;spnum++;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Ignored excess service point '%s'\n&quot;, value);
+                                        }
+                                } else if (!strcasecmp(name, &quot;device-ip&quot;)) {
+                                        provider-&gt;device = switch_core_strdup(osp_globals.pool, value);
+                                } else if (!strcasecmp(name, &quot;ssl-lifetime&quot;)) {
+                                        provider-&gt;lifetime = atoi(value);
+                                } else if (!strcasecmp(name, &quot;http-max-connections&quot;)) {
+                                        number = atoi(value);
+                                        if ((number &gt;= OSP_MIN_MAXCONN) &amp;&amp; (number &lt;= OSP_MAX_MAXCONN)) {
+                                                provider-&gt;maxconnect = number;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                                                        &quot;http-max-connections must be between %d and %d\n&quot;, OSP_MIN_MAXCONN, OSP_MAX_MAXCONN);
+                                        }
+                                } else if (!strcasecmp(name, &quot;http-persistence&quot;)) {
+                                        provider-&gt;persistence = atoi(value);
+                                } else if (!strcasecmp(name, &quot;http-retry-delay&quot;)) {
+                                        number = atoi(value);
+                                        if ((number &gt;= OSP_MIN_RETRYDELAY) &amp;&amp; (number &lt;= OSP_MAX_RETRYDELAY)) {
+                                                provider-&gt;retrydelay = number;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                                                        &quot;http-retry-delay must be between %d and %d\n&quot;, OSP_MIN_RETRYDELAY, OSP_MAX_RETRYDELAY);
+                                        }
+                                } else if (!strcasecmp(name, &quot;http-retry-limit&quot;)) {
+                                        number = atoi(value);
+                                        if ((number &gt;= OSP_MIN_RETRYLIMIT) &amp;&amp; (number &lt;= OSP_MAX_RETRYLIMIT)) {
+                                                provider-&gt;retrylimit = number;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                                                        &quot;http-retry-limit must be between %d and %d\n&quot;, OSP_MIN_RETRYLIMIT, OSP_MAX_RETRYLIMIT);
+                                        }
+                                } else if (!strcasecmp(name, &quot;http-timeout&quot;)) {
+                                        number = atoi(value);
+                                        if ((number &gt;= OSP_MIN_TIMEOUT) &amp;&amp; (number &lt;= OSP_MAX_TIMEOUT)) {
+                                                provider-&gt;timeout = number;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                                                        &quot;http-timeout must be between %d and %d\n&quot;, OSP_MIN_TIMEOUT, OSP_MAX_TIMEOUT);
+                                        }
+                                } else if (!strcasecmp(name, &quot;service-type&quot;)) {
+                                        if (!strcasecmp(value, &quot;voice&quot;)) {
+                                                provider-&gt;srvtype = OSP_SRV_VOICE;
+                                        } else if (!strcasecmp(value, &quot;npquery&quot;)) {
+                                                provider-&gt;srvtype = OSP_SRV_NPQUERY;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unknown service type '%s'\n&quot;, value);
+                                        }
+                                } else if (!strcasecmp(name, &quot;max-destinations&quot;)) {
+                                        number = atoi(value);
+                                        if ((number &gt;= OSP_MIN_MAXDEST) &amp;&amp; (number &lt;= OSP_MAX_MAXDEST)) {
+                                                provider-&gt;maxdest = number;
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                                                        &quot;max-destinations must be between %d and %d\n&quot;, OSP_MIN_MAXDEST, OSP_MAX_MAXDEST);
+                                        }
+                                } else if (!strcasecmp(name, &quot;user-phone&quot;)) {
+                                        provider-&gt;userphone = switch_true(value);
+                                } else {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unknown parameter '%s'\n&quot;, name);
+                                }
+                        }
+
+                        if (!provider-&gt;spnum) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Without service point URI in profile '%s'\n&quot;, provider-&gt;name);
+                                /* &quot;provider&quot; cannot free to pool in FreeSWITCH */
+                                continue;
+                        }
+
+                        provider-&gt;next = osp_providers;
+                        osp_providers = provider;
+                }
+        }
+
+        switch_xml_free(xml);
+
+        return status;
+}
+
+/* OSP default certificates */
+const char *B64PKey = &quot;MIIBOgIBAAJBAK8t5l+PUbTC4lvwlNxV5lpl+2dwSZGW46dowTe6y133XyVEwNiiRma2YNk3xKs/TJ3Wl9Wpns2SYEAJsFfSTukCAwEAAQJAPz13vCm2GmZ8Zyp74usTxLCqSJZNyMRLHQWBM0g44Iuy4wE3vpi7Wq+xYuSOH2mu4OddnxswCP4QhaXVQavTAQIhAOBVCKXtppEw9UaOBL4vW0Ed/6EA/1D8hDW6St0h7EXJAiEAx+iRmZKhJD6VT84dtX5ZYNVk3j3dAcIOovpzUj9a0CECIEduTCapmZQ5xqAEsLXuVlxRtQgLTUD4ZxDElPn8x0MhAiBE2HlcND0+qDbvtwJQQOUzDgqg5xk3w8capboVdzAlQQIhAMC+lDL7+gDYkNAft5Mu+NObJmQs4Cr+DkDFsKqoxqrm&quot;;
+const char *B64LCert = &quot;MIIBeTCCASMCEHqkOHVRRWr+1COq3CR/xsowDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTA1MDYyMzAwMjkxOFoXDTA2MDYyNDAwMjkxOFowRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCvLeZfj1G0wuJb8JTcVeZaZftncEmRluOnaME3ustd918lRMDYokZmtmDZN8SrP0yd1pfVqZ7NkmBACbBX0k7pAgMBAAEwDQYJKoZIhvcNAQEEBQADQQDnV8QNFVVJx/+7IselU0wsepqMurivXZzuxOmTEmTVDzCJx1xhA8jd3vGAj7XDIYiPub1PV23eY5a2ARJuw5w9&quot;;
+const char *B64CACert = &quot;MIIBYDCCAQoCAQEwDQYJKoZIhvcNAQEEBQAwOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMB4XDTAyMDIwNDE4MjU1MloXDTEyMDIwMzE4MjU1MlowOzElMCMGA1UEAxMcb3NwdGVzdHNlcnZlci50cmFuc25leHVzLmNvbTESMBAGA1UEChMJT1NQU2VydmVyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPGeGwV41EIhX0jEDFLRXQhDEr50OUQPq+f55VwQd0TQNts06BP29+UiNdRW3c3IRHdZcJdC1Cg68ME9cgeq0h8CAwEAATANBgkqhkiG9w0BAQQFAANBAGkzBSj1EnnmUxbaiG1N4xjIuLAWydun7o3bFk2tV8dBIhnuh445obYyk1EnQ27kI7eACCILBZqi2MHDOIMnoN0=&quot;;
+
+/*
+ * Init OSP client end
+ * return
+ */
+static void osp_init_osptk(void)
+{
+        osp_provider_t *provider;
+        OSPTPRIVATEKEY privatekey;
+        unsigned char privatekeydata[OSP_SIZE_KEYSTR];
+        OSPT_CERT localcert;
+        unsigned char localcertdata[OSP_SIZE_KEYSTR];
+        const OSPT_CERT *pcacert;
+        OSPT_CERT cacert;
+        unsigned char cacertdata[OSP_SIZE_KEYSTR];
+        int error;
+
+        if (osp_globals.hardware) {
+                if ((error = OSPPInit(OSPC_TRUE)) != OSPC_ERR_NO_ERROR) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unable to enable crypto hardware, error '%d'\n&quot;, error);
+                        osp_globals.hardware = SWITCH_FALSE;
+                        OSPPInit(OSPC_FALSE);
+                }
+        } else {
+                OSPPInit(OSPC_FALSE);
+        }
+
+        for (provider = osp_providers; provider; provider = provider-&gt;next) {
+                privatekey.PrivateKeyData = privatekeydata;
+                privatekey.PrivateKeyLength = sizeof(privatekeydata);
+
+                localcert.CertData = localcertdata;
+                localcert.CertDataLength = sizeof(localcertdata);
+
+                pcacert = &amp;cacert;
+                cacert.CertData = cacertdata;
+                cacert.CertDataLength = sizeof(cacertdata);
+
+                if ((error = OSPPBase64Decode(B64PKey, strlen(B64PKey), privatekey.PrivateKeyData, &amp;privatekey.PrivateKeyLength)) != OSPC_ERR_NO_ERROR) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unable to decode private key, error '%d'\n&quot;, error);
+                } else if ((error = OSPPBase64Decode(B64LCert, strlen(B64LCert), localcert.CertData, &amp;localcert.CertDataLength)) != OSPC_ERR_NO_ERROR) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unable to decode local cert, error '%d'\n&quot;, error);
+                } else if ((error = OSPPBase64Decode(B64CACert, strlen(B64CACert), cacert.CertData, &amp;cacert.CertDataLength)) != OSPC_ERR_NO_ERROR) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unable to decode cacert, error '%d'\n&quot;, error);
+                }
+
+                if (error == OSPC_ERR_NO_ERROR) {
+                        error = OSPPProviderNew(
+                                provider-&gt;spnum,                /* Number of service points */
+                                provider-&gt;spurls,                /* Service point URLs */
+                                NULL,                                        /* Weights */
+                                OSP_AUDIT_URL,                        /* Audit URL */
+                                &amp;privatekey,                        /* Provate key */
+                                &amp;localcert,                                /* Local cert */
+                                1,                                                /* Number of cacerts */
+                                &amp;pcacert,                                /* cacerts */
+                                OSP_LOCAL_VALID,                /* Validating method */
+                                provider-&gt;lifetime,                /* SS lifetime */
+                                provider-&gt;maxconnect,        /* HTTP max connections */
+                                provider-&gt;persistence,        /* HTTP persistence */
+                                provider-&gt;retrydelay,        /* HTTP retry delay, in seconds */
+                                provider-&gt;retrylimit,        /* HTTP retry times */
+                                provider-&gt;timeout,                /* HTTP timeout */
+                                OSP_CUSTOMER_ID,                /* Customer ID */
+                                OSP_DEVICE_ID,                        /* Device ID */
+                                &amp;provider-&gt;handle);                /* Provider handle */
+                        if (error != OSPC_ERR_NO_ERROR) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unable to create provider %s, error '%d'\n&quot;, provider-&gt;name, error);
+                                provider-&gt;handle = OSP_INVALID_HANDLE;
+                        }
+                }
+        }
+}
+
+/*
+ * Get protocol name
+ * param protocol Supported protocol
+ * return protocol name
+ */
+static const char *osp_get_protocol(
+        OSPE_DEST_PROTOCOL protocol)
+{
+        const char *name;
+
+        switch (protocol) {
+        case OSPC_DPROT_UNKNOWN:
+                name = OSP_PROTOCOL_UNKNO;
+                break;
+        case OSPC_DPROT_UNDEFINED:
+                name = OSP_PROTOCOL_UNDEF;
+                break;
+        case OSPC_DPROT_SIP:
+                name = OSP_PROTOCOL_SIP;
+                break;
+        case OSPC_DPROT_Q931:
+                name = OSP_PROTOCOL_H323;
+                break;
+        case OSPC_DPROT_IAX:
+                name = OSP_PROTOCOL_IAX;
+                break;
+        case OSPC_DPROT_SKYPE:
+                name = OSP_PROTOCOL_SKYPE;
+                break;
+        case OSPC_DPROT_LRQ:
+        case OSPC_DPROT_T37:
+        case OSPC_DPROT_T38:
+        case OSPC_DPROT_SMPP:
+        case OSPC_DPROT_XMPP:
+        case OSPC_DPROT_SMS:
+        default:
+                name = OSP_PROTOCOL_UNSUP;
+                break;
+        }
+
+        return name;
+}
+
+/*
+ * Macro expands to:
+ * static switch_status_t osp_api_function(_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream)
+ */
+SWITCH_STANDARD_API(osp_api_function)
+{
+        int i, argc = 0;
+        char *argv[2] = { 0 };
+        char *params = NULL;
+        char *param = NULL;
+        osp_provider_t *provider;
+        char *loglevel;
+
+        if (session) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;This function cannot be called from the dialplan.\n&quot;);
+                return SWITCH_STATUS_FALSE;
+        }
+
+        if (switch_strlen_zero(cmd)) {
+                stream-&gt;write_function(stream, &quot;Usage: osp status\n&quot;);
+                return SWITCH_STATUS_SUCCESS;
+        }
+
+        if (!(params = switch_safe_strdup(cmd))) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to duplicate parameters\n&quot;);
+                return SWITCH_STATUS_MEMERR;
+        }
+
+        if ((argc = switch_separate_string(params, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+                param = argv[0];
+                if (!strcasecmp(param, &quot;status&quot;)) {
+                        stream-&gt;write_function(stream, &quot;=============== OSP Module Settings &amp; Status ===============\n&quot;);
+                        stream-&gt;write_function(stream, &quot;                debug-info: %s\n&quot;, osp_globals.debug ? &quot;enabled&quot; : &quot;disabled&quot;);
+                        switch (osp_globals.loglevel) {
+                        case SWITCH_LOG_CONSOLE:
+                                loglevel = &quot;console&quot;;
+                                break;
+                        case SWITCH_LOG_ALERT:
+                                loglevel = &quot;alert&quot;;
+                                break;
+                        case SWITCH_LOG_CRIT:
+                                loglevel = &quot;crit&quot;;
+                                break;
+                        case SWITCH_LOG_ERROR:
+                                loglevel = &quot;error&quot;;
+                                break;
+                        case SWITCH_LOG_WARNING:
+                                loglevel = &quot;warning&quot;;
+                                break;
+                        case SWITCH_LOG_NOTICE:
+                                loglevel = &quot;notice&quot;;
+                                break;
+                        case SWITCH_LOG_INFO:
+                                loglevel = &quot;info&quot;;
+                                break;
+                        case SWITCH_LOG_DEBUG:
+                        default:
+                                loglevel = &quot;debug&quot;;
+                                break;
+                        }
+                        stream-&gt;write_function(stream, &quot;                 log-level: %s\n&quot;, loglevel);
+                        stream-&gt;write_function(stream, &quot;           crypto-hardware: %s\n&quot;, osp_globals.hardware ? &quot;enabled&quot; : &quot;disabled&quot;);
+                        if (switch_strlen_zero(osp_globals.module[OSPC_DPROT_SIP]) || switch_strlen_zero(osp_globals.profile[OSPC_DPROT_SIP])) {
+                                stream-&gt;write_function(stream, &quot;                       sip: unsupported\n&quot;);
+                        } else {
+                                stream-&gt;write_function(stream, &quot;                       sip: %s/%s\n&quot;,
+                                        osp_globals.module[OSPC_DPROT_SIP], osp_globals.profile[OSPC_DPROT_SIP]);
+                        }
+                        if (switch_strlen_zero(osp_globals.module[OSPC_DPROT_Q931]) || switch_strlen_zero(osp_globals.profile[OSPC_DPROT_Q931])) {
+                                stream-&gt;write_function(stream, &quot;                      h323: unsupported\n&quot;);
+                        } else {
+                                stream-&gt;write_function(stream, &quot;                      h323: %s/%s\n&quot;,
+                                        osp_globals.module[OSPC_DPROT_Q931], osp_globals.profile[OSPC_DPROT_Q931]);
+                        }
+                        if (switch_strlen_zero(osp_globals.module[OSPC_DPROT_IAX]) || switch_strlen_zero(osp_globals.profile[OSPC_DPROT_IAX])) {
+                                stream-&gt;write_function(stream, &quot;                       iax: unsupported\n&quot;);
+                        } else {
+                                stream-&gt;write_function(stream, &quot;                       iax: %s/%s\n&quot;,
+                                        osp_globals.module[OSPC_DPROT_IAX], osp_globals.profile[OSPC_DPROT_IAX]);
+                        }
+                        if (switch_strlen_zero(osp_globals.module[OSPC_DPROT_SKYPE]) || switch_strlen_zero(osp_globals.profile[OSPC_DPROT_SKYPE])) {
+                                stream-&gt;write_function(stream, &quot;                     skype: unsupported\n&quot;);
+                        } else {
+                                stream-&gt;write_function(stream, &quot;                     skype: %s/%s\n&quot;,
+                                        osp_globals.module[OSPC_DPROT_SKYPE], osp_globals.profile[OSPC_DPROT_SKYPE]);
+                        }
+                        stream-&gt;write_function(stream, &quot;          default-protocol: %s\n&quot;, osp_get_protocol(osp_globals.protocol));
+                        stream-&gt;write_function(stream, &quot;============== OSP Profiles Settings &amp; Status ==============\n&quot;);
+                        for (provider = osp_providers; provider; provider = provider-&gt;next) {
+                                stream-&gt;write_function(stream, &quot;Profile: %s\n&quot;, provider-&gt;name);
+                                for (i = 0; i &lt; provider-&gt;spnum; i++) {
+                                        stream-&gt;write_function(stream, &quot;         service-point-url: %s\n&quot;, provider-&gt;spurls[i]);
+                                }
+                                stream-&gt;write_function(stream, &quot;                 device-ip: %s\n&quot;, provider-&gt;device);
+                                stream-&gt;write_function(stream, &quot;              ssl-lifetime: %d\n&quot;, provider-&gt;lifetime);
+                                stream-&gt;write_function(stream, &quot;      http-max-connections: %d\n&quot;, provider-&gt;maxconnect);
+                                stream-&gt;write_function(stream, &quot;          http-persistence: %d\n&quot;, provider-&gt;persistence);
+                                stream-&gt;write_function(stream, &quot;          http-retry-dalay: %d\n&quot;, provider-&gt;retrydelay);
+                                stream-&gt;write_function(stream, &quot;          http-retry-limit: %d\n&quot;, provider-&gt;retrylimit);
+                                stream-&gt;write_function(stream, &quot;              http-timeout: %d\n&quot;, provider-&gt;timeout);
+                                switch (provider-&gt;srvtype) {
+                                case OSP_SRV_NPQUERY:
+                                        stream-&gt;write_function(stream, &quot;              service-type: npquery\n&quot;);
+                                        break;
+                                case OSP_SRV_VOICE:
+                                default:
+                                        stream-&gt;write_function(stream, &quot;              service-type: voice\n&quot;);
+                                        break;
+                                }
+                                stream-&gt;write_function(stream, &quot;          max-destinations: %d\n&quot;, provider-&gt;maxdest);
+                                stream-&gt;write_function(stream, &quot;                user-phone: %s\n&quot;, provider-&gt;userphone ? &quot;enabled&quot; : &quot;disabled&quot;);
+                                stream-&gt;write_function(stream, &quot;                    status: %s\n&quot;, provider-&gt;handle != OSP_INVALID_HANDLE ? &quot;enabled&quot; : &quot;disabled&quot;);
+                        }
+                } else {
+                        stream-&gt;write_function(stream, &quot;Invalid Syntax!\n&quot;);
+                }
+        } else {
+                stream-&gt;write_function(stream, &quot;Invalid Input!\n&quot;);
+        }
+
+        switch_safe_free(params);
+
+        return SWITCH_STATUS_SUCCESS;
+}
+
+/*
+ * Parse URI userinfo
+ * param user URI userinfo
+ * param user URI user
+ * param usersize Size of user buffer
+ * param inbound Inbound info
+ * return
+ */
+static void osp_parse_userinfo(
+        const char *userinfo,
+        char *user,
+        switch_size_t usersize,
+        osp_inbound_t *inbound)
+{
+        char buffer[OSP_SIZE_NORSTR];
+        char *item;
+        char *tmp;
+
+        if (switch_strlen_zero(userinfo)) {
+                if (user &amp;&amp; usersize) {
+                        user[0] = '\0';
+                }
+                /* It is not necessary to clean inbound */
+                return;
+        }
+
+        switch_copy_string(buffer, userinfo, sizeof(buffer));
+
+        item = strtok_r(buffer, OSP_USER_DELIM, &amp;tmp);
+
+        if (user &amp;&amp; usersize) {
+                switch_copy_string(user, item, usersize);
+        }
+
+        if (inbound) {
+                switch_copy_string(inbound-&gt;called, item, sizeof(inbound-&gt;called));
+
+                for (item = strtok_r(NULL, OSP_USER_DELIM, &amp;tmp); item; item = strtok_r(NULL, OSP_USER_DELIM, &amp;tmp)) {
+                        if (!strncasecmp(item, &quot;rn=&quot;, 3)) {
+                                switch_copy_string(inbound-&gt;nprn, item + 3, sizeof(inbound-&gt;nprn));
+                        } else if (!strncasecmp(item, &quot;cic=&quot;, 4)) {
+                                switch_copy_string(inbound-&gt;npcic, item + 4, sizeof(inbound-&gt;npcic));
+                        } else if (!strcasecmp(item, &quot;npdi&quot;)) {
+                                inbound-&gt;npdi = 1;
+                        }
+                }
+        }
+}
+
+/*
+ * Parse SIP Diversion
+ * param diversion SIP Diversion header
+ * param inbound Inbound info
+ * return
+ */
+static void osp_parse_diversion(
+        const char *diversion,
+        osp_inbound_t *inbound)
+{
+        char buffer[OSP_SIZE_NORSTR];
+        char userinfo[OSP_SIZE_NORSTR];
+        char *head;
+        char *tmp;
+
+        if (switch_strlen_zero(diversion)) {
+                /* It is not necessary to clean inbound */
+                return;
+        }
+
+        switch_copy_string(buffer, diversion, sizeof(buffer));
+
+        if ((head = strstr(buffer, &quot;sip:&quot;))) {
+                head += 4;
+                if ((tmp = strchr(head, OSP_URI_DELIM))) {
+                        *tmp = '\0';
+                        switch_copy_string(userinfo, head, sizeof(userinfo));
+                        osp_parse_userinfo(userinfo, inbound-&gt;divuser, sizeof(inbound-&gt;divuser), NULL);
+
+                        head = tmp + 1;
+                        tmp = strtok(head, OSP_HOST_DELIM);
+                        switch_copy_string(inbound-&gt;divhost, tmp, sizeof(inbound-&gt;divhost));
+                }
+        }
+}
+
+/*
+ * Log AuthReq parameters
+ * param provider OSP provider
+ * param inbound Inbound info
+ * return
+ */
+static void osp_log_authreq(
+        osp_provider_t *provider,
+        osp_inbound_t *inbound)
+{
+        char *srvtype;
+        char term[OSP_SIZE_NORSTR];
+        int total;
+
+        if (osp_globals.debug) {
+                if (provider-&gt;srvtype == OSP_SRV_NPQUERY) {
+                        srvtype = &quot;npquery&quot;;
+                        if (switch_strlen_zero(inbound-&gt;tohost)) {
+                                switch_copy_string(term, provider-&gt;device, sizeof(term));
+                        } else {
+                                if (switch_strlen_zero(inbound-&gt;toport)) {
+                                        switch_copy_string(term, inbound-&gt;tohost, sizeof(term));
+                                } else {
+                                        switch_snprintf(term, sizeof(term), &quot;%s:%s&quot;, inbound-&gt;tohost, inbound-&gt;toport);
+                                }
+                        }
+                        total = 1;
+                } else {
+                        srvtype = &quot;voice&quot;;
+                        term[0] = '\0';
+                        total = provider-&gt;maxdest;
+                }
+
+                switch_log_printf(SWITCH_CHANNEL_LOG, osp_globals.loglevel, 
+                        &quot;AuthReq: &quot;
+                        &quot;srvtype = '%s' &quot;
+                        &quot;source = '%s' &quot;
+                        &quot;srcdev = '%s' &quot;
+                        &quot;calling = '%s' &quot;
+                        &quot;called = '%s' &quot;
+                        &quot;lnp = '%s/%s/%d' &quot;
+                        &quot;prefer = '%s' &quot;
+                        &quot;div = '%s/%s' &quot;
+                        &quot;srcnid = '%s' &quot;
+                        &quot;cinfo = '%s/%s/%s/%s/%s/%s/%s/%s' &quot;
+                        &quot;maxcount = '%d'\n&quot;,
+                        srvtype,
+                        provider-&gt;device,
+                        inbound-&gt;srcdev,
+                        inbound-&gt;calling,
+                        inbound-&gt;called,
+                        inbound-&gt;nprn, inbound-&gt;npcic, inbound-&gt;npdi,
+                        term,
+                        inbound-&gt;divuser, inbound-&gt;divhost,
+                        osp_filter_null(inbound-&gt;srcnid),
+                        osp_filter_null(inbound-&gt;cinfo[0]), osp_filter_null(inbound-&gt;cinfo[1]),
+                        osp_filter_null(inbound-&gt;cinfo[2]), osp_filter_null(inbound-&gt;cinfo[3]),
+                        osp_filter_null(inbound-&gt;cinfo[4]), osp_filter_null(inbound-&gt;cinfo[5]),
+                        osp_filter_null(inbound-&gt;cinfo[6]), osp_filter_null(inbound-&gt;cinfo[7]),
+                        total);
+        }
+}
+
+/*
+ * Get inbound info
+ * param channel Inbound channel
+ * param inbound Inbound info
+ * return
+ */
+static void osp_get_inbound(
+        switch_channel_t *channel,
+        osp_inbound_t *inbound)
+{
+        switch_caller_profile_t *caller;
+        switch_channel_timetable_t *times;
+        const char *tmp;
+        int i;
+        char name[OSP_SIZE_NORSTR];
+
+        memset(inbound, 0, sizeof(*inbound));
+
+        caller = switch_channel_get_caller_profile(channel);
+        inbound-&gt;srcdev = caller-&gt;network_addr;
+        inbound-&gt;calling = caller-&gt;caller_id_number;
+        osp_parse_userinfo(caller-&gt;destination_number, NULL, 0, inbound);
+
+        inbound-&gt;tohost = switch_channel_get_variable(channel, OSP_FS_TOHOST);
+        inbound-&gt;toport = switch_channel_get_variable(channel, OSP_FS_TOPORT);
+
+        if ((tmp = switch_channel_get_variable(channel, OSP_FS_DIVERSION))) {
+                osp_parse_diversion(tmp, inbound);
+        }
+
+        inbound-&gt;srcnid = switch_channel_get_variable(channel, OSP_VAR_SRCNID);
+
+        times = switch_channel_get_timetable(channel);
+        inbound-&gt;start = times-&gt;created;
+
+        for (i = 0; i &lt; OSP_MAX_CINFO; i++) {
+                switch_snprintf(name, sizeof(name), &quot;%s%d&quot;, OSP_VAR_CUSTOMINFO, i + 1);
+                inbound-&gt;cinfo[i] = switch_channel_get_variable(channel, name);
+        }
+}
+
+/*
+ * Convert &quot;address:port&quot; to &quot;[x.x.x.x]:port&quot; or &quot;hostname:port&quot; format
+ * param src Source address string
+ * param dest Destination address string
+ * param destsize Size of dest buffer
+ * return
+ */
+static void osp_convert_inout(
+        const char *src,
+        char *dest,
+        int destsize)
+{
+        struct in_addr inp;
+        char buffer[OSP_SIZE_NORSTR];
+        char *port;
+
+        if (dest &amp;&amp; (destsize &gt; 0)) {
+                if (!switch_strlen_zero(src)) {
+                        switch_copy_string(buffer, src, sizeof(buffer));
+
+                        if((port = strchr(buffer, ':'))) {
+                                *port = '\0';
+                                port++;
+                        }
+
+                        if (inet_pton(AF_INET, buffer, &amp;inp) == 1) {
+                                if (port) {
+                                        switch_snprintf(dest, destsize, &quot;[%s]:%s&quot;, buffer, port);
+                                } else {
+                                        switch_snprintf(dest, destsize, &quot;[%s]&quot;, buffer);
+                                }
+                                dest[destsize - 1] = '\0';
+                        } else {
+                                switch_copy_string(dest, src, destsize);
+                        }
+                } else {
+                        *dest = '\0';
+                }
+        }
+}
+
+/*
+ * Convert &quot;[x.x.x.x]:port&quot; or &quot;hostname:prot&quot; to &quot;address:port&quot; format
+ * param src Source address string
+ * param dest Destination address string
+ * param destsize Size of dest buffer
+ * return
+ */
+static void osp_convert_outin(
+        const char *src,
+        char *dest,
+        int destsize)
+{
+        char buffer[OSP_SIZE_NORSTR];
+        char *end;
+        char *port;
+
+        if (dest &amp;&amp; (destsize &gt; 0)) {
+                if (!switch_strlen_zero(src)) {
+                        switch_copy_string(buffer, src, sizeof(buffer));
+
+                        if (buffer[0] == '[') {
+                                if((port = strchr(buffer + 1, ':'))) {
+                                        *port = '\0';
+                                        port++;
+                                }
+
+                                if ((end = strchr(buffer + 1, ']'))) {
+                                        *end = '\0';
+                                }
+
+                                if (port) {
+                                        switch_snprintf(dest, destsize, &quot;%s:%s&quot;, buffer + 1, port);
+                                        dest[destsize - 1] = '\0';
+                                } else {
+                                        switch_copy_string(dest, buffer + 1, destsize);
+                                }
+                        } else {
+                                switch_copy_string(dest, src, destsize);
+                        }
+                } else {
+                        *dest = '\0';
+                }
+        }
+}
+
+/*
+ * Check destination
+ * param handle Transaction handle
+ * param dest Destination
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_check_destination(
+        OSPTTRANHANDLE handle,
+        osp_destination_t *dest)
+{
+        OSPE_DEST_OSPENABLED enabled;
+        OSPE_DEST_PROTOCOL protocol;
+        OSPE_OPERATOR_NAME type;
+        int error;
+        switch_status_t status = SWITCH_STATUS_FALSE;
+
+        if ((handle == OSP_INVALID_HANDLE) || !dest) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Invalid parameters\n&quot;);
+                return status;
+        }
+
+        dest-&gt;supported = SWITCH_FALSE;
+
+        if ((error = OSPPTransactionIsDestOSPEnabled(handle, &amp;enabled)) != OSPC_ERR_NO_ERROR) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to get destination OSP version, error '%d'\n&quot;, error);
+                return status;
+        }
+
+        if ((error = OSPPTransactionGetDestProtocol(handle, &amp;protocol)) != OSPC_ERR_NO_ERROR) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to get destination protocol, error '%d'\n&quot;, error);
+                return status;
+        }
+
+        switch(protocol) {
+        case OSPC_DPROT_UNDEFINED:
+        case OSPC_DPROT_UNKNOWN:
+                protocol = osp_globals.protocol;
+        case OSPC_DPROT_SIP:
+        case OSPC_DPROT_Q931:
+        case OSPC_DPROT_IAX:
+        case OSPC_DPROT_SKYPE:
+                dest-&gt;protocol = protocol;
+                if (!switch_strlen_zero(osp_globals.module[protocol]) &amp;&amp; !switch_strlen_zero(osp_globals.profile[protocol])) {
+                        dest-&gt;supported = SWITCH_TRUE;
+                        status = SWITCH_STATUS_SUCCESS;
+                }
+                break;
+        case OSPC_DPROT_LRQ:
+        case OSPC_DPROT_T37:
+        case OSPC_DPROT_T38:
+        case OSPC_DPROT_SMPP:
+        case OSPC_DPROT_XMPP:
+        default:
+                dest-&gt;protocol = protocol;
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unsupported protocol '%d'\n&quot;, protocol);
+                break;
+        }
+
+        if ((error = OSPPTransactionGetDestinationNetworkId(handle, sizeof(dest-&gt;destnid), dest-&gt;destnid)) != OSPC_ERR_NO_ERROR) {
+                dest-&gt;destnid[0] = '\0';
+        }
+
+        error = OSPPTransactionGetNumberPortabilityParameters(handle,
+                sizeof(dest-&gt;nprn),
+                dest-&gt;nprn,
+                sizeof(dest-&gt;npcic),
+                dest-&gt;npcic,
+                &amp;dest-&gt;npdi);
+        if (error != OSPC_ERR_NO_ERROR) {
+                dest-&gt;nprn[0] = '\0';
+                dest-&gt;npcic[0] = '\0';
+                dest-&gt;npdi = 0;
+        }
+
+        for (type = OSPC_OPNAME_START; type &lt; OSPC_OPNAME_NUMBER; type++) {
+                if ((error = OSPPTransactionGetOperatorName(handle, type, sizeof(dest-&gt;opname[type]), dest-&gt;opname[type])) != OSPC_ERR_NO_ERROR) {
+                        dest-&gt;opname[type][0] = '\0';
+                }
+        }
+
+        return status;
+}
+
+/*
+ * Log AuthRsp parameters
+ * param results Routing info
+ * return
+ */
+static void osp_log_authrsp(
+        osp_results_t *results)
+{
+        int i;
+
+        if (osp_globals.debug) {
+                for (i = 0; i &lt; results-&gt;numdest; i++) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, osp_globals.loglevel, 
+                                &quot;AuthRsp: &quot;
+                                &quot;transid = '%llu' &quot;
+                                &quot;destcount = '%d' &quot;
+                                &quot;timelimit = '%u' &quot;
+                                &quot;destination = '%s' &quot;
+                                &quot;calling = '%s' &quot;
+                                &quot;called = '%s' &quot;
+                                &quot;destnid = '%s' &quot;
+                                &quot;lnp = '%s/%s/%d' &quot;
+                                &quot;protocol = '%s' &quot;
+                                &quot;supported = '%d'\n&quot;,
+                                results-&gt;transid,
+                                i + 1,
+                                results-&gt;dests[i].timelimit,
+                                results-&gt;dests[i].dest,
+                                results-&gt;dests[i].calling,
+                                results-&gt;dests[i].called,
+                                results-&gt;dests[i].destnid,
+                                results-&gt;dests[i].nprn, results-&gt;dests[i].npcic, results-&gt;dests[i].npdi,
+                                osp_get_protocol(results-&gt;dests[i].protocol),
+                                results-&gt;dests[i].supported);
+                }
+        }
+}
+
+/*
+ * Do auth/routing request
+ * param provider OSP provider
+ * param handle Transaction handle
+ * param source Call originator info
+ * param results Routing info
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_do_request(
+        osp_provider_t *provider,
+        OSPTTRANHANDLE handle,
+        osp_inbound_t *inbound,
+        osp_results_t *results)
+{
+        OSPTTRANS *context;
+        osp_destination_t *dest;
+        char tmp[OSP_SIZE_NORSTR];
+        char src[OSP_SIZE_NORSTR];
+        char dev[OSP_SIZE_NORSTR];
+        char term[OSP_SIZE_NORSTR];
+        const char *preferred[2] = { NULL };
+        unsigned int callidlen = 0, tokenlen = 0, total;
+        int count, error;
+        switch_status_t status = SWITCH_STATUS_FALSE;
+
+        osp_log_authreq(provider, inbound);
+
+        OSPPTransactionSetNumberPortability(handle, inbound-&gt;nprn, inbound-&gt;npcic, inbound-&gt;npdi);
+
+        osp_convert_inout(inbound-&gt;divhost, tmp, sizeof(tmp));
+        OSPPTransactionSetDiversion(handle, inbound-&gt;divuser, tmp);
+
+        OSPPTransactionSetNetworkIds(handle, inbound-&gt;srcnid, NULL);
+
+        for (count = 0; count &lt; OSP_MAX_CINFO; count++) {
+                if (!switch_strlen_zero(inbound-&gt;cinfo[count])) {
+                        OSPPTransactionSetCustomInfo(handle, count, inbound-&gt;cinfo[count]);
+                }
+        }
+
+        osp_convert_inout(provider-&gt;device, src, sizeof(src));
+        osp_convert_inout(inbound-&gt;srcdev, dev, sizeof(dev));
+        if (provider-&gt;srvtype == OSP_SRV_NPQUERY) {
+                OSPPTransactionSetServiceType(handle, OSPC_SERVICE_NPQUERY);
+
+                if (switch_strlen_zero(inbound-&gt;tohost)) {
+                        switch_copy_string(term, src, sizeof(term));
+                } else {
+                        if (switch_strlen_zero(inbound-&gt;toport)) {
+                                switch_copy_string(tmp, inbound-&gt;tohost, sizeof(tmp));
+                        } else {
+                                switch_snprintf(tmp, sizeof(tmp), &quot;%s:%s&quot;, inbound-&gt;tohost, inbound-&gt;toport);
+                        }
+                        osp_convert_inout(tmp, term, sizeof(term));
+                }
+                preferred[0] = term;
+
+                total = 1;
+        } else {
+                OSPPTransactionSetServiceType(handle, OSPC_SERVICE_VOICE);
+
+                total = provider-&gt;maxdest;
+        }
+
+        error = OSPPTransactionRequestAuthorisation(
+                handle,                                /* Transaction handle */
+                src,                                /* Source */
+                dev,                                /* Source device */
+                inbound-&gt;calling,        /* Calling */
+                OSPC_NFORMAT_E164,        /* Calling format */
+                inbound-&gt;called,        /* Called */
+                OSPC_NFORMAT_E164,        /* Called format */
+                NULL,                                /* User */
+                0,                                        /* Number of callids */
+                NULL,                                /* Callids */
+                preferred,                        /* Preferred destinations */
+                &amp;total,                                /* Destination number */
+                NULL,                                /* Log buffer size */
+                NULL);                                /* Log buffer */
+        if (error != OSPC_ERR_NO_ERROR) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Unable to request routing, error '%d'\n&quot;, error);
+                results-&gt;numdest = 0;
+                return status;
+        } else if (!total) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Without destination\n&quot;);
+                results-&gt;numdest = 0;
+                return status;
+        }
+
+        context = OSPPTransactionGetContext(handle, &amp;error);
+        if (error == OSPC_ERR_NO_ERROR) {
+                results-&gt;transid = context-&gt;TransactionID;
+        } else {
+                results-&gt;transid = 0;
+        }
+
+        switch_copy_string(results-&gt;called, inbound-&gt;called, sizeof(results-&gt;called));
+        results-&gt;srcnid = inbound-&gt;srcnid;
+        results-&gt;start = inbound-&gt;start;
+
+        count = 0;
+        dest = &amp;results-&gt;dests[count];
+        error = OSPPTransactionGetFirstDestination(
+                handle,                                        /* Transaction handle */
+                0,                                                /* Timestamp buffer size */
+                NULL,                                        /* Valid after */
+                NULL,                                        /* Valid until */
+                &amp;dest-&gt;timelimit,                /* Call duration limit */
+                &amp;callidlen,                                /* Callid buffer size */
+                NULL,                                        /* Callid buffer */
+                sizeof(dest-&gt;called),        /* Called buffer size */
+                dest-&gt;called,                        /* Called buffer */
+                sizeof(dest-&gt;calling),        /* Calling buffer size */
+                dest-&gt;calling,                        /* Calling buffer */
+                sizeof(term),                        /* Destination buffer size */
+                term,                                        /* Destination buffer */
+                0,                                                /* Destination device buffer size */
+                NULL,                                        /* Destination device buffer */
+                &amp;tokenlen,                                /* Token buffer length */
+                NULL);                                        /* Token buffer */
+        if (error != OSPC_ERR_NO_ERROR) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to get first destination, error '%d'\n&quot;, error);
+                return status;
+        }
+
+        osp_convert_outin(term, dest-&gt;dest, sizeof(dest-&gt;dest));
+        osp_check_destination(handle, dest);
+        dest-&gt;userphone = provider-&gt;userphone;
+
+        for (count = 1; count &lt; total; count++) {
+                dest = &amp;results-&gt;dests[count];
+                error = OSPPTransactionGetNextDestination(
+                        handle,                                        /* Transsaction handle */
+                        OSPC_FAIL_NONE,                        /* Failure reason */
+                        0,                                                /* Timestamp buffer size */
+                        NULL,                                        /* Valid after */
+                        NULL,                                        /* Valid until */
+                        &amp;dest-&gt;timelimit,                /* Call duration limit */
+                        &amp;callidlen,                                /* Callid buffer size */
+                        NULL,                                        /* Callid buffer */
+                        sizeof(dest-&gt;called),        /* Called buffer size */
+                        dest-&gt;called,                        /* Called buffer */
+                        sizeof(dest-&gt;calling),        /* Calling buffer size */
+                        dest-&gt;calling,                        /* Calling buffer */
+                        sizeof(term),                        /* Destination buffer size */
+                        term,                                        /* Destination buffer */
+                        0,                                                /* Destination device buffer size */
+                        NULL,                                        /* Destination device buffer */
+                        &amp;tokenlen,                                /* Token buffer length */
+                        NULL);                                        /* Token buffer */
+                if (error == OSPC_ERR_NO_ERROR) {
+                        osp_convert_outin(term, dest-&gt;dest, sizeof(dest-&gt;dest));
+                        osp_check_destination(handle, dest);
+                        dest-&gt;userphone = provider-&gt;userphone;
+                } else {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to get destination, error '%d'\n&quot;, error);
+                        break;
+                }
+        }
+        if (count == total) {
+                results-&gt;numdest = total;
+                osp_log_authrsp(results);
+                status = SWITCH_STATUS_SUCCESS;
+        }
+
+        return status;
+}
+
+/*
+ * Request auth/routing info
+ * param channel Originator channel
+ * param profile Provider name
+ * param results Routing info
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_request_routing(
+        switch_channel_t *channel,
+        const char *profile,
+        osp_results_t *results)
+{
+        osp_provider_t *provider;
+        OSPTTRANHANDLE handle;
+        osp_inbound_t inbound;
+        int error;
+        switch_status_t status = SWITCH_STATUS_FALSE;
+
+        if (osp_find_provider(profile, &amp;provider) != SWITCH_STATUS_SUCCESS) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to find provider '%s'\n&quot;, profile);
+                return status;
+        }
+
+        if (provider-&gt;handle == OSP_INVALID_HANDLE) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Disabled provider '%s'\n&quot;, profile);
+                return status;
+        }
+
+        results-&gt;profile = profile;
+
+        if ((error = OSPPTransactionNew(provider-&gt;handle, &amp;handle)) != OSPC_ERR_NO_ERROR) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to create transaction handle, error '%d'\n&quot;, error);
+                return status;
+        }
+
+        osp_get_inbound(channel, &amp;inbound);
+
+        status = osp_do_request(provider, handle, &amp;inbound, results);
+
+        OSPPTransactionDelete(handle);
+
+        return status;
+}
+
+/*
+ * Build parameter string for all channels
+ * param results Routing results
+ * param buffer Buffer
+ * param bufsize Buffer size
+ * return
+ */
+static void osp_build_allparam(
+        osp_results_t *results,
+        char *buffer,
+        switch_size_t bufsize)
+{
+        char *head = buffer;
+        switch_size_t len, size = bufsize;
+
+        if (results &amp;&amp; head &amp;&amp; size) {
+                switch_snprintf(head, size,
+                        &quot;{%s=%s,%s=%llu,%s=%llu&quot;,
+                        OSP_VAR_PROFILE, results-&gt;profile,
+                        OSP_VAR_TRANSID, results-&gt;transid,
+                        OSP_VAR_START, results-&gt;start);
+                osp_adjust_len(head, size, len);
+
+                if (!switch_strlen_zero(results-&gt;srcnid)) {
+                        switch_snprintf(head, size, &quot;,%s=%s&quot;, OSP_VAR_SRCNID, results-&gt;srcnid);
+                }
+                osp_adjust_len(head, size, len);
+
+                switch_snprintf(head, size, &quot;}&quot;);
+                osp_adjust_len(head, size, len);
+        }
+}
+
+/*
+ * Build parameter string for each channel
+ * param count Destination count
+ * param dest Destination
+ * param buffer Buffer
+ * param bufsize Buffer size
+ * return
+ */
+static void osp_build_eachparam(
+        int count,
+        osp_destination_t *dest,
+        char *buffer,
+        switch_size_t bufsize)
+{
+        char *head = buffer;
+        switch_size_t len, size = bufsize;
+
+        if ((count &gt; 0) &amp;&amp; dest &amp;&amp; head &amp;&amp; size) {
+                switch_snprintf(buffer, bufsize,
+                        &quot;[%s=%d,%s=%s,%s=%s&quot;,
+                        OSP_VAR_DESTCOUNT, count,
+                        OSP_VAR_DESTIP, dest-&gt;dest,
+                        OSP_FS_OUTCALLING, dest-&gt;calling);
+                osp_adjust_len(head, size, len);
+
+                if (!switch_strlen_zero_buf(dest-&gt;destnid)) {
+                        switch_snprintf(head, size, &quot;,%s=%s&quot;, OSP_VAR_DESTNID, dest-&gt;destnid);
+                }
+                osp_adjust_len(head, size, len);
+
+                switch_snprintf(head, size, &quot;]&quot;);
+                osp_adjust_len(head, size, len);
+        }
+}
+
+/*
+ * Build endpoint string
+ * param dest Destination
+ * param buffer Buffer
+ * param bufsize Buffer size
+ * return
+ */
+static void osp_build_endpoint(
+        osp_destination_t *dest,
+        char *buffer,
+        switch_size_t bufsize)
+{
+        char *head = buffer;
+        switch_size_t len, size = bufsize;
+
+        if (head &amp;&amp; size) {
+                switch (dest-&gt;protocol) {
+                case OSPC_DPROT_SIP:
+                        switch_snprintf(head, size, &quot;%s/%s/%s&quot;, osp_globals.module[OSPC_DPROT_SIP], osp_globals.profile[OSPC_DPROT_SIP], dest-&gt;called);
+                        osp_adjust_len(head, size, len);
+                        
+                        if (!switch_strlen_zero_buf(dest-&gt;nprn)) {
+                                switch_snprintf(head, size, &quot;;rn=%s&quot;, dest-&gt;nprn);
+                                osp_adjust_len(head, size, len);
+                        }
+                        if (!switch_strlen_zero_buf(dest-&gt;npcic)) {
+                                switch_snprintf(head, size, &quot;;cic=%s&quot;, dest-&gt;npcic);
+                                osp_adjust_len(head, size, len);
+                        }
+                        if (dest-&gt;npdi) {
+                                switch_snprintf(head, size, &quot;;npdi&quot;);
+                                osp_adjust_len(head, size, len);
+                        }
+
+                        switch_snprintf(head, size, &quot;@%s&quot;, dest-&gt;dest);
+                        osp_adjust_len(head, size, len);
+
+                        if (dest-&gt;userphone) {
+                                switch_snprintf(head, size, &quot;;user=phone&quot;);
+                                osp_adjust_len(head, size, len);
+                        }
+                        break;
+                case OSPC_DPROT_Q931:
+                        switch_snprintf(head, size, &quot;%s/%s/%s@%s&quot;, osp_globals.module[OSPC_DPROT_Q931], osp_globals.profile[OSPC_DPROT_Q931], dest-&gt;called, dest-&gt;dest);
+                        osp_adjust_len(head, size, len);
+                        break;
+                case OSPC_DPROT_IAX:
+                        switch_snprintf(head, size, &quot;%s/%s/%s/%s&quot;, osp_globals.module[OSPC_DPROT_Q931], osp_globals.profile[OSPC_DPROT_Q931], dest-&gt;dest, dest-&gt;called);
+                        osp_adjust_len(head, size, len);
+                        break;
+                case OSPC_DPROT_SKYPE:
+                        switch_snprintf(head, size, &quot;%s/%s/%s&quot;, osp_globals.module[OSPC_DPROT_Q931], osp_globals.profile[OSPC_DPROT_Q931], dest-&gt;called);
+                        osp_adjust_len(head, size, len);
+                        break;
+                default:
+                        buffer[0] = '\0';
+                        break;
+                }
+        }
+}
+
+/*
+ * Create route string
+ * param channel Originator channel
+ * param numdest Numer of destinations
+ * param destlist Destinations
+ * return
+ */
+static void osp_create_route(
+        switch_channel_t *channel,
+        osp_results_t *results)
+{
+        osp_destination_t *dest;
+        char name[OSP_SIZE_NORSTR];
+        char value[OSP_SIZE_NORSTR];
+        char eachparam[OSP_SIZE_NORSTR];
+        char endpoint[OSP_SIZE_NORSTR];
+        char buffer[OSP_SIZE_ROUSTR];
+        int i, len, count, size = sizeof(buffer);
+        char *head = buffer;
+        switch_event_header_t *hi;
+        char *var;
+
+        /* Cleanup OSP varibales in originator */
+        if ((hi = switch_channel_variable_first(channel))) {
+                for (; hi; hi = hi-&gt;next) {
+                        var = hi-&gt;name;
+                        if (var &amp;&amp; !strncmp(var, &quot;osp_&quot;, 4)) {
+                                switch_channel_set_variable(channel, var, NULL);
+                        }
+                }
+                switch_channel_variable_last(channel);
+        }
+
+        osp_build_allparam(results, head, size);
+        osp_adjust_len(head, size, len);
+
+        for (count = 0, i = 0; i &lt; results-&gt;numdest; i++) {
+                dest = &amp;results-&gt;dests[i];
+                if (dest-&gt;supported) {
+                        count++;
+                        osp_build_eachparam(i + 1, dest, eachparam, sizeof(eachparam));
+                        osp_build_endpoint(dest, endpoint, sizeof(endpoint));
+                        switch_snprintf(name, sizeof(name), &quot;%s%d&quot;, OSP_VAR_ROUTEPRE, count);
+                        switch_snprintf(value, sizeof(value), &quot;%s%s&quot;, eachparam, endpoint);
+                        switch_channel_set_variable_var_check(channel, name, value, SWITCH_FALSE);
+                        switch_snprintf(head, size, &quot;%s|&quot;, value);
+                        osp_adjust_len(head, size, len);
+                }
+        }
+
+        switch_snprintf(value, sizeof(value), &quot;%d&quot;, count);
+        switch_channel_set_variable_var_check(channel, OSP_VAR_ROUTECOUNT, value, SWITCH_FALSE);
+
+        if (count) {
+                *(buffer + strlen(buffer) - 1) = '\0';
+        } else {
+                buffer[0] = '\0';
+        }
+        switch_channel_set_variable_var_check(channel, OSP_VAR_AUTOROUTE, buffer, SWITCH_FALSE);
+}
+
+/*
+ * Macro expands to:
+ * static void osp_app_function(switch_core_session_t *session, const char *data)
+ */
+SWITCH_STANDARD_APP(osp_app_function)
+{
+        int argc = 0;
+        char *argv[2] = { 0 };
+        char *params = NULL;
+        char *profile = NULL;
+        switch_channel_t *channel;
+        osp_results_t results;
+        switch_status_t retval;
+
+        if (osp_globals.shutdown) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;OSP application inavailable\n&quot;);
+                return;
+        }
+
+        /* Make sure there is a valid channel when starting the OSP application */
+        if (!(channel = switch_core_session_get_channel(session))) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to find origiantor channel\n&quot;);
+                return;
+        }
+
+        if (!(params = switch_core_session_strdup(session, data))) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to alloc parameters\n&quot;);
+                return;
+        }
+
+        if ((argc = switch_separate_string(params, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+                profile = argv[0];
+        } else {
+                profile = OSP_DEF_PROFILE;
+        }
+
+        retval = osp_request_routing(channel, profile, &amp;results);
+        if (retval == SWITCH_STATUS_SUCCESS) {
+                osp_create_route(channel, &amp;results);
+        }
+}
+
+/*
+ * Add application
+ * param session
+ * param channel Originator channel
+ * param extenstion
+ * param results OSP routing info
+ * return
+ */
+static void osp_add_application(
+        switch_core_session_t *session,
+        switch_channel_t *channel,
+        switch_caller_extension_t **extension,
+        osp_results_t *results)
+{
+        osp_destination_t *dest;
+        char allparam[OSP_SIZE_NORSTR];
+        char eachparam[OSP_SIZE_NORSTR];
+        char endpoint[OSP_SIZE_NORSTR];
+        char buffer[OSP_SIZE_ROUSTR];
+        int i;
+
+        if ((*extension = switch_caller_extension_new(session, results-&gt;called, results-&gt;called)) == 0) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Failed to create extension\n&quot;);
+                return;
+        }
+
+        switch_channel_set_variable(channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE, &quot;true&quot;);
+
+        osp_build_allparam(results, allparam, sizeof(allparam));
+
+        for (i = 0; i &lt; results-&gt;numdest; i++) {
+                dest = &amp;results-&gt;dests[i];
+                if (dest-&gt;supported) {
+                        osp_build_eachparam(i + 1, dest, eachparam, sizeof(eachparam));
+                        osp_build_endpoint(dest, endpoint, sizeof(endpoint));
+                        switch_snprintf(buffer, sizeof(buffer), &quot;%s%s%s&quot;, allparam, eachparam, endpoint);
+                        switch_caller_extension_add_application(session, *extension, &quot;bridge&quot;, buffer);
+                }
+        }
+}
+
+/*
+ * Macro expands to:
+ * switch_caller_extension_t * osp_dialplan_function(switch_core_session_t *session, void *arg, switch_caller_profile_t *caller_profile)
+ */
+SWITCH_STANDARD_DIALPLAN(osp_dialplan_function)
+{
+        int argc = 0;
+        char *argv[2] = { 0 };
+        char *profile = NULL;
+        switch_caller_extension_t *extension = NULL;
+        switch_channel_t *channel = switch_core_session_get_channel(session);
+        osp_results_t results;
+        switch_status_t retval;
+
+        if (osp_globals.shutdown) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;OSP dialplan inavailable\n&quot;);
+                return extension;
+        }
+
+        if ((argc = switch_separate_string((char *)arg, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
+                profile = argv[0];
+        } else {
+                profile = OSP_DEF_PROFILE;
+        }
+
+        retval = osp_request_routing(channel, profile, &amp;results);
+        if (retval == SWITCH_STATUS_SUCCESS) {
+                osp_add_application(session, channel, &amp;extension, &amp;results);
+        }
+
+        return extension;
+}
+
+/*
+ * Retrieve OSP cookie
+ * param channel Destination channel
+ * param cookie OSP cookie
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_get_ospcookie(
+        switch_channel_t *channel,
+        osp_cookie_t *cookie)
+{
+        const char *strvar;
+
+        if (!(cookie-&gt;profile = switch_channel_get_variable(channel, OSP_VAR_PROFILE))) {
+                return SWITCH_STATUS_FALSE;
+        }
+
+        if ((strvar = switch_channel_get_variable(channel, OSP_VAR_TRANSID))) {
+                cookie-&gt;transid = atoll(strvar);
+        } else {
+                cookie-&gt;transid = 0;
+        }
+
+        if ((strvar = switch_channel_get_variable(channel, OSP_VAR_START))) {
+                cookie-&gt;start = atoll(strvar);
+        } else {
+                cookie-&gt;start = 0;
+        }
+
+        if ((strvar = switch_channel_get_variable(channel, OSP_VAR_DESTCOUNT))) {
+                cookie-&gt;destcount = atoi(strvar);
+        } else {
+                cookie-&gt;destcount = 0;
+        }
+        
+        cookie-&gt;dest = switch_channel_get_variable(channel, OSP_VAR_DESTIP);
+
+        cookie-&gt;srcnid = switch_channel_get_variable(channel, OSP_VAR_SRCNID);
+        cookie-&gt;destnid = switch_channel_get_variable(channel, OSP_VAR_DESTNID);
+
+        return SWITCH_STATUS_SUCCESS;
+}
+
+/*
+ * Retrieve usage info
+ * param channel Destination channel
+ * param originator Originator channel
+ * param cookie OSP cookie
+ * param usage Usage info
+ * return
+ */
+static void osp_get_usage(
+        switch_channel_t *channel,
+        switch_caller_profile_t *originator,
+        osp_cookie_t *cookie,
+        osp_usage_t *usage)
+{
+        const char *strvar;
+        switch_caller_profile_t *terminator;
+        switch_channel_timetable_t *times;
+
+        memset(usage, 0, sizeof(*usage));
+
+        usage-&gt;callid = switch_channel_get_variable(channel, OSP_FS_OUTCALLID);
+        if (switch_strlen_zero(usage-&gt;callid)) {
+                usage-&gt;callid = OSP_DEF_CALLID;
+        }
+
+        /* Originator had been checked by osp_on_reporting */
+        if (originator) {
+                usage-&gt;srcdev = originator-&gt;network_addr;
+                usage-&gt;calling = originator-&gt;caller_id_number;
+                osp_parse_userinfo(originator-&gt;destination_number, usage-&gt;called, sizeof(usage-&gt;called), NULL);
+        }
+
+        terminator = switch_channel_get_caller_profile(channel);
+        if (!strcasecmp(terminator-&gt;source, OSP_MODULE_SIP)) {
+                usage-&gt;protocol = OSPC_DPROT_SIP;
+                strvar = switch_channel_get_variable(channel, OSP_FS_SIPRELEASE);
+                if (!strvar || !strcasecmp(strvar, &quot;recv_bye&quot;)) {
+                        usage-&gt;release = 1;
+                }
+        } else if (!strcasecmp(terminator-&gt;source, OSP_MODULE_H323)) {
+                usage-&gt;protocol = OSPC_DPROT_Q931;
+        } else if (!strcasecmp(terminator-&gt;source, OSP_MODULE_IAX)) {
+                usage-&gt;protocol = OSPC_DPROT_IAX;
+        } else if (!strcasecmp(terminator-&gt;source, OSP_MODULE_SKYPE)) {
+                usage-&gt;protocol = OSPC_DPROT_SKYPE;
+        } else {
+                usage-&gt;protocol = OSPC_DPROT_UNKNOWN;
+        }
+
+        usage-&gt;cause = switch_channel_get_cause_q850(channel);
+
+        times = switch_channel_get_timetable(channel);
+        usage-&gt;alert = times-&gt;progress;
+        usage-&gt;connect = times-&gt;answered;
+        usage-&gt;end = times-&gt;hungup;
+        if (times-&gt;answered) {
+                usage-&gt;duration = times-&gt;hungup - times-&gt;answered;
+                usage-&gt;pdd = times-&gt;answered - cookie-&gt;start;
+        }
+
+        usage-&gt;fcodec = switch_channel_get_variable(channel, OSP_FS_DOWNCODEC);
+        usage-&gt;rcodec = switch_channel_get_variable(channel, OSP_FS_UPCODEC);
+        if ((strvar = switch_channel_get_variable(channel, OSP_FS_RTPDOWNOCTS))) {
+                usage-&gt;rtpdownoctets = atoi(strvar);
+        } else {
+                usage-&gt;rtpdownoctets = OSP_DEF_STATS;
+        }
+        if ((strvar = switch_channel_get_variable(channel, OSP_FS_RTPUPOCTS))) {
+                usage-&gt;rtpupoctets = atoi(strvar);
+        } else {
+                usage-&gt;rtpupoctets = OSP_DEF_STATS;
+        }
+        if ((strvar = switch_channel_get_variable(channel, OSP_FS_RTPDOWNPKTS))) {
+                usage-&gt;rtpdownpackets = atoi(strvar);
+        } else {
+                usage-&gt;rtpdownpackets = OSP_DEF_STATS;
+        }
+        if ((strvar = switch_channel_get_variable(channel, OSP_FS_RTPUPPKTS))) {
+                usage-&gt;rtpuppackets = atoi(strvar);
+        } else {
+                usage-&gt;rtpuppackets = OSP_DEF_STATS;
+        }
+}
+
+/*
+ * Report OSP usage thread function
+ * param threadarg Thread argments
+ * return
+ */
+static OSPTTHREADRETURN osp_report_thread(
+        void *threadarg)
+{
+        int i, error;
+        osp_threadarg_t *info;
+
+        info = (osp_threadarg_t *)threadarg;
+
+        OSPPTransactionRecordFailure(info-&gt;handle, info-&gt;cause);
+
+        for (i = 0; i &lt; 3; i++) {
+                error = OSPPTransactionReportUsage(
+                        info-&gt;handle,        /* Transaction handle */
+                        info-&gt;duration,        /* Call duration */
+                        info-&gt;start,        /* Call start time */
+                        info-&gt;end,                /* Call end time */
+                        info-&gt;alert,        /* Call alert time */
+                        info-&gt;connect,        /* Call connect time */
+                        info-&gt;pdd != 0,        /* Post dial delay present */
+                        info-&gt;pdd,                /* Post dial delay */
+                        info-&gt;release,        /* Release source */
+                        NULL,                        /* Conference ID */
+                        -1,                                /* Packets not received by peer */
+                        -1,                                /* Fraction of packets not received by peer */
+                        -1,                                /* Packets not received that were expected */
+                        -1,                                /* Fraction of packets expected but not received */
+                        NULL,                        /* Log buffer size */
+                        NULL);                        /* Log buffer */
+                if (error != OSPC_ERR_NO_ERROR) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
+                                &quot;Failed to report usage for '%llu' attempt '%d'\n&quot;,
+                                info-&gt;transid,
+                                i + 1);
+        } else {
+                        break;
+                }
+        }
+
+        OSPPTransactionDelete(info-&gt;handle);
+
+        switch_safe_free(info);
+
+        OSPTTHREADRETURN_NULL();
+}
+
+/*
+ * Report usage
+ * param cookie OSP cookie
+ * param usage Usage
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_report_usage(
+        osp_cookie_t *cookie,
+        osp_usage_t *usage)
+{
+        osp_provider_t *provider;
+        OSPTTRANHANDLE handle;
+        osp_threadarg_t *info;
+        OSPTTHREADID threadid;
+        OSPTTHRATTR threadattr;
+        int error;
+        switch_status_t status = SWITCH_STATUS_FALSE;
+
+        if (osp_find_provider(cookie-&gt;profile, &amp;provider) != SWITCH_STATUS_SUCCESS) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to find provider '%s'\n&quot;, cookie-&gt;profile);
+                return status;
+        }
+
+        if ((error = OSPPTransactionNew(provider-&gt;handle, &amp;handle)) != OSPC_ERR_NO_ERROR) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Failed to create transaction handle, error '%d'\n&quot;, error);
+                return status;
+        }
+
+        error = OSPPTransactionBuildUsageFromScratch(
+                handle,                                        /* Transaction handle */
+                cookie-&gt;transid,                /* Transaction ID */
+                OSPC_ROLE_SOURCE,                /* CDR type, source */
+                provider-&gt;device,                /* Source */
+                cookie-&gt;dest,                        /* Destination */
+                usage-&gt;srcdev,                        /* Source device */
+                OSP_DEF_STRING,                        /* Destination device */
+                usage-&gt;calling,                        /* Calling */
+                OSPC_NFORMAT_E164,                /* Calling format */
+                usage-&gt;called,                        /* Called */
+                OSPC_NFORMAT_E164,                /* Called format */
+                strlen(usage-&gt;callid),        /* Size of Call-ID */
+                usage-&gt;callid,                        /* Call-ID */
+                0,                                                /* Failure reason */
+                NULL,                                        /* Log buffer size */
+                NULL);                                        /* Log buffer */
+        if (error != OSPC_ERR_NO_ERROR) {
+                OSPPTransactionDelete(handle);
+                return status;
+        }
+
+        status = SWITCH_STATUS_SUCCESS;
+
+        OSPPTransactionSetDestinationCount(handle, cookie-&gt;destcount);
+
+        if (!switch_strlen_zero(cookie-&gt;srcnid)) {
+                OSPPTransactionSetSrcNetworkId(handle, cookie-&gt;srcnid);
+        }
+
+        if (!switch_strlen_zero(cookie-&gt;destnid)) {
+                OSPPTransactionSetDestNetworkId(handle, cookie-&gt;destnid);
+        }
+
+        if (!switch_strlen_zero(usage-&gt;fcodec)) {
+                OSPPTransactionSetForwardCodec(handle, usage-&gt;fcodec);
+        }
+        if (!switch_strlen_zero(usage-&gt;rcodec)) {
+                OSPPTransactionSetReverseCodec(handle, usage-&gt;rcodec);
+        }
+
+        if (usage-&gt;rtpdownoctets != OSP_DEF_STATS) {
+                OSPPTransactionSetOctets(handle, OSPC_SMETRIC_RTP, OSPC_SFLOW_DOWNSTREAM, usage-&gt;rtpdownoctets);
+        }
+        if (usage-&gt;rtpupoctets != OSP_DEF_STATS) {
+                OSPPTransactionSetOctets(handle, OSPC_SMETRIC_RTP, OSPC_SFLOW_UPSTREAM, usage-&gt;rtpupoctets);
+        }
+        if (usage-&gt;rtpdownpackets != OSP_DEF_STATS) {
+                OSPPTransactionSetPackets(handle, OSPC_SMETRIC_RTP, OSPC_SFLOW_DOWNSTREAM, usage-&gt;rtpdownpackets);
+        }
+        if (usage-&gt;rtpuppackets != OSP_DEF_STATS) {
+                OSPPTransactionSetPackets(handle, OSPC_SMETRIC_RTP, OSPC_SFLOW_UPSTREAM, usage-&gt;rtpuppackets);
+        }
+
+        info = (osp_threadarg_t *)malloc(sizeof(osp_threadarg_t));
+        info-&gt;handle = handle;
+        info-&gt;transid = cookie-&gt;transid;
+        info-&gt;cause = usage-&gt;cause;
+        info-&gt;start = cookie-&gt;start / 1000000;
+        info-&gt;alert = usage-&gt;alert / 1000000;
+        info-&gt;connect = usage-&gt;connect / 1000000;
+        info-&gt;end = usage-&gt;end / 1000000;
+        info-&gt;duration = usage-&gt;duration / 1000000;
+        info-&gt;pdd = usage-&gt;pdd / 1000000;
+        info-&gt;release = usage-&gt;release;
+
+        OSPM_THRATTR_INIT(threadattr, error);
+        OSPM_SETDETACHED_STATE(threadattr, error);
+        OSPM_CREATE_THREAD(threadid, &amp;threadattr, osp_report_thread, info, error);
+        OSPM_THRATTR_DESTROY(threadattr);
+
+        /* handle and info will be released by osp_report_thread */
+
+        return status;
+}
+
+/*
+ * Log UsageInd parameters
+ * param cookie OSP cookie
+ * param usage Usage info
+ * return
+ */
+static void osp_log_usageind(
+        osp_cookie_t *cookie,
+        osp_usage_t *usage)
+{
+        if (osp_globals.debug) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, osp_globals.loglevel,
+                        &quot;UsageInd: &quot;
+                        &quot;transid = '%llu' &quot;
+                        &quot;destcount = '%d' &quot;
+                        &quot;callid = '%s' &quot;
+                        &quot;calling = '%s' &quot;
+                        &quot;called = '%s' &quot;
+                        &quot;srcdev = '%s' &quot;
+                        &quot;dest = '%s' &quot;
+                        &quot;nid = '%s/%s' &quot;
+                        &quot;protocol = '%s' &quot;
+                        &quot;cause = '%d' &quot;
+                        &quot;release = '%s' &quot;
+                        &quot;times = '%llu/%llu/%llu/%llu' &quot;
+                        &quot;duration = '%llu' &quot;
+                        &quot;pdd = '%llu' &quot;
+                        &quot;outsessionid = '%s' &quot;
+                        &quot;codec = '%s/%s' &quot;
+                        &quot;rtpctets = '%d/%d' &quot;
+                        &quot;rtppackets = '%d/%d'\n&quot;,
+                        cookie-&gt;transid,
+                        cookie-&gt;destcount,
+                        usage-&gt;callid,
+                        usage-&gt;calling,
+                        usage-&gt;called,
+                        usage-&gt;srcdev,
+                        cookie-&gt;dest,
+                        osp_filter_null(cookie-&gt;srcnid), osp_filter_null(cookie-&gt;destnid),
+                        osp_get_protocol(usage-&gt;protocol),
+                        usage-&gt;cause,
+                        usage-&gt;release ? &quot;term&quot; : &quot;orig&quot;,
+                        cookie-&gt;start / 1000000, usage-&gt;alert / 1000000, usage-&gt;connect / 1000000, usage-&gt;end / 1000000,
+                        usage-&gt;duration / 1000000,
+                        usage-&gt;pdd / 1000000,
+                        usage-&gt;callid,
+                        osp_filter_null(usage-&gt;fcodec), osp_filter_null(usage-&gt;rcodec),
+                        usage-&gt;rtpdownoctets, usage-&gt;rtpupoctets,
+                        usage-&gt;rtpdownpackets, usage-&gt;rtpuppackets);
+        }
+}
+
+/*
+ * OSP module CS_REPORTING state handler
+ * param session Session
+ * return SWITCH_STATUS_SUCCESS Successful, SWITCH_STATUS_FALSE Failed
+ */
+static switch_status_t osp_on_reporting(
+        switch_core_session_t *session)
+{
+        switch_channel_t *channel;
+        osp_cookie_t cookie;
+        osp_usage_t usage;
+        switch_caller_profile_t *originator;
+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+        if (osp_globals.shutdown) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;OSP application inavailable\n&quot;);
+                return status;
+        }
+
+        /* Only report for B-leg */
+        if (!(channel = switch_core_session_get_channel(session)) || !(originator = switch_channel_get_originator_caller_profile(channel))) {
+                return status;
+        }
+
+        if (osp_get_ospcookie(channel, &amp;cookie) != SWITCH_STATUS_SUCCESS) {
+                return status;
+        }
+
+        osp_get_usage(channel, originator, &amp;cookie, &amp;usage);
+
+        osp_log_usageind(&amp;cookie, &amp;usage);
+
+        osp_report_usage(&amp;cookie, &amp;usage);
+
+        return status;
+}
+
+/*
+ * OSP module state handlers
+ */
+static switch_state_handler_table_t state_handlers = {
+        NULL,                                /*.on_init */
+        NULL,                                /*.on_routing */
+        NULL,                                /*.on_execute */
+        NULL,                                /*.on_hangup */
+        NULL,                                /*.on_exchange_media */
+        NULL,                                /*.on_soft_execute */
+        NULL,                                /*.on_consume_media */
+        NULL,                                /*.on_hibernate */
+        NULL,                                /*.on_reset */
+        NULL,                                /*.on_park */
+        osp_on_reporting        /*.on_reporting */
+};
+
+/*
+ * Macro expands to:
+ * switch_status_t mod_osp_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool)
+ */
+SWITCH_MODULE_LOAD_FUNCTION(mod_osp_load)
+{
+        switch_api_interface_t *api_interface;
+        switch_application_interface_t *app_interface;
+        switch_dialplan_interface_t *dp_interface;
+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+        /* Load OSP configuration */
+        if ((status = osp_load_settings(pool)) != SWITCH_STATUS_SUCCESS) {
+                return status;
+        }
+
+        /* Init OSP Toolkit */
+        osp_init_osptk();
+
+        /* Connect OSP internal structure to the blank pointer passed to OSP module */
+        *module_interface = switch_loadable_module_create_module_interface(pool, modname);
+
+        /* Add CLI OSP command */
+        SWITCH_ADD_API(api_interface, &quot;osp&quot;, &quot;OSP&quot;, osp_api_function, &quot;status&quot;);
+        switch_console_set_complete(&quot;add osp status&quot;);
+
+        /* Add OSP application */
+        SWITCH_ADD_APP(app_interface, &quot;osp&quot;, &quot;Perform an OSP lookup&quot;, &quot;Perform an OSP lookup&quot;, osp_app_function, &quot;&quot;, SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
+
+        /* Add OSP dialplan */
+        SWITCH_ADD_DIALPLAN(dp_interface, &quot;osp&quot;, osp_dialplan_function);
+
+        /* Add OSP state handlers */
+        switch_core_add_state_handler(&amp;state_handlers);
+
+        /* Indicate that the module should continue to be loaded */
+        return status;
+}
+
+/*
+ * Cleanup OSP client end
+ * return
+ */
+static void osp_cleanup_osptk(void)
+{
+        osp_provider_t *provider;
+
+        for (provider = osp_providers; provider; provider = provider-&gt;next) {
+                OSPPProviderDelete(provider-&gt;handle, 0);
+                provider-&gt;handle = OSP_INVALID_HANDLE;
+        }
+
+        OSPPCleanup();
+}
+
+/*
+ * Called when the system shuts down
+ * Macro expands to:
+ * switch_status_t mod_osp_shutdown(void)
+ */
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_osp_shutdown)
+{
+        /* Shutdown OSP module */
+        osp_globals.shutdown = SWITCH_TRUE;
+
+        /* Cleanup OSP Toolkit */
+        osp_cleanup_osptk();
+
+        /* Remoeve OSP state handlers */
+        switch_core_remove_state_handler(&amp;state_handlers);
+
+        return SWITCH_STATUS_SUCCESS;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:t
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4
+ */
+
</ins></span></pre>
</div>
</div>
<div id="footer">See you at ClueCon</div>

</body>
</html>