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/ChangeLog | 148 +++++ addressbook/backend/ebook/e-book-view.c | 37 ++ addressbook/backend/ebook/e-book-view.h | 5 +- addressbook/backend/ebook/e-book.c | 37 +- addressbook/backend/ebook/e-book.h | 2 + addressbook/backend/ebook/e-card-simple.c | 7 +- addressbook/backend/ebook/e-card.c | 144 ++++- addressbook/backend/ebook/e-card.h | 15 +- addressbook/backend/ebook/e-destination.c | 874 ++++++++++++++++++------------ addressbook/backend/ebook/e-destination.h | 27 +- 10 files changed, 922 insertions(+), 374 deletions(-) (limited to 'addressbook') diff --git a/addressbook/ChangeLog b/addressbook/ChangeLog index 9a2fcaf41e..e355eb6e52 100644 --- a/addressbook/ChangeLog +++ b/addressbook/ChangeLog @@ -1,3 +1,151 @@ +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. + 2001-07-06 Ettore Perazzoli * gui/component/addressbook-component.c (populate_context_menu): diff --git a/addressbook/backend/ebook/e-book-view.c b/addressbook/backend/ebook/e-book-view.c index 2a9f3f2f53..7dbe7d46fb 100644 --- a/addressbook/backend/ebook/e-book-view.c +++ b/addressbook/backend/ebook/e-book-view.c @@ -16,11 +16,14 @@ #include "e-card-cursor.h" #include "e-book-view-listener.h" #include "e-book-view.h" +#include "e-book.h" GtkObjectClass *e_book_view_parent_class; struct _EBookViewPrivate { GNOME_Evolution_Addressbook_BookView corba_book_view; + + EBook *book; EBookViewListener *listener; @@ -38,10 +41,25 @@ enum { static guint e_book_view_signals [LAST_SIGNAL]; +static void +add_book_iterator (gpointer data, gpointer closure) +{ + ECard *card = E_CARD (data); + EBook *book = E_BOOK (closure); + + if (card->book == NULL) { + card->book = book; + gtk_object_ref (GTK_OBJECT (book)); + } +} + static void e_book_view_do_added_event (EBookView *book_view, EBookViewListenerResponse *resp) { + if (book_view->priv->book) + g_list_foreach (resp->cards, add_book_iterator, book_view->priv->book); + gtk_signal_emit (GTK_OBJECT (book_view), e_book_view_signals [CARD_ADDED], resp->cards); @@ -53,6 +71,9 @@ static void e_book_view_do_modified_event (EBookView *book_view, EBookViewListenerResponse *resp) { + if (book_view->priv->book) + g_list_foreach (resp->cards, add_book_iterator, book_view->priv->book); + gtk_signal_emit (GTK_OBJECT (book_view), e_book_view_signals [CARD_CHANGED], resp->cards); @@ -178,10 +199,22 @@ e_book_view_new (GNOME_Evolution_Addressbook_BookView corba_book_view, EBookView return book_view; } +void +e_book_view_set_book (EBookView *book_view, EBook *book) +{ + g_return_if_fail (book_view && E_IS_BOOK_VIEW (book_view)); + g_return_if_fail (book && E_IS_BOOK (book)); + g_return_if_fail (book_view->priv->book == NULL); + + book_view->priv->book = book; + gtk_object_ref (GTK_OBJECT (book)); +} + static void e_book_view_init (EBookView *book_view) { book_view->priv = g_new0 (EBookViewPrivate, 1); + book_view->priv->book = NULL; book_view->priv->corba_book_view = CORBA_OBJECT_NIL; book_view->priv->listener = NULL; book_view->priv->responses_queued_id = 0; @@ -193,6 +226,10 @@ e_book_view_destroy (GtkObject *object) EBookView *book_view = E_BOOK_VIEW (object); CORBA_Environment ev; + if (book_view->priv->book) { + gtk_object_unref (GTK_OBJECT (book_view->priv->book)); + } + if (book_view->priv->corba_book_view) { CORBA_exception_init (&ev); diff --git a/addressbook/backend/ebook/e-book-view.h b/addressbook/backend/ebook/e-book-view.h index 726920f13d..5e576d6642 100644 --- a/addressbook/backend/ebook/e-book-view.h +++ b/addressbook/backend/ebook/e-book-view.h @@ -22,6 +22,8 @@ typedef struct _EBookView EBookView; typedef struct _EBookViewClass EBookViewClass; typedef struct _EBookViewPrivate EBookViewPrivate; +struct _EBook; /* Forward reference */ + struct _EBookView { GtkObject parent; EBookViewPrivate *priv; @@ -44,7 +46,8 @@ struct _EBookViewClass { EBookView *e_book_view_new (GNOME_Evolution_Addressbook_BookView corba_book_view, EBookViewListener *listener); GtkType e_book_view_get_type (void); -void e_book_view_get_book_view_listener (EBookView *book_view); + +void e_book_view_set_book (EBookView *book_view, struct _EBook *book); #define E_BOOK_VIEW_TYPE (e_book_view_get_type ()) #define E_BOOK_VIEW(o) (GTK_CHECK_CAST ((o), E_BOOK_VIEW_TYPE, EBookView)) diff --git a/addressbook/backend/ebook/e-book.c b/addressbook/backend/ebook/e-book.c index 3078d1d948..5a78fbe595 100644 --- a/addressbook/backend/ebook/e-book.c +++ b/addressbook/backend/ebook/e-book.c @@ -45,6 +45,8 @@ struct _EBookPrivate { GList *pending_ops; guint op_tag; + + gchar *uri; }; enum { @@ -97,8 +99,6 @@ e_book_unqueue_op (EBook *book) EBookOp *op; GList *removed; - g_print ("Unqueue Op\n"); - removed = g_list_last (book->priv->pending_ops); if (removed) { @@ -251,6 +251,7 @@ e_book_do_response_get_view (EBook *book, } book_view = e_book_view_new(resp->book_view, op->listener); + e_book_view_set_book (book_view, book); /* Only execute the callback if the operation is still flagged as active (i.e. hasn't been cancelled. This is mildly wasteful since we unnecessaryily create the @@ -299,6 +300,7 @@ e_book_do_response_get_changes (EBook *book, } book_view = e_book_view_new (resp->book_view, op->listener); + e_book_view_set_book (book_view, book); if (op->cb) { if (op->active) @@ -459,6 +461,7 @@ e_book_check_listener_queue (EBookListener *listener, EBook *book) /** * e_book_load_uri: */ + gboolean e_book_load_uri (EBook *book, const char *uri, @@ -478,6 +481,9 @@ e_book_load_uri (EBook *book, return FALSE; } + g_free (book->priv->uri); + book->priv->uri = g_strdup (uri); + /* * Create our local BookListener interface. */ @@ -489,7 +495,7 @@ e_book_load_uri (EBook *book, gtk_signal_connect (GTK_OBJECT (book->priv->listener), "responses_queued", e_book_check_listener_queue, book); - + /* * Load the addressbook into the PAS. */ @@ -558,6 +564,14 @@ e_book_unload_uri (EBook *book) book->priv->load_state = URINotLoaded; } +const char * +e_book_get_uri (EBook *book) +{ + g_return_val_if_fail (book && E_IS_BOOK (book), NULL); + + return book->priv->uri; +} + char * e_book_get_static_capabilities (EBook *book) { @@ -729,6 +743,8 @@ e_book_get_card (EBook *book, g_free(vcard); e_card_set_id(card, id); + card->book = book; + gtk_object_ref (GTK_OBJECT (card->book)); return card; } @@ -883,6 +899,11 @@ e_book_add_card (EBook *book, g_free (vcard); + if (card->book && card->book != book) + gtk_object_unref (GTK_OBJECT (card->book)); + card->book = book; + gtk_object_ref (GTK_OBJECT (card->book)); + return retval; } @@ -961,6 +982,11 @@ e_book_commit_card (EBook *book, g_free (vcard); + if (card->book && card->book != book) + gtk_object_unref (GTK_OBJECT (card->book)); + card->book = book; + gtk_object_ref (GTK_OBJECT (card->book)); + return retval; } @@ -1215,7 +1241,8 @@ e_book_init (EBook *book) { book->priv = g_new0 (EBookPrivate, 1); book->priv->load_state = URINotLoaded; - book->priv->op_tag = 1; + book->priv->op_tag = 1; + book->priv->uri = NULL; } static void @@ -1237,6 +1264,8 @@ e_book_destroy (GtkObject *object) CORBA_exception_init (&ev); } + g_free (book->priv->uri); + g_free (book->priv); GTK_OBJECT_CLASS (e_book_parent_class)->destroy (object); diff --git a/addressbook/backend/ebook/e-book.h b/addressbook/backend/ebook/e-book.h index 1deecdb1ed..1c9386cfa7 100644 --- a/addressbook/backend/ebook/e-book.h +++ b/addressbook/backend/ebook/e-book.h @@ -60,6 +60,8 @@ gboolean e_book_load_uri (EBook *book, gpointer closure); void e_book_unload_uri (EBook *book); +const char *e_book_get_uri (EBook *book); + char *e_book_get_static_capabilities (EBook *book); guint e_book_get_supported_fields (EBook *book, diff --git a/addressbook/backend/ebook/e-card-simple.c b/addressbook/backend/ebook/e-card-simple.c index 2eea743893..a9f33350d5 100644 --- a/addressbook/backend/ebook/e-card-simple.c +++ b/addressbook/backend/ebook/e-card-simple.c @@ -237,7 +237,8 @@ e_card_simple_new (ECard *card) return simple; } -ECardSimple *e_card_simple_duplicate(ECardSimple *simple) +ECardSimple * +e_card_simple_duplicate(ECardSimple *simple) { char *vcard = e_card_simple_get_vcard(simple); ECard *card = e_card_new(vcard); @@ -283,8 +284,8 @@ e_card_simple_set_id (ECardSimple *simple, const char *id) * * Returns: a string in vcard format, which is wrapped by the @simple. */ -char -*e_card_simple_get_vcard (ECardSimple *simple) +char * +e_card_simple_get_vcard (ECardSimple *simple) { if (simple->card) return e_card_get_vcard(simple->card); diff --git a/addressbook/backend/ebook/e-card.c b/addressbook/backend/ebook/e-card.c index 044f27be3b..53ba425a48 100644 --- a/addressbook/backend/ebook/e-card.c +++ b/addressbook/backend/ebook/e-card.c @@ -28,6 +28,7 @@ #include #include "e-util/ename/e-name-western.h" #include "e-util/ename/e-address-western.h" +#include "e-book.h" #define is_a_prop_of(obj,prop) (isAPropertyOf ((obj),(prop))) #define str_val(obj) (the_str = (vObjectValueType (obj))? fakeCString (vObjectUStringZValue (obj)) : calloc (1, 1)) @@ -129,6 +130,9 @@ static void set_phone_flags (VObject *vobj, ECardPhoneFlags flags); static ECardAddressFlags get_address_flags (VObject *vobj); static void set_address_flags (VObject *vobj, ECardAddressFlags flags); +static gchar *e_card_uri_extract_book_uri (const gchar *uri); +static gchar *e_card_uri_extract_card_id (const gchar *uri); + typedef void (* ParsePropertyFunc) (ECard *card, VObject *object); struct { @@ -234,6 +238,12 @@ e_card_duplicate(ECard *card) char *vcard = e_card_get_vcard(card); ECard *new_card = e_card_new(vcard); g_free (vcard); + + if (card->book) { + new_card->book = card->book; + gtk_object_ref (GTK_OBJECT (new_card->book)); + } + return new_card; } @@ -306,6 +316,8 @@ e_card_touch(ECard *card) const char * e_card_get_id (ECard *card) { + g_return_val_if_fail (card && E_IS_CARD (card), NULL); + return card->id; } @@ -320,11 +332,34 @@ e_card_get_id (ECard *card) void e_card_set_id (ECard *card, const char *id) { + g_return_if_fail (card && E_IS_CARD (card)); + if ( card->id ) g_free(card->id); card->id = g_strdup(id); } +EBook * +e_card_get_book (ECard *card) +{ + g_return_val_if_fail (card && E_IS_CARD (card), NULL); + + return card->book; +} + +const gchar * +e_card_get_uri (ECard *card) +{ + g_return_val_if_fail (card && E_IS_CARD (card), NULL); + + if (card->uri == NULL && card->id && *card->id && card->book) { + const char *book_uri = e_book_get_uri (card->book); + if (book_uri) + card->uri = g_strdup_printf ("%s/%s", book_uri, card->id); + } + return card->uri; +} + static gchar * e_card_date_to_string (ECardDate *dt) { @@ -341,6 +376,7 @@ static VObject * e_card_get_vobject (ECard *card) { VObject *vobj; + const char *tmp; vobj = newVObject (VCCardProp); @@ -571,8 +607,14 @@ e_card_get_vobject (ECard *card) } } - if (card->id) - addPropValue (vobj, VCUniqueStringProp, card->id); + tmp = e_card_get_uri (card); + if (tmp == NULL) + tmp = card->id; + if (tmp) { + g_message ("unique string = [%s]", tmp); + addPropValue (vobj, VCUniqueStringProp, tmp); + } + #if 0 @@ -1083,9 +1125,24 @@ parse_arbitrary(ECard *card, VObject *vobj) static void parse_id(ECard *card, VObject *vobj) { - if ( card->id ) - g_free(card->id); - assign_string(vobj, &(card->id)); + if ( vObjectValueType (vobj) ) { + gchar *str = fakeCString (vObjectUStringZValue (vobj)); + gchar *id; + if ( card->id ) + g_free(card->id); + if ( card->uri ) + g_free(card->uri); + + id = e_card_uri_extract_card_id (str); + if (id) { + card->id = id; + card->uri = g_strdup (str); + } else { + card->id = g_strdup (str); + card->uri = NULL; + } + free (str); + } } static void @@ -1770,6 +1827,9 @@ e_card_destroy (GtkObject *object) { ECard *card = E_CARD(object); g_free(card->id); + if (card->book) + gtk_object_unref (GTK_OBJECT (card->book)); + g_free(card->uri); g_free(card->file_as); g_free(card->fname); if ( card->name ) @@ -4047,3 +4107,77 @@ e_card_evolution_list_show_addresses (ECard *card) g_return_val_if_fail (card && E_IS_CARD (card), FALSE); return card->list_show_addresses; } + +static gchar * +e_card_uri_extract_book_uri (const gchar *uri) +{ + gchar *lastslash; + + if (uri == NULL) + return NULL; + + lastslash = strrchr (uri, '/'); + if (lastslash == NULL) + return NULL; + + return g_strndup (uri, lastslash - uri); +} + +static gchar * +e_card_uri_extract_card_id (const gchar *uri) +{ + gchar *lastslash; + + if (uri == NULL) + return NULL; + + lastslash = strrchr (uri, '/'); + return lastslash ? g_strdup (lastslash+1) : NULL; +} + +typedef struct _CardLoadData CardLoadData; +struct _CardLoadData { + gchar *card_id; + ECardCallback cb; + gpointer closure; +}; + +static void +card_load_cb (EBook *book, EBookStatus status, gpointer closure) +{ + CardLoadData *data = (CardLoadData *) closure; + ECard *card = NULL; + + if (status == E_BOOK_STATUS_SUCCESS) + card = e_book_get_card (book, data->card_id); + + if (data->cb != NULL) + data->cb (card, data->closure); + + g_free (data->card_id); + g_free (data); +} + +void +e_card_load_uri (const gchar *uri, ECardCallback cb, gpointer closure) +{ + CardLoadData *data; + gchar *book_uri; + gchar *card_id; + EBook *book; + + g_return_if_fail (uri != NULL); + + book_uri = e_card_uri_extract_book_uri (uri); + card_id = e_card_uri_extract_card_id (uri); + + data = g_new (CardLoadData, 1); + data->card_id = g_strdup (card_id); + data->cb = cb; + data->closure = closure; + + book = e_book_new (); + e_book_load_uri (book, book_uri, card_load_cb, data); + + g_free (book_uri); +} diff --git a/addressbook/backend/ebook/e-card.h b/addressbook/backend/ebook/e-card.h index fa448704a5..3aaeddb813 100644 --- a/addressbook/backend/ebook/e-card.h +++ b/addressbook/backend/ebook/e-card.h @@ -27,10 +27,15 @@ typedef struct _ECard ECard; typedef struct _ECardClass ECardClass; +struct _EBook; /* Forward reference */ + struct _ECard { GtkObject object; char *id; + struct _EBook *book; /* The EBook this card is from. */ + gchar *uri; /* The card's uri (book uri + id) */ + char *file_as; /* The File As field. */ char *fname; /* The full name. */ ECardName *name; /* The structured name. */ @@ -117,6 +122,10 @@ const char *e_card_get_id (ECard void e_card_set_id (ECard *card, const char *character); +struct _EBook *e_card_get_book (ECard *card); +const char *e_card_get_uri (ECard *card); + + char *e_card_get_vcard (ECard *card); char *e_card_list_get_vcard (GList *list); ECard *e_card_duplicate (ECard *card); @@ -126,7 +135,6 @@ void e_card_touch (ECard /* Evolution List convenience functions */ /* used for encoding uids in email addresses */ -#define ECARD_UID_LINK_PREFIX "|X-EVOLUTION-UID=" gboolean e_card_evolution_list (ECard *card); gboolean e_card_evolution_list_show_addresses(ECard *card); @@ -182,6 +190,11 @@ void e_card_send (ECard void e_card_list_send (GList *cards, ECardDisposition disposition); +/* Getting ECards via their URIs */ +typedef void (*ECardCallback) (ECard *card, gpointer closure); +void e_card_load_uri (const gchar *uri, ECardCallback cb, gpointer closure); + + /* Standard Gtk function */ GtkType e_card_get_type (void); 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; } diff --git a/addressbook/backend/ebook/e-destination.h b/addressbook/backend/ebook/e-destination.h index 85edf594be..c4d742afbe 100644 --- a/addressbook/backend/ebook/e-destination.h +++ b/addressbook/backend/ebook/e-destination.h @@ -31,6 +31,7 @@ #include #include #include +#include #define E_TYPE_DESTINATION (e_destination_get_type ()) #define E_DESTINATION(o) (GTK_CHECK_CAST ((o), E_TYPE_DESTINATION, EDestination)) @@ -60,39 +61,49 @@ GtkType e_destination_get_type (void); EDestination *e_destination_new (void); EDestination *e_destination_copy (EDestination *); +void e_destination_clear (EDestination *); gboolean e_destination_is_empty (EDestination *); void e_destination_set_card (EDestination *, ECard *card, gint email_num); +void e_destination_set_card_uri (EDestination *, const gchar *uri, gint email_num); + +void e_destination_set_name (EDestination *, const gchar *name); +void e_destination_set_email (EDestination *, const gchar *email); + void e_destination_set_string (EDestination *, const gchar *string); void e_destination_set_html_mail_pref (EDestination *, gboolean); -gboolean e_destination_has_card (const EDestination *); -gboolean e_destination_has_pending_card (const EDestination *); +gboolean e_destination_contains_card (const EDestination *); +gboolean e_destination_from_card (const EDestination *); void e_destination_use_card (EDestination *, EDestinationCardCallback cb, gpointer closure); ECard *e_destination_get_card (const EDestination *); +const gchar *e_destination_get_card_uri (const EDestination *); gint e_destination_get_email_num (const EDestination *); -const gchar *e_destination_get_string (const EDestination *); -gint e_destination_get_strlen (const EDestination *); /* a convenience function... */ -const gchar *e_destination_get_name (const EDestination *); +const gchar *e_destination_get_name (const EDestination *); /* "Jane Smith" */ +const gchar *e_destination_get_email (const EDestination *); /* "jane@assbarn.com" */ +const gchar *e_destination_get_address (const EDestination *); /* "Jane Smith " (or a comma-sep set of such for a list) */ -const gchar *e_destination_get_email (const EDestination *); -const gchar *e_destination_get_email_verbose (const EDestination *); +const gchar *e_destination_get_textrep (const EDestination *); /* "Jane Smith" or "jane@assbarn.com" */ + +gboolean e_destination_is_evolution_list (const EDestination *); /* If true, they want HTML mail. */ gboolean e_destination_get_html_mail_pref (const EDestination *); gchar *e_destination_get_address_textv (EDestination **); +xmlNodePtr e_destination_xml_encode (const EDestination *dest); +gboolean e_destination_xml_decode (EDestination *dest, xmlNodePtr node); + gchar *e_destination_export (const EDestination *); EDestination *e_destination_import (const gchar *str); gchar *e_destination_exportv (EDestination **); EDestination **e_destination_importv (const gchar *str); -EDestination **e_destination_importv_list (EBook *book, ECard *list); void e_destination_touch (EDestination *); -- cgit v1.2.3