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 */
34 * Utility structure used in both MIME document and parts, which maps
35 * field names to their values, and keeps an easily accessible list of
43 struct _PurpleMimeDocument
{
44 struct mime_fields fields
;
48 struct _PurpleMimePart
{
49 struct mime_fields fields
;
50 struct _PurpleMimeDocument
*doc
;
55 fields_set(struct mime_fields
*mf
, const char *key
, const char *val
)
59 g_return_if_fail(mf
!= NULL
);
60 g_return_if_fail(mf
->map
!= NULL
);
62 k
= g_ascii_strdown(key
, -1);
65 /* append to the keys list only if it's not already there */
66 if(! g_hash_table_lookup(mf
->map
, k
)) {
67 mf
->keys
= g_list_append(mf
->keys
, k
);
70 /* important to use insert. If the key is already in the table, then
71 it's already in the keys list. Insert will free the new instance
72 of the key rather than the old instance. */
73 g_hash_table_insert(mf
->map
, k
, v
);
78 fields_get(struct mime_fields
*mf
, const char *key
)
83 g_return_val_if_fail(mf
!= NULL
, NULL
);
84 g_return_val_if_fail(mf
->map
!= NULL
, NULL
);
86 kdown
= g_ascii_strdown(key
, -1);
87 ret
= g_hash_table_lookup(mf
->map
, kdown
);
95 fields_init(struct mime_fields
*mf
)
97 g_return_if_fail(mf
!= NULL
);
99 mf
->map
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
105 fields_loadline(struct mime_fields
*mf
, const char *line
, gsize len
)
107 /* split the line into key: value */
108 char *key
, *newkey
, *val
;
111 /* feh, need it to be NUL terminated */
112 key
= g_strndup(line
, len
);
115 val
= strchr(key
, ':');
122 /* normalize whitespace (sorta) and trim on key and value */
123 tokens
= g_strsplit(key
, "\t\r\n", 0);
124 newkey
= g_strjoinv("", tokens
);
128 tokens
= g_strsplit(val
, "\t\r\n", 0);
129 val
= g_strjoinv("", tokens
);
133 fields_set(mf
, newkey
, val
);
142 fields_load(struct mime_fields
*mf
, char **buf
, gsize
*len
)
146 while ((tail
= g_strstr_len(*buf
, *len
, "\r\n")))
151 /* determine the current line */
155 /* advance our search space past the CRLF */
159 /* empty line, end of headers */
162 /* look out for line continuations */
163 if(line
[ln
-1] == ';') {
164 tail
= g_strstr_len(*buf
, *len
, "\r\n");
171 /* advance our search space past the CRLF (again) */
177 /* process our super-cool line */
178 fields_loadline(mf
, line
, ln
);
184 field_write(const char *key
, const char *val
, GString
*str
)
186 g_string_append_printf(str
, "%s: %s\r\n", key
, val
);
191 fields_write(struct mime_fields
*mf
, GString
*str
)
193 g_return_if_fail(mf
!= NULL
);
195 g_hash_table_foreach(mf
->map
, (GHFunc
) field_write
, str
);
196 g_string_append(str
, "\r\n");
201 fields_destroy(struct mime_fields
*mf
)
203 g_return_if_fail(mf
!= NULL
);
205 g_hash_table_destroy(mf
->map
);
206 g_list_free(mf
->keys
);
213 static PurpleMimePart
*
214 part_new(PurpleMimeDocument
*doc
)
216 PurpleMimePart
*part
;
218 part
= g_new0(PurpleMimePart
, 1);
219 fields_init(&part
->fields
);
221 part
->data
= g_string_new(NULL
);
223 doc
->parts
= g_list_prepend(doc
->parts
, part
);
230 part_load(PurpleMimePart
*part
, const char *buf
, gsize len
)
233 char *b
= (char *) buf
;
236 fields_load(&part
->fields
, &b
, &n
);
238 /* the remainder will have a blank line, if there's anything at all,
239 so check if there's anything then trim off the trailing four
242 g_string_append_len(part
->data
, b
, n
);
247 part_write(PurpleMimePart
*part
, GString
*str
)
249 fields_write(&part
->fields
, str
);
250 g_string_append_printf(str
, "%s\r\n\r\n", part
->data
->str
);
255 part_free(PurpleMimePart
*part
)
258 fields_destroy(&part
->fields
);
260 g_string_free(part
->data
, TRUE
);
268 purple_mime_part_new(PurpleMimeDocument
*doc
)
270 g_return_val_if_fail(doc
!= NULL
, NULL
);
271 return part_new(doc
);
276 purple_mime_part_get_fields(PurpleMimePart
*part
)
278 g_return_val_if_fail(part
!= NULL
, NULL
);
279 return part
->fields
.keys
;
284 purple_mime_part_get_field(PurpleMimePart
*part
, const char *field
)
286 g_return_val_if_fail(part
!= NULL
, NULL
);
287 return fields_get(&part
->fields
, field
);
292 purple_mime_part_get_field_decoded(PurpleMimePart
*part
, const char *field
)
296 g_return_val_if_fail(part
!= NULL
, NULL
);
298 f
= fields_get(&part
->fields
, field
);
299 return purple_mime_decode_field(f
);
304 purple_mime_part_set_field(PurpleMimePart
*part
, const char *field
, const char *value
)
306 g_return_if_fail(part
!= NULL
);
307 fields_set(&part
->fields
, field
, value
);
312 purple_mime_part_get_data(PurpleMimePart
*part
)
314 g_return_val_if_fail(part
!= NULL
, NULL
);
315 g_return_val_if_fail(part
->data
!= NULL
, NULL
);
317 return part
->data
->str
;
322 purple_mime_part_get_data_decoded(PurpleMimePart
*part
, guchar
**data
, gsize
*len
)
326 g_return_if_fail(part
!= NULL
);
327 g_return_if_fail(data
!= NULL
);
328 g_return_if_fail(len
!= NULL
);
330 g_return_if_fail(part
->data
!= NULL
);
332 enc
= purple_mime_part_get_field(part
, "content-transfer-encoding");
335 *data
= (guchar
*)g_strdup(part
->data
->str
);
336 *len
= part
->data
->len
;
338 } else if(! g_ascii_strcasecmp(enc
, "7bit")) {
339 *data
= (guchar
*)g_strdup(part
->data
->str
);
340 *len
= part
->data
->len
;
342 } else if(! g_ascii_strcasecmp(enc
, "8bit")) {
343 *data
= (guchar
*)g_strdup(part
->data
->str
);
344 *len
= part
->data
->len
;
346 } else if(! g_ascii_strcasecmp(enc
, "base16")) {
347 *data
= purple_base16_decode(part
->data
->str
, len
);
349 } else if(! g_ascii_strcasecmp(enc
, "base64")) {
350 *data
= purple_base64_decode(part
->data
->str
, len
);
352 } else if(! g_ascii_strcasecmp(enc
, "quoted-printable")) {
353 *data
= purple_quotedp_decode(part
->data
->str
, len
);
356 purple_debug_warning("mime", "purple_mime_part_get_data_decoded:"
357 " unknown encoding '%s'\n", enc
);
365 purple_mime_part_get_length(PurpleMimePart
*part
)
367 g_return_val_if_fail(part
!= NULL
, 0);
368 g_return_val_if_fail(part
->data
!= NULL
, 0);
370 return part
->data
->len
;
375 purple_mime_part_set_data(PurpleMimePart
*part
, const char *data
)
377 g_return_if_fail(part
!= NULL
);
378 g_string_free(part
->data
, TRUE
);
379 part
->data
= g_string_new(data
);
384 purple_mime_document_new()
386 PurpleMimeDocument
*doc
;
388 doc
= g_new0(PurpleMimeDocument
, 1);
389 fields_init(&doc
->fields
);
396 doc_parts_load(PurpleMimeDocument
*doc
, const char *boundary
, const char *buf
, gsize len
)
398 char *b
= (char *) buf
;
404 bnd
= g_strdup_printf("--%s", boundary
);
407 for(b
= g_strstr_len(b
, n
, bnd
); b
; ) {
410 /* skip the boundary */
414 /* skip the trailing \r\n or -- as well */
420 /* find the next boundary */
421 tail
= g_strstr_len(b
, n
, bnd
);
428 PurpleMimePart
*part
= part_new(doc
);
429 part_load(part
, b
, sl
);
441 purple_mime_document_parsen(const char *buf
, gsize len
)
443 PurpleMimeDocument
*doc
;
445 char *b
= (char *) buf
;
448 g_return_val_if_fail(buf
!= NULL
, NULL
);
450 doc
= purple_mime_document_new();
455 fields_load(&doc
->fields
, &b
, &n
);
458 const char *ct
= fields_get(&doc
->fields
, "content-type");
459 if(ct
&& purple_str_has_prefix(ct
, "multipart")) {
460 char *bd
= strrchr(ct
, '=');
462 doc_parts_load(doc
, bd
, b
, n
);
472 purple_mime_document_parse(const char *buf
)
474 g_return_val_if_fail(buf
!= NULL
, NULL
);
475 return purple_mime_document_parsen(buf
, strlen(buf
));
480 purple_mime_document_write(PurpleMimeDocument
*doc
, GString
*str
)
482 const char *bd
= NULL
;
484 g_return_if_fail(doc
!= NULL
);
485 g_return_if_fail(str
!= NULL
);
488 const char *ct
= fields_get(&doc
->fields
, "content-type");
489 if(ct
&& purple_str_has_prefix(ct
, "multipart")) {
490 char *b
= strrchr(ct
, '=');
495 fields_write(&doc
->fields
, str
);
500 for(l
= doc
->parts
; l
; l
= l
->next
) {
501 g_string_append_printf(str
, "--%s\r\n", bd
);
503 part_write(l
->data
, str
);
506 g_string_append_printf(str
, "--%s--\r\n", bd
);
514 purple_mime_document_get_fields(PurpleMimeDocument
*doc
)
516 g_return_val_if_fail(doc
!= NULL
, NULL
);
517 return doc
->fields
.keys
;
522 purple_mime_document_get_field(PurpleMimeDocument
*doc
, const char *field
)
524 g_return_val_if_fail(doc
!= NULL
, NULL
);
525 return fields_get(&doc
->fields
, field
);
530 purple_mime_document_set_field(PurpleMimeDocument
*doc
, const char *field
, const char *value
)
532 g_return_if_fail(doc
!= NULL
);
533 fields_set(&doc
->fields
, field
, value
);
538 purple_mime_document_get_parts(PurpleMimeDocument
*doc
)
540 g_return_val_if_fail(doc
!= NULL
, NULL
);
546 purple_mime_document_free(PurpleMimeDocument
*doc
)
551 fields_destroy(&doc
->fields
);
554 part_free(doc
->parts
->data
);
555 doc
->parts
= g_list_delete_link(doc
->parts
, doc
->parts
);