2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2001-2022 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/>.
19 /* General functions for accessing address book files */
23 #include "claws-features.h"
37 #include "addrcache.h"
39 #include "adbookbase.h"
40 #include "file-utils.h"
42 #ifndef DEV_STANDALONE
43 #include "prefs_gtk.h"
47 #define ADDRBOOK_MAX_SEARCH_COUNT 1000
48 #define ADDRBOOK_PREFIX "addrbook-"
49 #define ADDRBOOK_SUFFIX ".xml"
50 #define FILE_NUMDIGITS 6
52 #define ID_TIME_OFFSET 998000000
55 static void addrbook_print_book ( AddressBookFile
*book
, FILE *stream
);
59 * Create new address book
60 * \return Address book.
62 AddressBookFile
*addrbook_create_book()
64 AddressBookFile
*book
;
66 book
= g_new0(AddressBookFile
, 1);
67 book
->type
= ADBOOKTYPE_BOOK
;
68 book
->addressCache
= addrcache_create();
69 book
->retVal
= MGU_SUCCESS
;
71 book
->fileName
= NULL
;
73 book
->tempList
= NULL
;
74 book
->tempHash
= NULL
;
75 book
->addressCache
->modified
= TRUE
;
81 * Specify name to be used
82 * \param book Address book.
85 void addrbook_set_name(AddressBookFile
*book
, const gchar
*value
)
87 cm_return_if_fail(book
!= NULL
);
88 addrcache_set_name(book
->addressCache
, value
);
91 gchar
*addrbook_get_name(AddressBookFile
*book
)
93 cm_return_val_if_fail(book
!= NULL
, NULL
);
94 return addrcache_get_name(book
->addressCache
);
98 * Specify path to address book file.
99 * \param book Address book.
102 void addrbook_set_path(AddressBookFile
*book
, const gchar
*value
)
104 cm_return_if_fail(book
!= NULL
);
105 book
->path
= mgu_replace_string(book
->path
, value
);
106 addrcache_set_dirty(book
->addressCache
, TRUE
);
110 * Specify filename to be used
111 * \param book Address book.
112 * \param value Filename.
114 void addrbook_set_file(AddressBookFile
*book
, const gchar
*value
)
116 cm_return_if_fail(book
!= NULL
);
117 book
->fileName
= mgu_replace_string(book
->fileName
, value
);
118 addrcache_set_dirty(book
->addressCache
, TRUE
);
121 gboolean
addrbook_get_modified(AddressBookFile
*book
)
123 cm_return_val_if_fail(book
!= NULL
, FALSE
);
124 return book
->addressCache
->modified
;
127 gboolean
addrbook_get_accessed(AddressBookFile
*book
)
129 cm_return_val_if_fail(book
!= NULL
, FALSE
);
130 return book
->addressCache
->accessFlag
;
134 * Specify address book as accessed.
135 * \param book Address book.
136 * \param value Value.
138 void addrbook_set_accessed(AddressBookFile
*book
, const gboolean value
)
140 cm_return_if_fail(book
!= NULL
);
141 book
->addressCache
->accessFlag
= value
;
144 gboolean
addrbook_get_read_flag(AddressBookFile
*book
)
146 cm_return_val_if_fail(book
!= NULL
, FALSE
);
147 return book
->addressCache
->dataRead
;
150 gint
addrbook_get_status(AddressBookFile
*book
)
152 cm_return_val_if_fail(book
!= NULL
, -1);
156 ItemFolder
*addrbook_get_root_folder(AddressBookFile
*book
)
158 cm_return_val_if_fail(book
!= NULL
, NULL
);
159 return addrcache_get_root_folder(book
->addressCache
);
162 GList
*addrbook_get_list_folder(AddressBookFile
*book
)
164 cm_return_val_if_fail(book
!= NULL
, NULL
);
165 return addrcache_get_list_folder(book
->addressCache
);
168 GList
*addrbook_get_list_person(AddressBookFile
*book
)
170 cm_return_val_if_fail(book
!= NULL
, NULL
);
171 return addrcache_get_list_person(book
->addressCache
);
174 gboolean
addrbook_get_dirty(AddressBookFile
*book
)
176 cm_return_val_if_fail(book
!= NULL
, FALSE
);
177 return addrcache_get_dirty(book
->addressCache
);
181 * Set address book as dirty (needs to be written to file).
182 * \param book Address book.
183 * \param value Dirty flag.
185 void addrbook_set_dirty(AddressBookFile
*book
, const gboolean value
)
187 cm_return_if_fail(book
!= NULL
);
188 addrcache_set_dirty(book
->addressCache
, value
);
193 * \param book Address book.
195 void addrbook_free_book(AddressBookFile
*book
)
197 cm_return_if_fail(book
!= NULL
);
200 addrcache_free(book
->addressCache
);
202 /* Free up internal objects */
204 g_free(book
->fileName
);
205 g_list_free(book
->tempList
);
208 book
->fileName
= NULL
;
210 book
->tempList
= NULL
;
211 book
->tempHash
= NULL
;
213 book
->type
= ADBOOKTYPE_NONE
;
214 book
->addressCache
= NULL
;
215 book
->retVal
= MGU_SUCCESS
;
220 #ifdef DEBUG_ADDRBOOK
222 * Print address book header.
223 * \param book Address book.
224 * \param stream Output stream.
226 static void addrbook_print_book(AddressBookFile
*book
, FILE *stream
)
228 cm_return_if_fail(book
!= NULL
);
230 fprintf(stream
, "AddressBook:\n");
231 fprintf(stream
, "\tpath : '%s'\n", book
->path
);
232 fprintf(stream
, "\tfile : '%s'\n", book
->fileName
);
233 fprintf(stream
, "\tstatus : %d\n", book
->retVal
);
234 addrcache_print(book
->addressCache
, stream
);
238 * Dump entire address book traversing folders.
239 * \param book Address book.
240 * \param stream Output stream.
242 void addrbook_dump_book(AddressBookFile
*book
, FILE *stream
)
246 cm_return_if_fail(book
!= NULL
);
248 addrbook_print_book(book
, stream
);
249 folder
= book
->addressCache
->rootFolder
;
250 addritem_print_item_folder(folder
, stream
);
255 * Remove specified group from address book. Note that object should still
257 * Specify name to be used
258 * \param book Address book.
259 * \param group Group to remove.
261 * \return Group, or NULL if not found.
263 ItemGroup
*addrbook_remove_group(AddressBookFile
*book
, ItemGroup
*group
)
265 cm_return_val_if_fail(book
!= NULL
, NULL
);
266 return addrcache_remove_group(book
->addressCache
, group
);
270 * Remove specified person from address book. Note that object should still
272 * \param book Address book.
273 * \param person Person to remove.
274 * \return Person, or NULL if not found.
276 ItemPerson
*addrbook_remove_person(AddressBookFile
*book
, ItemPerson
*person
)
278 cm_return_val_if_fail(book
!= NULL
, NULL
);
279 return addrcache_remove_person(book
->addressCache
, person
);
283 * Remove specified email address in address book for specified person.
284 * Note that object should still be freed.
285 * \param book Address book.
286 * \param person Person.
287 * \param email EMail to remove.
288 * \return EMail object, or NULL if not found.
290 ItemEMail
*addrbook_person_remove_email(AddressBookFile
*book
,
291 ItemPerson
*person
, ItemEMail
*email
)
293 cm_return_val_if_fail(book
!= NULL
, NULL
);
294 return addrcache_person_remove_email(book
->addressCache
, person
, email
);
298 * ***********************************************************************
299 * Read/Write XML data file...
300 * ===========================
302 * 1) The address book is structured as follows:
317 * 2) This sequence of elements was chosen so that the most important
318 * elements (person and their email addresses) appear first.
320 * 3) Groups then appear. When groups are loaded, person's email
321 * addresses have already been loaded and can be found.
323 * 4) Finally folders are loaded. Any forward and backward references
324 * to folders, groups and persons in the folders are resolved after
327 * ***********************************************************************
330 /* Element tag names */
331 #define AB_ELTAG_ADDRESS "address"
332 #define AB_ELTAG_ATTRIBUTE "attribute"
333 #define AB_ELTAG_ATTRIBUTE_LIST "attribute-list"
334 #define AB_ELTAG_ADDRESS_LIST "address-list"
335 #define AB_ELTAG_MEMBER "member"
336 #define AB_ELTAG_MEMBER_LIST "member-list"
337 #define AB_ELTAG_ITEM "item"
338 #define AB_ELTAG_ITEM_LIST "item-list"
339 #define AB_ELTAG_ADDRESS_BOOK "address-book"
340 #define AB_ELTAG_PERSON "person"
341 #define AB_ELTAG_GROUP "group"
342 #define AB_ELTAG_FOLDER "folder"
344 /* Attribute tag names */
345 #define AB_ATTAG_TYPE "type"
346 #define AB_ATTAG_UID "uid"
347 #define AB_ATTAG_NAME "name"
348 #define AB_ATTAG_REMARKS "remarks"
349 #define AB_ATTAG_FIRST_NAME "first-name"
350 #define AB_ATTAG_LAST_NAME "last-name"
351 #define AB_ATTAG_NICK_NAME "nick-name"
352 #define AB_ATTAG_COMMON_NAME "cn"
353 #define AB_ATTAG_ALIAS "alias"
354 #define AB_ATTAG_EMAIL "email"
355 #define AB_ATTAG_EID "eid"
356 #define AB_ATTAG_PID "pid"
358 /* Attribute values */
359 #define AB_ATTAG_VAL_PERSON "person"
360 #define AB_ATTAG_VAL_GROUP "group"
361 #define AB_ATTAG_VAL_FOLDER "folder"
364 * Parse address item for person from XML file.
365 * \param book Address book.
366 * \param file XML file handle.
367 * \param person Person.
369 static void addrbook_parse_address(AddressBookFile
*book
, XMLFile
*file
,
374 ItemEMail
*email
= NULL
;
376 attr
= xml_get_current_tag_attr(file
);
378 name
= ((XMLAttr
*)attr
->data
)->name
;
379 value
= ((XMLAttr
*)attr
->data
)->value
;
381 email
= addritem_create_item_email();
382 if (strcmp(name
, AB_ATTAG_UID
) == 0)
383 ADDRITEM_ID(email
) = g_strdup(value
);
384 else if (strcmp(name
, AB_ATTAG_ALIAS
) == 0)
385 ADDRITEM_NAME(email
) = g_strdup(value
);
386 else if (strcmp(name
, AB_ATTAG_EMAIL
) == 0)
387 email
->address
= g_strdup(value
);
388 else if (strcmp(name
, AB_ATTAG_REMARKS
) == 0)
389 email
->remarks
= g_strdup(value
);
390 attr
= g_list_next(attr
);
394 addrcache_person_add_email(book
->addressCache
, person
,
398 addritem_free_item_email(email
);
405 * Parse list of email address for person from XML file.
406 * \param book Address book.
407 * \param file XML file handle.
408 * \param person Person.
410 static void addrbook_parse_addr_list(AddressBookFile
*book
, XMLFile
*file
,
416 prev_level
= file
->level
;
417 if (xml_parse_next_tag(file
)) {
418 longjmp(book
->jumper
, 1);
420 if (file
->level
< prev_level
) return;
421 if (xml_compare_tag(file
, AB_ELTAG_ADDRESS
)) {
422 addrbook_parse_address(book
, file
, person
);
423 addrbook_parse_addr_list(book
, file
, person
);
429 * Parse attribute for person from XML file.
430 * \param book Address book.
431 * \param file XML file handle.
432 * \param person Person.
434 static void addrbook_parse_attribute(XMLFile
*file
, ItemPerson
*person
)
439 UserAttribute
*uAttr
= NULL
;
441 attr
= xml_get_current_tag_attr(file
);
443 name
= ((XMLAttr
*)attr
->data
)->name
;
444 value
= ((XMLAttr
*)attr
->data
)->value
;
445 if (!uAttr
) uAttr
= addritem_create_attribute();
446 if (strcmp(name
, AB_ATTAG_UID
) == 0)
447 addritem_attrib_set_id(uAttr
, value
);
448 else if (strcmp(name
, AB_ATTAG_NAME
) == 0)
449 addritem_attrib_set_name(uAttr
, value
);
450 attr
= g_list_next(attr
);
453 element
= xml_get_element(file
);
454 addritem_attrib_set_value(uAttr
, element
);
459 addritem_person_add_attribute(person
, uAttr
);
462 addritem_free_attribute(uAttr
);
469 * Parse list of attributes for person from XML file.
470 * \param book Address book.
471 * \param file XML file handle.
472 * \param person Person.
474 static void addrbook_parse_attr_list(AddressBookFile
*book
, XMLFile
*file
,
480 prev_level
= file
->level
;
481 if (xml_parse_next_tag(file
)) {
482 longjmp( book
->jumper
, 1 );
484 if (file
->level
< prev_level
) return;
485 if (xml_compare_tag(file
, AB_ELTAG_ATTRIBUTE
)) {
486 addrbook_parse_attribute(file
, person
);
487 addrbook_parse_attr_list(book
, file
, person
);
493 * Parse person from XML file.
494 * \param book Address book.
495 * \param file XML file handle.
497 static void addrbook_parse_person(AddressBookFile
*book
, XMLFile
*file
)
501 ItemPerson
*person
= NULL
;
503 attr
= xml_get_current_tag_attr(file
);
505 name
= ((XMLAttr
*)attr
->data
)->name
;
506 value
= ((XMLAttr
*)attr
->data
)->value
;
508 person
= addritem_create_item_person();
509 if (strcmp(name
, AB_ATTAG_UID
) == 0) {
510 ADDRITEM_ID(person
) = g_strdup(value
);
511 person
->picture
= g_strdup(value
);
513 else if (strcmp(name
, AB_ATTAG_FIRST_NAME
) == 0)
514 person
->firstName
= g_strdup(value
);
515 else if (strcmp(name
, AB_ATTAG_LAST_NAME
) == 0)
516 person
->lastName
= g_strdup(value
);
517 else if (strcmp(name
, AB_ATTAG_NICK_NAME
) == 0)
518 person
->nickName
= g_strdup(value
);
519 else if (strcmp(name
, AB_ATTAG_COMMON_NAME
) == 0)
520 ADDRITEM_NAME(person
) = g_strdup(value
);
521 attr
= g_list_next(attr
);
523 if (xml_parse_next_tag(file
)) { /* Consume closing tag */
524 longjmp(book
->jumper
, 1);
526 if (xml_compare_tag(file
, AB_ELTAG_ADDRESS_LIST
)) {
527 addrbook_parse_addr_list(book
, file
, person
);
529 addrcache_hash_add_person(book
->addressCache
, person
);
532 if (xml_parse_next_tag(file
)) { /* Consume closing tag */
533 longjmp(book
->jumper
, 1);
535 if (xml_compare_tag(file
, AB_ELTAG_ATTRIBUTE_LIST
)) {
536 addrbook_parse_attr_list(book
, file
, person
);
541 * Parse group member from XML file.
542 * \param book Address book.
543 * \param file XML file handle.
544 * \param group Group.
546 static void addrbook_parse_member(AddressBookFile
*book
, XMLFile
*file
,
552 /* gchar *pid = NULL; */
553 ItemEMail
*email
= NULL
;
555 attr
= xml_get_current_tag_attr(file
);
557 name
= ((XMLAttr
*)attr
->data
)->name
;
558 value
= ((XMLAttr
*)attr
->data
)->value
;
559 if( strcmp( name
, AB_ATTAG_EID
) == 0 )
560 eid
= g_strdup( value
);
561 attr
= g_list_next(attr
);
563 /* email = addrcache_get_email( book->addressCache, pid, eid ); */
564 email
= addrcache_get_email(book
->addressCache
, eid
);
568 addrcache_group_add_email(book
->addressCache
, group
,
572 addritem_free_item_email(email
);
579 * Parse list of group members from XML file.
580 * \param book Address book.
581 * \param file XML file handle.
582 * \param group Group.
584 static void addrbook_parse_member_list(AddressBookFile
*book
, XMLFile
*file
,
590 prev_level
= file
->level
;
591 if (xml_parse_next_tag(file
)) {
592 longjmp(book
->jumper
, 1);
594 if (file
->level
< prev_level
)
596 if (xml_compare_tag(file
, AB_ELTAG_MEMBER
)) {
597 addrbook_parse_member(book
, file
, group
);
598 addrbook_parse_member_list(book
, file
, group
);
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
);
638 addritem_free_item_group(group
);
643 * Parse folder item from XML file.
644 * \param book Address book.
645 * \param file XML file handle.
646 * \param folder Folder.
648 static void addrbook_parse_folder_item(AddressBookFile
*book
, XMLFile
*file
,
655 attr
= xml_get_current_tag_attr(file
);
657 name
= ((XMLAttr
*)attr
->data
)->name
;
658 value
= ((XMLAttr
*)attr
->data
)->value
;
659 if (strcmp(name
, AB_ATTAG_UID
) == 0) {
660 uid
= g_strdup(value
);
662 attr
= g_list_next(attr
);
666 folder
->listItems
= g_list_append(folder
->listItems
, uid
);
672 * Parse list of folder items from XML file.
673 * \param book Address book.
674 * \param file XML file handle.
675 * \param folder Folder.
677 static void addrbook_parse_folder_list(AddressBookFile
*book
, XMLFile
*file
,
683 prev_level
= file
->level
;
684 if (xml_parse_next_tag(file
)) {
685 longjmp(book
->jumper
, 1);
687 if (file
->level
< prev_level
)
689 if (xml_compare_tag(file
, AB_ELTAG_ITEM
)) {
690 addrbook_parse_folder_item(book
, file
, folder
);
691 addrbook_parse_folder_list(book
, file
, folder
);
697 * Parse folder from XML file.
698 * \param book Address book.
699 * \param file XML file handle.
701 static void addrbook_parse_folder(AddressBookFile
*book
, XMLFile
*file
)
705 ItemFolder
*folder
= NULL
;
707 attr
= xml_get_current_tag_attr(file
);
709 name
= ((XMLAttr
*)attr
->data
)->name
;
710 value
= ((XMLAttr
*)attr
->data
)->value
;
712 folder
= addritem_create_item_folder();
713 if (strcmp(name
, AB_ATTAG_UID
) == 0)
714 ADDRITEM_ID(folder
) = g_strdup(value
);
715 else if (strcmp(name
, AB_ATTAG_NAME
) == 0)
716 ADDRITEM_NAME(folder
) = g_strdup(value
);
717 else if (strcmp(name
, AB_ATTAG_REMARKS
) == 0)
718 folder
->remarks
= g_strdup(value
);
719 attr
= g_list_next(attr
);
721 if (xml_parse_next_tag(file
)) { /* Consume closing tag */
722 longjmp(book
->jumper
, 1);
724 if (xml_compare_tag(file
, AB_ELTAG_ITEM_LIST
)) {
726 if (addrcache_hash_add_folder(book
->addressCache
,
728 book
->tempList
= g_list_append(book
->tempList
,
730 /* We will resolve folder later */
731 ADDRITEM_PARENT(folder
) = NULL
;
734 addrbook_parse_folder_list(book
, file
, folder
);
737 addritem_free_item_folder(folder
);
742 * Read address book (DOM) tree from file.
743 * \param book Address book.
744 * \param file XML file handle.
745 * \return <i>TRUE</i> if data read successfully, <i>FALSE</i> if error
748 static gboolean
addrbook_read_tree(AddressBookFile
*book
, XMLFile
*file
)
754 book
->retVal
= MGU_BAD_FORMAT
;
755 if (xml_get_dtd(file
))
757 if (xml_parse_next_tag(file
))
758 longjmp(book
->jumper
, 1);
759 if (!xml_compare_tag(file
, AB_ELTAG_ADDRESS_BOOK
))
762 attr
= xml_get_current_tag_attr(file
);
764 name
= ((XMLAttr
*)attr
->data
)->name
;
765 value
= ((XMLAttr
*)attr
->data
)->value
;
766 if (strcmp( name
, AB_ATTAG_NAME
) == 0)
767 addrbook_set_name( book
, value
);
768 attr
= g_list_next( attr
);
775 /* Get next item tag (person, group or folder) */
776 if (xml_parse_next_tag(file
))
777 longjmp( book
->jumper
, 1 );
779 if (xml_compare_tag(file
, AB_ELTAG_PERSON
))
780 addrbook_parse_person(book
, file
);
781 else if (xml_compare_tag(file
, AB_ELTAG_GROUP
))
782 addrbook_parse_group(book
, file
);
783 else if (xml_compare_tag(file
, AB_ELTAG_FOLDER
))
784 addrbook_parse_folder(book
, file
);
786 if (retVal
) book
->retVal
= MGU_SUCCESS
;
791 * Resolve folder items callback function.
792 * \param key Table key.
793 * \param value Reference to object contained in folder.
794 * \param data Reference to address book.
796 static void addrbook_res_items_vis(gpointer key
, gpointer value
, gpointer data
)
798 AddressBookFile
*book
= data
;
799 AddrItemObject
*obj
= (AddrItemObject
*) value
;
800 ItemFolder
*rootFolder
= book
->addressCache
->rootFolder
;
801 if (obj
->parent
== NULL
) {
802 if (ADDRITEM_TYPE(obj
) == ITEMTYPE_PERSON
) {
803 rootFolder
->listPerson
= g_list_append(rootFolder
->listPerson
,
805 ADDRITEM_PARENT(obj
) = ADDRITEM_OBJECT(rootFolder
);
807 else if (ADDRITEM_TYPE(obj
) == ITEMTYPE_GROUP
) {
808 rootFolder
->listGroup
= g_list_append(rootFolder
->listGroup
,
810 ADDRITEM_PARENT(obj
) = ADDRITEM_OBJECT(rootFolder
);
816 * Resolve folder items. Lists of UID's are replaced with pointers to
818 * \param book Address book.
820 static void addrbook_resolve_folder_items(AddressBookFile
*book
)
822 GList
*nodeFolder
= NULL
;
823 GList
*listRemove
= NULL
;
825 ItemFolder
*rootFolder
= book
->addressCache
->rootFolder
;
826 nodeFolder
= book
->tempList
;
829 ItemFolder
*folder
= nodeFolder
->data
;
831 node
= folder
->listItems
;
833 gchar
*uid
= node
->data
;
834 AddrItemObject
*aio
= addrcache_get_object(book
->addressCache
,
837 if (aio
->type
== ITEMTYPE_FOLDER
) {
838 ItemFolder
*item
= (ItemFolder
*) aio
;
839 folder
->listFolder
= g_list_append(folder
->listFolder
, item
);
840 ADDRITEM_PARENT(item
) = ADDRITEM_OBJECT(folder
);
841 addrcache_hash_add_folder(book
->addressCache
, folder
);
843 else if (aio
->type
== ITEMTYPE_PERSON
) {
844 ItemPerson
*item
= (ItemPerson
*) aio
;
845 folder
->listPerson
= g_list_append(folder
->listPerson
, item
);
846 ADDRITEM_PARENT(item
) = ADDRITEM_OBJECT(folder
);
848 else if (aio
->type
== ITEMTYPE_GROUP
) {
849 ItemGroup
*item
= (ItemGroup
*) aio
;
850 folder
->listGroup
= g_list_append(folder
->listGroup
, item
);
851 ADDRITEM_PARENT(item
) = ADDRITEM_OBJECT(folder
);
853 /* Replace data with pointer to item */
857 else { /* Not found, append to remove list. */
858 listRemove
= g_list_append(listRemove
, uid
);
860 node
= g_list_next(node
);
862 rootFolder
->listFolder
= g_list_append(rootFolder
->listFolder
,
864 /* Process remove list */
867 gchar
*uid
= node
->data
;
868 folder
->listItems
= g_list_remove(folder
->listItems
,
871 node
= g_list_next(node
);
873 g_list_free(listRemove
);
874 nodeFolder
= g_list_next(nodeFolder
);
876 /* Remove folders with parents. */
878 node
= rootFolder
->listFolder
;
880 ItemFolder
*folder
= (ItemFolder
*) node
->data
;
881 if (ADDRITEM_PARENT(folder
))
882 /* Remove folders with parents */
883 listRemove
= g_list_append(listRemove
, folder
);
884 else /* Add to root folder */
885 ADDRITEM_PARENT(folder
) = ADDRITEM_OBJECT(book
->addressCache
->rootFolder
);
887 node
= g_list_next( node
);
889 /* Process remove list */
892 rootFolder
->listFolder
= g_list_remove(rootFolder
->listFolder
,
894 node
= g_list_next(node
);
896 g_list_free(listRemove
);
898 /* Move all unparented persons and groups into root folder */
899 g_hash_table_foreach(book
->addressCache
->itemHash
,
900 addrbook_res_items_vis
, book
);
902 /* Free up some more */
903 nodeFolder
= book
->tempList
;
905 ItemFolder
*folder
= nodeFolder
->data
;
906 g_list_free(folder
->listItems
);
907 folder
->listItems
= NULL
;
908 nodeFolder
= g_list_next(nodeFolder
);
910 g_list_free(book
->tempList
);
911 book
->tempList
= NULL
;
916 * \param book Address book.
917 * \return Status code.
919 gint
addrbook_read_data(AddressBookFile
*book
)
921 XMLFile
*file
= NULL
;
922 gchar
*fileSpec
= NULL
;
924 cm_return_val_if_fail(book
!= NULL
, -1);
927 g_print( "...addrbook_read_data :%s:\t:%s:\n", book->fileName,
928 addrcache_get_name( book->addressCache ) );
931 fileSpec
= g_strconcat(book
->path
, G_DIR_SEPARATOR_S
,
932 book
->fileName
, NULL
);
933 book
->retVal
= MGU_OPEN_FILE
;
934 addrcache_clear(book
->addressCache
);
935 book
->addressCache
->modified
= FALSE
;
936 book
->addressCache
->accessFlag
= FALSE
;
937 file
= xml_open_file(fileSpec
);
940 book
->tempList
= NULL
;
941 /* Trap for parsing errors. */
942 if (setjmp( book
->jumper
)) {
943 xml_close_file(file
);
946 addrbook_read_tree(book
, file
);
947 xml_close_file(file
);
948 /* Resolve folder items */
949 addrbook_resolve_folder_items(book
);
950 book
->tempList
= NULL
;
951 book
->addressCache
->modified
= FALSE
;
952 book
->addressCache
->dataRead
= TRUE
;
953 addrcache_set_dirty(book
->addressCache
, FALSE
);
959 * Write start element to file.
960 * \param fp File handle.
961 * \param lvl Indent level.
962 * \param name Element name.
964 static int addrbook_write_elem_s(FILE *fp
, gint lvl
, gchar
*name
)
967 for (i
= 0; i
< lvl
; i
++)
968 if (claws_fputs(" ", fp
) == EOF
)
970 if (claws_fputs("<", fp
) == EOF
)
972 if (claws_fputs(name
, fp
) == EOF
)
979 * Write end element to file.
980 * \param fp File handle.
981 * \param lvl Indent level.
982 * \param name Element name.
984 static int addrbook_write_elem_e(FILE *fp
, gint lvl
, gchar
*name
)
987 for(i
= 0; i
< lvl
; i
++)
988 if (claws_fputs(" ", fp
) == EOF
)
990 if (claws_fputs("</", fp
) == EOF
)
992 if (claws_fputs(name
, fp
) == EOF
)
994 if (claws_fputs(">\n", fp
) == EOF
)
1001 * Write attribute name/value pair to file.
1002 * \param fp File handle.
1003 * \param name Attribute name.
1004 * \param value Attribute value.
1006 static int addrbook_write_attr(FILE *fp
, gchar
*name
, gchar
*value
)
1008 if (claws_fputs(" ", fp
) == EOF
)
1010 if (claws_fputs(name
, fp
) == EOF
)
1012 if (claws_fputs("=\"", fp
) == EOF
)
1014 if (xml_file_put_escape_str(fp
, value
) < 0)
1016 if (claws_fputs("\"", fp
) == EOF
)
1022 typedef struct _HashLoopData
{
1028 * Write person and associated addresses and attributes to file.
1029 * file hash table visitor function.
1030 * \param key Table key.
1031 * \param value Reference to person.
1032 * \param data File pointer.
1034 static void addrbook_write_item_person_vis(gpointer key
, gpointer value
,
1037 AddrItemObject
*obj
= (AddrItemObject
*) value
;
1038 HashLoopData
*data
= (HashLoopData
*)d
;
1039 FILE *fp
= data
->fp
;
1044 if (ADDRITEM_TYPE(obj
) == ITEMTYPE_PERSON
) {
1045 ItemPerson
*person
= (ItemPerson
*) value
;
1047 if (addrbook_write_elem_s(fp
, 1, AB_ELTAG_PERSON
) < 0)
1049 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(person
)) < 0)
1051 if (addrbook_write_attr(fp
, AB_ATTAG_FIRST_NAME
, person
->firstName
) < 0)
1053 if (addrbook_write_attr(fp
, AB_ATTAG_LAST_NAME
, person
->lastName
) < 0)
1055 if (addrbook_write_attr(fp
, AB_ATTAG_NICK_NAME
, person
->nickName
) < 0)
1057 if (addrbook_write_attr(fp
, AB_ATTAG_COMMON_NAME
, ADDRITEM_NAME(person
)) < 0)
1059 if (claws_fputs(" >\n", fp
) == EOF
)
1062 /* Output email addresses */
1063 if (addrbook_write_elem_s(fp
, 2, AB_ELTAG_ADDRESS_LIST
) < 0)
1065 if (claws_fputs(">\n", fp
) == EOF
)
1067 node
= person
->listEMail
;
1069 ItemEMail
*email
= node
->data
;
1070 if (addrbook_write_elem_s(fp
, 3, AB_ELTAG_ADDRESS
) < 0)
1072 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(email
)) < 0)
1074 if (addrbook_write_attr(fp
, AB_ATTAG_ALIAS
, ADDRITEM_NAME(email
)) < 0)
1076 if (addrbook_write_attr(fp
, AB_ATTAG_EMAIL
, email
->address
) < 0)
1078 if (addrbook_write_attr(fp
, AB_ATTAG_REMARKS
, email
->remarks
) < 0)
1080 if (claws_fputs(" />\n", fp
) == EOF
)
1082 node
= g_list_next(node
);
1084 if (addrbook_write_elem_e(fp
, 2, AB_ELTAG_ADDRESS_LIST
) < 0)
1087 /* Output user attributes */
1088 if (addrbook_write_elem_s(fp
, 2, AB_ELTAG_ATTRIBUTE_LIST
) < 0)
1090 if (claws_fputs(">\n", fp
) == EOF
)
1092 node
= person
->listAttrib
;
1094 UserAttribute
*attrib
= node
->data
;
1095 if (addrbook_write_elem_s(fp
, 3, AB_ELTAG_ATTRIBUTE
) < 0)
1097 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, attrib
->uid
) < 0)
1099 if (addrbook_write_attr(fp
, AB_ATTAG_NAME
, attrib
->name
) < 0)
1101 if (claws_fputs(" >", fp
) == EOF
)
1103 if (xml_file_put_escape_str(fp
, attrib
->value
) < 0)
1105 if (addrbook_write_elem_e(fp
, 0, AB_ELTAG_ATTRIBUTE
) < 0)
1107 node
= g_list_next(node
);
1109 if (addrbook_write_elem_e(fp
, 2, AB_ELTAG_ATTRIBUTE_LIST
) < 0)
1111 if (addrbook_write_elem_e(fp
, 1, AB_ELTAG_PERSON
) < 0)
1118 * Write group and associated references to addresses to file.
1119 * file hash table visitor function.
1120 * \param key Table key.
1121 * \param value Reference to group.
1122 * \param data File pointer.
1124 static void addrbook_write_item_group_vis(gpointer key
, gpointer value
,
1127 AddrItemObject
*obj
= (AddrItemObject
*) value
;
1128 HashLoopData
*data
= (HashLoopData
*)d
;
1129 FILE *fp
= data
->fp
;
1135 if (ADDRITEM_TYPE(obj
) == ITEMTYPE_GROUP
) {
1136 ItemGroup
*group
= (ItemGroup
*) value
;
1138 if (addrbook_write_elem_s(fp
, 1, AB_ELTAG_GROUP
) < 0)
1140 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(group
)) < 0)
1142 if (addrbook_write_attr(fp
, AB_ATTAG_NAME
, ADDRITEM_NAME(group
)) < 0)
1144 if (addrbook_write_attr(fp
, AB_ATTAG_REMARKS
, group
->remarks
) < 0)
1146 if (claws_fputs(" >\n", fp
) == EOF
)
1149 /* Output email address links */
1150 if (addrbook_write_elem_s(fp
, 2, AB_ELTAG_MEMBER_LIST
) < 0)
1152 if (claws_fputs(">\n", fp
) == EOF
)
1154 node
= group
->listEMail
;
1156 ItemEMail
*email
= node
->data
;
1157 ItemPerson
*person
= (ItemPerson
*) ADDRITEM_PARENT(email
);
1158 if (addrbook_write_elem_s(fp
, 3, AB_ELTAG_MEMBER
) < 0)
1160 if (addrbook_write_attr(fp
, AB_ATTAG_PID
, ADDRITEM_ID(person
)) < 0)
1162 if (addrbook_write_attr(fp
, AB_ATTAG_EID
, ADDRITEM_ID(email
)) < 0)
1164 if (claws_fputs(" />\n", fp
) == EOF
)
1166 node
= g_list_next(node
);
1168 if (addrbook_write_elem_e(fp
, 2, AB_ELTAG_MEMBER_LIST
) < 0)
1170 if (addrbook_write_elem_e(fp
, 1, AB_ELTAG_GROUP
) < 0)
1177 * Write folder and associated references to addresses to file.
1178 * file hash table visitor function.
1179 * \param key Table key.
1180 * \param value Reference to folder.
1181 * \param data File pointer.
1183 static void addrbook_write_item_folder_vis(gpointer key
, gpointer value
,
1186 AddrItemObject
*obj
= (AddrItemObject
*) value
;
1187 HashLoopData
*data
= (HashLoopData
*)d
;
1188 FILE *fp
= data
->fp
;
1193 if (ADDRITEM_TYPE(obj
) == ITEMTYPE_FOLDER
) {
1194 ItemFolder
*folder
= (ItemFolder
*) value
;
1196 if (addrbook_write_elem_s(fp
, 1, AB_ELTAG_FOLDER
) < 0)
1198 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(folder
)) < 0)
1200 if (addrbook_write_attr(fp
, AB_ATTAG_NAME
, ADDRITEM_NAME(folder
)) < 0)
1202 if (addrbook_write_attr(fp
, AB_ATTAG_REMARKS
, folder
->remarks
) < 0)
1204 if (claws_fputs(" >\n", fp
) == EOF
)
1206 if (addrbook_write_elem_s(fp
, 2, AB_ELTAG_ITEM_LIST
) < 0)
1208 if (claws_fputs(">\n", fp
) == EOF
)
1211 /* Output persons */
1212 node
= folder
->listPerson
;
1214 ItemPerson
*item
= node
->data
;
1215 if (addrbook_write_elem_s(fp
, 3, AB_ELTAG_ITEM
) < 0)
1217 if (addrbook_write_attr(fp
, AB_ATTAG_TYPE
, AB_ATTAG_VAL_PERSON
) < 0)
1219 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(item
)) < 0)
1221 if (claws_fputs(" />\n", fp
) == EOF
)
1223 node
= g_list_next(node
);
1227 node
= folder
->listGroup
;
1229 ItemGroup
*item
= node
->data
;
1230 if (addrbook_write_elem_s(fp
, 3, AB_ELTAG_ITEM
) < 0)
1232 if (addrbook_write_attr(fp
, AB_ATTAG_TYPE
, AB_ATTAG_VAL_GROUP
) < 0)
1234 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(item
)) < 0)
1236 if (claws_fputs(" />\n", fp
) == EOF
)
1238 node
= g_list_next(node
);
1241 /* Output folders */
1242 node
= folder
->listFolder
;
1244 ItemFolder
*item
= node
->data
;
1245 if (addrbook_write_elem_s(fp
, 3, AB_ELTAG_ITEM
) < 0)
1247 if (addrbook_write_attr(fp
, AB_ATTAG_TYPE
, AB_ATTAG_VAL_FOLDER
) < 0)
1249 if (addrbook_write_attr(fp
, AB_ATTAG_UID
, ADDRITEM_ID(item
)) < 0)
1251 if (claws_fputs(" />\n", fp
) == EOF
)
1253 node
= g_list_next(node
);
1255 if (addrbook_write_elem_e(fp
, 2, AB_ELTAG_ITEM_LIST
) < 0)
1257 if (addrbook_write_elem_e(fp
, 1, AB_ELTAG_FOLDER
) < 0)
1264 * Output address book data to specified file.
1265 * \param book Address book.
1266 * \param newFile Filename of new file (in book's filepath).
1267 * \return Status code.
1269 static gint
addrbook_write_to(AddressBookFile
*book
, gchar
*newFile
)
1274 #ifndef DEV_STANDALONE
1278 cm_return_val_if_fail(book
!= NULL
, -1);
1279 cm_return_val_if_fail(newFile
!= NULL
, -1);
1281 fileSpec
= g_strconcat(book
->path
, G_DIR_SEPARATOR_S
, newFile
, NULL
);
1283 book
->retVal
= MGU_OPEN_FILE
;
1284 #ifdef DEV_STANDALONE
1285 fp
= claws_fopen(fileSpec
, "wb");
1288 if (claws_fputs("<?xml version=\"1.0\" ?>\n", fp
) == EOF
) {
1289 book
->retVal
= MGU_ERROR_WRITE
;
1290 return book
->retVal
;
1293 pfile
= prefs_write_open(fileSpec
);
1297 if (fprintf( fp
, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL
) < 0)
1300 if (addrbook_write_elem_s(fp
, 0, AB_ELTAG_ADDRESS_BOOK
) < 0)
1302 if (addrbook_write_attr(fp
, AB_ATTAG_NAME
,
1303 addrcache_get_name(book
->addressCache
)) < 0)
1305 if (claws_fputs(" >\n", fp
) == EOF
)
1308 /* Output all persons */
1312 g_hash_table_foreach(book
->addressCache
->itemHash
,
1313 addrbook_write_item_person_vis
, &data
);
1317 /* Output all groups */
1318 g_hash_table_foreach(book
->addressCache
->itemHash
,
1319 addrbook_write_item_group_vis
, &data
);
1324 /* Output all folders */
1325 g_hash_table_foreach(book
->addressCache
->itemHash
,
1326 addrbook_write_item_folder_vis
, &data
);
1331 if (addrbook_write_elem_e(fp
, 0, AB_ELTAG_ADDRESS_BOOK
) < 0)
1334 book
->retVal
= MGU_SUCCESS
;
1335 #ifdef DEV_STANDALONE
1336 claws_safe_fclose(fp
);
1338 if (prefs_file_close( pfile
) < 0)
1339 book
->retVal
= MGU_ERROR_WRITE
;
1344 return book
->retVal
;
1346 g_warning("error writing AB");
1347 book
->retVal
= MGU_ERROR_WRITE
;
1349 prefs_file_close_revert( pfile
);
1350 return book
->retVal
;
1354 * Output address book data to original file.
1355 * \param book Address book.
1356 * \return Status code.
1358 gint
addrbook_save_data(AddressBookFile
*book
)
1360 cm_return_val_if_fail(book
!= NULL
, -1);
1362 book
->retVal
= MGU_NO_FILE
;
1363 if (book
->fileName
== NULL
|| *book
->fileName
== '\0')
1364 return book
->retVal
;
1365 if (book
->path
== NULL
|| *book
->path
== '\0')
1366 return book
->retVal
;
1368 addrbook_write_to(book
, book
->fileName
);
1369 if (book
->retVal
== MGU_SUCCESS
)
1370 addrcache_set_dirty(book
->addressCache
, FALSE
);
1371 return book
->retVal
;
1375 * **********************************************************************
1376 * Address book edit interface functions.
1377 * **********************************************************************
1381 * Hash table callback function for simple deletion of hashtable entries.
1382 * \param key Table key (will be freed).
1383 * \param value Value stored in table.
1384 * \param data User data.
1385 * \return <i>TRUE</i> to indicate that entry freed.
1387 static gboolean
addrbook_free_simple_hash_vis(gpointer
*key
, gpointer
*value
,
1397 * Update address book email list for specified person. Note: The existing
1398 * email addresses are replaced with the new addresses. Any references to
1399 * old addresses in the groups are re-linked to the new addresses. All old
1400 * addresses linked to the person are removed.
1401 * \param book Address book.
1402 * \param person Person to update.
1403 * \param listEMail List of new email addresses.
1405 void addrbook_update_address_list(AddressBookFile
*book
, ItemPerson
*person
,
1412 cm_return_if_fail(book
!= NULL
);
1413 cm_return_if_fail(person
!= NULL
);
1415 /* Get groups where person's existing email addresses are listed */
1416 listGroup
= addrcache_get_group_for_person(book
->addressCache
, person
);
1418 GHashTable
*hashEMail
;
1419 GHashTable
*hashEMailAlias
;
1422 /* Load hash table with new address entries */
1423 hashEMail
= g_hash_table_new(g_str_hash
, g_str_equal
);
1424 hashEMailAlias
= g_hash_table_new(g_str_hash
, g_str_equal
);
1427 ItemEMail
*email
= node
->data
;
1428 gchar
*alias
= email
->obj
.name
;
1429 gchar
*addr
= g_utf8_strdown(email
->address
, -1);
1430 if (!g_hash_table_lookup(hashEMail
, addr
)) {
1431 g_hash_table_insert(hashEMail
, addr
, email
);
1433 if (*alias
!= '\0' && ! g_hash_table_lookup(hashEMailAlias
,
1435 g_hash_table_insert(hashEMailAlias
, alias
, email
);
1437 node
= g_list_next(node
);
1440 /* Re-parent new addresses to existing groups, where email address match. */
1441 nodeGrp
= listGroup
;
1443 ItemGroup
*group
= (ItemGroup
*) nodeGrp
->data
;
1444 GList
*groupEMail
= group
->listEMail
;
1446 GList
*listRemove
= NULL
;
1448 /* Process each email item linked to group */
1449 nodeGrpEM
= groupEMail
;
1451 ItemEMail
*emailGrp
= (ItemEMail
*) nodeGrpEM
->data
;
1453 if (ADDRITEM_PARENT(emailGrp
) == ADDRITEM_OBJECT(person
)) {
1454 /* Found an email address for this person */
1455 ItemEMail
*emailNew
= NULL
;
1456 gchar
*alias
= emailGrp
->obj
.name
;
1457 gchar
*addr
= g_utf8_strdown(emailGrp
->address
, -1);
1458 emailNew
= (ItemEMail
*)
1459 g_hash_table_lookup(hashEMail
, addr
);
1461 /* If no match by e-mail, try to match by e-mail alias */
1462 if (!emailNew
&& *alias
!= '\0') {
1463 emailNew
= (ItemEMail
*)
1464 g_hash_table_lookup(hashEMailAlias
, alias
);
1468 /* Point to this entry */
1469 nodeGrpEM
->data
= emailNew
;
1470 else if (g_hash_table_size(hashEMail
)==1)
1471 /* If the person has just one e-mail address, then
1472 change e-mail address in group list */
1473 nodeGrpEM
->data
= listEMail
->data
;
1475 /* Mark for removal */
1476 listRemove
= g_list_append(listRemove
, emailGrp
);
1478 /* Move on to next email link */
1479 nodeGrpEM
= g_list_next(nodeGrpEM
);
1482 /* Process all removed links in current group */
1483 nodeGrpEM
= listRemove
;
1485 ItemEMail
*emailGrp
= nodeGrpEM
->data
;
1486 groupEMail
= g_list_remove(groupEMail
, emailGrp
);
1487 nodeGrpEM
= g_list_next(nodeGrpEM
);
1490 g_list_free(listRemove
);
1492 /* Move on to next group */
1493 nodeGrp
= g_list_next(nodeGrp
);
1496 /* Clear hash table */
1497 g_hash_table_foreach_remove(hashEMail
, (GHRFunc
)
1498 addrbook_free_simple_hash_vis
, NULL
);
1499 g_hash_table_destroy(hashEMail
);
1501 g_hash_table_destroy(hashEMailAlias
);
1502 hashEMailAlias
= NULL
;
1503 g_list_free(listGroup
);
1506 /* Remove old addresses from person and cache */
1508 node
= person
->listEMail
;
1510 ItemEMail
*email
= node
->data
;
1512 if (addrcache_person_remove_email(book
->addressCache
, person
, email
))
1513 addrcache_remove_email(book
->addressCache
, email
);
1515 listDelete
= g_list_append(listDelete
, email
);
1516 node
= person
->listEMail
;
1518 /* Add new address entries */
1521 ItemEMail
*email
= node
->data
;
1523 if (ADDRITEM_ID(email
) == NULL
)
1524 /* Allocate an ID for new address */
1525 addrcache_id_email(book
->addressCache
, email
);
1527 addrcache_person_add_email( book
->addressCache
, person
, email
);
1528 node
= g_list_next( node
);
1531 addrcache_set_dirty(book
->addressCache
, TRUE
);
1533 /* Free up memory */
1534 g_list_free(listEMail
);
1539 ItemEMail
*email
= node
->data
;
1541 addritem_free_item_email(email
);
1542 node
= g_list_next(node
);
1544 g_list_free(listDelete
);
1550 * Create person object and add person with specified address data to address
1551 * book. Note: A new person is created with specified list of email addresses.
1552 * All objects inserted into address book.
1554 * \param book Address book.
1555 * \param folder Parent folder where to add person, or <i>NULL</i> for
1557 * \param listEMail List of new email addresses to associate with person.
1558 * \return Person object created.
1560 ItemPerson
*addrbook_add_address_list(AddressBookFile
*book
, ItemFolder
*folder
,
1564 ItemFolder
*f
= folder
;
1567 cm_return_val_if_fail(book
!= NULL
, NULL
);
1570 f
= book
->addressCache
->rootFolder
;
1571 person
= addritem_create_item_person();
1572 addrcache_id_person(book
->addressCache
, person
);
1573 addrcache_folder_add_person(book
->addressCache
, f
, person
);
1577 ItemEMail
*email
= node
->data
;
1578 if (ADDRITEM_ID(email
) == NULL
)
1579 addrcache_id_email(book
->addressCache
, email
);
1581 addrcache_person_add_email(book
->addressCache
, person
, email
);
1582 node
= g_list_next(node
);
1588 * Build available email list visitor function.
1589 * \param key Table key.
1590 * \param value Value stored in table.
1591 * \param data Reference to address book.
1593 static void addrbook_build_avail_email_vis(gpointer key
, gpointer value
,
1596 AddrItemObject
*obj
= (AddrItemObject
*) value
;
1598 if (ADDRITEM_TYPE(obj
) == ITEMTYPE_PERSON
) {
1599 AddressBookFile
*book
= data
;
1600 ItemPerson
*person
= (ItemPerson
*) obj
;
1601 GList
*node
= person
->listEMail
;
1603 ItemEMail
*email
= node
->data
;
1604 /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */
1606 if (!g_hash_table_lookup(book
->tempHash
,
1607 ADDRITEM_ID(email
)))
1608 book
->tempList
= g_list_append(book
->tempList
, email
);
1610 node
= g_list_next(node
);
1616 * Return link list of available email items that have not already been linked
1617 * to groups. Note that the list contains references to items and should be
1618 * <code>g_list_free()</code> when done. Do <b>*NOT*</b> attempt to used the
1619 * <code>addrbook_free_xxx()<code> functions... this will destroy the
1622 * \param book Address book.
1623 * \param group Group to process.
1624 * \return List of items, or <i>NULL</i> if none.
1626 GList
*addrbook_get_available_email_list(AddressBookFile
*book
, ItemGroup
*group
)
1631 cm_return_val_if_fail(book
!= NULL
, NULL
);
1633 /* Load hash table with group email entries */
1634 table
= g_hash_table_new(g_str_hash
, g_str_equal
);
1636 list
= group
->listEMail
;
1638 ItemEMail
*email
= list
->data
;
1639 g_hash_table_insert(table
, ADDRITEM_ID(email
), email
);
1640 list
= g_list_next(list
);
1644 /* Build list of available email addresses which exclude those already in groups */
1645 book
->tempList
= NULL
;
1646 book
->tempHash
= table
;
1647 g_hash_table_foreach(book
->addressCache
->itemHash
,
1648 addrbook_build_avail_email_vis
, book
);
1649 list
= book
->tempList
;
1650 book
->tempList
= NULL
;
1651 book
->tempHash
= NULL
;
1653 /* Clear hash table */
1654 g_hash_table_destroy(table
);
1661 * Update address book email list for specified group. Note: The existing email
1662 * addresses are replaced with the new addresses. Any references to old addresses
1663 * in the groups are re-linked to the new addresses. All old addresses linked to
1664 * the person are removed.
1666 * \param book Address book.
1667 * \param group Group to process.
1668 * \param listEMail List of email items. This should <b>*NOT*</b> be
1669 * <code>g_free()</code> when done.
1671 void addrbook_update_group_list(AddressBookFile
*book
, ItemGroup
*group
,
1676 cm_return_if_fail(book
!= NULL
);
1677 cm_return_if_fail(group
!= NULL
);
1679 addrcache_set_dirty(book
->addressCache
, TRUE
);
1681 /* Remember old list */
1682 oldData
= group
->listEMail
;
1683 group
->listEMail
= listEMail
;
1684 g_list_free(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
;
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 r
= g_stat(buf
, &statbuf
);
1852 if (r
== 0 && 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
)
1933 prev_level
= file
->level
;
1934 if (xml_parse_next_tag(file
))
1935 longjmp(book
->jumper
, 1);
1936 if (file
->level
< prev_level
)
1938 /* attr = xml_get_current_tag_attr(file); */
1939 /* addrbook_show_attribs( attr ); */
1940 if (xml_compare_tag(file
, AB_ELTAG_ADDRESS
))
1941 addrbook_chkparse_addr_list(book
, file
);
1946 * Attempt to parse attributes for person address from file.
1947 * \param book Address book.
1948 * \param file XML file handle.
1950 static void addrbook_chkparse_attribute(AddressBookFile
*book
, XMLFile
*file
)
1953 /* gchar *element; */
1955 /* attr = xml_get_current_tag_attr(file); */
1956 /* addrbook_show_attribs( attr ); */
1957 /* element = xml_get_element(file); */
1958 /* g_print( "\t\tattrib value : %s\n", element ); */
1962 * Attempt to parse list of attributes for person address from file.
1963 * \param book Address book.
1964 * \param file XML file handle.
1966 static void addrbook_chkparse_attr_list(AddressBookFile
*book
, XMLFile
*file
)
1971 prev_level
= file
->level
;
1972 if (xml_parse_next_tag(file
))
1973 longjmp(book
->jumper
, 1);
1974 if (file
->level
< prev_level
)
1976 if (xml_compare_tag(file
, AB_ELTAG_ATTRIBUTE
)) {
1977 addrbook_chkparse_attribute(book
, file
);
1978 addrbook_chkparse_attr_list(book
, file
);
1984 * Attempt to parse person from file.
1985 * \param book Address book.
1986 * \param file XML file handle.
1988 static void addrbook_chkparse_person(AddressBookFile
*book
, XMLFile
*file
)
1992 /* attr = xml_get_current_tag_attr(file); */
1993 /* addrbook_show_attribs( attr ); */
1994 if (xml_parse_next_tag(file
)) /* Consume closing tag */
1995 longjmp(book
->jumper
, 1);
1997 if (xml_compare_tag(file
, AB_ELTAG_ADDRESS_LIST
))
1998 addrbook_chkparse_addr_list(book
, file
);
2000 if (xml_parse_next_tag(file
)) /* Consume closing tag */
2001 longjmp(book
->jumper
, 1);
2003 if (xml_compare_tag(file
, AB_ELTAG_ATTRIBUTE_LIST
))
2004 addrbook_chkparse_attr_list(book
, file
);
2008 * Attempt to parse list of members from file.
2009 * \param book Address book.
2010 * \param file XML file handle.
2012 static void addrbook_chkparse_member_list(AddressBookFile
*book
, XMLFile
*file
)
2018 prev_level
= file
->level
;
2019 if (xml_parse_next_tag(file
))
2020 longjmp(book
->jumper
, 1);
2022 if (file
->level
< prev_level
)
2025 if (xml_compare_tag(file
, AB_ELTAG_MEMBER
)) {
2026 /* attr = xml_get_current_tag_attr(file); */
2027 /* addrbook_show_attribs( attr ); */
2028 addrbook_chkparse_member_list(book
, file
);
2031 /* attr = xml_get_current_tag_attr(file); */
2032 /* addrbook_show_attribs( attr ); */
2038 * Attempt to parse group from file.
2039 * \param book Address book.
2040 * \param file XML file handle.
2042 static void addrbook_chkparse_group(AddressBookFile
*book
, XMLFile
*file
)
2046 /* attr = xml_get_current_tag_attr(file); */
2047 /* addrbook_show_attribs( attr ); */
2048 if (xml_parse_next_tag(file
)) /* Consume closing tag */
2049 longjmp(book
->jumper
, 1);
2051 if (xml_compare_tag(file
, AB_ELTAG_MEMBER_LIST
))
2052 addrbook_chkparse_member_list(book
, file
);
2056 * Attempt to parse list of folders from file.
2057 * \param book Address book.
2058 * \param file XML file handle.
2060 static void addrbook_chkparse_folder_list(AddressBookFile
*book
, XMLFile
*file
)
2066 prev_level
= file
->level
;
2067 if (xml_parse_next_tag(file
))
2068 longjmp(book
->jumper
, 1);
2070 if (file
->level
< prev_level
)
2073 if (xml_compare_tag(file
, AB_ELTAG_ITEM
)) {
2074 /* attr = xml_get_current_tag_attr(file); */
2075 /* addrbook_show_attribs( attr ); */
2076 addrbook_chkparse_folder_list(book
, file
);
2079 /* attr = xml_get_current_tag_attr(file); */
2080 /* addrbook_show_attribs( attr ); */
2086 * Attempt to parse a folder from file.
2087 * \param book Address book.
2088 * \param file XML file handle.
2090 static void addrbook_chkparse_folder(AddressBookFile
*book
, XMLFile
*file
)
2094 /* attr = xml_get_current_tag_attr(file); */
2095 /* addrbook_show_attribs( attr ); */
2096 if (xml_parse_next_tag(file
)) /* Consume closing tag */
2097 longjmp(book
->jumper
, 1);
2099 if (xml_compare_tag(file
, AB_ELTAG_ITEM_LIST
))
2100 addrbook_chkparse_folder_list(book
, file
);
2104 * Attempt to parse (DOM) tree from file.
2105 * \param book Address book.
2106 * \param file XML file handle.
2108 static gboolean
addrbook_chkread_tree(AddressBookFile
*book
, XMLFile
*file
)
2113 if (xml_get_dtd(file
))
2116 if (xml_parse_next_tag(file
))
2119 if (!xml_compare_tag(file
, AB_ELTAG_ADDRESS_BOOK
))
2122 /* attr = xml_get_current_tag_attr(file); */
2123 /* addrbook_show_attribs( attr ); */
2130 if (xml_parse_next_tag(file
))
2131 longjmp(book
->jumper
, 1);
2133 /* Get next tag (person, group or folder) */
2134 if (xml_compare_tag(file
, AB_ELTAG_PERSON
))
2135 addrbook_chkparse_person( book
, file
);
2136 else if (xml_compare_tag(file
, AB_ELTAG_GROUP
))
2137 addrbook_chkparse_group(book
, file
);
2138 else if (xml_compare_tag(file
, AB_ELTAG_FOLDER
))
2139 addrbook_chkparse_folder(book
, file
);
2145 * Test address book file by parsing contents.
2146 * \param book Address book.
2147 * \param fileName Filename of XML file.
2148 * \return Status code <i>MGU_SUCCESS</i> if file appears to be valid format.
2150 gint
addrbook_test_read_file(AddressBookFile
*book
, gchar
*fileName
)
2152 XMLFile
*file
= NULL
;
2153 gchar
*fileSpec
= NULL
;
2155 cm_return_val_if_fail(book
!= NULL
, -1);
2157 fileSpec
= g_strconcat(book
->path
, G_DIR_SEPARATOR_S
, fileName
, NULL
);
2158 book
->retVal
= MGU_OPEN_FILE
;
2159 file
= xml_open_file(fileSpec
);
2162 book
->retVal
= MGU_BAD_FORMAT
;
2163 if (setjmp(book
->jumper
)) {
2164 /* g_print( "Caught Ya!!!\n" ); */
2165 xml_close_file(file
);
2166 return book
->retVal
;
2168 if (addrbook_chkread_tree(book
, file
))
2169 book
->retVal
= MGU_SUCCESS
;
2171 xml_close_file( file
);
2173 return book
->retVal
;
2177 * Return link list of all persons in address book. Note that the list
2178 * contains references to items. Do <b>*NOT*</b> attempt to use the
2179 * <code>addrbook_free_xxx()</code> functions... this will destroy the
2181 * \param book Address book.
2182 * \return List of persons, or NULL if none.
2184 GList
*addrbook_get_all_persons(AddressBookFile
*book
)
2186 cm_return_val_if_fail(book
!= NULL
, NULL
);
2187 return addrcache_get_all_persons(book
->addressCache
);
2190 GList
*addrbook_get_all_groups(AddressBookFile
*book
)
2192 cm_return_val_if_fail(book
!= NULL
, NULL
);
2193 return addrcache_get_all_groups(book
->addressCache
);
2197 * Add person and address data to address book.
2198 * \param book Address book.
2199 * \param folder Folder where to add person, or NULL for root folder.
2200 * \param name Common name.
2201 * \param address EMail address.
2202 * \param remarks Remarks.
2203 * \return Person added. Do not <b>*NOT*</b> to use the
2204 * <code>addrbook_free_xxx()</code> functions... this will destroy
2205 * the address book data.
2207 ItemPerson
*addrbook_add_contact(AddressBookFile
*book
, ItemFolder
*folder
,
2208 const gchar
*name
,const gchar
*address
,
2209 const gchar
*remarks
)
2213 cm_return_val_if_fail(book
!= NULL
, NULL
);
2214 person
= addrcache_add_contact(
2215 book
->addressCache
, folder
, name
, address
, remarks
);
2220 * Return file name for next address book file.
2221 * \param book Address book.
2222 * \return File name, or <i>NULL</i> if could not create. This should be
2223 * <code>g_free()</code> when done.
2225 gchar
*addrbook_guess_next_file(AddressBookFile
*book
)
2227 gchar
*newFile
= NULL
;
2228 GList
*fileList
= NULL
;
2230 fileList
= addrbook_get_bookfile_list(book
);
2232 fileNum
= 1 + book
->maxValue
;
2234 newFile
= addrbook_gen_new_file_name(fileNum
);
2235 g_list_free(fileList
);
2240 void addrbook_delete_book_file(AddressBookFile
*book
)
2244 if (!book
->path
|| !book
->fileName
)
2247 book_path
= g_strconcat(book
->path
, G_DIR_SEPARATOR_S
,
2248 book
->fileName
, NULL
);
2249 claws_unlink(book_path
);