From da9c4d10641eed27263f12c7415100348991ef32 Mon Sep 17 00:00:00 2001 From: Jon Trowbridge Date: Sat, 7 Jul 2001 06:38:52 +0000 Subject: Detect the embedded EDestination XML, and convert it to a nice-looking 2001-07-07 Jon Trowbridge * gui/widgets/e-minicard.c (add_field): Detect the embedded EDestination XML, and convert it to a nice-looking e-mail address. * gui/contact-list-editor/e-contact-list-editor.c: Adjusted to reflect changes to EContactListModel. (Yeah, this is vague, but the changes are _really_ obvious and boring.) * gui/contact-list-editor/e-contact-list-model.c: Revamped to make everything an EDestination, rather than storing either cards or strings. * gui/component/select-names/e-select-names-text-model.c (e_select_names_text_model_insert_length): Use e_destination_get_textrep. * gui/component/select-names/e-select-names-table-model.c (fill_in_info): Use e_destination_get_name and e_destination_get_email. * gui/component/select-names/e-select-names-popup.c (popup_menu_card): Use e_destination_get_name instead of e_card_name_to_string. (quick_add_cb): Use e_destination_get_address. (popup_menu_nocard): Use e_destination_get_name. * gui/component/select-names/e-select-names-model.c (e_select_names_model_changed): Removed obsolete debugging code. (e_select_names_model_get_textification): Use e_destination_get_textrep\. (e_select_names_model_get_address_text): Use e_destination_get_address. (e_select_names_model_get_string): Use e_destination_get_textrep. (e_select_names_model_replace): Use e_destination_get_textrep. (e_select_names_model_name_pos): Use e_destination_get_textrep. * gui/component/select-names/e-select-names-completion.c (emailify_match): Match only if this isn't a contact list. (match_email): Match only if this isn't a contact list. (match_name): Do the right thing in the case of a contact list. (book_query_process_card_list): Don't construct a match for each possible e-mail address if this is a contact list. * backend/ebook/e-destination.c: Major-league de-crufting and rationalization, combined with lots of hacks to make things work properly with contact lists. (e_destination_copy): Copy contact list info. (e_destination_clear_card): Clear contact list info. (e_destination_is_empty): If we contain a contact list, we aren't empty. (e_destination_set_card_uri): Added. Allows us to set a destination by card URI. (e_destination_set_name): Allows the contact's name only ("Jane Smith") to be set. (e_destination_set_email): Allows the contact's e-mail only ("jane@assbarn.com") to be set. (e_destination_set_string): Takes a free-form string containing a contact's name and/or e-mail, and tries to do the right thing with it. (e_destination_contains_card): Renamed. Used to be e_destination_has_card. (e_destination_from_card): Added. Returns TRUE if the EDestination comes from either a ECard (which we presently hold) or is specified by an ECard URI. (e_destination_use_card): Allows an EDestination's ECard to be accessed, via a callback. If we only are holding the URI, the card will be loaded and stored in the EDestination. (e_destination_get_name): Returns the name only ("Jane Smith"), or NULL. (e_destination_get_email): Returns the email only ("jane@assbarn.com"), or NULL. (e_destination_get_address): Added. Returns a "full address" if both the name & e-mail are available ("Jane Smith "), or just the e-mail if the name is unknown ("jane@assbarn.com>"). If the destination is a contact list, returns a comma-separated list of addresses. (e_destination_get_textrep): Added. Returns a "text representation" of the EDestination. This is what is now displayed for completed, "cardified" destinations in addressbook entries. (e_destination_is_evolution_list): Check to see if this destination is for a contact list. (e_destination_xml_encode): Added. Build an XML data structure representing an EDestination. (e_destination_xml_decode): Added. Parse an XML data structure, constructing an EDestination. (e_destination_export): Added. Returns a null-terminated string containing an XML representation of the EDestination, with newlines and excess whitespace removed. (e_destination_import): Added. Parses a string containing an XML representation of an EDestination. (e_destination_exportv): Added. Returns a null-terminated string containing an XML representation of a collection of EDestinations, with newlines and excess whitespace removed. (e_destination_importv): Added. Takes an XML representation of a collection of destinations, parses it, and returns a vector of EDestinations. * backend/ebook/e-card.c (e_card_duplicate): Copy the ->book pointer. (e_card_get_id): Check that the argument is valid. (e_card_set_id): Check that the argument is valid. (e_card_get_book): Added. Return the EBook the ECard came from. (e_card_get_uri): Added. Tries to returns a URI for the ECard, which is of the form (EBook URI)/(ECard unique ID). Returns NULL if the EBook URI or the ID are unknown/not set. (e_card_get_vobject): If we have the URI, use it as the VCUniqueStringProp, rather than just the ID. This is a hack to make DnD work properly. (parse_id): Detect if the unique ID we've been passed is a URI or just a plain card ID, and do the right thing in either case. (e_card_uri_extract_book_uri): Added. Convenience function for parsing card URIs. (e_card_uri_extract_card_id): Added. Convenience function for parsing card URIs. (e_card_load_uri): Added. Allows an ECard to be loaded by its URI. * backend/ebook/e-book-view.c: Added a EBook * to the _EBookViewPrivate struct. This is meant to contain the EBook the EBookView is associated with. (add_book_iterator): Added. A convenience function for attaching the EBook to a GList of cards (if no EBook is already stored). (e_book_view_do_added_event): Record the EBook in the added ECards. (e_book_view_do_modified_event): Record the EBook in the modified ECards. (e_book_view_set_book): Added. Stores a pointer to the EBookView's "parent" EBook. (e_book_view_init): Init book_view->priv->book to NULL. (e_book_view_destroy): Unref book_view->priv->book. * backend/ebook/e-book.c: Added a uri field to _EBookPrivate. (e_book_unqueue_op): Removed debugging spew. (e_book_do_response_get_view): Attach the current EBook to the created EBookView. (e_book_do_response_get_changes): Attach the current EBook to the created EBookView. (e_book_load_uri): Save a copy of the uri in the EBook. (e_book_get_uri): Added. Just returns book->priv->uri. (e_book_get_card): Attach a pointer to the Ebook to the newly-loaded ECard. (e_book_add_card): Attach a pointer to the EBook to the newly-added ECard. (e_book_commit_card): Attach a pointer to the EBook to the committed ECard. (e_book_init): Initialize the uri to NULL. (e_book_destroy): Free the uri string on destruction. svn path=/trunk/; revision=10882 --- addressbook/backend/ebook/e-destination.c | 874 ++++++++++++++++++------------ 1 file changed, 522 insertions(+), 352 deletions(-) (limited to 'addressbook/backend/ebook/e-destination.c') diff --git a/addressbook/backend/ebook/e-destination.c b/addressbook/backend/ebook/e-destination.c index 4a1d2992cb..421b13cf07 100644 --- a/addressbook/backend/ebook/e-destination.c +++ b/addressbook/backend/ebook/e-destination.c @@ -28,28 +28,33 @@ #include #include "e-destination.h" +#include +#include #include #include #include #include #include "e-book.h" #include "e-book-util.h" +#include +#include +#include struct _EDestinationPrivate { - gchar *pending_card_id; - + gchar *card_uri; ECard *card; gint card_email_num; gchar *name; - gchar *string; - gchar *string_email; - gchar *string_email_verbose; + gchar *email; + gchar *addr; gboolean html_mail_override; gboolean wants_html_mail; + + GList *list_dests; }; static void e_destination_clear_card (EDestination *); @@ -62,9 +67,7 @@ e_destination_destroy (GtkObject *obj) { EDestination *dest = E_DESTINATION (obj); - e_destination_clear_card (dest); - e_destination_clear_strings (dest); - + e_destination_clear (dest); g_free (dest->priv); if (parent_class->destroy) @@ -119,65 +122,85 @@ EDestination * e_destination_copy (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->pending_card_id = g_strdup (new_dest->priv->pending_card_id); + new_dest->priv->card_uri = g_strdup (dest->priv->card_uri); + 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->card_email_num = dest->priv->card_email_num; - new_dest->priv->card = dest->priv->card; + new_dest->priv->card = dest->priv->card; if (new_dest->priv->card) gtk_object_ref (GTK_OBJECT (new_dest->priv->card)); - new_dest->priv->card_email_num = dest->priv->card_email_num; + new_dest->priv->html_mail_override = dest->priv->html_mail_override; + new_dest->priv->wants_html_mail = dest->priv->wants_html_mail; - new_dest->priv->string = g_strdup (dest->priv->string); - new_dest->priv->string_email = g_strdup (dest->priv->string_email); - new_dest->priv->string_email_verbose = g_strdup (dest->priv->string_email_verbose); + 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))); + } return new_dest; } -gboolean -e_destination_is_empty (EDestination *dest) -{ - struct _EDestinationPrivate *p; - g_return_val_if_fail (dest && E_IS_DESTINATION (dest), TRUE); - p = dest->priv; - - return !(p->card - || p->pending_card_id - || (p->string && *p->string) - || (p->name && *p->name) - || (p->string_email && *p->string_email)); -} - static void e_destination_clear_card (EDestination *dest) { - g_free (dest->priv->pending_card_id); - dest->priv->pending_card_id = NULL; + g_free (dest->priv->card_uri); + dest->priv->card_uri = NULL; if (dest->priv->card) gtk_object_unref (GTK_OBJECT (dest->priv->card)); dest->priv->card = NULL; dest->priv->card_email_num = -1; + + g_list_foreach (dest->priv->list_dests, (GFunc) gtk_object_unref, NULL); + g_list_free (dest->priv->list_dests); + dest->priv->list_dests = NULL; } static void e_destination_clear_strings (EDestination *dest) { g_free (dest->priv->name); - g_free (dest->priv->string); - g_free (dest->priv->string_email); - g_free (dest->priv->string_email_verbose); - dest->priv->name = NULL; - dest->priv->string = NULL; - dest->priv->string_email = NULL; - dest->priv->string_email_verbose = NULL; + + g_free (dest->priv->email); + dest->priv->email = NULL; + + g_free (dest->priv->addr); + dest->priv->addr = NULL; +} + +void +e_destination_clear (EDestination *dest) +{ + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + + e_destination_clear_card (dest); + e_destination_clear_strings (dest); +} + +gboolean +e_destination_is_empty (EDestination *dest) +{ + struct _EDestinationPrivate *p; + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), TRUE); + p = dest->priv; + + return !(p->card != NULL + || (p->card_uri && *p->card_uri) + || (p->name && *p->name) + || (p->email && *p->email) + || (p->addr && *p->addr) + || (p->list_dests != NULL)); } void @@ -186,31 +209,103 @@ e_destination_set_card (EDestination *dest, ECard *card, gint email_num) g_return_if_fail (dest && E_IS_DESTINATION (dest)); g_return_if_fail (card && E_IS_CARD (card)); - if (dest->priv->pending_card_id) { - g_free (dest->priv->pending_card_id); - dest->priv->pending_card_id = NULL; - } + e_destination_clear (dest); - if (dest->priv->card != card) { - if (dest->priv->card) - gtk_object_unref (GTK_OBJECT (dest->priv->card)); - dest->priv->card = card; - gtk_object_ref (GTK_OBJECT (card)); - } + dest->priv->card = card; + gtk_object_ref (GTK_OBJECT (dest->priv->card)); dest->priv->card_email_num = email_num; +} - e_destination_clear_strings (dest); +void +e_destination_set_card_uri (EDestination *dest, const gchar *uri, gint email_num) +{ + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + g_return_if_fail (uri != NULL); + + g_free (dest->priv->card_uri); + dest->priv->card_uri = g_strdup (uri); + dest->priv->card_email_num = email_num; + + /* If we already have a card, remove it unless it's uri matches the one + we just set. */ + if (dest->priv->card && strcmp (uri, e_card_get_uri (dest->priv->card))) { + gtk_object_unref (GTK_OBJECT (dest->priv->card)); + dest->priv->card = NULL; + } } void -e_destination_set_string (EDestination *dest, const gchar *string) +e_destination_set_name (EDestination *dest, const gchar *name) { g_return_if_fail (dest && E_IS_DESTINATION (dest)); - g_return_if_fail (string != NULL); + g_return_if_fail (name != NULL); - g_free (dest->priv->string); - dest->priv->string = g_strdup (string); + g_free (dest->priv->name); + dest->priv->name = g_strdup (name); +} + +void +e_destination_set_email (EDestination *dest, const gchar *email) +{ + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + g_return_if_fail (email != NULL); + + g_free (dest->priv->email); + dest->priv->email = g_strdup (email); +} + + +/* This function takes a free-form string and tries to do something + intelligent with it. */ +void +e_destination_set_string (EDestination *dest, const gchar *str) +{ + gchar *name = NULL; + gchar *email = NULL; + gchar *lt, *gt; + + g_return_if_fail (dest && E_IS_DESTINATION (dest)); + g_return_if_fail (str != NULL); + + /* Look for something of the form Jane Smith */ + if ( (lt = strrchr (str, '<')) && (gt = strrchr (str, '>')) && lt+1 < gt) { + name = g_strndup (str, lt-str); + email = g_strndup (lt+1, gt-lt-1); + + /* I love using goto. It makes me feel so wicked. */ + goto finished; + } + + /* If it contains '@', assume it is an e-mail address. */ + if (strchr (str, '@')) { + email = g_strdup (str); + goto finished; + } + + /* If we contain whitespace, that is very suggestive of being a name. */ + if (strchr (str, ' ')) { + name = g_strdup (str); + goto finished; + } + + /* Default: Just treat it as an e-mail address. */ + email = g_strdup (str); + + finished: + if (name) { + g_strstrip (name); + if (*name) + e_destination_set_name (dest, name); + g_free (name); + } + + if (email) { + g_strstrip (email); + if (*email) + e_destination_set_email (dest, email); + g_free (email); + } } void @@ -223,17 +318,17 @@ e_destination_set_html_mail_pref (EDestination *dest, gboolean x) } gboolean -e_destination_has_card (const EDestination *dest) +e_destination_contains_card (const EDestination *dest) { g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE); return dest->priv->card != NULL; } gboolean -e_destination_has_pending_card (const EDestination *dest) +e_destination_from_card (const EDestination *dest) { g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE); - return dest->priv->pending_card_id != NULL; + return dest->priv->card != NULL || dest->priv->card_uri != NULL; } @@ -245,35 +340,22 @@ struct _UseCard { }; static void -use_card_cb (EBook *book, gpointer closure) +use_card_cb (ECard *card, gpointer closure) { UseCard *uc = (UseCard *) closure; - ECard *card; - - if (book != NULL && uc->dest->priv->card == NULL) { - - if (uc->dest->priv->pending_card_id) { - card = e_book_get_card (book, uc->dest->priv->pending_card_id); + if (card != NULL && uc->dest->priv->card == NULL) { - if (card) { - ECard *old = uc->dest->priv->card; - uc->dest->priv->card = card; - gtk_object_ref (GTK_OBJECT (card)); - if (old) - gtk_object_unref (GTK_OBJECT (old)); - } - - g_free (uc->dest->priv->pending_card_id); - uc->dest->priv->pending_card_id = NULL; - - } + uc->dest->priv->card = card; + gtk_object_ref (GTK_OBJECT (uc->dest->priv->card)); } - if (uc->cb) + if (uc->cb) { uc->cb (uc->dest, uc->dest->priv->card, uc->closure); + } + /* We held a copy of the destination during the callback. */ gtk_object_unref (GTK_OBJECT (uc->dest)); g_free (uc); } @@ -289,13 +371,15 @@ e_destination_use_card (EDestination *dest, EDestinationCardCallback cb, gpointe cb (dest, dest->priv->card, closure); } - } else { + } else if (dest->priv->card_uri) { + UseCard *uc = g_new (UseCard, 1); uc->dest = dest; + /* Hold a reference to the destination during the callback. */ gtk_object_ref (GTK_OBJECT (uc->dest)); uc->cb = cb; uc->closure = closure; - e_book_use_local_address_book (use_card_cb, uc); + e_card_load_uri (dest->priv->card_uri, use_card_cb, uc); } } @@ -307,62 +391,29 @@ e_destination_get_card (const EDestination *dest) return dest->priv->card; } -gint -e_destination_get_email_num (const EDestination *dest) -{ - g_return_val_if_fail (dest && E_IS_DESTINATION (dest), -1); - - return dest->priv->card_email_num; -} - const gchar * -e_destination_get_string (const EDestination *dest) +e_destination_get_card_uri (const EDestination *dest) { - struct _EDestinationPrivate *priv; - g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); - - priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ - if (priv->string == NULL) { - - if (priv->card) { - - priv->string = e_card_name_to_string (priv->card->name); - if (priv->string) { - g_strstrip (priv->string); - if (*(priv->string) == '\0') { - g_free (priv->string); - priv->string = NULL; - } - } - - if (priv->string == NULL) - priv->string = g_strdup (e_destination_get_email (dest)); - - if (priv->string == NULL) - priv->string = g_strdup (_("???")); - - } else { /* If there is no card... */ - - if (priv->name) - return priv->name; - - } - } + if (dest->priv->card_uri) + return dest->priv->card_uri; - return priv->string; + if (dest->priv->card) + return e_card_get_uri (dest->priv->card); + + return NULL; } gint -e_destination_get_strlen (const EDestination *dest) +e_destination_get_email_num (const EDestination *dest) { - const gchar *str; + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), -1); - g_return_val_if_fail (dest && E_IS_DESTINATION (dest), 0); + if (dest->priv->card == NULL && dest->priv->card_uri == NULL) + return -1; - str = e_destination_get_string (dest); - return str ? strlen (str) : 0; + return dest->priv->card_email_num; } const gchar * @@ -373,15 +424,9 @@ e_destination_get_name (const EDestination *dest) priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ - if (priv->name == NULL) { - - if (priv->card) { - - priv->name = e_card_name_to_string (priv->card->name); - - } - } - + if (priv->name == NULL && priv->card != NULL) + priv->name = e_card_name_to_string (priv->card->name); + return priv->name; } @@ -395,7 +440,7 @@ e_destination_get_email (const EDestination *dest) priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ - if (priv->string_email == NULL) { + if (priv->email == NULL) { if (priv->card) { /* Pull the address out of the card. */ @@ -410,46 +455,118 @@ e_destination_get_email (const EDestination *dest) if (e_iterator_is_valid (iter)) { gconstpointer ptr = e_iterator_get (iter); - priv->string_email = g_strdup ((gchar *) ptr); + priv->email = g_strdup ((gchar *) ptr); } } - } else if (priv->string) { /* Use the string as an e-mail address */ - return priv->string; } - - /* else we just return NULL */ } - return priv->string_email; + return priv->email; } const gchar * -e_destination_get_email_verbose (const EDestination *dest) +e_destination_get_address (const EDestination *dest) { struct _EDestinationPrivate *priv; - + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); priv = (struct _EDestinationPrivate *)dest->priv; /* cast out const */ + + if (priv->addr == NULL) { - if (priv->string_email_verbose == NULL) { - - const gchar *email = e_destination_get_email (dest); - const gchar *name = e_destination_get_name (dest); + if (e_destination_is_evolution_list (dest)) { + + gchar **strv = g_new0 (gchar *, g_list_length (priv->list_dests) + 1); + gint i = 0; + GList *iter = dest->priv->list_dests; + + while (iter) { + EDestination *list_dest = E_DESTINATION (iter->data); + if (! e_destination_is_empty (list_dest)) { + strv[i] = (gchar *) e_destination_get_address (list_dest); + ++i; + } + iter = g_list_next (iter); + } + + priv->addr = g_strjoinv (", ", strv); - if (name) { + g_message ("List address is [%s]", priv->addr); - priv->string_email_verbose = g_strdup_printf ("%s <%s>", name, email); + g_free (strv); } else { - return email; + const gchar *email = e_destination_get_email (dest); + + if (email) { /* If this isn't set, we return NULL */ + + const gchar *name = e_destination_get_name (dest); + + if (name) { + + gboolean needs_quotes = (strchr (name, '.') != NULL); + + priv->addr = g_strdup_printf ("%s%s%s <%s>", + needs_quotes ? "\"" : "", + name, + needs_quotes ? "\"" : "", + email); + + } else { + + priv->addr = g_strdup (email); + + } + } } + } + + return priv->addr; +} + +const gchar * +e_destination_get_textrep (const EDestination *dest) +{ + const gchar *txt; + + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + + txt = e_destination_get_name (dest); + if (txt) + return txt; + txt = e_destination_get_email (dest); + if (txt) + return txt; + + return NULL; +} + +gboolean +e_destination_is_evolution_list (const EDestination *dest) +{ + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE); + + if (dest->priv->list_dests == NULL + && dest->priv->card != NULL + && dest->priv->card->email != NULL + && e_card_evolution_list (dest->priv->card)) { + + EIterator *iter = e_list_get_iterator (dest->priv->card->email); + e_iterator_reset (iter); + while (e_iterator_is_valid (iter)) { + const gchar *dest_xml = (const gchar *) e_iterator_get (iter); + EDestination *list_dest = e_destination_import (dest_xml); + if (list_dest) + dest->priv->list_dests = g_list_append (dest->priv->list_dests, list_dest); + e_iterator_next (iter); + } } - return priv->string_email_verbose; + return dest->priv->list_dests != NULL; } gboolean @@ -480,7 +597,7 @@ e_destination_get_address_textv (EDestination **destv) for (i = 0, j = 0; destv[i]; ++i) { if (! e_destination_is_empty (destv[i])) { - const gchar *addr = e_destination_get_email_verbose (destv[i]); + const gchar *addr = e_destination_get_address (destv[i]); strv[j++] = addr ? (gchar *) addr : ""; } } @@ -492,289 +609,342 @@ e_destination_get_address_textv (EDestination **destv) return str; } -/* - * - * Serialization code - * - */ +xmlNodePtr +e_destination_xml_encode (const EDestination *dest) +{ + xmlNodePtr dest_node; + const gchar *str; -#define DESTINATION_TAG "DEST" + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); -#define DESTINATION_SEPARATOR "|" -#define DESTINATION_SEPARATOR_CHAR '|' + dest_node = xmlNewNode (NULL, "destination"); -#define VEC_SEPARATOR "\1" -#define VEC_SEPARATOR_CHAR '\1' + str = e_destination_get_name (dest); + if (str) + xmlNewTextChild (dest_node, NULL, "name", str); -static gchar * -join_strings (gchar **strv) -{ - /* FIXME: Should also quote any |'s that occur in any of the strings. */ - /* (We fake it by mapping | to _ when building our fields below) */ - return g_strjoinv (DESTINATION_SEPARATOR, strv); -} + if (! e_destination_is_evolution_list (dest)) { -static gchar ** -unjoin_string (const gchar *str) -{ - /* FIXME: Should properly handle quoteded |'s in the string. */ - /* (We fake it by mapping | to _ when building our fields below) */ - return g_strsplit (str, DESTINATION_SEPARATOR, 0); -} + str = e_destination_get_email (dest); + if (str) + xmlNewTextChild (dest_node, NULL, "email", str); -static gchar * -build_field (const gchar *key, const gchar *value) -{ - gchar *field; - gchar *c; + } else { - g_return_val_if_fail (key != NULL, NULL); - g_return_val_if_fail (value != NULL, NULL); + GList *iter = dest->priv->list_dests; + while (iter) { + EDestination *list_dest = E_DESTINATION (iter->data); + xmlNodePtr list_node = xmlNewNode (NULL, "list_entry"); + + str = e_destination_get_name (list_dest); + if (str) + xmlNewTextChild (list_node, NULL, "name", str); + + str = e_destination_get_email (list_dest); + if (str) + xmlNewTextChild (list_node, NULL, "email", str); - field = g_strdup_printf ("%s=%s", key, value); + xmlAddChild (dest_node, list_node); + + iter = g_list_next (iter); + } - /* Paranoia: Convert any '=' in the key to '_' */ - c = field; - while (*key) { - if (*c == '=') - *c = '_'; - ++key; - ++c; + xmlNewProp (dest_node, "is_list", "yes"); } - - /* Paranoia: Convert any '\1' or '|' in the key or value to '_' */ - for (c=field; *c; ++c) { - if (*c == VEC_SEPARATOR_CHAR || *c == DESTINATION_SEPARATOR_CHAR) - *c = '_'; + + str = e_destination_get_card_uri (dest); + if (str) { + gchar buf[16]; + xmlNodePtr uri_node = xmlNewTextChild (dest_node, NULL, "card_uri", str); + g_snprintf (buf, 16, "%d", e_destination_get_email_num (dest)); + xmlNewProp (uri_node, "email_num", buf); } - return field; -} + xmlNewProp (dest_node, "html_mail", e_destination_get_html_mail_pref (dest) ? "yes" : "no"); -/* Modifies string in place, \0-terminates after the key, returns pointer to "value", - or NULL if the field is malformed. */ -static gchar * -extract_field (gchar *field) -{ - gchar *s = strchr (field, '='); - if (s == NULL) - return NULL; - *s = '\0'; - return s+1; + return dest_node; } -#define EXPORT_MAX_FIELDS 10 -gchar * -e_destination_export (const EDestination *dest) +gboolean +e_destination_xml_decode (EDestination *dest, xmlNodePtr node) { - ECard *card; - gchar **fields; - gchar *str; - gint i; - - g_return_val_if_fail (dest && E_IS_DESTINATION (dest), NULL); + gchar *name = NULL, *email = NULL, *card_uri = NULL; + gint email_num = -1; + gboolean html_mail = FALSE; + gboolean is_list = FALSE; + gchar *tmp; + GList *list_dests = NULL; - fields = g_new (gchar *, EXPORT_MAX_FIELDS); - fields[0] = g_strdup (DESTINATION_TAG); - fields[1] = build_field ("addr", e_destination_get_email (dest)); + g_return_val_if_fail (dest && E_IS_DESTINATION (dest), FALSE); + g_return_val_if_fail (node != NULL, FALSE); - i = 2; + if (strcmp (node->name, "destination")) + return FALSE; - if (e_destination_get_name (dest)) - fields[i++] = build_field ("name", e_destination_get_name (dest)); + tmp = xmlGetProp (node, "html_mail"); + if (tmp) { + html_mail = !strcmp (tmp, "yes"); + xmlFree (tmp); + } - fields[i++] = build_field ("html", - e_destination_get_html_mail_pref (dest) ? "Y" : "N"); + tmp = xmlGetProp (node, "is_list"); + if (tmp) { + is_list = !strcmp (tmp, "yes"); + xmlFree (tmp); + } - card = e_destination_get_card (dest); - if (card) - fields[i++] = build_field ("card", e_card_get_id (card)); + node = node->xmlChildrenNode; + while (node) { - fields[i] = NULL; - + if (!strcmp (node->name, "name")) { + + tmp = xmlNodeGetContent (node); + g_free (name); + name = e_utf8_xml1_decode (tmp); + xmlFree (tmp); - str = join_strings (fields); - g_strfreev (fields); - - return str; -} + } else if (!is_list && !strcmp (node->name, "email")) { -EDestination * -e_destination_import (const gchar *str) -{ - EDestination *dest; - gchar **fields; - gint i; + tmp = xmlNodeGetContent (node); + g_free (email); + email = e_utf8_xml1_decode (tmp); + xmlFree (tmp); - gchar *addr = NULL, *name = NULL, *card = NULL; - gboolean want_html = FALSE; - - g_return_val_if_fail (str, NULL); + } else if (is_list && !strcmp (node->name, "list_entry")) { - fields = unjoin_string (str); - g_return_val_if_fail (fields && fields[0], NULL); - g_return_val_if_fail (!strcmp (fields[0], DESTINATION_TAG), NULL); - - for (i = 1; fields[i]; ++i) { - gchar *key = fields[i]; - gchar *value = extract_field (fields[i]); - - if (value) { + xmlNodePtr subnode = node->xmlChildrenNode; + gchar *list_name = NULL, *list_email = NULL; - if (!strcmp ("addr", key)) { + while (subnode) { - if (addr) { - g_warning ("addr redefined: \"%s\" => \"%s\"", addr, value); - } + if (!strcmp (subnode->name, "name")) { - addr = g_strdup (value); - - } else if (!strcmp ("name", key)) { + tmp = xmlNodeGetContent (subnode); + g_free (list_name); + list_name = e_utf8_xml1_decode (tmp); + xmlFree (tmp); - if (name) { - g_warning ("name redefined: \"%s\" => \"%s\"", name, value); - } + } else if (!strcmp (subnode->name, "email")) { + + tmp = xmlNodeGetContent (subnode); + g_free (list_email); + list_email = e_utf8_xml1_decode (tmp); + xmlFree (tmp); - name = g_strdup (value); + } - } else if (!strcmp ("html", key)) { + subnode = subnode->next; + } - want_html = (*value == 'Y'); + 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); - } else if (!strcmp ("card", key)) { + list_dests = g_list_append (list_dests, list_dest); + + } - if (card) { - g_warning ("card redefined: \"%s\" => \"%s\"", card, value); - } + } else if (!strcmp (node->name, "card_uri")) { - card = g_strdup (value); + tmp = xmlNodeGetContent (node); + g_free (card_uri); + card_uri = e_utf8_xml1_decode (tmp); + xmlFree (tmp); - } + tmp = xmlGetProp (node, "email_num"); + email_num = atoi (tmp); + xmlFree (tmp); } + node = node->next; } - dest = e_destination_new (); + e_destination_clear (dest); - /* We construct this part of the object in a rather abusive way. */ - dest->priv->string_email = addr; - dest->priv->name = name; - dest->priv->pending_card_id = card; + if (name) + e_destination_set_name (dest, name); + if (email) + e_destination_set_email (dest, email); + if (card_uri) + e_destination_set_card_uri (dest, card_uri, email_num); + if (list_dests) + dest->priv->list_dests = list_dests; - e_destination_set_html_mail_pref (dest, want_html); + return TRUE; +} - g_strfreev (fields); +/* FIXME: Make utf-8 safe */ +static gchar * +null_terminate_and_remove_extra_whitespace (xmlChar *xml_in, gint size) +{ + gchar *xml; + gchar *r, *w; + gboolean skip_white = FALSE; - return dest; + if (xml_in == NULL || size <= 0) + return NULL; + + xml = g_strndup (xml_in, size); + r = w = xml; + + while (*r) { + if (*r == '\n' || *r == '\r') { + skip_white = TRUE; + } else { + gboolean is_space = isspace (*r); + + *w = *r; + + if (! (skip_white && is_space)) + ++w; + if (! is_space) + skip_white = FALSE; + } + ++r; + } + + *w = '\0'; + + return xml; } gchar * -e_destination_exportv (EDestination **destv) +e_destination_export (const EDestination *dest) { - gint i, j, len = 0; - gchar **strv; - gchar *str; + 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); - g_return_val_if_fail (destv, NULL); + dest_node = e_destination_xml_encode (dest); + if (dest_node == NULL) + return NULL; - while (destv[len]) { - g_return_val_if_fail (E_IS_DESTINATION (destv[len]), NULL); - ++len; - } + dest_doc = xmlNewDoc (XML_DEFAULT_VERSION); + xmlDocSetRootElement (dest_doc, dest_node); - strv = g_new0 (gchar *, len+1); - for (i = 0, j = 0; i < len; ++i) { - if (! e_destination_is_empty (destv[i])) - strv[j++] = e_destination_export (destv[i]); - } + xmlDocDumpMemory (dest_doc, &buffer, &size); + xmlFreeDoc (dest_doc); + + str = null_terminate_and_remove_extra_whitespace (buffer, size); + xmlFree (buffer); + + return str; +} - str = g_strjoinv (VEC_SEPARATOR, strv); +EDestination * +e_destination_import (const gchar *str) +{ + EDestination *dest = NULL; + xmlDocPtr dest_doc; - for (i = 0; i < len; ++i) { - if (strv[i]) { - g_free (strv[i]); + 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)) { + gtk_object_unref (GTK_OBJECT (dest)); + dest = NULL; } } - g_free (strv); + xmlFreeDoc (dest_doc); - return str; + return dest; } -EDestination ** -e_destination_importv (const gchar *str) +gchar * +e_destination_exportv (EDestination **destv) { - gchar** strv; - EDestination **destv; - gint i = 0, j = 0, len = 0; - - if (!(str && *str)) + xmlDocPtr destv_doc; + xmlNodePtr destv_node; + xmlChar *buffer = NULL; + gint size = -1; + gchar *str; + gint i; + + if (destv == NULL || *destv == NULL) return NULL; - - strv = g_strsplit (str, VEC_SEPARATOR, 0); - while (strv[len]) - ++len; - destv = g_new0 (EDestination *, len+1); + destv_doc = xmlNewDoc (XML_DEFAULT_VERSION); + destv_node = xmlNewNode (NULL, "destinations"); + xmlDocSetRootElement (destv_doc, destv_node); - while (strv[i]) { - EDestination *dest = e_destination_import (strv[i]); - if (dest) { - destv[j++] = dest; + for (i=0; destv[i]; ++i) { + if (! e_destination_is_empty (destv[i])) { + xmlNodePtr dest_node = e_destination_xml_encode (destv[i]); + if (dest_node) + xmlAddChild (destv_node, dest_node); } - ++i; } - g_strfreev (strv); - return destv; + xmlDocDumpMemory (destv_doc, &buffer, &size); + xmlFreeDoc (destv_doc); + + str = null_terminate_and_remove_extra_whitespace (buffer, size); + xmlFree (buffer); + + return str; } EDestination ** -e_destination_importv_list (EBook *book, ECard *list) +e_destination_importv (const gchar *str) { - EList *email_list; - EIterator *email_iter; - EDestination **destv; - gint j = 0; + GList *dest_list = NULL, *iter; + xmlDocPtr destv_doc; + xmlNodePtr node; + EDestination **destv = NULL; + gint N; - if (!e_card_evolution_list (list)) + if (! (str && *str)) return NULL; - gtk_object_get (GTK_OBJECT(list), - "email", &email_list, - NULL); + destv_doc = xmlParseMemory ((gchar *)str, strlen (str)); + node = destv_doc->xmlRootNode; - destv = g_new0 (EDestination *, e_list_length (email_list) +1); + if (strcmp (node->name, "destinations")) + goto finished; - email_iter = e_list_get_iterator (email_list); - - while (e_iterator_is_valid (email_iter)) { - const char *email = e_iterator_get (email_iter); - - if (!strncmp (email, ECARD_UID_LINK_PREFIX, strlen (ECARD_UID_LINK_PREFIX))) { - /* it's a serialized uid */ - ECard *card; - const char *uid; - uid = email + strlen (ECARD_UID_LINK_PREFIX); - card = e_book_get_card (book, uid); - if (card) { - EDestination *dest = e_destination_new (); - e_destination_set_card (dest, card, 0); - gtk_object_unref (GTK_OBJECT (card)); /* XXX ? */ - destv[j++] = dest; - } - } - else { - /* it's an email address */ - EDestination *dest = e_destination_new(); - dest->priv->string_email = g_strdup (email); + node = node->xmlChildrenNode; + + while (node) { + EDestination *dest; - if (dest) { - destv[j++] = dest; - } + dest = e_destination_new (); + if (e_destination_xml_decode (dest, node)) { + dest_list = g_list_prepend (dest_list, dest); + } else { + gtk_object_unref (GTK_OBJECT (dest)); } + + node = node->next; } + N = g_list_length (dest_list); + destv = g_new0 (EDestination *, N+1); + + /* We write the EDestinations into the list from back to front, to + undo the reversal caused by using g_list_prepend instead of + g_list_append. */ + iter = dest_list; + while (iter != NULL) { + destv[N-1] = E_DESTINATION (iter->data); + iter = g_list_next (iter); + --N; + } + + finished: + xmlFreeDoc (destv_doc); + g_list_free (dest_list); + return destv; } -- cgit v1.2.3