add user specified stylesheet option
[claws.git] / src / addrbook.c
blob04e6c8c6c101aac81308ca33d201e8b7d74ecff1
1 /*
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 */
22 #include <glib.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <math.h>
27 #include <setjmp.h>
29 #include "utils.h"
30 #include "xml.h"
31 #include "mgutils.h"
32 #include "addritem.h"
33 #include "addrcache.h"
34 #include "addrbook.h"
35 #include "adbookbase.h"
37 #ifndef DEV_STANDALONE
38 #include "prefs_gtk.h"
39 #include "codeconv.h"
40 #endif
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 );
51 /**
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;
63 book->path = NULL;
64 book->fileName = NULL;
65 book->maxValue = 0;
66 book->tempList = NULL;
67 book->tempHash = NULL;
68 book->addressCache->modified = TRUE;
70 return book;
73 /**
74 * Specify name to be used
75 * \param book Address book.
76 * \param value Name.
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);
90 /**
91 * Specify path to address book file.
92 * \param book Address book.
93 * \param value Path.
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);
146 return book->retVal;
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);
185 * Free address book.
186 * \param book Address book.
188 void addrbook_free_book(AddressBookFile *book)
190 cm_return_if_fail(book != NULL);
192 /* Clear cache */
193 addrcache_free(book->addressCache);
195 /* Free up internal objects */
196 g_free(book->path);
197 g_free(book->fileName);
198 g_list_free(book->tempList);
200 book->path = NULL;
201 book->fileName = NULL;
202 book->maxValue = 0;
203 book->tempList = NULL;
204 book->tempHash = NULL;
206 book->type = ADBOOKTYPE_NONE;
207 book->addressCache = NULL;
208 book->retVal = MGU_SUCCESS;
210 g_free(book);
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)
236 ItemFolder *folder;
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
247 * be freed.
248 * Specify name to be used
249 * \param book Address book.
250 * \param group Group to remove.
251 * \param value Name.
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
262 * be freed.
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 * ===========================
292 * Notes:
293 * 1) The address book is structured as follows:
295 * address-book
296 * person
297 * address-list
298 * address
299 * attribute-list
300 * attribute
301 * group
302 * member-list
303 * member
304 * folder
305 * item-list
306 * item
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
316 * loading.
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,
361 ItemPerson *person)
363 GList *attr;
364 gchar *name, *value;
365 ItemEMail *email = NULL;
367 attr = xml_get_current_tag_attr(file);
368 while (attr) {
369 name = ((XMLAttr *)attr->data)->name;
370 value = ((XMLAttr *)attr->data)->value;
371 if (!email)
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);
383 if (email) {
384 if (person) {
385 addrcache_person_add_email(book->addressCache, person,
386 email);
388 else {
389 addritem_free_item_email(email);
390 email = NULL;
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,
402 ItemPerson *person)
404 guint prev_level;
406 for (;;) {
407 prev_level = file->level;
408 if (xml_parse_next_tag(file)) {
409 longjmp(book->jumper, 1);
411 if (file->level < prev_level) return;
412 if (xml_compare_tag(file, AB_ELTAG_ADDRESS)) {
413 addrbook_parse_address(book, file, person);
414 addrbook_parse_addr_list(book, file, person);
420 * Parse attribute for person from XML file.
421 * \param book Address book.
422 * \param file XML file handle.
423 * \param person Person.
425 static void addrbook_parse_attribute(XMLFile *file, ItemPerson *person)
427 GList *attr;
428 gchar *name, *value;
429 gchar *element;
430 UserAttribute *uAttr = NULL;
432 attr = xml_get_current_tag_attr(file);
433 while (attr) {
434 name = ((XMLAttr *)attr->data)->name;
435 value = ((XMLAttr *)attr->data)->value;
436 if (!uAttr) uAttr = addritem_create_attribute();
437 if (strcmp(name, AB_ATTAG_UID) == 0)
438 addritem_attrib_set_id(uAttr, value);
439 else if (strcmp(name, AB_ATTAG_NAME) == 0)
440 addritem_attrib_set_name(uAttr, value);
441 attr = g_list_next(attr);
444 element = xml_get_element(file);
445 addritem_attrib_set_value(uAttr, element);
446 g_free(element);
448 if (uAttr) {
449 if (person) {
450 addritem_person_add_attribute(person, uAttr);
452 else {
453 addritem_free_attribute(uAttr);
454 uAttr = NULL;
460 * Parse list of attributes for person from XML file.
461 * \param book Address book.
462 * \param file XML file handle.
463 * \param person Person.
465 static void addrbook_parse_attr_list(AddressBookFile *book, XMLFile *file,
466 ItemPerson *person)
468 guint prev_level;
470 for (;;) {
471 prev_level = file->level;
472 if (xml_parse_next_tag(file)) {
473 longjmp( book->jumper, 1 );
475 if (file->level < prev_level) return;
476 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
477 addrbook_parse_attribute(file, person);
478 addrbook_parse_attr_list(book, file, person);
484 * Parse person from XML file.
485 * \param book Address book.
486 * \param file XML file handle.
488 static void addrbook_parse_person(AddressBookFile *book, XMLFile *file)
490 GList *attr;
491 gchar *name, *value;
492 ItemPerson *person = NULL;
494 attr = xml_get_current_tag_attr(file);
495 while (attr) {
496 name = ((XMLAttr *)attr->data)->name;
497 value = ((XMLAttr *)attr->data)->value;
498 if (!person)
499 person = addritem_create_item_person();
500 if (strcmp(name, AB_ATTAG_UID) == 0) {
501 ADDRITEM_ID(person) = g_strdup(value);
502 person->picture = g_strdup(value);
504 else if (strcmp(name, AB_ATTAG_FIRST_NAME) == 0)
505 person->firstName = g_strdup(value);
506 else if (strcmp(name, AB_ATTAG_LAST_NAME) == 0)
507 person->lastName = g_strdup(value);
508 else if (strcmp(name, AB_ATTAG_NICK_NAME) == 0)
509 person->nickName = g_strdup(value);
510 else if (strcmp(name, AB_ATTAG_COMMON_NAME) == 0)
511 ADDRITEM_NAME(person) = g_strdup(value);
512 attr = g_list_next(attr);
514 if (xml_parse_next_tag(file)) { /* Consume closing tag */
515 longjmp(book->jumper, 1);
517 if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST)) {
518 addrbook_parse_addr_list(book, file, person);
519 if (person) {
520 addrcache_hash_add_person(book->addressCache, person);
523 if (xml_parse_next_tag(file)) { /* Consume closing tag */
524 longjmp(book->jumper, 1);
526 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST)) {
527 addrbook_parse_attr_list(book, file, person);
532 * Parse group member from XML file.
533 * \param book Address book.
534 * \param file XML file handle.
535 * \param group Group.
537 static void addrbook_parse_member(AddressBookFile *book, XMLFile *file,
538 ItemGroup *group)
540 GList *attr;
541 gchar *name, *value;
542 gchar *eid = NULL;
543 /* gchar *pid = NULL; */
544 ItemEMail *email = NULL;
546 attr = xml_get_current_tag_attr(file);
547 while (attr) {
548 name = ((XMLAttr *)attr->data)->name;
549 value = ((XMLAttr *)attr->data)->value;
550 if( strcmp( name, AB_ATTAG_EID ) == 0 )
551 eid = g_strdup( value );
552 attr = g_list_next(attr);
554 /* email = addrcache_get_email( book->addressCache, pid, eid ); */
555 email = addrcache_get_email(book->addressCache, eid);
556 g_free(eid);
557 if (email) {
558 if (group) {
559 addrcache_group_add_email(book->addressCache, group,
560 email);
562 else {
563 addritem_free_item_email(email);
564 email = NULL;
570 * Parse list of group members from XML file.
571 * \param book Address book.
572 * \param file XML file handle.
573 * \param group Group.
575 static void addrbook_parse_member_list(AddressBookFile *book, XMLFile *file,
576 ItemGroup *group)
578 guint prev_level;
580 for (;;) {
581 prev_level = file->level;
582 if (xml_parse_next_tag(file)) {
583 longjmp(book->jumper, 1);
585 if (file->level < prev_level)
586 return;
587 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
588 addrbook_parse_member(book, file, group);
589 addrbook_parse_member_list(book, file, group);
595 * Parse group object from XML file.
596 * \param book Address book.
597 * \param file XML file handle.
599 static void addrbook_parse_group(AddressBookFile *book, XMLFile *file)
601 GList *attr;
602 gchar *name, *value;
603 ItemGroup *group = NULL;
605 attr = xml_get_current_tag_attr(file);
606 while (attr) {
607 name = ((XMLAttr *)attr->data)->name;
608 value = ((XMLAttr *)attr->data)->value;
609 if (!group)
610 group = addritem_create_item_group();
611 if (strcmp(name, AB_ATTAG_UID) == 0)
612 ADDRITEM_ID(group) = g_strdup(value);
613 else if (strcmp(name, AB_ATTAG_NAME) == 0)
614 ADDRITEM_NAME(group) = g_strdup(value);
615 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
616 group->remarks = g_strdup(value);
617 attr = g_list_next(attr);
619 if (xml_parse_next_tag(file)) { /* Consume closing tag */
620 longjmp(book->jumper, 1);
622 if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST)) {
623 if (group) {
624 addrcache_hash_add_group(book->addressCache, group);
626 addrbook_parse_member_list(book, file, group);
631 * Parse folder item from XML file.
632 * \param book Address book.
633 * \param file XML file handle.
634 * \param folder Folder.
636 static void addrbook_parse_folder_item(AddressBookFile *book, XMLFile *file,
637 ItemFolder *folder)
639 GList *attr;
640 gchar *name, *value;
641 gchar *uid = NULL;
643 attr = xml_get_current_tag_attr(file);
644 while (attr) {
645 name = ((XMLAttr *)attr->data)->name;
646 value = ((XMLAttr *)attr->data)->value;
647 if (strcmp(name, AB_ATTAG_UID) == 0) {
648 uid = g_strdup(value);
650 attr = g_list_next(attr);
652 if (folder) {
653 if (uid) {
654 folder->listItems = g_list_append(folder->listItems, uid);
660 * Parse list of folder items from XML file.
661 * \param book Address book.
662 * \param file XML file handle.
663 * \param folder Folder.
665 static void addrbook_parse_folder_list(AddressBookFile *book, XMLFile *file,
666 ItemFolder *folder)
668 guint prev_level;
670 for (;;) {
671 prev_level = file->level;
672 if (xml_parse_next_tag(file)) {
673 longjmp(book->jumper, 1);
675 if (file->level < prev_level)
676 return;
677 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
678 addrbook_parse_folder_item(book, file, folder);
679 addrbook_parse_folder_list(book, file, folder);
685 * Parse folder from XML file.
686 * \param book Address book.
687 * \param file XML file handle.
689 static void addrbook_parse_folder(AddressBookFile *book, XMLFile *file)
691 GList *attr;
692 gchar *name, *value;
693 ItemFolder *folder = NULL;
695 attr = xml_get_current_tag_attr(file);
696 while (attr) {
697 name = ((XMLAttr *)attr->data)->name;
698 value = ((XMLAttr *)attr->data)->value;
699 if (!folder)
700 folder = addritem_create_item_folder();
701 if (strcmp(name, AB_ATTAG_UID) == 0)
702 ADDRITEM_ID(folder) = g_strdup(value);
703 else if (strcmp(name, AB_ATTAG_NAME) == 0)
704 ADDRITEM_NAME(folder) = g_strdup(value);
705 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
706 folder->remarks = g_strdup(value);
707 attr = g_list_next(attr);
709 if (xml_parse_next_tag(file)) { /* Consume closing tag */
710 longjmp(book->jumper, 1);
712 if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST)) {
713 if (folder) {
714 if (addrcache_hash_add_folder(book->addressCache,
715 folder)) {
716 book->tempList = g_list_append(book->tempList,
717 folder);
718 /* We will resolve folder later */
719 ADDRITEM_PARENT(folder) = NULL;
722 addrbook_parse_folder_list(book, file, folder);
727 * Read address book (DOM) tree from file.
728 * \param book Address book.
729 * \param file XML file handle.
730 * \return <i>TRUE</i> if data read successfully, <i>FALSE</i> if error
731 * reading data.
733 static gboolean addrbook_read_tree(AddressBookFile *book, XMLFile *file)
735 gboolean retVal;
736 GList *attr;
737 gchar *name, *value;
739 book->retVal = MGU_BAD_FORMAT;
740 if (xml_get_dtd(file))
741 return FALSE;
742 if (xml_parse_next_tag(file))
743 longjmp(book->jumper, 1);
744 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
745 return FALSE;
747 attr = xml_get_current_tag_attr(file);
748 while (attr) {
749 name = ((XMLAttr *)attr->data)->name;
750 value = ((XMLAttr *)attr->data)->value;
751 if (strcmp( name, AB_ATTAG_NAME) == 0)
752 addrbook_set_name( book, value );
753 attr = g_list_next( attr );
756 retVal = TRUE;
757 for (;;) {
758 if (!file->level)
759 break;
760 /* Get next item tag (person, group or folder) */
761 if (xml_parse_next_tag(file))
762 longjmp( book->jumper, 1 );
764 if (xml_compare_tag(file, AB_ELTAG_PERSON))
765 addrbook_parse_person(book, file);
766 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
767 addrbook_parse_group(book, file);
768 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
769 addrbook_parse_folder(book, file);
771 if (retVal) book->retVal = MGU_SUCCESS;
772 return retVal;
776 * Resolve folder items callback function.
777 * \param key Table key.
778 * \param value Reference to object contained in folder.
779 * \param data Reference to address book.
781 static void addrbook_res_items_vis(gpointer key, gpointer value, gpointer data)
783 AddressBookFile *book = data;
784 AddrItemObject *obj = (AddrItemObject *) value;
785 ItemFolder *rootFolder = book->addressCache->rootFolder;
786 if (obj->parent == NULL) {
787 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
788 rootFolder->listPerson = g_list_append(rootFolder->listPerson,
789 obj);
790 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
792 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
793 rootFolder->listGroup = g_list_append(rootFolder->listGroup,
794 obj);
795 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
801 * Resolve folder items. Lists of UID's are replaced with pointers to
802 * data items.
803 * \param book Address book.
805 static void addrbook_resolve_folder_items(AddressBookFile *book)
807 GList *nodeFolder = NULL;
808 GList *listRemove = NULL;
809 GList *node = NULL;
810 ItemFolder *rootFolder = book->addressCache->rootFolder;
811 nodeFolder = book->tempList;
813 while (nodeFolder) {
814 ItemFolder *folder = nodeFolder->data;
815 listRemove = NULL;
816 node = folder->listItems;
817 while (node) {
818 gchar *uid = node->data;
819 AddrItemObject *aio = addrcache_get_object(book->addressCache,
820 uid);
821 if (aio) {
822 if (aio->type == ITEMTYPE_FOLDER) {
823 ItemFolder *item = (ItemFolder *) aio;
824 folder->listFolder = g_list_append(folder->listFolder, item);
825 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
826 addrcache_hash_add_folder(book->addressCache, folder);
828 else if (aio->type == ITEMTYPE_PERSON) {
829 ItemPerson *item = (ItemPerson *) aio;
830 folder->listPerson = g_list_append(folder->listPerson, item);
831 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
833 else if (aio->type == ITEMTYPE_GROUP) {
834 ItemGroup *item = (ItemGroup *) aio;
835 folder->listGroup = g_list_append(folder->listGroup, item);
836 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
838 /* Replace data with pointer to item */
839 g_free(uid);
840 node->data = aio;
842 else { /* Not found, append to remove list. */
843 listRemove = g_list_append(listRemove, uid);
845 node = g_list_next(node);
847 rootFolder->listFolder = g_list_append(rootFolder->listFolder,
848 folder);
849 /* Process remove list */
850 node = listRemove;
851 while (node) {
852 gchar *uid = node->data;
853 folder->listItems = g_list_remove(folder->listItems,
854 uid);
855 g_free(uid);
856 node = g_list_next(node);
858 g_list_free(listRemove);
859 nodeFolder = g_list_next(nodeFolder);
861 /* Remove folders with parents. */
862 listRemove = NULL;
863 node = rootFolder->listFolder;
864 while (node) {
865 ItemFolder *folder = (ItemFolder *) node->data;
866 if (ADDRITEM_PARENT(folder))
867 /* Remove folders with parents */
868 listRemove = g_list_append(listRemove, folder);
869 else /* Add to root folder */
870 ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder);
872 node = g_list_next( node );
874 /* Process remove list */
875 node = listRemove;
876 while (node) {
877 rootFolder->listFolder = g_list_remove(rootFolder->listFolder,
878 node->data);
879 node = g_list_next(node);
881 g_list_free(listRemove);
883 /* Move all unparented persons and groups into root folder */
884 g_hash_table_foreach(book->addressCache->itemHash,
885 addrbook_res_items_vis, book);
887 /* Free up some more */
888 nodeFolder = book->tempList;
889 while (nodeFolder) {
890 ItemFolder *folder = nodeFolder->data;
891 g_list_free(folder->listItems);
892 folder->listItems = NULL;
893 nodeFolder = g_list_next(nodeFolder);
895 g_list_free(book->tempList);
896 book->tempList = NULL;
900 * Read address book.
901 * \param book Address book.
902 * \return Status code.
904 gint addrbook_read_data(AddressBookFile *book)
906 XMLFile *file = NULL;
907 gchar *fileSpec = NULL;
909 cm_return_val_if_fail(book != NULL, -1);
912 g_print( "...addrbook_read_data :%s:\t:%s:\n", book->fileName,
913 addrcache_get_name( book->addressCache ) );
916 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S,
917 book->fileName, NULL);
918 book->retVal = MGU_OPEN_FILE;
919 addrcache_clear(book->addressCache);
920 book->addressCache->modified = FALSE;
921 book->addressCache->accessFlag = FALSE;
922 file = xml_open_file(fileSpec);
923 g_free(fileSpec);
924 if (file) {
925 book->tempList = NULL;
926 /* Trap for parsing errors. */
927 if (setjmp( book->jumper)) {
928 xml_close_file(file);
929 return book->retVal;
931 addrbook_read_tree(book, file);
932 xml_close_file(file);
933 /* Resolve folder items */
934 addrbook_resolve_folder_items(book);
935 book->tempList = NULL;
936 book->addressCache->modified = FALSE;
937 book->addressCache->dataRead = TRUE;
938 addrcache_set_dirty(book->addressCache, FALSE);
940 return book->retVal;
944 * Write start element to file.
945 * \param fp File handle.
946 * \param lvl Indent level.
947 * \param name Element name.
949 static int addrbook_write_elem_s(FILE *fp, gint lvl, gchar *name)
951 gint i;
952 for (i = 0; i < lvl; i++)
953 if (fputs(" ", fp) == EOF)
954 return -1;
955 if (fputs("<", fp) == EOF)
956 return -1;
957 if (fputs(name, fp) == EOF)
958 return -1;
960 return 0;
964 * Write end element to file.
965 * \param fp File handle.
966 * \param lvl Indent level.
967 * \param name Element name.
969 static int addrbook_write_elem_e(FILE *fp, gint lvl, gchar *name)
971 gint i;
972 for(i = 0; i < lvl; i++)
973 if (fputs(" ", fp) == EOF)
974 return -1;
975 if (fputs("</", fp) == EOF)
976 return -1;
977 if (fputs(name, fp) == EOF)
978 return -1;
979 if (fputs(">\n", fp) == EOF)
980 return -1;
982 return 0;
986 * Write attribute name/value pair to file.
987 * \param fp File handle.
988 * \param name Attribute name.
989 * \param value Attribute value.
991 static int addrbook_write_attr(FILE *fp, gchar *name, gchar *value)
993 if (fputs(" ", fp) == EOF)
994 return -1;
995 if (fputs(name, fp) == EOF)
996 return -1;
997 if (fputs("=\"", fp) == EOF)
998 return -1;
999 if (xml_file_put_escape_str(fp, value) < 0)
1000 return -1;
1001 if (fputs("\"", fp) == EOF)
1002 return -1;
1004 return 0;
1007 typedef struct _HashLoopData {
1008 FILE *fp;
1009 gboolean error;
1010 } HashLoopData;
1013 * Write person and associated addresses and attributes to file.
1014 * file hash table visitor function.
1015 * \param key Table key.
1016 * \param value Reference to person.
1017 * \param data File pointer.
1019 static void addrbook_write_item_person_vis(gpointer key, gpointer value,
1020 gpointer d)
1022 AddrItemObject *obj = (AddrItemObject *) value;
1023 HashLoopData *data = (HashLoopData *)d;
1024 FILE *fp = data->fp;
1025 GList *node;
1027 if (!obj)
1028 return;
1029 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1030 ItemPerson *person = (ItemPerson *) value;
1031 if (person) {
1032 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_PERSON) < 0)
1033 data->error = TRUE;
1034 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(person)) < 0)
1035 data->error = TRUE;
1036 if (addrbook_write_attr(fp, AB_ATTAG_FIRST_NAME, person->firstName) < 0)
1037 data->error = TRUE;
1038 if (addrbook_write_attr(fp, AB_ATTAG_LAST_NAME, person->lastName) < 0)
1039 data->error = TRUE;
1040 if (addrbook_write_attr(fp, AB_ATTAG_NICK_NAME, person->nickName) < 0)
1041 data->error = TRUE;
1042 if (addrbook_write_attr(fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person)) < 0)
1043 data->error = TRUE;
1044 if (fputs(" >\n", fp) == EOF)
1045 data->error = TRUE;
1047 /* Output email addresses */
1048 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1049 data->error = TRUE;
1050 if (fputs(">\n", fp) == EOF)
1051 data->error = TRUE;
1052 node = person->listEMail;
1053 while (node) {
1054 ItemEMail *email = node->data;
1055 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ADDRESS) < 0)
1056 data->error = TRUE;
1057 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(email)) < 0)
1058 data->error = TRUE;
1059 if (addrbook_write_attr(fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email)) < 0)
1060 data->error = TRUE;
1061 if (addrbook_write_attr(fp, AB_ATTAG_EMAIL, email->address) < 0)
1062 data->error = TRUE;
1063 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, email->remarks) < 0)
1064 data->error = TRUE;
1065 if (fputs(" />\n", fp) == EOF)
1066 data->error = TRUE;
1067 node = g_list_next(node);
1069 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1070 data->error = TRUE;
1072 /* Output user attributes */
1073 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1074 data->error = TRUE;
1075 if (fputs(">\n", fp) == EOF)
1076 data->error = TRUE;
1077 node = person->listAttrib;
1078 while (node) {
1079 UserAttribute *attrib = node->data;
1080 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ATTRIBUTE) < 0)
1081 data->error = TRUE;
1082 if (addrbook_write_attr(fp, AB_ATTAG_UID, attrib->uid) < 0)
1083 data->error = TRUE;
1084 if (addrbook_write_attr(fp, AB_ATTAG_NAME, attrib->name) < 0)
1085 data->error = TRUE;
1086 if (fputs(" >", fp) == EOF)
1087 data->error = TRUE;
1088 if (xml_file_put_escape_str(fp, attrib->value) < 0)
1089 data->error = TRUE;
1090 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ATTRIBUTE) < 0)
1091 data->error = TRUE;
1092 node = g_list_next(node);
1094 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1095 data->error = TRUE;
1096 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_PERSON) < 0)
1097 data->error = TRUE;
1103 * Write group and associated references to addresses to file.
1104 * file hash table visitor function.
1105 * \param key Table key.
1106 * \param value Reference to group.
1107 * \param data File pointer.
1109 static void addrbook_write_item_group_vis(gpointer key, gpointer value,
1110 gpointer d)
1112 AddrItemObject *obj = (AddrItemObject *) value;
1113 HashLoopData *data = (HashLoopData *)d;
1114 FILE *fp = data->fp;
1116 GList *node;
1118 if (!obj)
1119 return;
1120 if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
1121 ItemGroup *group = (ItemGroup *) value;
1122 if (group) {
1123 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_GROUP) < 0)
1124 data->error = TRUE;
1125 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(group)) < 0)
1126 data->error = TRUE;
1127 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(group)) < 0)
1128 data->error = TRUE;
1129 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, group->remarks) < 0)
1130 data->error = TRUE;
1131 if (fputs(" >\n", fp) == EOF)
1132 data->error = TRUE;
1134 /* Output email address links */
1135 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1136 data->error = TRUE;
1137 if (fputs(">\n", fp) == EOF)
1138 data->error = TRUE;
1139 node = group->listEMail;
1140 while (node) {
1141 ItemEMail *email = node->data;
1142 ItemPerson *person = (ItemPerson *) ADDRITEM_PARENT(email);
1143 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_MEMBER) < 0)
1144 data->error = TRUE;
1145 if (addrbook_write_attr(fp, AB_ATTAG_PID, ADDRITEM_ID(person)) < 0)
1146 data->error = TRUE;
1147 if (addrbook_write_attr(fp, AB_ATTAG_EID, ADDRITEM_ID(email)) < 0)
1148 data->error = TRUE;
1149 if (fputs(" />\n", fp) == EOF)
1150 data->error = TRUE;
1151 node = g_list_next(node);
1153 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1154 data->error = TRUE;
1155 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_GROUP) < 0)
1156 data->error = TRUE;
1162 * Write folder and associated references to addresses to file.
1163 * file hash table visitor function.
1164 * \param key Table key.
1165 * \param value Reference to folder.
1166 * \param data File pointer.
1168 static void addrbook_write_item_folder_vis(gpointer key, gpointer value,
1169 gpointer d)
1171 AddrItemObject *obj = (AddrItemObject *) value;
1172 HashLoopData *data = (HashLoopData *)d;
1173 FILE *fp = data->fp;
1174 GList *node;
1176 if (!obj)
1177 return;
1178 if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) {
1179 ItemFolder *folder = (ItemFolder *) value;
1180 if (folder) {
1181 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_FOLDER) < 0)
1182 data->error = TRUE;
1183 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(folder)) < 0)
1184 data->error = TRUE;
1185 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder)) < 0)
1186 data->error = TRUE;
1187 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks) < 0)
1188 data->error = TRUE;
1189 if (fputs(" >\n", fp) == EOF)
1190 data->error = TRUE;
1191 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1192 data->error = TRUE;
1193 if (fputs(">\n", fp) == EOF)
1194 data->error = TRUE;
1196 /* Output persons */
1197 node = folder->listPerson;
1198 while (node) {
1199 ItemPerson *item = node->data;
1200 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1201 data->error = TRUE;
1202 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON) < 0)
1203 data->error = TRUE;
1204 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1205 data->error = TRUE;
1206 if (fputs(" />\n", fp) == EOF)
1207 data->error = TRUE;
1208 node = g_list_next(node);
1211 /* Output groups */
1212 node = folder->listGroup;
1213 while (node) {
1214 ItemGroup *item = node->data;
1215 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1216 data->error = TRUE;
1217 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP) < 0)
1218 data->error = TRUE;
1219 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1220 data->error = TRUE;
1221 if (fputs(" />\n", fp) == EOF)
1222 data->error = TRUE;
1223 node = g_list_next(node);
1226 /* Output folders */
1227 node = folder->listFolder;
1228 while (node) {
1229 ItemFolder *item = node->data;
1230 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1231 data->error = TRUE;
1232 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER) < 0)
1233 data->error = TRUE;
1234 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1235 data->error = TRUE;
1236 if (fputs(" />\n", fp) == EOF)
1237 data->error = TRUE;
1238 node = g_list_next(node);
1240 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1241 data->error = TRUE;
1242 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_FOLDER) < 0)
1243 data->error = TRUE;
1249 * Output address book data to specified file.
1250 * \param book Address book.
1251 * \param newFile Filename of new file (in book's filepath).
1252 * \return Status code.
1254 static gint addrbook_write_to(AddressBookFile *book, gchar *newFile)
1256 FILE *fp;
1257 gchar *fileSpec;
1258 HashLoopData data;
1259 #ifndef DEV_STANDALONE
1260 PrefFile *pfile;
1261 #endif
1263 cm_return_val_if_fail(book != NULL, -1);
1264 cm_return_val_if_fail(newFile != NULL, -1);
1266 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, newFile, NULL);
1268 book->retVal = MGU_OPEN_FILE;
1269 #ifdef DEV_STANDALONE
1270 fp = g_fopen(fileSpec, "wb");
1271 g_free(fileSpec);
1272 if (fp) {
1273 if (fputs("<?xml version=\"1.0\" ?>\n", fp) == EOF) {
1274 book->retVal = MGU_ERROR_WRITE;
1275 return book->retVal;
1277 #else
1278 pfile = prefs_write_open(fileSpec);
1279 g_free(fileSpec);
1280 if (pfile) {
1281 fp = pfile->fp;
1282 if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
1283 goto fail;
1284 #endif
1285 if (addrbook_write_elem_s(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1286 goto fail;
1287 if (addrbook_write_attr(fp, AB_ATTAG_NAME,
1288 addrcache_get_name(book->addressCache)) < 0)
1289 goto fail;
1290 if (fputs(" >\n", fp) == EOF)
1291 goto fail;
1293 /* Output all persons */
1294 data.fp = fp;
1295 data.error = FALSE;
1297 g_hash_table_foreach(book->addressCache->itemHash,
1298 addrbook_write_item_person_vis, &data);
1299 if (data.error)
1300 goto fail;
1302 /* Output all groups */
1303 g_hash_table_foreach(book->addressCache->itemHash,
1304 addrbook_write_item_group_vis, &data);
1306 if (data.error)
1307 goto fail;
1309 /* Output all folders */
1310 g_hash_table_foreach(book->addressCache->itemHash,
1311 addrbook_write_item_folder_vis, &data);
1313 if (data.error)
1314 goto fail;
1316 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1317 goto fail;
1319 book->retVal = MGU_SUCCESS;
1320 #ifdef DEV_STANDALONE
1321 fclose(fp);
1322 #else
1323 if (prefs_file_close( pfile ) < 0)
1324 book->retVal = MGU_ERROR_WRITE;
1325 #endif
1328 fileSpec = NULL;
1329 return book->retVal;
1330 fail:
1331 g_warning("error writing AB\n");
1332 book->retVal = MGU_ERROR_WRITE;
1333 if (pfile)
1334 prefs_file_close_revert( pfile );
1335 return book->retVal;
1339 * Output address book data to original file.
1340 * \param book Address book.
1341 * \return Status code.
1343 gint addrbook_save_data(AddressBookFile *book)
1345 cm_return_val_if_fail(book != NULL, -1);
1347 book->retVal = MGU_NO_FILE;
1348 if (book->fileName == NULL || *book->fileName == '\0')
1349 return book->retVal;
1350 if (book->path == NULL || *book->path == '\0')
1351 return book->retVal;
1353 addrbook_write_to(book, book->fileName);
1354 if (book->retVal == MGU_SUCCESS)
1355 addrcache_set_dirty(book->addressCache, FALSE);
1356 return book->retVal;
1360 * **********************************************************************
1361 * Address book edit interface functions.
1362 * **********************************************************************
1366 * Hash table callback function for simple deletion of hashtable entries.
1367 * \param key Table key (will be freed).
1368 * \param value Value stored in table.
1369 * \param data User data.
1370 * \return <i>TRUE</i> to indicate that entry freed.
1372 static gboolean addrbook_free_simple_hash_vis(gpointer *key, gpointer *value,
1373 gpointer *data)
1375 g_free(key);
1376 key = NULL;
1377 value = NULL;
1378 return TRUE;
1382 * Update address book email list for specified person. Note: The existing
1383 * email addresses are replaced with the new addresses. Any references to
1384 * old addresses in the groups are re-linked to the new addresses. All old
1385 * addresses linked to the person are removed.
1386 * \param book Address book.
1387 * \param person Person to update.
1388 * \param listEMail List of new email addresses.
1390 void addrbook_update_address_list(AddressBookFile *book, ItemPerson *person,
1391 GList *listEMail)
1393 GList *node;
1394 GList *listDelete;
1395 GList *listGroup;
1397 cm_return_if_fail(book != NULL);
1398 cm_return_if_fail(person != NULL);
1400 /* Get groups where person's existing email addresses are listed */
1401 listGroup = addrcache_get_group_for_person(book->addressCache, person);
1402 if (listGroup) {
1403 GHashTable *hashEMail;
1404 GHashTable *hashEMailAlias;
1405 GList *nodeGrp;
1407 /* Load hash table with new address entries */
1408 hashEMail = g_hash_table_new(g_str_hash, g_str_equal);
1409 hashEMailAlias = g_hash_table_new(g_str_hash, g_str_equal);
1410 node = listEMail;
1411 while (node) {
1412 ItemEMail *email = node->data;
1413 gchar *alias = email->obj.name ;
1414 gchar *addr = g_utf8_strdown(email->address, -1);
1415 if (!g_hash_table_lookup(hashEMail, addr)) {
1416 g_hash_table_insert(hashEMail, addr, email);
1418 if (*alias != '\0' && ! g_hash_table_lookup(hashEMailAlias,
1419 alias))
1420 g_hash_table_insert(hashEMailAlias, alias, email);
1422 node = g_list_next(node);
1425 /* Re-parent new addresses to existing groups, where email address match. */
1426 nodeGrp = listGroup;
1427 while (nodeGrp) {
1428 ItemGroup *group = (ItemGroup *) nodeGrp->data;
1429 GList *groupEMail = group->listEMail;
1430 GList *nodeGrpEM;
1431 GList *listRemove = NULL;
1433 /* Process each email item linked to group */
1434 nodeGrpEM = groupEMail;
1435 while (nodeGrpEM) {
1436 ItemEMail *emailGrp = (ItemEMail *) nodeGrpEM->data;
1438 if (ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person)) {
1439 /* Found an email address for this person */
1440 ItemEMail *emailNew = NULL;
1441 gchar *alias = emailGrp->obj.name;
1442 gchar *addr = g_utf8_strdown(emailGrp->address, -1);
1443 emailNew = (ItemEMail *)
1444 g_hash_table_lookup(hashEMail, addr);
1445 g_free( addr );
1446 /* If no match by e-mail, try to match by e-mail alias */
1447 if (!emailNew && *alias != '\0') {
1448 emailNew = (ItemEMail *)
1449 g_hash_table_lookup(hashEMailAlias, alias);
1452 if (emailNew)
1453 /* Point to this entry */
1454 nodeGrpEM->data = emailNew;
1455 else if (g_hash_table_size(hashEMail)==1)
1456 /* If the person has just one e-mail address, then
1457 change e-mail address in group list */
1458 nodeGrpEM->data = listEMail->data;
1459 else
1460 /* Mark for removal */
1461 listRemove = g_list_append(listRemove, emailGrp);
1463 /* Move on to next email link */
1464 nodeGrpEM = g_list_next(nodeGrpEM);
1467 /* Process all removed links in current group */
1468 nodeGrpEM = listRemove;
1469 while (nodeGrpEM) {
1470 ItemEMail *emailGrp = nodeGrpEM->data;
1471 groupEMail = g_list_remove(groupEMail, emailGrp);
1472 nodeGrpEM = g_list_next(nodeGrpEM);
1475 g_list_free(listRemove);
1477 /* Move on to next group */
1478 nodeGrp = g_list_next(nodeGrp);
1481 /* Clear hash table */
1482 g_hash_table_foreach_remove(hashEMail, (GHRFunc)
1483 addrbook_free_simple_hash_vis, NULL);
1484 g_hash_table_destroy(hashEMail);
1485 hashEMail = NULL;
1486 g_hash_table_destroy(hashEMailAlias);
1487 hashEMailAlias = NULL;
1488 g_list_free(listGroup);
1489 listGroup = NULL;
1491 /* Remove old addresses from person and cache */
1492 listDelete = NULL;
1493 node = person->listEMail;
1494 while (node) {
1495 ItemEMail *email = node->data;
1497 if (addrcache_person_remove_email(book->addressCache, person, email))
1498 addrcache_remove_email(book->addressCache, email);
1500 listDelete = g_list_append(listDelete, email);
1501 node = person->listEMail;
1503 /* Add new address entries */
1504 node = listEMail;
1505 while (node) {
1506 ItemEMail *email = node->data;
1508 if (ADDRITEM_ID(email) == NULL)
1509 /* Allocate an ID for new address */
1510 addrcache_id_email(book->addressCache, email);
1512 addrcache_person_add_email( book->addressCache, person, email );
1513 node = g_list_next( node );
1516 addrcache_set_dirty(book->addressCache, TRUE);
1518 /* Free up memory */
1519 g_list_free(listEMail);
1520 listEMail = NULL;
1522 node = listDelete;
1523 while (node) {
1524 ItemEMail *email = node->data;
1526 addritem_free_item_email(email);
1527 node = g_list_next(node);
1529 g_list_free(listDelete);
1530 listDelete = NULL;
1535 * Create person object and add person with specified address data to address
1536 * book. Note: A new person is created with specified list of email addresses.
1537 * All objects inserted into address book.
1539 * \param book Address book.
1540 * \param folder Parent folder where to add person, or <i>NULL</i> for
1541 * root folder.
1542 * \param listEMail List of new email addresses to associate with person.
1543 * \return Person object created.
1545 ItemPerson *addrbook_add_address_list(AddressBookFile *book, ItemFolder *folder,
1546 GList *listEMail)
1548 ItemPerson *person;
1549 ItemFolder *f = folder;
1550 GList *node;
1552 cm_return_val_if_fail(book != NULL, NULL);
1554 if (!f)
1555 f = book->addressCache->rootFolder;
1556 person = addritem_create_item_person();
1557 addrcache_id_person(book->addressCache, person);
1558 addrcache_folder_add_person(book->addressCache, f, person);
1560 node = listEMail;
1561 while (node) {
1562 ItemEMail *email = node->data;
1563 if (ADDRITEM_ID(email) == NULL)
1564 addrcache_id_email(book->addressCache, email);
1566 addrcache_person_add_email(book->addressCache, person, email);
1567 node = g_list_next(node);
1569 return person;
1573 * Build available email list visitor function.
1574 * \param key Table key.
1575 * \param value Value stored in table.
1576 * \param data Reference to address book.
1578 static void addrbook_build_avail_email_vis(gpointer key, gpointer value,
1579 gpointer data)
1581 AddrItemObject *obj = (AddrItemObject *) value;
1583 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1584 AddressBookFile *book = data;
1585 ItemPerson *person = (ItemPerson *) obj;
1586 GList *node = person->listEMail;
1587 while (node) {
1588 ItemEMail *email = node->data;
1589 /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */
1591 if (!g_hash_table_lookup(book->tempHash,
1592 ADDRITEM_ID(email)))
1593 book->tempList = g_list_append(book->tempList, email);
1595 node = g_list_next(node);
1601 * Return link list of available email items that have not already been linked
1602 * to groups. Note that the list contains references to items and should be
1603 * <code>g_free()</code> when done. Do <b>*NOT*</b> attempt to used the
1604 * <code>addrbook_free_xxx()<code> functions... this will destroy the
1605 * addressbook data!
1607 * \param book Address book.
1608 * \param group Group to process.
1609 * \return List of items, or <i>NULL</i> if none.
1611 GList *addrbook_get_available_email_list(AddressBookFile *book, ItemGroup *group)
1613 GList *list = NULL;
1614 GHashTable *table;
1616 cm_return_val_if_fail(book != NULL, NULL);
1618 /* Load hash table with group email entries */
1619 table = g_hash_table_new(g_str_hash, g_str_equal);
1620 if (group) {
1621 list = group->listEMail;
1622 while (list) {
1623 ItemEMail *email = list->data;
1624 g_hash_table_insert(table, ADDRITEM_ID(email), email);
1625 list = g_list_next(list);
1629 /* Build list of available email addresses which exclude those already in groups */
1630 book->tempList = NULL;
1631 book->tempHash = table;
1632 g_hash_table_foreach(book->addressCache->itemHash,
1633 addrbook_build_avail_email_vis, book);
1634 list = book->tempList;
1635 book->tempList = NULL;
1636 book->tempHash = NULL;
1638 /* Clear hash table */
1639 g_hash_table_destroy(table);
1640 table = NULL;
1642 return list;
1646 * Update address book email list for specified group. Note: The existing email
1647 * addresses are replaced with the new addresses. Any references to old addresses
1648 * in the groups are re-linked to the new addresses. All old addresses linked to
1649 * the person are removed.
1651 * \param book Address book.
1652 * \param group Group to process.
1653 * \param listEMail List of email items. This should <b>*NOT*</b> be
1654 * <code>g_free()</code> when done.
1656 void addrbook_update_group_list(AddressBookFile *book, ItemGroup *group,
1657 GList *listEMail)
1659 GList *oldData;
1661 cm_return_if_fail(book != NULL);
1662 cm_return_if_fail(group != NULL);
1664 addrcache_set_dirty(book->addressCache, TRUE);
1666 /* Remember old list */
1667 oldData = group->listEMail;
1668 group->listEMail = listEMail;
1669 mgu_clear_list(oldData);
1670 oldData = NULL;
1674 * Create group object and add with specifed list of email addresses to
1675 * address book. Note: The existing email addresses are replaced with the new
1676 * addresses. Any references to old addresses in the groups are re-linked to
1677 * the new addresses. All old addresses linked to the person are removed.
1679 * \param book Address book.
1680 * \param folder Parent folder where to add group, or <i>NULL</i> for
1681 * root folder.
1682 * \param listEMail List of email items. This should <b>*NOT*</b> be
1683 * <code>g_free()</code> when done.
1684 * \return Group object created.
1686 ItemGroup *addrbook_add_group_list(AddressBookFile *book, ItemFolder *folder,
1687 GList *listEMail)
1689 ItemGroup *group = NULL;
1690 ItemFolder *f = folder;
1692 cm_return_val_if_fail(book != NULL, NULL);
1694 if (!f)
1695 f = book->addressCache->rootFolder;
1696 group = addritem_create_item_group();
1697 addrcache_id_group(book->addressCache, group);
1698 addrcache_folder_add_group(book->addressCache, f, group);
1699 group->listEMail = listEMail;
1700 return group;
1704 * Create a new folder and add to address book.
1705 * \param book Address book.
1706 * \param folder Parent folder where to add folder, or <i>NULL</i> for
1707 * root folder.
1708 * \return Folder that was created. This should <b>*NOT*</b> be
1709 * <code>g_free()</code> when done.
1711 ItemFolder *addrbook_add_new_folder(AddressBookFile *book, ItemFolder *parent)
1713 cm_return_val_if_fail(book != NULL, NULL);
1714 return addrcache_add_new_folder( book->addressCache, parent );
1718 * Update address book attribute list for specified person. Note: The existing
1719 * attributes are replaced with the new addresses. All old attributes linked
1720 * to the person are removed.
1722 * \param book Address book.
1723 * \param person Person to receive attributes.
1724 * \param listAttrib New list of attributes.
1726 void addrbook_update_attrib_list(AddressBookFile *book, ItemPerson *person,
1727 GList *listAttrib)
1729 GList *node;
1730 GList *oldData;
1732 cm_return_if_fail(book != NULL);
1733 cm_return_if_fail(person != NULL);
1735 /* Remember old list */
1736 oldData = person->listAttrib;
1738 /* Attach new address list to person. */
1739 node = listAttrib;
1740 while (node) {
1741 UserAttribute *attrib = node->data;
1742 if (attrib->uid == NULL) {
1743 /* Allocate an ID */
1744 addrcache_id_attribute(book->addressCache, attrib);
1746 node = g_list_next(node);
1748 person->listAttrib = listAttrib;
1749 addrcache_set_dirty(book->addressCache, TRUE);
1751 /* Free up old data */
1752 addritem_free_list_attribute(oldData);
1753 oldData = NULL;
1757 * Add attribute data for specified person to address book. Note: Only
1758 * attributes are inserted into address book.
1759 * \param book Address book.
1760 * \param person Person to receive attributes.
1761 * \param listAttrib List of attributes.
1763 void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1764 GList *node;
1766 cm_return_if_fail( book != NULL );
1767 cm_return_if_fail( person != NULL );
1769 node = listAttrib;
1770 while( node ) {
1771 UserAttribute *attrib = node->data;
1772 if( attrib->uid == NULL ) {
1773 addrcache_id_attribute( book->addressCache, attrib );
1775 addritem_person_add_attribute( person, attrib );
1776 node = g_list_next( node );
1778 addrcache_set_dirty( book->addressCache, TRUE );
1781 #define WORK_BUFLEN 1024
1782 #define ADDRBOOK_DIGITS "0123456789"
1785 * Return list of existing address book files.
1786 * \param book Address book.
1787 * \return List of files (as strings).
1789 GList *addrbook_get_bookfile_list(AddressBookFile *book) {
1790 gchar *adbookdir;
1791 GDir *dir;
1792 const gchar *dir_name;
1793 struct stat statbuf;
1794 gchar buf[WORK_BUFLEN + 1];
1795 gchar numbuf[WORK_BUFLEN];
1796 gint len, lenpre, lensuf, lennum;
1797 long int val, maxval;
1798 GList *fileList = NULL;
1800 cm_return_val_if_fail(book != NULL, NULL);
1802 if (book->path == NULL || *book->path == '\0') {
1803 book->retVal = MGU_NO_PATH;
1804 return NULL;
1807 strncpy(buf, book->path, WORK_BUFLEN);
1808 len = strlen(buf);
1809 if (len > 0) {
1810 if (buf[len-1] != G_DIR_SEPARATOR) {
1811 buf[len] = G_DIR_SEPARATOR;
1812 buf[++len] = '\0';
1816 adbookdir = g_strdup(buf);
1817 strncat(buf, ADDRBOOK_PREFIX, WORK_BUFLEN - strlen(buf));
1819 if( ( dir = g_dir_open( adbookdir, 0, NULL ) ) == NULL ) {
1820 book->retVal = MGU_OPEN_DIRECTORY;
1821 g_free(adbookdir);
1822 return NULL;
1825 lenpre = strlen(ADDRBOOK_PREFIX);
1826 lensuf = strlen(ADDRBOOK_SUFFIX);
1827 lennum = FILE_NUMDIGITS + lenpre;
1828 maxval = -1;
1830 while( ( dir_name = g_dir_read_name( dir ) ) != NULL ) {
1831 gchar *endptr = NULL;
1832 gint i, r;
1833 gboolean flg;
1835 strncpy(buf, adbookdir, WORK_BUFLEN);
1836 strncat(buf, dir_name, WORK_BUFLEN - strlen(buf));
1837 r = g_stat(buf, &statbuf);
1838 if (r == 0 && S_ISREG(statbuf.st_mode)) {
1839 if (strncmp(
1840 dir_name,
1841 ADDRBOOK_PREFIX, lenpre) == 0)
1843 if (strncmp(
1844 (dir_name) + lennum,
1845 ADDRBOOK_SUFFIX, lensuf) == 0)
1847 strncpy(numbuf,
1848 (dir_name) + lenpre,
1849 FILE_NUMDIGITS);
1850 numbuf[FILE_NUMDIGITS] = '\0';
1851 flg = TRUE;
1852 for(i = 0; i < FILE_NUMDIGITS; i++) {
1853 if(!strchr(ADDRBOOK_DIGITS, numbuf[i])) {
1854 flg = FALSE;
1855 break;
1858 if (flg) {
1859 /* Get value */
1860 val = strtol(numbuf, &endptr, 10);
1861 if (endptr && val > -1) {
1862 if (val > maxval) maxval = val;
1863 fileList = g_list_append(
1864 fileList,
1865 g_strdup(dir_name));
1872 g_dir_close( dir );
1873 g_free(adbookdir);
1875 book->maxValue = maxval;
1876 book->retVal = MGU_SUCCESS;
1877 return fileList;
1881 * Return file name for specified file number.
1882 * \param fileNum File number.
1883 * \return File name, or <i>NULL</i> if file number too large. Should be
1884 * <code>g_free()</code> when done.
1886 gchar *addrbook_gen_new_file_name(gint fileNum) {
1887 gchar fmt[30];
1888 gchar buf[WORK_BUFLEN];
1889 gint n = fileNum;
1890 long int nmax;
1892 if (n < 1)
1893 n = 1;
1894 nmax = -1 + (long int) pow(10, FILE_NUMDIGITS);
1895 if (fileNum > nmax)
1896 return NULL;
1897 g_snprintf(fmt, sizeof(fmt), "%%s%%0%dd%%s", FILE_NUMDIGITS);
1898 g_snprintf(buf, sizeof(buf), fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX);
1899 return g_strdup(buf);
1903 * **********************************************************************
1904 * Address book test functions...
1905 * **********************************************************************
1909 * Attempt to parse list of email address from file.
1910 * \param book Address book.
1911 * \param file XML file handle.
1913 static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file )
1915 guint prev_level;
1916 /* GList *attr; */
1918 for (;;) {
1919 prev_level = file->level;
1920 if (xml_parse_next_tag(file))
1921 longjmp(book->jumper, 1);
1922 if (file->level < prev_level)
1923 return;
1924 /* attr = xml_get_current_tag_attr(file); */
1925 /* addrbook_show_attribs( attr ); */
1926 if (xml_compare_tag(file, AB_ELTAG_ADDRESS))
1927 addrbook_chkparse_addr_list(book, file);
1932 * Attempt to parse attributes for person address from file.
1933 * \param book Address book.
1934 * \param file XML file handle.
1936 static void addrbook_chkparse_attribute(AddressBookFile *book, XMLFile *file)
1938 /* GList *attr; */
1939 /* gchar *element; */
1941 /* attr = xml_get_current_tag_attr(file); */
1942 /* addrbook_show_attribs( attr ); */
1943 /* element = xml_get_element(file); */
1944 /* g_print( "\t\tattrib value : %s\n", element ); */
1948 * Attempt to parse list of attributes for person address from file.
1949 * \param book Address book.
1950 * \param file XML file handle.
1952 static void addrbook_chkparse_attr_list(AddressBookFile *book, XMLFile *file)
1954 guint prev_level;
1956 for (;;) {
1957 prev_level = file->level;
1958 if (xml_parse_next_tag(file))
1959 longjmp(book->jumper, 1);
1960 if (file->level < prev_level)
1961 return;
1962 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
1963 addrbook_chkparse_attribute(book, file);
1964 addrbook_chkparse_attr_list(book, file);
1970 * Attempt to parse person from file.
1971 * \param book Address book.
1972 * \param file XML file handle.
1974 static void addrbook_chkparse_person(AddressBookFile *book, XMLFile *file)
1976 /* GList *attr; */
1978 /* attr = xml_get_current_tag_attr(file); */
1979 /* addrbook_show_attribs( attr ); */
1980 if (xml_parse_next_tag(file)) /* Consume closing tag */
1981 longjmp(book->jumper, 1);
1983 if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST))
1984 addrbook_chkparse_addr_list(book, file);
1986 if (xml_parse_next_tag(file)) /* Consume closing tag */
1987 longjmp(book->jumper, 1);
1989 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST))
1990 addrbook_chkparse_attr_list(book, file);
1994 * Attempt to parse list of members from file.
1995 * \param book Address book.
1996 * \param file XML file handle.
1998 static void addrbook_chkparse_member_list(AddressBookFile *book, XMLFile *file)
2000 /* GList *attr; */
2001 guint prev_level;
2003 for (;;) {
2004 prev_level = file->level;
2005 if (xml_parse_next_tag(file))
2006 longjmp(book->jumper, 1);
2008 if (file->level < prev_level)
2009 return;
2011 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
2012 /* attr = xml_get_current_tag_attr(file); */
2013 /* addrbook_show_attribs( attr ); */
2014 addrbook_chkparse_member_list(book, file);
2016 else {
2017 /* attr = xml_get_current_tag_attr(file); */
2018 /* addrbook_show_attribs( attr ); */
2024 * Attempt to parse group from file.
2025 * \param book Address book.
2026 * \param file XML file handle.
2028 static void addrbook_chkparse_group(AddressBookFile *book, XMLFile *file)
2030 /* GList *attr; */
2032 /* attr = xml_get_current_tag_attr(file); */
2033 /* addrbook_show_attribs( attr ); */
2034 if (xml_parse_next_tag(file)) /* Consume closing tag */
2035 longjmp(book->jumper, 1);
2037 if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST))
2038 addrbook_chkparse_member_list(book, file);
2042 * Attempt to parse list of folders from file.
2043 * \param book Address book.
2044 * \param file XML file handle.
2046 static void addrbook_chkparse_folder_list(AddressBookFile *book, XMLFile *file)
2048 /* GList *attr; */
2049 guint prev_level;
2051 for (;;) {
2052 prev_level = file->level;
2053 if (xml_parse_next_tag(file))
2054 longjmp(book->jumper, 1);
2056 if (file->level < prev_level)
2057 return;
2059 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
2060 /* attr = xml_get_current_tag_attr(file); */
2061 /* addrbook_show_attribs( attr ); */
2062 addrbook_chkparse_folder_list(book, file);
2064 else {
2065 /* attr = xml_get_current_tag_attr(file); */
2066 /* addrbook_show_attribs( attr ); */
2072 * Attempt to parse a folder from file.
2073 * \param book Address book.
2074 * \param file XML file handle.
2076 static void addrbook_chkparse_folder(AddressBookFile *book, XMLFile *file)
2078 /* GList *attr; */
2080 /* attr = xml_get_current_tag_attr(file); */
2081 /* addrbook_show_attribs( attr ); */
2082 if (xml_parse_next_tag(file)) /* Consume closing tag */
2083 longjmp(book->jumper, 1);
2085 if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST))
2086 addrbook_chkparse_folder_list(book, file);
2090 * Attempt to parse (DOM) tree from file.
2091 * \param book Address book.
2092 * \param file XML file handle.
2094 static gboolean addrbook_chkread_tree(AddressBookFile *book, XMLFile *file)
2096 /* GList *attr; */
2097 gboolean retVal;
2099 if (xml_get_dtd(file))
2100 return FALSE;
2102 if (xml_parse_next_tag(file))
2103 return FALSE;
2105 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
2106 return FALSE;
2108 /* attr = xml_get_current_tag_attr(file); */
2109 /* addrbook_show_attribs( attr ); */
2111 retVal = TRUE;
2112 for (;;) {
2113 if (!file->level)
2114 break;
2115 /* Get item tag */
2116 if (xml_parse_next_tag(file))
2117 longjmp(book->jumper, 1);
2119 /* Get next tag (person, group or folder) */
2120 if (xml_compare_tag(file, AB_ELTAG_PERSON))
2121 addrbook_chkparse_person( book, file );
2122 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
2123 addrbook_chkparse_group(book, file);
2124 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
2125 addrbook_chkparse_folder(book, file);
2127 return retVal;
2131 * Test address book file by parsing contents.
2132 * \param book Address book.
2133 * \param fileName Filename of XML file.
2134 * \return Status code <i>MGU_SUCCESS</i> if file appears to be valid format.
2136 gint addrbook_test_read_file(AddressBookFile *book, gchar *fileName)
2138 XMLFile *file = NULL;
2139 gchar *fileSpec = NULL;
2141 cm_return_val_if_fail(book != NULL, -1);
2143 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, fileName, NULL);
2144 book->retVal = MGU_OPEN_FILE;
2145 file = xml_open_file(fileSpec);
2146 g_free(fileSpec);
2147 if (file) {
2148 book->retVal = MGU_BAD_FORMAT;
2149 if (setjmp(book->jumper)) {
2150 /* g_print( "Caught Ya!!!\n" ); */
2151 xml_close_file(file);
2152 return book->retVal;
2154 if (addrbook_chkread_tree(book, file))
2155 book->retVal = MGU_SUCCESS;
2157 xml_close_file( file );
2159 return book->retVal;
2163 * Return link list of all persons in address book. Note that the list
2164 * contains references to items. Do <b>*NOT*</b> attempt to use the
2165 * <code>addrbook_free_xxx()</code> functions... this will destroy the
2166 * addressbook data!
2167 * \param book Address book.
2168 * \return List of persons, or NULL if none.
2170 GList *addrbook_get_all_persons(AddressBookFile *book)
2172 cm_return_val_if_fail(book != NULL, NULL);
2173 return addrcache_get_all_persons(book->addressCache);
2176 GList *addrbook_get_all_groups(AddressBookFile *book)
2178 cm_return_val_if_fail(book != NULL, NULL);
2179 return addrcache_get_all_groups(book->addressCache);
2183 * Add person and address data to address book.
2184 * \param book Address book.
2185 * \param folder Folder where to add person, or NULL for root folder.
2186 * \param name Common name.
2187 * \param address EMail address.
2188 * \param remarks Remarks.
2189 * \return Person added. Do not <b>*NOT*</b> to use the
2190 * <code>addrbook_free_xxx()</code> functions... this will destroy
2191 * the address book data.
2193 ItemPerson *addrbook_add_contact(AddressBookFile *book, ItemFolder *folder,
2194 const gchar *name,const gchar *address,
2195 const gchar *remarks)
2197 ItemPerson *person;
2199 cm_return_val_if_fail(book != NULL, NULL);
2200 person = addrcache_add_contact(
2201 book->addressCache, folder, name, address, remarks );
2202 return person;
2206 * Return file name for next address book file.
2207 * \param book Address book.
2208 * \return File name, or <i>NULL</i> if could not create. This should be
2209 * <code>g_free()</code> when done.
2211 gchar *addrbook_guess_next_file(AddressBookFile *book)
2213 gchar *newFile = NULL;
2214 GList *fileList = NULL;
2215 gint fileNum = 1;
2216 fileList = addrbook_get_bookfile_list(book);
2217 if (fileList)
2218 fileNum = 1 + book->maxValue;
2220 newFile = addrbook_gen_new_file_name(fileNum);
2221 g_list_free(fileList);
2222 fileList = NULL;
2223 return newFile;
2226 void addrbook_delete_book_file(AddressBookFile *book)
2228 gchar *book_path;
2230 if (!book->path || !book->fileName)
2231 return;
2233 book_path = g_strconcat(book->path, G_DIR_SEPARATOR_S,
2234 book->fileName, NULL);
2235 claws_unlink(book_path);
2236 g_free(book_path);
2240 * End of Source.