SIP: Add DTMF support (receive INFO only)
[lcr.git] / sip.cpp
diff --git a/sip.cpp b/sip.cpp
index 43922fa..f2eb511 100644 (file)
--- a/sip.cpp
+++ b/sip.cpp
@@ -1071,6 +1071,7 @@ int Psip::message_connect(unsigned int epoint_id, int message_id, union paramete
                /* open local RTP peer (if not bridging) */
                if (rtp_connect() < 0) {
                        nua_cancel(p_s_handle, TAG_END());
+                       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", p_s_handle);
                        nua_handle_destroy(p_s_handle);
                        p_s_handle = NULL;
                        sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_OUT);
@@ -1151,6 +1152,7 @@ int Psip::message_release(unsigned int epoint_id, int message_id, union paramete
                add_trace("respond", "value", "%d %s", status, status_text);
                end_trace();
                nua_respond(p_s_handle, status, status_text, TAG_IF(cause_str[0], SIPTAG_REASON_STR(cause_str)), TAG_END());
+               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", p_s_handle);
                nua_handle_destroy(p_s_handle);
                p_s_handle = NULL;
                trigger_work(&p_s_delete);
@@ -1286,6 +1288,7 @@ int Psip::message_setup(unsigned int epoint_id, int message_id, union parameter
        PDEBUG(DEBUG_SIP, "Using SDP for invite: %s\n", sdp_str);
 
        SPRINT(from, "sip:%s@%s", p_callerinfo.id, remote);
+//     SPRINT(from, "\"%s\" <sip:%s@%s>", /*p_callerinfo.id*/ "4946448519988", p_callerinfo.id, remote);
        SPRINT(to, "sip:%s@%s", p_dialinginfo.id, remote);
        if (inst->asserted_id[0]) {
                SPRINT(asserted_id, "sip:%s@%s", inst->asserted_id, remote);
@@ -1692,8 +1695,6 @@ static void i_options(struct sip_inst *inst, int status, char const *phrase, nua
        end_trace();
 
        nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS_MSG(data->e_msg), TAG_END());
-       nua_handle_destroy(nh);
-       inst->register_handle = NULL;
 }
 
 static void i_register(struct sip_inst *inst, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
@@ -1721,6 +1722,7 @@ static void i_register(struct sip_inst *inst, int status, char const *phrase, nu
                add_trace("error", NULL, "forbidden, because we don't accept registration");
                end_trace();
                nua_respond(nh, SIP_403_FORBIDDEN, NUTAG_WITH_THIS_MSG(data->e_msg), TAG_END());
+               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
                nua_handle_destroy(nh);
                inst->register_handle = NULL;
                return;
@@ -1756,6 +1758,7 @@ static void i_register(struct sip_inst *inst, int status, char const *phrase, nu
        end_trace();
 
        nua_respond(nh, status, auth_text, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(data->e_msg), TAG_IF(auth_str[0], SIPTAG_WWW_AUTHENTICATE_STR(auth_str)), TAG_END());
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
        nua_handle_destroy(nh);
        inst->register_handle = NULL;
 }
@@ -1779,9 +1782,10 @@ static void r_register(struct sip_inst *inst, int status, char const *phrase, nu
                        inst->register_state = REGISTER_STATE_REGISTERED;
                }
                /* start option timer */
-               if (inst->options_interval)
+               if (inst->options_interval) {
                        PDEBUG(DEBUG_SIP, "register ok, scheduling option timer with %d seconds\n", inst->options_interval);
                        schedule_timer(&inst->register_option_timer, inst->options_interval, 0);
+               }
                break;
        case 401:
        case 407:
@@ -1798,6 +1802,7 @@ static void r_register(struct sip_inst *inst, int status, char const *phrase, nu
                status_400:
                PDEBUG(DEBUG_SIP, "Register failed, starting register timer\n");
                inst->register_state = REGISTER_STATE_FAILED;
+               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
                nua_handle_destroy(nh);
                inst->register_handle = NULL;
                /* stop option timer */
@@ -1807,6 +1812,26 @@ static void r_register(struct sip_inst *inst, int status, char const *phrase, nu
        }
 }
 
+static void r_options(struct sip_inst *inst, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
+{
+       PDEBUG(DEBUG_SIP, "options result %d received\n", status);
+
+//     if (status >= 200 && status <= 299) {
+               PDEBUG(DEBUG_SIP, "options ok, scheduling option timer with %d seconds\n", inst->options_interval);
+               /* restart option timer */
+               schedule_timer(&inst->register_option_timer, inst->options_interval, 0);
+               return;
+///    }
+
+       PDEBUG(DEBUG_SIP, "Register options failed, starting register timer\n");
+       inst->register_state = REGISTER_STATE_FAILED;
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
+       nua_handle_destroy(nh);
+       inst->register_handle = NULL;
+       /* if failed, start register interval timer with REGISTER_RETRY_TIMER */
+       schedule_timer(&inst->register_retry_timer, REGISTER_RETRY_TIMER);
+}
+
 void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
 {
        struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
@@ -1907,6 +1932,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                        nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END());
                else
                        nua_respond(nh, SIP_415_UNSUPPORTED_MEDIA, TAG_END());
+               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
                nua_handle_destroy(nh);
                p_s_handle = NULL;
                sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
@@ -1958,6 +1984,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        /* open local RTP peer (if not bridging) */
        if (!p_s_rtp_bridge && rtp_open() < 0) {
                nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
                nua_handle_destroy(nh);
                p_s_handle = NULL;
                sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
@@ -2078,6 +2105,7 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
                if (rtp_connect() < 0) {
 rtp_failed:
                        nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+                       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
                        nua_handle_destroy(nh);
                        p_s_handle = NULL;
                        sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
@@ -2125,6 +2153,69 @@ void Psip::i_options(int status, char const *phrase, nua_t *nua, nua_magic_t *ma
        nua_respond(nh, SIP_200_OK, TAG_END());
 }
 
+// code stolen from freeswitch....
+static char RFC2833_CHARS[] = "0123456789*#ABCDF";
+
+static char switch_rfc2833_to_char(int event)
+{
+        if (event > -1 && event < (int32_t) sizeof(RFC2833_CHARS)) {
+                return RFC2833_CHARS[event];
+        }
+        return '\0';
+}
+
+void Psip::i_info(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
+{
+       struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
+       char digit = '\0';
+
+       PDEBUG(DEBUG_SIP, "options received\n");
+
+       // code stolen from freeswitch....
+
+       if (sip && sip->sip_content_type && sip->sip_content_type->c_type && sip->sip_content_type->c_subtype && sip->sip_payload && sip->sip_payload->pl_data) {
+               if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf-relay")) {
+                       const char *signal_ptr;
+                       if ((signal_ptr = strstr(sip->sip_payload->pl_data, "Signal="))) {
+                               int tmp;
+                               /* move signal_ptr where we need it (right past Signal=) */
+                               signal_ptr = signal_ptr + 7;
+
+                               /* handle broken devices with spaces after the = (cough) VegaStream (cough) */
+                               while (*signal_ptr && *signal_ptr == ' ')
+                                       signal_ptr++;
+
+                               if (*signal_ptr
+                                       && (*signal_ptr == '*' || *signal_ptr == '#' || *signal_ptr == 'A' || *signal_ptr == 'B' || *signal_ptr == 'C'
+                                               || *signal_ptr == 'D')) {
+                                       digit = *signal_ptr;
+                               } else {
+                                       tmp = atoi(signal_ptr);
+                                       digit = switch_rfc2833_to_char(tmp);
+                               }
+                       }
+
+               } else if (!strncasecmp(sip->sip_content_type->c_type, "application", 11) && !strcasecmp(sip->sip_content_type->c_subtype, "dtmf")) {
+                       int tmp = atoi(sip->sip_payload->pl_data);
+                       digit = switch_rfc2833_to_char(tmp);
+               }
+       }
+
+       sip_trace_header(this, inst->interface_name, "INFO", DIRECTION_IN);
+       if (digit) {
+               char digitstr[2] = { digit, '\0' };
+               struct lcr_msg *message;
+               add_trace("dtmf", "digit", digitstr);
+               message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_DTMF);
+               message->param.dtmf = digit;
+               PDEBUG(DEBUG_SIP, "Psip(%s) INFO WITH DTMF digit '%c'\n", p_name, message->param.dtmf);
+               message_put(message);
+       }
+       end_trace();
+
+       nua_respond(nh, SIP_200_OK, TAG_END());
+}
+
 void Psip::i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[])
 {
        struct sip_inst *inst = (struct sip_inst *) p_s_sip_inst;
@@ -2145,6 +2236,7 @@ void Psip::i_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic,
        sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT);
        add_trace("respond", "value", "200 OK");
        end_trace();
+       PDEBUG(DEBUG_SIP, "just remove nua_handle without destruction %x\n", nh);
 //     nua_handle_destroy(nh);
        p_s_handle = NULL;
 
@@ -2173,6 +2265,7 @@ void Psip::i_cancel(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
        sip_trace_header(this, inst->interface_name, "CANCEL", DIRECTION_IN);
        end_trace();
 
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
        nua_handle_destroy(nh);
        p_s_handle = NULL;
 
@@ -2195,6 +2288,7 @@ void Psip::r_bye(int status, char const *phrase, nua_t *nua, nua_magic_t *magic,
 {
        PDEBUG(DEBUG_SIP, "bye response received\n");
 
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
        nua_handle_destroy(nh);
        p_s_handle = NULL;
 
@@ -2207,6 +2301,7 @@ void Psip::r_cancel(int status, char const *phrase, nua_t *nua, nua_magic_t *mag
 {
        PDEBUG(DEBUG_SIP, "cancel response received\n");
 
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
        nua_handle_destroy(nh);
        p_s_handle = NULL;
 
@@ -2373,6 +2468,7 @@ void Psip::r_options(int status, char const *phrase, nua_t *nua, nua_magic_t *ma
                return;
        }
 
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
        nua_handle_destroy(nh);
        p_s_handle = NULL;
 
@@ -2433,16 +2529,36 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_
        /* new handle */
        switch (event) {
        case nua_i_options:
-               if (!inst->register_handle) {
-                       PDEBUG(DEBUG_SIP, "New options instance\n");
-                       inst->register_handle = nh;
+               if (!psip) {
+                       i_options(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
+                       if (inst->register_handle != nh) {
+                               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
+                               nua_handle_destroy(nh);
+                       }
+                       return;
+               }
+               break;
+       case nua_r_options:
+               if (!psip) {
+                       r_options(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
+                       return;
                }
                break;
        case nua_i_register:
-               if (!inst->register_handle) {
+               if (!psip && !inst->register_handle) {
                        PDEBUG(DEBUG_SIP, "New register instance\n");
                        inst->register_handle = nh;
                }
+               if (!psip) {
+                       i_register(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
+                       return;
+               }
+               break;
+       case nua_r_register:
+               if (!psip) {
+                       r_register(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
+                       return;
+               }
                break;
        case nua_i_invite:
                if (!psip) {
@@ -2464,37 +2580,21 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_
                                FATAL("Cannot create Port instance.\n");
                }
                break;
+       case nua_i_outbound:
+               PDEBUG(DEBUG_SIP, "Outbound status\n");
+               break;
        default:
-               if (!psip && !inst->register_handle) {
-                       PDEBUG(DEBUG_SIP, "Destroying unknown instance\n");
-                       nua_handle_destroy(nh);
-                       return;
-               }
-       }
-
-       /* handle register process */
-       if (inst->register_handle == nh) {
-               switch (event) {
-               case nua_i_options:
-                       i_options(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
-                       break;
-               case nua_i_register:
-                       i_register(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
-                       break;
-               case nua_r_register:
-                       r_register(inst, status, phrase, nua, magic, nh, hmagic, sip, tags);
-                       break;
-               default:
-                       PDEBUG(DEBUG_SIP, "Event %d not handled\n", event);
-               }
-               return;
+               ;
        }
 
        /* handle port process */
        if (!psip) {
-               PERROR("no SIP Port found for handel %p\n", nh);
-               nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
-               nua_handle_destroy(nh);
+               if (nh != inst->register_handle) {
+                       PERROR("no SIP Port found for handel %p\n", nh);
+                       nua_respond(nh, SIP_500_INTERNAL_SERVER_ERROR, TAG_END());
+                       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", nh);
+                       nua_handle_destroy(nh);
+               }
                return;
        }
 
@@ -2523,6 +2623,9 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_
        case nua_i_options:
                psip->i_options(status, phrase, nua, magic, nh, hmagic, sip, tags);
                break;
+       case nua_i_info:
+               psip->i_info(status, phrase, nua, magic, nh, hmagic, sip, tags);
+               break;
        case nua_i_bye:
                psip->i_bye(status, phrase, nua, magic, nh, hmagic, sip, tags);
                break;
@@ -2592,6 +2695,7 @@ void Psip::rtp_shutdown(void)
        sip_trace_header(this, inst->interface_name, "RTP terminated", DIRECTION_IN);
        end_trace();
 
+       PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", p_s_handle);
        nua_handle_destroy(p_s_handle);
        p_s_handle = NULL;
 
@@ -2613,6 +2717,8 @@ static int invite_option_timer(struct lcr_timer *timer, void *instance, int inde
        class Psip *psip = (class Psip *)instance;
        struct sip_inst *inst = (struct sip_inst *) psip->p_s_sip_inst;
 
+       PDEBUG(DEBUG_SIP, "invite options timer fired\n");
+
        sip_trace_header(psip, inst->interface_name, "OPTIONS", psip->p_s_invite_direction);
        end_trace();
 
@@ -2642,6 +2748,7 @@ static int register_retry_timer(struct lcr_timer *timer, void *instance, int ind
        if (inst->register_handle) {
                /* stop option timer */
                unsched_timer(&inst->register_option_timer);
+               PDEBUG(DEBUG_SIP, "nua_handle_destroy %x\n", inst->register_handle);
                nua_handle_destroy(inst->register_handle);
                inst->register_handle = NULL;
        }
@@ -2653,6 +2760,9 @@ static int register_retry_timer(struct lcr_timer *timer, void *instance, int ind
 static int register_option_timer(struct lcr_timer *timer, void *instance, int index)
 {
        struct sip_inst *inst = (struct sip_inst *)instance;
+
+       PDEBUG(DEBUG_SIP, "register options timer fired\n");
+
        sip_trace_header(NULL, inst->interface_name, "OPTIONS", DIRECTION_OUT);
        end_trace();
 
@@ -2861,6 +2971,7 @@ static void sip_handle_register(struct sip_inst *inst)
        char from[128] = "";
        char to[128] = "";
        char contact[128] = "";
+       char expires[128] = "";
 
        switch (inst->register_state) {
        case REGISTER_STATE_UNREGISTERED:
@@ -2890,15 +3001,22 @@ static void sip_handle_register(struct sip_inst *inst)
                                SCAT(contact, p);
                }
 
+               if (inst->register_interval) {
+                       SPRINT(expires, "%d", inst->register_interval + 60);
+               }
+
                sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_OUT);
                add_trace("from", "uri", "%s", from);
                add_trace("to", "uri", "%s", to);
+               if (expires[0])
+                       add_trace("expires", NULL, "%s", expires);
                end_trace();
 
                nua_register(inst->register_handle,
                        TAG_IF(from[0], SIPTAG_FROM_STR(from)),
                        TAG_IF(to[0], SIPTAG_TO_STR(to)),
                        TAG_IF(contact[0], SIPTAG_CONTACT_STR(contact)),
+                       TAG_IF(expires[0], SIPTAG_EXPIRES_STR(expires)),
                        TAG_END());
 
                inst->register_state = REGISTER_STATE_REGISTERING;