4 * Purple is the legal property of its developers, whose names are too
5 * numerous to list here. Please refer to the COPYRIGHT file distributed
6 * with this source distribution
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or (at
11 * your option) any later version.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301,
26 /* this should become "util.h" if we ever get this into purple proper */
32 * Utility structure used in both MIME document and parts, which maps
33 * field names to their values, and keeps an easily accessible list of
41 struct _PurpleMimeDocument
{
42 struct mime_fields fields
;
46 struct _PurpleMimePart
{
47 struct mime_fields fields
;
48 struct _PurpleMimeDocument
*doc
;
53 fields_set(struct mime_fields
*mf
, const char *key
, const char *val
)
57 g_return_if_fail(mf
!= NULL
);
58 g_return_if_fail(mf
->map
!= NULL
);
60 k
= g_ascii_strdown(key
, -1);
63 /* append to the keys list only if it's not already there */
64 if(! g_hash_table_lookup(mf
->map
, k
)) {
65 mf
->keys
= g_list_append(mf
->keys
, k
);
68 /* important to use insert. If the key is already in the table, then
69 it's already in the keys list. Insert will free the new instance
70 of the key rather than the old instance. */
71 g_hash_table_insert(mf
->map
, k
, v
);
76 fields_get(struct mime_fields
*mf
, const char *key
)
81 g_return_val_if_fail(mf
!= NULL
, NULL
);
82 g_return_val_if_fail(mf
->map
!= NULL
, NULL
);
84 kdown
= g_ascii_strdown(key
, -1);
85 ret
= g_hash_table_lookup(mf
->map
, kdown
);
93 fields_init(struct mime_fields
*mf
)
95 g_return_if_fail(mf
!= NULL
);
97 mf
->map
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
103 fields_loadline(struct mime_fields
*mf
, const char *line
, gsize len
)
105 /* split the line into key: value */
106 char *key
, *newkey
, *val
;
109 /* feh, need it to be NUL terminated */
110 key
= g_strndup(line
, len
);
113 val
= strchr(key
, ':');
120 /* normalize whitespace (sorta) and trim on key and value */
121 tokens
= g_strsplit(key
, "\t\r\n", 0);
122 newkey
= g_strjoinv("", tokens
);
126 tokens
= g_strsplit(val
, "\t\r\n", 0);
127 val
= g_strjoinv("", tokens
);
131 fields_set(mf
, newkey
, val
);
140 fields_load(struct mime_fields
*mf
, char **buf
, gsize
*len
)
144 while ((tail
= g_strstr_len(*buf
, *len
, "\r\n")))
149 /* determine the current line */
153 /* advance our search space past the CRLF */
157 /* empty line, end of headers */
160 /* look out for line continuations */
161 if(line
[ln
-1] == ';') {
162 tail
= g_strstr_len(*buf
, *len
, "\r\n");
169 /* advance our search space past the CRLF (again) */
175 /* process our super-cool line */
176 fields_loadline(mf
, line
, ln
);
182 field_write(const char *key
, const char *val
, GString
*str
)
184 g_string_append_printf(str
, "%s: %s\r\n", key
, val
);
189 fields_write(struct mime_fields
*mf
, GString
*str
)
191 g_return_if_fail(mf
!= NULL
);
193 g_hash_table_foreach(mf
->map
, (GHFunc
) field_write
, str
);
194 g_string_append(str
, "\r\n");
199 fields_destroy(struct mime_fields
*mf
)
201 g_return_if_fail(mf
!= NULL
);
203 g_hash_table_destroy(mf
->map
);
204 g_list_free(mf
->keys
);
211 static PurpleMimePart
*
212 part_new(PurpleMimeDocument
*doc
)
214 PurpleMimePart
*part
;
216 part
= g_new0(PurpleMimePart
, 1);
217 fields_init(&part
->fields
);
219 part
->data
= g_string_new(NULL
);
221 doc
->parts
= g_list_prepend(doc
->parts
, part
);
228 part_load(PurpleMimePart
*part
, const char *buf
, gsize len
)
231 char *b
= (char *) buf
;
234 fields_load(&part
->fields
, &b
, &n
);
236 /* the remainder will have a blank line, if there's anything at all,
237 so check if there's anything then trim off the trailing four
240 g_string_append_len(part
->data
, b
, n
);
245 part_write(PurpleMimePart
*part
, GString
*str
)
247 fields_write(&part
->fields
, str
);
248 g_string_append_printf(str
, "%s\r\n\r\n", part
->data
->str
);
253 part_free(PurpleMimePart
*part
)
256 fields_destroy(&part
->fields
);
258 g_string_free(part
->data
, TRUE
);
266 purple_mime_part_new(PurpleMimeDocument
*doc
)
268 g_return_val_if_fail(doc
!= NULL
, NULL
);
269 return part_new(doc
);
274 purple_mime_part_get_fields(PurpleMimePart
*part
)
276 g_return_val_if_fail(part
!= NULL
, NULL
);
277 return part
->fields
.keys
;
282 purple_mime_part_get_field(PurpleMimePart
*part
, const char *field
)
284 g_return_val_if_fail(part
!= NULL
, NULL
);
285 return fields_get(&part
->fields
, field
);
290 purple_mime_part_get_field_decoded(PurpleMimePart
*part
, const char *field
)
294 g_return_val_if_fail(part
!= NULL
, NULL
);
296 f
= fields_get(&part
->fields
, field
);
297 return purple_mime_decode_field(f
);
302 purple_mime_part_set_field(PurpleMimePart
*part
, const char *field
, const char *value
)
304 g_return_if_fail(part
!= NULL
);
305 fields_set(&part
->fields
, field
, value
);
310 purple_mime_part_get_data(PurpleMimePart
*part
)
312 g_return_val_if_fail(part
!= NULL
, NULL
);
313 g_return_val_if_fail(part
->data
!= NULL
, NULL
);
315 return part
->data
->str
;
320 purple_mime_part_get_data_decoded(PurpleMimePart
*part
, guchar
**data
, gsize
*len
)
324 g_return_if_fail(part
!= NULL
);
325 g_return_if_fail(data
!= NULL
);
326 g_return_if_fail(len
!= NULL
);
328 g_return_if_fail(part
->data
!= NULL
);
330 enc
= purple_mime_part_get_field(part
, "content-transfer-encoding");
333 *data
= (guchar
*)g_strdup(part
->data
->str
);
334 *len
= part
->data
->len
;
336 } else if(! g_ascii_strcasecmp(enc
, "7bit")) {
337 *data
= (guchar
*)g_strdup(part
->data
->str
);
338 *len
= part
->data
->len
;
340 } else if(! g_ascii_strcasecmp(enc
, "8bit")) {
341 *data
= (guchar
*)g_strdup(part
->data
->str
);
342 *len
= part
->data
->len
;
344 } else if(! g_ascii_strcasecmp(enc
, "base16")) {
345 *data
= purple_base16_decode(part
->data
->str
, len
);
347 } else if(! g_ascii_strcasecmp(enc
, "base64")) {
348 *data
= g_base64_decode(part
->data
->str
, len
);
350 } else if(! g_ascii_strcasecmp(enc
, "quoted-printable")) {
351 *data
= purple_quotedp_decode(part
->data
->str
, len
);
354 purple_debug_warning("mime", "purple_mime_part_get_data_decoded:"
355 " unknown encoding '%s'\n", enc
);
363 purple_mime_part_get_length(PurpleMimePart
*part
)
365 g_return_val_if_fail(part
!= NULL
, 0);
366 g_return_val_if_fail(part
->data
!= NULL
, 0);
368 return part
->data
->len
;
373 purple_mime_part_set_data(PurpleMimePart
*part
, const char *data
)
375 g_return_if_fail(part
!= NULL
);
376 g_string_free(part
->data
, TRUE
);
377 part
->data
= g_string_new(data
);
382 purple_mime_document_new()
384 PurpleMimeDocument
*doc
;
386 doc
= g_new0(PurpleMimeDocument
, 1);
387 fields_init(&doc
->fields
);
394 doc_parts_load(PurpleMimeDocument
*doc
, const char *boundary
, const char *buf
, gsize len
)
396 char *b
= (char *) buf
;
402 bnd
= g_strdup_printf("--%s", boundary
);
405 for(b
= g_strstr_len(b
, n
, bnd
); b
; ) {
408 /* skip the boundary */
412 /* skip the trailing \r\n or -- as well */
418 /* find the next boundary */
419 tail
= g_strstr_len(b
, n
, bnd
);
426 PurpleMimePart
*part
= part_new(doc
);
427 part_load(part
, b
, sl
);
437 #define BOUNDARY "boundary="
439 parse_boundary(const char *ct
)
441 char *boundary_begin
= g_strstr_len(ct
, -1, BOUNDARY
);
447 boundary_begin
+= sizeof(BOUNDARY
) - 1;
449 if (*boundary_begin
== '"') {
450 boundary_end
= strchr(++boundary_begin
, '"');
454 boundary_end
= strchr(boundary_begin
, ' ');
456 boundary_end
= strchr(boundary_begin
, ';');
458 boundary_end
= boundary_begin
+ strlen(boundary_begin
);
462 return g_strndup(boundary_begin
, boundary_end
- boundary_begin
);
467 purple_mime_document_parsen(const char *buf
, gsize len
)
469 PurpleMimeDocument
*doc
;
471 char *b
= (char *) buf
;
474 g_return_val_if_fail(buf
!= NULL
, NULL
);
476 doc
= purple_mime_document_new();
481 fields_load(&doc
->fields
, &b
, &n
);
484 const char *ct
= fields_get(&doc
->fields
, "content-type");
485 if (ct
&& purple_str_has_prefix(ct
, "multipart")) {
486 char *bd
= parse_boundary(ct
);
488 doc_parts_load(doc
, bd
, b
, n
);
499 purple_mime_document_parse(const char *buf
)
501 g_return_val_if_fail(buf
!= NULL
, NULL
);
502 return purple_mime_document_parsen(buf
, strlen(buf
));
507 purple_mime_document_write(PurpleMimeDocument
*doc
, GString
*str
)
509 const char *bd
= NULL
;
511 g_return_if_fail(doc
!= NULL
);
512 g_return_if_fail(str
!= NULL
);
515 const char *ct
= fields_get(&doc
->fields
, "content-type");
516 if(ct
&& purple_str_has_prefix(ct
, "multipart")) {
517 char *b
= strrchr(ct
, '=');
522 fields_write(&doc
->fields
, str
);
527 for(l
= doc
->parts
; l
; l
= l
->next
) {
528 g_string_append_printf(str
, "--%s\r\n", bd
);
530 part_write(l
->data
, str
);
533 g_string_append_printf(str
, "--%s--\r\n", bd
);
541 purple_mime_document_get_fields(PurpleMimeDocument
*doc
)
543 g_return_val_if_fail(doc
!= NULL
, NULL
);
544 return doc
->fields
.keys
;
549 purple_mime_document_get_field(PurpleMimeDocument
*doc
, const char *field
)
551 g_return_val_if_fail(doc
!= NULL
, NULL
);
552 return fields_get(&doc
->fields
, field
);
557 purple_mime_document_set_field(PurpleMimeDocument
*doc
, const char *field
, const char *value
)
559 g_return_if_fail(doc
!= NULL
);
560 fields_set(&doc
->fields
, field
, value
);
565 purple_mime_document_get_parts(PurpleMimeDocument
*doc
)
567 g_return_val_if_fail(doc
!= NULL
, NULL
);
573 purple_mime_document_free(PurpleMimeDocument
*doc
)
578 fields_destroy(&doc
->fields
);
581 part_free(doc
->parts
->data
);
582 doc
->parts
= g_list_delete_link(doc
->parts
, doc
->parts
);