/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "cpr_types.h" #include "cpr_stdio.h" #include "ccsip_core.h" #include "ccsip_callinfo.h" #include "ccsip_messaging.h" #include "uiapi.h" #include "lsm.h" #include "fsm.h" #include "vcm.h" #include "phone_debug.h" #include "singly_link_list.h" #include "ccapi.h" extern int httpish_strncasecmp(const char *s1, const char *s2, size_t len); typedef unsigned int info_index_t; typedef unsigned int type_index_t; typedef struct { info_package_handler_t handler; info_index_t info_index; // the index of the info_package in g_registered_info array // i.e., the value returned by find_info_index() type_index_t type_index; // the index of the content_type in g_registered_type array // i.e., the value returned by find_type_index() } handler_record_t; /* Info Package handler registry */ static sll_handle_t s_handler_registry = NULL; #define INDEX_NOT_FOUND ((unsigned int)-1) /* * g_registered_info[] contains the Info Package strings (such as * "conference") for the registered handlers. */ char *g_registered_info[MAX_INFO_HANDLER]; static char *s_registered_type[MAX_INFO_HANDLER]; static sll_match_e is_matching_type(void *find_by_p, void *data_p); static info_index_t find_info_index(const char *info_package); static info_index_t find_next_available_info_index(void); static type_index_t find_type_index(const char *type_package); static type_index_t find_next_available_type_index(void); static handler_record_t *find_handler_record(info_index_t info_index, type_index_t type_index); static boolean is_info_package_registered(info_index_t info_index); static boolean is_content_type_registered(type_index_t type_index); static void update_recv_info_list(const char *header_field_value, string_t *info_packages); static void media_control_info_package_handler(line_t line, callid_t call_id, const char *info_package, const char *content_type, const char *message_body); #ifdef _CONF_ROSTER_ static void conf_info_package_handler(line_t line, callid_t call_id, const char *info_package, const char *content_type, const char *message_body); #endif /* * Function: find_info_index * * Parameters: * info_package - the Info Package to find * * Description: * Finds the Info Package in g_registered_info array. * * Return: * info_index_t - the index of the Info Package in the array * INDEX_NOT_FOUND - the Info Package was not found in the array */ static info_index_t find_info_index(const char *info_package) { info_index_t info_index; for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) { if (g_registered_info[info_index] && httpish_strncasecmp(info_package, g_registered_info[info_index], strlen(g_registered_info[info_index])) == 0) { return info_index; } } return INDEX_NOT_FOUND; } /* * Function: find_next_available_info_index * * Parameters: * None * * Description: * Finds an empty slot in g_registered_info array. * * Return: * info_index_t - the index of the empty slot in the array * INDEX_NOT_FOUND - the array is full */ static info_index_t find_next_available_info_index(void) { info_index_t info_index; for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) { if (g_registered_info[info_index] == NULL) { return info_index; } } return INDEX_NOT_FOUND; } /* * Function: find_type_index * * Parameters: * content_type - the Content Type to find * * Description: * Finds the Content Type in s_registered_type array. * * Return: * type_index_t - the index of the Content Type in the array * INDEX_NOT_FOUND - the Content Type was not found in the array */ static type_index_t find_type_index(const char *content_type) { type_index_t type_index; for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) { if (s_registered_type[type_index] && httpish_strncasecmp(content_type, s_registered_type[type_index], strlen(s_registered_type[type_index])) == 0) { return type_index; } } return INDEX_NOT_FOUND; } /* * Function: find_next_available_type_index * * Parameters: * None * * Description: * Finds an empty slot in s_registered_type array. * * Return: * type_index_t - the index of the empty slot in the array * INDEX_NOT_FOUND - the array is full */ static type_index_t find_next_available_type_index(void) { type_index_t type_index; for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) { if (s_registered_type[type_index] == NULL) { return type_index; } } return INDEX_NOT_FOUND; } /* * Function: is_matching_type * * Parameters: * find_by_p - searching criteria * data_p - a node * * Description: * Checks to see if the node matches the searching criteria. * * Return: * SLL_MATCH_FOUND - the node is a match * SLL_MATCH_NOT_FOUND - the node is not a match */ static sll_match_e is_matching_type(void *find_by_p, void *data_p) { handler_record_t *tuple = (handler_record_t *)find_by_p; handler_record_t *node = (handler_record_t *)data_p; if ((node->info_index == tuple->info_index) && (node->type_index == tuple->type_index)) { return SLL_MATCH_FOUND; } return SLL_MATCH_NOT_FOUND; } /* * Function: find_handler_record * * Parameters: * info_index - the index of the Info Package in g_registered_info array * type_index - the index of the Content Type in s_registered_type array * * Description: * Finds the Info Package handler registered for the Info Package/Content * Type pair. * * Return: * handler_record_t * - the registered handler record * NULL - otherwise */ static handler_record_t * find_handler_record(info_index_t info_index, type_index_t type_index) { handler_record_t tuple; tuple.info_index = info_index; tuple.type_index = type_index; return (handler_record_t *)sll_find(s_handler_registry, &tuple); } /* * Function: is_info_package_registered * * Parameters: * info_index - the index of the Info Package in g_registered_info array * * Description: * Checks to see if a handler was registered for the Info Package. * * Return: * TRUE - a handler was registered for the Info Package * FALSE - otherwise */ static boolean is_info_package_registered(info_index_t info_index) { handler_record_t *record; for (record = (handler_record_t *)sll_next(s_handler_registry, NULL); record != NULL; record = (handler_record_t *)sll_next(s_handler_registry, record)) { if (record->info_index == info_index) { return TRUE; } } return FALSE; } /* * Function: is_content_type_registered * * Parameters: * type_index - the index of the Content Type in s_registered_type array * * Description: * Checks to see if a handler was registered for the Content Type. * * Return: * TRUE - a handler was registered for the Content Type * FALSE - otherwise */ static boolean is_content_type_registered(type_index_t type_index) { handler_record_t *record; for (record = (handler_record_t *)sll_next(s_handler_registry, NULL); record != NULL; record = (handler_record_t *)sll_next(s_handler_registry, record)) { if (record->type_index == type_index) { return TRUE; } } return FALSE; } /* * Function: ccsip_register_info_package_handler * * Parameters: * info_package - the Info Package for the handler * content_type - the Content Type for the handler * handler - the handler * * Description: * Registers the handler for the Info Package/Content Type pair. * * Return: * SIP_OK - the handler was registered successfully * SIP_ERROR - otherwise */ int ccsip_register_info_package_handler(const char *info_package, const char *content_type, info_package_handler_t handler) { static const char *fname = "ccsip_register_info_package_handler"; info_index_t info_index; type_index_t type_index; char *tmp_info = NULL; char *tmp_type = NULL; handler_record_t *record; if (s_handler_registry == NULL) { CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname); return SIP_ERROR; } if ((info_package == NULL) || (content_type == NULL) || (handler == NULL)) { CCSIP_DEBUG_ERROR("%s: invalid parameter\n", fname); return SIP_ERROR; } /* Find the info_index for the info_package */ info_index = find_info_index(info_package); if (info_index == INDEX_NOT_FOUND) { /* Find the first available slot */ info_index = find_next_available_info_index(); if (info_index == INDEX_NOT_FOUND) { CCSIP_DEBUG_ERROR("%s: maximum reached\n", fname); return SIP_ERROR; } tmp_info = cpr_strdup(info_package); if (tmp_info == NULL) { CCSIP_DEBUG_ERROR("%s: failed to duplicate info_package string\n", fname); return SIP_ERROR; } } /* Find the type_index for the content_type */ type_index = find_type_index(content_type); if (type_index == INDEX_NOT_FOUND) { /* Find the first available slot */ type_index = find_next_available_type_index(); if (type_index == INDEX_NOT_FOUND) { CCSIP_DEBUG_ERROR("%s: maximum reached\n", fname); if (tmp_info != NULL) { cpr_free(tmp_info); } return SIP_ERROR; } tmp_type = cpr_strdup(content_type); if (tmp_type == NULL) { CCSIP_DEBUG_ERROR("%s: failed to duplicate info_package string\n", fname); if (tmp_info != NULL) { cpr_free(tmp_info); } return SIP_ERROR; } } /* Check to see if the info/type tuple has been registered before */ if (find_handler_record(info_index, type_index) != NULL) { CCSIP_DEBUG_ERROR("%s: Info Package handler already registered\n", fname); return SIP_ERROR; } /* * At this point, info_index points to the slot in g_registered_info where * either: * * 1) the info_package is residing, or * 2) the info_package (a copy of which is pointed to by *tmp_info) will be * copied to before the function returns * * type_index is similar. */ record = (handler_record_t *)cpr_malloc(sizeof(handler_record_t)); if (record == NULL) { if (tmp_type != NULL) { cpr_free(tmp_type); } if (tmp_info != NULL) { cpr_free(tmp_info); } CCSIP_DEBUG_ERROR("%s: failed to allocate info handler record\n", fname); return SIP_ERROR; } record->handler = handler; record->info_index = info_index; record->type_index = type_index; if (sll_append(s_handler_registry, record) != SLL_RET_SUCCESS) { cpr_free(record); if (tmp_type != NULL) { cpr_free(tmp_type); } if (tmp_info != NULL) { cpr_free(tmp_info); } CCSIP_DEBUG_ERROR("%s: failed to insert to the registry\n", fname); return SIP_ERROR; } if (tmp_info != NULL) { g_registered_info[info_index] = tmp_info; } if (tmp_type != NULL) { s_registered_type[type_index] = tmp_type; } return SIP_OK; } /* * Function: ccsip_deregister_info_package_handler * * Parameters: * info_package - the Info Package for the handler * content_type - the Content Type for the handler * handler - the handler * * Description: * Deregisters the handler for the Info Package/Content Type pair. * * Return: * SIP_OK - the handler was registered successfully * SIP_ERROR - otherwise */ int ccsip_deregister_info_package_handler(const char *info_package, const char *content_type, info_package_handler_t handler) { static const char *fname = "ccsip_deregister_info_package_handler"; info_index_t info_index; type_index_t type_index; handler_record_t *record; if (s_handler_registry == NULL) { CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname); return SIP_ERROR; } /* Find the info_index for the info_package */ info_index = find_info_index(info_package); if (info_index == INDEX_NOT_FOUND) { CCSIP_DEBUG_ERROR("%s: handler was not registered (%s)\n", fname, info_package); return SIP_ERROR; } /* Find the type_index for the content_type */ type_index = find_type_index(content_type); if (type_index == INDEX_NOT_FOUND) { CCSIP_DEBUG_ERROR("%s: handler was not registered (%s)\n", fname, content_type); return SIP_ERROR; } /* Find the handler record */ record = find_handler_record(info_index, type_index); if ((record == NULL) || (record->handler != handler)) { CCSIP_DEBUG_ERROR("%s: handler was not registered (%p)\n", fname, handler); return SIP_ERROR; } (void)sll_remove(s_handler_registry, record); cpr_free(record); if (!is_info_package_registered(info_index)) { /* The info_package was not found in the registry, meaning we're * the last one who registered for this particular info_package */ cpr_free(g_registered_info[info_index]); g_registered_info[info_index] = NULL; } if (!is_content_type_registered(type_index)) { /* The content_type was not found in the registry, meaning we're * the last one who registered for this particular content_type */ cpr_free(s_registered_type[type_index]); s_registered_type[type_index] = NULL; } return SIP_OK; } /* * Function: update_recv_info_list * * Parameters: * header_field_value - the header field value to match (e.g., * "conference") * info_packages - the Info Packages string to append the header * field value to * * Description: * Checks to see if a handler is registered for the header field value * (e.g., "conference"), if so, append the header field value to * the end of info_packages. * * Returns: * None */ static void update_recv_info_list(const char *header_field_value, string_t *info_packages) { static const char *fname = "update_recv_info_list"; info_index_t info_index; if ((header_field_value == NULL) || (info_packages == NULL) || (*info_packages == NULL)) { CCSIP_DEBUG_ERROR("%s: invalid parameter\n", fname); return; } info_index = find_info_index(header_field_value); if (info_index != INDEX_NOT_FOUND) { /* Info-Package is supported */ if (**info_packages == '\0') { *info_packages = strlib_update(*info_packages, g_registered_info[info_index]); } else { *info_packages = strlib_append(*info_packages, ", "); *info_packages = strlib_append(*info_packages, g_registered_info[info_index]); } } } /* * Function: ccsip_parse_send_info_header * * Parameters: * ccb - the SIP CCB * pSipMessage - the SIP message * * Description: * Checks the Send-Info header (if exists) to see if a handler was * registered for the Info Package. If so, append the Info Package * to the end of recv_info_list. * * Returns: * None */ void ccsip_parse_send_info_header(sipMessage_t *pSipMessage, string_t *recv_info_list) { char *send_info[MAX_INFO_HANDLER]; int count; int i; char *header_field_values; char *header_field_value; char *separator; // leading white spaces are trimmed, but not trailing ones count = sippmh_get_num_particular_headers(pSipMessage, SIP_HEADER_SEND_INFO, NULL, send_info, MAX_INFO_HANDLER); if (count == 0) { return; } for (i = 0; (i < count) && (i < MAX_INFO_HANDLER); i++) { header_field_values = cpr_strdup(send_info[i]); if (header_field_values == NULL) { return; } header_field_value = header_field_values; while ((separator = strchr(header_field_value, COMMA)) != NULL) { *separator++ = '\0'; update_recv_info_list(header_field_value, recv_info_list); header_field_value = separator; SKIP_WHITE_SPACE(header_field_value); } update_recv_info_list(header_field_value, recv_info_list); cpr_free(header_field_values); } } /* * Function: ccsip_handle_info_package * * Parameters: * ccb - the SIP CCB * pSipMessage - the SIP message * * Description: * Handles an incoming unsolicited Info Package message. * XXX Currently this function only handles the first part in a * multi-part Info Package message. * * Return: * SIP_OK - if request processed. * SIP_ERROR - if there is error */ int ccsip_handle_info_package(ccsipCCB_t *ccb, sipMessage_t *pSipMessage) { static const char *fname = "ccsip_handle_info_package"; const char *info_package; const char *content_type; info_index_t info_index; type_index_t type_index; handler_record_t *record; uint16_t status_code; const char *reason_phrase; int return_code = SIP_ERROR; /* FIXME Media Control currently does not follow the IETF draft draft-ietf-sip-info-events-01, so short-circuit here and bypass all the Info Package related stuff below. */ // leading white spaces are trimmed, but not trailing ones content_type = sippmh_get_cached_header_val(pSipMessage, CONTENT_TYPE); if (content_type && httpish_strncasecmp(content_type, SIP_CONTENT_TYPE_MEDIA_CONTROL, strlen(SIP_CONTENT_TYPE_MEDIA_CONTROL)) == 0) { media_control_info_package_handler(ccb->dn_line, ccb->gsm_id, "", // legacy mode, no Info Package SIP_CONTENT_TYPE_MEDIA_CONTROL, pSipMessage->mesg_body[0].msgBody); if (sipSPISendErrorResponse(pSipMessage, 200, SIP_SUCCESS_SETUP_PHRASE, 0, NULL, NULL) != TRUE) { CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR), fname, SIP_SUCCESS_SETUP_PHRASE); return SIP_ERROR; } return SIP_OK; } /* * Parse the Info-Package header */ // leading white spaces are trimmed, but not trailing ones info_package = sippmh_get_header_val(pSipMessage, SIP_HEADER_INFO_PACKAGE, NULL); if (info_package == NULL) { /* No Info-Package header */ CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing Info-Package header\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); if (pSipMessage->num_body_parts == 0) { /* No Info-Package header, and no body poarts */ CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing message body\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); /* Send 200 OK for legacy UA support */ status_code = 200; reason_phrase = SIP_SUCCESS_SETUP_PHRASE; return_code = SIP_OK; } else { /* No Info-Package header, but with body part(s) */ if (pSipMessage->num_body_parts > 1) { CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Multipart Info Package" DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); } type_index = find_type_index(pSipMessage->mesg_body[0].msgContentType); if (type_index == INDEX_NOT_FOUND) { /* No Info-Package header, and Content-Type is not supported */ CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Content Type\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); /* Send 415 Unsupported Media Type */ status_code = SIP_CLI_ERR_MEDIA; reason_phrase = SIP_CLI_ERR_MEDIA_PHRASE; } else { /* No Info-Package header, but Conent-Type is supported */ /* Send 200 OK for legacy UA support */ status_code = 200; reason_phrase = SIP_SUCCESS_SETUP_PHRASE; return_code = SIP_OK; } } } else { /* With Info-Package header */ if (pSipMessage->num_body_parts == 0) { /* With Info-Package header, but no body parts */ CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Missing message body\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); /* ? */ /* Send 489 Bad Event */ status_code = SIP_CLI_ERR_BAD_EVENT; reason_phrase = SIP_CLI_ERR_BAD_EVENT_PHRASE; } else { /* With Info-Package header and body part(s) */ if (pSipMessage->num_body_parts > 1) { CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Multipart Info Package " "(only the first part is processed)\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); } info_index = find_info_index(info_package); if (info_index == INDEX_NOT_FOUND) { /* Info-Package is not supported */ CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Info Package\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); /* Send 489 Bad Event */ status_code = SIP_CLI_ERR_BAD_EVENT; reason_phrase = SIP_CLI_ERR_BAD_EVENT_PHRASE; } else { /* Info-Package is supported */ type_index = find_type_index(pSipMessage->mesg_body[0].msgContentType); record = find_handler_record(info_index, type_index); if (record == NULL) { /* Info-Package header is supported, but Content-Type is not */ CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"Unsupported Content Type\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname)); /* Send 415 Unsupported Media Type */ status_code = SIP_CLI_ERR_MEDIA; reason_phrase = SIP_CLI_ERR_MEDIA_PHRASE; } else { /* a handler is registered */ (*record->handler)(ccb->dn_line, ccb->gsm_id, g_registered_info[record->info_index], s_registered_type[record->type_index], pSipMessage->mesg_body[0].msgBody); /* Send 200 OK */ status_code = 200; reason_phrase = SIP_SUCCESS_SETUP_PHRASE; return_code = SIP_OK; } } } } if (sipSPISendErrorResponse(pSipMessage, status_code, reason_phrase, 0, NULL, NULL) != TRUE) { CCSIP_DEBUG_ERROR(get_debug_string(DEBUG_SIP_SPI_SEND_ERROR), fname, reason_phrase); return SIP_ERROR; } return return_code; } static void media_control_info_package_handler(line_t line, callid_t call_id, const char *info_package, const char *content_type, const char *message_body) { } #ifdef _CONF_ROSTER_ static void conf_info_package_handler(line_t line, callid_t call_id, const char *info_package, const char *content_type, const char *message_body) { static const char *fname = "conf_info_package_handler"; CCSIP_DEBUG_MESSAGE(DEB_F_PREFIX"info_package: %s content_type: %s\n", DEB_F_PREFIX_ARGS(SIP_INFO_PACKAGE, fname), info_package, content_type); ui_info_received(line, lsm_get_ui_id(call_id), info_package, content_type, message_body); } #endif /* * Function: ccsip_info_package_handler_init * * Parameters: * None * * Description: * Initializes the Info Package handler framework. * * Return: * SIP_OK - Info Package handler framework initialized successfully * SIP_ERROR - otherwise */ int ccsip_info_package_handler_init(void) { static const char *fname = "ccsip_info_package_handler_init"; info_index_t info_index; type_index_t type_index; if (s_handler_registry != NULL) { // Is this considered an error? CCSIP_DEBUG_TASK("%s: Info Package handler already initialized\n", fname); return SIP_OK; } /* Create the SLL */ s_handler_registry = sll_create(is_matching_type); if (s_handler_registry == NULL) { CCSIP_DEBUG_ERROR("%s: failed to create the registry\n", fname); return SIP_ERROR; } for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) { g_registered_info[info_index] = NULL; } for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) { s_registered_type[type_index] = NULL; } // XXX Where is the best place to register the application-specific handler? #ifdef _CONF_ROSTER_ /* Register the handler for conference & x-cisco-conference Info Packages */ ccsip_register_info_package_handler(INFO_PACKAGE_CONFERENCE, CONTENT_TYPE_CONFERENCE_INFO, conf_info_package_handler); ccsip_register_info_package_handler(INFO_PACKAGE_CISCO_CONFERENCE, CONTENT_TYPE_CONFERENCE_INFO, conf_info_package_handler); #endif return SIP_OK; } /* * Function: ccsip_info_package_handler_shutdown * * Parameters: * None * * Description: * Shuts down the Info Package handler framework. * * Return: * None */ void ccsip_info_package_handler_shutdown(void) { static const char *fname = "ccsip_info_package_handler_shutdown"; info_index_t info_index; type_index_t type_index; handler_record_t *record; if (s_handler_registry == NULL) { // Is this considered an error? CCSIP_DEBUG_TASK("%s: Info Package handler was not initialized\n", fname); return; } for (type_index = 0; type_index < MAX_INFO_HANDLER; type_index++) { if (s_registered_type[type_index] != NULL) { cpr_free(s_registered_type[type_index]); s_registered_type[type_index] = NULL; } } for (info_index = 0; info_index < MAX_INFO_HANDLER; info_index++) { if (g_registered_info[info_index] != NULL) { cpr_free(g_registered_info[info_index]); g_registered_info[info_index] = NULL; } } /* Deregister each Info Package handler */ for (record = (handler_record_t *)sll_next(s_handler_registry, NULL); record != NULL; record = (handler_record_t *)sll_next(s_handler_registry, record)) { cpr_free(record); } /* Destroy the SLL */ sll_destroy(s_handler_registry); s_handler_registry = NULL; }