Implemented core multipart support and support in the invite session (re #1070)
- incoming multipart message will be handled automatically - for testing, enable HAVE_MULTIPART_TEST in pjsua_app.c git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@3243 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
ad56eb8ee9
commit
1c1d734e05
|
@ -43,6 +43,7 @@ SOURCE sip_dialog_wrap.cpp
|
|||
SOURCE sip_endpoint_wrap.cpp
|
||||
SOURCE sip_errno.c
|
||||
SOURCE sip_msg.c
|
||||
SOURCE sip_multipart.c
|
||||
SOURCE sip_parser_wrap.cpp
|
||||
SOURCE sip_resolve.c
|
||||
SOURCE sip_tel_uri_wrap.cpp
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
//#define STEREO_DEMO
|
||||
//#define TRANSPORT_ADAPTER_SAMPLE
|
||||
//#define HAVE_MULTIPART_TEST
|
||||
|
||||
/* Ringtones US UK */
|
||||
#define RINGBACK_FREQ1 440 /* 400 */
|
||||
|
@ -2218,6 +2219,36 @@ static void ring_start(pjsua_call_id call_id)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_MULTIPART_TEST
|
||||
/*
|
||||
* Enable multipart in msg_data and add a dummy body into the
|
||||
* multipart bodies.
|
||||
*/
|
||||
static void add_multipart(pjsua_msg_data *msg_data)
|
||||
{
|
||||
static pjsip_multipart_part *alt_part;
|
||||
|
||||
if (!alt_part) {
|
||||
pj_str_t type, subtype, content;
|
||||
|
||||
alt_part = pjsip_multipart_create_part(app_config.pool);
|
||||
|
||||
type = pj_str("text");
|
||||
subtype = pj_str("plain");
|
||||
content = pj_str("Sample text body of a multipart bodies");
|
||||
alt_part->body = pjsip_msg_body_create(app_config.pool, &type,
|
||||
&subtype, &content);
|
||||
}
|
||||
|
||||
msg_data->multipart_ctype.type = pj_str("multipart");
|
||||
msg_data->multipart_ctype.subtype = pj_str("mixed");
|
||||
pj_list_push_back(&msg_data->multipart_parts, alt_part);
|
||||
}
|
||||
# define TEST_MULTIPART(msg_data) add_multipart(msg_data)
|
||||
#else
|
||||
# define TEST_MULTIPART(msg_data)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Find next call when current call is disconnected or when user
|
||||
* press ']'
|
||||
|
@ -3432,6 +3463,7 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
char *uri;
|
||||
pj_str_t tmp;
|
||||
struct input_result result;
|
||||
pjsua_msg_data msg_data;
|
||||
pjsua_call_info call_info;
|
||||
pjsua_acc_info acc_info;
|
||||
|
||||
|
@ -3500,7 +3532,9 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
tmp.slen = 0;
|
||||
}
|
||||
|
||||
pjsua_call_make_call( current_acc, &tmp, 0, NULL, NULL, NULL);
|
||||
pjsua_msg_data_init(&msg_data);
|
||||
TEST_MULTIPART(&msg_data);
|
||||
pjsua_call_make_call( current_acc, &tmp, 0, NULL, &msg_data, NULL);
|
||||
break;
|
||||
|
||||
case 'M':
|
||||
|
@ -3638,7 +3672,6 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
pj_str_t hname = { "Contact", 7 };
|
||||
pj_str_t hvalue;
|
||||
pjsip_generic_string_hdr hcontact;
|
||||
pjsua_msg_data msg_data;
|
||||
|
||||
if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
|
||||
continue;
|
||||
|
@ -3886,7 +3919,6 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
|
||||
} else {
|
||||
int call = current_call;
|
||||
pjsua_msg_data msg_data;
|
||||
pjsip_generic_string_hdr refer_sub;
|
||||
pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
|
||||
pj_str_t STR_FALSE = { "false", 5 };
|
||||
|
@ -3941,7 +3973,6 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
} else {
|
||||
int call = current_call;
|
||||
int dst_call;
|
||||
pjsua_msg_data msg_data;
|
||||
pjsip_generic_string_hdr refer_sub;
|
||||
pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 };
|
||||
pj_str_t STR_FALSE = { "false", 5 };
|
||||
|
@ -4083,7 +4114,6 @@ void console_app_main(const pj_str_t *uri_to_call)
|
|||
|
||||
digits = pj_str(buf);
|
||||
for (i=0; i<digits.slen; ++i) {
|
||||
pjsua_msg_data msg_data;
|
||||
char body[80];
|
||||
|
||||
pjsua_msg_data_init(&msg_data);
|
||||
|
|
|
@ -36,7 +36,7 @@ export _CXXFLAGS:= $(_CFLAGS) $(CC_CXXFLAGS) $(OS_CXXFLAGS) $(M_CXXFLAGS) \
|
|||
#
|
||||
export PJSIP_SRCDIR = ../src/pjsip
|
||||
export PJSIP_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
|
||||
sip_config.o \
|
||||
sip_config.o sip_multipart.o \
|
||||
sip_errno.o sip_msg.o sip_parser.o sip_tel_uri.o sip_uri.o \
|
||||
sip_endpoint.o sip_util.o sip_util_proxy.o \
|
||||
sip_resolve.o sip_transport.o sip_transport_loop.o \
|
||||
|
@ -86,7 +86,7 @@ export CC_OUT CC AR RANLIB HOST_MV HOST_RM HOST_RMDIR HOST_MKDIR OBJEXT LD LDOUT
|
|||
#
|
||||
export TEST_SRCDIR = ../src/test
|
||||
export TEST_OBJS += dlg_core_test.o dns_test.o msg_err_test.o \
|
||||
msg_logger.o msg_test.o regc_test.o \
|
||||
msg_logger.o msg_test.o multipart_test.o regc_test.o \
|
||||
test.o transport_loop_test.o transport_tcp_test.o \
|
||||
transport_test.o transport_udp_test.o \
|
||||
tsx_basic_test.o tsx_bench.o tsx_uac_test.o \
|
||||
|
|
|
@ -381,6 +381,34 @@ struct pjsip_inv_session
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* This structure represents SDP information in a pjsip_rx_data. Application
|
||||
* retrieve this information by calling #pjsip_rdata_get_sdp_info(). This
|
||||
* mechanism supports multipart message body.
|
||||
*/
|
||||
typedef struct pjsip_rdata_sdp_info
|
||||
{
|
||||
/**
|
||||
* Pointer and length of the text body in the incoming message. If
|
||||
* the pointer is NULL, it means the message does not contain SDP
|
||||
* body.
|
||||
*/
|
||||
pj_str_t body;
|
||||
|
||||
/**
|
||||
* This will contain non-zero if an invalid SDP body is found in the
|
||||
* message.
|
||||
*/
|
||||
pj_status_t sdp_err;
|
||||
|
||||
/**
|
||||
* A parsed and validated SDP body.
|
||||
*/
|
||||
pjmedia_sdp_session *sdp;
|
||||
|
||||
} pjsip_rdata_sdp_info;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the invite usage module and register it to the endpoint.
|
||||
* The callback argument contains pointer to functions to be called on
|
||||
|
@ -874,6 +902,21 @@ PJ_DECL(pj_status_t) pjsip_create_sdp_body(pj_pool_t *pool,
|
|||
pjmedia_sdp_session *sdp,
|
||||
pjsip_msg_body **p_body);
|
||||
|
||||
/**
|
||||
* Retrieve SDP information from an incoming message. Application should
|
||||
* prefer to use this function rather than parsing the SDP manually since
|
||||
* this function supports multipart message body.
|
||||
*
|
||||
* This function will only parse the SDP once, the first time it is called
|
||||
* on the same message. Subsequent call on the same message will just pick
|
||||
* up the already parsed SDP from the message.
|
||||
*
|
||||
* @param rdata The incoming message.
|
||||
*
|
||||
* @return The SDP info.
|
||||
*/
|
||||
PJ_DECL(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata);
|
||||
|
||||
|
||||
PJ_END_DECL
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <pjsip/sip_uri.h>
|
||||
#include <pjsip/sip_tel_uri.h>
|
||||
#include <pjsip/sip_msg.h>
|
||||
#include <pjsip/sip_multipart.h>
|
||||
#include <pjsip/sip_parser.h>
|
||||
|
||||
/* Core */
|
||||
|
|
|
@ -0,0 +1,179 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __PJSIP_SIP_MULTIPART_H__
|
||||
#define __PJSIP_SIP_MULTIPART_H__
|
||||
|
||||
/**
|
||||
* @file pjsip/sip_multipart.h
|
||||
* @brief Multipart support.
|
||||
*/
|
||||
|
||||
#include <pjsip/sip_msg.h>
|
||||
|
||||
PJ_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* @defgroup PJSIP_MULTIPART Multipart message bodies.
|
||||
* @ingroup PJSIP_MSG
|
||||
* @brief Support for multipart message bodies.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* This structure describes the individual body part inside a multipart
|
||||
* message body. It mainly contains the message body itself and optional
|
||||
* headers.
|
||||
*/
|
||||
typedef struct pjsip_multipart_part
|
||||
{
|
||||
/**
|
||||
* Standard list element.
|
||||
*/
|
||||
PJ_DECL_LIST_MEMBER(struct pjsip_multipart_part);
|
||||
|
||||
/**
|
||||
* Optional message headers.
|
||||
*/
|
||||
pjsip_hdr hdr;
|
||||
|
||||
/**
|
||||
* Pointer to the message body.
|
||||
*/
|
||||
pjsip_msg_body *body;
|
||||
|
||||
} pjsip_multipart_part;
|
||||
|
||||
/**
|
||||
* Create an empty multipart body.
|
||||
*
|
||||
* @param pool Memory pool to allocate memory from.
|
||||
* @param ctype Optional MIME media type of the multipart
|
||||
* bodies. If not specified, "multipart/mixed"
|
||||
* will be used.
|
||||
* @param boundary Optional string to be set as part boundary.
|
||||
* The boundary string excludes the leading
|
||||
* hyphens. If this parameter is NULL or empty,
|
||||
* a random boundary will be generated.
|
||||
*
|
||||
* @return Multipart body instance with no part.
|
||||
*/
|
||||
PJ_DECL(pjsip_msg_body*) pjsip_multipart_create(pj_pool_t *pool,
|
||||
const pjsip_media_type *ctype,
|
||||
const pj_str_t *boundary);
|
||||
|
||||
/**
|
||||
* Create an empty multipart part.
|
||||
*
|
||||
* @param pool The memory pool.
|
||||
*
|
||||
* @return The multipart part.
|
||||
*/
|
||||
PJ_DECL(pjsip_multipart_part*) pjsip_multipart_create_part(pj_pool_t *pool);
|
||||
|
||||
|
||||
/**
|
||||
* Perform a deep clone to a multipart part.
|
||||
*
|
||||
* @param pool The memory pool.
|
||||
* @param part The part to be duplicated.
|
||||
*
|
||||
* @return Copy of the multipart part.
|
||||
*/
|
||||
PJ_DECL(pjsip_multipart_part*)
|
||||
pjsip_multipart_clone_part(pj_pool_t *pool,
|
||||
const pjsip_multipart_part *part);
|
||||
|
||||
/**
|
||||
* Add a part into multipart bodies.
|
||||
*
|
||||
* @param pool The memory pool.
|
||||
* @param mp The multipart bodies.
|
||||
* @param part The part to be added into the bodies.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_multipart_add_part(pj_pool_t *pool,
|
||||
pjsip_msg_body *mp,
|
||||
pjsip_multipart_part *part);
|
||||
|
||||
/**
|
||||
* Get the first part of multipart bodies.
|
||||
*
|
||||
* @param mp The multipart bodies.
|
||||
*
|
||||
* @return The first part, or NULL if the multipart
|
||||
* bodies currently doesn't hold any elements.
|
||||
*/
|
||||
PJ_DECL(pjsip_multipart_part*)
|
||||
pjsip_multipart_get_first_part(const pjsip_msg_body *mp);
|
||||
|
||||
/**
|
||||
* Get the next part after the specified part.
|
||||
*
|
||||
* @param mp The multipart bodies.
|
||||
* @param part The part.
|
||||
*
|
||||
* @return The next part, or NULL if there is no other part after
|
||||
* the part.
|
||||
*/
|
||||
PJ_DECL(pjsip_multipart_part*)
|
||||
pjsip_multipart_get_next_part(const pjsip_msg_body *mp,
|
||||
pjsip_multipart_part *part);
|
||||
|
||||
/**
|
||||
* Find a body inside multipart bodies which has the specified content type.
|
||||
*
|
||||
* @param mp The multipart body.
|
||||
* @param content_type Content type to find.
|
||||
* @param start If specified, the search will begin at
|
||||
* start->next. Otherwise it will begin at
|
||||
* the first part in the multipart bodies.
|
||||
*
|
||||
* @return The first part with the specified content type
|
||||
* if found, or NULL.
|
||||
*/
|
||||
PJ_DECL(pjsip_multipart_part*)
|
||||
pjsip_multipart_find_part( const pjsip_msg_body *mp,
|
||||
const pjsip_media_type *content_type,
|
||||
const pjsip_multipart_part *start);
|
||||
|
||||
/**
|
||||
* Parse multipart message.
|
||||
*
|
||||
* @param pool Memory pool.
|
||||
* @param buf Input buffer.
|
||||
* @param len The buffer length.
|
||||
* @param ctype Content type of the multipart body.
|
||||
* @param options Parsing options, must be zero for now.
|
||||
*
|
||||
* @return Multipart message body.
|
||||
*/
|
||||
PJ_DECL(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
|
||||
char *buf, pj_size_t len,
|
||||
const pjsip_media_type *ctype,
|
||||
unsigned options);
|
||||
|
||||
/**
|
||||
* @} PJSIP_MULTIPART
|
||||
*/
|
||||
|
||||
|
||||
PJ_END_DECL
|
||||
|
||||
#endif /* __PJSIP_SIP_MULTIPART_H__ */
|
|
@ -276,7 +276,7 @@ PJ_DECL(pj_status_t) pjsip_find_msg(const char *buf,
|
|||
* lines, and two when an error happen the value can
|
||||
* pinpoint the location of the error in the buffer.
|
||||
*
|
||||
* @return The instance of the header if parsing was successfull,
|
||||
* @return The instance of the header if parsing was successful,
|
||||
* or otherwise a NULL pointer will be returned.
|
||||
*/
|
||||
PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
|
||||
|
@ -287,21 +287,25 @@ PJ_DECL(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
|
|||
* Parse header line(s). Multiple headers can be parsed by this function.
|
||||
* When there are multiple headers, the headers MUST be separated by either
|
||||
* a newline (as in SIP message) or ampersand mark (as in URI). This separator
|
||||
* however is optional for the last header.
|
||||
* is optional for the last header.
|
||||
*
|
||||
* @param pool the pool.
|
||||
* @param input the input text to parse, which must be NULL terminated.
|
||||
* @param size the text length.
|
||||
* @param hlist the header list to store the parsed headers.
|
||||
* @param pool The pool.
|
||||
* @param input The input text to parse, which must be NULL terminated.
|
||||
* @param size The text length.
|
||||
* @param hlist The header list to store the parsed headers.
|
||||
* This list must have been initialized before calling
|
||||
* this function.
|
||||
* @param options Specify 1 here to make parsing stop when error is
|
||||
* encountered when parsing the header. Otherwise the
|
||||
* error is silently ignored and parsing resumes to the
|
||||
* next line.
|
||||
* @return zero if successfull, or -1 if error is encountered.
|
||||
* Upon error, the \a hlist argument MAY contain
|
||||
* successfully parsed headers.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool,
|
||||
char *input, pj_size_t size,
|
||||
pj_list *hlist );
|
||||
PJ_DECL(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, char *input,
|
||||
pj_size_t size, pjsip_hdr *hlist,
|
||||
unsigned options);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1186,10 +1186,26 @@ struct pjsua_msg_data
|
|||
pj_str_t content_type;
|
||||
|
||||
/**
|
||||
* Optional message body.
|
||||
* Optional message body to be added to the message, only when the
|
||||
* message doesn't have a body.
|
||||
*/
|
||||
pj_str_t msg_body;
|
||||
|
||||
/**
|
||||
* Content type of the multipart body. If application wants to send
|
||||
* multipart message bodies, it puts the parts in \a parts and set
|
||||
* the content type in \a multipart_ctype. If the message already
|
||||
* contains a body, the body will be added to the multipart bodies.
|
||||
*/
|
||||
pjsip_media_type multipart_ctype;
|
||||
|
||||
/**
|
||||
* List of multipart parts. If application wants to send multipart
|
||||
* message bodies, it puts the parts in \a parts and set the content
|
||||
* type in \a multipart_ctype. If the message already contains a body,
|
||||
* the body will be added to the multipart bodies.
|
||||
*/
|
||||
pjsip_multipart_part multipart_parts;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <pjsip/sip_module.h>
|
||||
#include <pjsip/sip_endpoint.h>
|
||||
#include <pjsip/sip_event.h>
|
||||
#include <pjsip/sip_multipart.h>
|
||||
#include <pjsip/sip_transaction.h>
|
||||
#include <pjmedia/sdp.h>
|
||||
#include <pjmedia/sdp_neg.h>
|
||||
|
@ -745,6 +746,67 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uac( pjsip_dialog *dlg,
|
|||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
PJ_DEF(pjsip_rdata_sdp_info*) pjsip_rdata_get_sdp_info(pjsip_rx_data *rdata)
|
||||
{
|
||||
pjsip_rdata_sdp_info *sdp_info;
|
||||
pjsip_msg_body *body = rdata->msg_info.msg->body;
|
||||
pjsip_ctype_hdr *ctype_hdr = rdata->msg_info.ctype;
|
||||
pjsip_media_type app_sdp;
|
||||
|
||||
sdp_info = (pjsip_rdata_sdp_info*)
|
||||
rdata->endpt_info.mod_data[mod_inv.mod.id];
|
||||
if (sdp_info)
|
||||
return sdp_info;
|
||||
|
||||
sdp_info = PJ_POOL_ZALLOC_T(rdata->tp_info.pool,
|
||||
pjsip_rdata_sdp_info);
|
||||
PJ_ASSERT_RETURN(mod_inv.mod.id >= 0, sdp_info);
|
||||
rdata->endpt_info.mod_data[mod_inv.mod.id] = sdp_info;
|
||||
|
||||
pjsip_media_type_init2(&app_sdp, "application", "sdp");
|
||||
|
||||
if (body && ctype_hdr &&
|
||||
pj_stricmp(&ctype_hdr->media.type, &app_sdp.type)==0 &&
|
||||
pj_stricmp(&ctype_hdr->media.subtype, &app_sdp.subtype)==0)
|
||||
{
|
||||
sdp_info->body.ptr = (char*)body->data;
|
||||
sdp_info->body.slen = body->len;
|
||||
} else if (body && ctype_hdr &&
|
||||
pj_stricmp2(&ctype_hdr->media.type, "multipart")==0 &&
|
||||
(pj_stricmp2(&ctype_hdr->media.subtype, "mixed")==0 ||
|
||||
pj_stricmp2(&ctype_hdr->media.subtype, "alternative")==0))
|
||||
{
|
||||
pjsip_multipart_part *part;
|
||||
|
||||
part = pjsip_multipart_find_part(body, &app_sdp, NULL);
|
||||
if (part) {
|
||||
sdp_info->body.ptr = (char*)part->body->data;
|
||||
sdp_info->body.slen = part->body->len;
|
||||
}
|
||||
}
|
||||
|
||||
if (sdp_info->body.ptr) {
|
||||
pj_status_t status;
|
||||
status = pjmedia_sdp_parse(rdata->tp_info.pool,
|
||||
sdp_info->body.ptr,
|
||||
sdp_info->body.slen,
|
||||
&sdp_info->sdp);
|
||||
if (status == PJ_SUCCESS)
|
||||
status = pjmedia_sdp_validate(sdp_info->sdp);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
sdp_info->sdp = NULL;
|
||||
PJ_PERROR(1,(THIS_FILE, status,
|
||||
"Error parsing/validating SDP body"));
|
||||
}
|
||||
|
||||
sdp_info->sdp_err = status;
|
||||
}
|
||||
|
||||
return sdp_info;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Verify incoming INVITE request.
|
||||
*/
|
||||
|
@ -765,6 +827,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
|
|||
unsigned rem_option = 0;
|
||||
pj_status_t status = PJ_SUCCESS;
|
||||
pjsip_hdr res_hdr_list;
|
||||
pjsip_rdata_sdp_info *sdp_info;
|
||||
|
||||
/* Init return arguments. */
|
||||
if (p_tdata) *p_tdata = NULL;
|
||||
|
@ -821,17 +884,17 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
|
|||
/* Check the request body, see if it's something that we support,
|
||||
* only when the body hasn't been parsed before.
|
||||
*/
|
||||
if (r_sdp==NULL && msg->body) {
|
||||
pjsip_msg_body *body = msg->body;
|
||||
pj_str_t str_application = {"application", 11};
|
||||
pj_str_t str_sdp = { "sdp", 3 };
|
||||
pjmedia_sdp_session *sdp;
|
||||
if (r_sdp == NULL) {
|
||||
sdp_info = pjsip_rdata_get_sdp_info(rdata);
|
||||
} else {
|
||||
sdp_info = NULL;
|
||||
}
|
||||
|
||||
/* Check content type. */
|
||||
if (pj_stricmp(&body->content_type.type, &str_application) != 0 ||
|
||||
pj_stricmp(&body->content_type.subtype, &str_sdp) != 0)
|
||||
{
|
||||
/* Not "application/sdp" */
|
||||
if (r_sdp==NULL && msg->body) {
|
||||
|
||||
/* Check if body really contains SDP. */
|
||||
if (sdp_info->body.ptr == NULL) {
|
||||
/* Couldn't find "application/sdp" */
|
||||
code = PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
|
||||
status = PJSIP_ERRNO_FROM_SIP_STATUS(code);
|
||||
|
||||
|
@ -848,13 +911,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
|
|||
goto on_return;
|
||||
}
|
||||
|
||||
/* Parse and validate SDP */
|
||||
status = pjmedia_sdp_parse(rdata->tp_info.pool,
|
||||
(char*)body->data, body->len, &sdp);
|
||||
if (status == PJ_SUCCESS)
|
||||
status = pjmedia_sdp_validate(sdp);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
if (sdp_info->sdp_err != PJ_SUCCESS) {
|
||||
/* Unparseable or invalid SDP */
|
||||
code = PJSIP_SC_BAD_REQUEST;
|
||||
|
||||
|
@ -864,7 +921,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
|
|||
|
||||
w = pjsip_warning_hdr_create_from_status(rdata->tp_info.pool,
|
||||
pjsip_endpt_name(endpt),
|
||||
status);
|
||||
sdp_info->sdp_err);
|
||||
PJ_ASSERT_RETURN(w, PJ_ENOMEM);
|
||||
|
||||
pj_list_push_back(&res_hdr_list, w);
|
||||
|
@ -873,7 +930,7 @@ PJ_DEF(pj_status_t) pjsip_inv_verify_request2(pjsip_rx_data *rdata,
|
|||
goto on_return;
|
||||
}
|
||||
|
||||
r_sdp = sdp;
|
||||
r_sdp = sdp_info->sdp;
|
||||
}
|
||||
|
||||
if (r_sdp) {
|
||||
|
@ -1163,7 +1220,7 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg,
|
|||
pjsip_inv_session *inv;
|
||||
struct tsx_inv_data *tsx_inv_data;
|
||||
pjsip_msg *msg;
|
||||
pjmedia_sdp_session *rem_sdp = NULL;
|
||||
pjsip_rdata_sdp_info *sdp_info;
|
||||
pj_status_t status;
|
||||
|
||||
/* Verify arguments. */
|
||||
|
@ -1211,26 +1268,17 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg,
|
|||
/* Object name will use the same dialog pointer. */
|
||||
pj_ansi_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg);
|
||||
|
||||
/* Parse SDP in message body, if present. */
|
||||
if (msg->body) {
|
||||
pjsip_msg_body *body = msg->body;
|
||||
|
||||
/* Parse and validate SDP */
|
||||
status = pjmedia_sdp_parse(inv->pool, (char*)body->data, body->len,
|
||||
&rem_sdp);
|
||||
if (status == PJ_SUCCESS)
|
||||
status = pjmedia_sdp_validate(rem_sdp);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
pjsip_dlg_dec_lock(dlg);
|
||||
return status;
|
||||
}
|
||||
/* Process SDP in message body, if present. */
|
||||
sdp_info = pjsip_rdata_get_sdp_info(rdata);
|
||||
if (sdp_info->sdp_err) {
|
||||
pjsip_dlg_dec_lock(dlg);
|
||||
return sdp_info->sdp_err;
|
||||
}
|
||||
|
||||
/* Create negotiator. */
|
||||
if (rem_sdp) {
|
||||
status = pjmedia_sdp_neg_create_w_remote_offer(inv->pool,
|
||||
local_sdp, rem_sdp,
|
||||
if (sdp_info->sdp) {
|
||||
status = pjmedia_sdp_neg_create_w_remote_offer(inv->pool, local_sdp,
|
||||
sdp_info->sdp,
|
||||
&inv->neg);
|
||||
|
||||
} else if (local_sdp) {
|
||||
|
@ -1374,8 +1422,8 @@ PJ_DEF(pj_status_t) pjsip_create_sdp_body( pj_pool_t *pool,
|
|||
body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
|
||||
PJ_ASSERT_RETURN(body != NULL, PJ_ENOMEM);
|
||||
|
||||
body->content_type.type = STR_APPLICATION;
|
||||
body->content_type.subtype = STR_SDP;
|
||||
pjsip_media_type_init(&body->content_type, (pj_str_t*)&STR_APPLICATION,
|
||||
(pj_str_t*)&STR_SDP);
|
||||
body->data = sdp;
|
||||
body->len = 0;
|
||||
body->clone_data = &clone_sdp;
|
||||
|
@ -1527,6 +1575,7 @@ static void swap_pool(pj_pool_t **p1, pj_pool_t **p2)
|
|||
*p2 = tmp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initiate SDP negotiation in the SDP negotiator.
|
||||
*/
|
||||
|
@ -1575,11 +1624,9 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
|
|||
pjsip_rx_data *rdata)
|
||||
{
|
||||
struct tsx_inv_data *tsx_inv_data;
|
||||
static const pj_str_t str_application = { "application", 11 };
|
||||
static const pj_str_t str_sdp = { "sdp", 3 };
|
||||
pj_status_t status;
|
||||
pjsip_msg *msg;
|
||||
pjmedia_sdp_session *rem_sdp;
|
||||
pjsip_rdata_sdp_info *sdp_info;
|
||||
|
||||
/* Check if SDP is present in the message. */
|
||||
|
||||
|
@ -1589,9 +1636,8 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
|
|||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
if (pj_stricmp(&msg->body->content_type.type, &str_application) ||
|
||||
pj_stricmp(&msg->body->content_type.subtype, &str_sdp))
|
||||
{
|
||||
sdp_info = pjsip_rdata_get_sdp_info(rdata);
|
||||
if (sdp_info->body.ptr == NULL) {
|
||||
/* Message body is not "application/sdp" */
|
||||
return PJMEDIA_SDP_EINSDP;
|
||||
}
|
||||
|
@ -1660,22 +1706,16 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
|
|||
}
|
||||
}
|
||||
|
||||
/* Parse the SDP body. */
|
||||
|
||||
status = pjmedia_sdp_parse(rdata->tp_info.pool,
|
||||
(char*)msg->body->data,
|
||||
msg->body->len, &rem_sdp);
|
||||
if (status == PJ_SUCCESS)
|
||||
status = pjmedia_sdp_validate(rem_sdp);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
char errmsg[PJ_ERR_MSG_SIZE];
|
||||
pj_strerror(status, errmsg, sizeof(errmsg));
|
||||
PJ_LOG(4,(THIS_FILE, "Error parsing SDP in %s: %s",
|
||||
pjsip_rx_data_get_info(rdata), errmsg));
|
||||
/* Process the SDP body. */
|
||||
if (sdp_info->sdp_err) {
|
||||
PJ_PERROR(4,(THIS_FILE, sdp_info->sdp_err,
|
||||
"Error parsing SDP in %s",
|
||||
pjsip_rx_data_get_info(rdata)));
|
||||
return PJMEDIA_SDP_EINSDP;
|
||||
}
|
||||
|
||||
pj_assert(sdp_info->sdp != NULL);
|
||||
|
||||
/* The SDP can be an offer or answer, depending on negotiator's state */
|
||||
|
||||
if (inv->neg == NULL ||
|
||||
|
@ -1689,17 +1729,16 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
|
|||
|
||||
if (inv->neg == NULL) {
|
||||
status=pjmedia_sdp_neg_create_w_remote_offer(inv->pool, NULL,
|
||||
rem_sdp, &inv->neg);
|
||||
sdp_info->sdp,
|
||||
&inv->neg);
|
||||
} else {
|
||||
status=pjmedia_sdp_neg_set_remote_offer(inv->pool_prov, inv->neg,
|
||||
rem_sdp);
|
||||
sdp_info->sdp);
|
||||
}
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
char errmsg[PJ_ERR_MSG_SIZE];
|
||||
pj_strerror(status, errmsg, sizeof(errmsg));
|
||||
PJ_LOG(4,(THIS_FILE, "Error processing SDP offer in %s: %s",
|
||||
pjsip_rx_data_get_info(rdata), errmsg));
|
||||
PJ_PERROR(4,(THIS_FILE, status, "Error processing SDP offer in %",
|
||||
pjsip_rx_data_get_info(rdata)));
|
||||
return PJMEDIA_SDP_EINSDP;
|
||||
}
|
||||
|
||||
|
@ -1707,7 +1746,7 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
|
|||
|
||||
if (mod_inv.cb.on_rx_offer && inv->notify) {
|
||||
|
||||
(*mod_inv.cb.on_rx_offer)(inv, rem_sdp);
|
||||
(*mod_inv.cb.on_rx_offer)(inv, sdp_info->sdp);
|
||||
|
||||
}
|
||||
|
||||
|
@ -1724,13 +1763,11 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
|
|||
pjsip_rx_data_get_info(rdata)));
|
||||
|
||||
status = pjmedia_sdp_neg_set_remote_answer(inv->pool_prov, inv->neg,
|
||||
rem_sdp);
|
||||
sdp_info->sdp);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
char errmsg[PJ_ERR_MSG_SIZE];
|
||||
pj_strerror(status, errmsg, sizeof(errmsg));
|
||||
PJ_LOG(4,(THIS_FILE, "Error processing SDP answer in %s: %s",
|
||||
pjsip_rx_data_get_info(rdata), errmsg));
|
||||
PJ_PERROR(4,(THIS_FILE, status, "Error processing SDP answer in %s",
|
||||
pjsip_rx_data_get_info(rdata)));
|
||||
return PJMEDIA_SDP_EINSDP;
|
||||
}
|
||||
|
||||
|
@ -3855,6 +3892,7 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
|
|||
pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
|
||||
pjsip_tx_data *tdata;
|
||||
pj_status_t status;
|
||||
pjsip_rdata_sdp_info *sdp_info;
|
||||
pjsip_status_code st_code;
|
||||
|
||||
/* Check if we have INVITE pending. */
|
||||
|
@ -3925,7 +3963,8 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
|
|||
/* If the INVITE request has SDP body, send answer.
|
||||
* Otherwise generate offer from local active SDP.
|
||||
*/
|
||||
if (rdata->msg_info.msg->body != NULL) {
|
||||
sdp_info = pjsip_rdata_get_sdp_info(rdata);
|
||||
if (sdp_info->sdp != NULL) {
|
||||
status = process_answer(inv, 200, tdata, NULL);
|
||||
} else {
|
||||
/* INVITE does not have SDP.
|
||||
|
|
|
@ -0,0 +1,644 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
* Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <pjsip/sip_multipart.h>
|
||||
#include <pjsip/sip_parser.h>
|
||||
#include <pjlib-util/scanner.h>
|
||||
#include <pj/assert.h>
|
||||
#include <pj/ctype.h>
|
||||
#include <pj/errno.h>
|
||||
#include <pj/except.h>
|
||||
#include <pj/guid.h>
|
||||
#include <pj/log.h>
|
||||
#include <pj/pool.h>
|
||||
#include <pj/string.h>
|
||||
|
||||
#define THIS_FILE "sip_multipart.c"
|
||||
|
||||
#define IS_SPACE(c) ((c)==' ' || (c)=='\t')
|
||||
|
||||
#if 0
|
||||
# define TRACE_(x) PJ_LOG(4,x)
|
||||
#else
|
||||
# define TRACE_(x)
|
||||
#endif
|
||||
|
||||
extern pj_bool_t pjsip_use_compact_form;
|
||||
|
||||
/* Type of "data" in multipart pjsip_msg_body */
|
||||
struct multipart_data
|
||||
{
|
||||
pj_str_t boundary;
|
||||
pjsip_multipart_part part_head;
|
||||
};
|
||||
|
||||
|
||||
static int multipart_print_body(struct pjsip_msg_body *msg_body,
|
||||
char *buf, pj_size_t size)
|
||||
{
|
||||
const struct multipart_data *m_data;
|
||||
pj_str_t clen_hdr = { "Content-Length: ", 16};
|
||||
pjsip_multipart_part *part;
|
||||
char *p = buf, *end = buf+size;
|
||||
|
||||
#define SIZE_LEFT() (end-p)
|
||||
|
||||
m_data = (const struct multipart_data*)msg_body->data;
|
||||
|
||||
PJ_ASSERT_RETURN(m_data && !pj_list_empty(&m_data->part_head), PJ_EINVAL);
|
||||
|
||||
part = m_data->part_head.next;
|
||||
while (part != &m_data->part_head) {
|
||||
enum { CLEN_SPACE = 5 };
|
||||
char *clen_pos;
|
||||
const pjsip_hdr *hdr;
|
||||
|
||||
clen_pos = NULL;
|
||||
|
||||
/* Print delimiter */
|
||||
if (SIZE_LEFT() <= (m_data->boundary.slen+8) << 1)
|
||||
return -1;
|
||||
*p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-';
|
||||
pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen);
|
||||
p += m_data->boundary.slen;
|
||||
*p++ = 13; *p++ = 10;
|
||||
|
||||
/* Print optional headers */
|
||||
hdr = part->hdr.next;
|
||||
while (hdr != &part->hdr) {
|
||||
int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, SIZE_LEFT());
|
||||
if (printed < 0)
|
||||
return -1;
|
||||
p += printed;
|
||||
hdr = hdr->next;
|
||||
}
|
||||
|
||||
/* Automaticly adds Content-Type and Content-Length headers, only
|
||||
* if content_type is set in the message body.
|
||||
*/
|
||||
if (part->body && part->body->content_type.type.slen) {
|
||||
pj_str_t ctype_hdr = { "Content-Type: ", 14};
|
||||
const pjsip_media_type *media = &part->body->content_type;
|
||||
|
||||
if (pjsip_use_compact_form) {
|
||||
ctype_hdr.ptr = "c: ";
|
||||
ctype_hdr.slen = 3;
|
||||
}
|
||||
|
||||
/* Add Content-Type header. */
|
||||
if ( (end-p) < 24 + media->type.slen + media->subtype.slen) {
|
||||
return -1;
|
||||
}
|
||||
pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
|
||||
p += ctype_hdr.slen;
|
||||
p += pjsip_media_type_print(p, end-p, media);
|
||||
*p++ = '\r';
|
||||
*p++ = '\n';
|
||||
|
||||
/* Add Content-Length header. */
|
||||
if ((end-p) < clen_hdr.slen + 12 + 2) {
|
||||
return -1;
|
||||
}
|
||||
pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
|
||||
p += clen_hdr.slen;
|
||||
|
||||
/* Print blanks after "Content-Length:", this is where we'll put
|
||||
* the content length value after we know the length of the
|
||||
* body.
|
||||
*/
|
||||
pj_memset(p, ' ', CLEN_SPACE);
|
||||
clen_pos = p;
|
||||
p += CLEN_SPACE;
|
||||
*p++ = '\r';
|
||||
*p++ = '\n';
|
||||
}
|
||||
|
||||
/* Empty newline */
|
||||
*p++ = 13; *p++ = 10;
|
||||
|
||||
/* Print the body */
|
||||
pj_assert(part->body != NULL);
|
||||
if (part->body) {
|
||||
int printed = part->body->print_body(part->body, p, SIZE_LEFT());
|
||||
if (printed < 0)
|
||||
return -1;
|
||||
p += printed;
|
||||
|
||||
/* Now that we have the length of the body, print this to the
|
||||
* Content-Length header.
|
||||
*/
|
||||
if (clen_pos) {
|
||||
char tmp[16];
|
||||
int len;
|
||||
|
||||
len = pj_utoa(printed, tmp);
|
||||
if (len > CLEN_SPACE) len = CLEN_SPACE;
|
||||
pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len);
|
||||
}
|
||||
}
|
||||
|
||||
part = part->next;
|
||||
}
|
||||
|
||||
/* Print closing delimiter */
|
||||
if (SIZE_LEFT() < m_data->boundary.slen+8)
|
||||
return -1;
|
||||
*p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-';
|
||||
pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen);
|
||||
p += m_data->boundary.slen;
|
||||
*p++ = '-'; *p++ = '-'; *p++ = 13; *p++ = 10;
|
||||
|
||||
#undef SIZE_LEFT
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
static void* multipart_clone_data(pj_pool_t *pool, const void *data,
|
||||
unsigned len)
|
||||
{
|
||||
const struct multipart_data *src;
|
||||
struct multipart_data *dst;
|
||||
const pjsip_multipart_part *src_part;
|
||||
|
||||
src = (const struct multipart_data*) data;
|
||||
dst = PJ_POOL_ALLOC_T(pool, struct multipart_data);
|
||||
|
||||
pj_strdup(pool, &dst->boundary, &src->boundary);
|
||||
|
||||
src_part = src->part_head.next;
|
||||
while (src_part != &src->part_head) {
|
||||
pjsip_multipart_part *dst_part;
|
||||
const pjsip_hdr *src_hdr;
|
||||
|
||||
dst_part = pjsip_multipart_create_part(pool);
|
||||
|
||||
src_hdr = src_part->hdr.next;
|
||||
while (src_hdr != &src_part->hdr) {
|
||||
pjsip_hdr *dst_hdr = pjsip_hdr_clone(pool, src_hdr);
|
||||
pj_list_push_back(&dst_part->hdr, dst_hdr);
|
||||
src_hdr = src_hdr->next;
|
||||
}
|
||||
|
||||
dst_part->body = pjsip_msg_body_clone(pool, src_part->body);
|
||||
|
||||
pj_list_push_back(&dst->part_head, dst_part);
|
||||
|
||||
src_part = src_part->next;
|
||||
}
|
||||
|
||||
return (void*)dst;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an empty multipart body.
|
||||
*/
|
||||
PJ_DEF(pjsip_msg_body*) pjsip_multipart_create( pj_pool_t *pool,
|
||||
const pjsip_media_type *ctype,
|
||||
const pj_str_t *boundary)
|
||||
{
|
||||
pjsip_msg_body *body;
|
||||
pjsip_param *ctype_param;
|
||||
struct multipart_data *mp_data;
|
||||
pj_str_t STR_BOUNDARY = { "boundary", 8 };
|
||||
|
||||
PJ_ASSERT_RETURN(pool, NULL);
|
||||
|
||||
body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
|
||||
|
||||
/* content-type */
|
||||
if (ctype && ctype->type.slen) {
|
||||
pjsip_media_type_cp(pool, &body->content_type, ctype);
|
||||
} else {
|
||||
const pj_str_t STR_MULTIPART = {"multipart", 9};
|
||||
const pj_str_t STR_MIXED = { "mixed", 5 };
|
||||
|
||||
body->content_type.type = STR_MULTIPART;
|
||||
body->content_type.subtype = STR_MIXED;
|
||||
}
|
||||
|
||||
/* multipart data */
|
||||
mp_data = PJ_POOL_ZALLOC_T(pool, struct multipart_data);
|
||||
pj_list_init(&mp_data->part_head);
|
||||
if (boundary) {
|
||||
pj_strdup(pool, &mp_data->boundary, boundary);
|
||||
} else {
|
||||
pj_create_unique_string(pool, &mp_data->boundary);
|
||||
}
|
||||
body->data = mp_data;
|
||||
|
||||
/* Add ";boundary" parameter to content_type parameter. */
|
||||
ctype_param = pjsip_param_find(&body->content_type.param, &STR_BOUNDARY);
|
||||
if (!ctype_param) {
|
||||
ctype_param = PJ_POOL_ALLOC_T(pool, pjsip_param);
|
||||
ctype_param->name = STR_BOUNDARY;
|
||||
pj_list_push_back(&body->content_type.param, ctype_param);
|
||||
}
|
||||
ctype_param->value = mp_data->boundary;
|
||||
|
||||
/* function pointers */
|
||||
body->print_body = &multipart_print_body;
|
||||
body->clone_data = &multipart_clone_data;
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an empty multipart part.
|
||||
*/
|
||||
PJ_DEF(pjsip_multipart_part*) pjsip_multipart_create_part(pj_pool_t *pool)
|
||||
{
|
||||
pjsip_multipart_part *mp;
|
||||
|
||||
mp = PJ_POOL_ZALLOC_T(pool, pjsip_multipart_part);
|
||||
pj_list_init(&mp->hdr);
|
||||
|
||||
return mp;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Deep clone.
|
||||
*/
|
||||
PJ_DEF(pjsip_multipart_part*)
|
||||
pjsip_multipart_clone_part(pj_pool_t *pool,
|
||||
const pjsip_multipart_part *src)
|
||||
{
|
||||
pjsip_multipart_part *dst;
|
||||
const pjsip_hdr *hdr;
|
||||
|
||||
dst = pjsip_multipart_create_part(pool);
|
||||
|
||||
hdr = src->hdr.next;
|
||||
while (hdr != &src->hdr) {
|
||||
pj_list_push_back(&dst->hdr, pjsip_hdr_clone(pool, hdr));
|
||||
hdr = hdr->next;
|
||||
}
|
||||
|
||||
dst->body = pjsip_msg_body_clone(pool, src->body);
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add a part into multipart bodies.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_multipart_add_part( pj_pool_t *pool,
|
||||
pjsip_msg_body *mp,
|
||||
pjsip_multipart_part *part)
|
||||
{
|
||||
struct multipart_data *m_data;
|
||||
|
||||
/* All params must be specified */
|
||||
PJ_ASSERT_RETURN(pool && mp && part, PJ_EINVAL);
|
||||
|
||||
/* mp must really point to an actual multipart msg body */
|
||||
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, PJ_EINVAL);
|
||||
|
||||
/* The multipart part must contain a valid message body */
|
||||
PJ_ASSERT_RETURN(part->body && part->body->print_body, PJ_EINVAL);
|
||||
|
||||
m_data = (struct multipart_data*)mp->data;
|
||||
pj_list_push_back(&m_data->part_head, part);
|
||||
|
||||
PJ_UNUSED_ARG(pool);
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the first part of multipart bodies.
|
||||
*/
|
||||
PJ_DEF(pjsip_multipart_part*)
|
||||
pjsip_multipart_get_first_part(const pjsip_msg_body *mp)
|
||||
{
|
||||
struct multipart_data *m_data;
|
||||
|
||||
/* Must specify mandatory params */
|
||||
PJ_ASSERT_RETURN(mp, NULL);
|
||||
|
||||
/* mp must really point to an actual multipart msg body */
|
||||
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
|
||||
|
||||
m_data = (struct multipart_data*)mp->data;
|
||||
if (pj_list_empty(&m_data->part_head))
|
||||
return NULL;
|
||||
|
||||
return m_data->part_head.next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the next part after the specified part.
|
||||
*/
|
||||
PJ_DEF(pjsip_multipart_part*)
|
||||
pjsip_multipart_get_next_part(const pjsip_msg_body *mp,
|
||||
pjsip_multipart_part *part)
|
||||
{
|
||||
struct multipart_data *m_data;
|
||||
|
||||
/* Must specify mandatory params */
|
||||
PJ_ASSERT_RETURN(mp && part, NULL);
|
||||
|
||||
/* mp must really point to an actual multipart msg body */
|
||||
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
|
||||
|
||||
m_data = (struct multipart_data*)mp->data;
|
||||
|
||||
/* the part parameter must be really member of the list */
|
||||
PJ_ASSERT_RETURN(pj_list_find_node(&m_data->part_head, part) != NULL,
|
||||
NULL);
|
||||
|
||||
if (part->next == &m_data->part_head)
|
||||
return NULL;
|
||||
|
||||
return part->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a body inside multipart bodies which has the specified content type.
|
||||
*/
|
||||
PJ_DEF(pjsip_multipart_part*)
|
||||
pjsip_multipart_find_part( const pjsip_msg_body *mp,
|
||||
const pjsip_media_type *content_type,
|
||||
const pjsip_multipart_part *start)
|
||||
{
|
||||
struct multipart_data *m_data;
|
||||
pjsip_multipart_part *part;
|
||||
|
||||
/* Must specify mandatory params */
|
||||
PJ_ASSERT_RETURN(mp && content_type, NULL);
|
||||
|
||||
/* mp must really point to an actual multipart msg body */
|
||||
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
|
||||
|
||||
m_data = (struct multipart_data*)mp->data;
|
||||
|
||||
if (start)
|
||||
part = start->next;
|
||||
else
|
||||
part = m_data->part_head.next;
|
||||
|
||||
while (part != &m_data->part_head) {
|
||||
if (pjsip_media_type_cmp(&part->body->content_type, content_type)==0)
|
||||
return part;
|
||||
part = part->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Parse a multipart part. "pct" is parent content-type */
|
||||
static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool,
|
||||
char *start,
|
||||
pj_size_t len,
|
||||
const pjsip_media_type *pct)
|
||||
{
|
||||
pjsip_multipart_part *part = pjsip_multipart_create_part(pool);
|
||||
char *p = start, *end = start+len, *end_hdr = NULL, *start_body = NULL;
|
||||
pjsip_ctype_hdr *ctype_hdr = NULL;
|
||||
|
||||
TRACE_((THIS_FILE, "Parsing part: begin--\n%.*s\n--end",
|
||||
(int)len, start));
|
||||
|
||||
/* Find the end of header area, by looking at an empty line */
|
||||
for (;;) {
|
||||
while (p!=end && *p!='\n') ++p;
|
||||
if (p==end) {
|
||||
start_body = end;
|
||||
break;
|
||||
}
|
||||
if ((p==start) || (p==start+1 && *(p-1)=='\r')) {
|
||||
/* Empty header section */
|
||||
end_hdr = start;
|
||||
start_body = ++p;
|
||||
break;
|
||||
} else if (p==end-1) {
|
||||
/* Empty body section */
|
||||
end_hdr = end;
|
||||
start_body = ++p;
|
||||
} else if ((p>=start+1 && *(p-1)=='\n') ||
|
||||
(p>=start+2 && *(p-1)=='\r' && *(p-2)=='\n'))
|
||||
{
|
||||
/* Found it */
|
||||
end_hdr = (*(p-1)=='\r') ? (p-1) : p;
|
||||
start_body = ++p;
|
||||
break;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse the headers */
|
||||
if (end_hdr-start > 0) {
|
||||
pjsip_hdr *hdr;
|
||||
pj_status_t status;
|
||||
|
||||
status = pjsip_parse_headers(pool, start, end_hdr-start,
|
||||
&part->hdr, 0);
|
||||
if (status != PJ_SUCCESS) {
|
||||
PJ_PERROR(2,(THIS_FILE, status, "Warning: error parsing multipart"
|
||||
" header"));
|
||||
}
|
||||
|
||||
/* Find Content-Type header */
|
||||
hdr = part->hdr.next;
|
||||
while (hdr != &part->hdr) {
|
||||
TRACE_((THIS_FILE, "Header parsed: %.*s", (int)hdr->name.slen,
|
||||
hdr->name.ptr));
|
||||
if (hdr->type == PJSIP_H_CONTENT_TYPE) {
|
||||
ctype_hdr = (pjsip_ctype_hdr*)hdr;
|
||||
}
|
||||
hdr = hdr->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign the body */
|
||||
part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
|
||||
if (ctype_hdr) {
|
||||
pjsip_media_type_cp(pool, &part->body->content_type, &ctype_hdr->media);
|
||||
} else if (pct && pj_stricmp2(&pct->subtype, "digest")==0) {
|
||||
part->body->content_type.type = pj_str("message");
|
||||
part->body->content_type.subtype = pj_str("rfc822");
|
||||
} else {
|
||||
part->body->content_type.type = pj_str("text");
|
||||
part->body->content_type.subtype = pj_str("plain");
|
||||
}
|
||||
|
||||
if (start_body < end) {
|
||||
part->body->data = start_body;
|
||||
part->body->len = end - start_body;
|
||||
} else {
|
||||
part->body->data = "";
|
||||
part->body->len = 0;
|
||||
}
|
||||
TRACE_((THIS_FILE, "Body parsed: \"%.*s\"", (int)part->body->len,
|
||||
part->body->data));
|
||||
part->body->print_body = &pjsip_print_text_body;
|
||||
part->body->clone_data = &pjsip_clone_text_data;
|
||||
|
||||
return part;
|
||||
}
|
||||
|
||||
/* Public function to parse multipart message bodies into its parts */
|
||||
PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
|
||||
char *buf, pj_size_t len,
|
||||
const pjsip_media_type *ctype,
|
||||
unsigned options)
|
||||
{
|
||||
pj_str_t boundary, delim;
|
||||
char *curptr, *endptr;
|
||||
const pjsip_param *ctype_param;
|
||||
const pj_str_t STR_BOUNDARY = { "boundary", 8 };
|
||||
pjsip_msg_body *body = NULL;
|
||||
|
||||
PJ_ASSERT_RETURN(pool && buf && len && ctype && !options, NULL);
|
||||
|
||||
TRACE_((THIS_FILE, "Started parsing multipart body"));
|
||||
|
||||
/* Get the boundary value in the ctype */
|
||||
boundary.slen = 0;
|
||||
ctype_param = pjsip_param_find(&ctype->param, &STR_BOUNDARY);
|
||||
if (ctype_param) {
|
||||
boundary = ctype_param->value;
|
||||
TRACE_((THIS_FILE, "Boundary is specified: '%.*s'", (int)boundary.slen,
|
||||
boundary.ptr));
|
||||
}
|
||||
|
||||
if (!boundary.slen) {
|
||||
/* Boundary not found or not specified. Try to be clever, get
|
||||
* the boundary from the body.
|
||||
*/
|
||||
char *p=buf, *end=buf+len;
|
||||
|
||||
PJ_LOG(4,(THIS_FILE, "Warning: boundary parameter not found or "
|
||||
"not specified when parsing multipart body"));
|
||||
|
||||
/* Find the first "--". This "--" must be right after a CRLF, unless
|
||||
* it really appears at the start of the buffer.
|
||||
*/
|
||||
for (;;) {
|
||||
while (p!=end && *p!='-') ++p;
|
||||
if (p!=end && *(p+1)=='-' &&
|
||||
((p>buf && *(p-1)=='\n') || (p==buf)))
|
||||
{
|
||||
p+=2;
|
||||
break;
|
||||
} else {
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
if (p==end) {
|
||||
/* Unable to determine boundary. Maybe this is not a multipart
|
||||
* message?
|
||||
*/
|
||||
PJ_LOG(4,(THIS_FILE, "Error: multipart boundary not specified and"
|
||||
" unable to calculate from the body"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
boundary.ptr = p;
|
||||
while (p!=end && !pj_isspace(*p)) ++p;
|
||||
boundary.slen = p - boundary.ptr;
|
||||
|
||||
TRACE_((THIS_FILE, "Boundary is calculated: '%.*s'",
|
||||
(int)boundary.slen, boundary.ptr));
|
||||
}
|
||||
|
||||
/* Build the delimiter:
|
||||
* delimiter = "--" boundary
|
||||
*/
|
||||
delim.slen = boundary.slen+2;
|
||||
delim.ptr = (char*)pj_pool_alloc(pool, (int)delim.slen);
|
||||
delim.ptr[0] = '-';
|
||||
delim.ptr[1] = '-';
|
||||
pj_memcpy(delim.ptr+2, boundary.ptr, boundary.slen);
|
||||
|
||||
/* Start parsing the body, skip until the first delimiter. */
|
||||
curptr = buf;
|
||||
endptr = buf + len;
|
||||
{
|
||||
pj_str_t body;
|
||||
|
||||
body.ptr = buf; body.slen = len;
|
||||
curptr = pj_strstr(&body, &delim);
|
||||
if (!curptr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
body = pjsip_multipart_create(pool, ctype, &boundary);
|
||||
|
||||
for (;;) {
|
||||
char *start_body, *end_body;
|
||||
pjsip_multipart_part *part;
|
||||
|
||||
/* Eat the boundary */
|
||||
curptr += delim.slen;
|
||||
if (*curptr=='-' && curptr<endptr-1 && *(curptr+1)=='-') {
|
||||
/* Found the closing delimiter */
|
||||
curptr += 2;
|
||||
break;
|
||||
}
|
||||
/* Optional whitespace after delimiter */
|
||||
while (curptr!=endptr && IS_SPACE(*curptr)) ++curptr;
|
||||
/* Mandatory CRLF */
|
||||
if (*curptr=='\r') ++curptr;
|
||||
if (*curptr!='\n') {
|
||||
/* Expecting a newline here */
|
||||
return NULL;
|
||||
}
|
||||
++curptr;
|
||||
|
||||
/* We now in the start of the body */
|
||||
start_body = curptr;
|
||||
|
||||
/* Find the next delimiter */
|
||||
{
|
||||
pj_str_t subbody;
|
||||
|
||||
subbody.ptr = curptr; subbody.slen = endptr - curptr;
|
||||
curptr = pj_strstr(&subbody, &delim);
|
||||
if (!curptr) {
|
||||
/* We're really expecting end delimiter to be found. */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
end_body = curptr;
|
||||
|
||||
/* The newline preceeding the delimiter is conceptually part of
|
||||
* the delimiter, so trim it from the body.
|
||||
*/
|
||||
if (*(end_body-1) == '\n')
|
||||
--end_body;
|
||||
if (*(end_body-1) == '\r')
|
||||
--end_body;
|
||||
|
||||
/* Now that we have determined the part's boundary, parse it
|
||||
* to get the header and body part of the part.
|
||||
*/
|
||||
part = parse_multipart_part(pool, start_body, end_body - start_body,
|
||||
ctype);
|
||||
if (part) {
|
||||
pjsip_multipart_add_part(pool, body, part);
|
||||
}
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
#include <pjsip/sip_parser.h>
|
||||
#include <pjsip/sip_uri.h>
|
||||
#include <pjsip/sip_msg.h>
|
||||
#include <pjsip/sip_multipart.h>
|
||||
#include <pjsip/sip_auth_parser.h>
|
||||
#include <pjsip/sip_errno.h>
|
||||
#include <pjsip/sip_transport.h> /* rdata structure */
|
||||
|
@ -34,6 +35,8 @@
|
|||
#include <pj/ctype.h>
|
||||
#include <pj/assert.h>
|
||||
|
||||
#define THIS_FILE "sip_parser.c"
|
||||
|
||||
#define ALNUM
|
||||
#define RESERVED ";/?:@&=+$,"
|
||||
#define MARK "-_.!~*'()"
|
||||
|
@ -268,13 +271,6 @@ PJ_DEF(void) pjsip_concat_param_imp(pj_str_t *param, pj_pool_t *pool,
|
|||
param->slen = p - new_param;
|
||||
}
|
||||
|
||||
/* Concatenate unrecognized params into single string. */
|
||||
static void concat_param( pj_str_t *param, pj_pool_t *pool,
|
||||
const pj_str_t *pname, const pj_str_t *pvalue )
|
||||
{
|
||||
pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
|
||||
}
|
||||
|
||||
/* Initialize static properties of the parser. */
|
||||
static pj_status_t init_parser()
|
||||
{
|
||||
|
@ -1052,15 +1048,27 @@ parse_headers:
|
|||
* as body.
|
||||
*/
|
||||
if (ctype_hdr && scanner->curptr!=scanner->end) {
|
||||
pjsip_msg_body *body = PJ_POOL_ALLOC_T(pool, pjsip_msg_body);
|
||||
body->content_type.type = ctype_hdr->media.type;
|
||||
body->content_type.subtype = ctype_hdr->media.subtype;
|
||||
body->content_type.param = ctype_hdr->media.param;
|
||||
/* New: if Content-Type indicates that this is a multipart
|
||||
* message body, parse it.
|
||||
*/
|
||||
const pj_str_t STR_MULTIPART = { "multipart", 9 };
|
||||
pjsip_msg_body *body;
|
||||
|
||||
body->data = scanner->curptr;
|
||||
body->len = scanner->end - scanner->curptr;
|
||||
body->print_body = &pjsip_print_text_body;
|
||||
body->clone_data = &pjsip_clone_text_data;
|
||||
if (pj_stricmp(&ctype_hdr->media.type, &STR_MULTIPART)==0) {
|
||||
body = pjsip_multipart_parse(pool, scanner->curptr,
|
||||
scanner->end - scanner->curptr,
|
||||
&ctype_hdr->media, 0);
|
||||
} else {
|
||||
body = PJ_POOL_ALLOC_T(pool, pjsip_msg_body);
|
||||
body->content_type.type = ctype_hdr->media.type;
|
||||
body->content_type.subtype = ctype_hdr->media.subtype;
|
||||
body->content_type.param = ctype_hdr->media.param;
|
||||
|
||||
body->data = scanner->curptr;
|
||||
body->len = scanner->end - scanner->curptr;
|
||||
body->print_body = &pjsip_print_text_body;
|
||||
body->clone_data = &pjsip_clone_text_data;
|
||||
}
|
||||
|
||||
msg->body = body;
|
||||
}
|
||||
|
@ -1847,9 +1855,9 @@ static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx )
|
|||
|
||||
/* Parse media parameters */
|
||||
while (*scanner->curptr == ';') {
|
||||
pj_str_t pname, pvalue;
|
||||
int_parse_param(scanner, ctx->pool, &pname, &pvalue, 0);
|
||||
concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue);
|
||||
pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param);
|
||||
int_parse_param(scanner, ctx->pool, ¶m->name, ¶m->value, 0);
|
||||
pj_list_push_back(&hdr->media.param, param);
|
||||
}
|
||||
|
||||
parse_hdr_end(ctx->scanner);
|
||||
|
@ -2264,3 +2272,109 @@ PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
|
|||
return hdr;
|
||||
}
|
||||
|
||||
/* Parse multiple header lines */
|
||||
PJ_DEF(pj_status_t) pjsip_parse_headers( pj_pool_t *pool, char *input,
|
||||
pj_size_t size, pjsip_hdr *hlist,
|
||||
unsigned options)
|
||||
{
|
||||
enum { STOP_ON_ERROR = 1 };
|
||||
pj_scanner scanner;
|
||||
pjsip_parse_ctx ctx;
|
||||
pj_str_t hname;
|
||||
PJ_USE_EXCEPTION;
|
||||
|
||||
pj_scan_init(&scanner, input, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
|
||||
&on_syntax_error);
|
||||
|
||||
pj_bzero(&ctx, sizeof(ctx));
|
||||
ctx.scanner = &scanner;
|
||||
ctx.pool = pool;
|
||||
|
||||
retry_parse:
|
||||
PJ_TRY
|
||||
{
|
||||
/* Parse headers. */
|
||||
do {
|
||||
pjsip_parse_hdr_func * handler;
|
||||
pjsip_hdr *hdr = NULL;
|
||||
|
||||
/* Init hname just in case parsing fails.
|
||||
* Ref: PROTOS #2412
|
||||
*/
|
||||
hname.slen = 0;
|
||||
|
||||
/* Get hname. */
|
||||
pj_scan_get( &scanner, &pconst.pjsip_TOKEN_SPEC, &hname);
|
||||
if (pj_scan_get_char( &scanner ) != ':') {
|
||||
PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
|
||||
}
|
||||
|
||||
/* Find handler. */
|
||||
handler = find_handler(&hname);
|
||||
|
||||
/* Call the handler if found.
|
||||
* If no handler is found, then treat the header as generic
|
||||
* hname/hvalue pair.
|
||||
*/
|
||||
if (handler) {
|
||||
hdr = (*handler)(&ctx);
|
||||
} else {
|
||||
hdr = parse_hdr_generic_string(&ctx);
|
||||
hdr->name = hdr->sname = hname;
|
||||
}
|
||||
|
||||
/* Single parse of header line can produce multiple headers.
|
||||
* For example, if one Contact: header contains Contact list
|
||||
* separated by comma, then these Contacts will be split into
|
||||
* different Contact headers.
|
||||
* So here we must insert list instead of just insert one header.
|
||||
*/
|
||||
if (hdr)
|
||||
pj_list_insert_nodes_before(hlist, hdr);
|
||||
|
||||
/* Parse until EOF or an empty line is found. */
|
||||
} while (!pj_scan_is_eof(&scanner) && !IS_NEWLINE(*scanner.curptr));
|
||||
|
||||
/* If empty line is found, eat it. */
|
||||
if (!pj_scan_is_eof(&scanner)) {
|
||||
if (IS_NEWLINE(*scanner.curptr)) {
|
||||
pj_scan_get_newline(&scanner);
|
||||
}
|
||||
}
|
||||
}
|
||||
PJ_CATCH_ANY
|
||||
{
|
||||
PJ_LOG(4,(THIS_FILE, "Error parsing header: '%.*s' line %d col %d",
|
||||
(int)hname.slen, hname.ptr, scanner.line,
|
||||
pj_scan_get_col(&scanner)));
|
||||
|
||||
/* Exception was thrown during parsing. */
|
||||
if ((options & STOP_ON_ERROR) == STOP_ON_ERROR) {
|
||||
pj_scan_fini(&scanner);
|
||||
return PJSIP_EINVALIDHDR;
|
||||
}
|
||||
|
||||
/* Skip until newline, and parse next header. */
|
||||
if (!pj_scan_is_eof(&scanner)) {
|
||||
/* Skip until next line.
|
||||
* Watch for header continuation.
|
||||
*/
|
||||
do {
|
||||
pj_scan_skip_line(&scanner);
|
||||
} while (IS_SPACE(*scanner.curptr));
|
||||
}
|
||||
|
||||
/* Restore flag. Flag may be set in int_parse_sip_url() */
|
||||
scanner.skip_ws = PJ_SCAN_AUTOSKIP_WS_HEADER;
|
||||
|
||||
/* Continue parse next header, if any. */
|
||||
if (!pj_scan_is_eof(&scanner) && !IS_NEWLINE(*scanner.curptr)) {
|
||||
goto retry_parse;
|
||||
}
|
||||
|
||||
}
|
||||
PJ_END;
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
|
@ -645,7 +645,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
|
|||
pjsua_call *call;
|
||||
int call_id = -1;
|
||||
int sip_err_code;
|
||||
pjmedia_sdp_session *offer, *answer;
|
||||
pjmedia_sdp_session *offer=NULL, *answer;
|
||||
pj_status_t status;
|
||||
|
||||
/* Don't want to handle anything but INVITE */
|
||||
|
@ -765,13 +765,14 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
|
|||
|
||||
/* Parse SDP from incoming request */
|
||||
if (rdata->msg_info.msg->body) {
|
||||
status = pjmedia_sdp_parse(rdata->tp_info.pool,
|
||||
(char*)rdata->msg_info.msg->body->data,
|
||||
rdata->msg_info.msg->body->len, &offer);
|
||||
if (status == PJ_SUCCESS) {
|
||||
/* Validate */
|
||||
status = pjmedia_sdp_validate(offer);
|
||||
}
|
||||
pjsip_rdata_sdp_info *sdp_info;
|
||||
|
||||
sdp_info = pjsip_rdata_get_sdp_info(rdata);
|
||||
offer = sdp_info->sdp;
|
||||
|
||||
status = sdp_info->sdp_err;
|
||||
if (status==PJ_SUCCESS && sdp_info->sdp==NULL)
|
||||
status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
|
||||
|
||||
if (status != PJ_SUCCESS) {
|
||||
const pj_str_t reason = pj_str("Bad SDP");
|
||||
|
|
|
@ -142,6 +142,8 @@ PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
|
|||
{
|
||||
pj_bzero(msg_data, sizeof(*msg_data));
|
||||
pj_list_init(&msg_data->hdr_list);
|
||||
pjsip_media_type_init(&msg_data->multipart_ctype, NULL, NULL);
|
||||
pj_list_init(&msg_data->multipart_parts);
|
||||
}
|
||||
|
||||
PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
|
||||
|
@ -2225,6 +2227,37 @@ void pjsua_process_msg_data(pjsip_tx_data *tdata,
|
|||
&msg_data->msg_body);
|
||||
tdata->msg->body = body;
|
||||
}
|
||||
|
||||
/* Multipart */
|
||||
if (!pj_list_empty(&msg_data->multipart_parts) &&
|
||||
msg_data->multipart_ctype.type.slen)
|
||||
{
|
||||
pjsip_msg_body *bodies;
|
||||
pjsip_multipart_part *part;
|
||||
pj_str_t *boundary = NULL;
|
||||
|
||||
bodies = pjsip_multipart_create(tdata->pool,
|
||||
&msg_data->multipart_ctype,
|
||||
boundary);
|
||||
part = msg_data->multipart_parts.next;
|
||||
while (part != &msg_data->multipart_parts) {
|
||||
pjsip_multipart_part *part_copy;
|
||||
|
||||
part_copy = pjsip_multipart_clone_part(tdata->pool, part);
|
||||
pjsip_multipart_add_part(tdata->pool, bodies, part_copy);
|
||||
part = part->next;
|
||||
}
|
||||
|
||||
if (tdata->msg->body) {
|
||||
part = pjsip_multipart_create_part(tdata->pool);
|
||||
part->body = tdata->msg->body;
|
||||
pjsip_multipart_add_part(tdata->pool, bodies, part);
|
||||
|
||||
tdata->msg->body = NULL;
|
||||
}
|
||||
|
||||
tdata->msg->body = bodies;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/* $Id$ */
|
||||
/*
|
||||
* Copyright (C) 2008-2010 Teluu Inc. (http://www.teluu.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "test.h"
|
||||
#include <pjsip.h>
|
||||
#include <pjlib.h>
|
||||
|
||||
#define THIS_FILE ""
|
||||
|
||||
/*
|
||||
* multipart tests
|
||||
*/
|
||||
typedef pj_status_t (*verify_ptr)(pj_pool_t*,pjsip_msg_body*);
|
||||
|
||||
static pj_status_t verify1(pj_pool_t *pool, pjsip_msg_body *body);
|
||||
|
||||
static struct test_t
|
||||
{
|
||||
char *ctype;
|
||||
char *csubtype;
|
||||
char *boundary;
|
||||
const char *msg;
|
||||
verify_ptr verify;
|
||||
} p_tests[] =
|
||||
{
|
||||
{
|
||||
/* Content-type */
|
||||
"multipart", "mixed", "12345",
|
||||
|
||||
/* Body: */
|
||||
"This is the prolog, which should be ignored.\r\n"
|
||||
"--12345\r\n"
|
||||
"Content-Type: my/text\r\n"
|
||||
"\r\n"
|
||||
"Header and body\r\n"
|
||||
"--12345 \t\r\n"
|
||||
"Content-Type: hello/world\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n"
|
||||
"--12345\r\n"
|
||||
"\r\n"
|
||||
"Body only\r\n"
|
||||
"--12345\r\n"
|
||||
"Content-Type: multipart/mixed;boundary=6789\r\n"
|
||||
"\r\n"
|
||||
"Prolog of the subbody, should be ignored\r\n"
|
||||
"--6789\r\n"
|
||||
"\r\n"
|
||||
"Subbody\r\n"
|
||||
"--6789--\r\n"
|
||||
"Epilogue of the subbody, should be ignored\r\n"
|
||||
"--12345--\r\n"
|
||||
"This is epilogue, which should be ignored too",
|
||||
|
||||
&verify1
|
||||
}
|
||||
};
|
||||
|
||||
static void init_media_type(pjsip_media_type *mt,
|
||||
char *type, char *subtype, char *boundary)
|
||||
{
|
||||
static pjsip_param prm;
|
||||
|
||||
pjsip_media_type_init(mt, NULL, NULL);
|
||||
if (type) mt->type = pj_str(type);
|
||||
if (subtype) mt->subtype = pj_str(subtype);
|
||||
if (boundary) {
|
||||
pj_list_init(&prm);
|
||||
prm.name = pj_str("boundary");
|
||||
prm.value = pj_str(boundary);
|
||||
pj_list_push_back(&mt->param, &prm);
|
||||
}
|
||||
}
|
||||
|
||||
static int verify_part(pjsip_multipart_part *part,
|
||||
char *h_content_type,
|
||||
char *h_content_subtype,
|
||||
char *boundary,
|
||||
int h_content_length,
|
||||
const char *body)
|
||||
{
|
||||
pjsip_ctype_hdr *ctype_hdr = NULL;
|
||||
pjsip_clen_hdr *clen_hdr = NULL;
|
||||
pjsip_hdr *hdr;
|
||||
pj_str_t the_body;
|
||||
|
||||
hdr = part->hdr.next;
|
||||
while (hdr != &part->hdr) {
|
||||
if (hdr->type == PJSIP_H_CONTENT_TYPE)
|
||||
ctype_hdr = (pjsip_ctype_hdr*)hdr;
|
||||
else if (hdr->type == PJSIP_H_CONTENT_LENGTH)
|
||||
clen_hdr = (pjsip_clen_hdr*)hdr;
|
||||
hdr = hdr->next;
|
||||
}
|
||||
|
||||
if (h_content_type) {
|
||||
pjsip_media_type mt;
|
||||
|
||||
if (ctype_hdr == NULL)
|
||||
return -10;
|
||||
|
||||
init_media_type(&mt, h_content_type, h_content_subtype, boundary);
|
||||
|
||||
if (pjsip_media_type_cmp(&ctype_hdr->media, &mt) != 0)
|
||||
return -20;
|
||||
|
||||
} else {
|
||||
if (ctype_hdr)
|
||||
return -30;
|
||||
}
|
||||
|
||||
if (h_content_length >= 0) {
|
||||
if (clen_hdr == NULL)
|
||||
return -50;
|
||||
if (clen_hdr->len != h_content_length)
|
||||
return -60;
|
||||
} else {
|
||||
if (clen_hdr)
|
||||
return -70;
|
||||
}
|
||||
|
||||
the_body.ptr = (char*)part->body->data;
|
||||
the_body.slen = part->body->len;
|
||||
|
||||
if (pj_strcmp2(&the_body, body) != 0)
|
||||
return -90;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pj_status_t verify1(pj_pool_t *pool, pjsip_msg_body *body)
|
||||
{
|
||||
pjsip_media_type mt;
|
||||
pjsip_multipart_part *part;
|
||||
int rc;
|
||||
|
||||
/* Check content-type: "multipart/mixed;boundary=12345" */
|
||||
init_media_type(&mt, "multipart", "mixed", "12345");
|
||||
if (pjsip_media_type_cmp(&body->content_type, &mt) != 0)
|
||||
return -200;
|
||||
|
||||
/* First part:
|
||||
"Content-Type: my/text\r\n"
|
||||
"\r\n"
|
||||
"Header and body\r\n"
|
||||
*/
|
||||
part = pjsip_multipart_get_first_part(body);
|
||||
if (!part)
|
||||
return -210;
|
||||
if (verify_part(part, "my", "text", NULL, -1, "Header and body"))
|
||||
return -220;
|
||||
|
||||
/* Next part:
|
||||
"Content-Type: hello/world\r\n"
|
||||
"Content-Length: 0\r\n"
|
||||
"\r\n"
|
||||
*/
|
||||
part = pjsip_multipart_get_next_part(body, part);
|
||||
if (!part)
|
||||
return -230;
|
||||
if ((rc=verify_part(part, "hello", "world", NULL, 0, ""))!=0) {
|
||||
PJ_LOG(3,(THIS_FILE, " err: verify_part rc=%d", rc));
|
||||
return -240;
|
||||
}
|
||||
|
||||
/* Next part:
|
||||
"\r\n"
|
||||
"Body only\r\n"
|
||||
*/
|
||||
part = pjsip_multipart_get_next_part(body, part);
|
||||
if (!part)
|
||||
return -260;
|
||||
if (verify_part(part, NULL, NULL, NULL, -1, "Body only"))
|
||||
return -270;
|
||||
|
||||
/* Next part:
|
||||
"Content-Type: multipart/mixed;boundary=6789\r\n"
|
||||
"\r\n"
|
||||
"Prolog of the subbody, should be ignored\r\n"
|
||||
"--6789\r\n"
|
||||
"\r\n"
|
||||
"Subbody\r\n"
|
||||
"--6789--\r\n"
|
||||
"Epilogue of the subbody, should be ignored\r\n"
|
||||
|
||||
*/
|
||||
part = pjsip_multipart_get_next_part(body, part);
|
||||
if (!part)
|
||||
return -280;
|
||||
if ((rc=verify_part(part, "multipart", "mixed", "6789", -1,
|
||||
"Prolog of the subbody, should be ignored\r\n"
|
||||
"--6789\r\n"
|
||||
"\r\n"
|
||||
"Subbody\r\n"
|
||||
"--6789--\r\n"
|
||||
"Epilogue of the subbody, should be ignored"))!=0) {
|
||||
PJ_LOG(3,(THIS_FILE, " err: verify_part rc=%d", rc));
|
||||
return -290;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_test(void)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i=0; i<PJ_ARRAY_SIZE(p_tests); ++i) {
|
||||
pj_pool_t *pool;
|
||||
pjsip_media_type ctype;
|
||||
pjsip_msg_body *body;
|
||||
pj_str_t str;
|
||||
int rc;
|
||||
|
||||
pool = pjsip_endpt_create_pool(endpt, NULL, 512, 512);
|
||||
|
||||
init_media_type(&ctype, p_tests[i].ctype, p_tests[i].csubtype,
|
||||
p_tests[i].boundary);
|
||||
|
||||
pj_strdup2_with_null(pool, &str, p_tests[i].msg);
|
||||
body = pjsip_multipart_parse(pool, str.ptr, str.slen, &ctype, 0);
|
||||
if (!body)
|
||||
return -100;
|
||||
|
||||
if (p_tests[i].verify) {
|
||||
rc = p_tests[i].verify(pool, body);
|
||||
} else {
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
pj_pool_release(pool);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int multipart_test(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = parse_test();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
@ -301,6 +301,10 @@ int test_main(void)
|
|||
DO_TEST(msg_err_test());
|
||||
#endif
|
||||
|
||||
#if INCLUDE_MULTIPART_TEST
|
||||
DO_TEST(multipart_test());
|
||||
#endif
|
||||
|
||||
#if INCLUDE_TXDATA_TEST
|
||||
DO_TEST(txdata_test());
|
||||
#endif
|
||||
|
|
|
@ -56,6 +56,7 @@ extern pjsip_endpoint *endpt;
|
|||
|
||||
#define INCLUDE_URI_TEST INCLUDE_MESSAGING_GROUP
|
||||
#define INCLUDE_MSG_TEST INCLUDE_MESSAGING_GROUP
|
||||
#define INCLUDE_MULTIPART_TEST INCLUDE_MESSAGING_GROUP
|
||||
#define INCLUDE_TXDATA_TEST INCLUDE_MESSAGING_GROUP
|
||||
#define INCLUDE_TSX_BENCH INCLUDE_MESSAGING_GROUP
|
||||
#define INCLUDE_UDP_TEST INCLUDE_TRANSPORT_GROUP
|
||||
|
@ -71,6 +72,7 @@ extern pjsip_endpoint *endpt;
|
|||
int uri_test(void);
|
||||
int msg_test(void);
|
||||
int msg_err_test(void);
|
||||
int multipart_test(void);
|
||||
int txdata_test(void);
|
||||
int tsx_bench(void);
|
||||
int transport_udp_test(void);
|
||||
|
|
|
@ -107,7 +107,7 @@ class Dialog:
|
|||
msg = msg.replace("$BRANCH", branch)
|
||||
return msg
|
||||
|
||||
def create_req(self, method, sdp, branch="", extra_headers=""):
|
||||
def create_req(self, method, sdp, branch="", extra_headers="", body=""):
|
||||
if branch=="":
|
||||
self.cseq = self.cseq + 1
|
||||
msg = req_templ
|
||||
|
@ -119,10 +119,14 @@ class Dialog:
|
|||
if sdp!="":
|
||||
msg = msg.replace("$CONTENT_LENGTH", str(len(sdp)))
|
||||
msg = msg + "Content-Type: application/sdp\r\n"
|
||||
msg = msg + "\r\n"
|
||||
msg = msg + sdp
|
||||
elif body!="":
|
||||
msg = msg.replace("$CONTENT_LENGTH", str(len(body)))
|
||||
msg = msg + "\r\n"
|
||||
msg = msg + body
|
||||
else:
|
||||
msg = msg.replace("$CONTENT_LENGTH", "0")
|
||||
msg = msg + "\r\n"
|
||||
msg = msg + sdp
|
||||
return self.update_fields(msg)
|
||||
|
||||
def create_response(self, request, code, reason, to_tag=""):
|
||||
|
@ -138,9 +142,9 @@ class Dialog:
|
|||
response = response + line + "\r\n"
|
||||
return response
|
||||
|
||||
def create_invite(self, sdp, extra_headers=""):
|
||||
def create_invite(self, sdp, extra_headers="", body=""):
|
||||
self.inv_branch = str(random.random())
|
||||
return self.create_req("INVITE", sdp, branch=self.inv_branch, extra_headers=extra_headers)
|
||||
return self.create_req("INVITE", sdp, branch=self.inv_branch, extra_headers=extra_headers, body=body)
|
||||
|
||||
def create_ack(self, sdp="", extra_headers=""):
|
||||
return self.create_req("ACK", sdp, extra_headers=extra_headers, branch=self.inv_branch)
|
||||
|
@ -252,10 +256,12 @@ class SendtoCfg:
|
|||
resp_include = []
|
||||
# List of RE patterns that must NOT exist in response
|
||||
resp_exclude = []
|
||||
# Full (non-SDP) body
|
||||
body = ""
|
||||
# Constructor
|
||||
def __init__(self, name, pjsua_args, sdp, resp_code,
|
||||
resp_inc=[], resp_exc=[], use_tcp=False,
|
||||
extra_headers="", complete_msg="",
|
||||
extra_headers="", body="", complete_msg="",
|
||||
enable_buffer = False):
|
||||
self.complete_msg = complete_msg
|
||||
self.sdp = sdp
|
||||
|
@ -264,6 +270,7 @@ class SendtoCfg:
|
|||
self.resp_exclude = resp_exc
|
||||
self.use_tcp = use_tcp
|
||||
self.extra_headers = extra_headers
|
||||
self.body = body
|
||||
self.inst_param = cfg.InstanceParam("pjsua", pjsua_args)
|
||||
self.inst_param.enable_buffer = enable_buffer
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ def test_func(t):
|
|||
if len(cfg.complete_msg) != 0:
|
||||
req = dlg.update_fields(cfg.complete_msg)
|
||||
else:
|
||||
req = dlg.create_invite(cfg.sdp, cfg.extra_headers)
|
||||
req = dlg.create_invite(cfg.sdp, cfg.extra_headers, cfg.body)
|
||||
resp = dlg.send_request_wait(req, 10)
|
||||
if resp=="":
|
||||
raise TestError("Timed-out waiting for response")
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# $Id$
|
||||
import inc_sip as sip
|
||||
import inc_sdp as sdp
|
||||
|
||||
body = \
|
||||
"""
|
||||
--12345
|
||||
Content-Type: application/sdp
|
||||
|
||||
v=0
|
||||
o=- 0 0 IN IP4 127.0.0.1
|
||||
s=pjmedia
|
||||
c=IN IP4 127.0.0.1
|
||||
t=0 0
|
||||
m=audio 4000 RTP/AVP 0 101
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=sendrecv
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-15
|
||||
|
||||
--12345
|
||||
Content-Type: text/plain
|
||||
|
||||
Hi there this is definitely not SDP
|
||||
|
||||
--12345--
|
||||
"""
|
||||
|
||||
args = "--null-audio --auto-answer 200 --max-calls 1"
|
||||
extra_headers = "Content-Type: multipart/mixed; boundary=12345"
|
||||
include = ["v=0", "m=audio"]
|
||||
exclude = []
|
||||
|
||||
sendto_cfg = sip.SendtoCfg( "Valid multipart/mixed body containing SDP",
|
||||
pjsua_args=args, sdp="", resp_code=200,
|
||||
extra_headers=extra_headers, body=body,
|
||||
resp_inc=include, resp_exc=exclude)
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# $Id$
|
||||
import inc_sip as sip
|
||||
import inc_sdp as sdp
|
||||
|
||||
body = \
|
||||
"""
|
||||
This is the preamble. It is to be ignored, though it
|
||||
is a handy place for composition agents to include an
|
||||
explanatory note to non-MIME conformant readers.
|
||||
|
||||
--123:45
|
||||
Content-Type: text/plain
|
||||
|
||||
The first part is definitely not SDP
|
||||
|
||||
--123:45
|
||||
|
||||
This is implicitly typed plain US-ASCII text.
|
||||
It does NOT end with a linebreak.
|
||||
--123:45
|
||||
Content-Type: application/sdp
|
||||
|
||||
v=0
|
||||
o=- 0 0 IN IP4 127.0.0.1
|
||||
s=pjmedia
|
||||
c=IN IP4 127.0.0.1
|
||||
t=0 0
|
||||
m=audio 4000 RTP/AVP 0 101
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=sendrecv
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-15
|
||||
|
||||
--123:45--
|
||||
This is the epilogue. It is also to be ignored.
|
||||
"""
|
||||
|
||||
args = "--null-audio --auto-answer 200 --max-calls 1"
|
||||
extra_headers = "Content-Type: multipart/mixed; boundary=\"123:45\""
|
||||
include = ["v=0", "m=audio"]
|
||||
exclude = []
|
||||
|
||||
sendto_cfg = sip.SendtoCfg( "Valid but cluttered multipart/mixed body containing SDP",
|
||||
pjsua_args=args, sdp="", resp_code=200,
|
||||
extra_headers=extra_headers, body=body,
|
||||
resp_inc=include, resp_exc=exclude)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# $Id$
|
||||
import inc_sip as sip
|
||||
import inc_sdp as sdp
|
||||
|
||||
body = \
|
||||
"""
|
||||
--12345
|
||||
Content-Type: application/notsdp
|
||||
|
||||
v=0
|
||||
o=- 0 0 IN IP4 127.0.0.1
|
||||
s=pjmedia
|
||||
c=IN IP4 127.0.0.1
|
||||
t=0 0
|
||||
m=audio 4000 RTP/AVP 0 101
|
||||
a=rtpmap:0 PCMU/8000
|
||||
a=sendrecv
|
||||
a=rtpmap:101 telephone-event/8000
|
||||
a=fmtp:101 0-15
|
||||
|
||||
--12345
|
||||
Content-Type: text/plain
|
||||
|
||||
Hi there this is definitely not SDP
|
||||
|
||||
--12345--
|
||||
"""
|
||||
|
||||
args = "--null-audio --auto-answer 200 --max-calls 1"
|
||||
extra_headers = "Content-Type: multipart/mixed; boundary=12345"
|
||||
include = []
|
||||
exclude = []
|
||||
|
||||
sendto_cfg = sip.SendtoCfg( "Multipart/mixed body without SDP",
|
||||
pjsua_args=args, sdp="", resp_code=400,
|
||||
extra_headers=extra_headers, body=body,
|
||||
resp_inc=include, resp_exc=exclude)
|
||||
|
Loading…
Reference in New Issue