]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Skinny: add support to DeviceToUser and UserToDevice messages
authorMathieu Parent <math.parent@gmail.com>
Thu, 23 Sep 2010 23:55:20 +0000 (01:55 +0200)
committerMathieu Parent <math.parent@gmail.com>
Thu, 23 Sep 2010 23:55:54 +0000 (01:55 +0200)
src/mod/endpoints/mod_skinny/mod_skinny.c
src/mod/endpoints/mod_skinny/mod_skinny.h
src/mod/endpoints/mod_skinny/skinny_api.c
src/mod/endpoints/mod_skinny/skinny_protocol.c
src/mod/endpoints/mod_skinny/skinny_protocol.h
src/mod/endpoints/mod_skinny/skinny_server.c

index c67c20fde46b5479c75c70e08a508ae040a3a5b6..fc7d8721a076599246ae4ba1fb634ea2dcc817a8 100644 (file)
@@ -1911,6 +1911,55 @@ static switch_status_t load_skinny_config(void)
        return SWITCH_STATUS_SUCCESS;
 }
 
+static void skinny_user_to_device_event_handler(switch_event_t *event)
+{
+       char *profile_name = switch_event_get_header_nil(event, "Skinny-Profile-Name");
+       skinny_profile_t *profile;
+
+       if ((profile = skinny_find_profile(profile_name))) {
+               char *device_name = switch_event_get_header_nil(event, "Skinny-Device-Name");
+               uint32_t device_instance = atoi(switch_event_get_header_nil(event, "Skinny-Station-Instance"));
+               listener_t *listener = NULL;
+               skinny_profile_find_listener_by_device_name_and_instance(profile, device_name, device_instance, &listener);
+               if(listener) {
+                       uint32_t message_type = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Message-Id"));
+                       uint32_t application_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Application-Id"));
+                       uint32_t line_instance = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Line-Instance"));
+                       uint32_t call_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Call-Id"));
+                       uint32_t transaction_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Transaction-Id"));
+                       uint32_t data_length = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Data-Length"));
+                       uint32_t sequence_flag = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Sequence-Flag"));
+                       uint32_t display_priority = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Display-Priority"));
+                       uint32_t conference_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Conference-Id"));
+                       uint32_t app_instance_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-App-Instance-Id"));
+                       uint32_t routing_id = atoi(switch_event_get_header_nil(event, "Skinny-UserToDevice-Routing-Id"));
+                       char *data = switch_event_get_body(event);
+                       if (message_type == 0) {
+                               message_type = skinny_str2message_type(switch_event_get_header_nil(event, "Skinny-UserToDevice-Message-Id-String"));
+                       }
+                       switch(message_type) {
+                               case USER_TO_DEVICE_DATA_MESSAGE:
+                               case USER_TO_DEVICE_DATA_VERSION1_MESSAGE:
+                                       data_length = strlen(data); /* we ignore data_length sent */
+                                       send_extended_data(listener, message_type, 
+                                               application_id, line_instance, call_id, transaction_id, data_length,
+                                               sequence_flag, display_priority, conference_id, app_instance_id, routing_id,
+                                               data);
+                                       break;
+                               default:
+                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                                               "Incorrect message type %s (%d).\n", skinny_message_type2str(message_type), message_type);
+                       }
+               } else {
+                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                               "Device %s:%d in profile '%s' not found.\n", device_name, device_instance, profile_name);
+               }
+       } else {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+                       "Profile '%s' not found.\n", profile_name);
+       }
+}
+
 static void skinny_call_state_event_handler(switch_event_t *event)
 {
        char *subclass;
@@ -2130,6 +2179,10 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't bind our trap handler!\n");
                /* Not such severe to prevent loading */
        }
+       if ((switch_event_bind_removable(modname, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_USER_TO_DEVICE, skinny_user_to_device_event_handler, NULL, &globals.user_to_device_node) != SWITCH_STATUS_SUCCESS)) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind our user_to_device handler!\n");
+               /* Not such severe to prevent loading */
+       }
 
        /* reserve events */
        if (switch_event_reserve_subclass(SKINNY_EVENT_REGISTER) != SWITCH_STATUS_SUCCESS) {
@@ -2152,6 +2205,14 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skinny_load)
                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_CALL_STATE);
                return SWITCH_STATUS_TERM;
        }
