diff options
Diffstat (limited to 'addressbook/util/eab-book-util.c')
-rw-r--r-- | addressbook/util/eab-book-util.c | 306 |
1 files changed, 306 insertions, 0 deletions
diff --git a/addressbook/util/eab-book-util.c b/addressbook/util/eab-book-util.c new file mode 100644 index 0000000000..d7c0941c69 --- /dev/null +++ b/addressbook/util/eab-book-util.c @@ -0,0 +1,306 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Jon Trowbridge <trow@ximian.com> + * Chris Toshok <toshok@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include "e-util/e-util.h" +#include "eab-book-util.h" + +/* Copied from camel_strstrcase */ +static gchar * +eab_strstrcase (const gchar *haystack, + const gchar *needle) +{ + /* find the needle in the haystack neglecting case */ + const gchar *ptr; + guint len; + + g_return_val_if_fail (haystack != NULL, NULL); + g_return_val_if_fail (needle != NULL, NULL); + + len = strlen (needle); + if (len > strlen (haystack)) + return NULL; + + if (len == 0) + return (gchar *) haystack; + + for (ptr = haystack; *(ptr + len - 1) != '\0'; ptr++) + if (!g_ascii_strncasecmp (ptr, needle, len)) + return (gchar *) ptr; + + return NULL; +} + +GSList * +eab_contact_list_from_string (const gchar *str) +{ + GSList *contacts = NULL; + GString *gstr = g_string_new (NULL); + gchar *str_stripped; + gchar *p = (gchar *) str; + gchar *q; + + if (!p) + return NULL; + + if (!strncmp (p, "Book: ", 6)) { + p = strchr (p, '\n'); + if (!p) { + g_warning (G_STRLOC ": Got book but no newline!"); + return NULL; + } + p++; + } + + while (*p) { + if (*p != '\r') g_string_append_c (gstr, *p); + + p++; + } + + p = str_stripped = g_string_free (gstr, FALSE); + + /* Note: The vCard standard says + * + * vcard = "BEGIN" [ws] ":" [ws] "VCARD" [ws] 1*CRLF + * items *CRLF "END" [ws] ":" [ws] "VCARD" + * + * which means we can have whitespace (e.g. "BEGIN : VCARD"). So we're not being + * fully compliant here, although I'm not sure it matters. The ideal solution + * would be to have a vcard parsing function that returned the end of the vcard + * parsed. Arguably, contact list parsing should all be in libebook's e-vcard.c, + * where we can do proper parsing and validation without code duplication. */ + + for (p = eab_strstrcase (p, "BEGIN:VCARD"); p; p = eab_strstrcase (q, "\nBEGIN:VCARD")) { + gchar *card_str; + + if (*p == '\n') + p++; + + for (q = eab_strstrcase (p, "END:VCARD"); q; q = eab_strstrcase (q, "END:VCARD")) { + gchar *temp; + + q += 9; + temp = q; + if (*temp) + temp += strspn (temp, "\r\n\t "); + + if (*temp == '\0' || !g_ascii_strncasecmp (temp, "BEGIN:VCARD", 11)) + break; /* Found the outer END:VCARD */ + } + + if (!q) + break; + + card_str = g_strndup (p, q - p); + contacts = g_slist_prepend (contacts, e_contact_new_from_vcard (card_str)); + g_free (card_str); + } + + g_free (str_stripped); + + return g_slist_reverse (contacts); +} + +gchar * +eab_contact_list_to_string (const GSList *contacts) +{ + GString *str = g_string_new (""); + const GSList *l; + + for (l = contacts; l; l = l->next) { + EContact *contact = l->data; + gchar *vcard_str; + + e_contact_inline_local_photos (contact, NULL); + vcard_str = e_vcard_to_string ( + E_VCARD (contact), EVC_FORMAT_VCARD_30); + + g_string_append (str, vcard_str); + if (l->next) + g_string_append (str, "\r\n\r\n"); + } + + return g_string_free (str, FALSE); +} + +gboolean +eab_source_and_contact_list_from_string (ESourceRegistry *registry, + const gchar *str, + ESource **out_source, + GSList **out_contacts) +{ + ESource *source; + const gchar *s0, *s1; + gchar *uid; + gboolean success = FALSE; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); + g_return_val_if_fail (str != NULL, FALSE); + + if (out_source != NULL) + *out_source = NULL; /* in case we fail */ + + if (out_contacts != NULL) + *out_contacts = NULL; /* in case we fail */ + + if (!strncmp (str, "Book: ", 6)) { + s0 = str + 6; + s1 = strchr (str, '\r'); + + if (!s1) + s1 = strchr (str, '\n'); + } else { + s0 = NULL; + s1 = NULL; + } + + if (!s0 || !s1) + return FALSE; + + uid = g_strndup (s0, s1 - s0); + source = e_source_registry_ref_source (registry, uid); + if (source != NULL) { + if (out_source != NULL) + *out_source = g_object_ref (source); + g_object_unref (source); + success = TRUE; + } + g_free (uid); + + if (success && out_contacts != NULL) + *out_contacts = eab_contact_list_from_string (str); + + return success; +} + +gchar * +eab_book_and_contact_list_to_string (EBookClient *book_client, + const GSList *contacts) +{ + gchar *s0, *s1; + + s0 = eab_contact_list_to_string (contacts); + if (!s0) + s0 = g_strdup (""); + + if (book_client != NULL) { + EClient *client; + ESource *source; + const gchar *uid; + + client = E_CLIENT (book_client); + source = e_client_get_source (client); + uid = e_source_get_uid (source); + s1 = g_strconcat ("Book: ", uid, "\r\n", s0, NULL); + } else + s1 = g_strdup (s0); + + g_free (s0); + return s1; +} + +/* bad place for this i know. */ +gint +e_utf8_casefold_collate_len (const gchar *str1, + const gchar *str2, + gint len) +{ + gchar *s1 = g_utf8_casefold (str1, len); + gchar *s2 = g_utf8_casefold (str2, len); + gint rv; + + rv = g_utf8_collate (s1, s2); + + g_free (s1); + g_free (s2); + + return rv; +} + +gint +e_utf8_casefold_collate (const gchar *str1, + const gchar *str2) +{ + return e_utf8_casefold_collate_len (str1, str2, -1); +} + +/* To parse something like... + * =?UTF-8?Q?=E0=A4=95=E0=A4=95=E0=A4=AC=E0=A5=82=E0=A5=8B=E0=A5=87?=\t\n=?UTF-8?Q?=E0=A4=B0?=\t\n<aa@aa.ccom> + * and return the decoded representation of name & email parts. */ +gboolean +eab_parse_qp_email (const gchar *string, + gchar **name, + gchar **email) +{ + struct _camel_header_address *address; + gboolean res = FALSE; + + address = camel_header_address_decode (string, "UTF-8"); + + if (!address) + return FALSE; + + /* report success only when we have filled both name and email address */ + if (address->type == CAMEL_HEADER_ADDRESS_NAME && address->name && *address->name && address->v.addr && *address->v.addr) { + *name = g_strdup (address->name); + *email = g_strdup (address->v.addr); + res = TRUE; + } + + camel_header_address_unref (address); + + return res; +} + +/* This is only wrapper to parse_qp_mail, it decodes string and if returned TRUE, + * then makes one string and returns it, otherwise returns NULL. + * Returned string is usable to place directly into GtkHtml stream. + * Returned value should be freed with g_free. */ +gchar * +eab_parse_qp_email_to_html (const gchar *string) +{ + gchar *name = NULL, *mail = NULL; + gchar *html_name, *html_mail; + gchar *value; + + if (!eab_parse_qp_email (string, &name, &mail)) + return NULL; + + html_name = e_text_to_html (name, 0); + html_mail = e_text_to_html (mail, E_TEXT_TO_HTML_CONVERT_ADDRESSES); + + value = g_strdup_printf ("%s <%s>", html_name, html_mail); + + g_free (html_name); + g_free (html_mail); + g_free (name); + g_free (mail); + + return value; +} |