Sun, 31 Mar 2019 18:14:48 -0400
Remove internal MIME handling code.
| ChangeLog.API | file | annotate | diff | comparison | revisions | |
| libpurple/meson.build | file | annotate | diff | comparison | revisions | |
| libpurple/mime.c | file | annotate | diff | comparison | revisions | |
| libpurple/mime.h | file | annotate | diff | comparison | revisions | |
| libpurple/tests/test_util.c | file | annotate | diff | comparison | revisions | |
| libpurple/util.c | file | annotate | diff | comparison | revisions | |
| libpurple/util.h | file | annotate | diff | comparison | revisions |
--- a/ChangeLog.API Sun Mar 31 17:45:25 2019 -0400 +++ b/ChangeLog.API Sun Mar 31 18:14:48 2019 -0400 @@ -432,6 +432,19 @@ * PurpleEventLoopUiOps. Manually drive the GLib event loop yourself. See GLib Main Event Loop docs. * purple_get_tzoff_str(). Use g_date_time_format, instead. + * PurpleMimeDocument, PurpleMimePart, + purple_mime_document_new, purple_mime_document_free, + purple_mime_document_parse, purple_mime_document_parsen, + purple_mime_document_write, purple_mime_document_get_fields, + purple_mime_document_get_field, + purple_mime_document_set_field, + purple_mime_document_get_parts, purple_mime_part_new, + purple_mime_part_get_fields, purple_mime_part_get_field, + purple_mime_part_get_field_decoded, + purple_mime_part_set_field, purple_mime_part_get_data, + purple_mime_part_get_data_decoded, + purple_mime_part_get_length, purple_mime_part_set_data, and + purple_mime_decode_field. Use the GMime library, instead. * purple_network_convert_idn_to_ascii. Use g_hostname_to_ascii, instead. * purple_network_listen_family. Use purple_network_listen, instead. @@ -480,6 +493,7 @@ * purple_print_utf8_to_console * PurplePluginProtocolInfo * purple_proxy_connect_socks5 + * purple_quotedp_decode. See the GMime library if needed. * purple_request_field_list_add * purple_srv_cancel * purple_srv_resolve_account
--- a/libpurple/meson.build Sun Mar 31 17:45:25 2019 -0400 +++ b/libpurple/meson.build Sun Mar 31 18:14:48 2019 -0400 @@ -36,7 +36,6 @@ 'mediamanager.c', 'memorypool.c', 'message.c', - 'mime.c', 'nat-pmp.c', 'network.c', 'notify.c', @@ -115,7 +114,6 @@ 'mediamanager.h', 'memorypool.h', 'message.h', - 'mime.h', 'nat-pmp.h', 'network.h', 'notify.h',
--- a/libpurple/mime.c Sun Mar 31 17:45:25 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,586 +0,0 @@ -/* - * Purple - * - * Purple is the legal property of its developers, whose names are too - * numerous to list here. Please refer to the COPYRIGHT file distributed - * with this source distribution - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, - * USA. - */ - -#include "internal.h" - -/* this should become "util.h" if we ever get this into purple proper */ -#include "debug.h" -#include "mime.h" -#include "util.h" - -/* - * Utility structure used in both MIME document and parts, which maps - * field names to their values, and keeps an easily accessible list of - * keys. - */ -struct mime_fields { - GHashTable *map; - GList *keys; -}; - -struct _PurpleMimeDocument { - struct mime_fields fields; - GList *parts; -}; - -struct _PurpleMimePart { - struct mime_fields fields; - struct _PurpleMimeDocument *doc; - GString *data; -}; - -static void -fields_set(struct mime_fields *mf, const char *key, const char *val) -{ - char *k, *v; - - g_return_if_fail(mf != NULL); - g_return_if_fail(mf->map != NULL); - - k = g_ascii_strdown(key, -1); - v = g_strdup(val); - - /* append to the keys list only if it's not already there */ - if(! g_hash_table_lookup(mf->map, k)) { - mf->keys = g_list_append(mf->keys, k); - } - - /* important to use insert. If the key is already in the table, then - it's already in the keys list. Insert will free the new instance - of the key rather than the old instance. */ - g_hash_table_insert(mf->map, k, v); -} - - -static const char * -fields_get(struct mime_fields *mf, const char *key) -{ - char *kdown; - const char *ret; - - g_return_val_if_fail(mf != NULL, NULL); - g_return_val_if_fail(mf->map != NULL, NULL); - - kdown = g_ascii_strdown(key, -1); - ret = g_hash_table_lookup(mf->map, kdown); - g_free(kdown); - - return ret; -} - - -static void -fields_init(struct mime_fields *mf) -{ - g_return_if_fail(mf != NULL); - - mf->map = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); -} - - -static void -fields_loadline(struct mime_fields *mf, const char *line, gsize len) -{ - /* split the line into key: value */ - char *key, *newkey, *val; - char **tokens; - - /* feh, need it to be NUL terminated */ - key = g_strndup(line, len); - - /* split */ - val = strchr(key, ':'); - if(! val) { - g_free(key); - return; - } - *val++ = '\0'; - - /* normalize whitespace (sorta) and trim on key and value */ - tokens = g_strsplit(key, "\t\r\n", 0); - newkey = g_strjoinv("", tokens); - g_strstrip(newkey); - g_strfreev(tokens); - - tokens = g_strsplit(val, "\t\r\n", 0); - val = g_strjoinv("", tokens); - g_strstrip(val); - g_strfreev(tokens); - - fields_set(mf, newkey, val); - - g_free(newkey); - g_free(key); - g_free(val); -} - - -static void -fields_load(struct mime_fields *mf, char **buf, gsize *len) -{ - char *tail; - - while ((tail = g_strstr_len(*buf, *len, "\r\n"))) - { - char *line; - gsize ln; - - /* determine the current line */ - line = *buf; - ln = tail - line; - - /* advance our search space past the CRLF */ - *buf = tail + 2; - *len -= (ln + 2); - - /* empty line, end of headers */ - if(! ln) return; - - /* look out for line continuations */ - if(line[ln-1] == ';') { - tail = g_strstr_len(*buf, *len, "\r\n"); - if(tail) { - gsize cln; - - cln = tail - *buf; - ln = tail - line; - - /* advance our search space past the CRLF (again) */ - *buf = tail + 2; - *len -= (cln + 2); - } - } - - /* process our super-cool line */ - fields_loadline(mf, line, ln); - } -} - - -static void -field_write(const char *key, const char *val, GString *str) -{ - g_string_append_printf(str, "%s: %s\r\n", key, val); -} - - -static void -fields_write(struct mime_fields *mf, GString *str) -{ - g_return_if_fail(mf != NULL); - - g_hash_table_foreach(mf->map, (GHFunc) field_write, str); - g_string_append(str, "\r\n"); -} - - -static void -fields_destroy(struct mime_fields *mf) -{ - g_return_if_fail(mf != NULL); - - g_hash_table_destroy(mf->map); - g_list_free(mf->keys); - - mf->map = NULL; - mf->keys = NULL; -} - - -static PurpleMimePart * -part_new(PurpleMimeDocument *doc) -{ - PurpleMimePart *part; - - part = g_new0(PurpleMimePart, 1); - fields_init(&part->fields); - part->doc = doc; - part->data = g_string_new(NULL); - - doc->parts = g_list_prepend(doc->parts, part); - - return part; -} - - -static void -part_load(PurpleMimePart *part, const char *buf, gsize len) -{ - - char *b = (char *) buf; - gsize n = len; - - fields_load(&part->fields, &b, &n); - - /* the remainder will have a blank line, if there's anything at all, - so check if there's anything then trim off the trailing four - bytes, \r\n\r\n */ - if(n > 4) n -= 4; - g_string_append_len(part->data, b, n); -} - - -static void -part_write(PurpleMimePart *part, GString *str) -{ - fields_write(&part->fields, str); - g_string_append_printf(str, "%s\r\n\r\n", part->data->str); -} - - -static void -part_free(PurpleMimePart *part) -{ - - fields_destroy(&part->fields); - - g_string_free(part->data, TRUE); - part->data = NULL; - - g_free(part); -} - - -PurpleMimePart * -purple_mime_part_new(PurpleMimeDocument *doc) -{ - g_return_val_if_fail(doc != NULL, NULL); - return part_new(doc); -} - - -GList * -purple_mime_part_get_fields(PurpleMimePart *part) -{ - g_return_val_if_fail(part != NULL, NULL); - return part->fields.keys; -} - - -const char * -purple_mime_part_get_field(PurpleMimePart *part, const char *field) -{ - g_return_val_if_fail(part != NULL, NULL); - return fields_get(&part->fields, field); -} - - -char * -purple_mime_part_get_field_decoded(PurpleMimePart *part, const char *field) -{ - const char *f; - - g_return_val_if_fail(part != NULL, NULL); - - f = fields_get(&part->fields, field); - return purple_mime_decode_field(f); -} - - -void -purple_mime_part_set_field(PurpleMimePart *part, const char *field, const char *value) -{ - g_return_if_fail(part != NULL); - fields_set(&part->fields, field, value); -} - - -const char * -purple_mime_part_get_data(PurpleMimePart *part) -{ - g_return_val_if_fail(part != NULL, NULL); - g_return_val_if_fail(part->data != NULL, NULL); - - return part->data->str; -} - - -void -purple_mime_part_get_data_decoded(PurpleMimePart *part, guchar **data, gsize *len) -{ - const char *enc; - - g_return_if_fail(part != NULL); - g_return_if_fail(data != NULL); - g_return_if_fail(len != NULL); - - g_return_if_fail(part->data != NULL); - - enc = purple_mime_part_get_field(part, "content-transfer-encoding"); - - if(! enc) { - *data = (guchar *)g_strdup(part->data->str); - *len = part->data->len; - - } else if(! g_ascii_strcasecmp(enc, "7bit")) { - *data = (guchar *)g_strdup(part->data->str); - *len = part->data->len; - - } else if(! g_ascii_strcasecmp(enc, "8bit")) { - *data = (guchar *)g_strdup(part->data->str); - *len = part->data->len; - - } else if(! g_ascii_strcasecmp(enc, "base16")) { - *data = purple_base16_decode(part->data->str, len); - - } else if(! g_ascii_strcasecmp(enc, "base64")) { - *data = g_base64_decode(part->data->str, len); - - } else if(! g_ascii_strcasecmp(enc, "quoted-printable")) { - *data = purple_quotedp_decode(part->data->str, len); - - } else { - purple_debug_warning("mime", "purple_mime_part_get_data_decoded:" - " unknown encoding '%s'\n", enc); - *data = NULL; - *len = 0; - } -} - - -gsize -purple_mime_part_get_length(PurpleMimePart *part) -{ - g_return_val_if_fail(part != NULL, 0); - g_return_val_if_fail(part->data != NULL, 0); - - return part->data->len; -} - - -void -purple_mime_part_set_data(PurpleMimePart *part, const char *data) -{ - g_return_if_fail(part != NULL); - g_string_free(part->data, TRUE); - part->data = g_string_new(data); -} - - -PurpleMimeDocument * -purple_mime_document_new() -{ - PurpleMimeDocument *doc; - - doc = g_new0(PurpleMimeDocument, 1); - fields_init(&doc->fields); - - return doc; -} - - -static void -doc_parts_load(PurpleMimeDocument *doc, const char *boundary, const char *buf, gsize len) -{ - char *b = (char *) buf; - gsize n = len; - - char *bnd; - gsize bl; - - bnd = g_strdup_printf("--%s", boundary); - bl = strlen(bnd); - - for(b = g_strstr_len(b, n, bnd); b; ) { - char *tail; - - /* skip the boundary */ - b += bl; - n -= bl; - - /* skip the trailing \r\n or -- as well */ - if(n >= 2) { - b += 2; - n -= 2; - } - - /* find the next boundary */ - tail = g_strstr_len(b, n, bnd); - - if(tail) { - gsize sl; - - sl = tail - b; - if(sl) { - PurpleMimePart *part = part_new(doc); - part_load(part, b, sl); - } - } - - b = tail; - } - - g_free(bnd); -} - -#define BOUNDARY "boundary=" -static char * -parse_boundary(const char *ct) -{ - char *boundary_begin = g_strstr_len(ct, -1, BOUNDARY); - char *boundary_end; - - if (!boundary_begin) - return NULL; - - boundary_begin += sizeof(BOUNDARY) - 1; - - if (*boundary_begin == '"') { - boundary_end = strchr(++boundary_begin, '"'); - if (!boundary_end) - return NULL; - } else { - boundary_end = strchr(boundary_begin, ' '); - if (!boundary_end) { - boundary_end = strchr(boundary_begin, ';'); - if (!boundary_end) - boundary_end = boundary_begin + strlen(boundary_begin); - } - } - - return g_strndup(boundary_begin, boundary_end - boundary_begin); -} -#undef BOUNDARY - -PurpleMimeDocument * -purple_mime_document_parsen(const char *buf, gsize len) -{ - PurpleMimeDocument *doc; - - char *b = (char *) buf; - gsize n = len; - - g_return_val_if_fail(buf != NULL, NULL); - - doc = purple_mime_document_new(); - - if (!len) - return doc; - - fields_load(&doc->fields, &b, &n); - - { - const char *ct = fields_get(&doc->fields, "content-type"); - if (ct && purple_str_has_prefix(ct, "multipart")) { - char *bd = parse_boundary(ct); - if (bd) { - doc_parts_load(doc, bd, b, n); - g_free(bd); - } - } - } - - return doc; -} - - -PurpleMimeDocument * -purple_mime_document_parse(const char *buf) -{ - g_return_val_if_fail(buf != NULL, NULL); - return purple_mime_document_parsen(buf, strlen(buf)); -} - - -void -purple_mime_document_write(PurpleMimeDocument *doc, GString *str) -{ - const char *bd = NULL; - - g_return_if_fail(doc != NULL); - g_return_if_fail(str != NULL); - - { - const char *ct = fields_get(&doc->fields, "content-type"); - if(ct && purple_str_has_prefix(ct, "multipart")) { - char *b = strrchr(ct, '='); - if(b++) bd = b; - } - } - - fields_write(&doc->fields, str); - - if(bd) { - GList *l; - - for(l = doc->parts; l; l = l->next) { - g_string_append_printf(str, "--%s\r\n", bd); - - part_write(l->data, str); - - if(! l->next) { - g_string_append_printf(str, "--%s--\r\n", bd); - } - } - } -} - - -GList * -purple_mime_document_get_fields(PurpleMimeDocument *doc) -{ - g_return_val_if_fail(doc != NULL, NULL); - return doc->fields.keys; -} - - -const char * -purple_mime_document_get_field(PurpleMimeDocument *doc, const char *field) -{ - g_return_val_if_fail(doc != NULL, NULL); - return fields_get(&doc->fields, field); -} - - -void -purple_mime_document_set_field(PurpleMimeDocument *doc, const char *field, const char *value) -{ - g_return_if_fail(doc != NULL); - fields_set(&doc->fields, field, value); -} - - -GList * -purple_mime_document_get_parts(PurpleMimeDocument *doc) -{ - g_return_val_if_fail(doc != NULL, NULL); - return doc->parts; -} - - -void -purple_mime_document_free(PurpleMimeDocument *doc) -{ - if (!doc) - return; - - fields_destroy(&doc->fields); - - while(doc->parts) { - part_free(doc->parts->data); - doc->parts = g_list_delete_link(doc->parts, doc->parts); - } - - g_free(doc); -}
--- a/libpurple/mime.h Sun Mar 31 17:45:25 2019 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,245 +0,0 @@ -/* - * Purple - * - * Purple is the legal property of its developers, whose names are too - * numerous to list here. Please refer to the COPYRIGHT file distributed - * with this source distribution - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, - * USA. - */ - -#ifndef _PURPLE_MIME_H -#define _PURPLE_MIME_H -/** - * SECTION:mime - * @section_id: libpurple-mime - * @short_description: <filename>mime.h</filename> - * @title: Multi-part MIME Message Parsing - * - * Rudimentary parsing of multi-part MIME messages into more - * accessible structures. - */ - -#include <glib.h> - -/** - * PurpleMimeDocument: - * - * A MIME document. - */ -typedef struct _PurpleMimeDocument PurpleMimeDocument; - -/** - * PurpleMimePart: - * - * A part of a multipart MIME document. - */ -typedef struct _PurpleMimePart PurpleMimePart; - -G_BEGIN_DECLS - -/** - * purple_mime_document_new: - * - * Allocate an empty MIME document. - */ -PurpleMimeDocument *purple_mime_document_new(void); - -/** - * purple_mime_document_free: - * @doc: The MIME document to free. - * - * Frees memory used in a MIME document and all of its parts and fields - */ -void purple_mime_document_free(PurpleMimeDocument *doc); - -/** - * purple_mime_document_parse: - * @buf: The NULL-terminated string containing the MIME-encoded data. - * - * Parse a MIME document from a NUL-terminated string. - * - * Returns: A MIME document. - */ -PurpleMimeDocument *purple_mime_document_parse(const char *buf); - -/** - * purple_mime_document_parsen: - * @buf: The string containing the MIME-encoded data. - * @len: Length of buf. - * - * Parse a MIME document from a string - * - * Returns: A MIME document. - */ -PurpleMimeDocument *purple_mime_document_parsen(const char *buf, gsize len); - -/** - * purple_mime_document_write: - * @doc: The MIME document. - * @str: The string to write the MIME document to. - * - * Write (append) a MIME document onto a GString. - */ -void purple_mime_document_write(PurpleMimeDocument *doc, GString *str); - -/** - * purple_mime_document_get_fields: - * @doc: The MIME document. - * - * The list of fields in the header of a document - * - * Returns: (element-type utf8) (transfer none): A list of strings indicating - * the fields (but not the values of the fields) in the header of doc. - */ -GList *purple_mime_document_get_fields(PurpleMimeDocument *doc); - -/** - * purple_mime_document_get_field: - * @doc: The MIME document. - * @field: Case-insensitive field name. - * - * Get the value of a specific field in the header of a document. - * - * Returns: Value associated with the indicated header field, or - * NULL if the field doesn't exist. - */ -const char *purple_mime_document_get_field(PurpleMimeDocument *doc, - const char *field); - -/** - * purple_mime_document_set_field: - * @doc: The MIME document. - * @field: Case-insensitive field name. - * @value: Value to associate with the indicated header field, - * of NULL to remove the field. - * - * Set or replace the value of a specific field in the header of a - * document. - */ -void purple_mime_document_set_field(PurpleMimeDocument *doc, - const char *field, - const char *value); - -/** - * purple_mime_document_get_parts: - * @doc: The MIME document. - * - * The list of parts in a multipart document. - * - * Returns: (element-type PurpleMimePart) (transfer none): List of MIME parts - * contained within doc. - */ -GList *purple_mime_document_get_parts(PurpleMimeDocument *doc); - -/** - * purple_mime_part_new: - * @doc: The new part's parent MIME document. - * - * Create and insert a new part into a MIME document. - */ -PurpleMimePart *purple_mime_part_new(PurpleMimeDocument *doc); - - -/** - * purple_mime_part_get_fields: - * @part: The MIME document part. - * - * The list of fields in the header of a document part. - * - * Returns: (element-type utf8) (transfer none): List of strings indicating the - * fields (but not the values of the fields) in the header of part. - */ -GList *purple_mime_part_get_fields(PurpleMimePart *part); - - -/** - * purple_mime_part_get_field: - * @part: The MIME document part. - * @field: Case-insensitive name of the header field. - * - * Get the value of a specific field in the header of a document part. - * - * Returns: Value of the specified header field, or NULL if the - * field doesn't exist. - */ -const char *purple_mime_part_get_field(PurpleMimePart *part, - const char *field); - -/** - * purple_mime_part_get_field_decoded: - * @part: The MIME document part. - * @field: Case-insensitive name of the header field. - * - * Get the decoded value of a specific field in the header of a - * document part. - */ -char *purple_mime_part_get_field_decoded(PurpleMimePart *part, - const char *field); - -/** - * purple_mime_part_set_field: - * @part: The part of the MIME document. - * @field: Case-insensitive field name - * @value: Value to associate with the indicated header field, - * of NULL to remove the field. - * - * Set or replace the value of a specific field in the header of a - * document. - */ -void purple_mime_part_set_field(PurpleMimePart *part, - const char *field, - const char *value); - -/** - * purple_mime_part_get_data: - * @part: The MIME document part. - * - * Get the (possibly encoded) data portion of a MIME document part. - * - * Returns: NULL-terminated data found in the document part - */ -const char *purple_mime_part_get_data(PurpleMimePart *part); - -/** - * purple_mime_part_get_data_decoded: - * @part: The MIME documemt part. - * @data: Buffer for the data. - * @len: The length of the buffer. - * - * Get the data portion of a MIME document part, after attempting to - * decode it according to the content-transfer-encoding field. If the - * specified encoding method is not supported, this function will - * return NULL. - */ -void purple_mime_part_get_data_decoded(PurpleMimePart *part, - guchar **data, gsize *len); - -/** - * purple_mime_part_get_length: - * @part: The MIME document part. - * - * Get the length of the data portion of a MIME document part. - * - * Returns: Length of the data in the document part. - */ -gsize purple_mime_part_get_length(PurpleMimePart *part); - -void purple_mime_part_set_data(PurpleMimePart *part, const char *data); - -G_END_DECLS - -#endif
--- a/libpurple/tests/test_util.c Sun Mar 31 17:45:25 2019 -0400 +++ b/libpurple/tests/test_util.c Sun Mar 31 18:14:48 2019 -0400 @@ -497,16 +497,6 @@ } /****************************************************************************** - * MIME tests - *****************************************************************************/ -static void -test_util_mime_decode_field(void) { - gchar *result = purple_mime_decode_field("=?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?="); - g_assert_cmpstr("Keld Jørn Simonsen", ==, result); - g_free(result); -} - -/****************************************************************************** * strdup_withhtml tests *****************************************************************************/ static void @@ -584,9 +574,6 @@ g_test_add_func("/util/utf8/strip unprintables", test_util_utf8_strip_unprintables); - g_test_add_func("/util/mime/decode field", - test_util_mime_decode_field); - g_test_add_func("/util/test_strdup_withhtml", test_util_strdup_withhtml);
--- a/libpurple/util.c Sun Mar 31 17:45:25 2019 -0400 +++ b/libpurple/util.c Sun Mar 31 18:14:48 2019 -0400 @@ -165,212 +165,6 @@ } /************************************************************************** - * Quoted Printable Functions (see RFC 2045). - **************************************************************************/ -static const char xdigits[] = - "0123456789abcdef"; - -guchar * -purple_quotedp_decode(const char *str, gsize *ret_len) -{ - char *n, *new; - const char *end, *p; - - n = new = g_malloc(strlen (str) + 1); - end = str + strlen(str); - - for (p = str; p < end; p++, n++) { - if (*p == '=') { - if (p[1] == '\r' && p[2] == '\n') { /* 5.1 #5 */ - n -= 1; - p += 2; - } else if (p[1] == '\n') { /* fuzzy case for 5.1 #5 */ - n -= 1; - p += 1; - } else if (p[1] && p[2]) { - char *nibble1 = strchr(xdigits, tolower(p[1])); - char *nibble2 = strchr(xdigits, tolower(p[2])); - if (nibble1 && nibble2) { /* 5.1 #1 */ - *n = ((nibble1 - xdigits) << 4) | (nibble2 - xdigits); - p += 2; - } else { /* This should never happen */ - *n = *p; - } - } else { /* This should never happen */ - *n = *p; - } - } - else if (*p == '_') - *n = ' '; - else - *n = *p; - } - - *n = '\0'; - - if (ret_len != NULL) - *ret_len = n - new; - - /* Resize to take less space */ - /* new = realloc(new, n - new); */ - - return (guchar *)new; -} - -/************************************************************************** - * MIME Functions - **************************************************************************/ -char * -purple_mime_decode_field(const char *str) -{ - /* - * This is wing's version, partially based on revo/shx's version - * See RFC2047 [which apparently obsoletes RFC1342] - */ - typedef enum { - state_start, state_equal1, state_question1, - state_charset, state_question2, - state_encoding, state_question3, - state_encoded_text, state_question4, state_equal2 = state_start - } encoded_word_state_t; - encoded_word_state_t state = state_start; - const char *cur, *mark; - const char *charset0 = NULL, *encoding0 = NULL, *encoded_text0 = NULL; - GString *new; - - /* token can be any CHAR (supposedly ISO8859-1/ISO2022), not just ASCII */ - #define token_char_p(c) \ - (c != ' ' && !iscntrl(c) && !strchr("()<>@,;:\"/[]?.=", c)) - - /* But encoded-text must be ASCII; alas, isascii() may not exist */ - #define encoded_text_char_p(c) \ - ((c & 0x80) == 0 && c != '?' && c != ' ' && isgraph(c)) - - g_return_val_if_fail(str != NULL, NULL); - - new = g_string_new(NULL); - - /* Here we will be looking for encoded words and if they seem to be - * valid then decode them. - * They are of this form: =?charset?encoding?text?= - */ - - for (cur = str, mark = NULL; *cur; cur += 1) { - switch (state) { - case state_equal1: - if (*cur == '?') { - state = state_question1; - } else { - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_question1: - if (token_char_p(*cur)) { - charset0 = cur; - state = state_charset; - } else { /* This should never happen */ - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_charset: - if (*cur == '?') { - state = state_question2; - } else if (!token_char_p(*cur)) { /* This should never happen */ - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_question2: - if (token_char_p(*cur)) { - encoding0 = cur; - state = state_encoding; - } else { /* This should never happen */ - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_encoding: - if (*cur == '?') { - state = state_question3; - } else if (!token_char_p(*cur)) { /* This should never happen */ - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_question3: - if (encoded_text_char_p(*cur)) { - encoded_text0 = cur; - state = state_encoded_text; - } else if (*cur == '?') { /* empty string */ - encoded_text0 = cur; - state = state_question4; - } else { /* This should never happen */ - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_encoded_text: - if (*cur == '?') { - state = state_question4; - } else if (!encoded_text_char_p(*cur)) { - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - case state_question4: - if (*cur == '=') { /* Got the whole encoded-word */ - char *charset = g_strndup(charset0, encoding0 - charset0 - 1); - char *encoding = g_strndup(encoding0, encoded_text0 - encoding0 - 1); - char *encoded_text = g_strndup(encoded_text0, cur - encoded_text0 - 1); - guchar *decoded = NULL; - gsize dec_len; - if (g_ascii_strcasecmp(encoding, "Q") == 0) - decoded = purple_quotedp_decode(encoded_text, &dec_len); - else if (g_ascii_strcasecmp(encoding, "B") == 0) - decoded = g_base64_decode(encoded_text, &dec_len); - else - decoded = NULL; - if (decoded) { - gsize len; - char *converted = g_convert((const gchar *)decoded, dec_len, "utf-8", charset, NULL, &len, NULL); - - if (converted) { - g_string_append_len(new, converted, len); - g_free(converted); - } - g_free(decoded); - } - g_free(charset); - g_free(encoding); - g_free(encoded_text); - state = state_equal2; /* Restart the FSM */ - } else { /* This should never happen */ - g_string_append_len(new, mark, cur - mark + 1); - state = state_start; - } - break; - default: - if (*cur == '=') { - mark = cur; - state = state_equal1; - } else { - /* Some unencoded text. */ - g_string_append_c(new, *cur); - } - break; - } /* switch */ - } /* for */ - - if (state != state_start) - g_string_append_len(new, mark, cur - mark + 1); - - return g_string_free(new, FALSE);; -} - - -/************************************************************************** * Date/Time Functions **************************************************************************/
--- a/libpurple/util.h Sun Mar 31 17:45:25 2019 -0400 +++ b/libpurple/util.h Sun Mar 31 18:14:48 2019 -0400 @@ -158,52 +158,6 @@ gchar *purple_base16_encode_chunked(const guchar *data, gsize len); /**************************************************************************/ -/* Quoted Printable Functions */ -/**************************************************************************/ - -/** - * purple_quotedp_decode: - * @str: The quoted printable ASCII string to convert to raw data. - * @ret_len: The length of the returned data. - * - * Converts a quoted printable string back to its readable equivalent. - * What is a quoted printable string, you ask? It's an encoding used - * to transmit binary data as ASCII. It's intended purpose is to send - * emails containing non-ASCII characters. Wikipedia has a pretty good - * explanation. Also see RFC 2045. - * - * Returns: The readable string. Must be g_free'd when no longer needed. - */ -guchar *purple_quotedp_decode(const char *str, gsize *ret_len); - -/**************************************************************************/ -/* MIME Functions */ -/**************************************************************************/ - -/** - * purple_mime_decode_field: - * @str: The ASCII string, possibly containing any number of - * encoded-word sections. - * - * Converts a MIME header field string back to its readable equivalent - * according to RFC 2047. Basically, a header is plain ASCII and can - * contain any number of sections called "encoded-words." The format - * of an encoded word is =?ISO-8859-1?Q?Keld_J=F8rn_Simonsen?= - * =? designates the beginning of the encoded-word - * ?= designates the end of the encoded-word - * - * An encoded word is segmented into three pieces by the use of a - * question mark. The first piece is the character set, the second - * piece is the encoding, and the third piece is the encoded text. - * - * Returns: The string, with any encoded-word sections decoded and - * converted to UTF-8. Must be g_free'd when no longer - * needed. - */ -char *purple_mime_decode_field(const char *str); - - -/**************************************************************************/ /* Date/Time Functions */ /**************************************************************************/