for release 4.2.0
[claws.git] / src / addrbook.c
blob6eadfa9f43c23ed5fd4db6002f9bfca226ca3548
1 /*
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 */
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #include "claws-features.h"
24 #endif
26 #include <glib.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/stat.h>
30 #include <math.h>
31 #include <setjmp.h>
33 #include "utils.h"
34 #include "xml.h"
35 #include "mgutils.h"
36 #include "addritem.h"
37 #include "addrcache.h"
38 #include "addrbook.h"
39 #include "adbookbase.h"
40 #include "file-utils.h"
42 #ifndef DEV_STANDALONE
43 #include "prefs_gtk.h"
44 #include "codeconv.h"
45 #endif
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
54 #ifdef DEBUG_ADDRBOOK
55 static void addrbook_print_book ( AddressBookFile *book, FILE *stream );
56 #endif
58 /**
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;
70 book->path = NULL;
71 book->fileName = NULL;
72 book->maxValue = 0;
73 book->tempList = NULL;
74 book->tempHash = NULL;
75 book->addressCache->modified = TRUE;
77 return book;
80 /**
81 * Specify name to be used
82 * \param book Address book.
83 * \param value Name.
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);
97 /**
98 * Specify path to address book file.
99 * \param book Address book.
100 * \param value Path.
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);
153 return book->retVal;
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);
192 * Free address book.
193 * \param book Address book.
195 void addrbook_free_book(AddressBookFile *book)
197 cm_return_if_fail(book != NULL);
199 /* Clear cache */
200 addrcache_free(book->addressCache);
202 /* Free up internal objects */
203 g_free(book->path);
204 g_free(book->fileName);
205 g_list_free(book->tempList);
207 book->path = NULL;
208 book->fileName = NULL;
209 book->maxValue = 0;
210 book->tempList = NULL;
211 book->tempHash = NULL;
213 book->type = ADBOOKTYPE_NONE;
214 book->addressCache = NULL;
215 book->retVal = MGU_SUCCESS;
217 g_free(book);
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)
244 ItemFolder *folder;
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);
252 #endif
255 * Remove specified group from address book. Note that object should still
256 * be freed.
257 * Specify name to be used
258 * \param book Address book.
259 * \param group Group to remove.
260 * \param value Name.
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
271 * be freed.
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 * ===========================
301 * Notes:
302 * 1) The address book is structured as follows:
304 * address-book
305 * person
306 * address-list
307 * address
308 * attribute-list
309 * attribute
310 * group
311 * member-list
312 * member
313 * folder
314 * item-list
315 * item
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
325 * loading.
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,
370 ItemPerson *person)
372 GList *attr;
373 gchar *name, *value;
374 ItemEMail *email = NULL;
376 attr = xml_get_current_tag_attr(file);
377 while (attr) {
378 name = ((XMLAttr *)attr->data)->name;
379 value = ((XMLAttr *)attr->data)->value;
380 if (!email)
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);
392 if (email) {
393 if (person) {
394 addrcache_person_add_email(book->addressCache, person,
395 email);
397 else {
398 addritem_free_item_email(email);
399 email = NULL;
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,
411 ItemPerson *person)
413 guint prev_level;
415 for (;;) {
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)
436 GList *attr;
437 gchar *name, *value;
438 gchar *element;
439 UserAttribute *uAttr = NULL;
441 attr = xml_get_current_tag_attr(file);
442 while (attr) {
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);
455 g_free(element);
457 if (uAttr) {
458 if (person) {
459 addritem_person_add_attribute(person, uAttr);
461 else {
462 addritem_free_attribute(uAttr);
463 uAttr = NULL;
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,
475 ItemPerson *person)
477 guint prev_level;
479 for (;;) {
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)
499 GList *attr;
500 gchar *name, *value;
501 ItemPerson *person = NULL;
503 attr = xml_get_current_tag_attr(file);
504 while (attr) {
505 name = ((XMLAttr *)attr->data)->name;
506 value = ((XMLAttr *)attr->data)->value;
507 if (!person)
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);
528 if (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,
547 ItemGroup *group)
549 GList *attr;
550 gchar *name, *value;
551 gchar *eid = NULL;
552 /* gchar *pid = NULL; */
553 ItemEMail *email = NULL;
555 attr = xml_get_current_tag_attr(file);
556 while (attr) {
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);
565 g_free(eid);
566 if (email) {
567 if (group) {
568 addrcache_group_add_email(book->addressCache, group,
569 email);
571 else {
572 addritem_free_item_email(email);
573 email = NULL;
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,
585 ItemGroup *group)
587 guint prev_level;
589 for (;;) {
590 prev_level = file->level;
591 if (xml_parse_next_tag(file)) {
592 longjmp(book->jumper, 1);
594 if (file->level < prev_level)
595 return;
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)
610 GList *attr;
611 gchar *name, *value;
612 ItemGroup *group = NULL;
614 attr = xml_get_current_tag_attr(file);
615 while (attr) {
616 name = ((XMLAttr *)attr->data)->name;
617 value = ((XMLAttr *)attr->data)->value;
618 if (!group)
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)) {
632 if (group) {
633 addrcache_hash_add_group(book->addressCache, group);
635 addrbook_parse_member_list(book, file, group);
636 } else {
637 if (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,
649 ItemFolder *folder)
651 GList *attr;
652 gchar *name, *value;
653 gchar *uid = NULL;
655 attr = xml_get_current_tag_attr(file);
656 while (attr) {
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);
664 if (folder) {
665 if (uid) {
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,
678 ItemFolder *folder)
680 guint prev_level;
682 for (;;) {
683 prev_level = file->level;
684 if (xml_parse_next_tag(file)) {
685 longjmp(book->jumper, 1);
687 if (file->level < prev_level)
688 return;
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)
703 GList *attr;
704 gchar *name, *value;
705 ItemFolder *folder = NULL;
707 attr = xml_get_current_tag_attr(file);
708 while (attr) {
709 name = ((XMLAttr *)attr->data)->name;
710 value = ((XMLAttr *)attr->data)->value;
711 if (!folder)
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)) {
725 if (folder) {
726 if (addrcache_hash_add_folder(book->addressCache,
727 folder)) {
728 book->tempList = g_list_append(book->tempList,
729 folder);
730 /* We will resolve folder later */
731 ADDRITEM_PARENT(folder) = NULL;
734 addrbook_parse_folder_list(book, file, folder);
735 } else {
736 if (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
746 * reading data.
748 static gboolean addrbook_read_tree(AddressBookFile *book, XMLFile *file)
750 gboolean retVal;
751 GList *attr;
752 gchar *name, *value;
754 book->retVal = MGU_BAD_FORMAT;
755 if (xml_get_dtd(file))
756 return FALSE;
757 if (xml_parse_next_tag(file))
758 longjmp(book->jumper, 1);
759 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
760 return FALSE;
762 attr = xml_get_current_tag_attr(file);
763 while (attr) {
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 );
771 retVal = TRUE;
772 for (;;) {
773 if (!file->level)
774 break;
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;
787 return retVal;
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,
804 obj);
805 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
807 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
808 rootFolder->listGroup = g_list_append(rootFolder->listGroup,
809 obj);
810 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
816 * Resolve folder items. Lists of UID's are replaced with pointers to
817 * data items.
818 * \param book Address book.
820 static void addrbook_resolve_folder_items(AddressBookFile *book)
822 GList *nodeFolder = NULL;
823 GList *listRemove = NULL;
824 GList *node = NULL;
825 ItemFolder *rootFolder = book->addressCache->rootFolder;
826 nodeFolder = book->tempList;
828 while (nodeFolder) {
829 ItemFolder *folder = nodeFolder->data;
830 listRemove = NULL;
831 node = folder->listItems;
832 while (node) {
833 gchar *uid = node->data;
834 AddrItemObject *aio = addrcache_get_object(book->addressCache,
835 uid);
836 if (aio) {
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 */
854 g_free(uid);
855 node->data = aio;
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,
863 folder);
864 /* Process remove list */
865 node = listRemove;
866 while (node) {
867 gchar *uid = node->data;
868 folder->listItems = g_list_remove(folder->listItems,
869 uid);
870 g_free(uid);
871 node = g_list_next(node);
873 g_list_free(listRemove);
874 nodeFolder = g_list_next(nodeFolder);
876 /* Remove folders with parents. */
877 listRemove = NULL;
878 node = rootFolder->listFolder;
879 while (node) {
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 */
890 node = listRemove;
891 while (node) {
892 rootFolder->listFolder = g_list_remove(rootFolder->listFolder,
893 node->data);
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;
904 while (nodeFolder) {
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;
915 * Read address book.
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);
938 g_free(fileSpec);
939 if (file) {
940 book->tempList = NULL;
941 /* Trap for parsing errors. */
942 if (setjmp( book->jumper)) {
943 xml_close_file(file);
944 return book->retVal;
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);
955 return book->retVal;
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)
966 gint i;
967 for (i = 0; i < lvl; i++)
968 if (claws_fputs(" ", fp) == EOF)
969 return -1;
970 if (claws_fputs("<", fp) == EOF)
971 return -1;
972 if (claws_fputs(name, fp) == EOF)
973 return -1;
975 return 0;
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)
986 gint i;
987 for(i = 0; i < lvl; i++)
988 if (claws_fputs(" ", fp) == EOF)
989 return -1;
990 if (claws_fputs("</", fp) == EOF)
991 return -1;
992 if (claws_fputs(name, fp) == EOF)
993 return -1;
994 if (claws_fputs(">\n", fp) == EOF)
995 return -1;
997 return 0;
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)
1009 return -1;
1010 if (claws_fputs(name, fp) == EOF)
1011 return -1;
1012 if (claws_fputs("=\"", fp) == EOF)
1013 return -1;
1014 if (xml_file_put_escape_str(fp, value) < 0)
1015 return -1;
1016 if (claws_fputs("\"", fp) == EOF)
1017 return -1;
1019 return 0;
1022 typedef struct _HashLoopData {
1023 FILE *fp;
1024 gboolean error;
1025 } 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,
1035 gpointer d)
1037 AddrItemObject *obj = (AddrItemObject *) value;
1038 HashLoopData *data = (HashLoopData *)d;
1039 FILE *fp = data->fp;
1040 GList *node;
1042 if (!obj)
1043 return;
1044 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1045 ItemPerson *person = (ItemPerson *) value;
1046 if (person) {
1047 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_PERSON) < 0)
1048 data->error = TRUE;
1049 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(person)) < 0)
1050 data->error = TRUE;
1051 if (addrbook_write_attr(fp, AB_ATTAG_FIRST_NAME, person->firstName) < 0)
1052 data->error = TRUE;
1053 if (addrbook_write_attr(fp, AB_ATTAG_LAST_NAME, person->lastName) < 0)
1054 data->error = TRUE;
1055 if (addrbook_write_attr(fp, AB_ATTAG_NICK_NAME, person->nickName) < 0)
1056 data->error = TRUE;
1057 if (addrbook_write_attr(fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person)) < 0)
1058 data->error = TRUE;
1059 if (claws_fputs(" >\n", fp) == EOF)
1060 data->error = TRUE;
1062 /* Output email addresses */
1063 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1064 data->error = TRUE;
1065 if (claws_fputs(">\n", fp) == EOF)
1066 data->error = TRUE;
1067 node = person->listEMail;
1068 while (node) {
1069 ItemEMail *email = node->data;
1070 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ADDRESS) < 0)
1071 data->error = TRUE;
1072 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(email)) < 0)
1073 data->error = TRUE;
1074 if (addrbook_write_attr(fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email)) < 0)
1075 data->error = TRUE;
1076 if (addrbook_write_attr(fp, AB_ATTAG_EMAIL, email->address) < 0)
1077 data->error = TRUE;
1078 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, email->remarks) < 0)
1079 data->error = TRUE;
1080 if (claws_fputs(" />\n", fp) == EOF)
1081 data->error = TRUE;
1082 node = g_list_next(node);
1084 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1085 data->error = TRUE;
1087 /* Output user attributes */
1088 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1089 data->error = TRUE;
1090 if (claws_fputs(">\n", fp) == EOF)
1091 data->error = TRUE;
1092 node = person->listAttrib;
1093 while (node) {
1094 UserAttribute *attrib = node->data;
1095 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ATTRIBUTE) < 0)
1096 data->error = TRUE;
1097 if (addrbook_write_attr(fp, AB_ATTAG_UID, attrib->uid) < 0)
1098 data->error = TRUE;
1099 if (addrbook_write_attr(fp, AB_ATTAG_NAME, attrib->name) < 0)
1100 data->error = TRUE;
1101 if (claws_fputs(" >", fp) == EOF)
1102 data->error = TRUE;
1103 if (xml_file_put_escape_str(fp, attrib->value) < 0)
1104 data->error = TRUE;
1105 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ATTRIBUTE) < 0)
1106 data->error = TRUE;
1107 node = g_list_next(node);
1109 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1110 data->error = TRUE;
1111 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_PERSON) < 0)
1112 data->error = TRUE;
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,
1125 gpointer d)
1127 AddrItemObject *obj = (AddrItemObject *) value;
1128 HashLoopData *data = (HashLoopData *)d;
1129 FILE *fp = data->fp;
1131 GList *node;
1133 if (!obj)
1134 return;
1135 if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
1136 ItemGroup *group = (ItemGroup *) value;
1137 if (group) {
1138 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_GROUP) < 0)
1139 data->error = TRUE;
1140 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(group)) < 0)
1141 data->error = TRUE;
1142 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(group)) < 0)
1143 data->error = TRUE;
1144 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, group->remarks) < 0)
1145 data->error = TRUE;
1146 if (claws_fputs(" >\n", fp) == EOF)
1147 data->error = TRUE;
1149 /* Output email address links */
1150 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1151 data->error = TRUE;
1152 if (claws_fputs(">\n", fp) == EOF)
1153 data->error = TRUE;
1154 node = group->listEMail;
1155 while (node) {
1156 ItemEMail *email = node->data;
1157 ItemPerson *person = (ItemPerson *) ADDRITEM_PARENT(email);
1158 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_MEMBER) < 0)
1159 data->error = TRUE;
1160 if (addrbook_write_attr(fp, AB_ATTAG_PID, ADDRITEM_ID(person)) < 0)
1161 data->error = TRUE;
1162 if (addrbook_write_attr(fp, AB_ATTAG_EID, ADDRITEM_ID(email)) < 0)
1163 data->error = TRUE;
1164 if (claws_fputs(" />\n", fp) == EOF)
1165 data->error = TRUE;
1166 node = g_list_next(node);
1168 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1169 data->error = TRUE;
1170 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_GROUP) < 0)
1171 data->error = TRUE;
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,
1184 gpointer d)
1186 AddrItemObject *obj = (AddrItemObject *) value;
1187 HashLoopData *data = (HashLoopData *)d;
1188 FILE *fp = data->fp;
1189 GList *node;
1191 if (!obj)
1192 return;
1193 if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) {
1194 ItemFolder *folder = (ItemFolder *) value;
1195 if (folder) {
1196 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_FOLDER) < 0)
1197 data->error = TRUE;
1198 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(folder)) < 0)
1199 data->error = TRUE;
1200 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder)) < 0)
1201 data->error = TRUE;
1202 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks) < 0)
1203 data->error = TRUE;
1204 if (claws_fputs(" >\n", fp) == EOF)
1205 data->error = TRUE;
1206 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1207 data->error = TRUE;
1208 if (claws_fputs(">\n", fp) == EOF)
1209 data->error = TRUE;
1211 /* Output persons */
1212 node = folder->listPerson;
1213 while (node) {
1214 ItemPerson *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_PERSON) < 0)
1218 data->error = TRUE;
1219 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1220 data->error = TRUE;
1221 if (claws_fputs(" />\n", fp) == EOF)
1222 data->error = TRUE;
1223 node = g_list_next(node);
1226 /* Output groups */
1227 node = folder->listGroup;
1228 while (node) {
1229 ItemGroup *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_GROUP) < 0)
1233 data->error = TRUE;
1234 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1235 data->error = TRUE;
1236 if (claws_fputs(" />\n", fp) == EOF)
1237 data->error = TRUE;
1238 node = g_list_next(node);
1241 /* Output folders */
1242 node = folder->listFolder;
1243 while (node) {
1244 ItemFolder *item = node->data;
1245 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1246 data->error = TRUE;
1247 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER) < 0)
1248 data->error = TRUE;
1249 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1250 data->error = TRUE;
1251 if (claws_fputs(" />\n", fp) == EOF)
1252 data->error = TRUE;
1253 node = g_list_next(node);
1255 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1256 data->error = TRUE;
1257 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_FOLDER) < 0)
1258 data->error = TRUE;
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)
1271 FILE *fp;
1272 gchar *fileSpec;
1273 HashLoopData data;
1274 #ifndef DEV_STANDALONE
1275 PrefFile *pfile;
1276 #endif
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");
1286 g_free(fileSpec);
1287 if (fp) {
1288 if (claws_fputs("<?xml version=\"1.0\" ?>\n", fp) == EOF) {
1289 book->retVal = MGU_ERROR_WRITE;
1290 return book->retVal;
1292 #else
1293 pfile = prefs_write_open(fileSpec);
1294 g_free(fileSpec);
1295 if (pfile) {
1296 fp = pfile->fp;
1297 if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
1298 goto fail;
1299 #endif
1300 if (addrbook_write_elem_s(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1301 goto fail;
1302 if (addrbook_write_attr(fp, AB_ATTAG_NAME,
1303 addrcache_get_name(book->addressCache)) < 0)
1304 goto fail;
1305 if (claws_fputs(" >\n", fp) == EOF)
1306 goto fail;
1308 /* Output all persons */
1309 data.fp = fp;
1310 data.error = FALSE;
1312 g_hash_table_foreach(book->addressCache->itemHash,
1313 addrbook_write_item_person_vis, &data);
1314 if (data.error)
1315 goto fail;
1317 /* Output all groups */
1318 g_hash_table_foreach(book->addressCache->itemHash,
1319 addrbook_write_item_group_vis, &data);
1321 if (data.error)
1322 goto fail;
1324 /* Output all folders */
1325 g_hash_table_foreach(book->addressCache->itemHash,
1326 addrbook_write_item_folder_vis, &data);
1328 if (data.error)
1329 goto fail;
1331 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1332 goto fail;
1334 book->retVal = MGU_SUCCESS;
1335 #ifdef DEV_STANDALONE
1336 claws_safe_fclose(fp);
1337 #else
1338 if (prefs_file_close( pfile ) < 0)
1339 book->retVal = MGU_ERROR_WRITE;
1340 #endif
1343 fileSpec = NULL;
1344 return book->retVal;
1345 fail:
1346 g_warning("error writing AB");
1347 book->retVal = MGU_ERROR_WRITE;
1348 if (pfile)
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,
1388 gpointer *data)
1390 g_free(key);
1391 key = NULL;
1392 value = NULL;
1393 return TRUE;
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,
1406 GList *listEMail)
1408 GList *node;
1409 GList *listDelete;
1410 GList *listGroup;
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);
1417 if (listGroup) {
1418 GHashTable *hashEMail;
1419 GHashTable *hashEMailAlias;
1420 GList *nodeGrp;
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);
1425 node = listEMail;
1426 while (node) {
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,
1434 alias))
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;
1442 while (nodeGrp) {
1443 ItemGroup *group = (ItemGroup *) nodeGrp->data;
1444 GList *groupEMail = group->listEMail;
1445 GList *nodeGrpEM;
1446 GList *listRemove = NULL;
1448 /* Process each email item linked to group */
1449 nodeGrpEM = groupEMail;
1450 while (nodeGrpEM) {
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);
1460 g_free( 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);
1467 if (emailNew)
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;
1474 else
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;
1484 while (nodeGrpEM) {
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);
1500 hashEMail = NULL;
1501 g_hash_table_destroy(hashEMailAlias);
1502 hashEMailAlias = NULL;
1503 g_list_free(listGroup);
1504 listGroup = NULL;
1506 /* Remove old addresses from person and cache */
1507 listDelete = NULL;
1508 node = person->listEMail;
1509 while (node) {
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 */
1519 node = listEMail;
1520 while (node) {
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);
1535 listEMail = NULL;
1537 node = listDelete;
1538 while (node) {
1539 ItemEMail *email = node->data;
1541 addritem_free_item_email(email);
1542 node = g_list_next(node);
1544 g_list_free(listDelete);
1545 listDelete = NULL;
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
1556 * root folder.
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,
1561 GList *listEMail)
1563 ItemPerson *person;
1564 ItemFolder *f = folder;
1565 GList *node;
1567 cm_return_val_if_fail(book != NULL, NULL);
1569 if (!f)
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);
1575 node = listEMail;
1576 while (node) {
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);
1584 return person;
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,
1594 gpointer data)
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;
1602 while (node) {
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
1620 * addressbook data!
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)
1628 GList *list = NULL;
1629 GHashTable *table;
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);
1635 if (group) {
1636 list = group->listEMail;
1637 while (list) {
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);
1655 table = NULL;
1657 return list;
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,
1672 GList *listEMail)
1674 GList *oldData;
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
1695 * root folder.
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,
1701 GList *listEMail)
1703 ItemGroup *group = NULL;
1704 ItemFolder *f = folder;
1706 cm_return_val_if_fail(book != NULL, NULL);
1708 if (!f)
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;
1714 return group;
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
1721 * root folder.
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,
1741 GList *listAttrib)
1743 GList *node;
1744 GList *oldData;
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. */
1753 node = listAttrib;
1754 while (node) {
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);
1767 oldData = NULL;
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 ) {
1778 GList *node;
1780 cm_return_if_fail( book != NULL );
1781 cm_return_if_fail( person != NULL );
1783 node = listAttrib;
1784 while( node ) {
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) {
1804 gchar *adbookdir;
1805 GDir *dir;
1806 const gchar *dir_name;
1807 GStatBuf statbuf;
1808 gchar buf[WORK_BUFLEN + 1];
1809 gchar numbuf[WORK_BUFLEN];
1810 gint len, lenpre, lensuf, lennum;
1811 long int val, maxval;
1812 GList *fileList = NULL;
1814 cm_return_val_if_fail(book != NULL, NULL);
1816 if (book->path == NULL || *book->path == '\0') {
1817 book->retVal = MGU_NO_PATH;
1818 return NULL;
1821 strncpy(buf, book->path, WORK_BUFLEN);
1822 len = strlen(buf);
1823 if (len > 0) {
1824 if (buf[len-1] != G_DIR_SEPARATOR) {
1825 buf[len] = G_DIR_SEPARATOR;
1826 buf[++len] = '\0';
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;
1835 g_free(adbookdir);
1836 return NULL;
1839 lenpre = strlen(ADDRBOOK_PREFIX);
1840 lensuf = strlen(ADDRBOOK_SUFFIX);
1841 lennum = FILE_NUMDIGITS + lenpre;
1842 maxval = -1;
1844 while( ( dir_name = g_dir_read_name( dir ) ) != NULL ) {
1845 gchar *endptr = NULL;
1846 gint i, r;
1847 gboolean flg;
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)) {
1853 if (strncmp(
1854 dir_name,
1855 ADDRBOOK_PREFIX, lenpre) == 0)
1857 if (strncmp(
1858 (dir_name) + lennum,
1859 ADDRBOOK_SUFFIX, lensuf) == 0)
1861 strncpy(numbuf,
1862 (dir_name) + lenpre,
1863 FILE_NUMDIGITS);
1864 numbuf[FILE_NUMDIGITS] = '\0';
1865 flg = TRUE;
1866 for(i = 0; i < FILE_NUMDIGITS; i++) {
1867 if(!strchr(ADDRBOOK_DIGITS, numbuf[i])) {
1868 flg = FALSE;
1869 break;
1872 if (flg) {
1873 /* Get value */
1874 val = strtol(numbuf, &endptr, 10);
1875 if (endptr && val > -1) {
1876 if (val > maxval) maxval = val;
1877 fileList = g_list_append(
1878 fileList,
1879 g_strdup(dir_name));
1886 g_dir_close( dir );
1887 g_free(adbookdir);
1889 book->maxValue = maxval;
1890 book->retVal = MGU_SUCCESS;
1891 return fileList;
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) {
1901 gchar fmt[30];
1902 gchar buf[WORK_BUFLEN];
1903 gint n = fileNum;
1904 long int nmax;
1906 if (n < 1)
1907 n = 1;
1908 nmax = -1 + (long int) pow(10, FILE_NUMDIGITS);
1909 if (fileNum > nmax)
1910 return NULL;
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 )
1929 guint prev_level;
1930 /* GList *attr; */
1932 for (;;) {
1933 prev_level = file->level;
1934 if (xml_parse_next_tag(file))
1935 longjmp(book->jumper, 1);
1936 if (file->level < prev_level)
1937 return;
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)
1952 /* GList *attr; */
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)
1968 guint prev_level;
1970 for (;;) {
1971 prev_level = file->level;
1972 if (xml_parse_next_tag(file))
1973 longjmp(book->jumper, 1);
1974 if (file->level < prev_level)
1975 return;
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)
1990 /* GList *attr; */
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)
2014 /* GList *attr; */
2015 guint prev_level;
2017 for (;;) {
2018 prev_level = file->level;
2019 if (xml_parse_next_tag(file))
2020 longjmp(book->jumper, 1);
2022 if (file->level < prev_level)
2023 return;
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);
2030 else {
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)
2044 /* GList *attr; */
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)
2062 /* GList *attr; */
2063 guint prev_level;
2065 for (;;) {
2066 prev_level = file->level;
2067 if (xml_parse_next_tag(file))
2068 longjmp(book->jumper, 1);
2070 if (file->level < prev_level)
2071 return;
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);
2078 else {
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)
2092 /* GList *attr; */
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)
2110 /* GList *attr; */
2111 gboolean retVal;
2113 if (xml_get_dtd(file))
2114 return FALSE;
2116 if (xml_parse_next_tag(file))
2117 return FALSE;
2119 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
2120 return FALSE;
2122 /* attr = xml_get_current_tag_attr(file); */
2123 /* addrbook_show_attribs( attr ); */
2125 retVal = TRUE;
2126 for (;;) {
2127 if (!file->level)
2128 break;
2129 /* Get item tag */
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);
2141 return retVal;
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);
2160 g_free(fileSpec);
2161 if (file) {
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
2180 * addressbook data!
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)
2211 ItemPerson *person;
2213 cm_return_val_if_fail(book != NULL, NULL);
2214 person = addrcache_add_contact(
2215 book->addressCache, folder, name, address, remarks );
2216 return person;
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;
2229 gint fileNum = 1;
2230 fileList = addrbook_get_bookfile_list(book);
2231 if (fileList)
2232 fileNum = 1 + book->maxValue;
2234 newFile = addrbook_gen_new_file_name(fileNum);
2235 g_list_free(fileList);
2236 fileList = NULL;
2237 return newFile;
2240 void addrbook_delete_book_file(AddressBookFile *book)
2242 gchar *book_path;
2244 if (!book->path || !book->fileName)
2245 return;
2247 book_path = g_strconcat(book->path, G_DIR_SEPARATOR_S,
2248 book->fileName, NULL);
2249 claws_unlink(book_path);
2250 g_free(book_path);
2254 * End of Source.