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
);
439 #define BOUNDARY "boundary="
441 parse_boundary(const char *ct
)
443 char *boundary_begin
= g_strstr_len(ct
, -1, BOUNDARY
);
449 boundary_begin
+= sizeof(BOUNDARY
) - 1;
451 if (*boundary_begin
== '"') {
452 boundary_end
= strchr(++boundary_begin
, '"');
456 boundary_end
= strchr(boundary_begin
, ' ');
458 boundary_end
= strchr(boundary_begin
, ';');
460 boundary_end
= boundary_begin
+ strlen(boundary_begin
);
464 return g_strndup(boundary_begin
, boundary_end
- boundary_begin
);
469 purple_mime_document_parsen(const char *buf
, gsize len
)
471 PurpleMimeDocument
*doc
;
473 char *b
= (char *) buf
;
476 g_return_val_if_fail(buf
!= NULL
, NULL
);
478 doc
= purple_mime_document_new();
483 fields_load(&doc
->fields
, &b
, &n
);
486 const char *ct
= fields_get(&doc
->fields
, "content-type");
487 if (ct
&& purple_str_has_prefix(ct
, "multipart")) {
488 char *bd
= parse_boundary(ct
);
490 doc_parts_load(doc
, bd
, b
, n
);
501 purple_mime_document_parse(const char *buf
)
503 g_return_val_if_fail(buf
!= NULL
, NULL
);
504 return purple_mime_document_parsen(buf
, strlen(buf
));
509 purple_mime_document_write(PurpleMimeDocument
*doc
, GString
*str
)
511 const char *bd
= NULL
;
513 g_return_if_fail(doc
!= NULL
);
514 g_return_if_fail(str
!= NULL
);
517 const char *ct
= fields_get(&doc
->fields
, "content-type");
518 if(ct
&& purple_str_has_prefix(ct
, "multipart")) {
519 char *b
= strrchr(ct
, '=');
524 fields_write(&doc
->fields
, str
);
529 for(l
= doc
->parts
; l
; l
= l
->next
) {
530 g_string_append_printf(str
, "--%s\r\n", bd
);
532 part_write(l
->data
, str
);
535 g_string_append_printf(str
, "--%s--\r\n", bd
);
543 purple_mime_document_get_fields(PurpleMimeDocument
*doc
)
545 g_return_val_if_fail(doc
!= NULL
, NULL
);
546 return doc
->fields
.keys
;
551 purple_mime_document_get_field(PurpleMimeDocument
*doc
, const char *field
)
553 g_return_val_if_fail(doc
!= NULL
, NULL
);
554 return fields_get(&doc
->fields
, field
);
559 purple_mime_document_set_field(PurpleMimeDocument
*doc
, const char *field
, const char *value
)
561 g_return_if_fail(doc
!= NULL
);
562 fields_set(&doc
->fields
, field
, value
);
567 purple_mime_document_get_parts(PurpleMimeDocument
*doc
)
569 g_return_val_if_fail(doc
!= NULL
, NULL
);
575 purple_mime_document_free(PurpleMimeDocument
*doc
)
580 fields_destroy(&doc
->fields
);
583 part_free(doc
->parts
->data
);
584 doc
->parts
= g_list_delete_link(doc
->parts
, doc
->parts
);