res_sip_dtmf_info: Support sending of 'raw' DTMF
authorKevin Harwell <kharwell@digium.com>
Thu, 22 Aug 2013 22:09:16 +0000 (22:09 +0000)
committerKevin Harwell <kharwell@digium.com>
Thu, 22 Aug 2013 22:09:16 +0000 (22:09 +0000)
Added the ability to handle 'raw' DTMF within the body of an INFO message.
Also made it so values 10-16 are mapped to valid DTMF values.

(closes issue ASTERISK-22144)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2776/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@397484 65c4cc65-6c06-0410-ace0-fbb531ad65f3

res/res_pjsip.c
res/res_pjsip_dtmf_info.c

index 645dcb5..9b5727f 100644 (file)
                                        Signifies that a domain is an alias. If the domain on a session is
                                        not found to match an AoR then this object is used to see if we have
                                        an alias for the AoR to which the endpoint is binding. This objects
-                                       name as defined in configuration should be the domain alias and a 
+                                       name as defined in configuration should be the domain alias and a
                                        config option is provided to specify the domain to be aliased.
                                </para></description>
                                <configOption name="type">
                                        This must be used in conjuction with the <literal>PJSIP_DIAL_CONTACTS</literal>.
                                        </para><para>
                                        Registrations: For Asterisk to match an inbound registration to an endpoint,
-                                       the AoR object name must match the user portion of the SIP URI in the "To:" 
+                                       the AoR object name must match the user portion of the SIP URI in the "To:"
                                        header of the inbound SIP registration. That will usually be equivalent
                                        to the "user name" set in your hard or soft phones configuration.
                                </para></description>
                                <description><para>
                                        The settings in this section are global. In addition to being global, the values will
                                        not be re-evaluated when a reload is performed. This is because the values must be set
-                                       before the SIP stack is initialized. The only way to reset these values is to either 
+                                       before the SIP stack is initialized. The only way to reset these values is to either
                                        restart Asterisk, or unload res_pjsip.so and then load it again.
                                </para></description>
                                <configOption name="timert1" default="500">
@@ -1724,7 +1724,7 @@ int ast_sip_is_content_type(pjsip_media_type *content_type, char *type, char *su
 
        pjsip_media_type_init2(&compare, type, subtype);
 
-       return pjsip_media_type_cmp(content_type, &compare, 0) ? -1 : 0;
+       return pjsip_media_type_cmp(content_type, &compare, 0) ? 0 : -1;
 }
 
 pj_caching_pool caching_pool;
index 3cd410d..72f93dc 100644 (file)
 #include "asterisk/res_pjsip_session.h"
 #include "asterisk/module.h"
 
-static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+static int is_media_type(pjsip_rx_data *rdata, char *subtype)
 {
-       int res = 0;
-       pjsip_msg_body *body = rdata->msg_info.msg->body;
+       return !pj_strcmp2(&rdata->msg_info.ctype->media.type, "application") &&
+               !pj_strcmp2(&rdata->msg_info.ctype->media.subtype, subtype);
+}
 
+static void send_response(struct ast_sip_session *session,
+                         struct pjsip_rx_data *rdata, int code)
+{
        pjsip_tx_data *tdata;
+       pjsip_dialog *dlg = session->inv_session->dlg;
+
+       if (pjsip_dlg_create_response(dlg, rdata, code,
+                                     NULL, &tdata) == PJ_SUCCESS) {
+               struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
+               pjsip_dlg_send_response(dlg, tsx, tdata);
+       }
+}
+
+static char get_event(const char *c)
+{
+       unsigned int event;
+
+       if (*c == '!' || *c == '*' || *c == '#' ||
+           ('A' <= *c && *c <= 'D') ||
+           ('a' <= *c && *c <= 'd')) {
+               return *c;
+       }
+
+       if ((sscanf(c, "%30u", &event) != 1) || event > 16) {
+               return '\0';
+       }
+
+       if (event < 10) {
+               return *c;
+       }
+
+       switch (event) {
+       case 10: return '*';
+       case 11: return '#';
+       case 16: return '!';
+       }
+
+       return 'A' + (event - 12);
+}
 
+static int dtmf_info_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+{
+       pjsip_msg_body *body = rdata->msg_info.msg->body;
        char buf[body ? body->len : 0];
        char *cur = buf;
        char *line;
 
        char event = '\0';
-       unsigned int duration = 0;
+       unsigned int duration = 100;
+
+       char is_dtmf = is_media_type(rdata, "dtmf");
+
+       if (!is_dtmf && !is_media_type(rdata, "dtmf-relay")) {
+               return 0;
+       }
 
-       if (!body || !ast_sip_is_content_type(&body->content_type, "application", "dtmf-relay")) {
+       if (!body || !body->len) {
+               /* need to return 200 OK on empty body */
+               send_response(session, rdata, 200);
                return 0;
        }
 
        body->print_body(body, buf, body->len);
 
-       while ((line = strsep(&cur, "\r\n"))) {
-               char *c;
+       if (is_dtmf) {
+               /* directly use what is in the message body */
+               event = get_event(cur);
+       } else { /* content type = application/dtmf-relay */
+               while ((line = strsep(&cur, "\r\n"))) {
+                       char *c;
 
-               if (!(c = strchr(line, '='))) {
-                       continue;
-               }
-               *c++ = '\0';
-
-               c = ast_skip_blanks(c);
-
-               if (!strcasecmp(line, "signal")) {
-                       if (c[0] == '!' || c[0] == '*' || c[0] == '#' ||
-                           ('0' <= c[0] && c[0] <= '9') ||
-                           ('A' <= c[0] && c[0] <= 'D') ||
-                           ('a' <= c[0] && c[0] <= 'd')) {
-                               event = c[0];
-                       } else {
-                               ast_log(LOG_ERROR, "Invalid DTMF event signal in INFO message.\n");
-                               res = -1;
-                               break;
+                       if (!(c = strchr(line, '='))) {
+                               continue;
                        }
-               } else if (!strcasecmp(line, "duration")) {
-                       sscanf(c, "%30u", &duration);
-               }
-       }
 
-       if (!duration) {
-               duration = 100;
+                       *c++ = '\0';
+                       c = ast_skip_blanks(c);
+
+                       if (!strcasecmp(line, "signal")) {
+                               if (!(event = get_event(c))) {
+                                       break;
+                               }
+                       } else if (!strcasecmp(line, "duration")) {
+                               sscanf(c, "%30u", &duration);
+                       }
+               }
        }
 
        if (event == '!') {
                struct ast_frame f = { AST_FRAME_CONTROL, { AST_CONTROL_FLASH, } };
-
                ast_queue_frame(session->channel, &f);
        } else if (event != '\0') {
                struct ast_frame f = { AST_FRAME_DTMF, };
                f.len = duration;
                f.subclass.integer = event;
-
                ast_queue_frame(session->channel, &f);
        } else {
-               res = -1;
-       }
-
-       if (pjsip_dlg_create_response(session->inv_session->dlg, rdata, !res ? 200 : 500, NULL, &tdata) == PJ_SUCCESS) {
-               struct pjsip_transaction *tsx = pjsip_rdata_get_tsx(rdata);
-
-               pjsip_dlg_send_response(session->inv_session->dlg, tsx, tdata);
+               ast_log(LOG_ERROR, "Invalid DTMF event signal in INFO message.\n");
        }
 
-       return res;
+       send_response(session, rdata, event ? 200 : 500);
+       return event ? 0 : -1;
 }
 
 static struct ast_sip_session_supplement dtmf_info_supplement = {