1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2012 Match Grun and the Claws Mail team
4  *
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.
9  *
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.
14  *
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/>.
17  *
18  */
19 
20 /* General functions for accessing address book files */
21 
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #include "claws-features.h"
25 #endif
26 
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>
33 
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"
42 
43 #ifndef DEV_STANDALONE
44 #include "prefs_gtk.h"
45 #include "codeconv.h"
46 #endif
47 
48 #define ADDRBOOK_MAX_SEARCH_COUNT 1000
49 #define ADDRBOOK_PREFIX           "addrbook-"
50 #define ADDRBOOK_SUFFIX           ".xml"
51 #define FILE_NUMDIGITS            6
52 
53 #define ID_TIME_OFFSET            998000000
54 
55 static void addrbook_print_book		( AddressBookFile *book, FILE *stream );
56 
57 /**
58  * Create new address book
59  * \return Address book.
60  */
addrbook_create_book()61 AddressBookFile *addrbook_create_book()
62 {
63 	AddressBookFile *book;
64 
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;
75 
76 	return book;
77 }
78 
79 /**
80  * Specify name to be used
81  * \param book  Address book.
82  * \param value Name.
83  */
addrbook_set_name(AddressBookFile * book,const gchar * value)84 void addrbook_set_name(AddressBookFile *book, const gchar *value)
85 {
86 	cm_return_if_fail(book != NULL);
87 	addrcache_set_name(book->addressCache, value);
88 }
89 
addrbook_get_name(AddressBookFile * book)90 gchar *addrbook_get_name(AddressBookFile *book)
91 {
92 	cm_return_val_if_fail(book != NULL, NULL);
93 	return addrcache_get_name(book->addressCache);
94 }
95 
96 /**
97  * Specify path to address book file.
98  * \param book  Address book.
99  * \param value Path.
100  */
addrbook_set_path(AddressBookFile * book,const gchar * value)101 void addrbook_set_path(AddressBookFile *book, const gchar *value)
102 {
103 	cm_return_if_fail(book != NULL);
104 	book->path = mgu_replace_string(book->path, value);
105 	addrcache_set_dirty(book->addressCache, TRUE);
106 }
107 
108 /**
109  * Specify filename to be used
110  * \param book  Address book.
111  * \param value Filename.
112  */
addrbook_set_file(AddressBookFile * book,const gchar * value)113 void addrbook_set_file(AddressBookFile *book, const gchar *value)
114 {
115 	cm_return_if_fail(book != NULL);
116 	book->fileName = mgu_replace_string(book->fileName, value);
117 	addrcache_set_dirty(book->addressCache, TRUE);
118 }
119 
addrbook_get_modified(AddressBookFile * book)120 gboolean addrbook_get_modified(AddressBookFile *book)
121 {
122 	cm_return_val_if_fail(book != NULL, FALSE);
123 	return book->addressCache->modified;
124 }
125 
addrbook_get_accessed(AddressBookFile * book)126 gboolean addrbook_get_accessed(AddressBookFile *book)
127 {
128 	cm_return_val_if_fail(book != NULL, FALSE);
129 	return book->addressCache->accessFlag;
130 }
131 
132 /**
133  * Specify address book as accessed.
134  * \param book  Address book.
135  * \param value Value.
136  */
addrbook_set_accessed(AddressBookFile * book,const gboolean value)137 void addrbook_set_accessed(AddressBookFile *book, const gboolean value)
138 {
139 	cm_return_if_fail(book != NULL);
140 	book->addressCache->accessFlag = value;
141 }
142 
addrbook_get_read_flag(AddressBookFile * book)143 gboolean addrbook_get_read_flag(AddressBookFile *book)
144 {
145 	cm_return_val_if_fail(book != NULL, FALSE);
146 	return book->addressCache->dataRead;
147 }
148 
addrbook_get_status(AddressBookFile * book)149 gint addrbook_get_status(AddressBookFile *book)
150 {
151 	cm_return_val_if_fail(book != NULL, -1);
152 	return book->retVal;
153 }
154 
addrbook_get_root_folder(AddressBookFile * book)155 ItemFolder *addrbook_get_root_folder(AddressBookFile *book)
156 {
157 	cm_return_val_if_fail(book != NULL, NULL);
158 	return addrcache_get_root_folder(book->addressCache);
159 }
160 
addrbook_get_list_folder(AddressBookFile * book)161 GList *addrbook_get_list_folder(AddressBookFile *book)
162 {
163 	cm_return_val_if_fail(book != NULL, NULL);
164 	return addrcache_get_list_folder(book->addressCache);
165 }
166 
addrbook_get_list_person(AddressBookFile * book)167 GList *addrbook_get_list_person(AddressBookFile *book)
168 {
169 	cm_return_val_if_fail(book != NULL, NULL);
170 	return addrcache_get_list_person(book->addressCache);
171 }
172 
addrbook_get_dirty(AddressBookFile * book)173 gboolean addrbook_get_dirty(AddressBookFile *book)
174 {
175 	cm_return_val_if_fail(book != NULL, FALSE);
176 	return addrcache_get_dirty(book->addressCache);
177 }
178 
179 /**
180  * Set address book as dirty (needs to be written to file).
181  * \param book  Address book.
182  * \param value Dirty flag.
183  */
addrbook_set_dirty(AddressBookFile * book,const gboolean value)184 void addrbook_set_dirty(AddressBookFile *book, const gboolean value)
185 {
186 	cm_return_if_fail(book != NULL);
187 	addrcache_set_dirty(book->addressCache, value);
188 }
189 
190 /**
191  * Free address book.
192  * \param book Address book.
193  */
addrbook_free_book(AddressBookFile * book)194 void addrbook_free_book(AddressBookFile *book)
195 {
196 	cm_return_if_fail(book != NULL);
197 
198 	/* Clear cache */
199 	addrcache_free(book->addressCache);
200 
201 	/* Free up internal objects */
202 	g_free(book->path);
203 	g_free(book->fileName);
204 	g_list_free(book->tempList);
205 
206 	book->path = NULL;
207 	book->fileName = NULL;
208 	book->maxValue = 0;
209 	book->tempList = NULL;
210 	book->tempHash = NULL;
211 
212 	book->type = ADBOOKTYPE_NONE;
213 	book->addressCache = NULL;
214 	book->retVal = MGU_SUCCESS;
215 
216 	g_free(book);
217 }
218 
219 /**
220  * Print address book header.
221  * \param book   Address book.
222  * \param stream Output stream.
223  */
addrbook_print_book(AddressBookFile * book,FILE * stream)224 static void addrbook_print_book(AddressBookFile *book, FILE *stream)
225 {
226 	cm_return_if_fail(book != NULL);
227 
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);
233 }
234 
235 /**
236  * Dump entire address book traversing folders.
237  * \param book   Address book.
238  * \param stream Output stream.
239  */
addrbook_dump_book(AddressBookFile * book,FILE * stream)240 void addrbook_dump_book(AddressBookFile *book, FILE *stream)
241 {
242 	ItemFolder *folder;
243 
244 	cm_return_if_fail(book != NULL);
245 
246 	addrbook_print_book(book, stream);
247 	folder = book->addressCache->rootFolder;
248 	addritem_print_item_folder(folder, stream);
249 }
250 
251 /**
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.
259  */
addrbook_remove_group(AddressBookFile * book,ItemGroup * group)260 ItemGroup *addrbook_remove_group(AddressBookFile *book, ItemGroup *group)
261 {
262 	cm_return_val_if_fail(book != NULL, NULL);
263 	return addrcache_remove_group(book->addressCache, group);
264 }
265 
266 /**
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.
272  */
addrbook_remove_person(AddressBookFile * book,ItemPerson * person)273 ItemPerson *addrbook_remove_person(AddressBookFile *book, ItemPerson *person)
274 {
275 	cm_return_val_if_fail(book != NULL, NULL);
276 	return addrcache_remove_person(book->addressCache, person);
277 }
278 
279 /**
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.
286  */
addrbook_person_remove_email(AddressBookFile * book,ItemPerson * person,ItemEMail * email)287 ItemEMail *addrbook_person_remove_email(AddressBookFile *book,
288 					ItemPerson *person, ItemEMail *email)
289 {
290 	cm_return_val_if_fail(book != NULL, NULL);
291 	return addrcache_person_remove_email(book->addressCache, person, email);
292 }
293 
294 /*
295 * ***********************************************************************
296 * Read/Write XML data file...
297 * ===========================
298 * Notes:
299 * 1)	The address book is structured as follows:
300 *
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
313 *
314 * 2)	This sequence of elements was chosen so that the most important
315 * 	elements (person and their email addresses) appear first.
316 *
317 * 3)	Groups then appear. When groups are loaded, person's email
318 *	addresses have already been loaded and can be found.
319 *
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.
323 *
324 * ***********************************************************************
325 */
326 
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"
340 
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"
354 
355 /* Attribute values */
356 #define AB_ATTAG_VAL_PERSON      "person"
357 #define AB_ATTAG_VAL_GROUP       "group"
358 #define AB_ATTAG_VAL_FOLDER      "folder"
359 
360 /**
361  * Parse address item for person from XML file.
362  * \param book   Address book.
363  * \param file   XML file handle.
364  * \param person Person.
365  */
addrbook_parse_address(AddressBookFile * book,XMLFile * file,ItemPerson * person)366 static void addrbook_parse_address(AddressBookFile *book, XMLFile *file,
367 				   ItemPerson *person)
368 {
369 	GList *attr;
370 	gchar *name, *value;
371 	ItemEMail *email = NULL;
372 
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);
388 	}
389 	if (email) {
390 		if (person) {
391 			addrcache_person_add_email(book->addressCache, person,
392 						   email);
393 		}
394 		else {
395 			addritem_free_item_email(email);
396 			email = NULL;
397 		}
398 	}
399 }
400 
401 /**
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.
406  */
addrbook_parse_addr_list(AddressBookFile * book,XMLFile * file,ItemPerson * person)407 static void addrbook_parse_addr_list(AddressBookFile *book, XMLFile *file,
408 				     ItemPerson *person)
409 {
410 	guint prev_level;
411 
412 	for (;;) {
413 		prev_level = file->level;
414 		if (xml_parse_next_tag(file)) {
415 			longjmp(book->jumper, 1);
416 		}
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);
421 		}
422 	}
423 }
424 
425 /**
426  * Parse attribute for person from XML file.
427  * \param book   Address book.
428  * \param file   XML file handle.
429  * \param person Person.
430  */
addrbook_parse_attribute(XMLFile * file,ItemPerson * person)431 static void addrbook_parse_attribute(XMLFile *file, ItemPerson *person)
432 {
433 	GList *attr;
434 	gchar *name, *value;
435 	gchar *element;
436 	UserAttribute *uAttr = NULL;
437 
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);
448 	}
449 
450 	element = xml_get_element(file);
451 	addritem_attrib_set_value(uAttr, element);
452 	g_free(element);
453 
454 	if (uAttr) {
455 		if (person) {
456 			addritem_person_add_attribute(person, uAttr);
457 		}
458 		else {
459 			addritem_free_attribute(uAttr);
460 			uAttr = NULL;
461 		}
462 	}
463 }
464 
465 /**
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.
470  */
addrbook_parse_attr_list(AddressBookFile * book,XMLFile * file,ItemPerson * person)471 static void addrbook_parse_attr_list(AddressBookFile *book, XMLFile *file,
472 				     ItemPerson *person)
473 {
474 	guint prev_level;
475 
476 	for (;;) {
477 		prev_level = file->level;
478 		if (xml_parse_next_tag(file)) {
479 			longjmp( book->jumper, 1 );
480 		}
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);
485 		}
486 	}
487 }
488 
489 /**
490  * Parse person from XML file.
491  * \param book Address book.
492  * \param file XML file handle.
493  */
addrbook_parse_person(AddressBookFile * book,XMLFile * file)494 static void addrbook_parse_person(AddressBookFile *book, XMLFile *file)
495 {
496 	GList *attr;
497 	gchar *name, *value;
498 	ItemPerson *person = NULL;
499 
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);
509 		}
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);
519 	}
520 	if (xml_parse_next_tag(file)) {	/* Consume closing tag */
521 		longjmp(book->jumper, 1);
522 	}
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);
527 		}
528 	}
529 	if (xml_parse_next_tag(file)) {	/* Consume closing tag */
530 		longjmp(book->jumper, 1);
531 	}
532 	if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST)) {
533 		addrbook_parse_attr_list(book, file, person);
534 	}
535 }
536 
537 /**
538  * Parse group member from XML file.
539  * \param book  Address book.
540  * \param file  XML file handle.
541  * \param group Group.
542  */
addrbook_parse_member(AddressBookFile * book,XMLFile * file,ItemGroup * group)543 static void addrbook_parse_member(AddressBookFile *book, XMLFile *file,
544 				  ItemGroup *group)
545 {
546 	GList *attr;
547 	gchar *name, *value;
548 	gchar *eid = NULL;
549 	/* gchar *pid = NULL; */
550 	ItemEMail *email = NULL;
551 
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);
559 	}
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);
567 		}
568 		else {
569 			addritem_free_item_email(email);
570 			email = NULL;
571 		}
572 	}
573 }
574 
575 /**
576  * Parse list of group members from XML file.
577  * \param book  Address book.
578  * \param file  XML file handle.
579  * \param group Group.
580  */
addrbook_parse_member_list(AddressBookFile * book,XMLFile * file,ItemGroup * group)581 static void addrbook_parse_member_list(AddressBookFile *book, XMLFile *file,
582 				       ItemGroup *group)
583 {
584 	guint prev_level;
585 
586 	for (;;) {
587 		prev_level = file->level;
588 		if (xml_parse_next_tag(file)) {
589 			longjmp(book->jumper, 1);
590 		}
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);
596 		}
597 	}
598 }
599 
600 /**
601  * Parse group object from XML file.
602  * \param book Address book.
603  * \param file XML file handle.
604  */
addrbook_parse_group(AddressBookFile * book,XMLFile * file)605 static void addrbook_parse_group(AddressBookFile *book, XMLFile *file)
606 {
607 	GList *attr;
608 	gchar *name, *value;
609 	ItemGroup *group = NULL;
610 
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);
624 	}
625 	if (xml_parse_next_tag(file)) {	/* Consume closing tag */
626 		longjmp(book->jumper, 1);
627 	}
628 	if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST)) {
629 		if (group) {
630 			addrcache_hash_add_group(book->addressCache, group);
631 		}
632 		addrbook_parse_member_list(book, file, group);
633 	}
634 }
635 
636 /**
637  * Parse folder item from XML file.
638  * \param book   Address book.
639  * \param file   XML file handle.
640  * \param folder Folder.
641  */
addrbook_parse_folder_item(AddressBookFile * book,XMLFile * file,ItemFolder * folder)642 static void addrbook_parse_folder_item(AddressBookFile *book, XMLFile *file,
643 				       ItemFolder *folder)
644 {
645 	GList *attr;
646 	gchar *name, *value;
647 	gchar *uid = NULL;
648 
649 	attr = xml_get_current_tag_attr(file);
650 	while (attr) {
651 		name = ((XMLAttr *)attr->data)->name;
652 		value = ((XMLAttr *)attr->data)->value;
653 		if (strcmp(name, AB_ATTAG_UID) == 0) {
654 			uid = g_strdup(value);
655 		}
656 		attr = g_list_next(attr);
657 	}
658 	if (folder) {
659 		if (uid) {
660 			folder->listItems = g_list_append(folder->listItems, uid);
661 		}
662 	}
663 }
664 
665 /**
666  * Parse list of folder items from XML file.
667  * \param book   Address book.
668  * \param file   XML file handle.
669  * \param folder Folder.
670  */
addrbook_parse_folder_list(AddressBookFile * book,XMLFile * file,ItemFolder * folder)671 static void addrbook_parse_folder_list(AddressBookFile *book, XMLFile *file,
672 				       ItemFolder *folder)
673 {
674 	guint prev_level;
675 
676 	for (;;) {
677 		prev_level = file->level;
678 		if (xml_parse_next_tag(file)) {
679 			longjmp(book->jumper, 1);
680 		}
681 		if (file->level < prev_level)
682 			return;
683 		if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
684 			addrbook_parse_folder_item(book, file, folder);
685 			addrbook_parse_folder_list(book, file, folder);
686 		}
687 	}
688 }
689 
690 /**
691  * Parse folder from XML file.
692  * \param book Address book.
693  * \param file XML file handle.
694  */
addrbook_parse_folder(AddressBookFile * book,XMLFile * file)695 static void addrbook_parse_folder(AddressBookFile *book, XMLFile *file)
696 {
697 	GList *attr;
698 	gchar *name, *value;
699 	ItemFolder *folder = NULL;
700 
701 	attr = xml_get_current_tag_attr(file);
702 	while (attr) {
703 		name = ((XMLAttr *)attr->data)->name;
704 		value = ((XMLAttr *)attr->data)->value;
705 		if (!folder)
706 			folder = addritem_create_item_folder();
707 		if (strcmp(name, AB_ATTAG_UID) == 0)
708 			ADDRITEM_ID(folder) = g_strdup(value);
709 		else if (strcmp(name, AB_ATTAG_NAME) == 0)
710 			ADDRITEM_NAME(folder) = g_strdup(value);
711 		else if (strcmp(name, AB_ATTAG_REMARKS) == 0)
712 			folder->remarks = g_strdup(value);
713 		attr = g_list_next(attr);
714 	}
715 	if (xml_parse_next_tag(file)) {	/* Consume closing tag */
716 		longjmp(book->jumper, 1);
717 	}
718 	if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST)) {
719 		if (folder) {
720 			if (addrcache_hash_add_folder(book->addressCache,
721 						      folder)) {
722 				book->tempList = g_list_append(book->tempList,
723 							       folder);
724 				/* We will resolve folder later */
725 				ADDRITEM_PARENT(folder) = NULL;
726 			}
727 		}
728 		addrbook_parse_folder_list(book, file, folder);
729 	}
730 }
731 
732 /**
733  * Read address book (DOM) tree from file.
734  * \param  book Address book.
735  * \param  file XML file handle.
736  * \return <i>TRUE</i> if data read successfully, <i>FALSE</i> if error
737  *         reading data.
738  */
addrbook_read_tree(AddressBookFile * book,XMLFile * file)739 static gboolean addrbook_read_tree(AddressBookFile *book, XMLFile *file)
740 {
741 	gboolean retVal;
742 	GList *attr;
743 	gchar *name, *value;
744 
745 	book->retVal = MGU_BAD_FORMAT;
746 	if (xml_get_dtd(file))
747 		return FALSE;
748 	if (xml_parse_next_tag(file))
749 		longjmp(book->jumper, 1);
750 	if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
751 		return FALSE;
752 
753 	attr = xml_get_current_tag_attr(file);
754 	while (attr) {
755 		name = ((XMLAttr *)attr->data)->name;
756 		value = ((XMLAttr *)attr->data)->value;
757 		if (strcmp( name, AB_ATTAG_NAME) == 0)
758 			addrbook_set_name( book, value );
759 		attr = g_list_next( attr );
760 	}
761 
762 	retVal = TRUE;
763 	for (;;) {
764 		if (!file->level)
765 			break;
766 		/* Get next item tag (person, group or folder) */
767 		if (xml_parse_next_tag(file))
768 			longjmp( book->jumper, 1 );
769 
770 		if (xml_compare_tag(file, AB_ELTAG_PERSON))
771 			addrbook_parse_person(book, file);
772 		else if (xml_compare_tag(file, AB_ELTAG_GROUP))
773 			addrbook_parse_group(book, file);
774 		else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
775 			addrbook_parse_folder(book, file);
776 	}
777 	if (retVal) book->retVal = MGU_SUCCESS;
778 		return retVal;
779 }
780 
781 /**
782  * Resolve folder items callback function.
783  * \param key   Table key.
784  * \param value Reference to object contained in folder.
785  * \param data  Reference to address book.
786  */
addrbook_res_items_vis(gpointer key,gpointer value,gpointer data)787 static void addrbook_res_items_vis(gpointer key, gpointer value, gpointer data)
788 {
789 	AddressBookFile *book = data;
790 	AddrItemObject *obj = (AddrItemObject *) value;
791 	ItemFolder *rootFolder = book->addressCache->rootFolder;
792 	if (obj->parent == NULL) {
793 		if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
794 			rootFolder->listPerson = g_list_append(rootFolder->listPerson,
795 							       obj);
796 			ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
797 		}
798 		else if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
799 			rootFolder->listGroup = g_list_append(rootFolder->listGroup,
800 							      obj);
801 			ADDRITEM_PARENT(obj) = ADDRITEM_OBJECT(rootFolder);
802 		}
803 	}
804 }
805 
806 /**
807  * Resolve folder items. Lists of UID's are replaced with pointers to
808  * data items.
809  * \param  book Address book.
810  */
addrbook_resolve_folder_items(AddressBookFile * book)811 static void addrbook_resolve_folder_items(AddressBookFile *book)
812 {
813 	GList *nodeFolder = NULL;
814 	GList *listRemove = NULL;
815 	GList *node = NULL;
816 	ItemFolder *rootFolder = book->addressCache->rootFolder;
817 	nodeFolder = book->tempList;
818 
819 	while (nodeFolder) {
820 		ItemFolder *folder = nodeFolder->data;
821 		listRemove = NULL;
822 		node = folder->listItems;
823 		while (node) {
824 			gchar *uid = node->data;
825 			AddrItemObject *aio = addrcache_get_object(book->addressCache,
826 								   uid);
827 			if (aio) {
828 				if (aio->type == ITEMTYPE_FOLDER) {
829 					ItemFolder *item = (ItemFolder *) aio;
830 					folder->listFolder = g_list_append(folder->listFolder, item);
831 					ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
832 					addrcache_hash_add_folder(book->addressCache, folder);
833 				}
834 				else if (aio->type == ITEMTYPE_PERSON) {
835 					ItemPerson *item = (ItemPerson *) aio;
836 					folder->listPerson = g_list_append(folder->listPerson, item);
837 					ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
838 				}
839 				else if (aio->type == ITEMTYPE_GROUP) {
840 					ItemGroup *item = (ItemGroup *) aio;
841 					folder->listGroup = g_list_append(folder->listGroup, item);
842 					ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
843 				}
844 				/* Replace data with pointer to item */
845 				g_free(uid);
846 				node->data = aio;
847 			}
848 			else { /* Not found, append to remove list. */
849 				listRemove = g_list_append(listRemove, uid);
850 			}
851 			node = g_list_next(node);
852 		}
853 		rootFolder->listFolder = g_list_append(rootFolder->listFolder,
854 						       folder);
855 		/* Process remove list */
856 		node = listRemove;
857 		while (node) {
858 			gchar *uid = node->data;
859 			folder->listItems = g_list_remove(folder->listItems,
860 							  uid);
861 			g_free(uid);
862 			node = g_list_next(node);
863 		}
864 		g_list_free(listRemove);
865 		nodeFolder = g_list_next(nodeFolder);
866 	}
867 	/* Remove folders with parents. */
868 	listRemove = NULL;
869 	node = rootFolder->listFolder;
870 	while (node) {
871 		ItemFolder *folder = (ItemFolder *) node->data;
872 		if (ADDRITEM_PARENT(folder))
873 			/* Remove folders with parents */
874 			listRemove = g_list_append(listRemove, folder);
875 		else /* Add to root folder */
876 			ADDRITEM_PARENT(folder) = ADDRITEM_OBJECT(book->addressCache->rootFolder);
877 
878 		node = g_list_next( node );
879 	}
880 	/* Process remove list */
881 	node = listRemove;
882 	while (node) {
883 		rootFolder->listFolder = g_list_remove(rootFolder->listFolder,
884 						       node->data);
885 		node = g_list_next(node);
886 	}
887 	g_list_free(listRemove);
888 
889 	/* Move all unparented persons and groups into root folder */
890 	g_hash_table_foreach(book->addressCache->itemHash,
891 			     addrbook_res_items_vis, book);
892 
893 	/* Free up some more */
894 	nodeFolder = book->tempList;
895 	while (nodeFolder) {
896 		ItemFolder *folder = nodeFolder->data;
897 		g_list_free(folder->listItems);
898 		folder->listItems = NULL;
899 		nodeFolder = g_list_next(nodeFolder);
900 	}
901 	g_list_free(book->tempList);
902 	book->tempList = NULL;
903 }
904 
905 /**
906  * Read address book.
907  * \param  book Address book.
908  * \return Status code.
909  */
addrbook_read_data(AddressBookFile * book)910 gint addrbook_read_data(AddressBookFile *book)
911 {
912 	XMLFile *file = NULL;
913 	gchar *fileSpec = NULL;
914 
915 	cm_return_val_if_fail(book != NULL, -1);
916 
917 	/*
918 	g_print( "...addrbook_read_data :%s:\t:%s:\n", book->fileName,
919 		addrcache_get_name( book->addressCache ) );
920 	*/
921 
922 	fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S,
923 			       book->fileName, NULL);
924 	book->retVal = MGU_OPEN_FILE;
925 	addrcache_clear(book->addressCache);
926 	book->addressCache->modified = FALSE;
927 	book->addressCache->accessFlag = FALSE;
928 	file = xml_open_file(fileSpec);
929 	g_free(fileSpec);
930 	if (file) {
931 		book->tempList = NULL;
932 		/* Trap for parsing errors. */
933 		if (setjmp( book->jumper)) {
934 			xml_close_file(file);
935 			return book->retVal;
936 		}
937 		addrbook_read_tree(book, file);
938 		xml_close_file(file);
939 		/* Resolve folder items */
940 		addrbook_resolve_folder_items(book);
941 		book->tempList = NULL;
942 		book->addressCache->modified = FALSE;
943 		book->addressCache->dataRead = TRUE;
944 		addrcache_set_dirty(book->addressCache, FALSE);
945 	}
946 	return book->retVal;
947 }
948 
949 /**
950  * Write start element to file.
951  * \param fp   File handle.
952  * \param lvl  Indent level.
953  * \param name Element name.
954  */
addrbook_write_elem_s(FILE * fp,gint lvl,gchar * name)955 static int addrbook_write_elem_s(FILE *fp, gint lvl, gchar *name)
956 {
957 	gint i;
958 	for (i = 0; i < lvl; i++)
959 		if (claws_fputs("  ", fp) == EOF)
960 			return -1;
961 	if (claws_fputs("<", fp) == EOF)
962 		return -1;
963 	if (claws_fputs(name, fp) == EOF)
964 		return -1;
965 
966 	return 0;
967 }
968 
969 /**
970  * Write end element to file.
971  * \param fp   File handle.
972  * \param lvl  Indent level.
973  * \param name Element name.
974  */
addrbook_write_elem_e(FILE * fp,gint lvl,gchar * name)975 static int addrbook_write_elem_e(FILE *fp, gint lvl, gchar *name)
976 {
977 	gint i;
978 	for(i = 0; i < lvl; i++)
979 		if (claws_fputs("  ", fp) == EOF)
980 			return -1;
981 	if (claws_fputs("</", fp) == EOF)
982 		return -1;
983 	if (claws_fputs(name, fp) == EOF)
984 		return -1;
985 	if (claws_fputs(">\n", fp) == EOF)
986 		return -1;
987 
988 	return 0;
989 }
990 
991 /**
992  * Write attribute name/value pair to file.
993  * \param fp    File handle.
994  * \param name  Attribute name.
995  * \param value Attribute value.
996  */
addrbook_write_attr(FILE * fp,gchar * name,gchar * value)997 static int addrbook_write_attr(FILE *fp, gchar *name, gchar *value)
998 {
999 	if (claws_fputs(" ", fp) == EOF)
1000 		return -1;
1001 	if (claws_fputs(name, fp) == EOF)
1002 		return -1;
1003 	if (claws_fputs("=\"", fp) == EOF)
1004 		return -1;
1005 	if (xml_file_put_escape_str(fp, value) < 0)
1006 		return -1;
1007 	if (claws_fputs("\"", fp) == EOF)
1008 		return -1;
1009 
1010 	return 0;
1011 }
1012 
1013 typedef struct _HashLoopData {
1014 	FILE *fp;
1015 	gboolean error;
1016 } HashLoopData;
1017 
1018 /**
1019  * Write person and associated addresses and attributes to file.
1020  * file hash table visitor function.
1021  * \param key   Table key.
1022  * \param value Reference to person.
1023  * \param data  File pointer.
1024  */
addrbook_write_item_person_vis(gpointer key,gpointer value,gpointer d)1025 static void addrbook_write_item_person_vis(gpointer key, gpointer value,
1026 					   gpointer d)
1027 {
1028 	AddrItemObject *obj = (AddrItemObject *) value;
1029 	HashLoopData *data = (HashLoopData *)d;
1030 	FILE *fp = data->fp;
1031 	GList *node;
1032 
1033 	if (!obj)
1034 		return;
1035 	if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1036 		ItemPerson *person = (ItemPerson *) value;
1037 		if (person) {
1038 			if (addrbook_write_elem_s(fp, 1, AB_ELTAG_PERSON) < 0)
1039 				data->error = TRUE;
1040 			if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(person)) < 0)
1041 				data->error = TRUE;
1042 			if (addrbook_write_attr(fp, AB_ATTAG_FIRST_NAME, person->firstName) < 0)
1043 				data->error = TRUE;
1044 			if (addrbook_write_attr(fp, AB_ATTAG_LAST_NAME, person->lastName) < 0)
1045 				data->error = TRUE;
1046 			if (addrbook_write_attr(fp, AB_ATTAG_NICK_NAME, person->nickName) < 0)
1047 				data->error = TRUE;
1048 			if (addrbook_write_attr(fp, AB_ATTAG_COMMON_NAME, ADDRITEM_NAME(person)) < 0)
1049 				data->error = TRUE;
1050 			if (claws_fputs(" >\n", fp) == EOF)
1051 				data->error = TRUE;
1052 
1053 			/* Output email addresses */
1054 			if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1055 				data->error = TRUE;
1056 			if (claws_fputs(">\n", fp) == EOF)
1057 				data->error = TRUE;
1058 			node = person->listEMail;
1059 			while (node) {
1060 				ItemEMail *email = node->data;
1061 				if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ADDRESS) < 0)
1062 					data->error = TRUE;
1063 				if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(email)) < 0)
1064 					data->error = TRUE;
1065 				if (addrbook_write_attr(fp, AB_ATTAG_ALIAS, ADDRITEM_NAME(email)) < 0)
1066 					data->error = TRUE;
1067 				if (addrbook_write_attr(fp, AB_ATTAG_EMAIL, email->address) < 0)
1068 					data->error = TRUE;
1069 				if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, email->remarks) < 0)
1070 					data->error = TRUE;
1071 				if (claws_fputs(" />\n", fp) == EOF)
1072 					data->error = TRUE;
1073 				node = g_list_next(node);
1074 			}
1075 			if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ADDRESS_LIST) < 0)
1076 				data->error = TRUE;
1077 
1078 			/* Output user attributes */
1079 			if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1080 				data->error = TRUE;
1081 			if (claws_fputs(">\n", fp) == EOF)
1082 				data->error = TRUE;
1083 			node = person->listAttrib;
1084 			while (node) {
1085 				UserAttribute *attrib = node->data;
1086 				if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ATTRIBUTE) < 0)
1087 					data->error = TRUE;
1088 				if (addrbook_write_attr(fp, AB_ATTAG_UID, attrib->uid) < 0)
1089 					data->error = TRUE;
1090 				if (addrbook_write_attr(fp, AB_ATTAG_NAME, attrib->name) < 0)
1091 					data->error = TRUE;
1092 				if (claws_fputs(" >", fp) == EOF)
1093 					data->error = TRUE;
1094 				if (xml_file_put_escape_str(fp, attrib->value) < 0)
1095 					data->error = TRUE;
1096 				if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ATTRIBUTE) < 0)
1097 					data->error = TRUE;
1098 				node = g_list_next(node);
1099 			}
1100 			if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ATTRIBUTE_LIST) < 0)
1101 				data->error = TRUE;
1102 			if (addrbook_write_elem_e(fp, 1, AB_ELTAG_PERSON) < 0)
1103 				data->error = TRUE;
1104 		}
1105 	}
1106 }
1107 
1108 /**
1109  * Write group and associated references to addresses to file.
1110  * file hash table visitor function.
1111  * \param key   Table key.
1112  * \param value Reference to group.
1113  * \param data  File pointer.
1114  */
addrbook_write_item_group_vis(gpointer key,gpointer value,gpointer d)1115 static void addrbook_write_item_group_vis(gpointer key, gpointer value,
1116 					  gpointer d)
1117 {
1118 	AddrItemObject *obj = (AddrItemObject *) value;
1119 	HashLoopData *data = (HashLoopData *)d;
1120 	FILE *fp = data->fp;
1121 
1122 	GList *node;
1123 
1124 	if (!obj)
1125 		return;
1126 	if (ADDRITEM_TYPE(obj) == ITEMTYPE_GROUP) {
1127 		ItemGroup *group = (ItemGroup *) value;
1128 		if (group) {
1129 			if (addrbook_write_elem_s(fp, 1, AB_ELTAG_GROUP) < 0)
1130 				data->error = TRUE;
1131 			if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(group)) < 0)
1132 				data->error = TRUE;
1133 			if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(group)) < 0)
1134 				data->error = TRUE;
1135 			if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, group->remarks) < 0)
1136 				data->error = TRUE;
1137 			if (claws_fputs(" >\n", fp) == EOF)
1138 				data->error = TRUE;
1139 
1140 			/* Output email address links */
1141 			if (addrbook_write_elem_s(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1142 				data->error = TRUE;
1143 			if (claws_fputs(">\n", fp) == EOF)
1144 				data->error = TRUE;
1145 			node = group->listEMail;
1146 			while (node) {
1147 				ItemEMail *email = node->data;
1148 				ItemPerson *person = (ItemPerson *) ADDRITEM_PARENT(email);
1149 				if (addrbook_write_elem_s(fp, 3, AB_ELTAG_MEMBER) < 0)
1150 					data->error = TRUE;
1151 				if (addrbook_write_attr(fp, AB_ATTAG_PID, ADDRITEM_ID(person)) < 0)
1152 					data->error = TRUE;
1153 				if (addrbook_write_attr(fp, AB_ATTAG_EID, ADDRITEM_ID(email)) < 0)
1154 					data->error = TRUE;
1155 				if (claws_fputs(" />\n", fp) == EOF)
1156 					data->error = TRUE;
1157 				node = g_list_next(node);
1158 			}
1159 			if (addrbook_write_elem_e(fp, 2, AB_ELTAG_MEMBER_LIST) < 0)
1160 				data->error = TRUE;
1161 			if (addrbook_write_elem_e(fp, 1, AB_ELTAG_GROUP) < 0)
1162 				data->error = TRUE;
1163 		}
1164 	}
1165 }
1166 
1167 /**
1168  * Write folder and associated references to addresses to file.
1169  * file hash table visitor function.
1170  * \param key   Table key.
1171  * \param value Reference to folder.
1172  * \param data  File pointer.
1173  */
addrbook_write_item_folder_vis(gpointer key,gpointer value,gpointer d)1174 static void addrbook_write_item_folder_vis(gpointer key, gpointer value,
1175 					   gpointer d)
1176 {
1177 	AddrItemObject *obj = (AddrItemObject *) value;
1178 	HashLoopData *data = (HashLoopData *)d;
1179 	FILE *fp = data->fp;
1180 	GList *node;
1181 
1182 	if (!obj)
1183 		return;
1184 	if (ADDRITEM_TYPE(obj) == ITEMTYPE_FOLDER) {
1185 		ItemFolder *folder = (ItemFolder *) value;
1186 		if (folder) {
1187 			if (addrbook_write_elem_s(fp, 1, AB_ELTAG_FOLDER) < 0)
1188 				data->error = TRUE;
1189 			if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(folder)) < 0)
1190 				data->error = TRUE;
1191 			if (addrbook_write_attr(fp, AB_ATTAG_NAME, ADDRITEM_NAME(folder)) < 0)
1192 				data->error = TRUE;
1193 			if (addrbook_write_attr(fp, AB_ATTAG_REMARKS, folder->remarks) < 0)
1194 				data->error = TRUE;
1195 			if (claws_fputs(" >\n", fp) == EOF)
1196 				data->error = TRUE;
1197 			if (addrbook_write_elem_s(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1198 				data->error = TRUE;
1199 			if (claws_fputs(">\n", fp) == EOF)
1200 				data->error = TRUE;
1201 
1202 			/* Output persons */
1203 			node = folder->listPerson;
1204 			while (node) {
1205 				ItemPerson *item = node->data;
1206 				if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1207 					data->error = TRUE;
1208 				if (addrbook_write_attr(fp, AB_ATTAG_TYPE,  AB_ATTAG_VAL_PERSON) < 0)
1209 					data->error = TRUE;
1210 				if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1211 					data->error = TRUE;
1212 				if (claws_fputs(" />\n", fp) == EOF)
1213 					data->error = TRUE;
1214 				node = g_list_next(node);
1215 			}
1216 
1217 			/* Output groups */
1218 			node = folder->listGroup;
1219 			while (node) {
1220 				ItemGroup *item = node->data;
1221 				if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1222 					data->error = TRUE;
1223 				if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_GROUP) < 0)
1224 					data->error = TRUE;
1225 				if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1226 					data->error = TRUE;
1227 				if (claws_fputs(" />\n", fp) == EOF)
1228 					data->error = TRUE;
1229 				node = g_list_next(node);
1230 			}
1231 
1232 			/* Output folders */
1233 			node = folder->listFolder;
1234 			while (node) {
1235 				ItemFolder *item = node->data;
1236 				if (addrbook_write_elem_s(fp, 3, AB_ELTAG_ITEM) < 0)
1237 					data->error = TRUE;
1238 				if (addrbook_write_attr(fp, AB_ATTAG_TYPE, AB_ATTAG_VAL_FOLDER) < 0)
1239 					data->error = TRUE;
1240 				if (addrbook_write_attr(fp, AB_ATTAG_UID, ADDRITEM_ID(item)) < 0)
1241 					data->error = TRUE;
1242 				if (claws_fputs(" />\n", fp) == EOF)
1243 					data->error = TRUE;
1244 				node = g_list_next(node);
1245 			}
1246 			if (addrbook_write_elem_e(fp, 2, AB_ELTAG_ITEM_LIST) < 0)
1247 				data->error = TRUE;
1248 			if (addrbook_write_elem_e(fp, 1, AB_ELTAG_FOLDER) < 0)
1249 				data->error = TRUE;
1250 		}
1251 	}
1252 }
1253 
1254 /**
1255  * Output address book data to specified file.
1256  * \param  book Address book.
1257  * \param  newFile Filename of new file (in book's filepath).
1258  * \return Status code.
1259  */
addrbook_write_to(AddressBookFile * book,gchar * newFile)1260 static gint addrbook_write_to(AddressBookFile *book, gchar *newFile)
1261 {
1262 	FILE *fp;
1263 	gchar *fileSpec;
1264 	HashLoopData data;
1265 #ifndef DEV_STANDALONE
1266 	PrefFile *pfile;
1267 #endif
1268 
1269 	cm_return_val_if_fail(book != NULL, -1);
1270 	cm_return_val_if_fail(newFile != NULL, -1);
1271 
1272 	fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, newFile, NULL);
1273 
1274 	book->retVal = MGU_OPEN_FILE;
1275 #ifdef DEV_STANDALONE
1276 	fp = claws_fopen(fileSpec, "wb");
1277 	g_free(fileSpec);
1278 	if (fp) {
1279 		if (claws_fputs("<?xml version=\"1.0\" ?>\n", fp) == EOF) {
1280 			book->retVal = MGU_ERROR_WRITE;
1281 			return book->retVal;
1282 		}
1283 #else
1284 	pfile = prefs_write_open(fileSpec);
1285 	g_free(fileSpec);
1286 	if (pfile) {
1287 		fp = pfile->fp;
1288 		if (fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL ) < 0)
1289 			goto fail;
1290 #endif
1291 		if (addrbook_write_elem_s(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1292 			goto fail;
1293 		if (addrbook_write_attr(fp, AB_ATTAG_NAME,
1294 				    addrcache_get_name(book->addressCache)) < 0)
1295 			goto fail;
1296 		if (claws_fputs(" >\n", fp) == EOF)
1297 			goto fail;
1298 
1299 		/* Output all persons */
1300 		data.fp = fp;
1301 		data.error = FALSE;
1302 
1303 		g_hash_table_foreach(book->addressCache->itemHash,
1304 				     addrbook_write_item_person_vis, &data);
1305 		if (data.error)
1306 			goto fail;
1307 
1308 		/* Output all groups */
1309 		g_hash_table_foreach(book->addressCache->itemHash,
1310 				     addrbook_write_item_group_vis, &data);
1311 
1312 		if (data.error)
1313 			goto fail;
1314 
1315 		/* Output all folders */
1316 		g_hash_table_foreach(book->addressCache->itemHash,
1317 				     addrbook_write_item_folder_vis, &data);
1318 
1319 		if (data.error)
1320 			goto fail;
1321 
1322 		if (addrbook_write_elem_e(fp, 0, AB_ELTAG_ADDRESS_BOOK) < 0)
1323 			goto fail;
1324 
1325 		book->retVal = MGU_SUCCESS;
1326 #ifdef DEV_STANDALONE
1327 		claws_safe_fclose(fp);
1328 #else
1329 		if (prefs_file_close( pfile ) < 0)
1330 			book->retVal = MGU_ERROR_WRITE;
1331 #endif
1332 	}
1333 
1334 	fileSpec = NULL;
1335 	return book->retVal;
1336 fail:
1337 	g_warning("error writing AB");
1338 	book->retVal = MGU_ERROR_WRITE;
1339 	if (pfile)
1340 		prefs_file_close_revert( pfile );
1341 	return book->retVal;
1342 }
1343 
1344 /**
1345  * Output address book data to original file.
1346  * \param  book Address book.
1347  * \return Status code.
1348  */
1349 gint addrbook_save_data(AddressBookFile *book)
1350 {
1351 	cm_return_val_if_fail(book != NULL, -1);
1352 
1353 	book->retVal = MGU_NO_FILE;
1354 	if (book->fileName == NULL || *book->fileName == '\0')
1355 		return book->retVal;
1356 	if (book->path == NULL || *book->path == '\0')
1357 		return book->retVal;
1358 
1359 	addrbook_write_to(book, book->fileName);
1360 	if (book->retVal == MGU_SUCCESS)
1361 		addrcache_set_dirty(book->addressCache, FALSE);
1362 	return book->retVal;
1363 }
1364 
1365 /*
1366  * **********************************************************************
1367  * Address book edit interface functions.
1368  * **********************************************************************
1369  */
1370 
1371 /**
1372  * Hash table callback function for simple deletion of hashtable entries.
1373  * \param  key   Table key (will be freed).
1374  * \param  value Value stored in table.
1375  * \param  data  User data.
1376  * \return <i>TRUE</i> to indicate that entry freed.
1377  */
1378 static gboolean addrbook_free_simple_hash_vis(gpointer *key, gpointer *value,
1379 					      gpointer *data)
1380 {
1381 	g_free(key);
1382 	key = NULL;
1383 	value = NULL;
1384 	return TRUE;
1385 }
1386 
1387 /**
1388  * Update address book email list for specified person. Note: The existing
1389  * email addresses are replaced with the new addresses. Any references to
1390  * old addresses in the groups are re-linked to the new addresses. All old
1391  * addresses linked to the person are removed.
1392  * \param book      Address book.
1393  * \param person    Person to update.
1394  * \param listEMail List of new email addresses.
1395  */
1396 void addrbook_update_address_list(AddressBookFile *book, ItemPerson *person,
1397 				  GList *listEMail)
1398 {
1399 	GList *node;
1400 	GList *listDelete;
1401 	GList *listGroup;
1402 
1403 	cm_return_if_fail(book != NULL);
1404 	cm_return_if_fail(person != NULL);
1405 
1406 	/* Get groups where person's existing email addresses are listed */
1407 	listGroup = addrcache_get_group_for_person(book->addressCache, person);
1408 	if (listGroup) {
1409 		GHashTable *hashEMail;
1410 		GHashTable *hashEMailAlias;
1411 		GList *nodeGrp;
1412 
1413 		/* Load hash table with new address entries */
1414 		hashEMail = g_hash_table_new(g_str_hash, g_str_equal);
1415 		hashEMailAlias = g_hash_table_new(g_str_hash, g_str_equal);
1416 	   	node = listEMail;
1417 		while (node) {
1418 			ItemEMail *email = node->data;
1419 			gchar *alias = email->obj.name ;
1420 			gchar *addr = g_utf8_strdown(email->address, -1);
1421 			if (!g_hash_table_lookup(hashEMail, addr)) {
1422 				g_hash_table_insert(hashEMail, addr, email);
1423 			}
1424 			if (*alias != '\0' && ! g_hash_table_lookup(hashEMailAlias,
1425 			    alias))
1426 				g_hash_table_insert(hashEMailAlias, alias, email);
1427 
1428 			node = g_list_next(node);
1429 		}
1430 
1431 		/* Re-parent new addresses to existing groups, where email address match. */
1432 		nodeGrp = listGroup;
1433 		while (nodeGrp) {
1434 			ItemGroup *group = (ItemGroup *) nodeGrp->data;
1435 			GList *groupEMail = group->listEMail;
1436 			GList *nodeGrpEM;
1437 			GList *listRemove = NULL;
1438 
1439 			/* Process each email item linked to group */
1440 			nodeGrpEM = groupEMail;
1441 			while (nodeGrpEM) {
1442 				ItemEMail *emailGrp = (ItemEMail *) nodeGrpEM->data;
1443 
1444 				if (ADDRITEM_PARENT(emailGrp) == ADDRITEM_OBJECT(person)) {
1445 					/* Found an email address for this person */
1446 					ItemEMail *emailNew = NULL;
1447 					gchar *alias = emailGrp->obj.name;
1448 					gchar *addr = g_utf8_strdown(emailGrp->address, -1);
1449 					emailNew = (ItemEMail *)
1450 						g_hash_table_lookup(hashEMail, addr);
1451 					g_free( addr );
1452 					/* If no match by e-mail, try to match by e-mail alias */
1453 					if (!emailNew && *alias != '\0') {
1454 						emailNew = (ItemEMail *)
1455 							g_hash_table_lookup(hashEMailAlias, alias);
1456 					}
1457 
1458 					if (emailNew)
1459 						/* Point to this entry */
1460 						nodeGrpEM->data = emailNew;
1461 					else if (g_hash_table_size(hashEMail)==1)
1462 						/* If the person has just one e-mail address, then
1463 						   change e-mail address in group list */
1464 						nodeGrpEM->data = listEMail->data;
1465 					else
1466 						/* Mark for removal */
1467 						listRemove = g_list_append(listRemove, emailGrp);
1468 				}
1469 				/* Move on to next email link */
1470 				nodeGrpEM = g_list_next(nodeGrpEM);
1471 			}
1472 
1473 			/* Process all removed links in current group */
1474 			nodeGrpEM = listRemove;
1475 			while (nodeGrpEM) {
1476 				ItemEMail *emailGrp = nodeGrpEM->data;
1477 				groupEMail = g_list_remove(groupEMail, emailGrp);
1478 				nodeGrpEM = g_list_next(nodeGrpEM);
1479 			}
1480 
1481 			g_list_free(listRemove);
1482 
1483 			/* Move on to next group */
1484 			nodeGrp = g_list_next(nodeGrp);
1485 
1486 		}
1487 		/* Clear hash table */
1488 		g_hash_table_foreach_remove(hashEMail, (GHRFunc)
1489 					    addrbook_free_simple_hash_vis, NULL);
1490 		g_hash_table_destroy(hashEMail);
1491 		hashEMail = NULL;
1492 		g_hash_table_destroy(hashEMailAlias);
1493 		hashEMailAlias = NULL;
1494 		g_list_free(listGroup);
1495 		listGroup = NULL;
1496 	}
1497 	/* Remove old addresses from person and cache */
1498 	listDelete = NULL;
1499 	node = person->listEMail;
1500 	while (node) {
1501 		ItemEMail *email = node->data;
1502 
1503 		if (addrcache_person_remove_email(book->addressCache, person, email))
1504 			addrcache_remove_email(book->addressCache, email);
1505 
1506 		listDelete = g_list_append(listDelete, email);
1507 		node = person->listEMail;
1508 	}
1509 	/* Add new address entries */
1510    	node = listEMail;
1511 	while (node) {
1512 		ItemEMail *email = node->data;
1513 
1514 		if (ADDRITEM_ID(email) == NULL)
1515 			/* Allocate an ID for new address */
1516 			addrcache_id_email(book->addressCache, email);
1517 
1518 		addrcache_person_add_email( book->addressCache, person, email );
1519 		node = g_list_next( node );
1520 	}
1521 
1522 	addrcache_set_dirty(book->addressCache, TRUE);
1523 
1524 	/* Free up memory */
1525 	g_list_free(listEMail);
1526 	listEMail = NULL;
1527 
1528 	node = listDelete;
1529 	while (node) {
1530 		ItemEMail *email = node->data;
1531 
1532 		addritem_free_item_email(email);
1533 		node = g_list_next(node);
1534 	}
1535 	g_list_free(listDelete);
1536 	listDelete = NULL;
1537 
1538 }
1539 
1540 /**
1541  * Create person object and add person with specified address data to address
1542  * book. Note: A new person is created with specified list of email addresses.
1543  * All objects inserted into address book.
1544  *
1545  * \param  book      Address book.
1546  * \param  folder    Parent folder where to add person, or <i>NULL</i> for
1547  *                   root folder.
1548  * \param  listEMail List of new email addresses to associate with person.
1549  * \return Person object created.
1550  */
1551 ItemPerson *addrbook_add_address_list(AddressBookFile *book, ItemFolder *folder,
1552 				      GList *listEMail)
1553 {
1554 	ItemPerson *person;
1555 	ItemFolder *f = folder;
1556 	GList *node;
1557 
1558 	cm_return_val_if_fail(book != NULL, NULL);
1559 
1560 	if (!f)
1561 		f = book->addressCache->rootFolder;
1562 	person = addritem_create_item_person();
1563 	addrcache_id_person(book->addressCache, person);
1564 	addrcache_folder_add_person(book->addressCache, f, person);
1565 
1566    	node = listEMail;
1567 	while (node) {
1568 		ItemEMail *email = node->data;
1569 		if (ADDRITEM_ID(email) == NULL)
1570 			addrcache_id_email(book->addressCache, email);
1571 
1572 		addrcache_person_add_email(book->addressCache, person, email);
1573 		node = g_list_next(node);
1574 	}
1575 	return person;
1576 }
1577 
1578 /**
1579  * Build available email list visitor function.
1580  * \param  key   Table key.
1581  * \param  value Value stored in table.
1582  * \param  data  Reference to address book.
1583  */
1584 static void addrbook_build_avail_email_vis(gpointer key, gpointer value,
1585 					   gpointer data)
1586 {
1587 	AddrItemObject *obj = (AddrItemObject *) value;
1588 
1589 	if (ADDRITEM_TYPE(obj) == ITEMTYPE_PERSON) {
1590 		AddressBookFile *book = data;
1591 		ItemPerson *person = (ItemPerson *) obj;
1592 		GList *node = person->listEMail;
1593 		while (node) {
1594 			ItemEMail *email = node->data;
1595 			/* gchar *newKey = g_strdup( ADDRITEM_ID(email) ); */
1596 
1597 			if (!g_hash_table_lookup(book->tempHash,
1598 					 	 ADDRITEM_ID(email)))
1599 				book->tempList = g_list_append(book->tempList, email);
1600 
1601 			node = g_list_next(node);
1602 		}
1603 	}
1604 }
1605 
1606 /**
1607  * Return link list of available email items that have not already been linked
1608  * to groups. Note that the list contains references to items and should be
1609  * <code>g_list_free()</code> when done. Do <b>*NOT*</b> attempt to used the
1610  * <code>addrbook_free_xxx()<code> functions... this will destroy the
1611  * addressbook data!
1612  *
1613  * \param  book  Address book.
1614  * \param  group Group to process.
1615  * \return List of items, or <i>NULL</i> if none.
1616  */
1617 GList *addrbook_get_available_email_list(AddressBookFile *book, ItemGroup *group)
1618 {
1619 	GList *list = NULL;
1620 	GHashTable *table;
1621 
1622 	cm_return_val_if_fail(book != NULL, NULL);
1623 
1624 	/* Load hash table with group email entries */
1625 	table = g_hash_table_new(g_str_hash, g_str_equal);
1626 	if (group) {
1627 		list = group->listEMail;
1628 		while (list) {
1629 			ItemEMail *email = list->data;
1630 			g_hash_table_insert(table, ADDRITEM_ID(email), email);
1631 			list = g_list_next(list);
1632 		}
1633 	}
1634 
1635 	/* Build list of available email addresses which exclude those already in groups */
1636 	book->tempList = NULL;
1637 	book->tempHash = table;
1638 	g_hash_table_foreach(book->addressCache->itemHash,
1639 			     addrbook_build_avail_email_vis, book);
1640 	list = book->tempList;
1641 	book->tempList = NULL;
1642 	book->tempHash = NULL;
1643 
1644 	/* Clear hash table */
1645 	g_hash_table_destroy(table);
1646 	table = NULL;
1647 
1648 	return list;
1649 }
1650 
1651 /**
1652  * Update address book email list for specified group. Note: The existing email
1653  * addresses are replaced with the new addresses. Any references to old addresses
1654  * in the groups are re-linked to the new addresses. All old addresses linked to
1655  * the person are removed.
1656  *
1657  * \param book      Address book.
1658  * \param group     Group to process.
1659  * \param listEMail List of email items. This should <b>*NOT*</b> be
1660  *                  <code>g_free()</code> when done.
1661  */
1662 void addrbook_update_group_list(AddressBookFile *book, ItemGroup *group,
1663 				GList *listEMail)
1664 {
1665 	GList *oldData;
1666 
1667 	cm_return_if_fail(book != NULL);
1668 	cm_return_if_fail(group != NULL);
1669 
1670 	addrcache_set_dirty(book->addressCache, TRUE);
1671 
1672 	/* Remember old list */
1673 	oldData = group->listEMail;
1674 	group->listEMail = listEMail;
1675 	g_list_free(oldData);
1676 }
1677 
1678 /**
1679  * Create group object and add with specifed list of email addresses to
1680  * address book. Note: The existing email addresses are replaced with the new
1681  * addresses. Any references to old addresses in the groups are re-linked to
1682  * the new addresses. All old addresses linked to the person are removed.
1683  *
1684  * \param  book      Address book.
1685  * \param  folder    Parent folder where to add group, or <i>NULL</i> for
1686  *                   root folder.
1687  * \param  listEMail List of email items. This should <b>*NOT*</b> be
1688  *                  <code>g_free()</code> when done.
1689  * \return Group object created.
1690  */
1691 ItemGroup *addrbook_add_group_list(AddressBookFile *book, ItemFolder *folder,
1692 				   GList *listEMail)
1693 {
1694 	ItemGroup *group = NULL;
1695 	ItemFolder *f = folder;
1696 
1697 	cm_return_val_if_fail(book != NULL, NULL);
1698 
1699 	if (!f)
1700 		f = book->addressCache->rootFolder;
1701 	group = addritem_create_item_group();
1702 	addrcache_id_group(book->addressCache, group);
1703 	addrcache_folder_add_group(book->addressCache, f, group);
1704 	group->listEMail = listEMail;
1705 	return group;
1706 }
1707 
1708 /**
1709  * Create a new folder and add to address book.
1710  * \param  book   Address book.
1711  * \param  folder Parent folder where to add folder, or <i>NULL</i> for
1712  *                root folder.
1713  * \return Folder that was created. This should <b>*NOT*</b> be
1714  *         <code>g_free()</code> when done.
1715  */
1716 ItemFolder *addrbook_add_new_folder(AddressBookFile *book, ItemFolder *parent)
1717 {
1718 	cm_return_val_if_fail(book != NULL, NULL);
1719 	return addrcache_add_new_folder( book->addressCache, parent );
1720 }
1721 
1722 /**
1723  * Update address book attribute list for specified person. Note: The existing
1724  * attributes are replaced with the new addresses. All old attributes linked
1725  * to the person are removed.
1726  *
1727  * \param book       Address book.
1728  * \param person     Person to receive attributes.
1729  * \param listAttrib New list of attributes.
1730  */
1731 void addrbook_update_attrib_list(AddressBookFile *book, ItemPerson *person,
1732 				 GList *listAttrib)
1733 {
1734 	GList *node;
1735 	GList *oldData;
1736 
1737 	cm_return_if_fail(book != NULL);
1738 	cm_return_if_fail(person != NULL);
1739 
1740 	/* Remember old list */
1741 	oldData = person->listAttrib;
1742 
1743 	/* Attach new address list to person. */
1744    	node = listAttrib;
1745 	while (node) {
1746 		UserAttribute *attrib = node->data;
1747 		if (attrib->uid == NULL) {
1748 			/* Allocate an ID */
1749 			addrcache_id_attribute(book->addressCache, attrib);
1750 		}
1751 		node = g_list_next(node);
1752 	}
1753 	person->listAttrib = listAttrib;
1754 	addrcache_set_dirty(book->addressCache, TRUE);
1755 
1756 	/* Free up old data */
1757 	addritem_free_list_attribute(oldData);
1758 	oldData = NULL;
1759 }
1760 
1761 /**
1762  * Add attribute data for specified person to address book. Note: Only
1763  * attributes are inserted into address book.
1764  * \param book       Address book.
1765  * \param person     Person to receive attributes.
1766  * \param listAttrib List of attributes.
1767  */
1768 void addrbook_add_attrib_list( AddressBookFile *book, ItemPerson *person, GList *listAttrib ) {
1769 	GList *node;
1770 
1771 	cm_return_if_fail( book != NULL );
1772 	cm_return_if_fail( person != NULL );
1773 
1774    	node = listAttrib;
1775 	while( node ) {
1776 		UserAttribute *attrib = node->data;
1777 		if( attrib->uid == NULL ) {
1778 			addrcache_id_attribute( book->addressCache, attrib );
1779 		}
1780 		addritem_person_add_attribute( person, attrib );
1781 		node = g_list_next( node );
1782 	}
1783 	addrcache_set_dirty( book->addressCache, TRUE );
1784 }
1785 
1786 #define WORK_BUFLEN     1024
1787 #define ADDRBOOK_DIGITS "0123456789"
1788 
1789 /**
1790  * Return list of existing address book files.
1791  * \param  book Address book.
1792  * \return List of files (as strings).
1793  */
1794 GList *addrbook_get_bookfile_list(AddressBookFile *book) {
1795 	gchar *adbookdir;
1796 	GDir *dir;
1797 	const gchar *dir_name;
1798 	GStatBuf statbuf;
1799 	gchar buf[WORK_BUFLEN + 1];
1800 	gchar numbuf[WORK_BUFLEN];
1801 	gint len, lenpre, lensuf, lennum;
1802 	long int val, maxval;
1803 	GList *fileList = NULL;
1804 
1805 	cm_return_val_if_fail(book != NULL, NULL);
1806 
1807 	if (book->path == NULL || *book->path == '\0') {
1808 		book->retVal = MGU_NO_PATH;
1809 		return NULL;
1810 	}
1811 
1812 	strncpy(buf, book->path, WORK_BUFLEN);
1813 	len = strlen(buf);
1814 	if (len > 0) {
1815 		if (buf[len-1] != G_DIR_SEPARATOR) {
1816 			buf[len] = G_DIR_SEPARATOR;
1817 			buf[++len] = '\0';
1818 		}
1819 	}
1820 
1821 	adbookdir = g_strdup(buf);
1822 	strncat(buf, ADDRBOOK_PREFIX, WORK_BUFLEN - strlen(buf));
1823 
1824 	if( ( dir = g_dir_open( adbookdir, 0, NULL ) ) == NULL ) {
1825 		book->retVal = MGU_OPEN_DIRECTORY;
1826 		g_free(adbookdir);
1827 		return NULL;
1828 	}
1829 
1830 	lenpre = strlen(ADDRBOOK_PREFIX);
1831 	lensuf = strlen(ADDRBOOK_SUFFIX);
1832 	lennum = FILE_NUMDIGITS + lenpre;
1833 	maxval = -1;
1834 
1835 	while( ( dir_name = g_dir_read_name( dir ) ) != NULL ) {
1836 		gchar *endptr = NULL;
1837 		gint i, r;
1838 		gboolean flg;
1839 
1840 		strncpy(buf, adbookdir, WORK_BUFLEN);
1841 		strncat(buf, dir_name, WORK_BUFLEN - strlen(buf));
1842 		r = g_stat(buf, &statbuf);
1843 		if (r == 0 && S_ISREG(statbuf.st_mode)) {
1844 			if (strncmp(
1845 				dir_name,
1846 				ADDRBOOK_PREFIX, lenpre) == 0)
1847 			{
1848 				if (strncmp(
1849 					(dir_name) + lennum,
1850 					ADDRBOOK_SUFFIX, lensuf) == 0)
1851 				{
1852 					strncpy(numbuf,
1853 						(dir_name) + lenpre,
1854 						FILE_NUMDIGITS);
1855 					numbuf[FILE_NUMDIGITS] = '\0';
1856 					flg = TRUE;
1857 					for(i = 0; i < FILE_NUMDIGITS; i++) {
1858 						if(!strchr(ADDRBOOK_DIGITS, numbuf[i])) {
1859 							flg = FALSE;
1860 							break;
1861 						}
1862 					}
1863 					if (flg) {
1864 						/* Get value */
1865 						val = strtol(numbuf, &endptr, 10);
1866 						if (endptr  && val > -1) {
1867 							if (val > maxval) maxval = val;
1868 							fileList = g_list_append(
1869 								fileList,
1870 								g_strdup(dir_name));
1871 						}
1872 					}
1873 				}
1874 			}
1875 		}
1876 	}
1877 	g_dir_close( dir );
1878 	g_free(adbookdir);
1879 
1880 	book->maxValue = maxval;
1881 	book->retVal = MGU_SUCCESS;
1882 	return fileList;
1883 }
1884 
1885 /**
1886  * Return file name for specified file number.
1887  * \param  fileNum File number.
1888  * \return File name, or <i>NULL</i> if file number too large. Should be
1889  *         <code>g_free()</code> when done.
1890  */
1891 gchar *addrbook_gen_new_file_name(gint fileNum) {
1892 	gchar fmt[30];
1893 	gchar buf[WORK_BUFLEN];
1894 	gint n = fileNum;
1895 	long int nmax;
1896 
1897 	if (n < 1)
1898 		n = 1;
1899 	nmax = -1 + (long int) pow(10, FILE_NUMDIGITS);
1900 	if (fileNum > nmax)
1901 		return NULL;
1902 	g_snprintf(fmt, sizeof(fmt), "%%s%%0%dd%%s", FILE_NUMDIGITS);
1903 	g_snprintf(buf, sizeof(buf), fmt, ADDRBOOK_PREFIX, n, ADDRBOOK_SUFFIX);
1904 	return g_strdup(buf);
1905 }
1906 
1907 /*
1908  * **********************************************************************
1909  * Address book test functions...
1910  * **********************************************************************
1911  */
1912 
1913 /**
1914  * Attempt to parse list of email address from file.
1915  * \param book Address book.
1916  * \param file XML file handle.
1917  */
1918 static void addrbook_chkparse_addr_list( AddressBookFile *book, XMLFile *file )
1919 {
1920 	guint prev_level;
1921 	/* GList *attr; */
1922 
1923 	for (;;) {
1924 		prev_level = file->level;
1925 		if (xml_parse_next_tag(file))
1926 			longjmp(book->jumper, 1);
1927 		if (file->level < prev_level)
1928 			return;
1929 		/* attr = xml_get_current_tag_attr(file); */
1930 		/* addrbook_show_attribs( attr ); */
1931 		if (xml_compare_tag(file, AB_ELTAG_ADDRESS))
1932 			addrbook_chkparse_addr_list(book, file);
1933 	}
1934 }
1935 
1936 /**
1937  * Attempt to parse attributes for person address from file.
1938  * \param book Address book.
1939  * \param file XML file handle.
1940  */
1941 static void addrbook_chkparse_attribute(AddressBookFile *book, XMLFile *file)
1942 {
1943 	/* GList *attr; */
1944 	/* gchar *element; */
1945 
1946 	/* attr = xml_get_current_tag_attr(file); */
1947 	/* addrbook_show_attribs( attr ); */
1948 	/* element = xml_get_element(file); */
1949 	/* g_print( "\t\tattrib value : %s\n", element ); */
1950 }
1951 
1952 /**
1953  * Attempt to parse list of attributes for person address from file.
1954  * \param book Address book.
1955  * \param file XML file handle.
1956  */
1957 static void addrbook_chkparse_attr_list(AddressBookFile *book, XMLFile *file)
1958 {
1959 	guint prev_level;
1960 
1961 	for (;;) {
1962 		prev_level = file->level;
1963 		if (xml_parse_next_tag(file))
1964 			longjmp(book->jumper, 1);
1965 		if (file->level < prev_level)
1966 			return;
1967 		if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE)) {
1968 			addrbook_chkparse_attribute(book, file);
1969 			addrbook_chkparse_attr_list(book, file);
1970 		}
1971 	}
1972 }
1973 
1974 /**
1975  * Attempt to parse person from file.
1976  * \param book Address book.
1977  * \param file XML file handle.
1978  */
1979 static void addrbook_chkparse_person(AddressBookFile *book, XMLFile *file)
1980 {
1981 	/* GList *attr; */
1982 
1983 	/* attr = xml_get_current_tag_attr(file); */
1984 	/* addrbook_show_attribs( attr ); */
1985 	if (xml_parse_next_tag(file)) /* Consume closing tag */
1986 		longjmp(book->jumper, 1);
1987 
1988 	if (xml_compare_tag(file, AB_ELTAG_ADDRESS_LIST))
1989 		addrbook_chkparse_addr_list(book, file);
1990 
1991 	if (xml_parse_next_tag(file))	/* Consume closing tag */
1992 		longjmp(book->jumper, 1);
1993 
1994 	if (xml_compare_tag(file, AB_ELTAG_ATTRIBUTE_LIST))
1995 		addrbook_chkparse_attr_list(book, file);
1996 }
1997 
1998 /**
1999  * Attempt to parse list of members from file.
2000  * \param book Address book.
2001  * \param file XML file handle.
2002  */
2003 static void addrbook_chkparse_member_list(AddressBookFile *book, XMLFile *file)
2004 {
2005 	/* GList *attr; */
2006 	guint prev_level;
2007 
2008 	for (;;) {
2009 		prev_level = file->level;
2010 		if (xml_parse_next_tag(file))
2011 			longjmp(book->jumper, 1);
2012 
2013 		if (file->level < prev_level)
2014 			return;
2015 
2016 		if (xml_compare_tag(file, AB_ELTAG_MEMBER)) {
2017 			/* attr = xml_get_current_tag_attr(file); */
2018 			/* addrbook_show_attribs( attr ); */
2019 			addrbook_chkparse_member_list(book, file);
2020 		}
2021 		else {
2022 			/* attr = xml_get_current_tag_attr(file); */
2023 			/* addrbook_show_attribs( attr ); */
2024 		}
2025 	}
2026 }
2027 
2028 /**
2029  * Attempt to parse group from file.
2030  * \param book Address book.
2031  * \param file XML file handle.
2032  */
2033 static void addrbook_chkparse_group(AddressBookFile *book, XMLFile *file)
2034 {
2035 	/* GList *attr; */
2036 
2037 	/* attr = xml_get_current_tag_attr(file); */
2038 	/* addrbook_show_attribs( attr ); */
2039 	if (xml_parse_next_tag(file))	/* Consume closing tag */
2040 		longjmp(book->jumper, 1);
2041 
2042 	if (xml_compare_tag(file, AB_ELTAG_MEMBER_LIST))
2043 		addrbook_chkparse_member_list(book, file);
2044 }
2045 
2046 /**
2047  * Attempt to parse list of folders from file.
2048  * \param book Address book.
2049  * \param file XML file handle.
2050  */
2051 static void addrbook_chkparse_folder_list(AddressBookFile *book, XMLFile *file)
2052 {
2053 	/* GList *attr; */
2054 	guint prev_level;
2055 
2056 	for (;;) {
2057 		prev_level = file->level;
2058 		if (xml_parse_next_tag(file))
2059 			longjmp(book->jumper, 1);
2060 
2061 		if (file->level < prev_level)
2062 			return;
2063 
2064 		if (xml_compare_tag(file, AB_ELTAG_ITEM)) {
2065 			/* attr = xml_get_current_tag_attr(file); */
2066 			/* addrbook_show_attribs( attr ); */
2067 			addrbook_chkparse_folder_list(book, file);
2068 		}
2069 		else {
2070 			/* attr = xml_get_current_tag_attr(file); */
2071 			/* addrbook_show_attribs( attr ); */
2072 		}
2073 	}
2074 }
2075 
2076 /**
2077  * Attempt to parse a folder from file.
2078  * \param book Address book.
2079  * \param file XML file handle.
2080  */
2081 static void addrbook_chkparse_folder(AddressBookFile *book, XMLFile *file)
2082 {
2083 	/* GList *attr; */
2084 
2085 	/* attr = xml_get_current_tag_attr(file); */
2086 	/* addrbook_show_attribs( attr ); */
2087 	if (xml_parse_next_tag(file))	/* Consume closing tag */
2088 		longjmp(book->jumper, 1);
2089 
2090 	if (xml_compare_tag(file, AB_ELTAG_ITEM_LIST))
2091 		addrbook_chkparse_folder_list(book, file);
2092 }
2093 
2094 /**
2095  * Attempt to parse (DOM) tree from file.
2096  * \param book Address book.
2097  * \param file XML file handle.
2098  */
2099 static gboolean addrbook_chkread_tree(AddressBookFile *book, XMLFile *file)
2100 {
2101 	/* GList *attr; */
2102 	gboolean retVal;
2103 
2104 	if (xml_get_dtd(file))
2105 		return FALSE;
2106 
2107 	if (xml_parse_next_tag(file))
2108 		return FALSE;
2109 
2110 	if (!xml_compare_tag(file, AB_ELTAG_ADDRESS_BOOK))
2111 		return FALSE;
2112 
2113 	/* attr = xml_get_current_tag_attr(file); */
2114 	/* addrbook_show_attribs( attr ); */
2115 
2116 	retVal = TRUE;
2117 	for (;;) {
2118 		if (!file->level)
2119 			break;
2120 		/* Get item tag */
2121 		if (xml_parse_next_tag(file))
2122 			longjmp(book->jumper, 1);
2123 
2124 		/* Get next tag (person, group or folder) */
2125 		if (xml_compare_tag(file, AB_ELTAG_PERSON))
2126 			addrbook_chkparse_person( book, file );
2127 		else if (xml_compare_tag(file, AB_ELTAG_GROUP))
2128 			addrbook_chkparse_group(book, file);
2129 		else if (xml_compare_tag(file, AB_ELTAG_FOLDER))
2130 			addrbook_chkparse_folder(book, file);
2131 	}
2132 	return retVal;
2133 }
2134 
2135 /**
2136  * Test address book file by parsing contents.
2137  * \param  book     Address book.
2138  * \param  fileName Filename of XML file.
2139  * \return Status code <i>MGU_SUCCESS</i> if file appears to be valid format.
2140  */
2141 gint addrbook_test_read_file(AddressBookFile *book, gchar *fileName)
2142 {
2143 	XMLFile *file = NULL;
2144 	gchar *fileSpec = NULL;
2145 
2146 	cm_return_val_if_fail(book != NULL, -1);
2147 
2148 	fileSpec = g_strconcat(book->path, G_DIR_SEPARATOR_S, fileName, NULL);
2149 	book->retVal = MGU_OPEN_FILE;
2150 	file = xml_open_file(fileSpec);
2151 	g_free(fileSpec);
2152 	if (file) {
2153 		book->retVal = MGU_BAD_FORMAT;
2154 		if (setjmp(book->jumper)) {
2155 			/* g_print( "Caught Ya!!!\n" ); */
2156 			xml_close_file(file);
2157 			return book->retVal;
2158 		}
2159 		if (addrbook_chkread_tree(book, file))
2160 			book->retVal = MGU_SUCCESS;
2161 
2162 		xml_close_file( file );
2163 	}
2164 	return book->retVal;
2165 }
2166 
2167 /**
2168  * Return link list of all persons in address book.  Note that the list
2169  * contains references to items. Do <b>*NOT*</b> attempt to use the
2170  * <code>addrbook_free_xxx()</code> functions... this will destroy the
2171  * addressbook data!
2172  * \param  book     Address book.
2173  * \return List of persons, or NULL if none.
2174  */
2175 GList *addrbook_get_all_persons(AddressBookFile *book)
2176 {
2177 	cm_return_val_if_fail(book != NULL, NULL);
2178 	return addrcache_get_all_persons(book->addressCache);
2179 }
2180 
2181 GList *addrbook_get_all_groups(AddressBookFile *book)
2182 {
2183 	cm_return_val_if_fail(book != NULL, NULL);
2184 	return addrcache_get_all_groups(book->addressCache);
2185 }
2186 
2187 /**
2188  * Add person and address data to address book.
2189  * \param  book    Address book.
2190  * \param  folder  Folder where to add person, or NULL for root folder.
2191  * \param  name    Common name.
2192  * \param  address EMail address.
2193  * \param  remarks Remarks.
2194  * \return Person added. Do not <b>*NOT*</b> to use the
2195  *         <code>addrbook_free_xxx()</code> functions... this will destroy
2196  *         the address book data.
2197  */
2198 ItemPerson *addrbook_add_contact(AddressBookFile *book, ItemFolder *folder,
2199 				 const gchar *name,const gchar *address,
2200 				 const gchar *remarks)
2201 {
2202 	ItemPerson *person;
2203 
2204 	cm_return_val_if_fail(book != NULL, NULL);
2205 	person = addrcache_add_contact(
2206 			book->addressCache, folder, name, address, remarks );
2207 	return person;
2208 }
2209 
2210 /**
2211  * Return file name for next address book file.
2212  * \param  book Address book.
2213  * \return File name, or <i>NULL</i> if could not create. This should be
2214  *         <code>g_free()</code> when done.
2215  */
2216 gchar *addrbook_guess_next_file(AddressBookFile *book)
2217 {
2218 	gchar *newFile = NULL;
2219 	GList *fileList = NULL;
2220 	gint fileNum = 1;
2221 	fileList = addrbook_get_bookfile_list(book);
2222 	if (fileList)
2223 		fileNum = 1 + book->maxValue;
2224 
2225 	newFile = addrbook_gen_new_file_name(fileNum);
2226 	g_list_free(fileList);
2227 	fileList = NULL;
2228 	return newFile;
2229 }
2230 
2231 void addrbook_delete_book_file(AddressBookFile *book)
2232 {
2233 	gchar *book_path;
2234 
2235 	if (!book->path || !book->fileName)
2236 		return;
2237 
2238 	book_path = g_strconcat(book->path, G_DIR_SEPARATOR_S,
2239 				book->fileName, NULL);
2240 	claws_unlink(book_path);
2241 	g_free(book_path);
2242 }
2243 
2244 /*
2245 * End of Source.
2246 */
2247 
2248 
2249