]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
FS-5106 fire an event when a sip client doesn't respond to option-ping
authorFlavio Grossi <flaviogrossi@gmail.com>
Tue, 9 Sep 2014 07:43:47 +0000 (09:43 +0200)
committerFlavio Grossi <flaviogrossi@gmail.com>
Thu, 2 Oct 2014 10:34:47 +0000 (12:34 +0200)
When all-reg-options-ping is enabled, this adds a new custom event to mod_sofia
(sofia::sip_user_state), which is fired when a client stops responding to such
ping packets (or when it is reachable again).

Add two needed new columns to the sip_registrations table:
  - ping_status, which is "Reachable" or "Unreachable" depending on the client
    status;
  - ping_count, which tracks the number of ping responses received and is used
    to provide some kind of hysteresis to avoid firing the event in case of
    transitory network failures.

Then ping_count is checked against two threshold values, sip-user-ping-min
and sip-user-ping-max in a similar fashion as the ping-{max,min} options for
the gateways. These two values are configurable in the profile's xml
configuration file.

Also, if unregister-on-options-fail is enabled, the client is unregistered
based on the number of OPTIONS failure which is also checked against the
sip-user-ping-{min,max} values.

src/mod/endpoints/mod_sofia/mod_sofia.c
src/mod/endpoints/mod_sofia/mod_sofia.h
src/mod/endpoints/mod_sofia/sofia.c
src/mod/endpoints/mod_sofia/sofia_glue.c
src/mod/endpoints/mod_sofia/sofia_reg.c

index aff86a63319af462ba9aa911e1be1b4fd464ae5d..931e6768efae924d6b99bb4ec274afe3af5ab9e9 100644 (file)
@@ -2418,6 +2418,7 @@ static int show_reg_callback(void *pArg, int argc, char **argv, char **columnNam
                                                           "Contact:    \t%s\n"
                                                           "Agent:      \t%s\n"
                                                           "Status:     \t%s(%s) EXP(%s) EXPSECS(%d)\n"
+                                                          "Ping-Status:\t%s\n"
                                                           "Host:       \t%s\n"
                                                           "IP:         \t%s\n"
                                                           "Port:       \t%s\n"
@@ -2425,9 +2426,9 @@ static int show_reg_callback(void *pArg, int argc, char **argv, char **columnNam
                                                           "Auth-Realm: \t%s\n"
                                                           "MWI-Account:\t%s@%s\n\n",
                                                           switch_str_nil(argv[0]), switch_str_nil(argv[1]), switch_str_nil(argv[2]), switch_str_nil(argv[3]),
-                                                          switch_str_nil(argv[7]), switch_str_nil(argv[4]), switch_str_nil(argv[5]), exp_buf, exp_secs, switch_str_nil(argv[11]),
-                                                          switch_str_nil(argv[12]), switch_str_nil(argv[13]), switch_str_nil(argv[14]), switch_str_nil(argv[15]),
-                                                          switch_str_nil(argv[16]), switch_str_nil(argv[17]));
+                                                          switch_str_nil(argv[7]), switch_str_nil(argv[4]), switch_str_nil(argv[5]), exp_buf, exp_secs, switch_str_nil(argv[18]),
+                                                          switch_str_nil(argv[11]), switch_str_nil(argv[12]), switch_str_nil(argv[13]), switch_str_nil(argv[14]),
+                                                          switch_str_nil(argv[15]), switch_str_nil(argv[16]), switch_str_nil(argv[17]));
        return 0;
 }
 
