Tristan Matthews | 0a329cc | 2013-07-17 13:20:14 -0400 | [diff] [blame] | 1 | /* $Id$ */ |
| 2 | /* |
| 3 | * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) |
| 4 | * |
| 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. |
| 9 | * |
| 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. |
| 14 | * |
| 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 |
| 18 | */ |
| 19 | #include <pjsip/sip_multipart.h> |
| 20 | #include <pjsip/sip_parser.h> |
| 21 | #include <pjlib-util/scanner.h> |
| 22 | #include <pj/assert.h> |
| 23 | #include <pj/ctype.h> |
| 24 | #include <pj/errno.h> |
| 25 | #include <pj/except.h> |
| 26 | #include <pj/guid.h> |
| 27 | #include <pj/log.h> |
| 28 | #include <pj/pool.h> |
| 29 | #include <pj/string.h> |
| 30 | |
| 31 | #define THIS_FILE "sip_multipart.c" |
| 32 | |
| 33 | #define IS_SPACE(c) ((c)==' ' || (c)=='\t') |
| 34 | |
| 35 | #if 0 |
| 36 | # define TRACE_(x) PJ_LOG(4,x) |
| 37 | #else |
| 38 | # define TRACE_(x) |
| 39 | #endif |
| 40 | |
| 41 | extern pj_bool_t pjsip_use_compact_form; |
| 42 | |
| 43 | /* Type of "data" in multipart pjsip_msg_body */ |
| 44 | struct multipart_data |
| 45 | { |
| 46 | pj_str_t boundary; |
| 47 | pjsip_multipart_part part_head; |
| 48 | }; |
| 49 | |
| 50 | |
| 51 | static int multipart_print_body(struct pjsip_msg_body *msg_body, |
| 52 | char *buf, pj_size_t size) |
| 53 | { |
| 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; |
| 58 | |
| 59 | #define SIZE_LEFT() (end-p) |
| 60 | |
| 61 | m_data = (const struct multipart_data*)msg_body->data; |
| 62 | |
| 63 | PJ_ASSERT_RETURN(m_data && !pj_list_empty(&m_data->part_head), PJ_EINVAL); |
| 64 | |
| 65 | part = m_data->part_head.next; |
| 66 | while (part != &m_data->part_head) { |
| 67 | enum { CLEN_SPACE = 5 }; |
| 68 | char *clen_pos; |
| 69 | const pjsip_hdr *hdr; |
| 70 | |
| 71 | clen_pos = NULL; |
| 72 | |
| 73 | /* Print delimiter */ |
| 74 | if (SIZE_LEFT() <= (m_data->boundary.slen+8) << 1) |
| 75 | return -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; |
| 79 | *p++ = 13; *p++ = 10; |
| 80 | |
| 81 | /* Print optional headers */ |
| 82 | hdr = part->hdr.next; |
| 83 | while (hdr != &part->hdr) { |
| 84 | int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, |
| 85 | SIZE_LEFT()-2); |
| 86 | if (printed < 0) |
| 87 | return -1; |
| 88 | p += printed; |
| 89 | *p++ = '\r'; |
| 90 | *p++ = '\n'; |
| 91 | hdr = hdr->next; |
| 92 | } |
| 93 | |
| 94 | /* Automaticly adds Content-Type and Content-Length headers, only |
| 95 | * if content_type is set in the message body. |
| 96 | */ |
| 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; |
| 100 | |
| 101 | if (pjsip_use_compact_form) { |
| 102 | ctype_hdr.ptr = "c: "; |
| 103 | ctype_hdr.slen = 3; |
| 104 | } |
| 105 | |
| 106 | /* Add Content-Type header. */ |
| 107 | if ( (end-p) < 24 + media->type.slen + media->subtype.slen) { |
| 108 | return -1; |
| 109 | } |
| 110 | pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); |
| 111 | p += ctype_hdr.slen; |
| 112 | p += pjsip_media_type_print(p, (unsigned)(end-p), media); |
| 113 | *p++ = '\r'; |
| 114 | *p++ = '\n'; |
| 115 | |
| 116 | /* Add Content-Length header. */ |
| 117 | if ((end-p) < clen_hdr.slen + 12 + 2) { |
| 118 | return -1; |
| 119 | } |
| 120 | pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); |
| 121 | p += clen_hdr.slen; |
| 122 | |
| 123 | /* Print blanks after "Content-Length:", this is where we'll put |
| 124 | * the content length value after we know the length of the |
| 125 | * body. |
| 126 | */ |
| 127 | pj_memset(p, ' ', CLEN_SPACE); |
| 128 | clen_pos = p; |
| 129 | p += CLEN_SPACE; |
| 130 | *p++ = '\r'; |
| 131 | *p++ = '\n'; |
| 132 | } |
| 133 | |
| 134 | /* Empty newline */ |
| 135 | *p++ = 13; *p++ = 10; |
| 136 | |
| 137 | /* Print the body */ |
| 138 | pj_assert(part->body != NULL); |
| 139 | if (part->body) { |
| 140 | int printed = part->body->print_body(part->body, p, SIZE_LEFT()); |
| 141 | if (printed < 0) |
| 142 | return -1; |
| 143 | p += printed; |
| 144 | |
| 145 | /* Now that we have the length of the body, print this to the |
| 146 | * Content-Length header. |
| 147 | */ |
| 148 | if (clen_pos) { |
| 149 | char tmp[16]; |
| 150 | int len; |
| 151 | |
| 152 | len = pj_utoa(printed, tmp); |
| 153 | if (len > CLEN_SPACE) len = CLEN_SPACE; |
| 154 | pj_memcpy(clen_pos+CLEN_SPACE-len, tmp, len); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | part = part->next; |
| 159 | } |
| 160 | |
| 161 | /* Print closing delimiter */ |
| 162 | if (SIZE_LEFT() < m_data->boundary.slen+8) |
| 163 | return -1; |
| 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; |
| 168 | |
| 169 | #undef SIZE_LEFT |
| 170 | |
| 171 | return (int)(p - buf); |
| 172 | } |
| 173 | |
| 174 | static void* multipart_clone_data(pj_pool_t *pool, const void *data, |
| 175 | unsigned len) |
| 176 | { |
| 177 | const struct multipart_data *src; |
| 178 | struct multipart_data *dst; |
| 179 | const pjsip_multipart_part *src_part; |
| 180 | |
| 181 | PJ_UNUSED_ARG(len); |
| 182 | |
| 183 | src = (const struct multipart_data*) data; |
| 184 | dst = PJ_POOL_ALLOC_T(pool, struct multipart_data); |
| 185 | |
| 186 | pj_strdup(pool, &dst->boundary, &src->boundary); |
| 187 | |
| 188 | src_part = src->part_head.next; |
| 189 | while (src_part != &src->part_head) { |
| 190 | pjsip_multipart_part *dst_part; |
| 191 | const pjsip_hdr *src_hdr; |
| 192 | |
| 193 | dst_part = pjsip_multipart_create_part(pool); |
| 194 | |
| 195 | src_hdr = src_part->hdr.next; |
| 196 | while (src_hdr != &src_part->hdr) { |
| 197 | pjsip_hdr *dst_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, src_hdr); |
| 198 | pj_list_push_back(&dst_part->hdr, dst_hdr); |
| 199 | src_hdr = src_hdr->next; |
| 200 | } |
| 201 | |
| 202 | dst_part->body = pjsip_msg_body_clone(pool, src_part->body); |
| 203 | |
| 204 | pj_list_push_back(&dst->part_head, dst_part); |
| 205 | |
| 206 | src_part = src_part->next; |
| 207 | } |
| 208 | |
| 209 | return (void*)dst; |
| 210 | } |
| 211 | |
| 212 | /* |
| 213 | * Create an empty multipart body. |
| 214 | */ |
| 215 | PJ_DEF(pjsip_msg_body*) pjsip_multipart_create( pj_pool_t *pool, |
| 216 | const pjsip_media_type *ctype, |
| 217 | const pj_str_t *boundary) |
| 218 | { |
| 219 | pjsip_msg_body *body; |
| 220 | pjsip_param *ctype_param; |
| 221 | struct multipart_data *mp_data; |
| 222 | pj_str_t STR_BOUNDARY = { "boundary", 8 }; |
| 223 | |
| 224 | PJ_ASSERT_RETURN(pool, NULL); |
| 225 | |
| 226 | body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); |
| 227 | |
| 228 | /* content-type */ |
| 229 | if (ctype && ctype->type.slen) { |
| 230 | pjsip_media_type_cp(pool, &body->content_type, ctype); |
| 231 | } else { |
| 232 | pj_str_t STR_MULTIPART = {"multipart", 9}; |
| 233 | pj_str_t STR_MIXED = { "mixed", 5 }; |
| 234 | |
| 235 | pjsip_media_type_init(&body->content_type, |
| 236 | &STR_MULTIPART, &STR_MIXED); |
| 237 | } |
| 238 | |
| 239 | /* multipart data */ |
| 240 | mp_data = PJ_POOL_ZALLOC_T(pool, struct multipart_data); |
| 241 | pj_list_init(&mp_data->part_head); |
| 242 | if (boundary) { |
| 243 | pj_strdup(pool, &mp_data->boundary, boundary); |
| 244 | } else { |
| 245 | pj_create_unique_string(pool, &mp_data->boundary); |
| 246 | } |
| 247 | body->data = mp_data; |
| 248 | |
| 249 | /* Add ";boundary" parameter to content_type parameter. */ |
| 250 | ctype_param = pjsip_param_find(&body->content_type.param, &STR_BOUNDARY); |
| 251 | if (!ctype_param) { |
| 252 | ctype_param = PJ_POOL_ALLOC_T(pool, pjsip_param); |
| 253 | ctype_param->name = STR_BOUNDARY; |
| 254 | pj_list_push_back(&body->content_type.param, ctype_param); |
| 255 | } |
| 256 | ctype_param->value = mp_data->boundary; |
| 257 | |
| 258 | /* function pointers */ |
| 259 | body->print_body = &multipart_print_body; |
| 260 | body->clone_data = &multipart_clone_data; |
| 261 | |
| 262 | return body; |
| 263 | } |
| 264 | |
| 265 | /* |
| 266 | * Create an empty multipart part. |
| 267 | */ |
| 268 | PJ_DEF(pjsip_multipart_part*) pjsip_multipart_create_part(pj_pool_t *pool) |
| 269 | { |
| 270 | pjsip_multipart_part *mp; |
| 271 | |
| 272 | mp = PJ_POOL_ZALLOC_T(pool, pjsip_multipart_part); |
| 273 | pj_list_init(&mp->hdr); |
| 274 | |
| 275 | return mp; |
| 276 | } |
| 277 | |
| 278 | |
| 279 | /* |
| 280 | * Deep clone. |
| 281 | */ |
| 282 | PJ_DEF(pjsip_multipart_part*) |
| 283 | pjsip_multipart_clone_part(pj_pool_t *pool, |
| 284 | const pjsip_multipart_part *src) |
| 285 | { |
| 286 | pjsip_multipart_part *dst; |
| 287 | const pjsip_hdr *hdr; |
| 288 | |
| 289 | dst = pjsip_multipart_create_part(pool); |
| 290 | |
| 291 | hdr = src->hdr.next; |
| 292 | while (hdr != &src->hdr) { |
| 293 | pj_list_push_back(&dst->hdr, pjsip_hdr_clone(pool, hdr)); |
| 294 | hdr = hdr->next; |
| 295 | } |
| 296 | |
| 297 | dst->body = pjsip_msg_body_clone(pool, src->body); |
| 298 | |
| 299 | return dst; |
| 300 | } |
| 301 | |
| 302 | |
| 303 | /* |
| 304 | * Add a part into multipart bodies. |
| 305 | */ |
| 306 | PJ_DEF(pj_status_t) pjsip_multipart_add_part( pj_pool_t *pool, |
| 307 | pjsip_msg_body *mp, |
| 308 | pjsip_multipart_part *part) |
| 309 | { |
| 310 | struct multipart_data *m_data; |
| 311 | |
| 312 | /* All params must be specified */ |
| 313 | PJ_ASSERT_RETURN(pool && mp && part, PJ_EINVAL); |
| 314 | |
| 315 | /* mp must really point to an actual multipart msg body */ |
| 316 | PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, PJ_EINVAL); |
| 317 | |
| 318 | /* The multipart part must contain a valid message body */ |
| 319 | PJ_ASSERT_RETURN(part->body && part->body->print_body, PJ_EINVAL); |
| 320 | |
| 321 | m_data = (struct multipart_data*)mp->data; |
| 322 | pj_list_push_back(&m_data->part_head, part); |
| 323 | |
| 324 | PJ_UNUSED_ARG(pool); |
| 325 | |
| 326 | return PJ_SUCCESS; |
| 327 | } |
| 328 | |
| 329 | /* |
| 330 | * Get the first part of multipart bodies. |
| 331 | */ |
| 332 | PJ_DEF(pjsip_multipart_part*) |
| 333 | pjsip_multipart_get_first_part(const pjsip_msg_body *mp) |
| 334 | { |
| 335 | struct multipart_data *m_data; |
| 336 | |
| 337 | /* Must specify mandatory params */ |
| 338 | PJ_ASSERT_RETURN(mp, NULL); |
| 339 | |
| 340 | /* mp must really point to an actual multipart msg body */ |
| 341 | PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL); |
| 342 | |
| 343 | m_data = (struct multipart_data*)mp->data; |
| 344 | if (pj_list_empty(&m_data->part_head)) |
| 345 | return NULL; |
| 346 | |
| 347 | return m_data->part_head.next; |
| 348 | } |
| 349 | |
| 350 | /* |
| 351 | * Get the next part after the specified part. |
| 352 | */ |
| 353 | PJ_DEF(pjsip_multipart_part*) |
| 354 | pjsip_multipart_get_next_part(const pjsip_msg_body *mp, |
| 355 | pjsip_multipart_part *part) |
| 356 | { |
| 357 | struct multipart_data *m_data; |
| 358 | |
| 359 | /* Must specify mandatory params */ |
| 360 | PJ_ASSERT_RETURN(mp && part, NULL); |
| 361 | |
| 362 | /* mp must really point to an actual multipart msg body */ |
| 363 | PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL); |
| 364 | |
| 365 | m_data = (struct multipart_data*)mp->data; |
| 366 | |
| 367 | /* the part parameter must be really member of the list */ |
| 368 | PJ_ASSERT_RETURN(pj_list_find_node(&m_data->part_head, part) != NULL, |
| 369 | NULL); |
| 370 | |
| 371 | if (part->next == &m_data->part_head) |
| 372 | return NULL; |
| 373 | |
| 374 | return part->next; |
| 375 | } |
| 376 | |
| 377 | /* |
| 378 | * Find a body inside multipart bodies which has the specified content type. |
| 379 | */ |
| 380 | PJ_DEF(pjsip_multipart_part*) |
| 381 | pjsip_multipart_find_part( const pjsip_msg_body *mp, |
| 382 | const pjsip_media_type *content_type, |
| 383 | const pjsip_multipart_part *start) |
| 384 | { |
| 385 | struct multipart_data *m_data; |
| 386 | pjsip_multipart_part *part; |
| 387 | |
| 388 | /* Must specify mandatory params */ |
| 389 | PJ_ASSERT_RETURN(mp && content_type, NULL); |
| 390 | |
| 391 | /* mp must really point to an actual multipart msg body */ |
| 392 | PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, NULL); |
| 393 | |
| 394 | m_data = (struct multipart_data*)mp->data; |
| 395 | |
| 396 | if (start) |
| 397 | part = start->next; |
| 398 | else |
| 399 | part = m_data->part_head.next; |
| 400 | |
| 401 | while (part != &m_data->part_head) { |
| 402 | if (pjsip_media_type_cmp(&part->body->content_type, |
| 403 | content_type, 0)==0) |
| 404 | { |
| 405 | return part; |
| 406 | } |
| 407 | part = part->next; |
| 408 | } |
| 409 | |
| 410 | return NULL; |
| 411 | } |
| 412 | |
| 413 | /* Parse a multipart part. "pct" is parent content-type */ |
| 414 | static pjsip_multipart_part *parse_multipart_part(pj_pool_t *pool, |
| 415 | char *start, |
| 416 | pj_size_t len, |
| 417 | const pjsip_media_type *pct) |
| 418 | { |
| 419 | pjsip_multipart_part *part = pjsip_multipart_create_part(pool); |
| 420 | char *p = start, *end = start+len, *end_hdr = NULL, *start_body = NULL; |
| 421 | pjsip_ctype_hdr *ctype_hdr = NULL; |
| 422 | |
| 423 | TRACE_((THIS_FILE, "Parsing part: begin--\n%.*s\n--end", |
| 424 | (int)len, start)); |
| 425 | |
| 426 | /* Find the end of header area, by looking at an empty line */ |
| 427 | for (;;) { |
| 428 | while (p!=end && *p!='\n') ++p; |
| 429 | if (p==end) { |
| 430 | start_body = end; |
| 431 | break; |
| 432 | } |
| 433 | if ((p==start) || (p==start+1 && *(p-1)=='\r')) { |
| 434 | /* Empty header section */ |
| 435 | end_hdr = start; |
| 436 | start_body = ++p; |
| 437 | break; |
| 438 | } else if (p==end-1) { |
| 439 | /* Empty body section */ |
| 440 | end_hdr = end; |
| 441 | start_body = ++p; |
| 442 | } else if ((p>=start+1 && *(p-1)=='\n') || |
| 443 | (p>=start+2 && *(p-1)=='\r' && *(p-2)=='\n')) |
| 444 | { |
| 445 | /* Found it */ |
| 446 | end_hdr = (*(p-1)=='\r') ? (p-1) : p; |
| 447 | start_body = ++p; |
| 448 | break; |
| 449 | } else { |
| 450 | ++p; |
| 451 | } |
| 452 | } |
| 453 | |
| 454 | /* Parse the headers */ |
| 455 | if (end_hdr-start > 0) { |
| 456 | pjsip_hdr *hdr; |
| 457 | pj_status_t status; |
| 458 | |
| 459 | status = pjsip_parse_headers(pool, start, end_hdr-start, |
| 460 | &part->hdr, 0); |
| 461 | if (status != PJ_SUCCESS) { |
| 462 | PJ_PERROR(2,(THIS_FILE, status, "Warning: error parsing multipart" |
| 463 | " header")); |
| 464 | } |
| 465 | |
| 466 | /* Find Content-Type header */ |
| 467 | hdr = part->hdr.next; |
| 468 | while (hdr != &part->hdr) { |
| 469 | TRACE_((THIS_FILE, "Header parsed: %.*s", (int)hdr->name.slen, |
| 470 | hdr->name.ptr)); |
| 471 | if (hdr->type == PJSIP_H_CONTENT_TYPE) { |
| 472 | ctype_hdr = (pjsip_ctype_hdr*)hdr; |
| 473 | } |
| 474 | hdr = hdr->next; |
| 475 | } |
| 476 | } |
| 477 | |
| 478 | /* Assign the body */ |
| 479 | part->body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); |
| 480 | if (ctype_hdr) { |
| 481 | pjsip_media_type_cp(pool, &part->body->content_type, &ctype_hdr->media); |
| 482 | } else if (pct && pj_stricmp2(&pct->subtype, "digest")==0) { |
| 483 | part->body->content_type.type = pj_str("message"); |
| 484 | part->body->content_type.subtype = pj_str("rfc822"); |
| 485 | } else { |
| 486 | part->body->content_type.type = pj_str("text"); |
| 487 | part->body->content_type.subtype = pj_str("plain"); |
| 488 | } |
| 489 | |
| 490 | if (start_body < end) { |
| 491 | part->body->data = start_body; |
| 492 | part->body->len = (unsigned)(end - start_body); |
| 493 | } else { |
| 494 | part->body->data = (void*)""; |
| 495 | part->body->len = 0; |
| 496 | } |
| 497 | TRACE_((THIS_FILE, "Body parsed: \"%.*s\"", (int)part->body->len, |
| 498 | part->body->data)); |
| 499 | part->body->print_body = &pjsip_print_text_body; |
| 500 | part->body->clone_data = &pjsip_clone_text_data; |
| 501 | |
| 502 | return part; |
| 503 | } |
| 504 | |
| 505 | /* Public function to parse multipart message bodies into its parts */ |
| 506 | PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool, |
| 507 | char *buf, pj_size_t len, |
| 508 | const pjsip_media_type *ctype, |
| 509 | unsigned options) |
| 510 | { |
| 511 | pj_str_t boundary, delim; |
| 512 | char *curptr, *endptr; |
| 513 | const pjsip_param *ctype_param; |
| 514 | const pj_str_t STR_BOUNDARY = { "boundary", 8 }; |
| 515 | pjsip_msg_body *body = NULL; |
| 516 | |
| 517 | PJ_ASSERT_RETURN(pool && buf && len && ctype && !options, NULL); |
| 518 | |
| 519 | TRACE_((THIS_FILE, "Started parsing multipart body")); |
| 520 | |
| 521 | /* Get the boundary value in the ctype */ |
| 522 | boundary.ptr = NULL; |
| 523 | boundary.slen = 0; |
| 524 | ctype_param = pjsip_param_find(&ctype->param, &STR_BOUNDARY); |
| 525 | if (ctype_param) { |
| 526 | boundary = ctype_param->value; |
| 527 | if (boundary.slen>2 && *boundary.ptr=='"') { |
| 528 | /* Remove quote */ |
| 529 | boundary.ptr++; |
| 530 | boundary.slen -= 2; |
| 531 | } |
| 532 | TRACE_((THIS_FILE, "Boundary is specified: '%.*s'", (int)boundary.slen, |
| 533 | boundary.ptr)); |
| 534 | } |
| 535 | |
| 536 | if (!boundary.slen) { |
| 537 | /* Boundary not found or not specified. Try to be clever, get |
| 538 | * the boundary from the body. |
| 539 | */ |
| 540 | char *p=buf, *end=buf+len; |
| 541 | |
| 542 | PJ_LOG(4,(THIS_FILE, "Warning: boundary parameter not found or " |
| 543 | "not specified when parsing multipart body")); |
| 544 | |
| 545 | /* Find the first "--". This "--" must be right after a CRLF, unless |
| 546 | * it really appears at the start of the buffer. |
| 547 | */ |
| 548 | for (;;) { |
| 549 | while (p!=end && *p!='-') ++p; |
| 550 | if (p!=end && *(p+1)=='-' && |
| 551 | ((p>buf && *(p-1)=='\n') || (p==buf))) |
| 552 | { |
| 553 | p+=2; |
| 554 | break; |
| 555 | } else { |
| 556 | ++p; |
| 557 | } |
| 558 | } |
| 559 | |
| 560 | if (p==end) { |
| 561 | /* Unable to determine boundary. Maybe this is not a multipart |
| 562 | * message? |
| 563 | */ |
| 564 | PJ_LOG(4,(THIS_FILE, "Error: multipart boundary not specified and" |
| 565 | " unable to calculate from the body")); |
| 566 | return NULL; |
| 567 | } |
| 568 | |
| 569 | boundary.ptr = p; |
| 570 | while (p!=end && !pj_isspace(*p)) ++p; |
| 571 | boundary.slen = p - boundary.ptr; |
| 572 | |
| 573 | TRACE_((THIS_FILE, "Boundary is calculated: '%.*s'", |
| 574 | (int)boundary.slen, boundary.ptr)); |
| 575 | } |
| 576 | |
| 577 | /* Build the delimiter: |
| 578 | * delimiter = "--" boundary |
| 579 | */ |
| 580 | delim.slen = boundary.slen+2; |
| 581 | delim.ptr = (char*)pj_pool_alloc(pool, (int)delim.slen); |
| 582 | delim.ptr[0] = '-'; |
| 583 | delim.ptr[1] = '-'; |
| 584 | pj_memcpy(delim.ptr+2, boundary.ptr, boundary.slen); |
| 585 | |
| 586 | /* Start parsing the body, skip until the first delimiter. */ |
| 587 | curptr = buf; |
| 588 | endptr = buf + len; |
| 589 | { |
| 590 | pj_str_t body; |
| 591 | |
| 592 | body.ptr = buf; body.slen = len; |
| 593 | curptr = pj_strstr(&body, &delim); |
| 594 | if (!curptr) |
| 595 | return NULL; |
| 596 | } |
| 597 | |
| 598 | body = pjsip_multipart_create(pool, ctype, &boundary); |
| 599 | |
| 600 | for (;;) { |
| 601 | char *start_body, *end_body; |
| 602 | pjsip_multipart_part *part; |
| 603 | |
| 604 | /* Eat the boundary */ |
| 605 | curptr += delim.slen; |
| 606 | if (*curptr=='-' && curptr<endptr-1 && *(curptr+1)=='-') { |
| 607 | /* Found the closing delimiter */ |
| 608 | curptr += 2; |
| 609 | break; |
| 610 | } |
| 611 | /* Optional whitespace after delimiter */ |
| 612 | while (curptr!=endptr && IS_SPACE(*curptr)) ++curptr; |
| 613 | /* Mandatory CRLF */ |
| 614 | if (*curptr=='\r') ++curptr; |
| 615 | if (*curptr!='\n') { |
| 616 | /* Expecting a newline here */ |
| 617 | return NULL; |
| 618 | } |
| 619 | ++curptr; |
| 620 | |
| 621 | /* We now in the start of the body */ |
| 622 | start_body = curptr; |
| 623 | |
| 624 | /* Find the next delimiter */ |
| 625 | { |
| 626 | pj_str_t subbody; |
| 627 | |
| 628 | subbody.ptr = curptr; subbody.slen = endptr - curptr; |
| 629 | curptr = pj_strstr(&subbody, &delim); |
| 630 | if (!curptr) { |
| 631 | /* We're really expecting end delimiter to be found. */ |
| 632 | return NULL; |
| 633 | } |
| 634 | } |
| 635 | |
| 636 | end_body = curptr; |
| 637 | |
| 638 | /* The newline preceeding the delimiter is conceptually part of |
| 639 | * the delimiter, so trim it from the body. |
| 640 | */ |
| 641 | if (*(end_body-1) == '\n') |
| 642 | --end_body; |
| 643 | if (*(end_body-1) == '\r') |
| 644 | --end_body; |
| 645 | |
| 646 | /* Now that we have determined the part's boundary, parse it |
| 647 | * to get the header and body part of the part. |
| 648 | */ |
| 649 | part = parse_multipart_part(pool, start_body, end_body - start_body, |
| 650 | ctype); |
| 651 | if (part) { |
| 652 | pjsip_multipart_add_part(pool, body, part); |
| 653 | } |
| 654 | } |
| 655 | |
| 656 | return body; |
| 657 | } |
| 658 | |