<!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][14917] </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=14917">14917</a></dd>
<dt>Author</dt> <dd>brian</dd>
<dt>Date</dt> <dd>2009-09-18 09:37:43 -0500 (Fri, 18 Sep 2009)</dd>
</dl>
<h3>Log Message</h3>
<pre> <a href="http://jira.freeswitch.org/browse/LBDING-15">LBDING-15</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#freeswitchtrunksrcmodendpointsmod_dingalingmod_dingalingc">freeswitch/trunk/src/mod/endpoints/mod_dingaling/mod_dingaling.c</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="freeswitchtrunksrcmodendpointsmod_dingalingmod_dingalingc"></a>
<div class="modfile"><h4>Modified: freeswitch/trunk/src/mod/endpoints/mod_dingaling/mod_dingaling.c (14916 => 14917)</h4>
<pre class="diff"><span>
<span class="info">--- freeswitch/trunk/src/mod/endpoints/mod_dingaling/mod_dingaling.c        2009-09-18 09:02:45 UTC (rev 14916)
+++ freeswitch/trunk/src/mod/endpoints/mod_dingaling/mod_dingaling.c        2009-09-18 14:37:43 UTC (rev 14917)
</span><span class="lines">@@ -128,6 +128,8 @@
</span><span class="cx">         char *odbc_dsn;
</span><span class="cx">         char *odbc_user;
</span><span class="cx">         char *odbc_pass;
</span><ins>+        switch_bool_t purge;
+        switch_thread_rwlock_t *rwlock;
</ins><span class="cx">         switch_odbc_handle_t *master_odbc;
</span><span class="cx">         switch_mutex_t *mutex;
</span><span class="cx">         ldl_handle_t *handle;
</span><span class="lines">@@ -215,6 +217,8 @@
</span><span class="cx"> static switch_status_t load_config(void);
</span><span class="cx"> static int sin_callback(void *pArg, int argc, char **argv, char **columnNames);
</span><span class="cx">
</span><ins>+static switch_status_t soft_reload(void);
+
</ins><span class="cx"> #define is_special(s) (s && (strstr(s, "ext+") || strstr(s, "user+")))
</span><span class="cx">
</span><span class="cx"> static char *translate_rpid(char *in, char *ext)
</span><span class="lines">@@ -1241,6 +1245,15 @@
</span><span class="cx">                 if (switch_core_codec_ready(&tech_pvt->write_codec)) {
</span><span class="cx">                         switch_core_codec_destroy(&tech_pvt->write_codec);
</span><span class="cx">                 }
</span><ins>+
+                switch_thread_rwlock_unlock(tech_pvt->profile->rwlock);
+
+                if(tech_pvt->profile->purge) {
+                        mdl_profile_t *profile = tech_pvt->profile;
+                        if(switch_core_hash_delete(globals.profile_hash, profile->name) == SWITCH_STATUS_SUCCESS) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Profile %s deleted successfully\n", profile->name);
+                        }
+                }
</ins><span class="cx">         }
</span><span class="cx">
</span><span class="cx">         return SWITCH_STATUS_SUCCESS;
</span><span class="lines">@@ -1673,6 +1686,18 @@
</span><span class="cx">                                 }
</span><span class="cx">                         }
</span><span class="cx">
</span><ins>+                        if(mdl_profile->purge) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile '%s' is marked for deletion, disallowing outgoing call\n", mdl_profile->name);
+                                terminate_session(new_session, __LINE__, SWITCH_CAUSE_NORMAL_UNSPECIFIED);
+                                return SWITCH_CAUSE_NORMAL_UNSPECIFIED;
+                        }
+
+                        if(switch_thread_rwlock_tryrdlock(mdl_profile->rwlock) != SWITCH_STATUS_SUCCESS) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't do read lock on profile '%s'\n", mdl_profile->name);
+                                terminate_session(new_session, __LINE__, SWITCH_CAUSE_NORMAL_UNSPECIFIED);
+                                return SWITCH_CAUSE_NORMAL_UNSPECIFIED;
+                        }
+
</ins><span class="cx">                         if (!ldl_handle_ready(mdl_profile->handle)) {
</span><span class="cx">                                 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(*new_session), SWITCH_LOG_DEBUG, "Doh! we are not logged in yet!\n");
</span><span class="cx">                                 terminate_session(new_session, __LINE__, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
</span><span class="lines">@@ -1845,7 +1870,7 @@
</span><span class="cx"> #define LOGOUT_SYNTAX "dl_logout <profile_name>"
</span><span class="cx"> #define LOGIN_SYNTAX "Existing Profile:\ndl_login profile=<profile_name>\nDynamic Profile:\ndl_login var1=val1;var2=val2;varN=valN\n"
</span><span class="cx"> #define DEBUG_SYNTAX "dl_debug [true|false]"
</span><del>-#define DINGALING_SYNTAX "dingaling [status]"
</del><ins>+#define DINGALING_SYNTAX "dingaling [status|reload]"
</ins><span class="cx">
</span><span class="cx">         SWITCH_ADD_API(api_interface, "dl_debug", "DingaLing Debug", dl_debug, DEBUG_SYNTAX);
</span><span class="cx">         SWITCH_ADD_API(api_interface, "dl_pres", "DingaLing Presence", dl_pres, PRES_SYNTAX);
</span><span class="lines">@@ -1905,6 +1930,9 @@
</span><span class="cx">                                                 profile->password,
</span><span class="cx">                                                 profile->server,
</span><span class="cx">                                                 profile->user_flags, profile->message, handle_loop, handle_signalling, handle_response, profile) == LDL_STATUS_SUCCESS) {
</span><ins>+                profile->purge = SWITCH_FALSE;
+                switch_thread_rwlock_create(&profile->rwlock, module_pool);
+
</ins><span class="cx">                 profile->handle = handle;
</span><span class="cx">                 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Started Thread for %s@%s\n", profile->login, profile->dialplan);
</span><span class="cx">                 switch_core_hash_insert(globals.profile_hash, profile->name, profile);
</span><span class="lines">@@ -2161,6 +2189,9 @@
</span><span class="cx">                         }
</span><span class="cx">                         stream->write_function(stream, "\n");
</span><span class="cx">                 }
</span><ins>+        } else if (argv[0] && !strncasecmp(argv[0], "reload", 6)) {
+                soft_reload();
+                stream->write_function(stream, "OK\n");
</ins><span class="cx">         } else {
</span><span class="cx">                 stream->write_function(stream, "USAGE: %s\n", DINGALING_SYNTAX);
</span><span class="cx">         }
</span><span class="lines">@@ -2237,6 +2268,229 @@
</span><span class="cx">         return status;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+
+static switch_bool_t match_profile(mdl_profile_t *profile, mdl_profile_t *new_profile)
+{
+        if(profile == new_profile) {
+                return SWITCH_TRUE;
+        }
+
+        if(
+        ((!new_profile->name && !profile->name) ||
+         (new_profile->name && profile->name && !strcasecmp(new_profile->name, profile->name))) &&
+        ((!new_profile->login && !profile->login) ||
+         (new_profile->login && profile->login && !strcasecmp(new_profile->login, profile->login))) &&
+        ((!new_profile->password && !profile->password) ||
+         (new_profile->password && profile->password && !strcasecmp(new_profile->password, profile->password))) &&
+        ((!new_profile->message && !profile->message) ||
+         (new_profile->message && profile->message && !strcasecmp(new_profile->message, profile->message))) &&
+        ((!new_profile->avatar && !profile->avatar) ||
+         (new_profile->avatar && profile->avatar && !strcasecmp(new_profile->avatar, profile->avatar))) &&
+#ifdef AUTO_REPLY
+        ((!new_profile->auto_reply && !profile->auto_reply) ||
+         (new_profile->auto_reply && profile->auto_reply && !strcasecmp(new_profile->auto_reply, profile->auto_reply))) &&
+#endif
+        ((!new_profile->dialplan && !profile->dialplan) ||
+         (new_profile->dialplan && profile->dialplan && !strcasecmp(new_profile->dialplan, profile->dialplan))) &&
+        ((!new_profile->local_network && !profile->local_network) ||
+         (new_profile->local_network && profile->local_network && !strcasecmp(new_profile->local_network, profile->local_network))) &&
+        ((!new_profile->ip && !profile->ip) ||
+         (new_profile->ip && profile->ip && !strcasecmp(new_profile->ip, profile->ip))) &&
+        ((!new_profile->extip && !profile->extip) ||
+         (new_profile->extip && profile->extip && !strcasecmp(new_profile->extip, profile->extip))) &&
+        ((!new_profile->server && !profile->server) ||
+         (new_profile->server && profile->server && !strcasecmp(new_profile->server, profile->server))) &&
+        ((!new_profile->timer_name && !profile->timer_name) ||
+         (new_profile->timer_name && profile->timer_name && !strcasecmp(new_profile->timer_name, profile->timer_name))) &&
+        ((!new_profile->lanaddr && !profile->lanaddr) ||
+         (new_profile->lanaddr && profile->lanaddr && !strcasecmp(new_profile->lanaddr, profile->lanaddr))) &&
+        ((!new_profile->exten && !profile->exten) ||
+         (new_profile->exten && profile->exten && !strcasecmp(new_profile->exten, profile->exten))) &&
+        ((!new_profile->context && !profile->context) ||
+         (new_profile->context && profile->context && !strcasecmp(new_profile->context, profile->context))) &&
+        (new_profile->user_flags == profile->user_flags) && (new_profile->acl_count == profile->acl_count)
+        ) {
+                if (switch_odbc_available()) {
+                        if(!(
+                        ((!new_profile->odbc_dsn && !profile->odbc_dsn) ||
+                         (new_profile->odbc_dsn && profile->odbc_dsn && !strcasecmp(new_profile->odbc_dsn, profile->odbc_dsn))) &&
+                        ((!new_profile->odbc_user && !profile->odbc_user) ||
+                         (new_profile->odbc_user && profile->odbc_user && !strcasecmp(new_profile->odbc_user, profile->odbc_user))) &&
+                        ((!new_profile->odbc_pass && !profile->odbc_pass) ||
+                         (new_profile->odbc_pass && profile->odbc_pass && !strcasecmp(new_profile->odbc_pass, profile->odbc_pass)))
+                        )) {
+                                return SWITCH_FALSE;
+                        }
+                }
+
+                for(int i=0; i<new_profile->acl_count; i++) {
+                        if(strcasecmp(new_profile->acl[i], profile->acl[i]) != 0) {
+                                return SWITCH_FALSE;
+                        }
+                }
+        }
+
+        return SWITCH_TRUE;
+}
+
+static switch_status_t destroy_profile(char *name)
+{
+        mdl_profile_t *profile = NULL;
+
+        if((profile = switch_core_hash_find(globals.profile_hash, name))) {
+                if (profile->user_flags & LDL_FLAG_COMPONENT) {
+                        if (switch_odbc_available() && profile->odbc_dsn && profile->master_odbc) {
+                                switch_odbc_handle_disconnect(profile->master_odbc);
+                                switch_odbc_handle_destroy(&profile->master_odbc);
+                        }
+
+                        switch_mutex_destroy(profile->mutex);
+                }
+
+                if(switch_thread_rwlock_trywrlock(profile->rwlock) != SWITCH_STATUS_SUCCESS) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Profile %s is busy\n", profile->name);
+                        profile->purge = SWITCH_TRUE;
+                        if(profile->handle) {
+                                ldl_handle_stop(profile->handle);
+                        }
+                } else {
+                        switch_thread_rwlock_unlock(profile->rwlock);
+                        profile->purge = SWITCH_TRUE;
+
+                        if(profile->handle) {
+                                ldl_handle_stop(profile->handle);
+                        }
+
+                        if(switch_core_hash_delete(globals.profile_hash, profile->name) == SWITCH_STATUS_SUCCESS) {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Profile %s deleted successfully\n", profile->name);
+                        }
+                }
+        }
+
+        return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t soft_reload(void)
+{
+        char *cf = "dingaling.conf";
+        mdl_profile_t *profile = NULL, *old_profile = NULL;
+        switch_xml_t cfg, xml, /*settings,*/ param, xmlint;
+
+        void *data = NULL;
+        switch_hash_t *name_hash;
+        switch_core_hash_init(&name_hash, module_pool);
+
+        if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
+                return SWITCH_STATUS_TERM;
+        }
+
+        if (!(xmlint = switch_xml_child(cfg, "profile"))) {
+                if ((xmlint = switch_xml_child(cfg, "interface"))) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "!!!!!!! DEPRICATION WARNING 'interface' is now 'profile' !!!!!!!\n");
+                }
+        }
+
+        for (; xmlint; xmlint = xmlint->next) {
+                char *type = (char *) switch_xml_attr_soft(xmlint, "type");
+                for (param = switch_xml_child(xmlint, "param"); param; param = param->next) {
+                        char *var = (char *) switch_xml_attr_soft(param, "name");
+                        char *val = (char *) switch_xml_attr_soft(param, "value");
+
+ if (!profile) {
+                                profile = switch_core_alloc(module_pool, sizeof(*profile));
+                        }
+
+                        set_profile_val(profile, var, val);
+                }
+
+                if (profile && type && !strcasecmp(type, "component")) {
+                        char dbname[256];
+                        switch_core_db_t *db;
+
+                        if (!profile->login && profile->name) {
+                                profile->login = switch_core_strdup(module_pool, profile->name);
+                        }
+
+                        switch_set_flag(profile, TFLAG_AUTO);
+                        profile->message = "";
+                        profile->user_flags |= LDL_FLAG_COMPONENT;
+                        switch_mutex_init(&profile->mutex, SWITCH_MUTEX_NESTED, module_pool);
+                        switch_snprintf(dbname, sizeof(dbname), "dingaling_%s", profile->name);
+                        profile->dbname = switch_core_strdup(module_pool, dbname);
+
+                        if (switch_odbc_available() && profile->odbc_dsn) {
+                                if (!(profile->master_odbc = switch_odbc_handle_new(profile->odbc_dsn, profile->odbc_user, profile->odbc_pass))) {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open ODBC Database!\n");
+                                        continue;
+
+                                }
+                                if (switch_odbc_handle_connect(profile->master_odbc) != SWITCH_ODBC_SUCCESS) {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open ODBC Database!\n");
+                                        continue;
+                                }
+
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected ODBC DSN: %s\n", profile->odbc_dsn);
+                                switch_odbc_handle_exec(profile->master_odbc, sub_sql, NULL);
+                                //mdl_execute_sql(profile, sub_sql, NULL);
+                        } else {
+                                if ((db = switch_core_db_open_file(profile->dbname))) {
+                                        switch_core_db_test_reactive(db, "select * from jabber_subscriptions", NULL, sub_sql);
+                                } else {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database!\n");
+                                        continue;
+                                }
+                                switch_core_db_close(db);
+                        }
+                }
+
+                if (profile) {
+                        switch_core_hash_insert(name_hash, profile->name, profile->login);
+
+                        if((old_profile = switch_core_hash_find(globals.profile_hash, profile->name))) {
+                                if(match_profile(old_profile, profile)) {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found pre-existing profile %s [%s]\n", profile->name, profile->login);
+                                } else {
+                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Overwriting pre-existing profile %s [%s]\n", profile->name, profile->login);
+                                        destroy_profile(old_profile->name);
+                                        init_profile(profile, switch_test_flag(profile, TFLAG_AUTO) ? 1 : 0);
+                                }
+                        } else {
+                                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found new profile %s [%s]\n", profile->name, profile->login);
+                                init_profile(profile, switch_test_flag(profile, TFLAG_AUTO) ? 1 : 0);
+                        }
+
+                        profile = NULL;
+                }
+        }
+
+        switch_xml_free(xml);
+
+        for (switch_hash_index_t *hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &data);
+                profile = (mdl_profile_t *) data;
+
+                if(switch_core_hash_find(name_hash, profile->name)) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "New profile %s [%s] activated\n", profile->name, profile->login);
+                } else {
+                        switch_core_hash_insert(name_hash, profile->name, profile->name);
+                }
+        }
+
+        for (switch_hash_index_t *hi = switch_hash_first(NULL, name_hash); hi; hi = switch_hash_next(hi)) {
+                switch_hash_this(hi, NULL, NULL, &data);
+
+                if((profile = switch_core_hash_find(globals.profile_hash, (char*)data))) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleting unused profile %s [%s]\n", profile->name, profile->login);
+                        destroy_profile(profile->name);
+                }
+        }
+
+        switch_core_hash_destroy(&name_hash);
+
+        return SWITCH_STATUS_SUCCESS;
+}
+
</ins><span class="cx"> static switch_status_t load_config(void)
</span><span class="cx"> {
</span><span class="cx">         char *cf = "dingaling.conf";
</span><span class="lines">@@ -2672,6 +2926,19 @@
</span><span class="cx">                         status = LDL_STATUS_FALSE;
</span><span class="cx">                         goto done;
</span><span class="cx">                 }
</span><ins>+
+                if(profile->purge) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Profile '%s' is marked for deletion, disallowing incoming call\n", profile->name);
+                        status = LDL_STATUS_FALSE;
+                        goto done;
+                }
+
+                if(switch_thread_rwlock_tryrdlock(profile->rwlock) != SWITCH_STATUS_SUCCESS) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't do read lock on profile '%s'\n", profile->name);
+                        status = LDL_STATUS_FALSE;
+                        goto done;
+                }
+
</ins><span class="cx">                 if ((session = switch_core_session_request(dingaling_endpoint_interface, SWITCH_CALL_DIRECTION_INBOUND, NULL)) != 0) {
</span><span class="cx">                         switch_core_session_add_stream(session, NULL);
</span><span class="cx">
</span></span></pre>
</div>
</div>
<div id="footer">See you at ClueCon</div>
</body>
</html>