@@ -2459,6 +2460,7 @@ static int show_reg_callback_xml(void *pArg, int argc, char **argv, char **colum
        cb->stream->write_function(cb->stream, "        <agent>%s</agent>\n", switch_amp_encode(switch_str_nil(argv[7]), xmlbuf, buflen));
        cb->stream->write_function(cb->stream, "        <status>%s(%s) exp(%s) expsecs(%d)</status>\n", switch_str_nil(argv[4]), switch_str_nil(argv[5]),
                                                           exp_buf, exp_secs);
+       cb->stream->write_function(cb->stream, "        <ping-status>%s</ping-status>\n", switch_str_nil(argv[18]));
        cb->stream->write_function(cb->stream, "        <host>%s</host>\n", switch_str_nil(argv[11]));
        cb->stream->write_function(cb->stream, "        <network-ip>%s</network-ip>\n", switch_str_nil(argv[12]));
        cb->stream->write_function(cb->stream, "        <network-port>%s</network-port>\n", switch_str_nil(argv[13]));
@@ -2682,19 +2684,19 @@ static switch_status_t cmd_status(char **argv, int argc, switch_stream_handle_t
                                if (!sql && argv[2] && !strcasecmp(argv[2], "pres") && argv[3]) {
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q' and presence_hosts like '%%%q%%'", profile->name, argv[3]);
                                }
                                if (!sql && argv[2] && !strcasecmp(argv[2], "reg") && argv[3]) {
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host, ping_status"
                                                                                 " from sip_registrations where profile_name='%q' and contact like '%%%q%%'", profile->name, argv[3]);
                                }
                                if (!sql && argv[2] && !strcasecmp(argv[2], "reg")) {
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q'", profile->name);
                                }
                                if (!sql && argv[2] && !strcasecmp(argv[2], "user") && argv[3]) {
@@ -2721,7 +2723,7 @@ static switch_status_t cmd_status(char **argv, int argc, switch_stream_handle_t
 
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q' and %s", profile->name, sqlextra);
                                        switch_safe_free(dup);
                                        switch_safe_free(sqlextra);
@@ -2965,21 +2967,21 @@ static switch_status_t cmd_xml_status(char **argv, int argc, switch_stream_handl
 
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q' and presence_hosts like '%%%q%%'", profile->name, argv[3]);
                                }
                                if (!sql && argv[2] && !strcasecmp(argv[2], "reg") && argv[3]) {
 
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q' and contact like '%%%q%%'", profile->name, argv[3]);
                                }
                                if (!sql && argv[2] && !strcasecmp(argv[2], "reg")) {
 
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q'", profile->name);
                                }
                                if (!sql && argv[2] && !strcasecmp(argv[2], "user") && argv[3]) {
@@ -3006,7 +3008,7 @@ static switch_status_t cmd_xml_status(char **argv, int argc, switch_stream_handl
 
                                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,"
                                                                                 "rpid,expires,user_agent,server_user,server_host,profile_name,hostname,"
-                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host"
+                                                                                "network_ip,network_port,sip_username,sip_realm,mwi_user,mwi_host,ping_status"
                                                                                 " from sip_registrations where profile_name='%q' and %s", profile->name, sqlextra);
                                        switch_safe_free(dup);
                                        switch_safe_free(sqlextra);
index ee03f235cea4bc33fdc54e350a5da8ce66de8b08..1030cf01d686c64d7a5ed0240bbc21ae66fe32fe 100644 (file)
@@ -84,6 +84,7 @@ typedef struct private_object private_object_t;
 #define MY_EVENT_UNREGISTER "sofia::unregister"
 #define MY_EVENT_EXPIRE "sofia::expire"
 #define MY_EVENT_GATEWAY_STATE "sofia::gateway_state"
+#define MY_EVENT_SIP_USER_STATE "sofia::sip_user_state"
 #define MY_EVENT_NOTIFY_REFER "sofia::notify_refer"
 #define MY_EVENT_REINVITE "sofia::reinvite"
 #define MY_EVENT_GATEWAY_ADD "sofia::gateway_add"
@@ -426,6 +427,13 @@ typedef enum {
        SOFIA_GATEWAY_INVALID
 } sofia_gateway_status_t;
 
+typedef enum {
+       SOFIA_REG_REACHABLE,
+       SOFIA_REG_UNREACHABLE,
+
+       SOFIA_REG_INVALID
+} sofia_sip_user_status_t;
+
 typedef enum {
        SUB_STATE_UNSUBED,
        SUB_STATE_TRYING,
@@ -603,6 +611,8 @@ struct sofia_profile {
        char *challenge_realm;
        char *pnp_prov_url;
        char *pnp_notify_profile;
+       int sip_user_ping_max;
+       int sip_user_ping_min;
        sofia_cid_type_t cid_type;
        switch_core_media_dtmf_t dtmf_type;
        int auto_restart;
@@ -1124,6 +1134,9 @@ void sofia_profile_destroy(sofia_profile_t *profile);
 switch_status_t sip_dig_function(_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream);
 const char *sofia_gateway_status_name(sofia_gateway_status_t status);
 void sofia_reg_fire_custom_gateway_state_event(sofia_gateway_t *gateway, int status, const char *phrase);
+const char *sofia_sip_user_status_name(sofia_sip_user_status_t status);
+void sofia_reg_fire_custom_sip_user_state_event(sofia_profile_t *profile, const char *sip_user, const char *contact,
+                                                       const char* from_user, const char* from_host, const char *call_id, sofia_sip_user_status_t status, int options_res, const char *phrase);
 uint32_t sofia_reg_reg_count(sofia_profile_t *profile, const char *user, const char *host);
 char *sofia_media_get_multipart(switch_core_session_t *session, const char *prefix, const char *sdp, char **mp_type);
 int sofia_glue_tech_simplify(private_object_t *tech_pvt);
index 6cf16e591228db7d3ff72d0fb57b459e4fb25560..2039be99861cff5d5effe120bcffe303d168475d 100644 (file)
@@ -2477,11 +2477,11 @@ void event_handler(switch_event_t *event)
                sql = switch_mprintf("insert into sip_registrations "
                                                         "(call_id, sip_user, sip_host, presence_hosts, contact, status, rpid, expires,"
                                                         "user_agent, server_user, server_host, profile_name, hostname, network_ip, network_port, sip_username, sip_realm,"
-                                                        "mwi_user, mwi_host, orig_server_host, orig_hostname) "
-                                                        "values ('%q','%q','%q','%q','%q','Registered','%q',%ld, '%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q')",
+                                                        "mwi_user, mwi_host, orig_server_host, orig_hostname, ping_status, ping_count) "
+                                                        "values ('%q','%q','%q','%q','%q','Registered','%q',%ld, '%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q','%q', '%q', %d)",
                                                         call_id, from_user, from_host, presence_hosts, contact_str, rpid, expires, user_agent, to_user, guess_ip4,
                                                         profile_name, mod_sofia_globals.hostname, network_ip, network_port, username, realm, mwi_user, mwi_host,
-                                                        orig_server_host, orig_hostname);
+                                                        orig_server_host, orig_hostname, "Reachable", 0);
 
                if (sql) {
                        sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
@@ -2495,6 +2495,33 @@ void event_handler(switch_event_t *event)
          end:
                switch_safe_free(fixed_contact_str);
                switch_safe_free(dup_mwi_account);
+       } else if ((subclass = switch_event_get_header_nil(event, "orig-event-subclass")) && !strcasecmp(subclass, MY_EVENT_SIP_USER_STATE)) {
+               char *profile_name = switch_event_get_header_nil(event, "orig-profile-name");
+               char *from_user = switch_event_get_header_nil(event, "orig-from-user");
+               char *from_host = switch_event_get_header_nil(event, "orig-from-host");
+               const char *call_id = switch_event_get_header_nil(event, "orig-call-id");
+               char *ping_status = switch_event_get_header_nil(event, "orig-Ping-Status");
+               sofia_profile_t *profile = NULL;
+
+               if (!profile_name || !(profile = sofia_glue_find_profile(profile_name))) {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Profile\n");
+               } else {
+                       if (!strcmp(ping_status, "REACHABLE")) {
+                               sql = switch_mprintf("update sip_registrations set ping_status='%s' where sip_user='%s' and sip_host='%s' and call_id='%q'",
+                                                                       "Reachable", from_user, from_host, call_id);
+                       } else {
+                               sql = switch_mprintf("update sip_registrations set ping_status='%s' where sip_user='%s' and sip_host='%s' and call_id='%q'",
+                                                                       "Unreachable", from_user, from_host, call_id);
+                       }
+                       if (sql) {
+                               sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
+                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Propagating sip_user_state for %s@%s. Ping-Status: %s\n", from_user, from_host, ping_status);
+                       }
+
+                       if (profile) {
+                               sofia_glue_release_profile(profile);
+                       }
+        }
        }
 }
 
@@ -4014,6 +4041,9 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
 
                                        profile->user_agent = switch_core_sprintf(profile->pool, "FreeSWITCH-mod_sofia/%s", switch_version_full());
 
+                                       profile->sip_user_ping_max = 3;
+                                       profile->sip_user_ping_min = 1;
+
                                        profile->name = switch_core_strdup(profile->pool, xprofilename);
                                        switch_snprintf(url, sizeof(url), "sofia_reg_%s", xprofilename);
 
@@ -4772,6 +4802,10 @@ switch_status_t config_sofia(sofia_config_t reload, char *profile_name)
                                                } else {
                                                        sofia_clear_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL);
                                                }
+                                       } else if (!strcasecmp(var, "sip-user-ping-max")) {
+                                               profile->sip_user_ping_max = atoi(val);
+                                       } else if (!strcasecmp(var, "sip-user-ping-min")) {
+                                               profile->sip_user_ping_min = atoi(val);
                                        } else if (!strcasecmp(var, "require-secure-rtp")) {
                                                if (switch_true(val)) {
                                                        sofia_set_pflag(profile, PFLAG_SECURE);
@@ -5545,6 +5579,44 @@ const char *sofia_gateway_status_name(sofia_gateway_status_t status)
        }
 }
 
+const char *sofia_sip_user_status_name(sofia_sip_user_status_t status)
+{
+       static const char *status_names[] = { "UNREACHABLE", "REACHABLE", NULL };
+
+       if (status < SOFIA_REG_INVALID) {
+               return status_names[status];
+       } else {
+               return "INVALID";
+       }
+}
+
+struct cb_helper_sip_user_status {
+       char *status;
+       size_t status_len;
+
+       char *contact;
+       size_t contact_len;
+
+       int count;
+};
+
+int sofia_sip_user_status_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+       struct cb_helper_sip_user_status *cbt = (struct cb_helper_sip_user_status *) pArg;
+
+       if (argc != 3) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "expected 3 arguments from query, instead got %d\n", argc);
+               return 0;
+       }
+
+       switch_copy_string(cbt->status, argv[0], cbt->status_len);
+       cbt->count = (argv[1] && switch_is_number(argv[1])) ? atoi(argv[1]) : 0;
+
+       switch_copy_string(cbt->contact, argv[2], cbt->contact_len);
+
+       return 1;
+}
+
 static void sofia_handle_sip_r_options(switch_core_session_t *session, int status,
                                                                           char const *phrase,
                                                                           nua_t *nua, sofia_profile_t *profile, nua_handle_t *nh, sofia_private_t *sofia_private, sip_t const *sip,
@@ -5608,18 +5680,85 @@ static void sofia_handle_sip_r_options(switch_core_session_t *session, int statu
                gateway->ping = switch_epoch_time_now(NULL) + gateway->ping_freq;
                sofia_reg_release_gateway(gateway);
                gateway->pinging = 0;
-       } else if (sofia_test_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL) && (status != 200 && status != 486) &&
-                          sip && sip->sip_to && sip->sip_call_id && sip->sip_call_id->i_id && strchr(sip->sip_call_id->i_id, '_')) {
-               char *sql;
-               time_t now = switch_epoch_time_now(NULL);
+       } else if (sip && sip->sip_to && sip->sip_call_id && sip->sip_call_id->i_id && strchr(sip->sip_call_id->i_id, '_')) {
                const char *call_id = strchr(sip->sip_call_id->i_id, '_') + 1;
+               char *sql;
+               struct cb_helper_sip_user_status sip_user_status;
+               char ping_status[255] = "";
+               char sip_contact[1024] = "";
+               int sip_user_ping_min = profile->sip_user_ping_min;
+               int sip_user_ping_max = profile->sip_user_ping_max;
+
+               char *sip_user = switch_mprintf("%s@%s", sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
+
+               sip_user_status.status = ping_status;
+               sip_user_status.status_len = sizeof(ping_status);
+               sip_user_status.contact = sip_contact;
+               sip_user_status.contact_len = sizeof(sip_contact);
+               sql = switch_mprintf("select ping_status, ping_count, contact from sip_registrations where sip_user='%s' and sip_host='%s' and call_id='%q'",
+                                    sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
+               sofia_glue_execute_sql_callback(profile, profile->ireg_mutex, sql, sofia_sip_user_status_callback, &sip_user_status);
+               switch_safe_free(sql);
+
+               if (status != 200 && status != 486) {
+                       sip_user_status.count--;
+                       if (sip_user_status.count >= 0) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Ping to sip user '%s@%s' failed with code %d - count %d, state %s\n",
+                                                 sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, status, sip_user_status.count, sip_user_status.status);
+                               sql = switch_mprintf("update sip_registrations set ping_count=%d where sip_user='%s' and sip_host='%s' and call_id='%q'", sip_user_status.count,
+                                                    sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
+                               sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
+                               switch_safe_free(sql);
+                       }
+                       if (sip_user_status.count < sip_user_ping_min) {
+                               if (strcmp(sip_user_status.status, "Unreachable")) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Sip user '%s@%s' is now Unreachable\n",
+                                                         sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
+                                       sql = switch_mprintf("update sip_registrations set ping_status='Unreachable' where sip_user='%s' and sip_host='%s' and call_id='%q'",
+                                                            sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
+                                       sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
+                                       switch_safe_free(sql);
+                                       sofia_reg_fire_custom_sip_user_state_event(profile, sip_user, sip_user_status.contact, sip->sip_to->a_url->url_user,
+                                                                                                                          sip->sip_to->a_url->url_host, call_id, SOFIA_REG_REACHABLE, status, phrase);
+
+                                       if (sofia_test_pflag(profile, PFLAG_UNREG_OPTIONS_FAIL)) {
+                                               time_t now = switch_epoch_time_now(NULL);
+                                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Expire sip user '%s@%s' due to options failure\n",
+                                                                 sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
+
+                                               sql = switch_mprintf("update sip_registrations set expires=%ld where sip_user='%s' and sip_host='%s' and call_id='%q'",
+                                                                    (long) now, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
+                                               sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
+                                               switch_safe_free(sql);
+                                       }
+                               }
+                       }
+               } else {
+                       sip_user_status.count++;
+                       if (sip_user_status.count <= sip_user_ping_max) {
+                               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Ping to sip user '%s@%s' succeeded with code %d - count %d, state %s\n",
+                                                 sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, status, sip_user_status.count, sip_user_status.status);
+                               sql = switch_mprintf("update sip_registrations set ping_count=%d where sip_user='%s' and sip_host='%s' and call_id='%q'", sip_user_status.count,
+                                                    sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
+                               sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
+                               switch_safe_free(sql);
+                       }
+                       if (sip_user_status.count >= sip_user_ping_min) {
+                               if (strcmp(sip_user_status.status, "Reachable")) {
+                                       switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Sip user '%s@%s' is now Reachable\n",
+                                                         sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
+                                       sql = switch_mprintf("update sip_registrations set ping_status='Reachable' where sip_user='%s' and sip_host='%s' and call_id='%q'",
+                                                            sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
+                                       sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
+                                       switch_safe_free(sql);
+                                       sofia_reg_fire_custom_sip_user_state_event(profile, sip_user, sip_user_status.contact, sip->sip_to->a_url->url_user,
+                                                                                                                          sip->sip_to->a_url->url_host, call_id, SOFIA_REG_UNREACHABLE, status, phrase);
+                               }
+                       }
+               }
 
-               switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Expire registration '%s@%s' due to options failure\n",
-                                                 sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host);
+               switch_safe_free(sip_user);
 
-               sql = switch_mprintf("update sip_registrations set expires=%ld where sip_user='%s' and sip_host='%s' and call_id='%q'",
-                                                        (long) now, sip->sip_to->a_url->url_user, sip->sip_to->a_url->url_host, call_id);
-               sofia_glue_execute_sql(profile, &sql, SWITCH_TRUE);
        }
 }
 
index 99caba5c2e002d6b97c508fba1f659c888a5a222..a4e766ad17b610518d764569f0da1f775e91de25 100644 (file)
@@ -1984,6 +1984,8 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
                "   presence_hosts   VARCHAR(255),\n"
                "   contact          VARCHAR(1024),\n"
                "   status           VARCHAR(255),\n"
+               "   ping_status      VARCHAR(255),\n"
+               "   ping_count       INTEGER,\n"
                "   rpid             VARCHAR(255),\n"
                "   expires          BIGINT,\n"
                "   user_agent       VARCHAR(255),\n"
@@ -2116,6 +2118,7 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
                "create index sr_expires on sip_registrations (expires)",
                "create index sr_hostname on sip_registrations (hostname)",
                "create index sr_status on sip_registrations (status)",
+               "create index sr_ping_status on sip_registrations (ping_status)",
                "create index sr_network_ip on sip_registrations (network_ip)",
                "create index sr_network_port on sip_registrations (network_port)",
                "create index sr_sip_username on sip_registrations (sip_username)",
@@ -2194,6 +2197,9 @@ int sofia_glue_init_sql(sofia_profile_t *profile)
 
 
        switch_cache_db_test_reactive(dbh, test_sql, "drop table sip_registrations", reg_sql);
+
+       switch_cache_db_test_reactive(dbh, "select ping_count from sip_registrations", NULL, "alter table sip_registrations add column ping_count INTEGER default 0");
+       switch_cache_db_test_reactive(dbh, "select ping_status from sip_registrations", NULL, "alter table sip_registrations add column ping_status VARCHAR(255) default \"Reachable\"");
        
        test2 = switch_mprintf("%s;%s", test_sql, test_sql);
                        
index 053ddd9a9e2f3454837a2153afa70c41ac8ec7c0..32af4901f3d96f1021c9028841f01666c57f1bdf 100644 (file)
@@ -161,6 +161,26 @@ void sofia_reg_fire_custom_gateway_state_event(sofia_gateway_t *gateway, int sta
        }
 }
 
