/*
* 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.
*
* 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 Lesser General Public License
* along with this 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 <glib/gi18n.h>
#include <string.h>
#include "e-util/e-util.h"
#include "eab-book-util.h"
static EABTypeLabel
email_types[] =
{
{ -1, "WORK", NULL, N_ ("Work Email") },
{ -1, "HOME", NULL, N_ ("Home Email") },
{ -1, "OTHER", NULL, N_ ("Other Email") }
};
static EABTypeLabel
sip_types[] =
{
{ E_CONTACT_SIP, "WORK", NULL, N_ ("Work SIP") },
{ E_CONTACT_SIP, "HOME", NULL, N_ ("Home SIP") },
{ E_CONTACT_SIP, "OTHER", NULL, N_ ("Other SIP") }
};
static EABTypeLabel
eab_phone_types[] = {
{ E_CONTACT_PHONE_ASSISTANT, EVC_X_ASSISTANT, NULL, NULL },
{ E_CONTACT_PHONE_BUSINESS, "WORK", "VOICE", NULL },
{ E_CONTACT_PHONE_BUSINESS_FAX, "WORK", "FAX", NULL },
{ E_CONTACT_PHONE_CALLBACK, EVC_X_CALLBACK, NULL, NULL },
{ E_CONTACT_PHONE_CAR, "CAR", NULL, NULL },
{ E_CONTACT_PHONE_COMPANY, "X-EVOLUTION-COMPANY", NULL, NULL },
{ E_CONTACT_PHONE_HOME, "HOME", "VOICE", NULL },
{ E_CONTACT_PHONE_HOME_FAX, "HOME", "FAX", NULL },
{ E_CONTACT_PHONE_ISDN, "ISDN", NULL, NULL },
{ E_CONTACT_PHONE_MOBILE, "CELL", NULL, NULL },
{ E_CONTACT_PHONE_OTHER, "VOICE", NULL, NULL },
{ E_CONTACT_PHONE_OTHER_FAX, "FAX", NULL, NULL },
{ E_CONTACT_PHONE_PAGER, "PAGER", NULL, NULL },
{ E_CONTACT_PHONE_PRIMARY, "PREF", NULL, NULL },
{ E_CONTACT_PHONE_RADIO, EVC_X_RADIO, NULL, NULL },
{ E_CONTACT_PHONE_TELEX, EVC_X_TELEX, NULL, NULL },
{ E_CONTACT_PHONE_TTYTDD, EVC_X_TTYTDD, NULL, NULL }
};
static gboolean eab_phone_types_init = TRUE;
static EABTypeLabel
eab_im_service[] =
{
{ E_CONTACT_IM_AIM, NULL, NULL, N_ ("AIM") },
{ E_CONTACT_IM_JABBER, NULL, NULL, N_ ("Jabber") },
{ E_CONTACT_IM_YAHOO, NULL, NULL, N_ ("Yahoo") },
{ E_CONTACT_IM_GADUGADU, NULL, NULL, N_ ("Gadu-Gadu") },
{ E_CONTACT_IM_MSN, NULL, NULL, N_ ("MSN") },
{ E_CONTACT_IM_ICQ, NULL, NULL, N_ ("ICQ") },
{ E_CONTACT_IM_GROUPWISE, NULL, NULL, N_ ("GroupWise") },
{ E_CONTACT_IM_SKYPE, NULL, NULL, N_ ("Skype") },
{ E_CONTACT_IM_TWITTER, NULL, NULL, N_ ("Twitter") },
{ E_CONTACT_IM_GOOGLE_TALK, NULL, NULL, N_ ("Google Talk")}
};
const EABTypeLabel*
eab_get_email_type_labels (gint *n_elements)
{
*n_elements = G_N_ELEMENTS (email_types);
return email_types;
}
gint
eab_get_email_type_index (EVCardAttribute *attr)
{
gint ii;
for (ii = 0; ii < G_N_ELEMENTS (email_types); ii++) {
if (e_vcard_attribute_has_type (attr, email_types[ii].type_1))
return ii;
}
return -1;
}
void
eab_email_index_to_type (gint index, const gchar **type_1)
{
*type_1 = email_types[index].type_1;
}
const gchar*
eab_get_email_label_text (EVCardAttribute *attr)
{
const gchar *result;
gint n_elements;
gint index = eab_get_email_type_index (attr);
if (index >= 0) {
result = _(eab_get_email_type_labels (&n_elements) [index].text);
} else {
result = _("Email");
}
return result;
}
const EABTypeLabel*
eab_get_sip_type_labels (gint *n_elements)
{
*n_elements = G_N_ELEMENTS (sip_types);
return sip_types;
}
gint
eab_get_sip_type_index (EVCardAttribute *attr)
{
gint ii;
for (ii = 0; ii < G_N_ELEMENTS (sip_types); ii++) {
if (e_vcard_attribute_has_type (attr, sip_types[ii].type_1))
return ii;
}
return -1;
}
void
eab_sip_index_to_type (gint index, const gchar **type_1)
{
*type_1 = sip_types[index].type_1;
}
const gchar*
eab_get_sip_label_text (EVCardAttribute *attr)
{
const gchar *result;
gint n_elements;
gint index = eab_get_sip_type_index (attr);
if (index >= 0) {
result = _(eab_get_sip_type_labels (&n_elements) [index].text);
} else {
result = _("SIP");
}
return result;
}
const EABTypeLabel*
eab_get_im_type_labels (gint *n_elements)
{
*n_elements = G_N_ELEMENTS (eab_im_service);
return eab_im_service;
}
gint
eab_get_im_type_index (EVCardAttribute *attr)
{
gint ii;
const gchar *name;
EContactField field;
for (ii = 0; ii < G_N_ELEMENTS (eab_im_service); ii++) {
name = e_vcard_attribute_get_name (attr);
field = e_contact_field_id_from_vcard (name);
if (field == eab_im_service[ii].field_id)
return ii;
}
return -1;
}
const gchar *
eab_get_im_label_text (EVCardAttribute *attr)
{
const gchar *result;
gint index = eab_get_im_type_index (attr);
if (index >= 0) {
result = _(eab_im_service [index].text);
} else {
result = _("IM");
}
return result;
}
const EABTypeLabel*
eab_get_phone_type_labels (gint *n_elements)
{
*n_elements = G_N_ELEMENTS (eab_phone_types);
if (eab_phone_types_init) {
gint i;
eab_phone_types_init = FALSE;
for (i = 0; i < *n_elements; i++) {
eab_phone_types[i].text = e_contact_pretty_name (eab_phone_types[i].field_id);
}
}
return eab_phone_types;
}
/*
* return the index within eab_phone_types[]
*/
gint
eab_get_phone_type_index (EVCardAttribute *attr)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (eab_phone_types); i++) {
if (e_vcard_attribute_has_type (attr, eab_phone_types[i].type_1) &&
(eab_phone_types[i].type_2 == NULL || e_vcard_attribute_has_type (attr, eab_phone_types[i].type_2)))
return i;
}
return -1;
}
const gchar*
eab_get_phone_label_text (EVCardAttribute *attr)
{
const gchar *result;
gint n_elements;
gint index = eab_get_phone_type_index (attr);
if (index >= 0) {
result = _(eab_get_phone_type_labels (&n_elements) [index].text);
} else {
result = _("Phone");
}
return result;
}
void
eab_phone_index_to_type (gint index,
const gchar **type_1,
const gchar **type_2)
{
*type_1 = eab_phone_types [index].type_1;
*type_2 = eab_phone_types [index].type_2;
}
/* 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;
}