<!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][16761] </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=16761">16761</a></dd>
<dt>Author</dt> <dd>sathieu</dd>
<dt>Date</dt> <dd>2010-02-24 05:59:41 -0600 (Wed, 24 Feb 2010)</dd>
</dl>

<h3>Log Message</h3>
<pre>Skinny: helper script: test-skinny.pl</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#freeswitchtrunksrcmodendpointsmod_skinnymod_skinnyc">freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#freeswitchtrunksrcmodendpointsmod_skinnytestskinnypl">freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="freeswitchtrunksrcmodendpointsmod_skinnymod_skinnyc"></a>
<div class="modfile"><h4>Modified: freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c (16760 => 16761)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c        2010-02-24 11:59:34 UTC (rev 16760)
+++ freeswitch/trunk/src/mod/endpoints/mod_skinny/mod_skinny.c        2010-02-24 11:59:41 UTC (rev 16761)
</span><span class="lines">@@ -43,55 +43,87 @@
</span><span class="cx"> 
</span><span class="cx"> switch_endpoint_interface_t *skinny_endpoint_interface;
</span><span class="cx"> static switch_memory_pool_t *module_pool = NULL;
</span><del>-static int running = 1;
</del><span class="cx"> 
</span><del>-static struct {
</del><ins>+struct skinny_profile {
</ins><span class="cx">         /* prefs */
</span><del>-        int debug;
</del><ins>+        char *name;
</ins><span class="cx">         char *domain;
</span><span class="cx">         char *ip;
</span><span class="cx">         unsigned int port;
</span><span class="cx">         char *dialplan;
</span><ins>+        uint32_t keep_alive;
+        char date_format[6];
+        /* db */
+        char *dbname;
+        char *odbc_dsn;
+        char *odbc_user;
+        char *odbc_pass;
+        switch_odbc_handle_t *master_odbc;
+        /* listener */
+        int listener_threads;
+        switch_mutex_t *listener_mutex;        
+        switch_socket_t *sock;
+        switch_mutex_t *sock_mutex;
+        struct listener *listeners;
+        uint8_t listener_ready;
+};
+typedef struct skinny_profile skinny_profile_t;
+
+struct skinny_globals {
+        /* prefs */
+        int debug;
</ins><span class="cx">         char *codec_string;
</span><span class="cx">         char *codec_order[SWITCH_MAX_CODECS];
</span><span class="cx">         int codec_order_last;
</span><span class="cx">         char *codec_rates_string;
</span><span class="cx">         char *codec_rates[SWITCH_MAX_CODECS];
</span><span class="cx">         int codec_rates_last;
</span><del>-        uint32_t keep_alive;
-        char date_format[6];
</del><ins>+        unsigned int flags;
</ins><span class="cx">         /* data */
</span><ins>+        int calls;
+        switch_mutex_t *calls_mutex;
+        switch_hash_t *profile_hash;
</ins><span class="cx">         switch_event_node_t *heartbeat_node;
</span><del>-        unsigned int flags;
-        int calls;
-        switch_mutex_t *mutex;
-        switch_mutex_t *listener_mutex;        
-        int listener_threads;
-} globals;
-
-struct private_object {
-        unsigned int flags;
-        switch_codec_t read_codec;
-        switch_codec_t write_codec;
-        switch_frame_t read_frame;
-        unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
-        switch_core_session_t *session;
-        switch_caller_profile_t *caller_profile;
-        switch_mutex_t *mutex;
-        switch_mutex_t *flag_mutex;
-        //switch_thread_cond_t *cond;
</del><ins>+        int running;
</ins><span class="cx"> };
</span><ins>+typedef struct skinny_globals skinny_globals_t;
</ins><span class="cx"> 
</span><del>-typedef struct private_object private_t;
</del><ins>+static skinny_globals_t globals;
</ins><span class="cx"> 
</span><del>-
-SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_domain, globals.domain);
-SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_ip, globals.ip);
-SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, globals.dialplan);
</del><span class="cx"> SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, globals.codec_string);
</span><span class="cx"> SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_rates_string, globals.codec_rates_string);
</span><span class="cx"> 
</span><span class="cx"> /*****************************************************************************/
</span><ins>+/* SQL TYPES */
+/*****************************************************************************/
+static char devices_sql[] =
+        &quot;CREATE TABLE skinny_devices (\n&quot;
+        &quot;   device_name      VARCHAR(16),\n&quot;
+        &quot;   user_id          INTEGER,\n&quot;
+        &quot;   instance         INTEGER,\n&quot;
+        &quot;   ip               VARCHAR(255),\n&quot;
+        &quot;   device_type      INTEGER,\n&quot;
+        &quot;   max_streams      INTEGER,\n&quot;
+        &quot;   port             INTEGER,\n&quot;
+        &quot;   codec_string     VARCHAR(255)\n&quot;
+        &quot;);\n&quot;;
+
+static char lines_sql[] =
+        &quot;CREATE TABLE skinny_lines (\n&quot;
+        &quot;   device_name      VARCHAR(16),\n&quot;
+        &quot;   line_name        VARCHAR(24),\n&quot;
+        &quot;   line_shortname   VARCHAR(40),\n&quot;
+        &quot;   line_displayname VARCHAR(44)\n&quot;
+        &quot;);\n&quot;;
+
+static char speed_sql[] =
+        &quot;CREATE TABLE skinny_speeddials (\n&quot;
+        &quot;   device_name       VARCHAR(16),\n&quot;
+        &quot;   speed_number      VARCHAR(24),\n&quot;
+        &quot;   speed_displayname VARCHAR(40)\n&quot;
+        &quot;);\n&quot;;
+
+/*****************************************************************************/
</ins><span class="cx"> /* CHANNEL TYPES */
</span><span class="cx"> /*****************************************************************************/
</span><span class="cx"> 
</span><span class="lines">@@ -111,6 +143,21 @@
</span><span class="cx">         GFLAG_MY_CODEC_PREFS = (1 &lt;&lt; 0)
</span><span class="cx"> } GFLAGS;
</span><span class="cx"> 
</span><ins>+struct private_object {
+        unsigned int flags;
+        switch_codec_t read_codec;
+        switch_codec_t write_codec;
+        switch_frame_t read_frame;
+        unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
+        switch_core_session_t *session;
+        switch_caller_profile_t *caller_profile;
+        switch_mutex_t *mutex;
+        switch_mutex_t *flag_mutex;
+        //switch_thread_cond_t *cond;
+};
+
+typedef struct private_object private_t;
+
</ins><span class="cx"> /*****************************************************************************/
</span><span class="cx"> /* SKINNY MESSAGE TYPES */
</span><span class="cx"> /*****************************************************************************/
</span><span class="lines">@@ -234,8 +281,8 @@
</span><span class="cx"> #define SKINNY_MAX_SPEEDDIALS 20
</span><span class="cx"> struct skinny_speeddial {
</span><span class="cx">         struct skinny_device_t *device;
</span><del>-        char number[24];
-        char displayname[40];
</del><ins>+        char line[24];
+        char label[40];
</ins><span class="cx"> };
</span><span class="cx"> typedef struct skinny_speeddial skinny_speeddial_t;
</span><span class="cx"> 
</span><span class="lines">@@ -308,7 +355,9 @@
</span><span class="cx"> } event_flag_t;
</span><span class="cx"> 
</span><span class="cx"> struct listener {
</span><del>-        skinny_device_t *device;
</del><ins>+        skinny_profile_t *profile;
+        skinny_device_t *device; /* TODO -&gt; SQL */
+
</ins><span class="cx">         switch_socket_t *sock;
</span><span class="cx">         switch_memory_pool_t *pool;
</span><span class="cx">         switch_core_session_t *session;
</span><span class="lines">@@ -327,13 +376,6 @@
</span><span class="cx"> 
</span><span class="cx"> typedef switch_status_t (*skinny_listener_callback_func_t) (listener_t *listener, void *pvt);
</span><span class="cx"> 
</span><del>-static struct {
-        switch_socket_t *sock;
-        switch_mutex_t *sock_mutex;
-        listener_t *listeners;
-        uint8_t ready;
-} listen_list;
-
</del><span class="cx"> /*****************************************************************************/
</span><span class="cx"> /* FUNCTIONS */
</span><span class="cx"> /*****************************************************************************/
</span><span class="lines">@@ -361,6 +403,81 @@
</span><span class="cx"> static switch_status_t keepalive_listener(listener_t *listener, void *pvt);
</span><span class="cx"> 
</span><span class="cx"> /*****************************************************************************/
</span><ins>+/* SQL FUNCTIONS */
+/*****************************************************************************/
+static void skinny_execute_sql(skinny_profile_t *profile, char *sql, switch_mutex_t *mutex)
+{
+        switch_core_db_t *db;
+
+        if (mutex) {
+                switch_mutex_lock(mutex);
+        }
+
+        if (switch_odbc_available() &amp;&amp; profile-&gt;odbc_dsn) {
+                switch_odbc_statement_handle_t stmt;
+                if (switch_odbc_handle_exec(profile-&gt;master_odbc, sql, &amp;stmt) != SWITCH_ODBC_SUCCESS) {
+                        char *err_str;
+                        err_str = switch_odbc_handle_get_error(profile-&gt;master_odbc, stmt);
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;ERR: [%s]\n[%s]\n&quot;, sql, switch_str_nil(err_str));
+                        switch_safe_free(err_str);
+                }
+                switch_odbc_statement_handle_free(&amp;stmt);
+        } else {
+                if (!(db = switch_core_db_open_file(profile-&gt;dbname))) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Error Opening DB %s\n&quot;, profile-&gt;dbname);
+                        goto end;
+                }
+                switch_core_db_persistant_execute(db, sql, 1);
+                switch_core_db_close(db);
+        }
+
+  end:
+        if (mutex) {
+                switch_mutex_unlock(mutex);
+        }
+}
+
+
+static switch_bool_t skinny_execute_sql_callback(skinny_profile_t *profile,
+                                                                                          switch_mutex_t *mutex, char *sql, switch_core_db_callback_func_t callback, void *pdata)
+{
+        switch_bool_t ret = SWITCH_FALSE;
+        switch_core_db_t *db;
+        char *errmsg = NULL;
+
+        if (mutex) {
+                switch_mutex_lock(mutex);
+        }
+
+        if (switch_odbc_available() &amp;&amp; profile-&gt;odbc_dsn) {
+                switch_odbc_handle_callback_exec(profile-&gt;master_odbc, sql, callback, pdata, NULL);
+        } else {
+                if (!(db = switch_core_db_open_file(profile-&gt;dbname))) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Error Opening DB %s\n&quot;, profile-&gt;dbname);
+                        goto end;
+                }
+                switch_core_db_exec(db, sql, callback, pdata, &amp;errmsg);
+
+                if (errmsg) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;SQL ERR: [%s] %s\n&quot;, sql, errmsg);
+                        free(errmsg);
+                }
+
+                if (db) {
+                        switch_core_db_close(db);
+                }
+        }
+
+  end:
+
+        if (mutex) {
+                switch_mutex_unlock(mutex);
+        }
+
+        return ret;
+}
+
+/*****************************************************************************/
</ins><span class="cx"> /* CHANNEL FUNCTIONS */
</span><span class="cx"> /*****************************************************************************/
</span><span class="cx"> 
</span><span class="lines">@@ -396,9 +513,9 @@
</span><span class="cx">            where a destination has been identified. If the channel is simply
</span><span class="cx">            left in the initial state, nothing will happen. */
</span><span class="cx">         switch_channel_set_state(channel, CS_ROUTING);
</span><del>-        switch_mutex_lock(globals.mutex);
</del><ins>+        switch_mutex_lock(globals.calls_mutex);
</ins><span class="cx">         globals.calls++;
</span><del>-        switch_mutex_unlock(globals.mutex);
</del><ins>+        switch_mutex_unlock(globals.calls_mutex);
</ins><span class="cx"> 
</span><span class="cx">         switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, &quot;%s CHANNEL INIT\n&quot;, switch_channel_get_name(channel));
</span><span class="cx"> 
</span><span class="lines">@@ -482,12 +599,12 @@
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">         switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, &quot;%s CHANNEL HANGUP\n&quot;, switch_channel_get_name(channel));
</span><del>-        switch_mutex_lock(globals.mutex);
</del><ins>+        switch_mutex_lock(globals.calls_mutex);
</ins><span class="cx">         globals.calls--;
</span><span class="cx">         if (globals.calls &lt; 0) {
</span><span class="cx">                 globals.calls = 0;
</span><span class="cx">         }
</span><del>-        switch_mutex_unlock(globals.mutex);
</del><ins>+        switch_mutex_unlock(globals.calls_mutex);
</ins><span class="cx"> 
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="cx"> }
</span><span class="lines">@@ -837,13 +954,13 @@
</span><span class="cx">                 return SWITCH_STATUS_MEMERR;
</span><span class="cx">         }
</span><span class="cx">         
</span><del>-        if (!running) {
</del><ins>+        if (!globals.running) {
</ins><span class="cx">                 return SWITCH_STATUS_FALSE;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         ptr = mbuf;
</span><span class="cx"> 
</span><del>-        while (listener-&gt;sock &amp;&amp; running) {
</del><ins>+        while (listener-&gt;sock &amp;&amp; globals.running) {
</ins><span class="cx">                 uint8_t do_sleep = 1;
</span><span class="cx">                 if(bytes &lt; SKINNY_MESSAGE_FIELD_SIZE) {
</span><span class="cx">                         /* We have nothing yet, get length header field */
</span><span class="lines">@@ -855,7 +972,7 @@
</span><span class="cx"> 
</span><span class="cx">                 status = switch_socket_recv(listener-&gt;sock, ptr, &amp;mlen);
</span><span class="cx">                 
</span><del>-                if (!running || (!SWITCH_STATUS_IS_BREAK(status) &amp;&amp; status != SWITCH_STATUS_SUCCESS)) {
</del><ins>+                if (!globals.running || (!SWITCH_STATUS_IS_BREAK(status) &amp;&amp; status != SWITCH_STATUS_SUCCESS)) {
</ins><span class="cx">                         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, &quot;Socket break.\n&quot;);
</span><span class="cx">                         return SWITCH_STATUS_FALSE;
</span><span class="cx">                 }
</span><span class="lines">@@ -928,10 +1045,18 @@
</span><span class="cx">         switch_status_t status = SWITCH_STATUS_FALSE;
</span><span class="cx">         skinny_message_t *message;
</span><span class="cx">         skinny_device_t *device;
</span><ins>+        skinny_profile_t *profile;
</ins><span class="cx">         switch_event_t *event = NULL;
</span><span class="cx">         switch_event_t *params = NULL;
</span><del>-        switch_xml_t xml = NULL, domain, group, users, user;
</del><ins>+        switch_xml_t xroot, xdomain, xgroup, xuser, xskinny, xlines, xline, xspeeddials, xspeeddial;
+        assert(listener-&gt;profile);
+        profile = listener-&gt;profile;
</ins><span class="cx"> 
</span><ins>+        skinny_execute_sql(profile, &quot;select * from skinny_devices&quot;, profile-&gt;listener_mutex);
+        skinny_execute_sql_callback(profile, profile-&gt;listener_mutex,
+                &quot;select * from skinny_devices&quot;, NULL, profile);
+
+
</ins><span class="cx">         if(listener-&gt;device) {
</span><span class="cx">                 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
</span><span class="cx">                         &quot;A device is already registred on this listener.\n&quot;);
</span><span class="lines">@@ -952,15 +1077,16 @@
</span><span class="cx">         device-&gt;maxStreams = request-&gt;data.reg.maxStreams;
</span><span class="cx">         device-&gt;codec_string = realloc(device-&gt;codec_string, 1);
</span><span class="cx">         device-&gt;codec_string[0] = '\0';
</span><ins>+        device-&gt;line_last = 0;
</ins><span class="cx"> 
</span><span class="cx">         /* Check directory */
</span><span class="cx">         skinny_device_event(listener, &amp;params, SWITCH_EVENT_REQUEST_PARAMS, SWITCH_EVENT_SUBCLASS_ANY);
</span><span class="cx">         switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, &quot;action&quot;, &quot;skinny-auth&quot;);
</span><span class="cx"> 
</span><del>-        if (switch_xml_locate_group(device-&gt;deviceName, globals.domain, &amp;xml, &amp;domain, &amp;group, params) != SWITCH_STATUS_SUCCESS) {
</del><ins>+        if (switch_xml_locate_user(&quot;id&quot;, device-&gt;deviceName, profile-&gt;domain, &quot;&quot;, &amp;xroot, &amp;xdomain, &amp;xuser, &amp;xgroup, params) != SWITCH_STATUS_SUCCESS) {
</ins><span class="cx">                 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, &quot;Can't find device [%s@%s]\n&quot;
</span><del>-                                          &quot;You must define a domain called '%s' in your directory and add a group with the name=\&quot;%s\&quot; attribute.\n&quot;
-                                          , device-&gt;deviceName, globals.domain, globals.domain, device-&gt;deviceName);
</del><ins>+                                          &quot;You must define a domain called '%s' in your directory and add a user with id=\&quot;%s\&quot;.\n&quot;
+                                          , device-&gt;deviceName, profile-&gt;domain, profile-&gt;domain, device-&gt;deviceName);
</ins><span class="cx">                 message = switch_core_alloc(listener-&gt;pool, 12+sizeof(message-&gt;data.reg_rej));
</span><span class="cx">                 message-&gt;type = REGISTER_REJ_MESSAGE;
</span><span class="cx">                 message-&gt;length = 4 + sizeof(message-&gt;data.reg_rej);
</span><span class="lines">@@ -969,18 +1095,34 @@
</span><span class="cx">                 return SWITCH_STATUS_FALSE;
</span><span class="cx">                 goto end;
</span><span class="cx">         }
</span><del>-        users = switch_xml_child(group, &quot;users&quot;);
-        device-&gt;line_last = 0;
-        if(users) {
-                for (user = switch_xml_child(users, &quot;user&quot;); user; user = user-&gt;next) {
-                        const char *id = switch_xml_attr_soft(user, &quot;id&quot;);
-                        //const char *type = switch_xml_attr_soft(user, &quot;type&quot;);
-                        //TODO device-&gt;line[device-&gt;line_last].device = *device;
-                        strcpy(device-&gt;line[device-&gt;line_last].name, id);
-                        strcpy(device-&gt;line[device-&gt;line_last].shortname, id);
-                        strcpy(device-&gt;line[device-&gt;line_last].displayname, id);
-                        device-&gt;line_last++;
</del><ins>+        xskinny = switch_xml_child(xuser, &quot;skinny&quot;);
+        if (xskinny) {
+                xlines = switch_xml_child(xskinny, &quot;lines&quot;);
+                if (xlines) {
+                        for (xline = switch_xml_child(xlines, &quot;line&quot;); xline; xline = xline-&gt;next) {
+                                //TODO const char *position = switch_xml_attr_soft(xline, &quot;position&quot;);
+                                const char *name = switch_xml_attr_soft(xline, &quot;name&quot;);
+                                const char *shortname = switch_xml_attr_soft(xline, &quot;shortname&quot;);
+                                const char *displayname = switch_xml_attr_soft(xline, &quot;displayname&quot;);
+                                //TODO device-&gt;line[device-&gt;line_last].device = *device;
+                                strcpy(device-&gt;line[device-&gt;line_last].name, name);
+                                strcpy(device-&gt;line[device-&gt;line_last].shortname, shortname);
+                                strcpy(device-&gt;line[device-&gt;line_last].displayname, displayname);
+                                device-&gt;line_last++;
+                        }
</ins><span class="cx">                 }
</span><ins>+                xspeeddials = switch_xml_child(xskinny, &quot;speed-dials&quot;);
+                if (xspeeddials) {
+                        for (xspeeddial = switch_xml_child(xspeeddials, &quot;speed-dial&quot;); xspeeddial; xspeeddial = xspeeddial-&gt;next) {
+                                //TODO const char *position = switch_xml_attr_soft(xspeeddial, &quot;position&quot;);
+                                const char *line = switch_xml_attr_soft(xspeeddial, &quot;line&quot;);
+                                const char *label = switch_xml_attr_soft(xspeeddial, &quot;label&quot;);
+                                //TODO device-&gt;speeddial[device-&gt;speeddial_last].device = *device;
+                                strcpy(device-&gt;speeddial[device-&gt;speeddial_last].line, line);
+                                strcpy(device-&gt;speeddial[device-&gt;speeddial_last].label, label);
+                                device-&gt;speeddial_last++;
+                        }
+                }
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         listener-&gt;device = device;
</span><span class="lines">@@ -990,9 +1132,9 @@
</span><span class="cx">         message = switch_core_alloc(listener-&gt;pool, 12+sizeof(message-&gt;data.reg_ack));
</span><span class="cx">         message-&gt;type = REGISTER_ACK_MESSAGE;
</span><span class="cx">         message-&gt;length = 4 + sizeof(message-&gt;data.reg_ack);
</span><del>-        message-&gt;data.reg_ack.keepAlive = globals.keep_alive;
-        memcpy(message-&gt;data.reg_ack.dateFormat, globals.date_format, 6);
-        message-&gt;data.reg_ack.secondaryKeepAlive = globals.keep_alive;
</del><ins>+        message-&gt;data.reg_ack.keepAlive = profile-&gt;keep_alive;
+        memcpy(message-&gt;data.reg_ack.dateFormat, profile-&gt;date_format, 6);
+        message-&gt;data.reg_ack.secondaryKeepAlive = profile-&gt;keep_alive;
</ins><span class="cx">         skinny_send_reply(listener, message);
</span><span class="cx"> 
</span><span class="cx">         /* Send CapabilitiesReqMessage */
</span><span class="lines">@@ -1173,41 +1315,58 @@
</span><span class="cx"> 
</span><span class="cx"> static void add_listener(listener_t *listener)
</span><span class="cx"> {
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        listener-&gt;next = listen_list.listeners;
-        listen_list.listeners = listener;
-        switch_mutex_unlock(globals.listener_mutex);
</del><ins>+        skinny_profile_t *profile;
+        switch_assert(listener);
+        assert(listener-&gt;profile);
+        profile = listener-&gt;profile;
+
+        switch_mutex_lock(profile-&gt;listener_mutex);
+        listener-&gt;next = profile-&gt;listeners;
+        profile-&gt;listeners = listener;
+        switch_mutex_unlock(profile-&gt;listener_mutex);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void remove_listener(listener_t *listener)
</span><span class="cx"> {
</span><span class="cx">         listener_t *l, *last = NULL;
</span><ins>+        skinny_profile_t *profile;
+        switch_assert(listener);
+        assert(listener-&gt;profile);
+        profile = listener-&gt;profile;
</ins><span class="cx"> 
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        for (l = listen_list.listeners; l; l = l-&gt;next) {
</del><ins>+        switch_mutex_lock(profile-&gt;listener_mutex);
+        for (l = profile-&gt;listeners; l; l = l-&gt;next) {
</ins><span class="cx">                 if (l == listener) {
</span><span class="cx">                         if (last) {
</span><span class="cx">                                 last-&gt;next = l-&gt;next;
</span><span class="cx">                         } else {
</span><del>-                                listen_list.listeners = l-&gt;next;
</del><ins>+                                profile-&gt;listeners = l-&gt;next;
</ins><span class="cx">                         }
</span><span class="cx">                 }
</span><span class="cx">                 last = l;
</span><span class="cx">         }
</span><del>-        switch_mutex_unlock(globals.listener_mutex);
</del><ins>+        switch_mutex_unlock(profile-&gt;listener_mutex);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> static void walk_listeners(skinny_listener_callback_func_t callback, void *pvt)
</span><span class="cx"> {
</span><ins>+        switch_hash_index_t *hi;
+        void *val;
+        skinny_profile_t *profile;
</ins><span class="cx">         listener_t *l;
</span><ins>+        
+        /* walk listeners */
+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
</ins><span class="cx"> 
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        for (l = listen_list.listeners; l; l = l-&gt;next) {
-                callback(l, pvt);
</del><ins>+                switch_mutex_lock(profile-&gt;listener_mutex);
+                for (l = profile-&gt;listeners; l; l = l-&gt;next) {
+                        callback(l, pvt);
+                }
+                switch_mutex_unlock(profile-&gt;listener_mutex);
</ins><span class="cx">         }
</span><del>-        switch_mutex_unlock(globals.listener_mutex);
-
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void flush_listener(listener_t *listener, switch_bool_t flush_log, switch_bool_t flush_events)
</span><span class="lines">@@ -1218,22 +1377,34 @@
</span><span class="cx"> 
</span><span class="cx"> static listener_t *find_listener(char *device_name)
</span><span class="cx"> {
</span><ins>+        switch_hash_index_t *hi;
+        void *val;
+        skinny_profile_t *profile;
</ins><span class="cx">         listener_t *l, *r = NULL;
</span><span class="cx">         skinny_device_t *device;
</span><ins>+        
+        /* walk listeners */
+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
</ins><span class="cx"> 
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        for (l = listen_list.listeners; l; l = l-&gt;next) {
-                if (l-&gt;device) {
-                        device = l-&gt;device;
-                        if(!strcasecmp(device-&gt;deviceName,device_name)) {
-                                if (switch_thread_rwlock_tryrdlock(l-&gt;rwlock) == SWITCH_STATUS_SUCCESS) {
-                                        r = l;
</del><ins>+                switch_mutex_lock(profile-&gt;listener_mutex);
+                for (l = profile-&gt;listeners; l; l = l-&gt;next) {
+                        if (l-&gt;device) {
+                                device = l-&gt;device;
+                                if(!strcasecmp(device-&gt;deviceName,device_name)) {
+                                        if (switch_thread_rwlock_tryrdlock(l-&gt;rwlock) == SWITCH_STATUS_SUCCESS) {
+                                                r = l;
+                                        }
+                                        break;
</ins><span class="cx">                                 }
</span><del>-                                break;
</del><span class="cx">                         }
</span><span class="cx">                 }
</span><ins>+                switch_mutex_unlock(profile-&gt;listener_mutex);
+                if(r) {
+                        break;
+                }
</ins><span class="cx">         }
</span><del>-        switch_mutex_unlock(globals.listener_mutex);
</del><span class="cx">         return r;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -1260,13 +1431,17 @@
</span><span class="cx"> 
</span><span class="cx"> static void close_socket(switch_socket_t **sock)
</span><span class="cx"> {
</span><del>-        switch_mutex_lock(listen_list.sock_mutex);
</del><ins>+        /* TODO
+        switch_mutex_lock(profile-&gt;sock_mutex);
+        */
</ins><span class="cx">         if (*sock) {
</span><span class="cx">                 switch_socket_shutdown(*sock, SWITCH_SHUTDOWN_READWRITE);
</span><span class="cx">                 switch_socket_close(*sock);
</span><span class="cx">                 *sock = NULL;
</span><span class="cx">         }
</span><del>-        switch_mutex_unlock(listen_list.sock_mutex);
</del><ins>+        /* TODO
+        switch_mutex_unlock(profile-&gt;sock_mutex);
+        */
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static switch_status_t kill_listener(listener_t *listener, void *pvt)
</span><span class="lines">@@ -1292,9 +1467,12 @@
</span><span class="cx"> 
</span><span class="cx"> static switch_status_t keepalive_listener(listener_t *listener, void *pvt)
</span><span class="cx"> {
</span><ins>+        skinny_profile_t *profile;
</ins><span class="cx">         switch_assert(listener);
</span><ins>+        assert(listener-&gt;profile);
+        profile = listener-&gt;profile;
</ins><span class="cx">         
</span><del>-        listener-&gt;expire_time = switch_epoch_time_now(NULL)+globals.keep_alive*110/100;
</del><ins>+        listener-&gt;expire_time = switch_epoch_time_now(NULL)+profile-&gt;keep_alive*110/100;
</ins><span class="cx"> 
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="cx"> }
</span><span class="lines">@@ -1307,10 +1485,14 @@
</span><span class="cx">         switch_channel_t *channel = NULL;
</span><span class="cx">         skinny_message_t *request = NULL;
</span><span class="cx">         skinny_message_t *reply = NULL;
</span><ins>+        skinny_profile_t *profile;
+        switch_assert(listener);
+        assert(listener-&gt;profile);
+        profile = listener-&gt;profile;
</ins><span class="cx"> 
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        globals.listener_threads++;
-        switch_mutex_unlock(globals.listener_mutex);
</del><ins>+        switch_mutex_lock(profile-&gt;listener_mutex);
+        profile-&gt;listener_threads++;
+        switch_mutex_unlock(profile-&gt;listener_mutex);
</ins><span class="cx">         
</span><span class="cx">         switch_assert(listener != NULL);
</span><span class="cx">         
</span><span class="lines">@@ -1337,7 +1519,7 @@
</span><span class="cx">         add_listener(listener);
</span><span class="cx"> 
</span><span class="cx"> 
</span><del>-        while (running &amp;&amp; switch_test_flag(listener, LFLAG_RUNNING) &amp;&amp; listen_list.ready) {
</del><ins>+        while (globals.running &amp;&amp; switch_test_flag(listener, LFLAG_RUNNING) &amp;&amp; profile-&gt;listener_ready) {
</ins><span class="cx">                 status = skinny_read_packet(listener, &amp;request);
</span><span class="cx"> 
</span><span class="cx">                 if (status != SWITCH_STATUS_SUCCESS) {
</span><span class="lines">@@ -1395,9 +1577,9 @@
</span><span class="cx">                 switch_core_destroy_memory_pool(&amp;pool);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        globals.listener_threads--;
-        switch_mutex_unlock(globals.listener_mutex);
</del><ins>+        switch_mutex_lock(profile-&gt;listener_mutex);
+        profile-&gt;listener_threads--;
+        switch_mutex_unlock(profile-&gt;listener_mutex);
</ins><span class="cx"> 
</span><span class="cx">         return NULL;
</span><span class="cx"> }
</span><span class="lines">@@ -1414,7 +1596,7 @@
</span><span class="cx">         switch_thread_create(&amp;thread, thd_attr, listener_run, listener, listener-&gt;pool);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-int skinny_socket_create_and_bind()
</del><ins>+int skinny_socket_create_and_bind(skinny_profile_t *profile)
</ins><span class="cx"> {
</span><span class="cx">         switch_status_t rv;
</span><span class="cx">         switch_sockaddr_t *sa;
</span><span class="lines">@@ -1428,41 +1610,41 @@
</span><span class="cx">                 return SWITCH_STATUS_TERM;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        while(running) {
-                rv = switch_sockaddr_info_get(&amp;sa, globals.ip, SWITCH_INET, globals.port, 0, pool);
</del><ins>+        while(globals.running) {
+                rv = switch_sockaddr_info_get(&amp;sa, profile-&gt;ip, SWITCH_INET, profile-&gt;port, 0, pool);
</ins><span class="cx">                 if (rv)
</span><span class="cx">                         goto fail;
</span><del>-                rv = switch_socket_create(&amp;listen_list.sock, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool);
</del><ins>+                rv = switch_socket_create(&amp;profile-&gt;sock, switch_sockaddr_get_family(sa), SOCK_STREAM, SWITCH_PROTO_TCP, pool);
</ins><span class="cx">                 if (rv)
</span><span class="cx">                         goto sock_fail;
</span><del>-                rv = switch_socket_opt_set(listen_list.sock, SWITCH_SO_REUSEADDR, 1);
</del><ins>+                rv = switch_socket_opt_set(profile-&gt;sock, SWITCH_SO_REUSEADDR, 1);
</ins><span class="cx">                 if (rv)
</span><span class="cx">                         goto sock_fail;
</span><del>-                rv = switch_socket_bind(listen_list.sock, sa);
</del><ins>+                rv = switch_socket_bind(profile-&gt;sock, sa);
</ins><span class="cx">                 if (rv)
</span><span class="cx">                         goto sock_fail;
</span><del>-                rv = switch_socket_listen(listen_list.sock, 5);
</del><ins>+                rv = switch_socket_listen(profile-&gt;sock, 5);
</ins><span class="cx">                 if (rv)
</span><span class="cx">                         goto sock_fail;
</span><del>-                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;Socket up listening on %s:%u\n&quot;, globals.ip, globals.port);
</del><ins>+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, &quot;Socket up listening on %s:%u\n&quot;, profile-&gt;ip, profile-&gt;port);
</ins><span class="cx"> 
</span><span class="cx">                 break;
</span><span class="cx">           sock_fail:
</span><del>-                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Socket Error! Could not listen on %s:%u\n&quot;, globals.ip, globals.port);
</del><ins>+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Socket Error! Could not listen on %s:%u\n&quot;, profile-&gt;ip, profile-&gt;port);
</ins><span class="cx">                 switch_yield(100000);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        listen_list.ready = 1;
</del><ins>+        profile-&gt;listener_ready = 1;
</ins><span class="cx"> 
</span><del>-        while(running) {
</del><ins>+        while(globals.running) {
</ins><span class="cx"> 
</span><span class="cx">                 if (switch_core_new_memory_pool(&amp;listener_pool) != SWITCH_STATUS_SUCCESS) {
</span><span class="cx">                         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;OH OH no pool\n&quot;);
</span><span class="cx">                         goto fail;
</span><span class="cx">                 }
</span><span class="cx"> 
</span><del>-                if ((rv = switch_socket_accept(&amp;inbound_socket, listen_list.sock, listener_pool))) {
-                        if (!running) {
</del><ins>+                if ((rv = switch_socket_accept(&amp;inbound_socket, profile-&gt;sock, listener_pool))) {
+                        if (!globals.running) {
</ins><span class="cx">                                 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, &quot;Shutting Down\n&quot;);
</span><span class="cx">                                 goto end;
</span><span class="cx">                         } else {
</span><span class="lines">@@ -1488,6 +1670,7 @@
</span><span class="cx">                 listener-&gt;pool = listener_pool;
</span><span class="cx">                 listener_pool = NULL;
</span><span class="cx">                 listener-&gt;device = NULL;
</span><ins>+                listener-&gt;profile = profile;
</ins><span class="cx"> 
</span><span class="cx">                 switch_mutex_init(&amp;listener-&gt;flag_mutex, SWITCH_MUTEX_NESTED, listener-&gt;pool);
</span><span class="cx"> 
</span><span class="lines">@@ -1500,7 +1683,7 @@
</span><span class="cx"> 
</span><span class="cx">  end:
</span><span class="cx"> 
</span><del>-        close_socket(&amp;listen_list.sock);
</del><ins>+        close_socket(&amp;profile-&gt;sock);
</ins><span class="cx">         
</span><span class="cx">         if (pool) {
</span><span class="cx">                 switch_core_destroy_memory_pool(&amp;pool);
</span><span class="lines">@@ -1518,33 +1701,56 @@
</span><span class="cx"> /*****************************************************************************/
</span><span class="cx"> /* MODULE FUNCTIONS */
</span><span class="cx"> /*****************************************************************************/
</span><ins>+static void skinny_profile_set(skinny_profile_t *profile, char *var, char *val)
+{
+        if (!var)
+                return;
+
+        if (!strcasecmp(var, &quot;domain&quot;)) {
+                profile-&gt;domain = switch_core_strdup(module_pool, val);
+        } else if (!strcasecmp(var, &quot;ip&quot;)) {
+                profile-&gt;ip = switch_core_strdup(module_pool, val);
+        } else if (!strcasecmp(var, &quot;dialplan&quot;)) {
+                profile-&gt;dialplan = switch_core_strdup(module_pool, val);
+        } else if (!strcasecmp(var, &quot;odbc-dsn&quot;) &amp;&amp; !zstr(val)) {
+                if (switch_odbc_available()) {
+                        profile-&gt;odbc_dsn = switch_core_strdup(module_pool, val);
+                        if ((profile-&gt;odbc_user = strchr(profile-&gt;odbc_dsn, ':'))) {
+                                *profile-&gt;odbc_user++ = '\0';
+                                if ((profile-&gt;odbc_pass = strchr(profile-&gt;odbc_user, ':'))) {
+                                        *profile-&gt;odbc_pass++ = '\0';
+                                }
+                        }
+                } else {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;ODBC IS NOT AVAILABLE!\n&quot;);
+                }
+        }
+}
+
</ins><span class="cx"> static switch_status_t load_skinny_config(void)
</span><span class="cx"> {
</span><span class="cx">         char *cf = &quot;skinny.conf&quot;;
</span><del>-        switch_xml_t cfg, xml, settings, param;
</del><ins>+        switch_xml_t xcfg, xml, xsettings, xprofiles, xprofile, xparam;
</ins><span class="cx"> 
</span><span class="cx">         memset(&amp;globals, 0, sizeof(globals));
</span><del>-        switch_mutex_init(&amp;globals.mutex, SWITCH_MUTEX_NESTED, module_pool);
-        if (!(xml = switch_xml_open_cfg(cf, &amp;cfg, NULL))) {
</del><ins>+        globals.running = 1;
+
+        switch_core_hash_init(&amp;globals.profile_hash, module_pool);
+
+        switch_mutex_init(&amp;globals.calls_mutex, SWITCH_MUTEX_NESTED, module_pool);
+        
+        if (!(xml = switch_xml_open_cfg(cf, &amp;xcfg, NULL))) {
</ins><span class="cx">                 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Open of %s failed\n&quot;, cf);
</span><span class="cx">                 return SWITCH_STATUS_TERM;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        if ((settings = switch_xml_child(cfg, &quot;settings&quot;))) {
-                for (param = switch_xml_child(settings, &quot;param&quot;); param; param = param-&gt;next) {
-                        char *var = (char *) switch_xml_attr_soft(param, &quot;name&quot;);
-                        char *val = (char *) switch_xml_attr_soft(param, &quot;value&quot;);
</del><ins>+        if ((xsettings = switch_xml_child(xcfg, &quot;settings&quot;))) {
+                for (xparam = switch_xml_child(xsettings, &quot;param&quot;); xparam; xparam = xparam-&gt;next) {
+                        char *var = (char *) switch_xml_attr_soft(xparam, &quot;name&quot;);
+                        char *val = (char *) switch_xml_attr_soft(xparam, &quot;value&quot;);
</ins><span class="cx"> 
</span><span class="cx">                         if (!strcmp(var, &quot;debug&quot;)) {
</span><span class="cx">                                 globals.debug = atoi(val);
</span><del>-                        } else if (!strcmp(var, &quot;domain&quot;)) {
-                                set_global_domain(val);
-                        } else if (!strcmp(var, &quot;ip&quot;)) {
-                                set_global_ip(val);
-                        } else if (!strcmp(var, &quot;port&quot;)) {
-                                globals.port = atoi(val);
-                        } else if (!strcmp(var, &quot;dialplan&quot;)) {
-                                set_global_dialplan(val);
</del><span class="cx">                         } else if (!strcmp(var, &quot;codec-prefs&quot;)) {
</span><span class="cx">                                 set_global_codec_string(val);
</span><span class="cx">                                 globals.codec_order_last = switch_separate_string(globals.codec_string, ',', globals.codec_order, SWITCH_MAX_CODECS);
</span><span class="lines">@@ -1555,21 +1761,93 @@
</span><span class="cx">                         } else if (!strcmp(var, &quot;codec-rates&quot;)) {
</span><span class="cx">                                 set_global_codec_rates_string(val);
</span><span class="cx">                                 globals.codec_rates_last = switch_separate_string(globals.codec_rates_string, ',', globals.codec_rates, SWITCH_MAX_CODECS);
</span><del>-                        } else if (!strcmp(var, &quot;keep-alive&quot;)) {
-                                globals.keep_alive = atoi(val);
-                        } else if (!strcmp(var, &quot;date-format&quot;)) {
-                                memcpy(globals.date_format, val, 6);
</del><span class="cx">                         }
</span><del>-                }
-        }
-        if (!globals.dialplan) {
-                set_global_dialplan(&quot;default&quot;);
-        }
</del><ins>+                } /* param */
+        } /* settings */
</ins><span class="cx"> 
</span><del>-        if (!globals.port) {
-                globals.port = 2000;
</del><ins>+        if ((xprofiles = switch_xml_child(xcfg, &quot;profiles&quot;))) {
+                for (xprofile = switch_xml_child(xprofiles, &quot;profile&quot;); xprofile; xprofile = xprofile-&gt;next) {
+                        char *profile_name = (char *) switch_xml_attr_soft(xprofile, &quot;name&quot;);
+                        switch_xml_t xsettings = switch_xml_child(xprofile, &quot;settings&quot;);
+                        if (zstr(profile_name)) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
+                                        &quot;&lt;profile&gt; is missing name attribute\n&quot;);
+                                continue;
+                        }
+                        if (xsettings) {
+                                char dbname[256];
+                                switch_core_db_t *db;
+                                skinny_profile_t *profile = NULL;
+                                switch_xml_t param;
+                                
+                                profile = switch_core_alloc(module_pool, sizeof(skinny_profile_t));
+                                profile-&gt;name = profile_name;
+                                
+                                for (param = switch_xml_child(xsettings, &quot;param&quot;); param; param = param-&gt;next) {
+                                        char *var = (char *) switch_xml_attr_soft(param, &quot;name&quot;);
+                                        char *val = (char *) switch_xml_attr_soft(param, &quot;value&quot;);
+
+                                        if (!strcmp(var, &quot;domain&quot;)) {
+                                                skinny_profile_set(profile, &quot;domain&quot;, val);
+                                        } else if (!strcmp(var, &quot;ip&quot;)) {
+                                                skinny_profile_set(profile, &quot;ip&quot;, val);
+                                        } else if (!strcmp(var, &quot;port&quot;)) {
+                                                profile-&gt;port = atoi(val);
+                                        } else if (!strcmp(var, &quot;dialplan&quot;)) {
+                                                skinny_profile_set(profile, &quot;dialplan&quot;, val);
+                                        } else if (!strcmp(var, &quot;keep-alive&quot;)) {
+                                                profile-&gt;keep_alive = atoi(val);
+                                        } else if (!strcmp(var, &quot;date-format&quot;)) {
+                                                memcpy(profile-&gt;date_format, val, 6);
+                                        }
+                                } /* param */
+                                
+                                if (!profile-&gt;dialplan) {
+                                        skinny_profile_set(profile, &quot;dialplan&quot;,&quot;default&quot;);
+                                }
+
+                                if (!profile-&gt;port) {
+                                        profile-&gt;port = 2000;
+                                }
+
+                                switch_snprintf(dbname, sizeof(dbname), &quot;skinny_%s&quot;, profile-&gt;name);
+                                profile-&gt;dbname = switch_core_strdup(module_pool, dbname);
+
+                                if (switch_odbc_available() &amp;&amp; profile-&gt;odbc_dsn) {
+                                        if (!(profile-&gt;master_odbc = switch_odbc_handle_new(profile-&gt;odbc_dsn, profile-&gt;odbc_user, profile-&gt;odbc_pass))) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Cannot Open ODBC Database!\n&quot;);
+                                                continue;
+
+                                        }
+                                        if (switch_odbc_handle_connect(profile-&gt;master_odbc) != SWITCH_ODBC_SUCCESS) {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Cannot Open ODBC Database!\n&quot;);
+                                                continue;
+                                        }
+
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, &quot;Connected ODBC DSN: %s\n&quot;, profile-&gt;odbc_dsn);
+                                        switch_odbc_handle_exec(profile-&gt;master_odbc, devices_sql, NULL);
+                                        switch_odbc_handle_exec(profile-&gt;master_odbc, lines_sql, NULL);
+                                        switch_odbc_handle_exec(profile-&gt;master_odbc, speed_sql, NULL);
+                                } else {
+                                        if ((db = switch_core_db_open_file(profile-&gt;dbname))) {
+                                                switch_core_db_test_reactive(db, &quot;select * from skinny_devices&quot;, NULL, devices_sql);
+                                                switch_core_db_test_reactive(db, &quot;select * from skinny_lines&quot;, NULL, lines_sql);
+                                                switch_core_db_test_reactive(db, &quot;select * from skinny_speeddials&quot;, NULL, speed_sql);
+                                        } else {
+                                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, &quot;Cannot Open SQL Database!\n&quot;);
+                                                continue;
+                                        }
+                                        switch_core_db_close(db);
+                                }
+                                
+                                switch_core_hash_insert(globals.profile_hash, profile-&gt;name, profile);
+                                profile = NULL;
+                        } else {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
+                                        &quot;Settings are missing from profile %s.\n&quot;, profile_name);
+                        } /* settings */
+                } /* profile */
</ins><span class="cx">         }
</span><del>-
</del><span class="cx">         switch_xml_free(xml);
</span><span class="cx"> 
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="lines">@@ -1656,18 +1934,28 @@
</span><span class="cx"> {
</span><span class="cx">         switch_console_callback_match_t *my_matches = NULL;
</span><span class="cx">         switch_status_t status = SWITCH_STATUS_FALSE;
</span><ins>+        switch_hash_index_t *hi;
+        void *val;
+        skinny_profile_t *profile;
+        
</ins><span class="cx">         listener_t *l;
</span><span class="cx">         skinny_device_t *device;
</span><span class="cx"> 
</span><del>-        switch_mutex_lock(globals.listener_mutex);
-        for (l = listen_list.listeners; l; l = l-&gt;next) {
-                if(l-&gt;device) {
-                        device = l-&gt;device;
-                        switch_console_push_match(&amp;my_matches, device-&gt;deviceName);
</del><ins>+        /* walk listeners */
+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
+
+                switch_mutex_lock(profile-&gt;listener_mutex);
+                for (l = profile-&gt;listeners; l; l = l-&gt;next) {
+                        if(l-&gt;device) {
+                                device = l-&gt;device;
+                                switch_console_push_match(&amp;my_matches, device-&gt;deviceName);
+                        }
</ins><span class="cx">                 }
</span><ins>+                switch_mutex_unlock(profile-&gt;listener_mutex);
</ins><span class="cx">         }
</span><del>-        switch_mutex_unlock(globals.listener_mutex);
-
</del><ins>+        
</ins><span class="cx">         if (my_matches) {
</span><span class="cx">                 *matches = my_matches;
</span><span class="cx">                 status = SWITCH_STATUS_SUCCESS;
</span><span class="lines">@@ -1678,18 +1966,25 @@
</span><span class="cx"> 
</span><span class="cx"> SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)
</span><span class="cx"> {
</span><ins>+        switch_hash_index_t *hi;
+        void *val;
+        skinny_profile_t *profile;
+
</ins><span class="cx">         switch_api_interface_t *api_interface;
</span><span class="cx"> 
</span><span class="cx">         module_pool = pool;
</span><span class="cx"> 
</span><del>-        memset(&amp;globals, 0, sizeof(globals));
-
</del><span class="cx">         load_skinny_config();
</span><span class="cx"> 
</span><del>-        switch_mutex_init(&amp;globals.listener_mutex, SWITCH_MUTEX_NESTED, module_pool);
</del><ins>+        /* init listeners */
+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
+        
+                switch_mutex_init(&amp;profile-&gt;listener_mutex, SWITCH_MUTEX_NESTED, module_pool);
+                switch_mutex_init(&amp;profile-&gt;sock_mutex, SWITCH_MUTEX_NESTED, module_pool);
</ins><span class="cx"> 
</span><del>-        memset(&amp;listen_list, 0, sizeof(listen_list));
-        switch_mutex_init(&amp;listen_list.sock_mutex, SWITCH_MUTEX_NESTED, module_pool);
</del><ins>+        }
</ins><span class="cx"> 
</span><span class="cx">         if ((switch_event_bind_removable(modname, SWITCH_EVENT_HEARTBEAT, NULL, event_handler, NULL, &amp;globals.heartbeat_node) != SWITCH_STATUS_SUCCESS)) {
</span><span class="cx">                 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, &quot;Couldn't bind our heartbeat handler!\n&quot;);
</span><span class="lines">@@ -1701,7 +1996,6 @@
</span><span class="cx">                 return SWITCH_STATUS_TERM;
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-
</del><span class="cx">         /* connect my internal structure to the blank pointer passed to me */
</span><span class="cx">         *module_interface = switch_loadable_module_create_module_interface(pool, modname);
</span><span class="cx">         skinny_endpoint_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ENDPOINT_INTERFACE);
</span><span class="lines">@@ -1722,34 +2016,62 @@
</span><span class="cx"> 
</span><span class="cx"> SWITCH_MODULE_RUNTIME_FUNCTION(mod_skinny_runtime)
</span><span class="cx"> {
</span><del>-        return skinny_socket_create_and_bind();
</del><ins>+        switch_status_t status = SWITCH_STATUS_SUCCESS;
+        switch_hash_index_t *hi;
+        void *val;
+        skinny_profile_t *profile;
+        
+        /* launch listeners */
+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
+        
+                status = skinny_socket_create_and_bind(profile);
+                if(status != SWITCH_STATUS_SUCCESS) {
+                        return status;
+                }
+        }
+        return status;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown)
</span><span class="cx"> {
</span><ins>+        switch_hash_index_t *hi;
+        void *val;
+        skinny_profile_t *profile;
</ins><span class="cx">         int sanity = 0;
</span><span class="cx"> 
</span><span class="cx">         switch_event_free_subclass(SKINNY_EVENT_REGISTER);
</span><span class="cx">         switch_event_unbind(&amp;globals.heartbeat_node);
</span><span class="cx"> 
</span><del>-        running = 0;
</del><ins>+        globals.running = 0;
</ins><span class="cx"> 
</span><span class="cx">         walk_listeners(kill_listener, NULL);
</span><span class="cx"> 
</span><del>-        close_socket(&amp;listen_list.sock);
</del><ins>+        /* launch listeners */
+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
</ins><span class="cx"> 
</span><del>-        while (globals.listener_threads) {
-                switch_yield(100000);
-                walk_listeners(kill_listener, NULL);
-                if (++sanity &gt;= 200) {
-                        break;
</del><ins>+                close_socket(&amp;profile-&gt;sock);
+
+                while (profile-&gt;listener_threads) {
+                        switch_yield(100000);
+                        walk_listeners(kill_listener, NULL);
+                        if (++sanity &gt;= 200) {
+                                break;
+                        }
</ins><span class="cx">                 }
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         /* Free dynamically allocated strings */
</span><del>-        switch_safe_free(globals.domain);
-        switch_safe_free(globals.ip);
-        switch_safe_free(globals.dialplan);
</del><ins>+        for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &amp;val);
+                profile = (skinny_profile_t *) val;
+                switch_safe_free(profile-&gt;domain);
+                switch_safe_free(profile-&gt;ip);
+                switch_safe_free(profile-&gt;dialplan);
+        }
</ins><span class="cx">         switch_safe_free(globals.codec_string);
</span><span class="cx">         switch_safe_free(globals.codec_rates_string);
</span><span class="cx">         
</span></span></pre></div>
<a id="freeswitchtrunksrcmodendpointsmod_skinnytestskinnypl"></a>
<div class="addfile"><h4>Added: freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl (0 => 16761)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl                                (rev 0)
+++ freeswitch/trunk/src/mod/endpoints/mod_skinny/test-skinny.pl        2010-02-24 11:59:41 UTC (rev 16761)
</span><span class="lines">@@ -0,0 +1,104 @@
</span><ins>+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IO::Socket;
+
+$| = 1;
+
+my $socket;
+
+sub skinny_connect
+{
+        $socket = IO::Socket::INET-&gt;new(
+                PeerAddr =&gt; '127.0.0.1',
+                PeerPort =&gt; 2000,
+                );
+}
+
+sub skinny_send
+{
+        my $type = shift;
+        my $data = shift;
+        my $len = length($data)+4;
+        printf &quot;Sending message (length=%d, type=%X)&quot;, $len, $type;
+        $socket-&gt;send(
+                pack(&quot;VVV&quot;, $len, 0, $type).
+                $data);
+        printf &quot;.\n&quot;;
+}
+
+sub skinny_recv
+{
+        my $buf;
+        $socket-&gt;recv($buf, 4);
+        my $len = unpack(&quot;V&quot;, $buf);
+        printf &quot;Receiving message (length=%d,&quot;, $len;
+        if($len &lt; 4) {
+                printf &quot;type=?).\n&quot;;
+                printf &quot;Problem! Length is &lt; 4.\n&quot;;
+                exit 1;
+        }
+        $socket-&gt;recv($buf, 4); #reserved
+        $socket-&gt;recv($buf, 4); #type
+        my $type = unpack(&quot;V&quot;, $buf);
+        printf &quot;type=%X)&quot;, $type;
+        if($len &gt; 4) {
+                $socket-&gt;recv($buf, $len-4);
+        }
+        printf &quot;.\n&quot;;
+}
+
+sub skinny_sleep
+{
+        my $t = shift;
+        
+        printf &quot;Sleeping %d seconds&quot;, $t;
+        while(--$t){
+                sleep(1);
+                printf &quot;.&quot; if $t % 10;
+                printf &quot;_&quot; unless $t % 10;
+        }
+        printf &quot;.\n&quot;;
+}
+
+skinny_connect();
+
+#
+skinny_send(0x0001, # register
+        pack(&quot;a16VVVVV&quot;,
+                &quot;SEP001120AABBCC&quot;,
+                0, # userId;
+                1, # instance;
+                12,# ip;
+                7, # deviceType;
+                0, # maxStreams;
+        ));
+skinny_recv(); # registerack
+
+skinny_send(0x0002, # port
+        pack(&quot;n&quot;, 2000
+        ));
+
+skinny_recv(); # capreq
+skinny_send(0x0010, # capres
+        pack(&quot;V&quot;.&quot;Vva10&quot;.&quot;Vva10&quot;,
+                2, # count
+                2, 8, &quot;&quot;, # codec, frames, res
+                4, 16, &quot;&quot;, # codec, frames, res
+        ));
+
+skinny_send(0x000B, # linestatreq
+        pack(&quot;V&quot;, 1));
+skinny_recv(); # linestatres
+
+skinny_send(0x002D, # registeravlines
+        pack(&quot;V&quot;, 2
+        ));
+
+
+skinny_sleep(3);
+skinny_send(0x0000, # keepalive
+        &quot;&quot;);
+skinny_recv(); # keepaliveack
+
</ins></span></pre>
</div>
</div>
<div id="footer">See you at ClueCon</div>

</body>
</html>