+void sofia_reg_fire_custom_sip_user_state_event(sofia_profile_t *profile, const char *sip_user, const char *contact,
+                                                       const char* from_user, const char* from_host, const char *call_id, sofia_sip_user_status_t status, int options_res, const char *phrase)
+{
+       switch_event_t *s_event;
+       if (switch_event_create_subclass(&s_event, SWITCH_EVENT_CUSTOM, MY_EVENT_SIP_USER_STATE) == SWITCH_STATUS_SUCCESS) {
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "sip_contact", contact);
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "profile-name", profile->name);
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "sip_user", sip_user);
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-user", from_user);
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "from-host", from_host);
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "call-id", call_id);
+               switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Ping-Status", sofia_sip_user_status_name(status));
+               switch_event_add_header(s_event, SWITCH_STACK_BOTTOM, "Status", "%d", options_res);
+               if (!zstr(phrase)) {
+                       switch_event_add_header_string(s_event, SWITCH_STACK_BOTTOM, "Phrase", phrase);
+               }
+               switch_event_fire(&s_event);
+       }
+}
+
 void sofia_reg_unregister(sofia_profile_t *profile)
 {
        sofia_gateway_t *gateway_ptr;
@@ -830,7 +850,7 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot)
                        sql = switch_mprintf("select call_id,sip_user,sip_host,contact,status,rpid,"
                                                        "expires,user_agent,server_user,server_host,profile_name"
  " from sip_registrations where hostname='%s' and " 
- "profile_name='%s'", mod_sofia_globals.hostname, profile->name); 
+ "profile_name='%s' and orig_hostname='%s'", mod_sofia_globals.hostname, profile->name, mod_sofia_globals.hostname); 
                        
                        sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile);
                        switch_safe_free(sql);
