1
/* $Id: sip_multipart.c 4792 2014-03-13 09:33:32Z bennylp $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
#include <pjsip/sip_multipart.h>
20
#include <pjsip/sip_parser.h>
21
#include <pjlib-util/scanner.h>
22
#include <pj/assert.h>
25
#include <pj/except.h>
29
#include <pj/string.h>
31
#define THIS_FILE "sip_multipart.c"
33
#define IS_SPACE(c) ((c)==' ' || (c)=='\t')
36
# define TRACE_(x) PJ_LOG(4,x)
41
extern pj_bool_t pjsip_use_compact_form;
43
/* Type of "data" in multipart pjsip_msg_body */
47
pjsip_multipart_part part_head;
51
static int multipart_print_body(struct pjsip_msg_body *msg_body,
52
char *buf, pj_size_t size)
54
const struct multipart_data *m_data;
55
pj_str_t clen_hdr = { "Content-Length: ", 16};
56
pjsip_multipart_part *part;
57
char *p = buf, *end = buf+size;
59
#define SIZE_LEFT() (end-p)
61
m_data = (const struct multipart_data*)msg_body->data;
63
PJ_ASSERT_RETURN(m_data && !pj_list_empty(&m_data->part_head), PJ_EINVAL);
65
part = m_data->part_head.next;
66
while (part != &m_data->part_head) {
67
enum { CLEN_SPACE = 5 };
74
if (SIZE_LEFT() <= (m_data->boundary.slen+8) << 1)
76
*p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-';
77
pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen);
78
p += m_data->boundary.slen;
81
/* Print optional headers */
83
while (hdr != &part->hdr) {
84
int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p,
94
/* Automaticly adds Content-Type and Content-Length headers, only
95
* if content_type is set in the message body.
97
if (part->body && part->body->content_type.type.slen) {
98
pj_str_t ctype_hdr = { "Content-Type: ", 14};
99
const pjsip_media_type *media = &part->body->content_type;
101
if (pjsip_use_compact_form) {
102
ctype_hdr.ptr = "c: ";
106
/* Add Content-Type header. */
107
if ( (end-p) < 24 + media->type.slen + media->subtype.slen) {
110
pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
112
p += pjsip_media_type_print(p, (unsigned)(end-p), media);
116
/* Add Content-Length header. */
117
if ((end-p) < clen_hdr.slen + 12 + 2) {
120
pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
123
/* Print blanks after "Content-Length:", this is where we'll put
124
* the content length value after we know the length of the
127
pj_memset(p, ' ', CLEN_SPACE);
135
*p++ = 13; *p++ = 10;
138
pj_assert(part->body != NULL);
140
int printed = part->body->print_body(part->body, p, SIZE_LEFT());
145
/* Now that we have the length of the body, print this to the
146
* Content-Length header.
152
len = pj_utoa(printed, tmp);
153
if (len > CLEN_SPACE) len = CLEN_SPACE;
154
pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len);
161
/* Print closing delimiter */
162
if (SIZE_LEFT() < m_data->boundary.slen+8)
164
*p++ = 13; *p++ = 10; *p++ = '-'; *p++ = '-';
165
pj_memcpy(p, m_data->boundary.ptr, m_data->boundary.slen);
166
p += m_data->boundary.slen;
167
*p++ = '-'; *p++ = '-'; *p++ = 13; *p++ = 10;
171
return (int)(p - buf);
174
static void* multipart_clone_data(pj_pool_t *pool, const void *data,
177
const struct multipart_data *src;
178
struct multipart_data *dst;
179
const pjsip_multipart_part *src_part;
183
src = (const struct multipart_data*) data;
184
dst = PJ_POOL_ALLOC_T(pool, struct multipart_data);
185
pj_list_init(&dst->part_head);
187
pj_strdup(pool, &dst->boundary, &src->boundary);
189
src_part = src->part_head.next;
190
while (src_part != &src->part_head) {
191
pjsip_multipart_part *dst_part;
192
const pjsip_hdr *src_hdr;
194
dst_part = pjsip_multipart_create_part(pool);
196
src_hdr = src_part->hdr.next;
197
while (src_hdr != &src_part->hdr) {
198
pjsip_hdr *dst_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, src_hdr);
199
pj_list_push_back(&dst_part->hdr, dst_hdr);
200
src_hdr = src_hdr->next;
203
dst_part->body = pjsip_msg_body_clone(pool, src_part->body);
205
pj_list_push_back(&dst->part_head, dst_part);
207
src_part = src_part->next;
214
* Create an empty multipart body.
216
PJ_DEF(pjsip_msg_body*) pjsip_multipart_create( pj_pool_t *pool,
217
const pjsip_media_type *ctype,
218
const pj_str_t *boundary)
220
pjsip_msg_body *body;
221
pjsip_param *ctype_param;
222
struct multipart_data *mp_data;
223
pj_str_t STR_BOUNDARY = { "boundary", 8 };
225
PJ_ASSERT_RETURN(pool, NULL);
227
body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
230
if (ctype && ctype->type.slen) {
231
pjsip_media_type_cp(pool, &body->content_type, ctype);
233
pj_str_t STR_MULTIPART = {"multipart", 9};
234
pj_str_t STR_MIXED = { "mixed", 5 };
236
pjsip_media_type_init(&body->content_type,
237
&STR_MULTIPART, &STR_MIXED);
241
mp_data = PJ_POOL_ZALLOC_T(pool, struct multipart_data);
242
pj_list_init(&mp_data->part_head);
244
pj_strdup(pool, &mp_data->boundary, boundary);
246
pj_create_unique_string(pool, &mp_data->boundary);
248
body->data = mp_data;
250
/* Add ";boundary" parameter to content_type parameter. */
251
ctype_param = pjsip_param_find(&body->content_type.param, &STR_BOUNDARY);
253
ctype_param = PJ_POOL_ALLOC_T(pool, pjsip_param);
254
ctype_param->name = STR_BOUNDARY;
255
pj_list_push_back(&body->content_type.param, ctype_param);
257
ctype_param->value = mp_data->boundary;
259
/* function pointers */
260
body->print_body = &multipart_print_body;
261
body->clone_data = &multipart_clone_data;
267
* Create an empty multipart part.
269
PJ_DEF(pjsip_multipart_part*) pjsip_multipart_create_part(pj_pool_t *pool)
271
pjsip_multipart_part *mp;
273
mp = PJ_POOL_ZALLOC_T(pool, pjsip_multipart_part);
274
pj_list_init(&mp->hdr);
283
PJ_DEF(pjsip_multipart_part*)
284
pjsip_multipart_clone_part(pj_pool_t *pool,
285
const pjsip_multipart_part *src)
287
pjsip_multipart_part *dst;
288
const pjsip_hdr *hdr;
290
dst = pjsip_multipart_create_part(pool);
293
while (hdr != &src->hdr) {
294
pj_list_push_back(&dst->hdr, pjsip_hdr_clone(pool, hdr));
298
dst->body = pjsip_msg_body_clone(pool, src->body);
305
* Add a part into multipart bodies.
307
PJ_DEF(pj_status_t) pjsip_multipart_add_part( pj_pool_t *pool,
309
pjsip_multipart_part *part)
311
struct multipart_data *m_data;
313
/* All params must be specified */
314
PJ_ASSERT_RETURN(pool && mp && part, PJ_EINVAL);
316
/* mp must really point to an actual multipart msg body */
317
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, PJ_EINVAL);
319
/* The multipart part must contain a valid message body */
320
PJ_ASSERT_RETURN(part->body && part->body->print_body, PJ_EINVAL);
322
m_data = (struct multipart_data*)mp->data;
323
pj_list_push_back(&m_data->part_head, part);
331
* Get the first part of multipart bodies.
333
PJ_DEF(pjsip_multipart_part*)
334
pjsip_multipart_get_first_part(const pjsip_msg_body *mp)
336
struct multipart_data *m_data;
338
/* Must specify mandatory params */
339
PJ_ASSERT_RETURN(mp, NULL);
341
/* mp must really point to an actual multipart msg body */
342
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
344
m_data = (struct multipart_data*)mp->data;
345
if (pj_list_empty(&m_data->part_head))
348
return m_data->part_head.next;
352
* Get the next part after the specified part.
354
PJ_DEF(pjsip_multipart_part*)
355
pjsip_multipart_get_next_part(const pjsip_msg_body *mp,
356
pjsip_multipart_part *part)
358
struct multipart_data *m_data;
360
/* Must specify mandatory params */
361
PJ_ASSERT_RETURN(mp && part, NULL);
363
/* mp must really point to an actual multipart msg body */
364
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
366
m_data = (struct multipart_data*)mp->data;
368
/* the part parameter must be really member of the list */
369
PJ_ASSERT_RETURN(pj_list_find_node(&m_data->part_head, part) != NULL,
372
if (part->next == &m_data->part_head)
379
* Find a body inside multipart bodies which has the specified content type.
381
PJ_DEF(pjsip_multipart_part*)
382
pjsip_multipart_find_part( const pjsip_msg_body *mp,
383
const pjsip_media_type *content_type,
384
const pjsip_multipart_part *start)
386
struct multipart_data *m_data;
387
pjsip_multipart_part *part;
389
/* Must specify mandatory params */
390
PJ_ASSERT_RETURN(mp && content_type, NULL);
392
/* mp must really point to an actual multipart msg body */
393
PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL);
395
m_data = (struct multipart_data*)mp->data;
400
part = m_data->part_head.next;
402
while (part != &m_data->part_head) {
403
if (pjsip_media_type_cmp(&part->body->content_type,
414
/* Parse a multipart part. "pct" is parent content-type */
415
static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool,
418
const pjsip_media_type *pct)
420
pjsip_multipart_part *part = pjsip_multipart_create_part(pool);
421
char *p = start, *end = start+len, *end_hdr = NULL, *start_body = NULL;
422
pjsip_ctype_hdr *ctype_hdr = NULL;
424
TRACE_((THIS_FILE, "Parsing part: begin--\n%.*s\n--end",
427
/* Find the end of header area, by looking at an empty line */
429
while (p!=end && *p!='\n') ++p;
434
if ((p==start) || (p==start+1 && *(p-1)=='\r')) {
435
/* Empty header section */
439
} else if (p==end-1) {
440
/* Empty body section */
443
} else if ((p>=start+1 && *(p-1)=='\n') ||
444
(p>=start+2 && *(p-1)=='\r' && *(p-2)=='\n'))
447
end_hdr = (*(p-1)=='\r') ? (p-1) : p;
455
/* Parse the headers */
456
if (end_hdr-start > 0) {
460
status = pjsip_parse_headers(pool, start, end_hdr-start,
462
if (status != PJ_SUCCESS) {
463
PJ_PERROR(2,(THIS_FILE, status, "Warning: error parsing multipart"
467
/* Find Content-Type header */
468
hdr = part->hdr.next;
469
while (hdr != &part->hdr) {
470
TRACE_((THIS_FILE, "Header parsed: %.*s", (int)hdr->name.slen,
472
if (hdr->type == PJSIP_H_CONTENT_TYPE) {
473
ctype_hdr = (pjsip_ctype_hdr*)hdr;
479
/* Assign the body */
480
part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body);
482
pjsip_media_type_cp(pool, &part->body->content_type, &ctype_hdr->media);
483
} else if (pct && pj_stricmp2(&pct->subtype, "digest")==0) {
484
part->body->content_type.type = pj_str("message");
485
part->body->content_type.subtype = pj_str("rfc822");
487
part->body->content_type.type = pj_str("text");
488
part->body->content_type.subtype = pj_str("plain");
491
if (start_body < end) {
492
part->body->data = start_body;
493
part->body->len = (unsigned)(end - start_body);
495
part->body->data = (void*)"";
498
TRACE_((THIS_FILE, "Body parsed: \"%.*s\"", (int)part->body->len,
500
part->body->print_body = &pjsip_print_text_body;
501
part->body->clone_data = &pjsip_clone_text_data;
506
/* Public function to parse multipart message bodies into its parts */
507
PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
508
char *buf, pj_size_t len,
509
const pjsip_media_type *ctype,
512
pj_str_t boundary, delim;
513
char *curptr, *endptr;
514
const pjsip_param *ctype_param;
515
const pj_str_t STR_BOUNDARY = { "boundary", 8 };
516
pjsip_msg_body *body = NULL;
518
PJ_ASSERT_RETURN(pool && buf && len && ctype && !options, NULL);
520
TRACE_((THIS_FILE, "Started parsing multipart body"));
522
/* Get the boundary value in the ctype */
525
ctype_param = pjsip_param_find(&ctype->param, &STR_BOUNDARY);
527
boundary = ctype_param->value;
528
if (boundary.slen>2 && *boundary.ptr=='"') {
533
TRACE_((THIS_FILE, "Boundary is specified: '%.*s'", (int)boundary.slen,
537
if (!boundary.slen) {
538
/* Boundary not found or not specified. Try to be clever, get
539
* the boundary from the body.
541
char *p=buf, *end=buf+len;
543
PJ_LOG(4,(THIS_FILE, "Warning: boundary parameter not found or "
544
"not specified when parsing multipart body"));
546
/* Find the first "--". This "--" must be right after a CRLF, unless
547
* it really appears at the start of the buffer.
550
while (p!=end && *p!='-') ++p;
551
if (p!=end && *(p+1)=='-' &&
552
((p>buf && *(p-1)=='\n') || (p==buf)))
562
/* Unable to determine boundary. Maybe this is not a multipart
565
PJ_LOG(4,(THIS_FILE, "Error: multipart boundary not specified and"
566
" unable to calculate from the body"));
571
while (p!=end && !pj_isspace(*p)) ++p;
572
boundary.slen = p - boundary.ptr;
574
TRACE_((THIS_FILE, "Boundary is calculated: '%.*s'",
575
(int)boundary.slen, boundary.ptr));
578
/* Build the delimiter:
579
* delimiter = "--" boundary
581
delim.slen = boundary.slen+2;
582
delim.ptr = (char*)pj_pool_alloc(pool, (int)delim.slen);
585
pj_memcpy(delim.ptr+2, boundary.ptr, boundary.slen);
587
/* Start parsing the body, skip until the first delimiter. */
593
body.ptr = buf; body.slen = len;
594
curptr = pj_strstr(&body, &delim);
599
body = pjsip_multipart_create(pool, ctype, &boundary);
602
char *start_body, *end_body;
603
pjsip_multipart_part *part;
605
/* Eat the boundary */
606
curptr += delim.slen;
607
if (*curptr=='-' && curptr<endptr-1 && *(curptr+1)=='-') {
608
/* Found the closing delimiter */
612
/* Optional whitespace after delimiter */
613
while (curptr!=endptr && IS_SPACE(*curptr)) ++curptr;
615
if (*curptr=='\r') ++curptr;
617
/* Expecting a newline here */
622
/* We now in the start of the body */
625
/* Find the next delimiter */
629
subbody.ptr = curptr; subbody.slen = endptr - curptr;
630
curptr = pj_strstr(&subbody, &delim);
632
/* We're really expecting end delimiter to be found. */
639
/* The newline preceeding the delimiter is conceptually part of
640
* the delimiter, so trim it from the body.
642
if (*(end_body-1) == '\n')
644
if (*(end_body-1) == '\r')
647
/* Now that we have determined the part's boundary, parse it
648
* to get the header and body part of the part.
650
part = parse_multipart_part(pool, start_body, end_body - start_body,
653
pjsip_multipart_add_part(pool, body, part);