+       if (switch_event_reserve_subclass(SKINNY_EVENT_USER_TO_DEVICE) != SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_USER_TO_DEVICE);
+               return SWITCH_STATUS_TERM;
+       }
+       if (switch_event_reserve_subclass(SKINNY_EVENT_DEVICE_TO_USER) != SWITCH_STATUS_SUCCESS) {
+               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", SKINNY_EVENT_DEVICE_TO_USER);
+               return SWITCH_STATUS_TERM;
+       }
 
        /* connect my internal structure to the blank pointer passed to me */
        *module_interface = switch_loadable_module_create_module_interface(globals.pool, modname);
@@ -2190,6 +2251,7 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown)
        skinny_api_unregister();
        
        /* release events */
+       switch_event_unbind(&globals.user_to_device_node);
        switch_event_unbind(&globals.call_state_node);
        switch_event_unbind(&globals.message_waiting_node);
        switch_event_unbind(&globals.trap_node);
@@ -2198,6 +2260,8 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skinny_shutdown)
        switch_event_free_subclass(SKINNY_EVENT_EXPIRE);
        switch_event_free_subclass(SKINNY_EVENT_ALARM);
        switch_event_free_subclass(SKINNY_EVENT_CALL_STATE);
+       switch_event_free_subclass(SKINNY_EVENT_USER_TO_DEVICE);
+       switch_event_free_subclass(SKINNY_EVENT_DEVICE_TO_USER);
 
        switch_mutex_lock(mutex);
 
index 7a4df6ff03f0232a37a0955da571b40371bd6f54..b5cc96b7aea5c11a423c742cdd938427edaf5ef9 100644 (file)
 #define SKINNY_EVENT_EXPIRE "skinny::expire"
 #define SKINNY_EVENT_ALARM "skinny::alarm"
 #define SKINNY_EVENT_CALL_STATE "skinny::call_state"
+#define SKINNY_EVENT_USER_TO_DEVICE "skinny::user_to_device"
+#define SKINNY_EVENT_DEVICE_TO_USER "skinny::device_to_user"
 
 struct skinny_globals {
     int running;
     switch_memory_pool_t *pool;
     switch_mutex_t *mutex;
     switch_hash_t *profile_hash;
+    switch_event_node_t *user_to_device_node;
     switch_event_node_t *call_state_node;
     switch_event_node_t *message_waiting_node;
     switch_event_node_t *trap_node;
index 90699d1ad2a5a93430960e601ee0ffd99a991c94..9f527f544c6e81da68862d4a07a2ee087709996e 100644 (file)
@@ -366,6 +366,62 @@ static switch_status_t skinny_api_cmd_profile_device_send_reset_message(const ch
     return SWITCH_STATUS_SUCCESS;
 }
 
+static switch_status_t skinny_api_cmd_profile_device_send_data(const char *profile_name, const char *device_name, const char *message_type, char *params, const char *body, switch_stream_handle_t *stream)
+{
+    skinny_profile_t *profile;
+
+    if ((profile = skinny_find_profile(profile_name))) {
+           listener_t *listener = NULL;
+           skinny_profile_find_listener_by_device_name(profile, device_name, &listener);
+           if(listener) {
+                       switch_event_t *event = NULL;
+                       char *argv[64] = { 0 };
+                       int argc = 0;
+                       int x = 0;
+                       /* skinny::user_to_device event */
+                       skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_USER_TO_DEVICE);
+                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Message-Id-String", "%s", message_type);
+                       argc = switch_separate_string(params, ';', argv, (sizeof(argv) / sizeof(argv[0])));
+                       for (x = 0; x < argc; x++) {
+                               char *var_name, *var_value = NULL;
+                               var_name = argv[x];
+                               if (var_name && (var_value = strchr(var_name, '='))) {
+                                       *var_value++ = '\0';
+                               }
+                               if (zstr(var_name)) {
+                                       stream->write_function(stream, "-ERR No variable specified\n");
+                               } else {
+                                       char *tmp = switch_mprintf("Skinny-UserToDevice-%s", var_name);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, tmp, "%s", var_value);
+                                       switch_safe_free(tmp);
+                                       /*
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Application-Id", "%d", request->data.extended_data.application_id);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Line-Instance", "%d", request->data.extended_data.line_instance);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Call-Id", "%d", request->data.extended_data.call_id);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Transaction-Id", "%d", request->data.extended_data.transaction_id);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Data-Length", "%d", request->data.extended_data.data_length);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Sequence-Flag", "%d", request->data.extended_data.sequence_flag);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Display-Priority", "%d", request->data.extended_data.display_priority);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Conference-Id", "%d", request->data.extended_data.conference_id);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-App-Instance-Id", "%d", request->data.extended_data.app_instance_id);
+                                       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-UserToDevice-Routing-Id", "%d", request->data.extended_data.routing_id);
+                                       */
+                               }
+                       }
+                       switch_event_add_body(event, body);
+                       switch_event_fire(&event);
+                       stream->write_function(stream, "+OK\n");
+           } else {
+                   stream->write_function(stream, "Listener not found!\n");
+           }
+    } else {
+           stream->write_function(stream, "Profile not found!\n");
+    }
+
+    return SWITCH_STATUS_SUCCESS;
+}
+
+
 static switch_status_t skinny_api_cmd_profile_set(const char *profile_name, const char *name, const char *value, switch_stream_handle_t *stream)
 {
     skinny_profile_t *profile;
@@ -403,6 +459,7 @@ SWITCH_STANDARD_API(skinny_function)
            "skinny profile <profile_name> device <device_name> send SetLampMessage <stimulus> <instance> <lamp_mode>\n"
            "skinny profile <profile_name> device <device_name> send SetSpeakerModeMessage <speaker_mode>\n"
            "skinny profile <profile_name> device <device_name> send CallStateMessage <call_state> <line_instance> <call_id>\n"
+           "skinny profile <profile_name> device <device_name> send <UserToDeviceDataMessage|UserToDeviceDataVersion1Message> [ <param>=<value>;... ] <data>\n"
            "skinny profile <profile_name> set <name> <value>\n"
            "--------------------------------------------------------------------------------\n";
     if (session) {
@@ -465,6 +522,16 @@ SWITCH_STANDARD_API(skinny_function)
                                    status = skinny_api_cmd_profile_device_send_reset_message(argv[1], argv[3], argv[6], stream);
                            }
                            break;
+                       case USER_TO_DEVICE_DATA_MESSAGE:
+                       case USER_TO_DEVICE_DATA_VERSION1_MESSAGE:
+                           if(argc == 8) {
+                                       /* <UserToDeviceDataMessage|UserToDeviceDataVersion1Message> [ <param>=<value>;... ] <data> */
+                                       status = skinny_api_cmd_profile_device_send_data(argv[1], argv[3], argv[5], argv[6], argv[7], stream);
+                           } else if(argc == 7) {
+                                       /* <UserToDeviceDataMessage|UserToDeviceDataVersion1Message> <data> */
+                                       status = skinny_api_cmd_profile_device_send_data(argv[1], argv[3], argv[5], "", argv[6], stream);
+                               }
+                           break;
                    default:
                            stream->write_function(stream, "Unhandled message %s\n", argv[5]);
            }
@@ -495,6 +562,8 @@ switch_status_t skinny_api_register(switch_loadable_module_interface_t **module_
     switch_console_set_complete("add skinny profile ::skinny::list_profiles device ::skinny::list_devices send SetLampMessage ::skinny::list_stimuli ::skinny::list_stimulus_instances ::skinny::list_stimulus_modes");
     switch_console_set_complete("add skinny profile ::skinny::list_profiles device ::skinny::list_devices send SetSpeakerModeMessage ::skinny::list_speaker_modes");
     switch_console_set_complete("add skinny profile ::skinny::list_profiles device ::skinny::list_devices send CallStateMessage ::skinny::list_call_states ::skinny::list_line_instances ::skinny::list_call_ids");
+    switch_console_set_complete("add skinny profile ::skinny::list_profiles device ::skinny::list_devices send UserToDeviceDataMessage");
+    switch_console_set_complete("add skinny profile ::skinny::list_profiles device ::skinny::list_devices send UserToDeviceDataVersion1Message");
     switch_console_set_complete("add skinny profile ::skinny::list_profiles set ::skinny::list_settings");
 
     switch_console_add_complete_func("::skinny::list_profiles", skinny_api_list_profiles);
index 465e41b21c92be6fc2ba5931c771fb6e52b13b2d..5486f0833d5773c66837e91836967c75f518b5d6 100644 (file)
@@ -902,6 +902,42 @@ switch_status_t send_reset(listener_t *listener, uint32_t reset_type)
        return skinny_send_reply(listener, message);
 }
 