@@ -847,7 +867,7 @@ void sofia_reg_check_expire(sofia_profile_t *profile, time_t now, int reboot)
                                                        "expires,user_agent,server_user,server_host,profile_name"
                                                        " from sip_registrations where (status like '%%NAT%%' "
  "or contact like '%%fs_nat=yes%%') and hostname='%s' " 
- "and profile_name='%s'", mod_sofia_globals.hostname, profile->name); 
+ "and profile_name='%s' and orig_hostname='%s'", mod_sofia_globals.hostname, profile->name, mod_sofia_globals.hostname); 
                        
                        sofia_glue_execute_sql_callback(profile, profile->dbh_mutex, sql, sofia_reg_nat_callback, profile);
                        switch_safe_free(sql);
@@ -1805,12 +1825,12 @@ uint8_t sofia_reg_handle_register_token(nua_t *nua, sofia_profile_t *profile, nu
                        sql = switch_mprintf("insert into sip_registrations "
                                        "(call_id,sip_user,sip_host,presence_hosts,contact,status,rpid,expires,"
                                        "user_agent,server_user,server_host,profile_name,hostname,network_ip,network_port,sip_username,sip_realm,"
-                                       "mwi_user,mwi_host, orig_server_host, orig_hostname, sub_host) "
-                                       "values ('%q','%q', '%q','%q','%q','%q', '%q', %ld, '%q', '%q', '%q', '%q', '%q', '%q', '%q','%q','%q','%q','%q','%q','%q','%q')", 
+                                       "mwi_user,mwi_host, orig_server_host, orig_hostname, sub_host, ping_status, ping_count) "
+                                       "values ('%q','%q', '%q','%q','%q','%q', '%q', %ld, '%q', '%q', '%q', '%q', '%q', '%q', '%q','%q','%q','%q','%q','%q','%q','%q', '%q', %d)", 
                                        call_id, to_user, reg_host, profile->presence_hosts ? profile->presence_hosts : "", 
                                        contact_str, reg_desc, rpid, (long) reg_time + (long) exptime + profile->sip_expires_late_margin,
                                        agent, from_user, guess_ip4, profile->name, mod_sofia_globals.hostname, network_ip, network_port_c, username, realm, 
-                                                                mwi_user, mwi_host, guess_ip4, mod_sofia_globals.hostname, sub_host);
+                                                                mwi_user, mwi_host, guess_ip4, mod_sofia_globals.hostname, sub_host, "Reachable", 0);
                } else {
                        sql = switch_mprintf("update sip_registrations set call_id='%q',"
                                                                 "sub_host='%q', network_ip='%q',network_port='%q',"