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