+switch_status_t send_extended_data(listener_t *listener, uint32_t message_type,
+       uint32_t application_id,
+       uint32_t line_instance,
+       uint32_t call_id,
+       uint32_t transaction_id,
+       uint32_t data_length,
+       uint32_t sequence_flag,
+       uint32_t display_priority,
+       uint32_t conference_id,
+       uint32_t app_instance_id,
+       uint32_t routing_id,
+       const char *data)
+{
+       skinny_message_t *message;
+       switch_assert(data_length == strlen(data));
+       /* data_length should be a multiple of 4 */
+       if ((data_length % 4) != 0) {
+               data_length = (data_length / 4 + 1) * 4;
+       }
+       message = switch_core_alloc(listener->pool, 12+sizeof(message->data.extended_data)+data_length-1);
+       message->type = message_type;
+       message->length = 4 + sizeof(message->data.extended_data)+data_length-1;
+       message->data.extended_data.application_id = application_id;
+       message->data.extended_data.line_instance = line_instance;
+       message->data.extended_data.call_id = call_id;
+       message->data.extended_data.transaction_id = transaction_id;
+       message->data.extended_data.data_length = data_length;
+       message->data.extended_data.sequence_flag = sequence_flag;
+       message->data.extended_data.display_priority = display_priority;
+       message->data.extended_data.conference_id = conference_id;
+       message->data.extended_data.app_instance_id = app_instance_id;
+       message->data.extended_data.routing_id = routing_id;
+       strncpy(message->data.extended_data.data, data, data_length);
+       return skinny_send_reply(listener, message);
+}
+
 switch_status_t skinny_perform_send_reply(listener_t *listener, const char *file, const char *func, int line, skinny_message_t *reply)
 {
        char *ptr;
index 567c1b1003680623f027822a1187b0eed94db27e..b741fa9c8a274ed3c27be862b26d65e525f1eae2 100644 (file)
@@ -176,7 +176,7 @@ struct PACKED register_available_lines_message {
 
 /* DeviceToUserDataMessage */
 #define DEVICE_TO_USER_DATA_MESSAGE 0x002E
-struct PACKED device_to_user_data_message {
+struct PACKED data_message {
        uint32_t application_id;
        uint32_t line_instance;
        uint32_t call_id;
@@ -187,14 +187,7 @@ struct PACKED device_to_user_data_message {
 
 /* DeviceToUserDataResponseMessage */
 #define DEVICE_TO_USER_DATA_RESPONSE_MESSAGE 0x002F
-struct PACKED device_to_user_data_response_message {
-       uint32_t application_id;
-       uint32_t line_instance;
-       uint32_t call_id;
-       uint32_t transaction_id;
-       uint32_t data_length;
-       char data[1];
-};
+/* See struct PACKED data_message */
 
 /* ServiceUrlStatReqMessage */
 #define SERVICE_URL_STAT_REQ_MESSAGE 0x0033
@@ -210,7 +203,7 @@ struct PACKED feature_stat_req_message {
 
 /* DeviceToUserDataVersion1Message */
 #define DEVICE_TO_USER_DATA_VERSION1_MESSAGE 0x0041
-struct PACKED device_to_user_data_version1_message {
+struct PACKED extended_data_message {
        uint32_t application_id;
        uint32_t line_instance;
        uint32_t call_id;
@@ -226,19 +219,7 @@ struct PACKED device_to_user_data_version1_message {
 
 /* DeviceToUserDataResponseVersion1Message */
 #define DEVICE_TO_USER_DATA_RESPONSE_VERSION1_MESSAGE 0x0042
-struct PACKED device_to_user_data_response_version1_message {
-       uint32_t application_id;
-       uint32_t line_instance;
-       uint32_t call_id;
-       uint32_t transaction_id;
-       uint32_t data_length;
-       uint32_t sequence_flag;
-       uint32_t display_priority;
-       uint32_t conference_id;
-       uint32_t app_instance_id;
-       uint32_t routing_id;
-       char data[1];
-};
+/* See struct PACKED extended_data_message */
 
 /* RegisterAckMessage */
 #define REGISTER_ACK_MESSAGE 0x0081
@@ -535,14 +516,7 @@ struct PACKED dialed_number_message {
 
 /* UserToDeviceDataMessage */
 #define USER_TO_DEVICE_DATA_MESSAGE 0x011E
-struct PACKED user_to_device_data_message {
-       uint32_t application_id;
-       uint32_t line_instance;
-       uint32_t call_id;
-       uint32_t transaction_id;
-       uint32_t data_length;
-       char data[1];
-};
+/* See struct PACKED data_message */
 
 /* FeatureStatMessage */
 #define FEATURE_STAT_RES_MESSAGE 0x011F
@@ -571,19 +545,7 @@ struct PACKED service_url_stat_res_message {
 
 /* UserToDeviceDataVersion1Message */
 #define USER_TO_DEVICE_DATA_VERSION1_MESSAGE 0x013F
-struct PACKED user_to_device_data_version1_message {
-       uint32_t application_id;
-       uint32_t line_instance;
-       uint32_t call_id;
-       uint32_t transaction_id;
-       uint32_t data_length;
-       uint32_t sequence_flag;
-       uint32_t display_priority;
-       uint32_t conference_id;
-       uint32_t app_instance_id;
-       uint32_t routing_id;
-       char data[1];
-};
+/* See struct PACKED extended_data_message */
 
 /*****************************************************************************/
 /* SKINNY MESSAGE */
@@ -615,12 +577,12 @@ union skinny_data {
     /* no data for SOFT_KEY_TEMPLATE_REQ_MESSAGE */
     struct headset_status_message headset_status;
     struct register_available_lines_message reg_lines;
-    struct device_to_user_data_message d2u_data;
-    struct device_to_user_data_response_message d2u_data_response;
+    /* see field "data" for DEVICE_TO_USER_DATA_MESSAGE */
+    /* see field "data" for DEVICE_TO_USER_DATA_RESPONSE_MESSAGE */
     struct service_url_stat_req_message service_url_req;
     struct feature_stat_req_message feature_req;
-    struct device_to_user_data_version1_message d2u_data_v1;
-    struct device_to_user_data_response_version1_message d2u_data_response_v1;
+    /* see field "extended_data" for DEVICE_TO_USER_DATA_VERSION1_MESSAGE */
+    /* see field "extended_data" for DEVICE_TO_USER_DATA_RESPONSE_VERSION1_MESSAGE */
     struct register_ack_message reg_ack;
     struct start_tone_message start_tone;
     struct stop_tone_message stop_tone;
@@ -652,11 +614,15 @@ union skinny_data {
     struct unregister_ack_message unregister_ack;
     struct back_space_req_message back_space_req;
     struct dialed_number_message dialed_number;
-       struct user_to_device_data_message u2d_data;
+    /* see field "data" for USER_TO_DEVICE_DATA_MESSAGE */
     struct feature_stat_res_message feature_res;
     struct display_pri_notify_message display_pri_notify;
     struct service_url_stat_res_message service_url_res;
-       struct user_to_device_data_version1_message u2d_data_v1;
+    /* see field "extended_data" for USER_TO_DEVICE_DATA_VERSION1_MESSAGE */
+
+       struct data_message data;
+       struct extended_data_message extended_data;
+
     uint16_t as_uint16;
     char as_char;
     void *raw;
@@ -873,6 +839,19 @@ switch_status_t send_display_pri_notify(listener_t *listener,
 switch_status_t send_reset(listener_t *listener,
     uint32_t reset_type);
 
+switch_status_t send_extended_data(listener_t *listener, uint32_t message_type,
+       uint32_t application_id,
+       uint32_t line_instance,
+       uint32_t call_id,
+       uint32_t transaction_id,
+       uint32_t data_length,
+       uint32_t sequence_flag,
+       uint32_t display_priority,
+       uint32_t conference_id,
+       uint32_t app_instance_id,
+       uint32_t routing_id,
+       const char *data);
+
 #endif /* _SKINNY_PROTOCOL_H */
 
 /* For Emacs:
index 3fa4aab6c4380534293f4fafb9b7c68c964f6f12..945611698be69793e6344a326c7734e6ea81642b 100644 (file)
@@ -1864,6 +1864,30 @@ switch_status_t skinny_handle_register_available_lines_message(listener_t *liste
        return SWITCH_STATUS_SUCCESS;
 }
 
+switch_status_t skinny_handle_data_message(listener_t *listener, skinny_message_t *request)
+{
+       switch_event_t *event = NULL;
+       char *tmp = NULL;
+       skinny_check_data_length(request, sizeof(request->data.data));
+       skinny_check_data_length(request, sizeof(request->data.data) + request->data.data.data_length - 1);
+
+       /* skinny::device_to_user event */
+       skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_DEVICE_TO_USER);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Message-Id", "%d", request->type);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Message-Id-String", "%s", skinny_message_type2str(request->type));
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Application-Id", "%d", request->data.data.application_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Line-Instance", "%d", request->data.data.line_instance);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Call-Id", "%d", request->data.data.call_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Transaction-Id", "%d", request->data.data.transaction_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Data-Length", "%d", request->data.data.data_length);
+       tmp = strndup(request->data.data.data, request->data.data.data_length);
+       switch_event_add_body(event, tmp);
+       switch_safe_free(tmp);
+       switch_event_fire(&event);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
 switch_status_t skinny_handle_service_url_stat_request(listener_t *listener, skinny_message_t *request)
 {
        skinny_message_t *message;
@@ -1904,6 +1928,35 @@ switch_status_t skinny_handle_feature_stat_request(listener_t *listener, skinny_
        return SWITCH_STATUS_SUCCESS;
 }
 
+switch_status_t skinny_handle_extended_data_message(listener_t *listener, skinny_message_t *request)
+{
+       switch_event_t *event = NULL;
+       char *tmp = NULL;
+       skinny_check_data_length(request, sizeof(request->data.extended_data));
+       skinny_check_data_length(request, sizeof(request->data.extended_data)+request->data.extended_data.data_length-1);
+
+       /* skinny::device_to_user event */
+       skinny_device_event(listener, &event, SWITCH_EVENT_CUSTOM, SKINNY_EVENT_DEVICE_TO_USER);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Message-Id", "%d", request->type);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Message-Id-String", "%s", skinny_message_type2str(request->type));
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Application-Id", "%d", request->data.extended_data.application_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Line-Instance", "%d", request->data.extended_data.line_instance);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Call-Id", "%d", request->data.extended_data.call_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Transaction-Id", "%d", request->data.extended_data.transaction_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Data-Length", "%d", request->data.extended_data.data_length);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Sequence-Flag", "%d", request->data.extended_data.sequence_flag);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Display-Priority", "%d", request->data.extended_data.display_priority);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Conference-Id", "%d", request->data.extended_data.conference_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-App-Instance-Id", "%d", request->data.extended_data.app_instance_id);
+       switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Skinny-DeviceToUser-Routing-Id", "%d", request->data.extended_data.routing_id);
+       tmp = strndup(request->data.extended_data.data, request->data.extended_data.data_length);
+       switch_event_add_body(event, tmp);
+       switch_safe_free(tmp);
+       switch_event_fire(&event);
+
+       return SWITCH_STATUS_SUCCESS;
+}
+
 switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *request)
 {
        if (listener->profile->debug >= 10 || request->type != KEEP_ALIVE_MESSAGE) {
@@ -1961,10 +2014,18 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re
                        return skinny_headset_status_message(listener, request);
                case REGISTER_AVAILABLE_LINES_MESSAGE:
                        return skinny_handle_register_available_lines_message(listener, request);
+               case DEVICE_TO_USER_DATA_MESSAGE:
+                       return skinny_handle_data_message(listener, request);
+               case DEVICE_TO_USER_DATA_RESPONSE_MESSAGE:
+                       return skinny_handle_data_message(listener, request);
                case SERVICE_URL_STAT_REQ_MESSAGE:
                        return skinny_handle_service_url_stat_request(listener, request);
                case FEATURE_STAT_REQ_MESSAGE:
                        return skinny_handle_feature_stat_request(listener, request);
+               case DEVICE_TO_USER_DATA_VERSION1_MESSAGE:
+                       return skinny_handle_extended_data_message(listener, request);
+               case DEVICE_TO_USER_DATA_RESPONSE_VERSION1_MESSAGE:
+                       return skinny_handle_extended_data_message(listener, request);
                default:
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
                                "Unhandled request %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length);