2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2012 Match Grun and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 /* General functions for accessing address book files */
33 #include "addrcache.h"
35 #include "adbookbase.h"
37 #ifndef DEV_STANDALONE
38 #include "prefs_gtk.h"
42 #define ADDRBOOK_MAX_SEARCH_COUNT 1000
43 #define ADDRBOOK_PREFIX "addrbook-"
44 #define ADDRBOOK_SUFFIX ".xml"
45 #define FILE_NUMDIGITS 6
47 #define ID_TIME_OFFSET 998000000
49 static void addrbook_print_book ( AddressBookFile *book, FILE *stream );
52 * Create new address book
53 * \return Address book.
55 AddressBookFile *addrbook_create_book()
57 AddressBookFile *book;
59 book = g_new0(AddressBookFile, 1);
60 book->type = ADBOOKTYPE_BOOK;
61 book->addressCache = addrcache_create();
62 book->retVal = MGU_SUCCESS;
64 book->fileName = NULL;
66 book->tempList = NULL;
67 book->tempHash = NULL;
68 book->addressCache->modified = TRUE;
74 * Specify name to be used
75 * \param book Address book.
78 void addrbook_set_name(AddressBookFile *book, const gchar *value)
80 cm_return_if_fail(book != NULL);
81 addrcache_set_name(book->addressCache, value);
84 gchar *addrbook_get_name(AddressBookFile *book)
86 cm_return_val_if_fail(book != NULL, NULL);
87 return addrcache_get_name(book->addressCache);
91 * Specify path to address book file.
92 * \param book Address book.
95 void addrbook_set_path(AddressBookFile *book, const gchar *value)
97 cm_return_if_fail(book != NULL);
98 book->path = mgu_replace_string(book->path, value);
99 addrcache_set_dirty(book->addressCache, TRUE);
103 * Specify filename to be used
104 * \param book Address book.
105 * \param value Filename.
107 void addrbook_set_file(AddressBookFile *book, const gchar *value)
109 cm_return_if_fail(book != NULL);
110 book->fileName = mgu_replace_string(book->fileName, value);
111 addrcache_set_dirty(book->addressCache, TRUE);
114 gboolean addrbook_get_modified(AddressBookFile *book)
116 cm_return_val_if_fail(book != NULL, FALSE);
117 return book->addressCache->modified;
120 gboolean addrbook_get_accessed(AddressBookFile *book)
122 cm_return_val_if_fail(book != NULL, FALSE);
123 return book->addressCache->accessFlag;
127 * Specify address book as accessed.
128 * \param book Address book.
129 * \param value Value.
131 void addrbook_set_accessed(AddressBookFile *book, const gboolean value)
133 cm_return_if_fail(book != NULL);
134 book->addressCache->accessFlag = value;
137 gboolean addrbook_get_read_flag(AddressBookFile *book)
139 cm_return_val_if_fail(book != NULL, FALSE);
140 return book->addressCache->dataRead;
143 gint addrbook_get_status(AddressBookFile *book)
145 cm_return_val_if_fail(book != NULL, -1);
149 ItemFolder *addrbook_get_root_folder(AddressBookFile *book)
151 cm_return_val_if_fail(book != NULL, NULL);
152 return addrcache_get_root_folder(book->addressCache);
155 GList *addrbook_get_list_folder(AddressBookFile *book)
157 cm_return_val_if_fail(book != NULL, NULL);
158 return addrcache_get_list_folder(book->addressCache);
161 GList *addrbook_get_list_person(AddressBookFile *book)
163 cm_return_val_if_fail(book != NULL, NULL);
164 return addrcache_get_list_person(book->addressCache);
167 gboolean addrbook_get_dirty(AddressBookFile *book)
169 cm_return_val_if_fail(book != NULL, FALSE);
170 return addrcache_get_dirty(book->addressCache);
174 * Set address book as dirty (needs to be written to file).
175 * \param book Address book.
176 * \param value Dirty flag.
178 void addrbook_set_dirty(AddressBookFile *book, const gboolean value)
180 cm_return_if_fail(book != NULL);
181 addrcache_set_dirty(book->addressCache, value);
186 * \param book Address book.
188 void addrbook_free_book(AddressBookFile *book)
190 cm_return_if_fail(book != NULL);
193 addrcache_free(book->addressCache);
195 /* Free up internal objects */
197 g_free(book->fileName);
198 g_list_free(book->tempList);
201 book->fileName = NULL;
203 book->tempList = NULL;
204 book->tempHash = NULL;
206 book->type = ADBOOKTYPE_NONE;
207 book->addressCache = NULL;
208 book->retVal = MGU_SUCCESS;
214 * Print address book header.
215 * \param book Address book.
216 * \param stream Output stream.
218 static void addrbook_print_book(AddressBookFile *book, FILE *stream)
220 cm_return_if_fail(book != NULL);
222 fprintf(stream, "AddressBook:\n");
223 fprintf(stream, "\tpath : '%s'\n", book->path);
224 fprintf(stream, "\tfile : '%s'\n", book->fileName);
225 fprintf(stream, "\tstatus : %d\n", book->retVal );
226 addrcache_print(book->addressCache, stream);
230 * Dump entire address book traversing folders.
231 * \param book Address book.
232 * \param stream Output stream.
234 void addrbook_dump_book(AddressBookFile *book, FILE *stream)
238 cm_return_if_fail(book != NULL);
240 addrbook_print_book(book, stream);
241 folder = book->addressCache->rootFolder;
242 addritem_print_item_folder(folder, stream);
246 * Remove specified group from address book. Note that object should still
248 * Specify name to be used
249 * \param book Address book.
250 * \param group Group to remove.
252 * \return Group, or NULL if not found.
254 ItemGroup *addrbook_remove_group(AddressBookFile *book, ItemGroup *group)
256 cm_return_val_if_fail(book != NULL, NULL);
257 return addrcache_remove_group(book->addressCache, group);
261 * Remove specified person from address book. Note that object should still
263 * \param book Address book.
264 * \param person Person to remove.
265 * \return Person, or NULL if not found.
267 ItemPerson *addrbook_remove_person(AddressBookFile *book, ItemPerson *person)
269 cm_return_val_if_fail(book != NULL, NULL);
270 return addrcache_remove_person(book->addressCache, person);
274 * Remove specified email address in address book for specified person.
275 * Note that object should still be freed.
276 * \param book Address book.
277 * \param person Person.
278 * \param email EMail to remove.
279 * \return EMail object, or NULL if not found.
281 ItemEMail *addrbook_person_remove_email(AddressBookFile *book,
282 ItemPerson *person, ItemEMail *email)
284 cm_return_val_if_fail(book != NULL, NULL);
285 return addrcache_person_remove_email(book->addressCache, person, email);
289 * ***********************************************************************
290 * Read/Write XML data file...
291 * ===========================
293 * 1) The address book is structured as follows:
308 * 2) This sequence of elements was chosen so that the most important
309 * elements (person and their email addresses) appear first.
311 * 3) Groups then appear. When groups are loaded, person's email
312 * addresses have already been loaded and can be found.
314 * 4) Finally folders are loaded. Any forward and backward references
315 * to folders, groups and persons in the folders are resolved after
318 * ***********************************************************************
321 /* Element tag names */
322 #define AB_ELTAG_ADDRESS "address"
323 #define AB_ELTAG_ATTRIBUTE "attribute"
324 #define AB_ELTAG_ATTRIBUTE_LIST "attribute-list"
325 #define AB_ELTAG_ADDRESS_LIST "address-list"
326 #define AB_ELTAG_MEMBER "member"
327 #define AB_ELTAG_MEMBER_LIST "member-list"
328 #define AB_ELTAG_ITEM "item"
329 #define AB_ELTAG_ITEM_LIST "item-list"
330 #define AB_ELTAG_ADDRESS_BOOK "address-book"
331 #define AB_ELTAG_PERSON "person"
332 #define AB_ELTAG_GROUP "group"
333 #define AB_ELTAG_FOLDER "folder"
335 /* Attribute tag names */
336 #define AB_ATTAG_TYPE "type"
337 #define AB_ATTAG_UID "uid"
338 #define AB_ATTAG_NAME "name"
339 #define AB_ATTAG_REMARKS "remarks"
340 #define AB_ATTAG_FIRST_NAME "first-name"
341 #define AB_ATTAG_LAST_NAME "last-name"
342 #define AB_ATTAG_NICK_NAME "nick-name"
343 #define AB_ATTAG_COMMON_NAME "cn"
344 #define AB_ATTAG_ALIAS "alias"
345 #define AB_ATTAG_EMAIL "email"
346 #define AB_ATTAG_EID "eid"
347 #define AB_ATTAG_PID "pid"
349 /* Attribute values */
350 #define AB_ATTAG_VAL_PERSON "person"
351 #define AB_ATTAG_VAL_GROUP "group"
352 #define AB_ATTAG_VAL_FOLDER "folder"
355 * Parse address item for person from XML file.
356 * \param book Address book.
357 * \param file XML file handle.
358 * \param person Person.
360 static void addrbook_parse_address(AddressBookFile *book, XMLFile *file,
365 ItemEMail *email = NULL;
367 attr = xml_get_current_tag_attr(file);
369 name = ((XMLAttr *)attr->data)->name;
370 value = ((XMLAttr *)attr->data)->value;
372 email = addritem_create_item_email();
373 if (strcmp(name, AB_ATTAG_UID) == 0)
374 ADDRITEM_ID(email) = g_strdup(value);
375 else if (strcmp(name, AB_ATTAG_ALIAS) == 0)
376 ADDRITEM_NAME(email) = g_strdup(value);
377 else if (strcmp(name, AB_ATTAG_EMAIL) == 0)
378 email->address = g_strdup(value);
379 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
380 email->remarks = g_strdup(value);
381 attr = g_list_next(attr);
385 addrcache_person_add_email(book->addressCache, person,
389 addritem_free_item_email(email);
396 * Parse list of email address for person from XML file.
397 * \param book Address book.
398 * \param file XML file handle.
399 * \param person Person.
401 static void addrbook_parse_addr_list(AddressBookFile *book, XMLFile *file,
408 prev_level = file->level;
409 if (xml_parse_next_tag(file)) {
410 longjmp(book->jumper, 1);
412 if (file->level < prev_level) return;
413 if (xml_compare_tag(file, AB_ELTAG_ADDRESS)) {
414 attr = xml_get_current_tag_attr(file);
415 addrbook_parse_address(book, file, person);
416 addrbook_parse_addr_list(book, file, person);
422 * Parse attribute for person from XML file.
423 * \param book Address book.
424 * \param file XML file handle.
425 * \param person Person.
427 static void addrbook_parse_attribute(XMLFile *file, ItemPerson *person)
432 UserAttribute *uAttr = NULL;
434 attr = xml_get_current_tag_attr(file);
436 name = ((XMLAttr *)attr->data)->name;
437 value = ((XMLAttr *)attr->data)->value;
438 if (!uAttr) uAttr = addritem_create_attribute();
439 if (strcmp(name, AB_ATTAG_UID) == 0)
440 addritem_attrib_set_id(uAttr, value);
441 else if (strcmp(name, AB_ATTAG_NAME) == 0)
442 addritem_attrib_set_name(uAttr, value);
443 attr = g_list_next(attr);
446 element = xml_get_element(file);
447 addritem_attrib_set_value(uAttr, element);
452 addritem_person_add_attribute(person, uAttr);
455 addritem_free_attribute(uAttr);
462 * Parse list of attributes for person from XML file.
463 * \param book Address book.
464 * \param file XML file handle.
465 * \param person Person.
467 static void addrbook_parse_attr_list(AddressBookFile *book, XMLFile *file,
474 prev_level = file->level;
475 if (xml_parse_next_tag(file)) {
476 longjmp( book->jumper, 1 );
478 if (file->level < prev_level) return;
479 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
480 attr = xml_get_current_tag_attr(file);
481 addrbook_parse_attribute(file, person);
482 addrbook_parse_attr_list(book, file, person);
488 * Parse person from XML file.
489 * \param book Address book.
490 * \param file XML file handle.
492 static void addrbook_parse_person(AddressBookFile *book, XMLFile *file)
496 ItemPerson *person = NULL;
498 attr = xml_get_current_tag_attr(file);
500 name = ((XMLAttr *)attr->data)->name;
501 value = ((XMLAttr *)attr->data)->value;
503 person = addritem_create_item_person();
504 if (strcmp(name, AB_ATTAG_UID) == 0) {
505 ADDRITEM_ID(person) = g_strdup(value);
506 person->picture = g_strdup(value);
508 else if (strcmp(name, AB_ATTAG_FIRST_NAME) == 0)
509 person->firstName = g_strdup(value);
510 else if (strcmp(name, AB_ATTAG_LAST_NAME) == 0)
511 person->lastName = g_strdup(value);
512 else if (strcmp(name, AB_ATTAG_NICK_NAME) == 0)
513 person->nickName = g_strdup(value);
514 else if (strcmp(name, AB_ATTAG_COMMON_NAME) == 0)
515 ADDRITEM_NAME(person) = g_strdup(value);
516 attr = g_list_next(attr);
518 if (xml_parse_next_tag(file)) { /* Consume closing tag */
519 longjmp(book->jumper, 1);
521 if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST)) {
522 addrbook_parse_addr_list(book, file, person);
524 addrcache_hash_add_person(book->addressCache, person);
527 if (xml_parse_next_tag(file)) { /* Consume closing tag */
528 longjmp(book->jumper, 1);
530 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST)) {
531 addrbook_parse_attr_list(book, file, person);
536 * Parse group member from XML file.
537 * \param book Address book.
538 * \param file XML file handle.
539 * \param group Group.
541 static void addrbook_parse_member(AddressBookFile *book, XMLFile *file,
547 /* gchar *pid = NULL; */
548 ItemEMail *email = NULL;
550 attr = xml_get_current_tag_attr(file);
552 name = ((XMLAttr *)attr->data)->name;
553 value = ((XMLAttr *)attr->data)->value;
554 if( strcmp( name, AB_ATTAG_EID ) == 0 )
555 eid = g_strdup( value );
556 attr = g_list_next(attr);
558 /* email = addrcache_get_email( book->addressCache, pid, eid ); */
559 email = addrcache_get_email(book->addressCache, eid);
563 addrcache_group_add_email(book->addressCache, group,
567 addritem_free_item_email(email);
574 * Parse list of group members from XML file.
575 * \param book Address book.
576 * \param file XML file handle.
577 * \param group Group.
579 static void addrbook_parse_member_list(AddressBookFile *book, XMLFile *file,
586 prev_level = file->level;
587 if (xml_parse_next_tag(file)) {
588 longjmp(book->jumper, 1);
590 if (file->level < prev_level)
592 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
593 attr = xml_get_current_tag_attr(file);
594 addrbook_parse_member(book, file, group);
595 addrbook_parse_member_list(book, file, group);
598 attr = xml_get_current_tag_attr(file);
604 * Parse group object from XML file.
605 * \param book Address book.
606 * \param file XML file handle.
608 static void addrbook_parse_group(AddressBookFile *book, XMLFile *file)
612 ItemGroup *group = NULL;
614 attr = xml_get_current_tag_attr(file);
616 name = ((XMLAttr *)attr->data)->name;
617 value = ((XMLAttr *)attr->data)->value;
619 group = addritem_create_item_group();
620 if (strcmp(name, AB_ATTAG_UID) == 0)
621 ADDRITEM_ID(group) = g_strdup(value);
622 else if (strcmp(name, AB_ATTAG_NAME) == 0)
623 ADDRITEM_NAME(group) = g_strdup(value);
624 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
625 group->remarks = g_strdup(value);
626 attr = g_list_next(attr);
628 if (xml_parse_next_tag(file)) { /* Consume closing tag */
629 longjmp(book->jumper, 1);
631 if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST)) {
633 addrcache_hash_add_group(book->addressCache, group);
635 addrbook_parse_member_list(book, file, group);
640 * Parse folder item from XML file.
641 * \param book Address book.
642 * \param file XML file handle.
643 * \param folder Folder.
645 static void addrbook_parse_folder_item(AddressBookFile *book, XMLFile *file,
652 attr = xml_get_current_tag_attr(file);
654 name = ((XMLAttr *)attr->data)->name;
655 value = ((XMLAttr *)attr->data)->value;
656 if (strcmp(name, AB_ATTAG_UID) == 0) {
657 uid = g_strdup(value);
659 attr = g_list_next(attr);
663 folder->listItems = g_list_append(folder->listItems, uid);
669 * Parse list of folder items from XML file.
670 * \param book Address book.
671 * \param file XML file handle.
672 * \param folder Folder.
674 static void addrbook_parse_folder_list(AddressBookFile *book, XMLFile *file,
681 prev_level = file->level;
682 if (xml_parse_next_tag(file)) {
683 longjmp(book->jumper, 1);
685 if (file->level < prev_level)
687 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
688 attr = xml_get_current_tag_attr(file);
689 addrbook_parse_folder_item(book, file, folder);
690 addrbook_parse_folder_list(book, file, folder);
693 attr = xml_get_current_tag_attr(file);
699 * Parse folder from XML file.
700 * \param book Address book.
701 * \param file XML file handle.
703 static void addrbook_parse_folder(AddressBookFile *book, XMLFile *file)
707 ItemFolder *folder = NULL;
709 attr = xml_get_current_tag_attr(file);
711 name = ((XMLAttr *)attr->data)->name;
712 value = ((XMLAttr *)attr->data)->value;
714 folder = addritem_create_item_folder();
715 if (strcmp(name, AB_ATTAG_UID) == 0)
716 ADDRITEM_ID(folder) = g_strdup(value);
717 else if (strcmp(name, AB_ATTAG_NAME) == 0)
718 ADDRITEM_NAME(folder) = g_strdup(value);
719 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
720 folder->remarks = g_strdup(value);
721 attr = g_list_next(attr);
723 if (xml_parse_next_tag(file)) { /* Consume closing tag */
724 longjmp(book->jumper, 1);
726 if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST)) {
728 if (addrcache_hash_add_folder(book->addressCache,
730 book->tempList = g_list_append(book->tempList,
732 /* We will resolve folder later */
733 ADDRITEM_PARENT(folder) = NULL;
736 addrbook_parse_folder_list(book, file, folder);
741 * Read address book (DOM) tree from file.
742 * \param book Address book.
743 * \param file XML file handle.
744 * \return <i>TRUE</i> if data read successfully, <i>FALSE</i> if error
747 static gboolean addrbook_read_tree(AddressBookFile *book, XMLFile *file)
753 book->retVal = MGU_BAD_FORMAT;
754 if (xml_get_dtd(file))
756 if (xml_parse_next_tag(file))
757 longjmp(book->jumper, 1);
758 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
761 attr = xml_get_current_tag_attr(file);
763 name = ((XMLAttr *)attr->data)->name;
764 value = ((XMLAttr *)attr->data)->value;
765 if (strcmp( name, AB_ATTAG_NAME) == 0)
766 addrbook_set_name( book, value );
767 attr = g_list_next( attr );
774 /* Get next item tag (person, group or folder) */
775 if (xml_parse_next_tag(file))
776 longjmp( book->jumper, 1 );
778 if (xml_compare_tag(file, AB_ELTAG_PERSON))
779 addrbook_parse_person(book, file);
780 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
781 addrbook_parse_group(book, file);
782 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
783 addrbook_parse_folder(book, file);
785 if (retVal) book->retVal = MGU_SUCCESS;
790 * Resolve folder items callback function.
791 * \param key Table key.
792 * \param value Reference to object contained in folder.
793 * \param data Reference to address book.
795 static void addrbook_res_items_vis(gpointer key, gpointer value, gpointer data)
797 AddressBookFile *book = data;
798 AddrItemObject *obj = (AddrItemObject *) value;
799 ItemFolder *rootFolder = book->addressCache->rootFolder;
800 if (obj->parent == NULL) {
801 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
802 rootFolder->listPerson = g_list_append(rootFolder->listPerson,
804 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
806 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
807 rootFolder->listGroup = g_list_append(rootFolder->listGroup,
809 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
815 * Resolve folder items. Lists of UID's are replaced with pointers to
817 * \param book Address book.
819 static void addrbook_resolve_folder_items(AddressBookFile *book)
821 GList *nodeFolder = NULL;
822 GList *listRemove = NULL;
824 ItemFolder *rootFolder = book->addressCache->rootFolder;
825 nodeFolder = book->tempList;
828 ItemFolder *folder = nodeFolder->data;
830 node = folder->listItems;
832 gchar *uid = node->data;
833 AddrItemObject *aio = addrcache_get_object(book->addressCache,
836 if (aio->type == ITEMTYPE_FOLDER) {
837 ItemFolder *item = (ItemFolder *) aio;
838 folder->listFolder = g_list_append(folder->listFolder, item);
839 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
840 addrcache_hash_add_folder(book->addressCache, folder);
842 else if (aio->type == ITEMTYPE_PERSON) {
843 ItemPerson *item = (ItemPerson *) aio;
844 folder->listPerson = g_list_append(folder->listPerson, item);
845 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
847 else if (aio->type == ITEMTYPE_GROUP) {
848 ItemGroup *item = (ItemGroup *) aio;
849 folder->listGroup = g_list_append(folder->listGroup, item);
850 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
852 /* Replace data with pointer to item */
856 else { /* Not found, append to remove list. */
857 listRemove = g_list_append(listRemove, uid);
859 node = g_list_next(node);
861 rootFolder->listFolder = g_list_append(rootFolder->listFolder,
863 /* Process remove list */
866 gchar *uid = node->data;
867 folder->listItems = g_list_remove(folder->listItems,
870 node = g_list_next(node);
872 g_list_free(listRemove);
873 nodeFolder = g_list_next(nodeFolder);
875 /* Remove folders with parents. */
877 node = rootFolder->listFolder;
879 ItemFolder *folder = (ItemFolder *) node->data;
880 if (ADDRITEM_PARENT(folder))
881 /* Remove folders with parents */
882 listRemove = g_list_append(listRemove, folder);
883 else /* Add to root folder */
884 ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder);
886 node = g_list_next( node );
888 /* Process remove list */
891 rootFolder->listFolder = g_list_remove(rootFolder->listFolder,
893 node = g_list_next(node);
895 g_list_free(listRemove);
897 /* Move all unparented persons and groups into root folder */
898 g_hash_table_foreach(book->addressCache->itemHash,
899 addrbook_res_items_vis, book);
901 /* Free up some more */
902 nodeFolder = book->tempList;
904 ItemFolder *folder = nodeFolder->data;
905 g_list_free(folder->listItems);
906 folder->listItems = NULL;
907 nodeFolder = g_list_next(nodeFolder);
909 g_list_free(book->tempList);
910 book->tempList = NULL;
915 * \param book Address book.
916 * \return Status code.
918 gint addrbook_read_data(AddressBookFile *book)
920 XMLFile *file = NULL;
921 gchar *fileSpec = NULL;
923 cm_return_val_if_fail(book != NULL, -1);
926 g_print( "...addrbook_read_data :%s:\t:%s:\n", book->fileName,
927 addrcache_get_name( book->addressCache ) );
930 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S,
931 book->fileName, NULL);
932 book->retVal = MGU_OPEN_FILE;
933 addrcache_clear(book->addressCache);
934 book->addressCache->modified = FALSE;
935 book->addressCache->accessFlag = FALSE;
936 file = xml_open_file(fileSpec);
939 book->tempList = NULL;
940 /* Trap for parsing errors. */
941 if (setjmp( book->jumper)) {
942 xml_close_file(file);
945 addrbook_read_tree(book, file);
946 xml_close_file(file);
947 /* Resolve folder items */
948 addrbook_resolve_folder_items(book);
949 book->tempList = NULL;
950 book->addressCache->modified = FALSE;
951 book->addressCache->dataRead = TRUE;
952 addrcache_set_dirty(book->addressCache, FALSE);
958 * Write start element to file.
959 * \param fp File handle.
960 * \param lvl Indent level.
961 * \param name Element name.
963 static int addrbook_write_elem_s(FILE *fp, gint lvl, gchar *name)
966 for (i = 0; i < lvl; i++)
967 if (fputs(" ", fp) == EOF)
969 if (fputs("<", fp) == EOF)
971 if (fputs(name, fp) == EOF)
978 * Write end element to file.
979 * \param fp File handle.
980 * \param lvl Indent level.
981 * \param name Element name.
983 static int addrbook_write_elem_e(FILE *fp, gint lvl, gchar *name)
986 for(i = 0; i < lvl; i++)
987 if (fputs(" ", fp) == EOF)
989 if (fputs("</", fp) == EOF)
991 if (fputs(name, fp) == EOF)
993 if (fputs(">\n", fp) == EOF)
1000 * Write attribute name/value pair to file.
1001 * \param fp File handle.
1002 * \param name Attribute name.
1003 * \param value Attribute value.
1005 static int addrbook_write_attr(FILE *fp, gchar *name, gchar *value)
1007 if (fputs(" ", fp) == EOF)
1009 if (fputs(name, fp) == EOF)
1011 if (fputs("=\"", fp) == EOF)
1013 if (xml_file_put_escape_str(fp, value) < 0)
1015 if (fputs("\"", fp) == EOF)
1021 typedef struct _HashLoopData {
1027 * Write person and associated addresses and attributes to file.
1028 * file hash table visitor function.
1029 * \param key Table key.
1030 * \param value Reference to person.
1031 * \param data File pointer.
1033 static void addrbook_write_item_person_vis(gpointer key, gpointer value,
1036 AddrItemObject *obj = (AddrItemObject *) value;
1037 HashLoopData *data = (HashLoopData *)d;
1038 FILE *fp = data->fp;
1043 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1044 ItemPerson *person = (ItemPerson *) value;
1046 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_PERSON) < 0)
1048 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(person)) < 0)
1050 if (addrbook_write_attr(fp, AB_ATTAG_FIRST_NAME, person->firstName) < 0)
1052 if (addrbook_write_attr(fp, AB_ATTAG_LAST_NAME, person->lastName) < 0)
1054 if (addrbook_write_attr(fp, AB_ATTAG_NICK_NAME, person->nickName) < 0)
1056 if (addrbook_write_attr(fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person)) < 0)
1058 if (fputs(" >\n", fp) == EOF)
1061 /* Output email addresses */
1062 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1064 if (fputs(">\n", fp) == EOF)
1066 node = person->listEMail;
1068 ItemEMail *email = node->data;
1069 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ADDRESS) < 0)
1071 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(email)) < 0)
1073 if (addrbook_write_attr(fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email)) < 0)
1075 if (addrbook_write_attr(fp, AB_ATTAG_EMAIL, email->address) < 0)
1077 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, email->remarks) < 0)
1079 if (fputs(" />\n", fp) == EOF)
1081 node = g_list_next(node);
1083 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1086 /* Output user attributes */
1087 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1089 if (fputs(">\n", fp) == EOF)
1091 node = person->listAttrib;
1093 UserAttribute *attrib = node->data;
1094 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ATTRIBUTE) < 0)
1096 if (addrbook_write_attr(fp, AB_ATTAG_UID, attrib->uid) < 0)
1098 if (addrbook_write_attr(fp, AB_ATTAG_NAME, attrib->name) < 0)
1100 if (fputs(" >", fp) == EOF)
1102 if (xml_file_put_escape_str(fp, attrib->value) < 0)
1104 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ATTRIBUTE) < 0)
1106 node = g_list_next(node);
1108 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1110 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_PERSON) < 0)
1117 * Write group and associated references to addresses to file.
1118 * file hash table visitor function.
1119 * \param key Table key.
1120 * \param value Reference to group.
1121 * \param data File pointer.
1123 static void addrbook_write_item_group_vis(gpointer key, gpointer value,
1126 AddrItemObject *obj = (AddrItemObject *) value;
1127 HashLoopData *data = (HashLoopData *)d;
1128 FILE *fp = data->fp;
1134 if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
1135 ItemGroup *group = (ItemGroup *) value;
1137 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_GROUP) < 0)
1139 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(group)) < 0)
1141 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(group)) < 0)
1143 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, group->remarks) < 0)
1145 if (fputs(" >\n", fp) == EOF)
1148 /* Output email address links */
1149 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1151 if (fputs(">\n", fp) == EOF)
1153 node = group->listEMail;
1155 ItemEMail *email = node->data;
1156 ItemPerson *person = (ItemPerson *) ADDRITEM_PARENT(email);
1157 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_MEMBER) < 0)
1159 if (addrbook_write_attr(fp, AB_ATTAG_PID, ADDRITEM_ID(person)) < 0)
1161 if (addrbook_write_attr(fp, AB_ATTAG_EID, ADDRITEM_ID(email)) < 0)
1163 if (fputs(" />\n", fp) == EOF)
1165 node = g_list_next(node);
1167 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1169 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_GROUP) < 0)
1176 * Write folder and associated references to addresses to file.
1177 * file hash table visitor function.
1178 * \param key Table key.
1179 * \param value Reference to folder.
1180 * \param data File pointer.
1182 static void addrbook_write_item_folder_vis(gpointer key, gpointer value,
1185 AddrItemObject *obj = (AddrItemObject *) value;
1186 HashLoopData *data = (HashLoopData *)d;
1187 FILE *fp = data->fp;
1192 if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) {
1193 ItemFolder *folder = (ItemFolder *) value;
1195 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_FOLDER) < 0)
1197 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(folder)) < 0)
1199 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder)) < 0)
1201 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks) < 0)
1203 if (fputs(" >\n", fp) == EOF)
1205 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1207 if (fputs(">\n", fp) == EOF)
1210 /* Output persons */
1211 node = folder->listPerson;
1213 ItemPerson *item = node->data;
1214 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1216 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON) < 0)
1218 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1220 if (fputs(" />\n", fp) == EOF)
1222 node = g_list_next(node);
1226 node = folder->listGroup;
1228 ItemGroup *item = node->data;
1229 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1231 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP) < 0)
1233 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1235 if (fputs(" />\n", fp) == EOF)
1237 node = g_list_next(node);
1240 /* Output folders */
1241 node = folder->listFolder;
1243 ItemFolder *item = node->data;
1244 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1246 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER) < 0)
1248 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1250 if (fputs(" />\n", fp) == EOF)
1252 node = g_list_next(node);
1254 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1256 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_FOLDER) < 0)
1263 * Output address book data to specified file.
1264 * \param book Address book.
1265 * \param newFile Filename of new file (in book's filepath).
1266 * \return Status code.
1268 static gint addrbook_write_to(AddressBookFile *book, gchar *newFile)
1273 #ifndef DEV_STANDALONE
1277 cm_return_val_if_fail(book != NULL, -1);
1278 cm_return_val_if_fail(newFile != NULL, -1);
1280 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, newFile, NULL);
1282 book->retVal = MGU_OPEN_FILE;
1283 #ifdef DEV_STANDALONE
1284 fp = g_fopen(fileSpec, "wb");
1287 if (fputs("<?xml version=\"1.0\" ?>\n", fp) == EOF) {
1288 book->retVal = MGU_ERROR_WRITE;
1289 return book->retVal;
1292 pfile = prefs_write_open(fileSpec);
1296 if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
1299 if (addrbook_write_elem_s(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1301 if (addrbook_write_attr(fp, AB_ATTAG_NAME,
1302 addrcache_get_name(book->addressCache)) < 0)
1304 if (fputs(" >\n", fp) == EOF)
1307 /* Output all persons */
1311 g_hash_table_foreach(book->addressCache->itemHash,
1312 addrbook_write_item_person_vis, &data);
1316 /* Output all groups */
1317 g_hash_table_foreach(book->addressCache->itemHash,
1318 addrbook_write_item_group_vis, &data);
1323 /* Output all folders */
1324 g_hash_table_foreach(book->addressCache->itemHash,
1325 addrbook_write_item_folder_vis, &data);
1330 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1333 book->retVal = MGU_SUCCESS;
1334 #ifdef DEV_STANDALONE
1337 if (prefs_file_close( pfile ) < 0)
1338 book->retVal = MGU_ERROR_WRITE;
1343 return book->retVal;
1345 g_warning("error writing AB\n");
1346 book->retVal = MGU_ERROR_WRITE;
1348 prefs_file_close_revert( pfile );
1349 return book->retVal;
1353 * Output address book data to original file.
1354 * \param book Address book.
1355 * \return Status code.
1357 gint addrbook_save_data(AddressBookFile *book)
1359 cm_return_val_if_fail(book != NULL, -1);
1361 book->retVal = MGU_NO_FILE;
1362 if (book->fileName == NULL || *book->fileName == '\0')
1363 return book->retVal;
1364 if (book->path == NULL || *book->path == '\0')
1365 return book->retVal;
1367 addrbook_write_to(book, book->fileName);
1368 if (book->retVal == MGU_SUCCESS)
1369 addrcache_set_dirty(book->addressCache, FALSE);
1370 return book->retVal;
1374 * **********************************************************************
1375 * Address book edit interface functions.
1376 * **********************************************************************
1380 * Hash table callback function for simple deletion of hashtable entries.
1381 * \param key Table key (will be freed).
1382 * \param value Value stored in table.
1383 * \param data User data.
1384 * \return <i>TRUE</i> to indicate that entry freed.
1386 static gboolean addrbook_free_simple_hash_vis(gpointer *key, gpointer *value,
1396 * Update address book email list for specified person. Note: The existing
1397 * email addresses are replaced with the new addresses. Any references to
1398 * old addresses in the groups are re-linked to the new addresses. All old
1399 * addresses linked to the person are removed.
1400 * \param book Address book.
1401 * \param person Person to update.
1402 * \param listEMail List of new email addresses.
1404 void addrbook_update_address_list(AddressBookFile *book, ItemPerson *person,
1411 cm_return_if_fail(book != NULL);
1412 cm_return_if_fail(person != NULL);
1414 /* Get groups where person's existing email addresses are listed */
1415 listGroup = addrcache_get_group_for_person(book->addressCache, person);
1417 GHashTable *hashEMail;
1418 GHashTable *hashEMailAlias;
1421 /* Load hash table with new address entries */
1422 hashEMail = g_hash_table_new(g_str_hash, g_str_equal);
1423 hashEMailAlias = g_hash_table_new(g_str_hash, g_str_equal);
1426 ItemEMail *email = node->data;
1427 gchar *alias = email->obj.name ;
1428 gchar *addr = g_utf8_strdown(email->address, -1);
1429 if (!g_hash_table_lookup(hashEMail, addr)) {
1430 g_hash_table_insert(hashEMail, addr, email);
1432 if (*alias != '\0' && ! g_hash_table_lookup(hashEMailAlias,
1434 g_hash_table_insert(hashEMailAlias, alias, email);
1436 node = g_list_next(node);
1439 /* Re-parent new addresses to existing groups, where email address match. */
1440 nodeGrp = listGroup;
1442 ItemGroup *group = (ItemGroup *) nodeGrp->data;
1443 GList *groupEMail = group->listEMail;
1445 GList *listRemove = NULL;
1447 /* Process each email item linked to group */
1448 nodeGrpEM = groupEMail;
1450 ItemEMail *emailGrp = (ItemEMail *) nodeGrpEM->data;
1452 if (ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person)) {
1453 /* Found an email address for this person */
1454 ItemEMail *emailNew = NULL;
1455 gchar *alias = emailGrp->obj.name;
1456 gchar *addr = g_utf8_strdown(emailGrp->address, -1);
1457 emailNew = (ItemEMail *)
1458 g_hash_table_lookup(hashEMail, addr);
1460 /* If no match by e-mail, try to match by e-mail alias */
1461 if (!emailNew && *alias != '\0') {
1462 emailNew = (ItemEMail *)
1463 g_hash_table_lookup(hashEMailAlias, alias);
1467 /* Point to this entry */
1468 nodeGrpEM->data = emailNew;
1469 else if (g_hash_table_size(hashEMail)==1)
1470 /* If the person has just one e-mail address, then
1471 change e-mail address in group list */
1472 nodeGrpEM->data = listEMail->data;
1474 /* Mark for removal */
1475 listRemove = g_list_append(listRemove, emailGrp);
1477 /* Move on to next email link */
1478 nodeGrpEM = g_list_next(nodeGrpEM);
1481 /* Process all removed links in current group */
1482 nodeGrpEM = listRemove;
1484 ItemEMail *emailGrp = nodeGrpEM->data;
1485 groupEMail = g_list_remove(groupEMail, emailGrp);
1486 nodeGrpEM = g_list_next(nodeGrpEM);
1489 g_list_free(listRemove);
1491 /* Move on to next group */
1492 nodeGrp = g_list_next(nodeGrp);
1495 /* Clear hash table */
1496 g_hash_table_foreach_remove(hashEMail, (GHRFunc)
1497 addrbook_free_simple_hash_vis, NULL);
1498 g_hash_table_destroy(hashEMail);
1500 g_hash_table_destroy(hashEMailAlias);
1501 hashEMailAlias = NULL;
1502 g_list_free(listGroup);
1505 /* Remove old addresses from person and cache */
1507 node = person->listEMail;
1509 ItemEMail *email = node->data;
1511 if (addrcache_person_remove_email(book->addressCache, person, email))
1512 addrcache_remove_email(book->addressCache, email);
1514 listDelete = g_list_append(listDelete, email);
1515 node = person->listEMail;
1517 /* Add new address entries */
1520 ItemEMail *email = node->data;
1522 if (ADDRITEM_ID(email) == NULL)
1523 /* Allocate an ID for new address */
1524 addrcache_id_email(book->addressCache, email);
1526 addrcache_person_add_email( book->addressCache, person, email );
1527 node = g_list_next( node );
1530 addrcache_set_dirty(book->addressCache, TRUE);
1532 /* Free up memory */
1533 g_list_free(listEMail);
1538 ItemEMail *email = node->data;
1540 addritem_free_item_email(email);
1541 node = g_list_next(node);
1543 g_list_free(listDelete);
1549 * Create person object and add person with specified address data to address
1550 * book. Note: A new person is created with specified list of email addresses.
1551 * All objects inserted into address book.
1553 * \param book Address book.
1554 * \param folder Parent folder where to add person, or <i>NULL</i> for
1556 * \param listEMail List of new email addresses to associate with person.
1557 * \return Person object created.
1559 ItemPerson *addrbook_add_address_list(AddressBookFile *book, ItemFolder *folder,
1563 ItemFolder *f = folder;
1566 cm_return_val_if_fail(book != NULL, NULL);
1569 f = book->addressCache->rootFolder;
1570 person = addritem_create_item_person();
1571 addrcache_id_person(book->addressCache, person);
1572 addrcache_folder_add_person(book->addressCache, f, person);
1576 ItemEMail *email = node->data;
1577 if (ADDRITEM_ID(email) == NULL)
1578 addrcache_id_email(book->addressCache, email);
1580 addrcache_person_add_email(book->addressCache, person, email);
1581 node = g_list_next(node);
1587 * Build available email list visitor function.
1588 * \param key Table key.
1589 * \param value Value stored in table.
1590 * \param data Reference to address book.
1592 static void addrbook_build_avail_email_vis(gpointer key, gpointer value,
1595 AddrItemObject *obj = (AddrItemObject *) value;
1597 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1598 AddressBookFile *book = data;
1599 ItemPerson *person = (ItemPerson *) obj;
1600 GList *node = person->listEMail;
1602 ItemEMail *email = node->data;
1603 /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */
1605 if (!g_hash_table_lookup(book->tempHash,
1606 ADDRITEM_ID(email)))
1607 book->tempList = g_list_append(book->tempList, email);
1609 node = g_list_next(node);
1615 * Return link list of available email items that have not already been linked
1616 * to groups. Note that the list contains references to items and should be
1617 * <code>g_free()</code> when done. Do <b>*NOT*</b> attempt to used the
1618 * <code>addrbook_free_xxx()<code> functions... this will destroy the
1621 * \param book Address book.
1622 * \param group Group to process.
1623 * \return List of items, or <i>NULL</i> if none.
1625 GList *addrbook_get_available_email_list(AddressBookFile *book, ItemGroup *group)
1630 cm_return_val_if_fail(book != NULL, NULL);
1632 /* Load hash table with group email entries */
1633 table = g_hash_table_new(g_str_hash, g_str_equal);
1635 list = group->listEMail;
1637 ItemEMail *email = list->data;
1638 g_hash_table_insert(table, ADDRITEM_ID(email), email);
1639 list = g_list_next(list);
1643 /* Build list of available email addresses which exclude those already in groups */
1644 book->tempList = NULL;
1645 book->tempHash = table;
1646 g_hash_table_foreach(book->addressCache->itemHash,
1647 addrbook_build_avail_email_vis, book);
1648 list = book->tempList;
1649 book->tempList = NULL;
1650 book->tempHash = NULL;
1652 /* Clear hash table */
1653 g_hash_table_destroy(table);
1660 * Update address book email list for specified group. Note: The existing email
1661 * addresses are replaced with the new addresses. Any references to old addresses
1662 * in the groups are re-linked to the new addresses. All old addresses linked to
1663 * the person are removed.
1665 * \param book Address book.
1666 * \param group Group to process.
1667 * \param listEMail List of email items. This should <b>*NOT*</b> be
1668 * <code>g_free()</code> when done.
1670 void addrbook_update_group_list(AddressBookFile *book, ItemGroup *group,
1675 cm_return_if_fail(book != NULL);
1676 cm_return_if_fail(group != NULL);
1678 addrcache_set_dirty(book->addressCache, TRUE);
1680 /* Remember old list */
1681 oldData = group->listEMail;
1682 group->listEMail = listEMail;
1683 mgu_clear_list(oldData);
1688 * Create group object and add with specifed list of email addresses to
1689 * address book. Note: The existing email addresses are replaced with the new
1690 * addresses. Any references to old addresses in the groups are re-linked to
1691 * the new addresses. All old addresses linked to the person are removed.
1693 * \param book Address book.
1694 * \param folder Parent folder where to add group, or <i>NULL</i> for
1696 * \param listEMail List of email items. This should <b>*NOT*</b> be
1697 * <code>g_free()</code> when done.
1698 * \return Group object created.
1700 ItemGroup *addrbook_add_group_list(AddressBookFile *book, ItemFolder *folder,
1703 ItemGroup *group = NULL;
1704 ItemFolder *f = folder;
1706 cm_return_val_if_fail(book != NULL, NULL);
1709 f = book->addressCache->rootFolder;
1710 group = addritem_create_item_group();
1711 addrcache_id_group(book->addressCache, group);
1712 addrcache_folder_add_group(book->addressCache, f, group);
1713 group->listEMail = listEMail;
1718 * Create a new folder and add to address book.
1719 * \param book Address book.
1720 * \param folder Parent folder where to add folder, or <i>NULL</i> for
1722 * \return Folder that was created. This should <b>*NOT*</b> be
1723 * <code>g_free()</code> when done.
1725 ItemFolder *addrbook_add_new_folder(AddressBookFile *book, ItemFolder *parent)
1727 cm_return_val_if_fail(book != NULL, NULL);
1728 return addrcache_add_new_folder( book->addressCache, parent );
1732 * Update address book attribute list for specified person. Note: The existing
1733 * attributes are replaced with the new addresses. All old attributes linked
1734 * to the person are removed.
1736 * \param book Address book.
1737 * \param person Person to receive attributes.
1738 * \param listAttrib New list of attributes.
1740 void addrbook_update_attrib_list(AddressBookFile *book, ItemPerson *person,
1746 cm_return_if_fail(book != NULL);
1747 cm_return_if_fail(person != NULL);
1749 /* Remember old list */
1750 oldData = person->listAttrib;
1752 /* Attach new address list to person. */
1755 UserAttribute *attrib = node->data;
1756 if (attrib->uid == NULL) {
1757 /* Allocate an ID */
1758 addrcache_id_attribute(book->addressCache, attrib);
1760 node = g_list_next(node);
1762 person->listAttrib = listAttrib;
1763 addrcache_set_dirty(book->addressCache, TRUE);
1765 /* Free up old data */
1766 addritem_free_list_attribute(oldData);
1771 * Add attribute data for specified person to address book. Note: Only
1772 * attributes are inserted into address book.
1773 * \param book Address book.
1774 * \param person Person to receive attributes.
1775 * \param listAttrib List of attributes.
1777 void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1780 cm_return_if_fail( book != NULL );
1781 cm_return_if_fail( person != NULL );
1785 UserAttribute *attrib = node->data;
1786 if( attrib->uid == NULL ) {
1787 addrcache_id_attribute( book->addressCache, attrib );
1789 addritem_person_add_attribute( person, attrib );
1790 node = g_list_next( node );
1792 addrcache_set_dirty( book->addressCache, TRUE );
1795 #define WORK_BUFLEN 1024
1796 #define ADDRBOOK_DIGITS "0123456789"
1799 * Return list of existing address book files.
1800 * \param book Address book.
1801 * \return List of files (as strings).
1803 GList *addrbook_get_bookfile_list(AddressBookFile *book) {
1806 const gchar *dir_name;
1807 struct stat statbuf;
1808 gchar buf[WORK_BUFLEN + 1];
1809 gchar numbuf[WORK_BUFLEN];
1810 gint len, lenpre, lensuf, lennum;
1811 long int val, maxval;
1812 GList *fileList = NULL;
1814 cm_return_val_if_fail(book != NULL, NULL);
1816 if (book->path == NULL || *book->path == '\0') {
1817 book->retVal = MGU_NO_PATH;
1821 strncpy(buf, book->path, WORK_BUFLEN);
1824 if (buf[len-1] != G_DIR_SEPARATOR) {
1825 buf[len] = G_DIR_SEPARATOR;
1830 adbookdir = g_strdup(buf);
1831 strncat(buf, ADDRBOOK_PREFIX, WORK_BUFLEN - strlen(buf));
1833 if( ( dir = g_dir_open( adbookdir, 0, NULL ) ) == NULL ) {
1834 book->retVal = MGU_OPEN_DIRECTORY;
1839 lenpre = strlen(ADDRBOOK_PREFIX);
1840 lensuf = strlen(ADDRBOOK_SUFFIX);
1841 lennum = FILE_NUMDIGITS + lenpre;
1844 while( ( dir_name = g_dir_read_name( dir ) ) != NULL ) {
1845 gchar *endptr = NULL;
1849 strncpy(buf, adbookdir, WORK_BUFLEN);
1850 strncat(buf, dir_name, WORK_BUFLEN - strlen(buf));
1851 g_stat(buf, &statbuf);
1852 if (S_ISREG(statbuf.st_mode)) {
1855 ADDRBOOK_PREFIX, lenpre) == 0)
1858 (dir_name) + lennum,
1859 ADDRBOOK_SUFFIX, lensuf) == 0)
1862 (dir_name) + lenpre,
1864 numbuf[FILE_NUMDIGITS] = '\0';
1866 for(i = 0; i < FILE_NUMDIGITS; i++) {
1867 if(!strchr(ADDRBOOK_DIGITS, numbuf[i])) {
1874 val = strtol(numbuf, &endptr, 10);
1875 if (endptr && val > -1) {
1876 if (val > maxval) maxval = val;
1877 fileList = g_list_append(
1879 g_strdup(dir_name));
1889 book->maxValue = maxval;
1890 book->retVal = MGU_SUCCESS;
1895 * Return file name for specified file number.
1896 * \param fileNum File number.
1897 * \return File name, or <i>NULL</i> if file number too large. Should be
1898 * <code>g_free()</code> when done.
1900 gchar *addrbook_gen_new_file_name(gint fileNum) {
1902 gchar buf[WORK_BUFLEN];
1908 nmax = -1 + (long int) pow(10, FILE_NUMDIGITS);
1911 g_snprintf(fmt, sizeof(fmt), "%%s%%0%dd%%s", FILE_NUMDIGITS);
1912 g_snprintf(buf, sizeof(buf), fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX);
1913 return g_strdup(buf);
1917 * **********************************************************************
1918 * Address book test functions...
1919 * **********************************************************************
1923 * Attempt to parse list of email address from file.
1924 * \param book Address book.
1925 * \param file XML file handle.
1927 static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file ){
1932 prev_level = file->level;
1933 if (xml_parse_next_tag(file))
1934 longjmp(book->jumper, 1);
1935 if (file->level < prev_level)
1937 attr = xml_get_current_tag_attr(file);
1938 /* addrbook_show_attribs( attr ); */
1939 if (xml_compare_tag(file, AB_ELTAG_ADDRESS))
1940 addrbook_chkparse_addr_list(book, file);
1945 * Attempt to parse attributes for person address from file.
1946 * \param book Address book.
1947 * \param file XML file handle.
1949 static void addrbook_chkparse_attribute(AddressBookFile *book, XMLFile *file)
1954 attr = xml_get_current_tag_attr(file);
1955 /* addrbook_show_attribs( attr ); */
1956 element = xml_get_element(file);
1957 /* g_print( "\t\tattrib value : %s\n", element ); */
1961 * Attempt to parse list of attributes for person address from file.
1962 * \param book Address book.
1963 * \param file XML file handle.
1965 static void addrbook_chkparse_attr_list(AddressBookFile *book, XMLFile *file)
1970 prev_level = file->level;
1971 if (xml_parse_next_tag(file))
1972 longjmp(book->jumper, 1);
1973 if (file->level < prev_level)
1975 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
1976 addrbook_chkparse_attribute(book, file);
1977 addrbook_chkparse_attr_list(book, file);
1983 * Attempt to parse person from file.
1984 * \param book Address book.
1985 * \param file XML file handle.
1987 static void addrbook_chkparse_person(AddressBookFile *book, XMLFile *file)
1991 attr = xml_get_current_tag_attr(file);
1992 /* addrbook_show_attribs( attr ); */
1993 if (xml_parse_next_tag(file)) /* Consume closing tag */
1994 longjmp(book->jumper, 1);
1996 if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST))
1997 addrbook_chkparse_addr_list(book, file);
1999 if (xml_parse_next_tag(file)) /* Consume closing tag */
2000 longjmp(book->jumper, 1);
2002 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST))
2003 addrbook_chkparse_attr_list(book, file);
2007 * Attempt to parse list of members from file.
2008 * \param book Address book.
2009 * \param file XML file handle.
2011 static void addrbook_chkparse_member_list(AddressBookFile *book, XMLFile *file)
2017 prev_level = file->level;
2018 if (xml_parse_next_tag(file))
2019 longjmp(book->jumper, 1);
2021 if (file->level < prev_level)
2024 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
2025 attr = xml_get_current_tag_attr(file);
2026 /* addrbook_show_attribs( attr ); */
2027 addrbook_chkparse_member_list(book, file);
2030 attr = xml_get_current_tag_attr(file);
2031 /* addrbook_show_attribs( attr ); */
2037 * Attempt to parse group from file.
2038 * \param book Address book.
2039 * \param file XML file handle.
2041 static void addrbook_chkparse_group(AddressBookFile *book, XMLFile *file)
2045 attr = xml_get_current_tag_attr(file);
2046 /* addrbook_show_attribs( attr ); */
2047 if (xml_parse_next_tag(file)) /* Consume closing tag */
2048 longjmp(book->jumper, 1);
2050 if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST))
2051 addrbook_chkparse_member_list(book, file);
2055 * Attempt to parse list of folders from file.
2056 * \param book Address book.
2057 * \param file XML file handle.
2059 static void addrbook_chkparse_folder_list(AddressBookFile *book, XMLFile *file)
2065 prev_level = file->level;
2066 if (xml_parse_next_tag(file))
2067 longjmp(book->jumper, 1);
2069 if (file->level < prev_level)
2072 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
2073 attr = xml_get_current_tag_attr(file);
2074 /* addrbook_show_attribs( attr ); */
2075 addrbook_chkparse_folder_list(book, file);
2078 attr = xml_get_current_tag_attr(file);
2079 /* addrbook_show_attribs( attr ); */
2085 * Attempt to parse a folder from file.
2086 * \param book Address book.
2087 * \param file XML file handle.
2089 static void addrbook_chkparse_folder(AddressBookFile *book, XMLFile *file)
2093 attr = xml_get_current_tag_attr(file);
2094 /* addrbook_show_attribs( attr ); */
2095 if (xml_parse_next_tag(file)) /* Consume closing tag */
2096 longjmp(book->jumper, 1);
2098 if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST))
2099 addrbook_chkparse_folder_list(book, file);
2103 * Attempt to parse (DOM) tree from file.
2104 * \param book Address book.
2105 * \param file XML file handle.
2107 static gboolean addrbook_chkread_tree(AddressBookFile *book, XMLFile *file)
2112 if (xml_get_dtd(file))
2115 if (xml_parse_next_tag(file))
2118 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
2121 attr = xml_get_current_tag_attr(file);
2122 /* addrbook_show_attribs( attr ); */
2129 if (xml_parse_next_tag(file))
2130 longjmp(book->jumper, 1);
2132 /* Get next tag (person, group or folder) */
2133 if (xml_compare_tag(file, AB_ELTAG_PERSON))
2134 addrbook_chkparse_person( book, file );
2135 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
2136 addrbook_chkparse_group(book, file);
2137 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
2138 addrbook_chkparse_folder(book, file);
2144 * Test address book file by parsing contents.
2145 * \param book Address book.
2146 * \param fileName Filename of XML file.
2147 * \return Status code <i>MGU_SUCCESS</i> if file appears to be valid format.
2149 gint addrbook_test_read_file(AddressBookFile *book, gchar *fileName)
2151 XMLFile *file = NULL;
2152 gchar *fileSpec = NULL;
2154 cm_return_val_if_fail(book != NULL, -1);
2156 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, fileName, NULL);
2157 book->retVal = MGU_OPEN_FILE;
2158 file = xml_open_file(fileSpec);
2161 book->retVal = MGU_BAD_FORMAT;
2162 if (setjmp(book->jumper)) {
2163 /* g_print( "Caught Ya!!!\n" ); */
2164 xml_close_file(file);
2165 return book->retVal;
2167 if (addrbook_chkread_tree(book, file))
2168 book->retVal = MGU_SUCCESS;
2170 xml_close_file( file );
2172 return book->retVal;
2176 * Return link list of all persons in address book. Note that the list
2177 * contains references to items. Do <b>*NOT*</b> attempt to use the
2178 * <code>addrbook_free_xxx()</code> functions... this will destroy the
2180 * \param book Address book.
2181 * \return List of persons, or NULL if none.
2183 GList *addrbook_get_all_persons(AddressBookFile *book)
2185 cm_return_val_if_fail(book != NULL, NULL);
2186 return addrcache_get_all_persons(book->addressCache);
2189 GList *addrbook_get_all_groups(AddressBookFile *book)
2191 cm_return_val_if_fail(book != NULL, NULL);
2192 return addrcache_get_all_groups(book->addressCache);
2196 * Add person and address data to address book.
2197 * \param book Address book.
2198 * \param folder Folder where to add person, or NULL for root folder.
2199 * \param name Common name.
2200 * \param address EMail address.
2201 * \param remarks Remarks.
2202 * \return Person added. Do not <b>*NOT*</b> to use the
2203 * <code>addrbook_free_xxx()</code> functions... this will destroy
2204 * the address book data.
2206 ItemPerson *addrbook_add_contact(AddressBookFile *book, ItemFolder *folder,
2207 const gchar *name,const gchar *address,
2208 const gchar *remarks)
2212 cm_return_val_if_fail(book != NULL, NULL);
2213 person = addrcache_add_contact(
2214 book->addressCache, folder, name, address, remarks );
2219 * Return file name for next address book file.
2220 * \param book Address book.
2221 * \return File name, or <i>NULL</i> if could not create. This should be
2222 * <code>g_free()</code> when done.
2224 gchar *addrbook_guess_next_file(AddressBookFile *book)
2226 gchar *newFile = NULL;
2227 GList *fileList = NULL;
2229 fileList = addrbook_get_bookfile_list(book);
2231 fileNum = 1 + book->maxValue;
2233 newFile = addrbook_gen_new_file_name(fileNum);
2234 g_list_free(fileList);
2239 void addrbook_delete_book_file(AddressBookFile *book)
2243 if (!book->path || !book->fileName)
2246 book_path = g_strconcat(book->path, G_DIR_SEPARATOR_S,
2247 book->fileName, NULL);
2248 claws_unlink(book_path);