00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069 #include <string.h>
00070 #include <limits.h>
00071 #include <ctype.h>
00072
00073 #include "gwlib/gwlib.h"
00074 #include "gwlib/mime.h"
00075
00076 struct MIMEEntity {
00077 List *headers;
00078 List *multiparts;
00079 Octstr *body;
00080 struct MIMEEntity *start;
00081 };
00082
00083
00084
00085
00086
00087 MIMEEntity *mime_entity_create(void)
00088 {
00089 MIMEEntity *e;
00090
00091 e = gw_malloc(sizeof(MIMEEntity));
00092 e->headers = http_create_empty_headers();
00093 e->multiparts = gwlist_create();
00094 e->body = NULL;
00095 e->start = NULL;
00096
00097 return e;
00098 }
00099
00100 void static mime_entity_destroy_item(void *e)
00101 {
00102 mime_entity_destroy(e);
00103 }
00104
00105 void mime_entity_destroy(MIMEEntity *e)
00106 {
00107 gw_assert(e != NULL);
00108
00109 if (e->headers != NULL)
00110 gwlist_destroy(e->headers, octstr_destroy_item);
00111 if (e->multiparts != NULL)
00112 gwlist_destroy(e->multiparts, mime_entity_destroy_item);
00113 octstr_destroy(e->body);
00114 e->start = NULL;
00115
00116 gw_free(e);
00117 }
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129 static int read_mime_headers(ParseContext *context, List *headers)
00130 {
00131 Octstr *line, *prev;
00132
00133 if (gwlist_len(headers) == 0)
00134 prev = NULL;
00135 else
00136 prev = gwlist_get(headers, gwlist_len(headers) - 1);
00137
00138 for (;;) {
00139 line = parse_get_line(context);
00140 if (line == NULL) {
00141 return -1;
00142 }
00143 if (octstr_len(line) == 0) {
00144 octstr_destroy(line);
00145 break;
00146 }
00147 if (isspace(octstr_get_char(line, 0)) && prev != NULL) {
00148 octstr_append(prev, line);
00149 octstr_destroy(line);
00150 } else {
00151 gwlist_append(headers, line);
00152 prev = line;
00153 }
00154 }
00155
00156 return 0;
00157 }
00158
00159
00160
00161
00162
00163 static void fix_boundary_element(List *headers, Octstr **boundary_elem)
00164 {
00165 Octstr *value, *boundary;
00166 long len;
00167
00168
00169
00170
00171
00172
00173
00174 value = http_header_value(headers, octstr_imm("Content-Type"));
00175 boundary = value ? http_get_header_parameter(value, octstr_imm("boundary")) : NULL;
00176
00177 if (value == NULL) {
00178
00179 value = octstr_create("multipart/mixed");
00180 http_header_add(headers, "Content-Type", "multipart/mixed");
00181 }
00182 if (boundary == NULL) {
00183 Octstr *v;
00184 boundary = octstr_format("_boundary_%d_%ld_%c_%c_bd%d",
00185 random(), (long) time(NULL), 'A' + (random() % 26),
00186 'a' + (random() % 26), random());
00187
00188 octstr_format_append(value, "; boundary=%S", boundary);
00189
00190 http_header_remove_all(headers, "Content-Type");
00191 http_header_add(headers, "Content-Type", octstr_get_cstr(value));
00192 if ((v = http_header_value(headers, octstr_imm("MIME-Version"))) == NULL)
00193 http_header_add(headers, "MIME-Version", "1.0");
00194 else
00195 octstr_destroy(v);
00196 }
00197 else if ((len = octstr_len(boundary)) > 0 &&
00198 octstr_get_char(boundary, 0) == '"' &&
00199 octstr_get_char(boundary, len - 1) == '"') {
00200 octstr_delete(boundary, 0, 1);
00201 octstr_delete(boundary, len - 2, 1);
00202 }
00203
00204 octstr_destroy(value);
00205 if (boundary_elem)
00206 *boundary_elem = boundary;
00207 else
00208 octstr_destroy(boundary);
00209 }
00210
00211
00212
00213
00214
00215
00216 Octstr *mime_entity_to_octstr(MIMEEntity *m)
00217 {
00218 Octstr *mime, *boundary = NULL;
00219 List *headers;
00220 long i;
00221
00222 gw_assert(m != NULL && m->headers != NULL);
00223
00224 mime = octstr_create("");
00225
00226
00227
00228
00229
00230
00231
00232 if (gwlist_len(m->multiparts) == 0) {
00233 for (i = 0; i < gwlist_len(m->headers); i++) {
00234 octstr_append(mime, gwlist_get(m->headers, i));
00235 octstr_append(mime, octstr_imm("\r\n"));
00236 }
00237 octstr_append(mime, octstr_imm("\r\n"));
00238 if (m->body != NULL)
00239 octstr_append(mime, m->body);
00240 goto finished;
00241 }
00242
00243
00244 fix_boundary_element(m->headers, &boundary);
00245 headers = http_header_duplicate(m->headers);
00246
00247
00248 for (i = 0; i < gwlist_len(headers); i++) {
00249 octstr_append(mime, gwlist_get(headers, i));
00250 octstr_append(mime, octstr_imm("\r\n"));
00251 }
00252 http_destroy_headers(headers);
00253 octstr_append(mime, octstr_imm("\r\n"));
00254
00255
00256 for (i = 0; i < gwlist_len(m->multiparts); i++) {
00257 MIMEEntity *e = gwlist_get(m->multiparts, i);
00258 Octstr *body;
00259
00260 octstr_append(mime, octstr_imm("\r\n--"));
00261 octstr_append(mime, boundary);
00262 octstr_append(mime, octstr_imm("\r\n"));
00263
00264
00265 body = mime_entity_to_octstr(e);
00266 octstr_append(mime, body);
00267
00268 octstr_destroy(body);
00269 }
00270
00271 octstr_append(mime, octstr_imm("\r\n--"));
00272 octstr_append(mime, boundary);
00273 octstr_append(mime, octstr_imm("--\r\n"));
00274
00275 octstr_destroy(boundary);
00276
00277 finished:
00278
00279 return mime;
00280 }
00281
00282 static Octstr *get_start_param(Octstr *content_type)
00283 {
00284 Octstr *start;
00285 int len;
00286
00287 if (!content_type)
00288 return NULL;
00289
00290 start = http_get_header_parameter(content_type, octstr_imm("start"));
00291 if (start && (len = octstr_len(start)) > 0 &&
00292 octstr_get_char(start, 0) == '"' && octstr_get_char(start, len-1) == '"') {
00293 octstr_delete(start, 0, 1);
00294 octstr_delete(start, len-2, 1);
00295 }
00296
00297 return start;
00298 }
00299
00300 static int cid_matches(List *headers, Octstr *start)
00301 {
00302 Octstr *cid = http_header_value(headers, octstr_imm("Content-ID"));
00303 char *cid_str;
00304 int cid_len;
00305 int ret;
00306
00307 if (cid == NULL)
00308 return 0;
00309
00310
00311 cid_str = octstr_get_cstr(cid);
00312 cid_len = octstr_len(cid);
00313 if (cid_str[0] == '<') {
00314 cid_str+=1;
00315 cid_len-=2;
00316 }
00317 if (start != NULL && cid != NULL &&
00318 (octstr_compare(start, cid) == 0 || octstr_str_ncompare(start, cid_str, cid_len) == 0))
00319 ret = 1;
00320 else
00321 ret = 0;
00322
00323 octstr_destroy(cid);
00324 return ret;
00325 }
00326
00327
00328
00329
00330
00331
00332 static MIMEEntity *mime_something_to_entity(Octstr *mime, List *headers)
00333 {
00334 MIMEEntity *e;
00335 ParseContext *context;
00336 Octstr *value, *boundary, *start;
00337 int len = 0;
00338
00339 gw_assert(mime != NULL);
00340
00341 value = boundary = start = NULL;
00342 context = parse_context_create(mime);
00343 e = mime_entity_create();
00344
00345
00346
00347 if (headers != NULL) {
00348
00349
00350 http_destroy_headers(e->headers);
00351 e->headers = http_header_duplicate(headers);
00352 } else {
00353
00354 if ((read_mime_headers(context, e->headers) != 0) || e->headers == NULL) {
00355 debug("mime.parse",0,"Failed to read MIME headers in Octstr block:");
00356 octstr_dump(mime, 0);
00357 mime_entity_destroy(e);
00358 parse_context_destroy(context);
00359 return NULL;
00360 }
00361 }
00362
00363
00364
00365
00366
00367
00368 value = http_header_value(e->headers, octstr_imm("Content-Type"));
00369 boundary = http_get_header_parameter(value, octstr_imm("boundary"));
00370 start = get_start_param(value);
00371
00372
00373
00374 if (boundary && (len = octstr_len(boundary)) > 0 &&
00375 octstr_get_char(boundary, 0) == '"' && octstr_get_char(boundary, len-1) == '"') {
00376 octstr_delete(boundary, 0, 1);
00377 octstr_delete(boundary, len-2, 1);
00378
00379 }
00380
00381 if (boundary != NULL) {
00382
00383 Octstr *entity, *seperator, *os;
00384
00385
00386 seperator = octstr_create("--");
00387 octstr_append(seperator, boundary);
00388 while ((entity = parse_get_seperated_block(context, seperator)) != NULL) {
00389 MIMEEntity *m;
00390 int del2 = 0;
00391
00392
00393
00394
00395 del2 = (octstr_get_char(entity, 0) == '\r');
00396 if (del2)
00397 octstr_delete(entity, 0, 2);
00398 else
00399 octstr_delete(entity, 0, 1);
00400
00401
00402
00403 if (del2)
00404 octstr_delete(entity, octstr_len(entity) - 2, 2);
00405 else
00406 octstr_delete(entity, octstr_len(entity) - 1, 1);
00407
00408 debug("mime.parse",0,"MIME multipart: Parsing entity:");
00409 octstr_dump(entity, 0);
00410
00411
00412 if ((m = mime_octstr_to_entity(entity))) {
00413 gwlist_append(e->multiparts, m);
00414
00415
00416
00417 if (cid_matches(m->headers, start)) {
00418
00419 e->start = (e->start == NULL) ? m : e->start;
00420 }
00421 }
00422
00423 octstr_destroy(entity);
00424 }
00425
00426 octstr_append_cstr(seperator, "--");
00427 os = parse_get_line(context);
00428 if (os != NULL && octstr_compare(os, seperator) != 0) {
00429 debug("mime.parse",0,"Failed to see end boundary, parsed line is '%s'.",
00430 octstr_get_cstr(os));
00431 }
00432
00433 octstr_destroy(seperator);
00434 octstr_destroy(os);
00435 }
00436 else {
00437
00438
00439
00440 e->body = parse_get_rest(context);
00441
00442 }
00443
00444 parse_context_destroy(context);
00445 octstr_destroy(value);
00446 octstr_destroy(boundary);
00447 octstr_destroy(start);
00448
00449 return e;
00450 }
00451
00452
00453 MIMEEntity *mime_octstr_to_entity(Octstr *mime)
00454 {
00455 gw_assert(mime != NULL);
00456
00457 return mime_something_to_entity(mime, NULL);
00458 }
00459
00460
00461 MIMEEntity *mime_http_to_entity(List *headers, Octstr *body)
00462 {
00463 gw_assert(headers != NULL && body != NULL);
00464
00465 return mime_something_to_entity(body, headers);
00466 }
00467
00468
00469 List *mime_entity_headers(MIMEEntity *m)
00470 {
00471 List *headers;
00472
00473 gw_assert(m != NULL && m->headers != NULL);
00474
00475
00476 if (mime_entity_num_parts(m) > 0)
00477 fix_boundary_element(m->headers, NULL);
00478
00479 headers = http_header_duplicate(m->headers);
00480
00481 return headers;
00482 }
00483
00484
00485 Octstr *mime_entity_body(MIMEEntity *m)
00486 {
00487 Octstr *os, *body;
00488 ParseContext *context;
00489 MIMEEntity *e;
00490
00491 gw_assert(m != NULL && m->headers != NULL);
00492
00493
00494 if (mime_entity_num_parts(m) == 0)
00495 return octstr_duplicate(m->body);
00496
00497 os = mime_entity_to_octstr(m);
00498 context = parse_context_create(os);
00499 e = mime_entity_create();
00500
00501
00502 if ((read_mime_headers(context, e->headers) != 0) || e->headers == NULL) {
00503 debug("mime.parse",0,"Failed to read MIME headers in Octstr block:");
00504 octstr_dump(os, 0);
00505 mime_entity_destroy(e);
00506 parse_context_destroy(context);
00507 return NULL;
00508 }
00509
00510
00511 body = parse_get_rest(context);
00512
00513 octstr_destroy(os);
00514 mime_entity_destroy(e);
00515 parse_context_destroy(context);
00516
00517 return body;
00518 }
00519
00520
00521 MIMEEntity *mime_entity_duplicate(MIMEEntity *e)
00522 {
00523 MIMEEntity *copy = mime_entity_create();
00524 int i, n;
00525
00526 mime_replace_headers(copy, e->headers);
00527 copy->body = e->body ? octstr_duplicate(e->body) : NULL;
00528
00529 for (i = 0, n = gwlist_len(e->multiparts); i < n; i++)
00530 gwlist_append(copy->multiparts,
00531 mime_entity_duplicate(gwlist_get(e->multiparts, i)));
00532 return copy;
00533 }
00534
00535
00536 void mime_replace_headers(MIMEEntity *e, List *headers)
00537 {
00538 gw_assert(e != NULL);
00539 gw_assert(headers != NULL);
00540
00541 http_destroy_headers(e->headers);
00542 e->headers = http_header_duplicate(headers);
00543 e->start = NULL;
00544 }
00545
00546
00547
00548
00549
00550 int mime_entity_num_parts(MIMEEntity *e)
00551 {
00552 gw_assert(e != NULL);
00553 return e->multiparts ? gwlist_len(e->multiparts) : 0;
00554 }
00555
00556
00557
00558
00559
00560 void mime_entity_add_part(MIMEEntity *e, MIMEEntity *part)
00561 {
00562 gw_assert(e != NULL);
00563 gw_assert(part != NULL);
00564
00565 gwlist_append(e->multiparts, mime_entity_duplicate(part));
00566 }
00567
00568
00569
00570 MIMEEntity *mime_entity_get_part(MIMEEntity *e, int i)
00571 {
00572 MIMEEntity *m;
00573 gw_assert(e != NULL);
00574 gw_assert(i >= 0);
00575 gw_assert(i < gwlist_len(e->multiparts));
00576
00577 m = gwlist_get(e->multiparts, i);
00578 gw_assert(m);
00579 return mime_entity_duplicate(m);
00580 }
00581
00582
00583
00584 void mime_entity_remove_part(MIMEEntity *e, int i)
00585 {
00586 MIMEEntity *m;
00587
00588 gw_assert(e != NULL);
00589 gw_assert(i >= 0);
00590 gw_assert(i < gwlist_len(e->multiparts));
00591
00592
00593 m = gwlist_get(e->multiparts, i);
00594 gwlist_delete(e->multiparts, i, 1);
00595 if (m == e->start) e->start = NULL;
00596
00597 mime_entity_destroy(m);
00598 }
00599
00600
00601 void mime_entity_replace_part(MIMEEntity *e, int i, MIMEEntity *newpart)
00602 {
00603
00604 MIMEEntity *m;
00605
00606 gw_assert(e != NULL);
00607 gw_assert(i >= 0);
00608 gw_assert(i < gwlist_len(e->multiparts));
00609
00610 m = gwlist_get(e->multiparts, i);
00611 gwlist_delete(e->multiparts, i, 1);
00612 gwlist_insert(e->multiparts, i, mime_entity_duplicate(newpart));
00613 if (m == e->start) e->start = NULL;
00614
00615 mime_entity_destroy(m);
00616 }
00617
00618
00619
00620
00621
00622 void mime_entity_set_body(MIMEEntity *e, Octstr *body)
00623 {
00624 gw_assert(e != NULL);
00625 gw_assert(body != NULL);
00626
00627 if (e->body)
00628 octstr_destroy(e->body);
00629 e->body = octstr_duplicate(body);
00630 }
00631
00632
00633 MIMEEntity *mime_multipart_start_elem(MIMEEntity *e)
00634 {
00635 gw_assert(e != NULL);
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645 if (!e->start) {
00646 Octstr *ctype = http_header_value(e->headers, octstr_imm("Content-Type"));
00647 Octstr *start = get_start_param(ctype);
00648 int i;
00649
00650 if (!ctype)
00651 e->start = NULL;
00652 else if (!start) {
00653 if (gwlist_len(e->multiparts) > 0)
00654 e->start = gwlist_get(e->multiparts, 0);
00655 else
00656 e->start = NULL;
00657 } else
00658 for (i = 0; i < gwlist_len(e->multiparts); i++) {
00659 MIMEEntity *x = gwlist_get(e->multiparts, i);
00660 if (cid_matches(x->headers, start)) {
00661 e->start = x;
00662 break;
00663 }
00664 }
00665
00666 if (ctype)
00667 octstr_destroy(ctype);
00668 if (start)
00669 octstr_destroy(start);
00670 }
00671
00672 return (e->start) ? mime_entity_duplicate(e->start) : NULL;
00673 }
00674
00675
00676
00677
00678
00679 static void mime_entity_dump_real(MIMEEntity *m, unsigned int level)
00680 {
00681 long i, items;
00682 Octstr *prefix, *type, *charset;
00683 unsigned int j;
00684
00685 gw_assert(m != NULL && m->headers != NULL);
00686
00687 prefix = octstr_create("");
00688 for (j = 0; j < level * 2; j++)
00689 octstr_append_cstr(prefix, " ");
00690
00691 http_header_get_content_type(m->headers, &type, &charset);
00692 debug("mime.dump",0,"%sContent-Type `%s'", octstr_get_cstr(prefix),
00693 octstr_get_cstr(type));
00694 if (m->start != NULL) {
00695 Octstr *cid = http_header_value(m->start->headers, octstr_imm("Content-ID"));
00696 debug("mime.dump",0,"%sRelated to Content-ID <%s> MIMEEntity at address `%p'",
00697 octstr_get_cstr(prefix), octstr_get_cstr(cid), m->start);
00698 octstr_destroy(cid);
00699 }
00700 items = gwlist_len(m->multiparts);
00701 debug("mime.dump",0,"%sBody contains %ld MIME entities, size %ld", octstr_get_cstr(prefix),
00702 items, (items == 0 && m->body) ? octstr_len(m->body) : -1);
00703
00704 octstr_destroy(prefix);
00705 octstr_destroy(type);
00706 octstr_destroy(charset);
00707
00708 for (i = 0; i < items; i++) {
00709 MIMEEntity *e = gwlist_get(m->multiparts, i);
00710
00711 mime_entity_dump_real(e, level + 1);
00712 }
00713
00714 }
00715
00716
00717 void mime_entity_dump(MIMEEntity *m)
00718 {
00719 gw_assert(m != NULL && m->headers != NULL);
00720
00721 debug("mms",0,"Dumping MIMEEntity at address %p", m);
00722 mime_entity_dump_real(m, 0);
00723 }
00724
See file LICENSE for details about the license agreement for using,
modifying, copying or deriving work from this software.