Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 256484) +++ channels/chan_sip.c (working copy) @@ -345,6 +345,25 @@ that name to retrieve. Headers start at offset 1. + + + Gets the specified SIP body part. + + + + + + + Since the body of SIP INVITE message may contain different content types + (not just SDP) or may be a multipart body, this function gets the body of the + requested Content-Type. Since there might be several body parts of the given + content type, an optional number can be used to get a specific body part (default + value of 1 means the first body part of the given content type). + As line delimiter \r\n is returned (body part always ends with \r\n). + Note: this function only works on the INVITE that starts a dialog. Body parts of + other SIP messages can't be managed from the dial plan. + + Gets SIP peer information. @@ -1054,6 +1073,7 @@ static const char *get_sdp_iterate(int* start, struct sip_request *req, const char *name); static char get_sdp_line(int *start, int stop, struct sip_request *req, const char **value); static int find_sdp(struct sip_request *req); +static int find_content(struct sip_request *req, const char *cont, int *num, unsigned int *start, unsigned int *end, unsigned int *length); static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action); static int process_sdp_o(const char *o, struct sip_pvt *p); static int process_sdp_c(const char *c, struct ast_hostent *hp); @@ -6556,94 +6576,25 @@ */ static int find_sdp(struct sip_request *req) { - const char *content_type; - const char *content_length; - const char *search; - char *boundary; - unsigned int x; - int boundaryisquoted = FALSE; - int found_application_sdp = FALSE; - int found_end_of_headers = FALSE; - content_length = get_header(req, "Content-Length"); + const char *content_type = "application/sdp"; + unsigned int start = 0; /* first line of SDP */ + unsigned int end = 0; /* last line of SDP */ + unsigned int length = 0; /* do not request the length to be calculated by find_content */ + int num = 1; + unsigned int found_sdp = 0; + + found_sdp = find_content(req, content_type, &num, &start, &end, &length); - if (!ast_strlen_zero(content_length)) { - if (sscanf(content_length, "%30u", &x) != 1) { - ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length); - return 0; - } - - /* Content-Length of zero means there can't possibly be an - SDP here, even if the Content-Type says there is */ - if (x == 0) - return 0; - } - - content_type = get_header(req, "Content-Type"); - - /* if the body contains only SDP, this is easy */ - if (!strncasecmp(content_type, "application/sdp", 15)) { + if (found_sdp == 0) { + req->sdp_count = 0; req->sdp_start = 0; - req->sdp_count = req->lines; - return req->lines ? 1 : 0; - } - - /* if it's not multipart/mixed, there cannot be an SDP */ - if (strncasecmp(content_type, "multipart/mixed", 15)) return 0; - - /* if there is no boundary marker, it's invalid */ - if ((search = strcasestr(content_type, ";boundary="))) - search += 10; - else if ((search = strcasestr(content_type, "; boundary="))) - search += 11; - else - return 0; - - if (ast_strlen_zero(search)) - return 0; - - /* If the boundary is quoted with ", remove quote */ - if (*search == '\"') { - search++; - boundaryisquoted = TRUE; } + req->sdp_count = end - start + 1; /* set number of lines of the SDP */ + req->sdp_start = start; /* set line where the SDP starts */ - /* make a duplicate of the string, with two extra characters - at the beginning */ - boundary = ast_strdupa(search - 2); - boundary[0] = boundary[1] = '-'; - /* Remove final quote */ - if (boundaryisquoted) - boundary[strlen(boundary) - 1] = '\0'; - - /* search for the boundary marker, the empty line delimiting headers from - sdp part and the end boundry if it exists */ - - for (x = 0; x < (req->lines); x++) { - const char *line = REQ_OFFSET_TO_STR(req, line[x]); - if (!strncasecmp(line, boundary, strlen(boundary))){ - if (found_application_sdp && found_end_of_headers) { - req->sdp_count = (x - 1) - req->sdp_start; - return 1; - } - found_application_sdp = FALSE; - } - if (!strcasecmp(line, "Content-Type: application/sdp")) - found_application_sdp = TRUE; - - if (ast_strlen_zero(line)) { - if (found_application_sdp && !found_end_of_headers){ - req->sdp_start = x; - found_end_of_headers = TRUE; - } - } - } - if (found_application_sdp && found_end_of_headers) { - req->sdp_count = x - req->sdp_start; - return TRUE; - } - return FALSE; + return TRUE; } @@ -16216,7 +16167,243 @@ return 0; } + + +/*! \brief Finds a specific body part +\return Returns 0 if body part not found +\note can be used to get the specified body part by content-type and the number (num) of the occurance of the content-type. + If length is set to 1, the sum of characters of all the lines of the respective body part are returned in length (excluding + the \r\n). + start and end indicate the start and end of the body part. +*/ +static int find_content(struct sip_request *req, const char *cont, int *num, unsigned int *start, unsigned int *end, unsigned int *length) +{ + const char *content_type; + const char *content_length; + const char *search; + char *boundary; + unsigned int x; + int boundaryisquoted = FALSE; + int found_content_type = FALSE; + int found_end_of_headers = FALSE; + int count = 0; /*< counts the occurances of the body part of the requested content type */ + + *start = 0; /*!< the line number where the body part begins */ + *end = 0; /*!< the line number where the body part ends */ + + content_length = get_header(req, "Content-Length"); + + if (!ast_strlen_zero(content_length)) { + if (sscanf(content_length, "%ud", &x) != 1) { + ast_log(LOG_WARNING, "Invalid Content-Length: %s\n", content_length); + return FALSE; + } + + /* Content-Length of zero means there can't possibly be a + body here, even if the Content-Type says there is */ + if (x == 0) { + return FALSE; + } + } + + content_type = get_header(req, "Content-Type"); + + /* if the body contains no multipart, this is easy */ + if (!strncasecmp(content_type, cont, strlen(cont))) { + *start = 0; + *end = req->lines - 1; + + /* calculate the real length of the body if requested (in case content length is wrong) */ + if (*length == 1) { + *length = 0; + for (x = 0; x < (req->lines); x++) { + const char *line = REQ_OFFSET_TO_STR(req, line[x]); + *length += strlen(line); + } + } + + return (req->lines && *num == 1) ? 1 : 0; + } + + /* if it's not multipart/mixed, there cannot be the content type we are looking for */ + if (strncasecmp(content_type, "multipart/mixed", 15)) { + return FALSE; + } + + /* if there is no boundary marker, it's invalid */ + if ((search = strcasestr(content_type, ";boundary="))) { + search += 10; + } else if ((search = strcasestr(content_type, "; boundary="))) { + search += 11; + } else { + return FALSE; + } + + if (ast_strlen_zero(search)) { + return FALSE; + } + + /* If the boundary is quoted with ", remove quote */ + if (*search == '\"') { + search++; + boundaryisquoted = TRUE; + } + + /* make a duplicate of the string, with two extra characters + at the beginning */ + boundary = ast_strdupa(search - 2); + boundary[0] = boundary[1] = '-'; + /* Remove final quote */ + if (boundaryisquoted) { + boundary[strlen(boundary) - 1] = '\0'; + } + + /* search for the boundary marker, the empty line delimiting headers from + body part and the end boundry if it exists */ + *length = 0; /* calculate the length anyway since we are iterating through the lines */ + + for (x = 0; x < (req->lines); x++) { + const char *line = REQ_OFFSET_TO_STR(req, line[x]); + if (!strncasecmp(line, boundary, strlen(boundary))){ + if (found_content_type && found_end_of_headers) { + count++; + /* check if we have the right number of body with this content-type. otherwise countinue search. */ + if (count == *num) { + *end = x-1; + return 1; + } + + *end = 0; + *start = 0; + found_end_of_headers = FALSE; + } + found_content_type = FALSE; + } + + if(!strncmp(line, "Content-Type:", 13)) { + const char * ct = ast_skip_blanks(line+13); + if (!strncasecmp(cont, ct, strlen(cont))) { + found_content_type = TRUE; + } + } + + + if (found_content_type && found_end_of_headers) { + *length += strlen(line); + } + + if (ast_strlen_zero(line)) { + if (found_content_type && !found_end_of_headers) { + *start = x + 1; + found_end_of_headers = TRUE; + } + } + + } + + if (found_content_type && found_end_of_headers && count++ == *num) { + *end = x; + return TRUE; + } + + *start = 0; + *end = 0; + return FALSE; +} + + + +/*! \brief Read SIP body (dialplan function), finds first body part of given content type */ +static int func_body_read(struct ast_channel *chan, const char *function, char *data, struct ast_str **buf, ssize_t maxlen) +{ + unsigned int start = 0; + unsigned int end = 0; + unsigned int length = 1; /* we request the total length of our body part for allocation enough memory. */ + struct sip_pvt *p = chan->tech_pvt; + struct sip_request *req = &p->initreq; + int x, size; + int number = 1; + char *tmp; + unsigned int found = 0; + + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(content_type); + AST_APP_ARG(number); + ); + + + if (ast_strlen_zero(data)) { + ast_log(LOG_WARNING, "This function requires a content type.\n"); + return -1; + } + + ast_channel_lock(chan); + if (!IS_SIP_TECH(chan->tech)) { + ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n"); + ast_channel_unlock(chan); + return -1; + } + + AST_STANDARD_APP_ARGS(args, data); + + /* If there is no private structure, this channel is no longer alive */ + if (!p) { + ast_channel_unlock(chan); + return -1; + } + + if (args.number) { + number = atoi(args.number); + } + + found = find_content(req, args.content_type, &number, &start, &end, &length); + + if (found == 0) { + ast_channel_unlock(chan); + return -1; + } + + size = length + (end - start + 1) * 2 + 1; /* total length of our body part + for \r\n in each line and \0 at the end. */ + tmp = ast_calloc(size, sizeof(char)); + + if (!tmp) { + ast_channel_unlock(chan); + ast_debug(3, "No body part of content type %s and number %i. \n", args.content_type, number); + return -1; + } + + + for (x = start; x <= end; x++) { + const char *line = REQ_OFFSET_TO_STR(req, line[x]); + strcat(tmp, line); + strcat(tmp, "\r\n"); + } + + if ( ( (maxlen < 0 ) && (ast_str_size(*buf) < size)) || ( (maxlen > 0) && (maxlen < size) ) ) { + ast_log(LOG_WARNING, "Buffer size too small - body part truncated.\n"); + } + + if (maxlen < 0) { + maxlen = -1; + } + + ast_str_set(buf, maxlen, "%s", tmp); + + ast_channel_unlock(chan); + + ast_free(tmp); + + ast_debug(3, "Found body part of content type %s, number %i, starting in line %i and ending in line %i. Body has %i lines.\n", args.content_type, number, start, end, req->lines); + + return 0; +} + +static struct ast_custom_function sip_body_function = { + .name = "SIP_BODY", + .read2 = func_body_read, +}; + /*! \brief Read SIP header (dialplan function) */ static int func_header_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len) { @@ -25272,6 +25459,7 @@ /* Register dialplan functions */ ast_custom_function_register(&sip_header_function); + ast_custom_function_register(&sip_body_function); ast_custom_function_register(&sippeer_function); ast_custom_function_register(&sipchaninfo_function); ast_custom_function_register(&checksipdomain_function); @@ -25324,6 +25512,7 @@ ast_custom_function_unregister(&sipchaninfo_function); ast_custom_function_unregister(&sippeer_function); ast_custom_function_unregister(&sip_header_function); + ast_custom_function_unregister(&sip_body_function); ast_custom_function_unregister(&checksipdomain_function); /* Unregister dial plan applications */