update team data
[claws.git] / src / addrbook.c
blobad237080062d2167e8518c3232197b753993f722
1 /*
2 * Claws Mail -- 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 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #include "claws-features.h"
25 #endif
27 #include <glib.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <sys/stat.h>
31 #include <math.h>
32 #include <setjmp.h>
34 #include "utils.h"
35 #include "xml.h"
36 #include "mgutils.h"
37 #include "addritem.h"
38 #include "addrcache.h"
39 #include "addrbook.h"
40 #include "adbookbase.h"
41 #include "file-utils.h"
43 #ifndef DEV_STANDALONE
44 #include "prefs_gtk.h"
45 #include "codeconv.h"
46 #endif
48 #define ADDRBOOK_MAX_SEARCH_COUNT 1000
49 #define ADDRBOOK_PREFIX "addrbook-"
50 #define ADDRBOOK_SUFFIX ".xml"
51 #define FILE_NUMDIGITS 6
53 #define ID_TIME_OFFSET 998000000
55 static void addrbook_print_book ( AddressBookFile *book, FILE *stream );
57 /**
58 * Create new address book
59 * \return Address book.
61 AddressBookFile *addrbook_create_book()
63 AddressBookFile *book;
65 book = g_new0(AddressBookFile, 1);
66 book->type = ADBOOKTYPE_BOOK;
67 book->addressCache = addrcache_create();
68 book->retVal = MGU_SUCCESS;
69 book->path = NULL;
70 book->fileName = NULL;
71 book->maxValue = 0;
72 book->tempList = NULL;
73 book->tempHash = NULL;
74 book->addressCache->modified = TRUE;
76 return book;
79 /**
80 * Specify name to be used
81 * \param book Address book.
82 * \param value Name.
84 void addrbook_set_name(AddressBookFile *book, const gchar *value)
86 cm_return_if_fail(book != NULL);
87 addrcache_set_name(book->addressCache, value);
90 gchar *addrbook_get_name(AddressBookFile *book)
92 cm_return_val_if_fail(book != NULL, NULL);
93 return addrcache_get_name(book->addressCache);
96 /**
97 * Specify path to address book file.
98 * \param book Address book.
99 * \param value Path.
101 void addrbook_set_path(AddressBookFile *book, const gchar *value)
103 cm_return_if_fail(book != NULL);
104 book->path = mgu_replace_string(book->path, value);
105 addrcache_set_dirty(book->addressCache, TRUE);
109 * Specify filename to be used
110 * \param book Address book.
111 * \param value Filename.
113 void addrbook_set_file(AddressBookFile *book, const gchar *value)
115 cm_return_if_fail(book != NULL);
116 book->fileName = mgu_replace_string(book->fileName, value);
117 addrcache_set_dirty(book->addressCache, TRUE);
120 gboolean addrbook_get_modified(AddressBookFile *book)
122 cm_return_val_if_fail(book != NULL, FALSE);
123 return book->addressCache->modified;
126 gboolean addrbook_get_accessed(AddressBookFile *book)
128 cm_return_val_if_fail(book != NULL, FALSE);
129 return book->addressCache->accessFlag;
133 * Specify address book as accessed.
134 * \param book Address book.
135 * \param value Value.
137 void addrbook_set_accessed(AddressBookFile *book, const gboolean value)
139 cm_return_if_fail(book != NULL);
140 book->addressCache->accessFlag = value;
143 gboolean addrbook_get_read_flag(AddressBookFile *book)
145 cm_return_val_if_fail(book != NULL, FALSE);
146 return book->addressCache->dataRead;
149 gint addrbook_get_status(AddressBookFile *book)
151 cm_return_val_if_fail(book != NULL, -1);
152 return book->retVal;
155 ItemFolder *addrbook_get_root_folder(AddressBookFile *book)
157 cm_return_val_if_fail(book != NULL, NULL);
158 return addrcache_get_root_folder(book->addressCache);
161 GList *addrbook_get_list_folder(AddressBookFile *book)
163 cm_return_val_if_fail(book != NULL, NULL);
164 return addrcache_get_list_folder(book->addressCache);
167 GList *addrbook_get_list_person(AddressBookFile *book)
169 cm_return_val_if_fail(book != NULL, NULL);
170 return addrcache_get_list_person(book->addressCache);
173 gboolean addrbook_get_dirty(AddressBookFile *book)
175 cm_return_val_if_fail(book != NULL, FALSE);
176 return addrcache_get_dirty(book->addressCache);
180 * Set address book as dirty (needs to be written to file).
181 * \param book Address book.
182 * \param value Dirty flag.
184 void addrbook_set_dirty(AddressBookFile *book, const gboolean value)
186 cm_return_if_fail(book != NULL);
187 addrcache_set_dirty(book->addressCache, value);
191 * Free address book.
192 * \param book Address book.
194 void addrbook_free_book(AddressBookFile *book)
196 cm_return_if_fail(book != NULL);
198 /* Clear cache */
199 addrcache_free(book->addressCache);
201 /* Free up internal objects */
202 g_free(book->path);
203 g_free(book->fileName);
204 g_list_free(book->tempList);
206 book->path = NULL;
207 book->fileName = NULL;
208 book->maxValue = 0;
209 book->tempList = NULL;
210 book->tempHash = NULL;
212 book->type = ADBOOKTYPE_NONE;
213 book->addressCache = NULL;
214 book->retVal = MGU_SUCCESS;
216 g_free(book);
220 * Print address book header.
221 * \param book Address book.
222 * \param stream Output stream.
224 static void addrbook_print_book(AddressBookFile *book, FILE *stream)
226 cm_return_if_fail(book != NULL);
228 fprintf(stream, "AddressBook:\n");
229 fprintf(stream, "\tpath : '%s'\n", book->path);
230 fprintf(stream, "\tfile : '%s'\n", book->fileName);
231 fprintf(stream, "\tstatus : %d\n", book->retVal );
232 addrcache_print(book->addressCache, stream);
236 * Dump entire address book traversing folders.
237 * \param book Address book.
238 * \param stream Output stream.
240 void addrbook_dump_book(AddressBookFile *book, FILE *stream)
242 ItemFolder *folder;
244 cm_return_if_fail(book != NULL);
246 addrbook_print_book(book, stream);
247 folder = book->addressCache->rootFolder;
248 addritem_print_item_folder(folder, stream);
252 * Remove specified group from address book. Note that object should still
253 * be freed.
254 * Specify name to be used
255 * \param book Address book.
256 * \param group Group to remove.
257 * \param value Name.
258 * \return Group, or NULL if not found.
260 ItemGroup *addrbook_remove_group(AddressBookFile *book, ItemGroup *group)
262 cm_return_val_if_fail(book != NULL, NULL);
263 return addrcache_remove_group(book->addressCache, group);
267 * Remove specified person from address book. Note that object should still
268 * be freed.
269 * \param book Address book.
270 * \param person Person to remove.
271 * \return Person, or NULL if not found.
273 ItemPerson *addrbook_remove_person(AddressBookFile *book, ItemPerson *person)
275 cm_return_val_if_fail(book != NULL, NULL);
276 return addrcache_remove_person(book->addressCache, person);
280 * Remove specified email address in address book for specified person.
281 * Note that object should still be freed.
282 * \param book Address book.
283 * \param person Person.
284 * \param email EMail to remove.
285 * \return EMail object, or NULL if not found.
287 ItemEMail *addrbook_person_remove_email(AddressBookFile *book,
288 ItemPerson *person, ItemEMail *email)
290 cm_return_val_if_fail(book != NULL, NULL);
291 return addrcache_person_remove_email(book->addressCache, person, email);
295 * ***********************************************************************
296 * Read/Write XML data file...
297 * ===========================
298 * Notes:
299 * 1) The address book is structured as follows:
301 * address-book
302 * person
303 * address-list
304 * address
305 * attribute-list
306 * attribute
307 * group
308 * member-list
309 * member
310 * folder
311 * item-list
312 * item
314 * 2) This sequence of elements was chosen so that the most important
315 * elements (person and their email addresses) appear first.
317 * 3) Groups then appear. When groups are loaded, person's email
318 * addresses have already been loaded and can be found.
320 * 4) Finally folders are loaded. Any forward and backward references
321 * to folders, groups and persons in the folders are resolved after
322 * loading.
324 * ***********************************************************************
327 /* Element tag names */
328 #define AB_ELTAG_ADDRESS "address"
329 #define AB_ELTAG_ATTRIBUTE "attribute"
330 #define AB_ELTAG_ATTRIBUTE_LIST "attribute-list"
331 #define AB_ELTAG_ADDRESS_LIST "address-list"
332 #define AB_ELTAG_MEMBER "member"
333 #define AB_ELTAG_MEMBER_LIST "member-list"
334 #define AB_ELTAG_ITEM "item"
335 #define AB_ELTAG_ITEM_LIST "item-list"
336 #define AB_ELTAG_ADDRESS_BOOK "address-book"
337 #define AB_ELTAG_PERSON "person"
338 #define AB_ELTAG_GROUP "group"
339 #define AB_ELTAG_FOLDER "folder"
341 /* Attribute tag names */
342 #define AB_ATTAG_TYPE "type"
343 #define AB_ATTAG_UID "uid"
344 #define AB_ATTAG_NAME "name"
345 #define AB_ATTAG_REMARKS "remarks"
346 #define AB_ATTAG_FIRST_NAME "first-name"
347 #define AB_ATTAG_LAST_NAME "last-name"
348 #define AB_ATTAG_NICK_NAME "nick-name"
349 #define AB_ATTAG_COMMON_NAME "cn"
350 #define AB_ATTAG_ALIAS "alias"
351 #define AB_ATTAG_EMAIL "email"
352 #define AB_ATTAG_EID "eid"
353 #define AB_ATTAG_PID "pid"
355 /* Attribute values */
356 #define AB_ATTAG_VAL_PERSON "person"
357 #define AB_ATTAG_VAL_GROUP "group"
358 #define AB_ATTAG_VAL_FOLDER "folder"
361 * Parse address item for person from XML file.
362 * \param book Address book.
363 * \param file XML file handle.
364 * \param person Person.
366 static void addrbook_parse_address(AddressBookFile *book, XMLFile *file,
367 ItemPerson *person)
369 GList *attr;
370 gchar *name, *value;
371 ItemEMail *email = NULL;
373 attr = xml_get_current_tag_attr(file);
374 while (attr) {
375 name = ((XMLAttr *)attr->data)->name;
376 value = ((XMLAttr *)attr->data)->value;
377 if (!email)
378 email = addritem_create_item_email();
379 if (strcmp(name, AB_ATTAG_UID) == 0)
380 ADDRITEM_ID(email) = g_strdup(value);
381 else if (strcmp(name, AB_ATTAG_ALIAS) == 0)
382 ADDRITEM_NAME(email) = g_strdup(value);
383 else if (strcmp(name, AB_ATTAG_EMAIL) == 0)
384 email->address = g_strdup(value);
385 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
386 email->remarks = g_strdup(value);
387 attr = g_list_next(attr);
389 if (email) {
390 if (person) {
391 addrcache_person_add_email(book->addressCache, person,
392 email);
394 else {
395 addritem_free_item_email(email);
396 email = NULL;
402 * Parse list of email address for person from XML file.
403 * \param book Address book.
404 * \param file XML file handle.
405 * \param person Person.
407 static void addrbook_parse_addr_list(AddressBookFile *book, XMLFile *file,
408 ItemPerson *person)
410 guint prev_level;
412 for (;;) {
413 prev_level = file->level;
414 if (xml_parse_next_tag(file)) {
415 longjmp(book->jumper, 1);
417 if (file->level < prev_level) return;
418 if (xml_compare_tag(file, AB_ELTAG_ADDRESS)) {
419 addrbook_parse_address(book, file, person);
420 addrbook_parse_addr_list(book, file, person);
426 * Parse attribute for person from XML file.
427 * \param book Address book.
428 * \param file XML file handle.
429 * \param person Person.
431 static void addrbook_parse_attribute(XMLFile *file, ItemPerson *person)
433 GList *attr;
434 gchar *name, *value;
435 gchar *element;
436 UserAttribute *uAttr = NULL;
438 attr = xml_get_current_tag_attr(file);
439 while (attr) {
440 name = ((XMLAttr *)attr->data)->name;
441 value = ((XMLAttr *)attr->data)->value;
442 if (!uAttr) uAttr = addritem_create_attribute();
443 if (strcmp(name, AB_ATTAG_UID) == 0)
444 addritem_attrib_set_id(uAttr, value);
445 else if (strcmp(name, AB_ATTAG_NAME) == 0)
446 addritem_attrib_set_name(uAttr, value);
447 attr = g_list_next(attr);
450 element = xml_get_element(file);
451 addritem_attrib_set_value(uAttr, element);
452 g_free(element);
454 if (uAttr) {
455 if (person) {
456 addritem_person_add_attribute(person, uAttr);
458 else {
459 addritem_free_attribute(uAttr);
460 uAttr = NULL;
466 * Parse list of attributes for person from XML file.
467 * \param book Address book.
468 * \param file XML file handle.
469 * \param person Person.
471 static void addrbook_parse_attr_list(AddressBookFile *book, XMLFile *file,
472 ItemPerson *person)
474 guint prev_level;
476 for (;;) {
477 prev_level = file->level;
478 if (xml_parse_next_tag(file)) {
479 longjmp( book->jumper, 1 );
481 if (file->level < prev_level) return;
482 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
483 addrbook_parse_attribute(file, person);
484 addrbook_parse_attr_list(book, file, person);
490 * Parse person from XML file.
491 * \param book Address book.
492 * \param file XML file handle.
494 static void addrbook_parse_person(AddressBookFile *book, XMLFile *file)
496 GList *attr;
497 gchar *name, *value;
498 ItemPerson *person = NULL;
500 attr = xml_get_current_tag_attr(file);
501 while (attr) {
502 name = ((XMLAttr *)attr->data)->name;
503 value = ((XMLAttr *)attr->data)->value;
504 if (!person)
505 person = addritem_create_item_person();
506 if (strcmp(name, AB_ATTAG_UID) == 0) {
507 ADDRITEM_ID(person) = g_strdup(value);
508 person->picture = g_strdup(value);
510 else if (strcmp(name, AB_ATTAG_FIRST_NAME) == 0)
511 person->firstName = g_strdup(value);
512 else if (strcmp(name, AB_ATTAG_LAST_NAME) == 0)
513 person->lastName = g_strdup(value);
514 else if (strcmp(name, AB_ATTAG_NICK_NAME) == 0)
515 person->nickName = g_strdup(value);
516 else if (strcmp(name, AB_ATTAG_COMMON_NAME) == 0)
517 ADDRITEM_NAME(person) = g_strdup(value);
518 attr = g_list_next(attr);
520 if (xml_parse_next_tag(file)) { /* Consume closing tag */
521 longjmp(book->jumper, 1);
523 if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST)) {
524 addrbook_parse_addr_list(book, file, person);
525 if (person) {
526 addrcache_hash_add_person(book->addressCache, person);
529 if (xml_parse_next_tag(file)) { /* Consume closing tag */
530 longjmp(book->jumper, 1);
532 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST)) {
533 addrbook_parse_attr_list(book, file, person);
538 * Parse group member from XML file.
539 * \param book Address book.
540 * \param file XML file handle.
541 * \param group Group.
543 static void addrbook_parse_member(AddressBookFile *book, XMLFile *file,
544 ItemGroup *group)
546 GList *attr;
547 gchar *name, *value;
548 gchar *eid = NULL;
549 /* gchar *pid = NULL; */
550 ItemEMail *email = NULL;
552 attr = xml_get_current_tag_attr(file);
553 while (attr) {
554 name = ((XMLAttr *)attr->data)->name;
555 value = ((XMLAttr *)attr->data)->value;
556 if( strcmp( name, AB_ATTAG_EID ) == 0 )
557 eid = g_strdup( value );
558 attr = g_list_next(attr);
560 /* email = addrcache_get_email( book->addressCache, pid, eid ); */
561 email = addrcache_get_email(book->addressCache, eid);
562 g_free(eid);
563 if (email) {
564 if (group) {
565 addrcache_group_add_email(book->addressCache, group,
566 email);
568 else {
569 addritem_free_item_email(email);
570 email = NULL;
576 * Parse list of group members from XML file.
577 * \param book Address book.
578 * \param file XML file handle.
579 * \param group Group.
581 static void addrbook_parse_member_list(AddressBookFile *book, XMLFile *file,
582 ItemGroup *group)
584 guint prev_level;
586 for (;;) {
587 prev_level = file->level;
588 if (xml_parse_next_tag(file)) {
589 longjmp(book->jumper, 1);
591 if (file->level < prev_level)
592 return;
593 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
594 addrbook_parse_member(book, file, group);
595 addrbook_parse_member_list(book, file, group);
601 * Parse group object from XML file.
602 * \param book Address book.
603 * \param file XML file handle.
605 static void addrbook_parse_group(AddressBookFile *book, XMLFile *file)
607 GList *attr;
608 gchar *name, *value;
609 ItemGroup *group = NULL;
611 attr = xml_get_current_tag_attr(file);
612 while (attr) {
613 name = ((XMLAttr *)attr->data)->name;
614 value = ((XMLAttr *)attr->data)->value;
615 if (!group)
616 group = addritem_create_item_group();
617 if (strcmp(name, AB_ATTAG_UID) == 0)
618 ADDRITEM_ID(group) = g_strdup(value);
619 else if (strcmp(name, AB_ATTAG_NAME) == 0)
620 ADDRITEM_NAME(group) = g_strdup(value);
621 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
622 group->remarks = g_strdup(value);
623 attr = g_list_next(attr);
625 if (xml_parse_next_tag(file)) { /* Consume closing tag */
626 longjmp(book->jumper, 1);
628 if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST)) {
629 if (group) {
630 addrcache_hash_add_group(book->addressCache, group);
632 addrbook_parse_member_list(book, file, group);
633 } else {
634 if (group)
635 addritem_free_item_group(group);
640 * Parse folder item from XML file.
641 * \param book Address book.
642 * \param file XML file handle.
643 * \param folder Folder.
645 static void addrbook_parse_folder_item(AddressBookFile *book, XMLFile *file,
646 ItemFolder *folder)
648 GList *attr;
649 gchar *name, *value;
650 gchar *uid = NULL;
652 attr = xml_get_current_tag_attr(file);
653 while (attr) {
654 name = ((XMLAttr *)attr->data)->name;
655 value = ((XMLAttr *)attr->data)->value;
656 if (strcmp(name, AB_ATTAG_UID) == 0) {
657 uid = g_strdup(value);
659 attr = g_list_next(attr);
661 if (folder) {
662 if (uid) {
663 folder->listItems = g_list_append(folder->listItems, uid);
669 * Parse list of folder items from XML file.
670 * \param book Address book.
671 * \param file XML file handle.
672 * \param folder Folder.
674 static void addrbook_parse_folder_list(AddressBookFile *book, XMLFile *file,
675 ItemFolder *folder)
677 guint prev_level;
679 for (;;) {
680 prev_level = file->level;
681 if (xml_parse_next_tag(file)) {
682 longjmp(book->jumper, 1);
684 if (file->level < prev_level)
685 return;
686 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
687 addrbook_parse_folder_item(book, file, folder);
688 addrbook_parse_folder_list(book, file, folder);
694 * Parse folder from XML file.
695 * \param book Address book.
696 * \param file XML file handle.
698 static void addrbook_parse_folder(AddressBookFile *book, XMLFile *file)
700 GList *attr;
701 gchar *name, *value;
702 ItemFolder *folder = NULL;
704 attr = xml_get_current_tag_attr(file);
705 while (attr) {
706 name = ((XMLAttr *)attr->data)->name;
707 value = ((XMLAttr *)attr->data)->value;
708 if (!folder)
709 folder = addritem_create_item_folder();
710 if (strcmp(name, AB_ATTAG_UID) == 0)
711 ADDRITEM_ID(folder) = g_strdup(value);
712 else if (strcmp(name, AB_ATTAG_NAME) == 0)
713 ADDRITEM_NAME(folder) = g_strdup(value);
714 else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
715 folder->remarks = g_strdup(value);
716 attr = g_list_next(attr);
718 if (xml_parse_next_tag(file)) { /* Consume closing tag */
719 longjmp(book->jumper, 1);
721 if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST)) {
722 if (folder) {
723 if (addrcache_hash_add_folder(book->addressCache,
724 folder)) {
725 book->tempList = g_list_append(book->tempList,
726 folder);
727 /* We will resolve folder later */
728 ADDRITEM_PARENT(folder) = NULL;
731 addrbook_parse_folder_list(book, file, folder);
732 } else {
733 if (folder)
734 addritem_free_item_folder(folder);
739 * Read address book (DOM) tree from file.
740 * \param book Address book.
741 * \param file XML file handle.
742 * \return <i>TRUE</i> if data read successfully, <i>FALSE</i> if error
743 * reading data.
745 static gboolean addrbook_read_tree(AddressBookFile *book, XMLFile *file)
747 gboolean retVal;
748 GList *attr;
749 gchar *name, *value;
751 book->retVal = MGU_BAD_FORMAT;
752 if (xml_get_dtd(file))
753 return FALSE;
754 if (xml_parse_next_tag(file))
755 longjmp(book->jumper, 1);
756 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
757 return FALSE;
759 attr = xml_get_current_tag_attr(file);
760 while (attr) {
761 name = ((XMLAttr *)attr->data)->name;
762 value = ((XMLAttr *)attr->data)->value;
763 if (strcmp( name, AB_ATTAG_NAME) == 0)
764 addrbook_set_name( book, value );
765 attr = g_list_next( attr );
768 retVal = TRUE;
769 for (;;) {
770 if (!file->level)
771 break;
772 /* Get next item tag (person, group or folder) */
773 if (xml_parse_next_tag(file))
774 longjmp( book->jumper, 1 );
776 if (xml_compare_tag(file, AB_ELTAG_PERSON))
777 addrbook_parse_person(book, file);
778 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
779 addrbook_parse_group(book, file);
780 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
781 addrbook_parse_folder(book, file);
783 if (retVal) book->retVal = MGU_SUCCESS;
784 return retVal;
788 * Resolve folder items callback function.
789 * \param key Table key.
790 * \param value Reference to object contained in folder.
791 * \param data Reference to address book.
793 static void addrbook_res_items_vis(gpointer key, gpointer value, gpointer data)
795 AddressBookFile *book = data;
796 AddrItemObject *obj = (AddrItemObject *) value;
797 ItemFolder *rootFolder = book->addressCache->rootFolder;
798 if (obj->parent == NULL) {
799 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
800 rootFolder->listPerson = g_list_append(rootFolder->listPerson,
801 obj);
802 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
804 else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
805 rootFolder->listGroup = g_list_append(rootFolder->listGroup,
806 obj);
807 ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
813 * Resolve folder items. Lists of UID's are replaced with pointers to
814 * data items.
815 * \param book Address book.
817 static void addrbook_resolve_folder_items(AddressBookFile *book)
819 GList *nodeFolder = NULL;
820 GList *listRemove = NULL;
821 GList *node = NULL;
822 ItemFolder *rootFolder = book->addressCache->rootFolder;
823 nodeFolder = book->tempList;
825 while (nodeFolder) {
826 ItemFolder *folder = nodeFolder->data;
827 listRemove = NULL;
828 node = folder->listItems;
829 while (node) {
830 gchar *uid = node->data;
831 AddrItemObject *aio = addrcache_get_object(book->addressCache,
832 uid);
833 if (aio) {
834 if (aio->type == ITEMTYPE_FOLDER) {
835 ItemFolder *item = (ItemFolder *) aio;
836 folder->listFolder = g_list_append(folder->listFolder, item);
837 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
838 addrcache_hash_add_folder(book->addressCache, folder);
840 else if (aio->type == ITEMTYPE_PERSON) {
841 ItemPerson *item = (ItemPerson *) aio;
842 folder->listPerson = g_list_append(folder->listPerson, item);
843 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
845 else if (aio->type == ITEMTYPE_GROUP) {
846 ItemGroup *item = (ItemGroup *) aio;
847 folder->listGroup = g_list_append(folder->listGroup, item);
848 ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
850 /* Replace data with pointer to item */
851 g_free(uid);
852 node->data = aio;
854 else { /* Not found, append to remove list. */
855 listRemove = g_list_append(listRemove, uid);
857 node = g_list_next(node);
859 rootFolder->listFolder = g_list_append(rootFolder->listFolder,
860 folder);
861 /* Process remove list */
862 node = listRemove;
863 while (node) {
864 gchar *uid = node->data;
865 folder->listItems = g_list_remove(folder->listItems,
866 uid);
867 g_free(uid);
868 node = g_list_next(node);
870 g_list_free(listRemove);
871 nodeFolder = g_list_next(nodeFolder);
873 /* Remove folders with parents. */
874 listRemove = NULL;
875 node = rootFolder->listFolder;
876 while (node) {
877 ItemFolder *folder = (ItemFolder *) node->data;
878 if (ADDRITEM_PARENT(folder))
879 /* Remove folders with parents */
880 listRemove = g_list_append(listRemove, folder);
881 else /* Add to root folder */
882 ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder);
884 node = g_list_next( node );
886 /* Process remove list */
887 node = listRemove;
888 while (node) {
889 rootFolder->listFolder = g_list_remove(rootFolder->listFolder,
890 node->data);
891 node = g_list_next(node);
893 g_list_free(listRemove);
895 /* Move all unparented persons and groups into root folder */
896 g_hash_table_foreach(book->addressCache->itemHash,
897 addrbook_res_items_vis, book);
899 /* Free up some more */
900 nodeFolder = book->tempList;
901 while (nodeFolder) {
902 ItemFolder *folder = nodeFolder->data;
903 g_list_free(folder->listItems);
904 folder->listItems = NULL;
905 nodeFolder = g_list_next(nodeFolder);
907 g_list_free(book->tempList);
908 book->tempList = NULL;
912 * Read address book.
913 * \param book Address book.
914 * \return Status code.
916 gint addrbook_read_data(AddressBookFile *book)
918 XMLFile *file = NULL;
919 gchar *fileSpec = NULL;
921 cm_return_val_if_fail(book != NULL, -1);
924 g_print( "...addrbook_read_data :%s:\t:%s:\n", book->fileName,
925 addrcache_get_name( book->addressCache ) );
928 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S,
929 book->fileName, NULL);
930 book->retVal = MGU_OPEN_FILE;
931 addrcache_clear(book->addressCache);
932 book->addressCache->modified = FALSE;
933 book->addressCache->accessFlag = FALSE;
934 file = xml_open_file(fileSpec);
935 g_free(fileSpec);
936 if (file) {
937 book->tempList = NULL;
938 /* Trap for parsing errors. */
939 if (setjmp( book->jumper)) {
940 xml_close_file(file);
941 return book->retVal;
943 addrbook_read_tree(book, file);
944 xml_close_file(file);
945 /* Resolve folder items */
946 addrbook_resolve_folder_items(book);
947 book->tempList = NULL;
948 book->addressCache->modified = FALSE;
949 book->addressCache->dataRead = TRUE;
950 addrcache_set_dirty(book->addressCache, FALSE);
952 return book->retVal;
956 * Write start element to file.
957 * \param fp File handle.
958 * \param lvl Indent level.
959 * \param name Element name.
961 static int addrbook_write_elem_s(FILE *fp, gint lvl, gchar *name)
963 gint i;
964 for (i = 0; i < lvl; i++)
965 if (claws_fputs(" ", fp) == EOF)
966 return -1;
967 if (claws_fputs("<", fp) == EOF)
968 return -1;
969 if (claws_fputs(name, fp) == EOF)
970 return -1;
972 return 0;
976 * Write end element to file.
977 * \param fp File handle.
978 * \param lvl Indent level.
979 * \param name Element name.
981 static int addrbook_write_elem_e(FILE *fp, gint lvl, gchar *name)
983 gint i;
984 for(i = 0; i < lvl; i++)
985 if (claws_fputs(" ", fp) == EOF)
986 return -1;
987 if (claws_fputs("</", fp) == EOF)
988 return -1;
989 if (claws_fputs(name, fp) == EOF)
990 return -1;
991 if (claws_fputs(">\n", fp) == EOF)
992 return -1;
994 return 0;
998 * Write attribute name/value pair to file.
999 * \param fp File handle.
1000 * \param name Attribute name.
1001 * \param value Attribute value.
1003 static int addrbook_write_attr(FILE *fp, gchar *name, gchar *value)
1005 if (claws_fputs(" ", fp) == EOF)
1006 return -1;
1007 if (claws_fputs(name, fp) == EOF)
1008 return -1;
1009 if (claws_fputs("=\"", fp) == EOF)
1010 return -1;
1011 if (xml_file_put_escape_str(fp, value) < 0)
1012 return -1;
1013 if (claws_fputs("\"", fp) == EOF)
1014 return -1;
1016 return 0;
1019 typedef struct _HashLoopData {
1020 FILE *fp;
1021 gboolean error;
1022 } HashLoopData;
1025 * Write person and associated addresses and attributes to file.
1026 * file hash table visitor function.
1027 * \param key Table key.
1028 * \param value Reference to person.
1029 * \param data File pointer.
1031 static void addrbook_write_item_person_vis(gpointer key, gpointer value,
1032 gpointer d)
1034 AddrItemObject *obj = (AddrItemObject *) value;
1035 HashLoopData *data = (HashLoopData *)d;
1036 FILE *fp = data->fp;
1037 GList *node;
1039 if (!obj)
1040 return;
1041 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1042 ItemPerson *person = (ItemPerson *) value;
1043 if (person) {
1044 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_PERSON) < 0)
1045 data->error = TRUE;
1046 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(person)) < 0)
1047 data->error = TRUE;
1048 if (addrbook_write_attr(fp, AB_ATTAG_FIRST_NAME, person->firstName) < 0)
1049 data->error = TRUE;
1050 if (addrbook_write_attr(fp, AB_ATTAG_LAST_NAME, person->lastName) < 0)
1051 data->error = TRUE;
1052 if (addrbook_write_attr(fp, AB_ATTAG_NICK_NAME, person->nickName) < 0)
1053 data->error = TRUE;
1054 if (addrbook_write_attr(fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person)) < 0)
1055 data->error = TRUE;
1056 if (claws_fputs(" >\n", fp) == EOF)
1057 data->error = TRUE;
1059 /* Output email addresses */
1060 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1061 data->error = TRUE;
1062 if (claws_fputs(">\n", fp) == EOF)
1063 data->error = TRUE;
1064 node = person->listEMail;
1065 while (node) {
1066 ItemEMail *email = node->data;
1067 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ADDRESS) < 0)
1068 data->error = TRUE;
1069 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(email)) < 0)
1070 data->error = TRUE;
1071 if (addrbook_write_attr(fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email)) < 0)
1072 data->error = TRUE;
1073 if (addrbook_write_attr(fp, AB_ATTAG_EMAIL, email->address) < 0)
1074 data->error = TRUE;
1075 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, email->remarks) < 0)
1076 data->error = TRUE;
1077 if (claws_fputs(" />\n", fp) == EOF)
1078 data->error = TRUE;
1079 node = g_list_next(node);
1081 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1082 data->error = TRUE;
1084 /* Output user attributes */
1085 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1086 data->error = TRUE;
1087 if (claws_fputs(">\n", fp) == EOF)
1088 data->error = TRUE;
1089 node = person->listAttrib;
1090 while (node) {
1091 UserAttribute *attrib = node->data;
1092 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ATTRIBUTE) < 0)
1093 data->error = TRUE;
1094 if (addrbook_write_attr(fp, AB_ATTAG_UID, attrib->uid) < 0)
1095 data->error = TRUE;
1096 if (addrbook_write_attr(fp, AB_ATTAG_NAME, attrib->name) < 0)
1097 data->error = TRUE;
1098 if (claws_fputs(" >", fp) == EOF)
1099 data->error = TRUE;
1100 if (xml_file_put_escape_str(fp, attrib->value) < 0)
1101 data->error = TRUE;
1102 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ATTRIBUTE) < 0)
1103 data->error = TRUE;
1104 node = g_list_next(node);
1106 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1107 data->error = TRUE;
1108 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_PERSON) < 0)
1109 data->error = TRUE;
1115 * Write group and associated references to addresses to file.
1116 * file hash table visitor function.
1117 * \param key Table key.
1118 * \param value Reference to group.
1119 * \param data File pointer.
1121 static void addrbook_write_item_group_vis(gpointer key, gpointer value,
1122 gpointer d)
1124 AddrItemObject *obj = (AddrItemObject *) value;
1125 HashLoopData *data = (HashLoopData *)d;
1126 FILE *fp = data->fp;
1128 GList *node;
1130 if (!obj)
1131 return;
1132 if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
1133 ItemGroup *group = (ItemGroup *) value;
1134 if (group) {
1135 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_GROUP) < 0)
1136 data->error = TRUE;
1137 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(group)) < 0)
1138 data->error = TRUE;
1139 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(group)) < 0)
1140 data->error = TRUE;
1141 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, group->remarks) < 0)
1142 data->error = TRUE;
1143 if (claws_fputs(" >\n", fp) == EOF)
1144 data->error = TRUE;
1146 /* Output email address links */
1147 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1148 data->error = TRUE;
1149 if (claws_fputs(">\n", fp) == EOF)
1150 data->error = TRUE;
1151 node = group->listEMail;
1152 while (node) {
1153 ItemEMail *email = node->data;
1154 ItemPerson *person = (ItemPerson *) ADDRITEM_PARENT(email);
1155 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_MEMBER) < 0)
1156 data->error = TRUE;
1157 if (addrbook_write_attr(fp, AB_ATTAG_PID, ADDRITEM_ID(person)) < 0)
1158 data->error = TRUE;
1159 if (addrbook_write_attr(fp, AB_ATTAG_EID, ADDRITEM_ID(email)) < 0)
1160 data->error = TRUE;
1161 if (claws_fputs(" />\n", fp) == EOF)
1162 data->error = TRUE;
1163 node = g_list_next(node);
1165 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1166 data->error = TRUE;
1167 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_GROUP) < 0)
1168 data->error = TRUE;
1174 * Write folder and associated references to addresses to file.
1175 * file hash table visitor function.
1176 * \param key Table key.
1177 * \param value Reference to folder.
1178 * \param data File pointer.
1180 static void addrbook_write_item_folder_vis(gpointer key, gpointer value,
1181 gpointer d)
1183 AddrItemObject *obj = (AddrItemObject *) value;
1184 HashLoopData *data = (HashLoopData *)d;
1185 FILE *fp = data->fp;
1186 GList *node;
1188 if (!obj)
1189 return;
1190 if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) {
1191 ItemFolder *folder = (ItemFolder *) value;
1192 if (folder) {
1193 if (addrbook_write_elem_s(fp, 1, AB_ELTAG_FOLDER) < 0)
1194 data->error = TRUE;
1195 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(folder)) < 0)
1196 data->error = TRUE;
1197 if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder)) < 0)
1198 data->error = TRUE;
1199 if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks) < 0)
1200 data->error = TRUE;
1201 if (claws_fputs(" >\n", fp) == EOF)
1202 data->error = TRUE;
1203 if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1204 data->error = TRUE;
1205 if (claws_fputs(">\n", fp) == EOF)
1206 data->error = TRUE;
1208 /* Output persons */
1209 node = folder->listPerson;
1210 while (node) {
1211 ItemPerson *item = node->data;
1212 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1213 data->error = TRUE;
1214 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_PERSON) < 0)
1215 data->error = TRUE;
1216 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1217 data->error = TRUE;
1218 if (claws_fputs(" />\n", fp) == EOF)
1219 data->error = TRUE;
1220 node = g_list_next(node);
1223 /* Output groups */
1224 node = folder->listGroup;
1225 while (node) {
1226 ItemGroup *item = node->data;
1227 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1228 data->error = TRUE;
1229 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP) < 0)
1230 data->error = TRUE;
1231 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1232 data->error = TRUE;
1233 if (claws_fputs(" />\n", fp) == EOF)
1234 data->error = TRUE;
1235 node = g_list_next(node);
1238 /* Output folders */
1239 node = folder->listFolder;
1240 while (node) {
1241 ItemFolder *item = node->data;
1242 if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1243 data->error = TRUE;
1244 if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER) < 0)
1245 data->error = TRUE;
1246 if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1247 data->error = TRUE;
1248 if (claws_fputs(" />\n", fp) == EOF)
1249 data->error = TRUE;
1250 node = g_list_next(node);
1252 if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1253 data->error = TRUE;
1254 if (addrbook_write_elem_e(fp, 1, AB_ELTAG_FOLDER) < 0)
1255 data->error = TRUE;
1261 * Output address book data to specified file.
1262 * \param book Address book.
1263 * \param newFile Filename of new file (in book's filepath).
1264 * \return Status code.
1266 static gint addrbook_write_to(AddressBookFile *book, gchar *newFile)
1268 FILE *fp;
1269 gchar *fileSpec;
1270 HashLoopData data;
1271 #ifndef DEV_STANDALONE
1272 PrefFile *pfile;
1273 #endif
1275 cm_return_val_if_fail(book != NULL, -1);
1276 cm_return_val_if_fail(newFile != NULL, -1);
1278 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, newFile, NULL);
1280 book->retVal = MGU_OPEN_FILE;
1281 #ifdef DEV_STANDALONE
1282 fp = claws_fopen(fileSpec, "wb");
1283 g_free(fileSpec);
1284 if (fp) {
1285 if (claws_fputs("<?xml version=\"1.0\" ?>\n", fp) == EOF) {
1286 book->retVal = MGU_ERROR_WRITE;
1287 return book->retVal;
1289 #else
1290 pfile = prefs_write_open(fileSpec);
1291 g_free(fileSpec);
1292 if (pfile) {
1293 fp = pfile->fp;
1294 if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
1295 goto fail;
1296 #endif
1297 if (addrbook_write_elem_s(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1298 goto fail;
1299 if (addrbook_write_attr(fp, AB_ATTAG_NAME,
1300 addrcache_get_name(book->addressCache)) < 0)
1301 goto fail;
1302 if (claws_fputs(" >\n", fp) == EOF)
1303 goto fail;
1305 /* Output all persons */
1306 data.fp = fp;
1307 data.error = FALSE;
1309 g_hash_table_foreach(book->addressCache->itemHash,
1310 addrbook_write_item_person_vis, &data);
1311 if (data.error)
1312 goto fail;
1314 /* Output all groups */
1315 g_hash_table_foreach(book->addressCache->itemHash,
1316 addrbook_write_item_group_vis, &data);
1318 if (data.error)
1319 goto fail;
1321 /* Output all folders */
1322 g_hash_table_foreach(book->addressCache->itemHash,
1323 addrbook_write_item_folder_vis, &data);
1325 if (data.error)
1326 goto fail;
1328 if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1329 goto fail;
1331 book->retVal = MGU_SUCCESS;
1332 #ifdef DEV_STANDALONE
1333 claws_safe_fclose(fp);
1334 #else
1335 if (prefs_file_close( pfile ) < 0)
1336 book->retVal = MGU_ERROR_WRITE;
1337 #endif
1340 fileSpec = NULL;
1341 return book->retVal;
1342 fail:
1343 g_warning("error writing AB");
1344 book->retVal = MGU_ERROR_WRITE;
1345 if (pfile)
1346 prefs_file_close_revert( pfile );
1347 return book->retVal;
1351 * Output address book data to original file.
1352 * \param book Address book.
1353 * \return Status code.
1355 gint addrbook_save_data(AddressBookFile *book)
1357 cm_return_val_if_fail(book != NULL, -1);
1359 book->retVal = MGU_NO_FILE;
1360 if (book->fileName == NULL || *book->fileName == '\0')
1361 return book->retVal;
1362 if (book->path == NULL || *book->path == '\0')
1363 return book->retVal;
1365 addrbook_write_to(book, book->fileName);
1366 if (book->retVal == MGU_SUCCESS)
1367 addrcache_set_dirty(book->addressCache, FALSE);
1368 return book->retVal;
1372 * **********************************************************************
1373 * Address book edit interface functions.
1374 * **********************************************************************
1378 * Hash table callback function for simple deletion of hashtable entries.
1379 * \param key Table key (will be freed).
1380 * \param value Value stored in table.
1381 * \param data User data.
1382 * \return <i>TRUE</i> to indicate that entry freed.
1384 static gboolean addrbook_free_simple_hash_vis(gpointer *key, gpointer *value,
1385 gpointer *data)
1387 g_free(key);
1388 key = NULL;
1389 value = NULL;
1390 return TRUE;
1394 * Update address book email list for specified person. Note: The existing
1395 * email addresses are replaced with the new addresses. Any references to
1396 * old addresses in the groups are re-linked to the new addresses. All old
1397 * addresses linked to the person are removed.
1398 * \param book Address book.
1399 * \param person Person to update.
1400 * \param listEMail List of new email addresses.
1402 void addrbook_update_address_list(AddressBookFile *book, ItemPerson *person,
1403 GList *listEMail)
1405 GList *node;
1406 GList *listDelete;
1407 GList *listGroup;
1409 cm_return_if_fail(book != NULL);
1410 cm_return_if_fail(person != NULL);
1412 /* Get groups where person's existing email addresses are listed */
1413 listGroup = addrcache_get_group_for_person(book->addressCache, person);
1414 if (listGroup) {
1415 GHashTable *hashEMail;
1416 GHashTable *hashEMailAlias;
1417 GList *nodeGrp;
1419 /* Load hash table with new address entries */
1420 hashEMail = g_hash_table_new(g_str_hash, g_str_equal);
1421 hashEMailAlias = g_hash_table_new(g_str_hash, g_str_equal);
1422 node = listEMail;
1423 while (node) {
1424 ItemEMail *email = node->data;
1425 gchar *alias = email->obj.name ;
1426 gchar *addr = g_utf8_strdown(email->address, -1);
1427 if (!g_hash_table_lookup(hashEMail, addr)) {
1428 g_hash_table_insert(hashEMail, addr, email);
1430 if (*alias != '\0' && ! g_hash_table_lookup(hashEMailAlias,
1431 alias))
1432 g_hash_table_insert(hashEMailAlias, alias, email);
1434 node = g_list_next(node);
1437 /* Re-parent new addresses to existing groups, where email address match. */
1438 nodeGrp = listGroup;
1439 while (nodeGrp) {
1440 ItemGroup *group = (ItemGroup *) nodeGrp->data;
1441 GList *groupEMail = group->listEMail;
1442 GList *nodeGrpEM;
1443 GList *listRemove = NULL;
1445 /* Process each email item linked to group */
1446 nodeGrpEM = groupEMail;
1447 while (nodeGrpEM) {
1448 ItemEMail *emailGrp = (ItemEMail *) nodeGrpEM->data;
1450 if (ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person)) {
1451 /* Found an email address for this person */
1452 ItemEMail *emailNew = NULL;
1453 gchar *alias = emailGrp->obj.name;
1454 gchar *addr = g_utf8_strdown(emailGrp->address, -1);
1455 emailNew = (ItemEMail *)
1456 g_hash_table_lookup(hashEMail, addr);
1457 g_free( addr );
1458 /* If no match by e-mail, try to match by e-mail alias */
1459 if (!emailNew && *alias != '\0') {
1460 emailNew = (ItemEMail *)
1461 g_hash_table_lookup(hashEMailAlias, alias);
1464 if (emailNew)
1465 /* Point to this entry */
1466 nodeGrpEM->data = emailNew;
1467 else if (g_hash_table_size(hashEMail)==1)
1468 /* If the person has just one e-mail address, then
1469 change e-mail address in group list */
1470 nodeGrpEM->data = listEMail->data;
1471 else
1472 /* Mark for removal */
1473 listRemove = g_list_append(listRemove, emailGrp);
1475 /* Move on to next email link */
1476 nodeGrpEM = g_list_next(nodeGrpEM);
1479 /* Process all removed links in current group */
1480 nodeGrpEM = listRemove;
1481 while (nodeGrpEM) {
1482 ItemEMail *emailGrp = nodeGrpEM->data;
1483 groupEMail = g_list_remove(groupEMail, emailGrp);
1484 nodeGrpEM = g_list_next(nodeGrpEM);
1487 g_list_free(listRemove);
1489 /* Move on to next group */
1490 nodeGrp = g_list_next(nodeGrp);
1493 /* Clear hash table */
1494 g_hash_table_foreach_remove(hashEMail, (GHRFunc)
1495 addrbook_free_simple_hash_vis, NULL);
1496 g_hash_table_destroy(hashEMail);
1497 hashEMail = NULL;
1498 g_hash_table_destroy(hashEMailAlias);
1499 hashEMailAlias = NULL;
1500 g_list_free(listGroup);
1501 listGroup = NULL;
1503 /* Remove old addresses from person and cache */
1504 listDelete = NULL;
1505 node = person->listEMail;
1506 while (node) {
1507 ItemEMail *email = node->data;
1509 if (addrcache_person_remove_email(book->addressCache, person, email))
1510 addrcache_remove_email(book->addressCache, email);
1512 listDelete = g_list_append(listDelete, email);
1513 node = person->listEMail;
1515 /* Add new address entries */
1516 node = listEMail;
1517 while (node) {
1518 ItemEMail *email = node->data;
1520 if (ADDRITEM_ID(email) == NULL)
1521 /* Allocate an ID for new address */
1522 addrcache_id_email(book->addressCache, email);
1524 addrcache_person_add_email( book->addressCache, person, email );
1525 node = g_list_next( node );
1528 addrcache_set_dirty(book->addressCache, TRUE);
1530 /* Free up memory */
1531 g_list_free(listEMail);
1532 listEMail = NULL;
1534 node = listDelete;
1535 while (node) {
1536 ItemEMail *email = node->data;
1538 addritem_free_item_email(email);
1539 node = g_list_next(node);
1541 g_list_free(listDelete);
1542 listDelete = NULL;
1547 * Create person object and add person with specified address data to address
1548 * book. Note: A new person is created with specified list of email addresses.
1549 * All objects inserted into address book.
1551 * \param book Address book.
1552 * \param folder Parent folder where to add person, or <i>NULL</i> for
1553 * root folder.
1554 * \param listEMail List of new email addresses to associate with person.
1555 * \return Person object created.
1557 ItemPerson *addrbook_add_address_list(AddressBookFile *book, ItemFolder *folder,
1558 GList *listEMail)
1560 ItemPerson *person;
1561 ItemFolder *f = folder;
1562 GList *node;
1564 cm_return_val_if_fail(book != NULL, NULL);
1566 if (!f)
1567 f = book->addressCache->rootFolder;
1568 person = addritem_create_item_person();
1569 addrcache_id_person(book->addressCache, person);
1570 addrcache_folder_add_person(book->addressCache, f, person);
1572 node = listEMail;
1573 while (node) {
1574 ItemEMail *email = node->data;
1575 if (ADDRITEM_ID(email) == NULL)
1576 addrcache_id_email(book->addressCache, email);
1578 addrcache_person_add_email(book->addressCache, person, email);
1579 node = g_list_next(node);
1581 return person;
1585 * Build available email list visitor function.
1586 * \param key Table key.
1587 * \param value Value stored in table.
1588 * \param data Reference to address book.
1590 static void addrbook_build_avail_email_vis(gpointer key, gpointer value,
1591 gpointer data)
1593 AddrItemObject *obj = (AddrItemObject *) value;
1595 if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1596 AddressBookFile *book = data;
1597 ItemPerson *person = (ItemPerson *) obj;
1598 GList *node = person->listEMail;
1599 while (node) {
1600 ItemEMail *email = node->data;
1601 /* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */
1603 if (!g_hash_table_lookup(book->tempHash,
1604 ADDRITEM_ID(email)))
1605 book->tempList = g_list_append(book->tempList, email);
1607 node = g_list_next(node);
1613 * Return link list of available email items that have not already been linked
1614 * to groups. Note that the list contains references to items and should be
1615 * <code>g_list_free()</code> when done. Do <b>*NOT*</b> attempt to used the
1616 * <code>addrbook_free_xxx()<code> functions... this will destroy the
1617 * addressbook data!
1619 * \param book Address book.
1620 * \param group Group to process.
1621 * \return List of items, or <i>NULL</i> if none.
1623 GList *addrbook_get_available_email_list(AddressBookFile *book, ItemGroup *group)
1625 GList *list = NULL;
1626 GHashTable *table;
1628 cm_return_val_if_fail(book != NULL, NULL);
1630 /* Load hash table with group email entries */
1631 table = g_hash_table_new(g_str_hash, g_str_equal);
1632 if (group) {
1633 list = group->listEMail;
1634 while (list) {
1635 ItemEMail *email = list->data;
1636 g_hash_table_insert(table, ADDRITEM_ID(email), email);
1637 list = g_list_next(list);
1641 /* Build list of available email addresses which exclude those already in groups */
1642 book->tempList = NULL;
1643 book->tempHash = table;
1644 g_hash_table_foreach(book->addressCache->itemHash,
1645 addrbook_build_avail_email_vis, book);
1646 list = book->tempList;
1647 book->tempList = NULL;
1648 book->tempHash = NULL;
1650 /* Clear hash table */
1651 g_hash_table_destroy(table);
1652 table = NULL;
1654 return list;
1658 * Update address book email list for specified group. Note: The existing email
1659 * addresses are replaced with the new addresses. Any references to old addresses
1660 * in the groups are re-linked to the new addresses. All old addresses linked to
1661 * the person are removed.
1663 * \param book Address book.
1664 * \param group Group to process.
1665 * \param listEMail List of email items. This should <b>*NOT*</b> be
1666 * <code>g_free()</code> when done.
1668 void addrbook_update_group_list(AddressBookFile *book, ItemGroup *group,
1669 GList *listEMail)
1671 GList *oldData;
1673 cm_return_if_fail(book != NULL);
1674 cm_return_if_fail(group != NULL);
1676 addrcache_set_dirty(book->addressCache, TRUE);
1678 /* Remember old list */
1679 oldData = group->listEMail;
1680 group->listEMail = listEMail;
1681 g_list_free(oldData);
1685 * Create group object and add with specifed list of email addresses to
1686 * address book. Note: The existing email addresses are replaced with the new
1687 * addresses. Any references to old addresses in the groups are re-linked to
1688 * the new addresses. All old addresses linked to the person are removed.
1690 * \param book Address book.
1691 * \param folder Parent folder where to add group, or <i>NULL</i> for
1692 * root folder.
1693 * \param listEMail List of email items. This should <b>*NOT*</b> be
1694 * <code>g_free()</code> when done.
1695 * \return Group object created.
1697 ItemGroup *addrbook_add_group_list(AddressBookFile *book, ItemFolder *folder,
1698 GList *listEMail)
1700 ItemGroup *group = NULL;
1701 ItemFolder *f = folder;
1703 cm_return_val_if_fail(book != NULL, NULL);
1705 if (!f)
1706 f = book->addressCache->rootFolder;
1707 group = addritem_create_item_group();
1708 addrcache_id_group(book->addressCache, group);
1709 addrcache_folder_add_group(book->addressCache, f, group);
1710 group->listEMail = listEMail;
1711 return group;
1715 * Create a new folder and add to address book.
1716 * \param book Address book.
1717 * \param folder Parent folder where to add folder, or <i>NULL</i> for
1718 * root folder.
1719 * \return Folder that was created. This should <b>*NOT*</b> be
1720 * <code>g_free()</code> when done.
1722 ItemFolder *addrbook_add_new_folder(AddressBookFile *book, ItemFolder *parent)
1724 cm_return_val_if_fail(book != NULL, NULL);
1725 return addrcache_add_new_folder( book->addressCache, parent );
1729 * Update address book attribute list for specified person. Note: The existing
1730 * attributes are replaced with the new addresses. All old attributes linked
1731 * to the person are removed.
1733 * \param book Address book.
1734 * \param person Person to receive attributes.
1735 * \param listAttrib New list of attributes.
1737 void addrbook_update_attrib_list(AddressBookFile *book, ItemPerson *person,
1738 GList *listAttrib)
1740 GList *node;
1741 GList *oldData;
1743 cm_return_if_fail(book != NULL);
1744 cm_return_if_fail(person != NULL);
1746 /* Remember old list */
1747 oldData = person->listAttrib;
1749 /* Attach new address list to person. */
1750 node = listAttrib;
1751 while (node) {
1752 UserAttribute *attrib = node->data;
1753 if (attrib->uid == NULL) {
1754 /* Allocate an ID */
1755 addrcache_id_attribute(book->addressCache, attrib);
1757 node = g_list_next(node);
1759 person->listAttrib = listAttrib;
1760 addrcache_set_dirty(book->addressCache, TRUE);
1762 /* Free up old data */
1763 addritem_free_list_attribute(oldData);
1764 oldData = NULL;
1768 * Add attribute data for specified person to address book. Note: Only
1769 * attributes are inserted into address book.
1770 * \param book Address book.
1771 * \param person Person to receive attributes.
1772 * \param listAttrib List of attributes.
1774 void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1775 GList *node;
1777 cm_return_if_fail( book != NULL );
1778 cm_return_if_fail( person != NULL );
1780 node = listAttrib;
1781 while( node ) {
1782 UserAttribute *attrib = node->data;
1783 if( attrib->uid == NULL ) {
1784 addrcache_id_attribute( book->addressCache, attrib );
1786 addritem_person_add_attribute( person, attrib );
1787 node = g_list_next( node );
1789 addrcache_set_dirty( book->addressCache, TRUE );
1792 #define WORK_BUFLEN 1024
1793 #define ADDRBOOK_DIGITS "0123456789"
1796 * Return list of existing address book files.
1797 * \param book Address book.
1798 * \return List of files (as strings).
1800 GList *addrbook_get_bookfile_list(AddressBookFile *book) {
1801 gchar *adbookdir;
1802 GDir *dir;
1803 const gchar *dir_name;
1804 GStatBuf statbuf;
1805 gchar buf[WORK_BUFLEN + 1];
1806 gchar numbuf[WORK_BUFLEN];
1807 gint len, lenpre, lensuf, lennum;
1808 long int val, maxval;
1809 GList *fileList = NULL;
1811 cm_return_val_if_fail(book != NULL, NULL);
1813 if (book->path == NULL || *book->path == '\0') {
1814 book->retVal = MGU_NO_PATH;
1815 return NULL;
1818 strncpy(buf, book->path, WORK_BUFLEN);
1819 len = strlen(buf);
1820 if (len > 0) {
1821 if (buf[len-1] != G_DIR_SEPARATOR) {
1822 buf[len] = G_DIR_SEPARATOR;
1823 buf[++len] = '\0';
1827 adbookdir = g_strdup(buf);
1828 strncat(buf, ADDRBOOK_PREFIX, WORK_BUFLEN - strlen(buf));
1830 if( ( dir = g_dir_open( adbookdir, 0, NULL ) ) == NULL ) {
1831 book->retVal = MGU_OPEN_DIRECTORY;
1832 g_free(adbookdir);
1833 return NULL;
1836 lenpre = strlen(ADDRBOOK_PREFIX);
1837 lensuf = strlen(ADDRBOOK_SUFFIX);
1838 lennum = FILE_NUMDIGITS + lenpre;
1839 maxval = -1;
1841 while( ( dir_name = g_dir_read_name( dir ) ) != NULL ) {
1842 gchar *endptr = NULL;
1843 gint i, r;
1844 gboolean flg;
1846 strncpy(buf, adbookdir, WORK_BUFLEN);
1847 strncat(buf, dir_name, WORK_BUFLEN - strlen(buf));
1848 r = g_stat(buf, &statbuf);
1849 if (r == 0 && S_ISREG(statbuf.st_mode)) {
1850 if (strncmp(
1851 dir_name,
1852 ADDRBOOK_PREFIX, lenpre) == 0)
1854 if (strncmp(
1855 (dir_name) + lennum,
1856 ADDRBOOK_SUFFIX, lensuf) == 0)
1858 strncpy(numbuf,
1859 (dir_name) + lenpre,
1860 FILE_NUMDIGITS);
1861 numbuf[FILE_NUMDIGITS] = '\0';
1862 flg = TRUE;
1863 for(i = 0; i < FILE_NUMDIGITS; i++) {
1864 if(!strchr(ADDRBOOK_DIGITS, numbuf[i])) {
1865 flg = FALSE;
1866 break;
1869 if (flg) {
1870 /* Get value */
1871 val = strtol(numbuf, &endptr, 10);
1872 if (endptr && val > -1) {
1873 if (val > maxval) maxval = val;
1874 fileList = g_list_append(
1875 fileList,
1876 g_strdup(dir_name));
1883 g_dir_close( dir );
1884 g_free(adbookdir);
1886 book->maxValue = maxval;
1887 book->retVal = MGU_SUCCESS;
1888 return fileList;
1892 * Return file name for specified file number.
1893 * \param fileNum File number.
1894 * \return File name, or <i>NULL</i> if file number too large. Should be
1895 * <code>g_free()</code> when done.
1897 gchar *addrbook_gen_new_file_name(gint fileNum) {
1898 gchar fmt[30];
1899 gchar buf[WORK_BUFLEN];
1900 gint n = fileNum;
1901 long int nmax;
1903 if (n < 1)
1904 n = 1;
1905 nmax = -1 + (long int) pow(10, FILE_NUMDIGITS);
1906 if (fileNum > nmax)
1907 return NULL;
1908 g_snprintf(fmt, sizeof(fmt), "%%s%%0%dd%%s", FILE_NUMDIGITS);
1909 g_snprintf(buf, sizeof(buf), fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX);
1910 return g_strdup(buf);
1914 * **********************************************************************
1915 * Address book test functions...
1916 * **********************************************************************
1920 * Attempt to parse list of email address from file.
1921 * \param book Address book.
1922 * \param file XML file handle.
1924 static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file )
1926 guint prev_level;
1927 /* GList *attr; */
1929 for (;;) {
1930 prev_level = file->level;
1931 if (xml_parse_next_tag(file))
1932 longjmp(book->jumper, 1);
1933 if (file->level < prev_level)
1934 return;
1935 /* attr = xml_get_current_tag_attr(file); */
1936 /* addrbook_show_attribs( attr ); */
1937 if (xml_compare_tag(file, AB_ELTAG_ADDRESS))
1938 addrbook_chkparse_addr_list(book, file);
1943 * Attempt to parse attributes for person address from file.
1944 * \param book Address book.
1945 * \param file XML file handle.
1947 static void addrbook_chkparse_attribute(AddressBookFile *book, XMLFile *file)
1949 /* GList *attr; */
1950 /* gchar *element; */
1952 /* attr = xml_get_current_tag_attr(file); */
1953 /* addrbook_show_attribs( attr ); */
1954 /* element = xml_get_element(file); */
1955 /* g_print( "\t\tattrib value : %s\n", element ); */
1959 * Attempt to parse list of attributes for person address from file.
1960 * \param book Address book.
1961 * \param file XML file handle.
1963 static void addrbook_chkparse_attr_list(AddressBookFile *book, XMLFile *file)
1965 guint prev_level;
1967 for (;;) {
1968 prev_level = file->level;
1969 if (xml_parse_next_tag(file))
1970 longjmp(book->jumper, 1);
1971 if (file->level < prev_level)
1972 return;
1973 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
1974 addrbook_chkparse_attribute(book, file);
1975 addrbook_chkparse_attr_list(book, file);
1981 * Attempt to parse person from file.
1982 * \param book Address book.
1983 * \param file XML file handle.
1985 static void addrbook_chkparse_person(AddressBookFile *book, XMLFile *file)
1987 /* GList *attr; */
1989 /* attr = xml_get_current_tag_attr(file); */
1990 /* addrbook_show_attribs( attr ); */
1991 if (xml_parse_next_tag(file)) /* Consume closing tag */
1992 longjmp(book->jumper, 1);
1994 if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST))
1995 addrbook_chkparse_addr_list(book, file);
1997 if (xml_parse_next_tag(file)) /* Consume closing tag */
1998 longjmp(book->jumper, 1);
2000 if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST))
2001 addrbook_chkparse_attr_list(book, file);
2005 * Attempt to parse list of members from file.
2006 * \param book Address book.
2007 * \param file XML file handle.
2009 static void addrbook_chkparse_member_list(AddressBookFile *book, XMLFile *file)
2011 /* GList *attr; */
2012 guint prev_level;
2014 for (;;) {
2015 prev_level = file->level;
2016 if (xml_parse_next_tag(file))
2017 longjmp(book->jumper, 1);
2019 if (file->level < prev_level)
2020 return;
2022 if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
2023 /* attr = xml_get_current_tag_attr(file); */
2024 /* addrbook_show_attribs( attr ); */
2025 addrbook_chkparse_member_list(book, file);
2027 else {
2028 /* attr = xml_get_current_tag_attr(file); */
2029 /* addrbook_show_attribs( attr ); */
2035 * Attempt to parse group from file.
2036 * \param book Address book.
2037 * \param file XML file handle.
2039 static void addrbook_chkparse_group(AddressBookFile *book, XMLFile *file)
2041 /* GList *attr; */
2043 /* attr = xml_get_current_tag_attr(file); */
2044 /* addrbook_show_attribs( attr ); */
2045 if (xml_parse_next_tag(file)) /* Consume closing tag */
2046 longjmp(book->jumper, 1);
2048 if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST))
2049 addrbook_chkparse_member_list(book, file);
2053 * Attempt to parse list of folders from file.
2054 * \param book Address book.
2055 * \param file XML file handle.
2057 static void addrbook_chkparse_folder_list(AddressBookFile *book, XMLFile *file)
2059 /* GList *attr; */
2060 guint prev_level;
2062 for (;;) {
2063 prev_level = file->level;
2064 if (xml_parse_next_tag(file))
2065 longjmp(book->jumper, 1);
2067 if (file->level < prev_level)
2068 return;
2070 if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
2071 /* attr = xml_get_current_tag_attr(file); */
2072 /* addrbook_show_attribs( attr ); */
2073 addrbook_chkparse_folder_list(book, file);
2075 else {
2076 /* attr = xml_get_current_tag_attr(file); */
2077 /* addrbook_show_attribs( attr ); */
2083 * Attempt to parse a folder from file.
2084 * \param book Address book.
2085 * \param file XML file handle.
2087 static void addrbook_chkparse_folder(AddressBookFile *book, XMLFile *file)
2089 /* GList *attr; */
2091 /* attr = xml_get_current_tag_attr(file); */
2092 /* addrbook_show_attribs( attr ); */
2093 if (xml_parse_next_tag(file)) /* Consume closing tag */
2094 longjmp(book->jumper, 1);
2096 if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST))
2097 addrbook_chkparse_folder_list(book, file);
2101 * Attempt to parse (DOM) tree from file.
2102 * \param book Address book.
2103 * \param file XML file handle.
2105 static gboolean addrbook_chkread_tree(AddressBookFile *book, XMLFile *file)
2107 /* GList *attr; */
2108 gboolean retVal;
2110 if (xml_get_dtd(file))
2111 return FALSE;
2113 if (xml_parse_next_tag(file))
2114 return FALSE;
2116 if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
2117 return FALSE;
2119 /* attr = xml_get_current_tag_attr(file); */
2120 /* addrbook_show_attribs( attr ); */
2122 retVal = TRUE;
2123 for (;;) {
2124 if (!file->level)
2125 break;
2126 /* Get item tag */
2127 if (xml_parse_next_tag(file))
2128 longjmp(book->jumper, 1);
2130 /* Get next tag (person, group or folder) */
2131 if (xml_compare_tag(file, AB_ELTAG_PERSON))
2132 addrbook_chkparse_person( book, file );
2133 else if (xml_compare_tag(file, AB_ELTAG_GROUP))
2134 addrbook_chkparse_group(book, file);
2135 else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
2136 addrbook_chkparse_folder(book, file);
2138 return retVal;
2142 * Test address book file by parsing contents.
2143 * \param book Address book.
2144 * \param fileName Filename of XML file.
2145 * \return Status code <i>MGU_SUCCESS</i> if file appears to be valid format.
2147 gint addrbook_test_read_file(AddressBookFile *book, gchar *fileName)
2149 XMLFile *file = NULL;
2150 gchar *fileSpec = NULL;
2152 cm_return_val_if_fail(book != NULL, -1);
2154 fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, fileName, NULL);
2155 book->retVal = MGU_OPEN_FILE;
2156 file = xml_open_file(fileSpec);
2157 g_free(fileSpec);
2158 if (file) {
2159 book->retVal = MGU_BAD_FORMAT;
2160 if (setjmp(book->jumper)) {
2161 /* g_print( "Caught Ya!!!\n" ); */
2162 xml_close_file(file);
2163 return book->retVal;
2165 if (addrbook_chkread_tree(book, file))
2166 book->retVal = MGU_SUCCESS;
2168 xml_close_file( file );
2170 return book->retVal;
2174 * Return link list of all persons in address book. Note that the list
2175 * contains references to items. Do <b>*NOT*</b> attempt to use the
2176 * <code>addrbook_free_xxx()</code> functions... this will destroy the
2177 * addressbook data!
2178 * \param book Address book.
2179 * \return List of persons, or NULL if none.
2181 GList *addrbook_get_all_persons(AddressBookFile *book)
2183 cm_return_val_if_fail(book != NULL, NULL);
2184 return addrcache_get_all_persons(book->addressCache);
2187 GList *addrbook_get_all_groups(AddressBookFile *book)
2189 cm_return_val_if_fail(book != NULL, NULL);
2190 return addrcache_get_all_groups(book->addressCache);
2194 * Add person and address data to address book.
2195 * \param book Address book.
2196 * \param folder Folder where to add person, or NULL for root folder.
2197 * \param name Common name.
2198 * \param address EMail address.
2199 * \param remarks Remarks.
2200 * \return Person added. Do not <b>*NOT*</b> to use the
2201 * <code>addrbook_free_xxx()</code> functions... this will destroy
2202 * the address book data.
2204 ItemPerson *addrbook_add_contact(AddressBookFile *book, ItemFolder *folder,
2205 const gchar *name,const gchar *address,
2206 const gchar *remarks)
2208 ItemPerson *person;
2210 cm_return_val_if_fail(book != NULL, NULL);
2211 person = addrcache_add_contact(
2212 book->addressCache, folder, name, address, remarks );
2213 return person;
2217 * Return file name for next address book file.
2218 * \param book Address book.
2219 * \return File name, or <i>NULL</i> if could not create. This should be
2220 * <code>g_free()</code> when done.
2222 gchar *addrbook_guess_next_file(AddressBookFile *book)
2224 gchar *newFile = NULL;
2225 GList *fileList = NULL;
2226 gint fileNum = 1;
2227 fileList = addrbook_get_bookfile_list(book);
2228 if (fileList)
2229 fileNum = 1 + book->maxValue;
2231 newFile = addrbook_gen_new_file_name(fileNum);
2232 g_list_free(fileList);
2233 fileList = NULL;
2234 return newFile;
2237 void addrbook_delete_book_file(AddressBookFile *book)
2239 gchar *book_path;
2241 if (!book->path || !book->fileName)
2242 return;
2244 book_path = g_strconcat(book->path, G_DIR_SEPARATOR_S,
2245 book->fileName, NULL);
2246 claws_unlink(book_path);
2247 g_free(book_path);
2251 * End of Source.