/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* e-destination.c
*
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This library 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 library 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 this library. If not, see .
*
* Authors: Jon Trowbridge
* Chris Toshok
*/
/*
* We should probably make most of the functions in this file a little
* stupider.. all the work extracting useful info from the
* EContact/raw text/etc should happen in e_destination_set_contact
* (and the other setters), not in a bunch of if's in the respective
* _get_*() functions.
*/
#include "evolution-data-server-config.h"
#include "e-destination.h"
#include
#include
#include
#include
#include
#include
#include
#define d(x)
struct _EDestinationPrivate {
gchar *raw;
gchar *source_uid;
EContact *contact;
gchar *contact_uid;
gint email_num;
gchar *name;
gchar *email;
gchar *addr;
gchar *textrep;
gboolean ignored;
GList *list_dests;
GList *list_alldests;
guint html_mail_override : 1;
guint wants_html_mail : 1;
guint show_addresses : 1;
guint auto_recipient : 1;
};
G_DEFINE_TYPE_WITH_PRIVATE (EDestination, e_destination, G_TYPE_OBJECT)
static gboolean e_destination_from_contact (const EDestination *);
static xmlNodePtr e_destination_xml_encode (const EDestination *dest);
static gboolean e_destination_xml_decode (EDestination *dest, xmlNodePtr node);
static void e_destination_clear (EDestination *dest);
/* Signals */
enum {
CHANGED,
LAST_SIGNAL
};
enum CONTACT_TYPE {
NONE,
CONTACT,
CONTACT_LIST
};
static guint signals[LAST_SIGNAL] = { 0 };
/* Copied from eab-book-util.c. The name selector also keeps its own copy... */
static gint
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;
}
/* Copied from eab-book-util.c. The name selector also keeps its own copy... */
static gint
utf8_casefold_collate (const gchar *str1,
const gchar *str2)
{
return utf8_casefold_collate_len (str1, str2, -1);
}
static void
destination_finalize (GObject *object)
{
EDestinationPrivate *priv;
priv = E_DESTINATION (object)->priv;
e_destination_clear (E_DESTINATION (object));
g_free (priv->source_uid);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_destination_parent_class)->finalize (object);
}
static void
e_destination_class_init (EDestinationClass *class)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (class);
object_class->finalize = destination_finalize;
signals[CHANGED] = g_signal_new (
"changed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EDestinationClass, changed),
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
e_destination_init (EDestination *dest)
{
dest->priv = e_destination_get_instance_private (dest);
}
/**
* e_destination_new:
*
* Creates a new #EDestination with blank values.
*
* Returns: A newly created #EDestination.
**/
EDestination *
e_destination_new (void)
{
return g_object_new (E_TYPE_DESTINATION, NULL);
}
/**
* e_destination_copy:
* @dest: an #EDestination
*
* Creates a new #EDestination identical to @dest.
*
* Returns: (transfer full): A newly created #EDestination, identical to @dest.
*/
EDestination *
e_destination_copy (const EDestination *dest)
{
EDestination *new_dest;
GList *iter;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
new_dest = e_destination_new ();
new_dest->priv->source_uid = g_strdup (dest->priv->source_uid);
new_dest->priv->contact_uid = g_strdup (dest->priv->contact_uid);
new_dest->priv->name = g_strdup (dest->priv->name);
new_dest->priv->email = g_strdup (dest->priv->email);
new_dest->priv->addr = g_strdup (dest->priv->addr);
new_dest->priv->email_num = dest->priv->email_num;
new_dest->priv->ignored = dest->priv->ignored;
if (dest->priv->contact)
new_dest->priv->contact = g_object_ref (dest->priv->contact);
new_dest->priv->html_mail_override = dest->priv->html_mail_override;
new_dest->priv->wants_html_mail = dest->priv->wants_html_mail;
/* deep copy, recursively copy our children */
for (iter = dest->priv->list_dests; iter != NULL; iter = g_list_next (iter)) {
new_dest->priv->list_dests = g_list_append (
new_dest->priv->list_dests,
e_destination_copy (E_DESTINATION (iter->data)));
}
/* XXX other settings? */
new_dest->priv->raw = g_strdup (dest->priv->raw);
return new_dest;
}
static void
e_destination_clear (EDestination *dest)
{
g_free (dest->priv->contact_uid);
dest->priv->contact_uid = NULL;
g_free (dest->priv->raw);
dest->priv->raw = NULL;
g_free (dest->priv->name);
dest->priv->name = NULL;
g_free (dest->priv->email);
dest->priv->email = NULL;
g_free (dest->priv->addr);
dest->priv->addr = NULL;
g_free (dest->priv->textrep);
dest->priv->textrep = NULL;
g_clear_object (&dest->priv->contact);
dest->priv->email_num = -1;
g_list_foreach (dest->priv->list_dests, (GFunc) g_object_unref, NULL);
g_list_free (dest->priv->list_dests);
dest->priv->list_dests = NULL;
g_list_free (dest->priv->list_alldests);
dest->priv->list_alldests = NULL;
}
static gboolean
nonempty (const gchar *s)
{
gunichar c;
if (s == NULL)
return FALSE;
while (*s) {
c = g_utf8_get_char (s);
if (!g_unichar_isspace (c))
return TRUE;
s = g_utf8_next_char (s);
}
return FALSE;
}
/**
* e_destination_empty:
* @dest: an #EDestination
*
* Checks if @dest is blank.
*
* Returns: %TRUE if @dest is empty, %FALSE otherwise.
*/
gboolean
e_destination_empty (const EDestination *dest)
{
EDestinationPrivate *p;
g_return_val_if_fail (E_IS_DESTINATION (dest), TRUE);
p = dest->priv;
return !(p->contact != NULL
|| (p->source_uid && *p->source_uid)
|| (p->contact_uid && *p->contact_uid)
|| (nonempty (p->raw))
|| (nonempty (p->name))
|| (nonempty (p->email))
|| (nonempty (p->addr))
|| (p->list_dests != NULL));
}
/**
* e_destination_equal:
* @a: an #EDestination
* @b: an #EDestination
*
* Checks if @a and @b are equal.
*
* Returns: %TRUE if the destinations are equal, %FALSE otherwise.
**/
gboolean
e_destination_equal (const EDestination *a,
const EDestination *b)
{
const EDestinationPrivate *pa, *pb;
const gchar *na, *nb;
g_return_val_if_fail (E_IS_DESTINATION (a), FALSE);
g_return_val_if_fail (E_IS_DESTINATION (b), FALSE);
if (a == b)
return TRUE;
pa = a->priv;
pb = b->priv;
/* Check equality of contacts. */
if (pa->contact || pb->contact) {
if (!(pa->contact && pb->contact))
return FALSE;
if (pa->contact == pb->contact || !strcmp (e_contact_get_const (pa->contact, E_CONTACT_UID),
e_contact_get_const (pb->contact, E_CONTACT_UID)))
return TRUE;
return FALSE;
}
/* Just in case name returns NULL */
na = e_destination_get_name (a);
nb = e_destination_get_name (b);
if ((na || nb) && !(na && nb && !utf8_casefold_collate (na, nb)))
return FALSE;
if (!g_ascii_strcasecmp (e_destination_get_email (a), e_destination_get_email (b)))
return TRUE;
else
return FALSE;
}
static void
remove_empty_subgroups (EDestination *dest,
GHashTable *lists_hash)
{
EDestination *s_dest;
GSList *to_remove = NULL, *siter;
GList *iter;
if (!dest)
return;
for (iter = dest->priv->list_dests; iter; iter = g_list_next (iter)) {
s_dest = iter->data;
remove_empty_subgroups (s_dest, lists_hash);
if (g_hash_table_lookup (lists_hash, s_dest) &&
!s_dest->priv->list_dests)
to_remove = g_slist_prepend (to_remove, s_dest);
}
for (siter = to_remove; siter; siter = g_slist_next (siter)) {
s_dest = siter->data;
dest->priv->list_dests = g_list_remove (dest->priv->list_dests, s_dest);
dest->priv->list_alldests = g_list_remove (dest->priv->list_alldests, s_dest);
}
g_slist_free_full (to_remove, g_object_unref);
}
/**
* e_destination_set_contact:
* @dest: an #EDestination
* @contact: an #EContact
* @email_num: an email index
*
* Sets @dest to point to one of @contact's e-mail addresses
* indicated by @email_num.
**/
void
e_destination_set_contact (EDestination *dest,
EContact *contact,
gint email_num)
{
g_return_if_fail (dest && E_IS_DESTINATION (dest));
g_return_if_fail (contact && E_IS_CONTACT (contact));
if (dest->priv->contact != contact ) {
e_destination_clear (dest);
dest->priv->contact = e_contact_duplicate (contact);
dest->priv->contact_uid = e_contact_get (dest->priv->contact, E_CONTACT_UID);
dest->priv->email_num = email_num;
dest->priv->ignored = FALSE;
/* handle the mailing list case */
if (e_contact_get (dest->priv->contact, E_CONTACT_IS_LIST)) {
gint list_length;
GList *attr, *attrs;
GHashTable *hash_table, *lists_hash;
gint list_iterations = 0;
gint lists_count = 0;
hash_table = g_hash_table_new_full (
g_str_hash, g_str_equal,
(GDestroyNotify) g_free, NULL);
lists_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
g_hash_table_insert (hash_table, g_strdup ("0"), dest);
e_destination_set_name (
dest,
e_contact_get_const (
dest->priv->contact,
E_CONTACT_FILE_AS));
attrs = g_list_copy (e_vcard_get_attributes (E_VCARD (dest->priv->contact)));
list_length = g_list_length (attrs);
attr = attrs;
while (list_length) {
EDestination *parent_dest;
gint type;
gboolean remove = FALSE; /* Can item be removed from attrs list? */
GList *params, *param, *value;
const gchar *parent_id;
param = e_vcard_attribute_get_param (attr->data, EVC_PARENT_CL);
if (param)
parent_id = param->data;
else
parent_id = "0";
/* This is so just that we don't have to call g_ascii_strcasecmp more times */
if (g_ascii_strcasecmp (EVC_CONTACT_LIST, e_vcard_attribute_get_name (attr->data)) == 0) {
lists_count++;
type = CONTACT_LIST;
} else if (g_ascii_strcasecmp (EVC_EMAIL, e_vcard_attribute_get_name (attr->data)) == 0) {
type = CONTACT;
} else {
type = NONE;
remove = TRUE;
}
/* Is parent of current attribute already in the tree? */
parent_dest = g_hash_table_lookup (hash_table, parent_id);
/* Make sure that when parent with parent_id does not exist the item will be appended to root
* destination. */
if (parent_dest == NULL && lists_count == 0 && list_iterations > 0) {
parent_id = "0";
parent_dest = dest;
}
if (type != NONE && parent_dest) {
gchar *id = NULL;
gint set_email_num = 0;
EDestination *s_dest;
s_dest = e_destination_new ();
s_dest->priv->ignored = FALSE;
params = e_vcard_attribute_get_params (attr->data);
for (param = params; param; param = param->next) {
const gchar *param_name = e_vcard_attribute_param_get_name (param->data);
if ((g_ascii_strcasecmp (param_name, EVC_CL_UID) == 0) ||
(g_ascii_strcasecmp (param_name, EVC_X_DEST_CONTACT_UID) == 0)) {
value = e_vcard_attribute_param_get_values (param->data);
id = value ? g_strdup (value->data) : NULL;
} else if (g_ascii_strcasecmp (param_name, EVC_X_DEST_EMAIL_NUM) == 0) {
value = e_vcard_attribute_param_get_values (param->data);
set_email_num = value ? atoi (value->data) : -1;
} else if (!g_ascii_strcasecmp (param_name, EVC_X_DEST_HTML_MAIL)) {
value = e_vcard_attribute_param_get_values (param->data);
e_destination_set_html_mail_pref (s_dest, value ? !g_ascii_strcasecmp (value->data, "true") : FALSE);
}
}
if (type == CONTACT) {
CamelInternetAddress *addr;
const gchar *name, *email;
gchar *raw;
raw = e_vcard_attribute_get_value (attr->data);
addr = camel_internet_address_new ();
if (camel_address_unformat (CAMEL_ADDRESS (addr), raw) > 0) {
camel_internet_address_sanitize_ascii_domain (addr);
if (camel_internet_address_get (addr, 0, &name, &email)) {
e_destination_set_name (s_dest, name);
e_destination_set_email (s_dest, email);
dest->priv->list_alldests = g_list_append (dest->priv->list_alldests, s_dest);
}
}
g_object_unref (addr);
g_free (raw);
} else {
gchar *name = e_vcard_attribute_get_value (attr->data);
e_destination_set_name (s_dest, name);
g_free (name);
if (id)
g_hash_table_insert (hash_table, g_strdup (id), s_dest);
lists_count--;
g_hash_table_insert (lists_hash, s_dest, GINT_TO_POINTER (1));
}
if (id) {
e_destination_set_contact_uid (s_dest, id, set_email_num);
g_free (id);
}
parent_dest->priv->list_dests = g_list_append (parent_dest->priv->list_dests, s_dest);
remove = TRUE;
}
/* Go to next attribute */
if (attr->next) {
attr = attr->next;
if (remove) {
attrs = g_list_delete_link (attrs, attr->prev);
list_length--;
}
continue;
/* Or return to first attribute */
} else if (attrs) {
if (remove) {
attrs = g_list_delete_link (attrs, attr);
list_length--;
}
attr = attrs;
list_iterations++;
continue;
/* When all attribute are processed, leave. */
} else {
break;
}
}
remove_empty_subgroups (dest, lists_hash);
g_hash_table_unref (lists_hash);
g_hash_table_unref (hash_table);
g_list_free (attrs);
} else {
/* handle the normal contact case */
/* is there anything to do here? */
}
g_signal_emit (dest, signals[CHANGED], 0);
} else if (dest->priv->email_num != email_num) {
/* Splitting here would help the contact lists not rebuiding, so that it remembers ignored values */
/* increase ref counter, because e_destination_clear calls g_object_unref, but we want to keep the contact */
g_object_ref (contact);
e_destination_clear (dest);
dest->priv->contact = contact;
dest->priv->contact_uid = e_contact_get (dest->priv->contact, E_CONTACT_UID);
dest->priv->email_num = email_num;
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/**
* e_destination_set_book:
* @dest: an #EDestination
* @book: an #EBook
*
* Specify the source @dest's contact comes from. This is useful
* if you need to update the contact later.
*
* Deprecated: 3.2: Use e_destination_set_client() instead.
**/
void
e_destination_set_book (EDestination *dest,
EBook *book)
{
ESource *source;
const gchar *uid;
g_return_if_fail (dest && E_IS_DESTINATION (dest));
g_return_if_fail (book && E_IS_BOOK (book));
source = e_book_get_source (book);
uid = e_source_get_uid (source);
g_return_if_fail (uid != NULL);
if (!dest->priv->source_uid || strcmp (uid, dest->priv->source_uid)) {
g_free (dest->priv->source_uid);
dest->priv->source_uid = g_strdup (uid);
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/**
* e_destination_set_client:
* @dest: an #EDestination
* @client: an #EBookClient
*
* Specify the source @dest's contact comes from. This is useful
* if you need to update the contact later.
*
* Since: 3.2
**/
void
e_destination_set_client (EDestination *dest,
EBookClient *client)
{
ESource *source;
const gchar *uid;
g_return_if_fail (dest && E_IS_DESTINATION (dest));
g_return_if_fail (client && E_IS_BOOK_CLIENT (client));
source = e_client_get_source (E_CLIENT (client));
uid = e_source_get_uid (source);
g_return_if_fail (uid != NULL);
if (!dest->priv->source_uid || strcmp (uid, dest->priv->source_uid)) {
g_free (dest->priv->source_uid);
dest->priv->source_uid = g_strdup (uid);
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/**
* e_destination_set_contact_uid:
* @dest: an #EDestination
* @uid: a unique contact ID
* @email_num: an email index
*
* Sets @dest to point to one of the contact specified by @uid's e-mail
* addresses indicated by @email_num.
**/
void
e_destination_set_contact_uid (EDestination *dest,
const gchar *uid,
gint email_num)
{
g_return_if_fail (dest && E_IS_DESTINATION (dest));
g_return_if_fail (uid != NULL);
if (dest->priv->contact_uid == NULL
|| strcmp (dest->priv->contact_uid, uid)
|| dest->priv->email_num != email_num) {
g_free (dest->priv->contact_uid);
dest->priv->contact_uid = g_strdup (uid);
dest->priv->email_num = email_num;
/* If we already have a contact, remove it unless its uid matches the one
* we just set. */
if (dest->priv->contact && strcmp (uid,
e_contact_get_const (dest->priv->contact, E_CONTACT_UID))) {
g_object_unref (dest->priv->contact);
dest->priv->contact = NULL;
}
g_signal_emit (dest, signals[CHANGED], 0);
}
}
static void
e_destination_set_source_uid (EDestination *dest,
const gchar *uid)
{
g_return_if_fail (dest && E_IS_DESTINATION (dest));
g_return_if_fail (uid != NULL);
if (dest->priv->source_uid == NULL
|| strcmp (dest->priv->source_uid, uid)) {
g_free (dest->priv->source_uid);
dest->priv->source_uid = g_strdup (uid);
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/**
* e_destination_set_name:
* @dest: an #EDestination
* @name: the destination's full name
*
* Sets the full name of @dest's addressee.
**/
void
e_destination_set_name (EDestination *dest,
const gchar *name)
{
gboolean changed = FALSE;
g_return_if_fail (E_IS_DESTINATION (dest));
if (name == NULL) {
if (dest->priv->name != NULL) {
g_free (dest->priv->name);
dest->priv->name = NULL;
changed = TRUE;
}
} else if (dest->priv->name == NULL || strcmp (dest->priv->name, name)) {
g_free (dest->priv->name);
dest->priv->name = g_strdup (name);
changed = TRUE;
}
if (changed) {
g_free (dest->priv->addr);
dest->priv->addr = NULL;
g_free (dest->priv->textrep);
dest->priv->textrep = NULL;
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/**
* e_destination_set_email:
* @dest: an #EDestination
* @email: the destination's e-mail address
*
* Sets the e-mail address of @dest's addressee.
**/
void
e_destination_set_email (EDestination *dest,
const gchar *email)
{
gboolean changed = FALSE;
g_return_if_fail (E_IS_DESTINATION (dest));
if (email == NULL) {
if (dest->priv->email != NULL) {
g_free (dest->priv->email);
dest->priv->email = NULL;
changed = TRUE;
}
} else if (dest->priv->email == NULL || strcmp (dest->priv->email, email)) {
g_free (dest->priv->email);
dest->priv->email = camel_utils_sanitize_ascii_domain_in_address (email, TRUE);
if (!dest->priv->email)
dest->priv->email = g_strdup (email);
changed = TRUE;
}
if (changed) {
g_free (dest->priv->addr);
dest->priv->addr = NULL;
g_free (dest->priv->textrep);
dest->priv->textrep = NULL;
g_signal_emit (dest, signals[CHANGED], 0);
}
}
static gboolean
e_destination_from_contact (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE);
return dest->priv->contact != NULL || dest->priv->source_uid != NULL || dest->priv->contact_uid != NULL;
}
/**
* e_destination_is_auto_recipient:
* @dest: an #EDestination
*
* Checks if @dest is flagged as an automatic recipient, meaning
* it was not explicitly specified by the user. This can be used
* to hide it from some UI elements.
*
* Returns: %TRUE if destination is an auto recipient, %FALSE otherwise.
**/
gboolean
e_destination_is_auto_recipient (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE);
return dest->priv->auto_recipient;
}
/**
* e_destination_set_auto_recipient:
* @dest: an #EDestination
* @value: the auto recipient flag
*
* Sets the flag indicating if @dest is an automatic recipient, meaning
* it was not explicitly specified by the user. This can be used
* to hide it from some UI elements.
**/
void
e_destination_set_auto_recipient (EDestination *dest,
gboolean value)
{
g_return_if_fail (dest && E_IS_DESTINATION (dest));
dest->priv->auto_recipient = value;
g_signal_emit (dest, signals[CHANGED], 0);
}
/**
* e_destination_get_contact:
* @dest: an #EDestination
*
* Gets the contact @dest is pointing to, if any.
*
* Returns: (transfer none) (nullable): An #EContact, or %NULL if none was set.
**/
EContact *
e_destination_get_contact (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
return dest->priv->contact;
}
/**
* e_destination_get_contact_uid:
* @dest: an #EDestination
*
* Gets the unique contact ID @dest is pointing to, if any.
*
* Returns: (nullable): A unique contact ID, or %NULL if none was set.
*/
const gchar *
e_destination_get_contact_uid (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
return dest->priv->contact_uid;
}
/**
* e_destination_get_source_uid:
* @dest: an #EDestination
*
* Gets the unique source ID @dest is pointing to, if any. The source
* ID specifies which address book @dest's contact came from.
*
* Returns: (nullable): A unique source ID, or %NULL if none was set.
*/
const gchar *
e_destination_get_source_uid (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
return dest->priv->source_uid;
}
/**
* e_destination_get_email_num:
* @dest: an #EDestination
*
* Gets the index of the e-mail address of the contact that
* @dest is pointing to, if any.
*
* Returns: The e-mail index, or -1 if none was set.
**/
gint
e_destination_get_email_num (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), -1);
if (dest->priv->contact == NULL && (dest->priv->source_uid == NULL || dest->priv->contact_uid == NULL))
return -1;
return dest->priv->email_num;
}
/**
* e_destination_get_name:
* @dest: an #EDestination
*
* Gets the full name of @dest's addressee, or if the addressee is
* a contact list, the name the list was filed under. The name can
* be encoded in quoted printable.
*
* Returns: (nullable): The full name of the addressee, or %NULL if none was set.
**/
const gchar *
e_destination_get_name (const EDestination *dest)
{
EDestinationPrivate *priv;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
priv = (EDestinationPrivate *) dest->priv; /* cast out const */
if (priv->name == NULL) {
if (priv->contact != NULL) {
priv->name = e_contact_get (priv->contact, E_CONTACT_FULL_NAME);
if (priv->name == NULL || *priv->name == '\0') {
g_free (priv->name);
priv->name = e_contact_get (priv->contact, E_CONTACT_FILE_AS);
}
if (priv->name == NULL || *priv->name == '\0') {
g_free (priv->name);
if (e_contact_get (priv->contact, E_CONTACT_IS_LIST))
priv->name = g_strdup (_("Unnamed List"));
else
priv->name = g_strdup (e_destination_get_email (dest));
}
}
else if (priv->raw != NULL) {
CamelInternetAddress *addr = camel_internet_address_new ();
if (camel_address_unformat (CAMEL_ADDRESS (addr), priv->raw)) {
const gchar *camel_name = NULL;
if (camel_internet_address_get (addr, 0, &camel_name, NULL))
priv->name = g_strdup (camel_name);
}
g_object_unref (addr);
}
}
return priv->name;
}
/**
* e_destination_is_ignored:
* @dest: an #EDestination
*
* Check if @dest is to be ignored.
*
* Returns: %TRUE if this destination should be ignored, else %FALSE.
*/
gboolean
e_destination_is_ignored (const EDestination *dest)
{
return dest->priv->ignored;
}
/**
* e_destination_set_ignored:
* @dest: an #EDestination
* @ignored: %TRUE if this #EDestination should be ignored.
*
* Set the ignore flag on an #EDestination.
*/
void
e_destination_set_ignored (EDestination *dest,
gboolean ignored)
{
dest->priv->ignored = ignored;
}
/**
* e_destination_get_email:
* @dest: an #EDestination
*
* Gets the e-mail address of @dest's addressee.
*
* Returns: An e-mail address, or an empty string if none was set.
**/
const gchar *
e_destination_get_email (const EDestination *dest)
{
EDestinationPrivate *priv;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
priv = (EDestinationPrivate *) dest->priv; /* cast out const */
if (priv->email == NULL) {
if (priv->contact != NULL) {
/* Pull the address out of the card. */
GList *email = e_contact_get (priv->contact, E_CONTACT_EMAIL);
if (email) {
gchar *e = g_list_nth_data (email, priv->email_num);
if (e)
priv->email = g_strdup (e);
}
if (email) {
g_list_foreach (email, (GFunc) g_free, NULL);
g_list_free (email);
}
} else if (priv->raw != NULL) {
CamelInternetAddress *addr = camel_internet_address_new ();
if (camel_address_unformat (CAMEL_ADDRESS (addr), priv->raw)) {
const gchar *camel_email = NULL;
camel_internet_address_sanitize_ascii_domain (addr);
if (camel_internet_address_get (addr, 0, NULL, &camel_email))
priv->email = g_strdup (camel_email);
}
g_object_unref (addr);
}
/* Force e-mail to be non-null... */
if (priv->email == NULL) {
priv->email = g_strdup ("");
}
}
return priv->email;
}
/* Helper function to e_destination_get_address capable of recursively
* iterating through structured destinations list */
static void
destination_get_address (const EDestination *dest,
CamelInternetAddress *addr)
{
const GList *iter;
if (e_destination_is_evolution_list (dest)) {
for (iter = dest->priv->list_dests; iter; iter = iter->next) {
EDestination *list_dest = E_DESTINATION (iter->data);
destination_get_address (list_dest, addr);
}
} else if (!dest->priv->ignored) {
const gchar *name, *email;
name = e_destination_get_name (dest);
email = e_destination_get_email (dest);
if (nonempty (name) && nonempty (email))
camel_internet_address_add (addr, name, email);
else if (nonempty (email))
camel_address_decode (CAMEL_ADDRESS (addr), email);
else /* this case loses i suppose, but there's
nothing we can do here */
camel_address_decode (CAMEL_ADDRESS (addr), name);
}
}
/**
* e_destination_get_address:
* @dest: an #EDestination
*
* Gets the encoded name and email address, or in the case of lists, the
* encoded list of email addresses, from @dest. The returned string is
* suitable for use in an email header, but not for displaying to users.
*
* Returns: (nullable): an encoded destination string suitable for use in an
* email header, or %NULL if the destination was empty
**/
const gchar *
e_destination_get_address (const EDestination *dest)
{
EDestinationPrivate *priv;
CamelInternetAddress *addr = camel_internet_address_new ();
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
priv = (EDestinationPrivate *) dest->priv; /* cast out const */
g_clear_pointer (&priv->addr, g_free);
if (e_destination_is_evolution_list (dest)) {
destination_get_address (dest, addr);
camel_internet_address_sanitize_ascii_domain (addr);
priv->addr = camel_address_encode (CAMEL_ADDRESS (addr));
} else if (priv->raw) {
if (camel_address_unformat (CAMEL_ADDRESS (addr), priv->raw)) {
camel_internet_address_sanitize_ascii_domain (addr);
priv->addr = camel_address_encode (CAMEL_ADDRESS (addr));
}
} else {
destination_get_address (dest, addr);
camel_internet_address_sanitize_ascii_domain (addr);
priv->addr = camel_address_encode (CAMEL_ADDRESS (addr));
}
g_object_unref (addr);
return priv->addr;
}
/**
* e_destination_set_raw:
* @dest: an #EDestination
* @raw: an unparsed string
*
* Sets @dest to point to the name and e-mail address resulting from
* parsing the supplied string. Useful for user input.
**/
void
e_destination_set_raw (EDestination *dest,
const gchar *raw)
{
g_return_if_fail (E_IS_DESTINATION (dest));
g_return_if_fail (raw != NULL);
if (dest->priv->raw == NULL || strcmp (dest->priv->raw, raw)) {
CamelInternetAddress *addr = camel_internet_address_new ();
e_destination_clear (dest);
if (camel_address_unformat (CAMEL_ADDRESS (addr), raw) > 0 &&
camel_internet_address_sanitize_ascii_domain (addr))
dest->priv->raw = camel_address_format (CAMEL_ADDRESS (addr));
else
dest->priv->raw = g_strdup (raw);
g_object_unref (addr);
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/**
* e_destination_get_textrep:
* @dest: an #EDestination
* @include_email: whether to include the e-mail address
*
* Generates a textual representation of @dest, suitable for referring
* to the destination during user interaction. The name can be encoded
* in quoted printable.
*
* Returns: A textual representation of the destination.
**/
const gchar *
e_destination_get_textrep (const EDestination *dest,
gboolean include_email)
{
const gchar *name, *email;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
if (dest->priv->raw)
return dest->priv->raw;
name = e_destination_get_name (dest);
email = e_destination_get_email (dest);
if (e_destination_from_contact (dest) && name != NULL && (!include_email || !email || !*email))
return name;
/* Make sure that our address gets quoted properly */
if (email && dest->priv->textrep == NULL) {
CamelInternetAddress *addr = camel_internet_address_new ();
camel_internet_address_add (addr, name, email);
g_free (dest->priv->textrep);
camel_internet_address_sanitize_ascii_domain (addr);
dest->priv->textrep = camel_address_format (CAMEL_ADDRESS (addr));
g_object_unref (addr);
}
if (dest->priv->textrep != NULL)
return dest->priv->textrep;
if (email)
return email;
return "";
}
/**
* e_destination_is_evolution_list:
* @dest: an #EDestination
*
* Checks if @dest is a list of addresses.
*
* Returns: %TRUE if destination is a list, %FALSE if it is an individual.
**/
gboolean
e_destination_is_evolution_list (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE);
return dest->priv->list_dests != NULL;
}
/**
* e_destination_list_show_addresses:
* @dest: an #EDestination
*
* If @dest is a list, checks if the addresses in the list
* should be presented to the user during interaction.
*
* Returns: %TRUE if addresses should be shown, %FALSE otherwise.
**/
gboolean
e_destination_list_show_addresses (const EDestination *dest)
{
g_return_val_if_fail (E_IS_DESTINATION (dest), FALSE);
if (dest->priv->contact != NULL)
return GPOINTER_TO_UINT (e_contact_get (dest->priv->contact, E_CONTACT_LIST_SHOW_ADDRESSES));
return dest->priv->show_addresses;
}
/**
* e_destination_list_get_root_dests:
* @dest: an #EDestination
*
* If @dest is a list, gets the list of EDestinations assigned directly
* to @dest.
* The list and its elements belong to @dest, and should not be freed.
*
* Returns: (element-type EDestination) (transfer none) (nullable): A list of elements of type
* #EDestination, or %NULL.
*
* Since: 3.2
**/
const GList *
e_destination_list_get_root_dests (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
if (!e_destination_is_evolution_list (dest))
return NULL;
return dest->priv->list_dests;
}
/**
* e_destination_list_get_dests:
* @dest: an #EDestination
*
* If @dest is a list, gets recursively list of all destinations.
* Everything returned from this function belongs to @dest and
* thus should not be freed.
*
* Returns: (element-type EDestination) (transfer none) (nullable): A list of elements of type
* #EDestination, or %NULL.
*
* Since: 3.2
**/
const GList *
e_destination_list_get_dests (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
if (!e_destination_is_evolution_list (dest))
return NULL;
if (!dest->priv->list_alldests) {
GList *iter;
for (iter = dest->priv->list_dests; iter; iter = iter->next) {
if (e_destination_is_evolution_list (iter->data)) {
GList *l = g_list_copy ((GList *) e_destination_list_get_dests (iter->data));
dest->priv->list_alldests = g_list_concat (dest->priv->list_alldests, l);
} else {
dest->priv->list_alldests = g_list_append (dest->priv->list_alldests, iter->data);
}
}
}
return dest->priv->list_alldests;
}
/**
* e_destination_get_html_mail_pref:
* @dest: an #EDestination
*
* Check if @dest wants to get mail formatted as HTML.
*
* Returns: %TRUE if destination wants HTML, %FALSE if not.
**/
gboolean
e_destination_get_html_mail_pref (const EDestination *dest)
{
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE);
if (dest->priv->html_mail_override || dest->priv->contact == NULL)
return dest->priv->wants_html_mail;
return e_contact_get (dest->priv->contact, E_CONTACT_WANTS_HTML) ? TRUE : FALSE;
}
/**
* e_destination_set_html_mail_pref:
* @dest: an #EDestination
* @flag: whether the destination wants HTML mail
*
* Specifies whether @dest wants to get mail formatted as HTML.
**/
void
e_destination_set_html_mail_pref (EDestination *dest,
gboolean flag)
{
g_return_if_fail (dest && E_IS_DESTINATION (dest));
dest->priv->html_mail_override = TRUE;
if (dest->priv->wants_html_mail != flag) {
dest->priv->wants_html_mail = flag;
g_signal_emit (dest, signals[CHANGED], 0);
}
}
/*
* Destination import/export
*/
/**
* e_destination_get_textrepv:
* @destv: (array zero-terminated=1): %NULL-terminated array of pointers to #EDestination
*
* Generates a joint text representation of all the #EDestination
* elements in @destv.
*
* Returns: The text representation of @destv.
**/
gchar *
e_destination_get_textrepv (EDestination **destv)
{
gint i, j, len = 0;
gchar **strv;
gchar *str;
g_return_val_if_fail (destv, NULL);
/* Q: Please tell me this is only for assertion
* reasons. If this is considered to be ok behavior then you
* shouldn't use g_return's. Just a reminder;-)
*
* A: Yes, this is just an assertion. (Though it does find the
* length of the vector in the process...)
*/
while (destv[len]) {
g_return_val_if_fail (E_IS_DESTINATION (destv[len]), NULL);
len++;
}
strv = g_new0 (gchar *, len + 1);
for (i = 0, j = 0; destv[i]; i++) {
if (!e_destination_empty (destv[i])) {
const gchar *addr = e_destination_get_address (destv[i]);
strv[j++] = addr ? (gchar *) addr : (gchar *) "";
}
}
str = g_strjoinv (", ", strv);
g_free (strv);
return str;
}
/**
* e_destination_xml_encode:
* @dest: an #EDestination
*
* Generates an XML tree from @dest.
*
* Returns: Pointer to the root node of the XML tree.
**/
static xmlNodePtr
e_destination_xml_encode (const EDestination *dest)
{
xmlNodePtr dest_node;
const gchar *str;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
dest_node = xmlNewNode (NULL, (xmlChar *)"destination");
str = e_destination_get_name (dest);
if (str)
xmlNewTextChild (dest_node, NULL, (xmlChar *)"name", (xmlChar *) str);
if (!e_destination_is_evolution_list (dest)) {
str = e_destination_get_email (dest);
if (str)
xmlNewTextChild (dest_node, NULL, (xmlChar *)"email", (xmlChar *) str);
} else {
GList *iter = dest->priv->list_dests;
while (iter) {
EDestination *list_dest = E_DESTINATION (iter->data);
xmlNodePtr list_node = xmlNewNode (NULL, (xmlChar *)"list_entry");
str = e_destination_get_name (list_dest);
if (str) {
xmlChar *escaped = xmlEncodeEntitiesReentrant (NULL, (xmlChar *) str);
xmlNewTextChild (list_node, NULL, (xmlChar *)"name", escaped);
xmlFree (escaped);
}
str = e_destination_get_email (list_dest);
if (str) {
xmlChar *escaped = xmlEncodeEntitiesReentrant (NULL, (xmlChar *) str);
xmlNewTextChild (list_node, NULL, (xmlChar *)"email", escaped);
xmlFree (escaped);
}
xmlAddChild (dest_node, list_node);
iter = g_list_next (iter);
}
xmlNewProp (dest_node, (xmlChar *)"is_list", (xmlChar *)"yes");
xmlNewProp (
dest_node, (xmlChar *)"show_addresses",
e_destination_list_show_addresses (dest) ?
(xmlChar *)"yes" : (xmlChar *)"no");
}
str = e_destination_get_source_uid (dest);
if (str) {
xmlChar *escaped = xmlEncodeEntitiesReentrant (NULL, (xmlChar *) str);
xmlNewTextChild (dest_node, NULL, (xmlChar *)"source_uid", escaped);
xmlFree (escaped);
}
str = e_destination_get_contact_uid (dest);
if (str) {
gchar buf[16];
xmlNodePtr uri_node = xmlNewTextChild (dest_node, NULL, (xmlChar *)"card_uid", (xmlChar *) str);
g_snprintf (buf, 16, "%d", e_destination_get_email_num (dest));
xmlNewProp (uri_node, (xmlChar *)"email_num", (xmlChar *) buf);
}
xmlNewProp (
dest_node, (xmlChar *)"html_mail",
e_destination_get_html_mail_pref (dest) ?
(xmlChar *)"yes" : (xmlChar *)"no");
xmlNewProp (
dest_node, (xmlChar *)"auto_recipient",
e_destination_is_auto_recipient (dest) ?
(xmlChar *)"yes" : (xmlChar *)"no");
return dest_node;
}
/**
* e_destination_xml_decode:
* @dest: an #EDestination
* @node: the root node of an XML tree
*
* Initializes @dest based on the information encoded in the
* XML tree under @node.
*
* Returns: %TRUE if the XML tree was well-formed, %FALSE otherwise.
**/
static gboolean
e_destination_xml_decode (EDestination *dest,
xmlNodePtr node)
{
gchar *name = NULL, *email = NULL, *source_uid = NULL, *card_uid = NULL;
gboolean is_list = FALSE, show_addr = FALSE, auto_recip = FALSE;
gboolean html_mail = FALSE;
GList *list_dests = NULL;
gint email_num = -1;
gchar *tmp;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE);
g_return_val_if_fail (node != NULL, FALSE);
if (strcmp ((gchar *) node->name, "destination"))
return FALSE;
tmp = (gchar *) xmlGetProp (node, (xmlChar *)"html_mail");
if (tmp) {
html_mail = !strcmp (tmp, "yes");
xmlFree (tmp);
}
tmp = (gchar *) xmlGetProp (node, (xmlChar *)"is_list");
if (tmp) {
is_list = !strcmp (tmp, "yes");
xmlFree (tmp);
}
tmp = (gchar *) xmlGetProp (node, (xmlChar *)"show_addresses");
if (tmp) {
show_addr = !strcmp (tmp, "yes");
xmlFree (tmp);
}
tmp = (gchar *) xmlGetProp (node, (xmlChar *)"auto_recipient");
if (tmp) {
auto_recip = !strcmp (tmp, "yes");
xmlFree (tmp);
}
node = node->xmlChildrenNode;
while (node) {
if (!strcmp ((gchar *) node->name, "name")) {
tmp = (gchar *) xmlNodeGetContent (node);
g_free (name);
name = g_strdup (tmp);
xmlFree (tmp);
} else if (!is_list && !strcmp ((gchar *) node->name, "email")) {
tmp = (gchar *) xmlNodeGetContent (node);
g_free (email);
email = g_strdup (tmp);
xmlFree (tmp);
} else if (is_list && !strcmp ((gchar *) node->name, "list_entry")) {
xmlNodePtr subnode = node->xmlChildrenNode;
gchar *list_name = NULL, *list_email = NULL;
while (subnode) {
if (!strcmp ((gchar *) subnode->name, "name")) {
tmp = (gchar *) xmlNodeGetContent (subnode);
g_free (list_name);
list_name = g_strdup (tmp);
xmlFree (tmp);
} else if (!strcmp ((gchar *) subnode->name, "email")) {
tmp = (gchar *) xmlNodeGetContent (subnode);
g_free (list_email);
list_email = g_strdup (tmp);
xmlFree (tmp);
}
subnode = subnode->next;
}
if (list_name || list_email) {
EDestination *list_dest = e_destination_new ();
if (list_name)
e_destination_set_name (list_dest, list_name);
if (list_email)
e_destination_set_email (list_dest, list_email);
g_free (list_name);
g_free (list_email);
list_dests = g_list_append (list_dests, list_dest);
}
} else if (!strcmp ((gchar *) node->name, "source_uid")) {
tmp = (gchar *) xmlNodeGetContent (node);
g_free (source_uid);
source_uid = g_strdup (tmp);
xmlFree (tmp);
} else if (!strcmp ((gchar *) node->name, "card_uid")) {
tmp = (gchar *) xmlNodeGetContent (node);
g_free (card_uid);
card_uid = g_strdup (tmp);
xmlFree (tmp);
tmp = (gchar *) xmlGetProp (node, (xmlChar *)"email_num");
email_num = atoi (tmp);
xmlFree (tmp);
}
node = node->next;
}
e_destination_clear (dest);
if (name) {
e_destination_set_name (dest, name);
g_free (name);
}
if (email) {
e_destination_set_email (dest, email);
g_free (email);
}
if (source_uid) {
e_destination_set_source_uid (dest, source_uid);
g_free (source_uid);
}
if (card_uid) {
e_destination_set_contact_uid (dest, card_uid, email_num);
g_free (card_uid);
}
if (list_dests)
dest->priv->list_dests = list_dests;
dest->priv->html_mail_override = TRUE;
dest->priv->wants_html_mail = html_mail;
dest->priv->show_addresses = show_addr;
dest->priv->auto_recipient = auto_recip;
return TRUE;
}
static gchar *
null_terminate_and_remove_extra_whitespace (xmlChar *xml_in,
gint size)
{
gboolean skip_white = FALSE;
gchar *xml, *r, *w;
if (xml_in == NULL || size <= 0)
return NULL;
xml = g_strndup ((gchar *) xml_in, size);
r = w = xml;
while (*r) {
if (*r == '\n' || *r == '\r') {
skip_white = TRUE;
} else {
gunichar c = g_utf8_get_char (r);
gboolean is_space = g_unichar_isspace (c);
*w = *r;
if (!(skip_white && is_space))
w++;
if (!is_space)
skip_white = FALSE;
}
r = g_utf8_next_char (r);
}
*w = '\0';
return xml;
}
/**
* e_destination_export:
* @dest: an #EDestination
*
* Exports a destination to an XML document.
*
* Returns: An XML string, allocated with g_malloc.
**/
gchar *
e_destination_export (const EDestination *dest)
{
xmlNodePtr dest_node;
xmlDocPtr dest_doc;
xmlChar *buffer = NULL;
gint size = -1;
gchar *str;
g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL);
dest_node = e_destination_xml_encode (dest);
if (dest_node == NULL)
return NULL;
dest_doc = xmlNewDoc ((xmlChar *) XML_DEFAULT_VERSION);
xmlDocSetRootElement (dest_doc, dest_node);
xmlDocDumpMemory (dest_doc, &buffer, &size);
xmlFreeDoc (dest_doc);
str = null_terminate_and_remove_extra_whitespace (buffer, size);
xmlFree (buffer);
return str;
}
/**
* e_destination_import:
* @str: an XML string
*
* Creates an #EDestination from an XML document.
*
* Returns: (transfer full) (nullable): An #EDestination, or %NULL if the document was not
* well-formed.
**/
EDestination *
e_destination_import (const gchar *str)
{
EDestination *dest = NULL;
xmlDocPtr dest_doc;
if (!(str && *str))
return NULL;
dest_doc = xmlParseMemory ((gchar *) str, strlen (str));
if (dest_doc && dest_doc->xmlRootNode) {
dest = e_destination_new ();
if (!e_destination_xml_decode (dest, dest_doc->xmlRootNode)) {
g_object_unref (dest);
dest = NULL;
}
}
xmlFreeDoc (dest_doc);
return dest;
}
/**
* e_destination_exportv:
* @destv: (array zero-terminated=1): a %NULL-terminated array of pointers to #EDestination
*
* Exports multiple #EDestination elements to a single XML document.
*
* Returns: An XML string, allocated with g_malloc.
**/
gchar *
e_destination_exportv (EDestination **destv)
{
xmlDocPtr destv_doc;
xmlNodePtr destv_node;
xmlChar *buffer = NULL;
gint i, size = -1;
gchar *str;
if (destv == NULL || *destv == NULL)
return NULL;
destv_doc = xmlNewDoc ((xmlChar *) XML_DEFAULT_VERSION);
destv_node = xmlNewNode (NULL, (xmlChar *)"destinations");
xmlDocSetRootElement (destv_doc, destv_node);
for (i = 0; destv[i]; i++) {
if (!e_destination_empty (destv[i])) {
xmlNodePtr dest_node = e_destination_xml_encode (destv[i]);
if (dest_node)
xmlAddChild (destv_node, dest_node);
}
}
xmlDocDumpMemory (destv_doc, &buffer, &size);
xmlFreeDoc (destv_doc);
str = null_terminate_and_remove_extra_whitespace (buffer, size);
xmlFree (buffer);
return str;
}
/**
* e_destination_importv:
* @str: an XML string
*
* Creates an array of pointers to #EDestination elements
* from an XML document.
*
* Returns: (transfer full) (array zero-terminated=1): A %NULL-terminated
* array of pointers to #EDestination elements.
**/
EDestination **
e_destination_importv (const gchar *str)
{
GPtrArray *dest_array = NULL;
xmlDocPtr destv_doc;
xmlNodePtr node;
EDestination **destv = NULL;
if (!(str && *str))
return NULL;
destv_doc = xmlParseMemory ((gchar *) str, strlen (str));
if (destv_doc == NULL)
return NULL;
node = destv_doc->xmlRootNode;
if (strcmp ((gchar *) node->name, "destinations"))
goto finished;
node = node->xmlChildrenNode;
dest_array = g_ptr_array_new ();
while (node) {
EDestination *dest;
dest = e_destination_new ();
if (e_destination_xml_decode (dest, node) && !e_destination_empty (dest)) {
g_ptr_array_add (dest_array, dest);
} else {
g_object_unref (dest);
}
node = node->next;
}
/* we need destv to be NULL terminated */
g_ptr_array_add (dest_array, NULL);
destv = (EDestination **) dest_array->pdata;
g_ptr_array_free (dest_array, FALSE);
finished:
xmlFreeDoc (destv_doc);
return destv;
}
/**
* e_destination_freev:
* @destv: (array zero-terminated=1): a %NULL-terminated array of pointers to #EDestination
*
* Unrefs the elements of @destv and frees @destv itself.
**/
void
e_destination_freev (EDestination **destv)
{
gint i;
if (destv) {
for (i = 0; destv[i] != NULL; ++i) {
g_object_unref (destv[i]);
}
g_free (destv);
}
}
/**
* e_destination_export_to_vcard_attribute:
* @dest: an #EDestination
* @attr: an #EVCardAttribute
*
* Exports the contact information from @dest to parameters
* and values in @attr, suitable for an address book.
**/
void
e_destination_export_to_vcard_attribute (EDestination *dest,
EVCardAttribute *attr)
{
e_vcard_attribute_remove_values (attr);
e_vcard_attribute_remove_params (attr);
if (e_destination_get_contact_uid (dest))
e_vcard_attribute_add_param_with_value (
attr,
e_vcard_attribute_param_new (EVC_X_DEST_CONTACT_UID),
e_destination_get_contact_uid (dest));
if (e_destination_get_source_uid (dest))
e_vcard_attribute_add_param_with_value (
attr,
e_vcard_attribute_param_new (EVC_X_DEST_SOURCE_UID),
e_destination_get_source_uid (dest));
if (-1 != e_destination_get_email_num (dest)) {
gchar buf[10];
g_snprintf (buf, sizeof (buf), "%d", e_destination_get_email_num (dest));
e_vcard_attribute_add_param_with_value (
attr,
e_vcard_attribute_param_new (EVC_X_DEST_EMAIL_NUM),
buf);
}
e_vcard_attribute_add_param_with_value (
attr,
e_vcard_attribute_param_new (EVC_X_DEST_HTML_MAIL),
e_destination_get_html_mail_pref (dest) ? "TRUE" : "FALSE");
if (e_destination_get_address (dest))
e_vcard_attribute_add_value (attr, e_destination_get_address (dest));
}