📄 sip_msg.c
字号:
/* $Id: sip_msg.c 1121 2007-04-01 22:58:47Z bennylp $ */
/*
* Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
*
* 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_msg.h>
#include <pjsip/sip_parser.h>
#include <pjsip/print_util.h>
#include <pjsip/sip_errno.h>
#include <pj/string.h>
#include <pj/pool.h>
#include <pj/assert.h>
const pjsip_method
pjsip_invite_method = { PJSIP_INVITE_METHOD, { "INVITE",6 } },
pjsip_cancel_method = { PJSIP_CANCEL_METHOD, { "CANCEL",6 } },
pjsip_ack_method = { PJSIP_ACK_METHOD, { "ACK",3} },
pjsip_bye_method = { PJSIP_BYE_METHOD, { "BYE",3} },
pjsip_register_method = { PJSIP_REGISTER_METHOD, { "REGISTER",8} },
pjsip_options_method = { PJSIP_OPTIONS_METHOD, { "OPTIONS",7} };
static const pj_str_t *method_names[] =
{
&pjsip_invite_method.name,
&pjsip_cancel_method.name,
&pjsip_ack_method.name,
&pjsip_bye_method.name,
&pjsip_register_method.name,
&pjsip_options_method.name
};
const pj_str_t pjsip_hdr_names[] =
{
{ "Accept", 6 }, // PJSIP_H_ACCEPT,
{ "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING,
{ "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE,
{ "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO,
{ "Allow", 5 }, // PJSIP_H_ALLOW,
{ "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO,
{ "Authorization", 13 }, // PJSIP_H_AUTHORIZATION,
{ "Call-ID", 7 }, // PJSIP_H_CALL_ID,
{ "Call-Info", 9 }, // PJSIP_H_CALL_INFO,
{ "Contact", 7 }, // PJSIP_H_CONTACT,
{ "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION,
{ "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING,
{ "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE,
{ "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH,
{ "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE,
{ "CSeq", 4 }, // PJSIP_H_CSEQ,
{ "Date", 4 }, // PJSIP_H_DATE,
{ "Error-Info", 10 }, // PJSIP_H_ERROR_INFO,
{ "Expires", 7 }, // PJSIP_H_EXPIRES,
{ "From", 4 }, // PJSIP_H_FROM,
{ "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO,
{ "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS,
{ "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION,
{ "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES,
{ "Organization", 12 }, // PJSIP_H_ORGANIZATION,
{ "Priority", 8 }, // PJSIP_H_PRIORITY,
{ "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE,
{ "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION,
{ "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE,
{ "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE,
{ "Reply-To", 8 }, // PJSIP_H_REPLY_TO,
{ "Require", 7 }, // PJSIP_H_REQUIRE,
{ "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER,
{ "Route", 5 }, // PJSIP_H_ROUTE,
{ "Server", 6 }, // PJSIP_H_SERVER,
{ "Subject", 7 }, // PJSIP_H_SUBJECT,
{ "Supported", 9 }, // PJSIP_H_SUPPORTED,
{ "Timestamp", 9 }, // PJSIP_H_TIMESTAMP,
{ "To", 2 }, // PJSIP_H_TO,
{ "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED,
{ "User-Agent", 10 }, // PJSIP_H_USER_AGENT,
{ "Via", 3 }, // PJSIP_H_VIA,
{ "Warning", 7 }, // PJSIP_H_WARNING,
{ "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE,
{ "_Unknown-Header", 15 }, // PJSIP_H_OTHER,
};
static pj_str_t status_phrase[710];
static int print_media_type(char *buf, const pjsip_media_type *media);
static int init_status_phrase()
{
int i;
pj_str_t default_reason_phrase = { "Default status message", 22};
for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i)
status_phrase[i] = default_reason_phrase;
pj_strset2( &status_phrase[100], "Trying");
pj_strset2( &status_phrase[180], "Ringing");
pj_strset2( &status_phrase[181], "Call Is Being Forwarded");
pj_strset2( &status_phrase[182], "Queued");
pj_strset2( &status_phrase[183], "Session Progress");
pj_strset2( &status_phrase[200], "OK");
pj_strset2( &status_phrase[202], "Accepted");
pj_strset2( &status_phrase[300], "Multiple Choices");
pj_strset2( &status_phrase[301], "Moved Permanently");
pj_strset2( &status_phrase[302], "Moved Temporarily");
pj_strset2( &status_phrase[305], "Use Proxy");
pj_strset2( &status_phrase[380], "Alternative Service");
pj_strset2( &status_phrase[400], "Bad Request");
pj_strset2( &status_phrase[401], "Unauthorized");
pj_strset2( &status_phrase[402], "Payment Required");
pj_strset2( &status_phrase[403], "Forbidden");
pj_strset2( &status_phrase[404], "Not Found");
pj_strset2( &status_phrase[405], "Method Not Allowed");
pj_strset2( &status_phrase[406], "Not Acceptable");
pj_strset2( &status_phrase[407], "Proxy Authentication Required");
pj_strset2( &status_phrase[408], "Request Timeout");
pj_strset2( &status_phrase[410], "Gone");
pj_strset2( &status_phrase[413], "Request Entity Too Large");
pj_strset2( &status_phrase[414], "Request URI Too Long");
pj_strset2( &status_phrase[415], "Unsupported Media Type");
pj_strset2( &status_phrase[416], "Unsupported URI Scheme");
pj_strset2( &status_phrase[420], "Bad Extension");
pj_strset2( &status_phrase[421], "Extension Required");
pj_strset2( &status_phrase[422], "Session Timer Too Small");
pj_strset2( &status_phrase[423], "Interval Too Brief");
pj_strset2( &status_phrase[480], "Temporarily Unavailable");
pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist");
pj_strset2( &status_phrase[482], "Loop Detected");
pj_strset2( &status_phrase[483], "Too Many Hops");
pj_strset2( &status_phrase[484], "Address Incompleted");
pj_strset2( &status_phrase[485], "Ambiguous");
pj_strset2( &status_phrase[486], "Busy Here");
pj_strset2( &status_phrase[487], "Request Terminated");
pj_strset2( &status_phrase[488], "Not Acceptable Here");
pj_strset2( &status_phrase[489], "Bad Event");
pj_strset2( &status_phrase[490], "Request Updated");
pj_strset2( &status_phrase[491], "Request Pending");
pj_strset2( &status_phrase[493], "Undecipherable");
pj_strset2( &status_phrase[500], "Internal Server Error");
pj_strset2( &status_phrase[501], "Not Implemented");
pj_strset2( &status_phrase[502], "Bad Gateway");
pj_strset2( &status_phrase[503], "Service Unavailable");
pj_strset2( &status_phrase[504], "Server Timeout");
pj_strset2( &status_phrase[505], "Version Not Supported");
pj_strset2( &status_phrase[513], "Message Too Large");
pj_strset2( &status_phrase[580], "Precondition Failure");
pj_strset2( &status_phrase[600], "Busy Everywhere");
pj_strset2( &status_phrase[603], "Decline");
pj_strset2( &status_phrase[604], "Does Not Exist Anywhere");
pj_strset2( &status_phrase[606], "Not Acceptable");
pj_strset2( &status_phrase[701], "No response from destination server");
pj_strset2( &status_phrase[702], "Unable to resolve destination server");
pj_strset2( &status_phrase[703], "Error sending message to destination server");
return 1;
}
///////////////////////////////////////////////////////////////////////////////
/*
* Method.
*/
PJ_DEF(void) pjsip_method_init( pjsip_method *m,
pj_pool_t *pool,
const pj_str_t *str)
{
pj_str_t dup;
pjsip_method_init_np(m, pj_strdup(pool, &dup, str));
}
PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
{
pj_assert(me < PJSIP_OTHER_METHOD);
m->id = me;
m->name = *method_names[me];
}
PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
pj_str_t *str)
{
int i;
for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
if (pj_memcmp(str->ptr, method_names[i]->ptr, str->slen)==0 ||
pj_stricmp(str, method_names[i])==0)
{
m->id = (pjsip_method_e)i;
m->name = *method_names[i];
return;
}
}
m->id = PJSIP_OTHER_METHOD;
m->name = *str;
}
PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool,
pjsip_method *method,
const pjsip_method *rhs )
{
method->id = rhs->id;
if (rhs->id != PJSIP_OTHER_METHOD) {
method->name = rhs->name;
} else {
pj_strdup(pool, &method->name, &rhs->name);
}
}
PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2)
{
if (m1->id == m2->id) {
if (m1->id != PJSIP_OTHER_METHOD)
return 0;
/* Method comparison is case sensitive! */
return pj_strcmp(&m1->name, &m2->name);
}
return ( m1->id < m2->id ) ? -1 : 1;
}
///////////////////////////////////////////////////////////////////////////////
/*
* Message.
*/
PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
{
pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg));
pj_list_init(&msg->hdr);
msg->type = type;
msg->body = NULL;
return msg;
}
PJ_DEF(pjsip_msg*) pjsip_msg_clone( pj_pool_t *pool, const pjsip_msg *src)
{
pjsip_msg *dst;
const pjsip_hdr *sh;
dst = pjsip_msg_create(pool, src->type);
/* Clone request/status line */
if (src->type == PJSIP_REQUEST_MSG) {
pjsip_method_copy(pool, &dst->line.req.method, &src->line.req.method);
dst->line.req.uri = pjsip_uri_clone(pool, src->line.req.uri);
} else {
dst->line.status.code = src->line.status.code;
pj_strdup(pool, &dst->line.status.reason, &src->line.status.reason);
}
/* Clone headers */
sh = src->hdr.next;
while (sh != &src->hdr) {
pjsip_hdr *dh = pjsip_hdr_clone(pool, sh);
pjsip_msg_add_hdr(dst, dh);
sh = sh->next;
}
/* Clone message body */
if (src->body) {
dst->body = pjsip_msg_body_clone(pool, src->body);
}
return dst;
}
PJ_DEF(void*) pjsip_msg_find_hdr( const pjsip_msg *msg,
pjsip_hdr_e hdr_type, const void *start)
{
const pjsip_hdr *hdr=start, *end=&msg->hdr;
if (hdr == NULL) {
hdr = msg->hdr.next;
}
for (; hdr!=end; hdr = hdr->next) {
if (hdr->type == hdr_type)
return (void*)hdr;
}
return NULL;
}
PJ_DEF(void*) pjsip_msg_find_hdr_by_name( const pjsip_msg *msg,
const pj_str_t *name,
const void *start)
{
const pjsip_hdr *hdr=start, *end=&msg->hdr;
if (hdr == NULL) {
hdr = msg->hdr.next;
}
for (; hdr!=end; hdr = hdr->next) {
if (hdr->type < PJSIP_H_OTHER) {
if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
return (void*)hdr;
} else {
if (pj_stricmp(&hdr->name, name) == 0)
return (void*)hdr;
}
}
return NULL;
}
PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
pjsip_hdr_e hdr_type, void *start)
{
pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start);
if (hdr) {
pj_list_erase(hdr);
}
return hdr;
}
PJ_DEF(pj_ssize_t) pjsip_msg_print( const pjsip_msg *msg,
char *buf, pj_size_t size)
{
char *p=buf, *end=buf+size;
int len;
pjsip_hdr *hdr;
pj_str_t clen_hdr = { "Content-Length: ", 16};
/* Get a wild guess on how many bytes are typically needed.
* We'll check this later in detail, but this serves as a quick check.
*/
if (size < 256)
return -1;
/* Print request line or status line depending on message type */
if (msg->type == PJSIP_REQUEST_MSG) {
pjsip_uri *uri;
/* Add method. */
len = msg->line.req.method.name.slen;
pj_memcpy(p, msg->line.req.method.name.ptr, len);
p += len;
*p++ = ' ';
/* Add URI */
uri = pjsip_uri_get_uri(msg->line.req.uri);
len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p);
if (len < 1)
return -1;
p += len;
/* Add ' SIP/2.0' */
if (end-p < 16)
return -1;
pj_memcpy(p, " SIP/2.0\r\n", 10);
p += 10;
} else {
/* Add 'SIP/2.0 ' */
pj_memcpy(p, "SIP/2.0 ", 8);
p += 8;
/* Add status code. */
len = pj_utoa(msg->line.status.code, p);
p += len;
*p++ = ' ';
/* Add reason text. */
len = msg->line.status.reason.slen;
pj_memcpy(p, msg->line.status.reason.ptr, len );
p += len;
/* Add newline. */
*p++ = '\r';
*p++ = '\n';
}
/* Print each of the headers. */
for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
len = (*hdr->vptr->print_on)(hdr, p, end-p);
if (len < 1)
return -1;
p += len;
if (p+3 >= end)
return -1;
*p++ = '\r';
*p++ = '\n';
}
/* Process message body. */
if (msg->body) {
enum { CLEN_SPACE = 5 };
char *clen_pos = NULL;
/* Automaticly adds Content-Type and Content-Length headers, only
* if content_type is set in the message body.
*/
if (msg->body->content_type.type.slen) {
pj_str_t ctype_hdr = { "Content-Type: ", 14};
const pjsip_media_type *media = &msg->body->content_type;
/* Add Content-Type header. */
if ( (end-p) < 24 + media->type.slen + media->subtype.slen +
media->param.slen)
{
return -1;
}
pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
p += ctype_hdr.slen;
p += print_media_type(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';
}
/* Add blank newline. */
*p++ = '\r';
*p++ = '\n';
/* Print the message body itself. */
len = (*msg->body->print_body)(msg->body, p, end-p);
if (len < 0) {
return -1;
}
p += len;
/* Now that we have the length of the body, print this to the
* Content-Length header.
*/
if (clen_pos) {
char tmp[16];
len = pj_utoa(len, tmp);
if (len > CLEN_SPACE) len = CLEN_SPACE;
pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len);
}
} else {
/* There's no message body.
* Add Content-Length with zero value.
*/
if ((end-p) < clen_hdr.slen+8) {
return -1;
}
pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
p += clen_hdr.slen;
*p++ = ' ';
*p++ = '0';
*p++ = '\r';
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -