clamav plugin: fix signature count
[claws.git] / src / addritem.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2001-2003 Match Grun
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 2 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, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * General primitive address item objects.
22  */
23
24 #include <glib.h>
25 #include <stdio.h>
26 #include <string.h>
27
28 #include "utils.h"
29 #include "addritem.h"
30 #include "mgutils.h"
31
32 /**
33  * Create new email address item.
34  * \return Initialized email item.
35  */
36 ItemEMail *addritem_create_item_email( void ) {
37         ItemEMail *item;
38         item = g_new0( ItemEMail, 1 );
39         ADDRITEM_TYPE(item) = ITEMTYPE_EMAIL;
40         ADDRITEM_ID(item) = NULL;
41         ADDRITEM_NAME(item) = NULL;
42         ADDRITEM_PARENT(item) = NULL;
43         ADDRITEM_SUBTYPE(item) = 0;
44         item->address = NULL;
45         item->remarks = NULL;
46         return item;
47 }
48
49 /**
50  * Create a shallow copy of specified email address item.
51  * \param  item E-Mail to copy.
52  * \return Copy of email, or <i>NULL</i> if null argument supplied.
53  */
54 ItemEMail *addritem_copy_item_email( ItemEMail *item ) {
55         ItemEMail *itemNew = NULL;
56         if( item ) {
57                 itemNew = addritem_create_item_email();
58                 ADDRITEM_NAME(itemNew) = g_strdup( ADDRITEM_NAME(item) );
59                 itemNew->address = g_strdup( item->address );
60                 itemNew->remarks = g_strdup( item->remarks );
61         }
62         return itemNew;
63 }
64
65 /**
66  * Create a full copy (deep copy) of specified email address item.
67  * \param  item E-Mail to copy.
68  * \return Copy of email.
69  */
70 ItemEMail *addritem_copyfull_item_email( ItemEMail *item ) {
71         ItemEMail *itemNew = NULL;
72         if( item ) {
73                 itemNew = addritem_create_item_email();
74                 ADDRITEM_ID(itemNew) = g_strdup( ADDRITEM_ID(item) );
75                 ADDRITEM_NAME(itemNew) = g_strdup( ADDRITEM_NAME(item) );
76                 ADDRITEM_PARENT(itemNew) = ADDRITEM_PARENT(item);
77                 itemNew->address = g_strdup( item->address );
78                 itemNew->remarks = g_strdup( item->remarks );
79         }
80         return itemNew;
81 }
82
83 /**
84  * Specify ID for email.
85  * \param email E-Mail item.
86  * \param value ID.
87  */
88 void addritem_email_set_id( ItemEMail *email, const gchar *value ) {
89         ADDRITEM_ID(email) = mgu_replace_string( ADDRITEM_ID(email), value );
90 }
91
92 /**
93  * Specify alias for email.
94  * \param email E-Mail item.
95  * \param value Alias.
96  */
97 void addritem_email_set_alias( ItemEMail *email, const gchar *value ) {
98         ADDRITEM_NAME(email) = mgu_replace_string( ADDRITEM_NAME(email), value );
99 }
100
101 /**
102  * Specify address for email.
103  * \param email E-Mail item.
104  * \param value Address.
105  */
106 void addritem_email_set_address( ItemEMail *email, const gchar *value ) {
107         email->address = mgu_replace_string( email->address, value );
108 }
109
110 /**
111  * Specify remarks for email.
112  * \param email E-Mail item.
113  * \param value Remarks.
114  */
115 void addritem_email_set_remarks( ItemEMail *email, const gchar *value ) {
116         email->remarks = mgu_replace_string( email->remarks, value );
117 }
118
119 /**
120  * Free address item email object.
121  * \param item E-Mail item to free.
122  */
123 void addritem_free_item_email( ItemEMail *item ) {
124         g_return_if_fail( item != NULL );
125
126         /* Free internal stuff */
127         g_free( ADDRITEM_ID(item) );
128         g_free( ADDRITEM_NAME(item) );
129         g_free( item->address );
130         g_free( item->remarks );
131
132         ADDRITEM_OBJECT(item)->type = ITEMTYPE_NONE;
133         ADDRITEM_ID(item) = NULL;
134         ADDRITEM_NAME(item) = NULL;
135         ADDRITEM_PARENT(item) = NULL;
136         ADDRITEM_SUBTYPE(item) = 0;
137         item->address = NULL;
138         item->remarks = NULL;
139         g_free( item );
140 }
141
142 /**
143  * Create new attribute object.
144  * \return Initialized attribute object.
145  */
146 UserAttribute *addritem_create_attribute( void ) {
147         UserAttribute *item;
148         item = g_new0( UserAttribute, 1 );
149         item->uid = NULL;
150         item->name = NULL;
151         item->value = NULL;
152         return item;
153 }
154
155 /**
156  * Create copy (deep copy) of specified attribute.
157  * \param  item Attribute to copy.
158  * \return Copy of attribute, or <i>NULL</i> if null argument supplied.
159  */
160 UserAttribute *addritem_copy_attribute( UserAttribute *item ) {
161         UserAttribute *itemNew = NULL;
162         if( item ) {
163                 itemNew = addritem_create_attribute();
164                 itemNew->uid = g_strdup( item->uid );
165                 itemNew->name = g_strdup( item->name );
166                 itemNew->value = g_strdup( item->value );
167         }
168         return itemNew;
169 }
170
171 /**
172  * Specify ID for attribute.
173  * \param item Attribute object.
174  * \param value ID.
175  */
176 void addritem_attrib_set_id( UserAttribute *item, const gchar *value ) {
177         g_return_if_fail( item != NULL );
178         item->uid = mgu_replace_string( item->uid, value );
179 }
180
181 /**
182  * Specify name for attribute.
183  * \param item Attribute object.
184  * \param value Name.
185  */
186 void addritem_attrib_set_name( UserAttribute *item, const gchar *value ) {
187         g_return_if_fail( item != NULL );
188         item->name = mgu_replace_string( item->name, value );
189 }
190
191 /**
192  * Specify value for attribute.
193  * \param item Attribute object.
194  * \param value Value.
195  */
196 void addritem_attrib_set_value( UserAttribute *item, const gchar *value ) {
197         g_return_if_fail( item != NULL );
198         item->value = mgu_replace_string( item->value, value );
199 }
200
201 /**
202  * Free user attribute.
203  * \param item Attribute object to free.
204  */
205 void addritem_free_attribute( UserAttribute *item ) {
206         g_return_if_fail( item != NULL );
207         g_free( item->uid );
208         g_free( item->name );
209         g_free( item->value );
210         item->uid = NULL;
211         item->name = NULL;
212         item->value = NULL;
213         g_free( item );
214 }
215
216 /**
217  * Create new address book person.
218  * \return Initialized person object.
219  */
220 ItemPerson *addritem_create_item_person( void ) {
221         ItemPerson *person;
222         person = g_new0( ItemPerson, 1 );
223         ADDRITEM_TYPE(person) = ITEMTYPE_PERSON;
224         ADDRITEM_ID(person) = NULL;
225         ADDRITEM_NAME(person) = NULL;
226         ADDRITEM_PARENT(person) = NULL;
227         ADDRITEM_SUBTYPE(person) = 0;
228         person->firstName = NULL;
229         person->lastName = NULL;
230         person->nickName = NULL;
231         person->listEMail = NULL;
232         person->listAttrib = NULL;
233         person->externalID = NULL;
234         person->isOpened = FALSE;
235         return person;
236 }
237
238 /**
239  * Create a shallow copy of address book person object.
240  * \param  item Person to copy.
241  * \return Copy of person, or <i>NULL</i> if null argument supplied.
242  */
243 ItemPerson *addritem_copy_item_person( ItemPerson *item ) {
244         ItemPerson *itemNew;
245
246         itemNew = NULL;
247         if( item ) {
248                 itemNew = addritem_create_item_person();
249                 ADDRITEM_NAME(itemNew) = g_strdup( ADDRITEM_NAME(item) );
250                 itemNew->firstName = g_strdup( item->firstName );
251                 itemNew->lastName = g_strdup( item->lastName );
252                 itemNew->nickName = g_strdup( item->nickName );
253                 itemNew->externalID = g_strdup( item->externalID );
254         }
255         return itemNew;
256 }
257
258 /**
259  * Specify ID for person object.
260  * \param person Person object.
261  * \param value ID.
262  */
263 void addritem_person_set_id( ItemPerson *person, const gchar *value ) {
264         ADDRITEM_ID(person) = mgu_replace_string( ADDRITEM_ID(person), value );
265 }
266
267 /**
268  * Specify first name for person object.
269  * \param person Person object.
270  * \param value Name.
271  */
272 void addritem_person_set_first_name( ItemPerson *person, const gchar *value ) {
273         person->firstName = mgu_replace_string( person->firstName, value );
274 }
275
276 /**
277  * Specify last name for person object.
278  * \param person Person object.
279  * \param value name.
280  */
281 void addritem_person_set_last_name( ItemPerson *person, const gchar *value ) {
282         person->lastName = mgu_replace_string( person->lastName, value );
283 }
284
285 /**
286  * Specify nick name for person object.
287  * \param person Person object.
288  * \param value name.
289  */
290 void addritem_person_set_nick_name( ItemPerson *person, const gchar *value ) {
291         person->nickName = mgu_replace_string( person->nickName, value );
292 }
293
294 /**
295  * Specify common name for person object.
296  * \param person Person object.
297  * \param value name.
298  */
299 void addritem_person_set_common_name( ItemPerson *person, const gchar *value ) {
300         ADDRITEM_NAME(person) = mgu_replace_string( ADDRITEM_NAME(person), value );
301 }
302
303 /**
304  * Specify external ID for person object.
305  * \param person Person object.
306  * \param value ID.
307  */
308 void addritem_person_set_external_id( ItemPerson *person, const gchar *value ) {
309         person->externalID = mgu_replace_string( person->externalID, value );
310 }
311
312 /**
313  * Specify value of open indicator for person object. This is typically used to
314  * simplify open/close folders in the address book GUI.
315  * \param person Person object.
316  * \param value  Value for indicator. Set to <i>TRUE</i> if opened.
317  */
318 void addritem_person_set_opened( ItemPerson *person, const gboolean value ) {
319         person->isOpened = value;
320 }
321
322 /**
323  * Test whether person's data is empty.
324  * \param  person Person to test.
325  * \return <i>TRUE</i> if empty.
326  */
327 gboolean addritem_person_empty( ItemPerson *person ) {
328         gchar *t;
329
330         if( person == NULL ) return FALSE;
331
332         t = ADDRITEM_NAME(person);
333         if( t != NULL && strlen( t ) > 0 ) return FALSE;
334
335         t = person->firstName;
336         if( t != NULL && strlen( t ) > 0 ) return FALSE;
337
338         t = person->lastName;
339         if( t != NULL && strlen( t ) > 0 ) return FALSE;
340
341         t = person->nickName;
342         if( t != NULL && strlen( t ) > 0 ) return FALSE;
343
344         if( person->listEMail  != NULL ) return FALSE;
345         if( person->listAttrib != NULL ) return FALSE;
346
347         return TRUE;
348 }
349
350 /**
351  * Free linked list of item addresses; both addresses and the list are freed.
352  * It is assumed that addresses are *NOT* contained within some other
353  * container.
354  * \param list List of addresses to be freed.
355  */
356 void addritem_free_list_email( GList *list ) {
357         GList *node = list;
358         while( node ) {
359                 ItemEMail *email = node->data;
360
361                 addritem_free_item_email( email );
362                 node->data = NULL;
363                 node = g_list_next( node );
364         }
365         g_list_free( list );
366         list = NULL;
367 }
368
369 /**
370  * Free linked list of attributes; both attributes and the list are freed.
371  * It is assumed that attributes are *NOT* contained within some other
372  * container.
373  * \param list List of attributes to be freed.
374  */
375 void addritem_free_list_attribute( GList *list ) {
376         GList *node = list;
377         while( node ) {
378                 addritem_free_attribute( node->data );
379                 node->data = NULL;
380                 node = g_list_next( node );
381         }
382         g_list_free( list );
383 }
384
385 /**
386  * Free address person object.
387  * \param person Person object to free.
388  */
389 void addritem_free_item_person( ItemPerson *person ) {
390         g_return_if_fail( person != NULL );
391
392         /* Free internal stuff */
393         g_free( ADDRITEM_ID(person) );
394         g_free( ADDRITEM_NAME(person) );
395         g_free( person->firstName );
396         g_free( person->lastName );
397         g_free( person->nickName );
398         g_free( person->externalID );
399         g_list_free( person->listEMail );
400         addritem_free_list_attribute( person->listAttrib );
401
402         ADDRITEM_OBJECT(person)->type = ITEMTYPE_NONE;
403         ADDRITEM_ID(person) = NULL;
404         ADDRITEM_NAME(person) = NULL;
405         ADDRITEM_PARENT(person) = NULL;
406         ADDRITEM_SUBTYPE(person) = 0;
407         person->firstName = NULL;
408         person->lastName = NULL;
409         person->nickName = NULL;
410         person->externalID = NULL;
411         person->listEMail = NULL;
412         person->listAttrib = NULL;
413
414         g_free( person );
415 }
416
417 /**
418  * Print E-Mail address object for debug.
419  * \param item   Item to print.
420  * \param stream Output stream.
421  */
422 void addritem_print_item_email( ItemEMail *item, FILE *stream ) {
423         g_return_if_fail( item != NULL );
424         fprintf( stream, "\t\tt/id: %d : '%s'\n", ADDRITEM_TYPE(item), ADDRITEM_ID(item) );
425         fprintf( stream, "\t\tsubty: %d\n", ADDRITEM_SUBTYPE(item) );
426         fprintf( stream, "\t\talis: '%s'\n", ADDRITEM_NAME(item) );
427         fprintf( stream, "\t\taddr: '%s'\n", item->address );
428         fprintf( stream, "\t\trems: '%s'\n", item->remarks );
429         fprintf( stream, "\t\t---\n" );
430 }
431
432 /**
433  * Print user attribute object for debug.
434  * \param item   Attribute to print.
435  * \param stream Output stream.
436  */
437 void addritem_print_attribute( UserAttribute *item, FILE *stream ) {
438         g_return_if_fail( item != NULL );
439         fprintf( stream, "\t\tuid  : '%s'\n", item->uid );
440         fprintf( stream, "\t\tname : '%s'\n", item->name );
441         fprintf( stream, "\t\tvalue: '%s'\n", item->value );
442         fprintf( stream, "\t\t---\n" );
443 }
444
445 /**
446  * Print person item for debug.
447  * \param person Person to print.
448  * \param stream Output stream.
449  */
450 void addritem_print_item_person( ItemPerson *person, FILE *stream ) {
451         GList *node;
452         g_return_if_fail( person != NULL );
453         fprintf( stream, "Person:\n" );
454         fprintf( stream, "\tt/uid: %d : '%s'\n", ADDRITEM_TYPE(person), ADDRITEM_ID(person) );
455         fprintf( stream, "\tsubty: %d\n", ADDRITEM_SUBTYPE(person) );
456         fprintf( stream, "\tcommn: '%s'\n", ADDRITEM_NAME(person) );
457         fprintf( stream, "\tfirst: '%s'\n", person->firstName );
458         fprintf( stream, "\tlast : '%s'\n", person->lastName );
459         fprintf( stream, "\tnick : '%s'\n", person->nickName );
460         fprintf( stream, "\textID: '%s'\n", person->externalID );
461         fprintf( stream, "\teMail:\n" );
462         fprintf( stream, "\t---\n" );
463         node = person->listEMail;
464         while( node ) {
465                 addritem_print_item_email( node->data, stream );
466                 node = g_list_next( node );
467         }
468         fprintf( stream, "\tuAttr:\n" );
469         fprintf( stream, "\t---\n" );
470         node = person->listAttrib;
471         while( node ) {
472                 addritem_print_attribute( node->data, stream );
473                 node = g_list_next( node );
474         }
475         fprintf( stream, "\t===\n" );
476 }
477
478 /**
479  * Add E-Mail address object to person.
480  * \param  person Person.
481  * \param  email  E-Mail object to add.
482  * \return <i>TRUE</i> if E-Mail added.
483  */
484 gboolean addritem_person_add_email( ItemPerson *person, ItemEMail *email ) {
485         GList *node;
486
487         g_return_val_if_fail( person != NULL, FALSE );
488         g_return_val_if_fail( email != NULL, FALSE );
489
490         node = person->listEMail;
491         while( node ) {
492                 if( node->data == email ) return FALSE;
493                 node = g_list_next( node );
494         }
495         person->listEMail = g_list_append( person->listEMail, email );
496         ADDRITEM_PARENT(email) = ADDRITEM_OBJECT(person);
497         return TRUE;
498 }
499
500 /**
501  * Return email object with specified ID for specified person.
502  * \param  person Person object.
503  * \param  eid    EMail ID.
504  * \return EMail object, or <i>NULL</i> if not found.
505  */
506 ItemEMail *addritem_person_get_email( ItemPerson *person, const gchar *eid ) {
507         ItemEMail *email = NULL;
508         GList *node;
509
510         g_return_val_if_fail( person != NULL, NULL );
511         if( eid == NULL || *eid == '\0' ) return NULL;
512
513         /* Look for email */
514         node = person->listEMail;
515         while( node ) {
516                 AddrItemObject *objE = node->data;
517                 gchar *ide = ADDRITEM_ID(objE);
518                 if( ide ) {
519                         if( strcmp( ide, eid ) == 0 ) {
520                                 email = ( ItemEMail * ) objE;
521                         }
522                 }
523                 node = g_list_next( node );
524         }
525         return email;
526 }
527
528 /**
529  * Remove email address with specified ID for specified person.
530  * \param  person Person object.
531  * \param  eid    EMail ID.
532  * \return EMail object, or <i>NULL</i> if not found. Note that object should
533  *         still be freed after calling this function.
534  */
535 ItemEMail *addritem_person_remove_email_id( ItemPerson *person, const gchar *eid ) {
536         ItemEMail *email = NULL;
537         GList *node;
538
539         g_return_val_if_fail( person != NULL, NULL );
540         if( eid == NULL || *eid == '\0' ) return NULL;
541
542         /* Look for email */
543         node = person->listEMail;
544         while( node ) {
545                 AddrItemObject *objE = node->data;
546                 gchar *ide = ADDRITEM_ID(objE);
547                 if( ide ) {
548                         if( strcmp( ide, eid ) == 0 ) {
549                                 email = ( ItemEMail * ) objE;
550                         }
551                 }
552                 node = g_list_next( node );
553         }
554
555         if( email ) {
556                 /* Remove email from person's address list */
557                 if( person->listEMail ) {
558                         person->listEMail = g_list_remove( person->listEMail, email );
559                 }
560                 /* Unlink reference to person. */
561                 ADDRITEM_PARENT(email) = NULL;
562         }
563         return email;
564 }
565
566 /**
567  * Remove email address for specified person.
568  * \param  person Person.
569  * \param  email  EMail to remove.
570  * \return EMail object, or <i>NULL</i> if not found. Note that object should
571  *         still be freed after calling this method.
572  */
573 ItemEMail *addritem_person_remove_email( ItemPerson *person, ItemEMail *email ) {
574         gboolean found = FALSE;
575         GList *node;
576
577         g_return_val_if_fail( person != NULL, NULL );
578         if( email == NULL ) return NULL;
579
580         /* Look for email */
581         node = person->listEMail;
582         while( node ) {
583                 if( node-> data == email ) {
584                         found = TRUE;
585                         break;
586                 }
587                 node = g_list_next( node );
588         }
589
590         if( found ) {
591                 /* Remove email from person's address list */
592                 if( person->listEMail ) {
593                         person->listEMail = g_list_remove( person->listEMail, email );
594                 }
595                 /* Unlink reference to person. */
596                 ADDRITEM_PARENT(email) = NULL;
597                 return email;
598         }
599         return NULL;
600 }
601
602 /**
603  * Add user attribute to specified person.
604  * \param  person Person.
605  * \param  attrib Attribute to add.
606  * \return <i>TRUE</i> if item added.
607  */
608 void addritem_person_add_attribute(
609                         ItemPerson *person, UserAttribute *attrib )
610 {
611         g_return_if_fail( person != NULL );
612         person->listAttrib = g_list_append( person->listAttrib, attrib );
613 }
614
615 /**
616  * Return attribute with specified ID for person.
617  * \param  person Person object.
618  * \param  aid    Attribute ID.
619  * \return Reference to UserAttribute object, or <i>NULL</i> if not found.
620  */
621 UserAttribute *addritem_person_get_attribute(
622                         ItemPerson *person, const gchar *aid )
623 {
624         UserAttribute *attrib = NULL;
625         GList *node;
626
627         g_return_val_if_fail( person != NULL, NULL );
628         if( aid == NULL || *aid == '\0' ) return NULL;
629
630         /* Look for attribute */
631         node = person->listAttrib;
632         while( node ) {
633                 UserAttribute *attr = node->data;
634                 gchar *ida = attr->uid;
635                 if( ida ) {
636                         if( strcmp( ida, aid ) == 0 ) {
637                                 attrib = attr;
638                         }
639                 }
640                 node = g_list_next( node );
641         }
642         return attrib;
643 }
644
645 /**
646  * Remove attribute with specified ID from person.
647  * \param  person Person object.
648  * \param  aid    Attribute ID to remove.
649  * \return UserAttribute object, or <i>NULL</i> if not found. Note that
650  *         attribute object should still be freed after calling this method.
651  */
652 UserAttribute *addritem_person_remove_attrib_id(
653                         ItemPerson *person, const gchar *aid )
654 {
655         UserAttribute *attrib = NULL;
656         GList *node;
657
658         g_return_val_if_fail( person != NULL, NULL );
659         if( aid == NULL || *aid == '\0' ) return NULL;
660
661         /* Look for attribute */
662         node = person->listAttrib;
663         while( node ) {
664                 UserAttribute *attr = node->data;
665                 gchar *ida = attr->uid;
666                 if( ida ) {
667                         if( strcmp( ida, aid ) == 0 ) {
668                                 attrib = attr;
669                         }
670                 }
671                 node = g_list_next( node );
672         }
673
674         /* Remove email from person's address list */
675         if( person->listAttrib ) {
676                 person->listAttrib = g_list_remove( person->listAttrib, attrib );
677         }
678         return attrib;
679 }
680
681 /**
682  * Remove attribute from person.
683  * \param  person Person.
684  * \param  attrib Attribute to remove.
685  * \return UserAttribute object to remove. Note that attribute object should
686  *         still be freed.
687  */
688 UserAttribute *addritem_person_remove_attribute(
689                         ItemPerson *person, UserAttribute *attrib )
690 {
691         gboolean found = FALSE;
692         GList *node;
693
694         g_return_val_if_fail( person != NULL, NULL );
695         if( attrib == NULL ) return NULL;
696
697         /* Look for attribute */
698         node = person->listAttrib;
699         while( node ) {
700                 if( node-> data == attrib ) {
701                         found = TRUE;
702                         break;
703                 }
704                 node = g_list_next( node );
705         }
706
707         if( found ) {
708                 /* Remove attribute */
709                 if( person->listAttrib ) {
710                         person->listAttrib = g_list_remove( person->listAttrib, attrib );
711                 }
712         }
713         return attrib;
714 }
715
716 /**
717  * Create new address book group object.
718  * \return Initialized group object.
719  */
720 ItemGroup *addritem_create_item_group( void ) {
721         ItemGroup *group;
722
723         group = g_new0( ItemGroup, 1 );
724         ADDRITEM_TYPE(group) = ITEMTYPE_GROUP;
725         ADDRITEM_ID(group) = NULL;
726         ADDRITEM_NAME(group) = NULL;
727         ADDRITEM_PARENT(group) = NULL;
728         ADDRITEM_SUBTYPE(group) = 0;
729         group->remarks = NULL;
730         group->listEMail = NULL;
731         return group;
732 }
733
734 /**
735  * Copy (deep copy) address book group.
736  * \param  item Group to copy.
737  * \return Copy of the group object, or <i>NULL</i> if null argument supplied.
738  */
739 ItemGroup *addritem_copy_item_group( ItemGroup *item ) {
740         ItemGroup *itemNew;
741
742         itemNew = g_new0( ItemGroup, 1 );
743         if( item ) {
744                 itemNew = addritem_create_item_group();
745                 ADDRITEM_NAME(itemNew) = g_strdup( ADDRITEM_NAME(item) );
746                 itemNew->remarks = g_strdup( item->remarks );
747         }
748         return itemNew;
749 }
750
751 /**
752  * Specify ID to be used for group.
753  * \param group Group object.
754  * \param value ID of group.
755  */
756 void addritem_group_set_id( ItemGroup *group, const gchar *value ) {
757         ADDRITEM_ID(group) = mgu_replace_string( ADDRITEM_ID(group), value );
758 }
759
760 /**
761  * Specify name to be used for group.
762  * \param group Group object.
763  * \param value Name of group.
764  */
765 void addritem_group_set_name( ItemGroup *group, const gchar *value ) {
766         ADDRITEM_NAME(group) = mgu_replace_string( ADDRITEM_NAME(group), value );
767 }
768
769 /**
770  * Specify remarks to be used for group.
771  * \param group Group object.
772  * \param value Remarks for group.
773  */
774 void addritem_group_set_remarks( ItemGroup *group, const gchar *value ) {
775         group->remarks = mgu_replace_string( group->remarks, value );
776 }
777
778 /**
779  * Free address group object.
780  * \param group Group to free.
781  */
782 void addritem_free_item_group( ItemGroup *group ) {
783         g_return_if_fail( group != NULL );
784
785         /* Free internal stuff */
786         g_free( ADDRITEM_ID(group) );
787         g_free( ADDRITEM_NAME(group) );
788         g_free( group->remarks );
789         mgu_clear_list( group->listEMail );
790         g_list_free( group->listEMail );
791
792         ADDRITEM_TYPE(group) = ITEMTYPE_NONE;
793         ADDRITEM_ID(group) = NULL;
794         ADDRITEM_NAME(group) = NULL;
795         ADDRITEM_PARENT(group) = NULL;
796         ADDRITEM_SUBTYPE(group) = 0;
797         group->remarks = NULL;
798         group->listEMail = NULL;
799
800         g_free( group );
801 }
802
803 /**
804  * Add EMail address to group. Note that a reference to an E-Mail item is
805  * added to a group. A person object is the only container that for an
806  * address.
807  * \param  group Group.
808  * \param  email E-Mail object.
809  * \return <i>TRUE</i> if email item added.
810  */
811 gboolean addritem_group_add_email( ItemGroup *group, ItemEMail *email ) {
812         GList *node;
813
814         g_return_val_if_fail( group != NULL, FALSE );
815         g_return_val_if_fail( email != NULL, FALSE );
816
817         node = group->listEMail;
818         while( node ) {
819                 if( node->data == email ) return FALSE;
820                 node = g_list_next( node );
821         }
822         group->listEMail = g_list_append( group->listEMail, email );
823         return TRUE;
824 }
825
826 /**
827  * Remove email address object for specified group.
828  * \param  group Group from which to remove address.
829  * \param  email EMail to remove
830  * \return EMail object, or <i>NULL if email not found in group. Note that
831  *         this object is referenced (linked) to a group and should *NOT*
832  *         be freed. An E-Mail object object should only be freed after
833  *         removing from a person.
834  */
835 ItemEMail *addritem_group_remove_email( ItemGroup *group, ItemEMail *email ) {
836         if( group && email ) {
837                 GList *node = group->listEMail;
838                 while( node ) {
839                         if( node->data == email ) {
840                                 group->listEMail = g_list_remove( group->listEMail, email );
841                                 return email;
842                         }
843                         node = g_list_next( node );
844                 }
845         }
846         return NULL;
847 }
848
849 /**
850  * Remove email address of specified ID for specified group.
851  * \param  group Group from which to remove address.
852  * \param  eid  EMail ID.
853  * \return EMail object, or <i>NULL</i> if email not found in group. Note that
854  *         this object is referenced (linked) to a group and should *NOT* be
855  *         freed. An E-Mail object should only be freed after removing from a
856  *         person.
857  */
858 ItemEMail *addritem_group_remove_email_id( ItemGroup *group, const gchar *eid ) {
859         if( group ) {
860                 GList *node = group->listEMail;
861                 while( node ) {
862                         ItemEMail *email = ( ItemEMail * ) node->data;
863                         if( strcmp( ADDRITEM_ID( email ), eid ) == 0 ) {
864                                 group->listEMail = g_list_remove( group->listEMail, email );
865                                 return email;
866                         }
867                         node = g_list_next( node );
868                 }
869         }
870         return NULL;
871 }
872
873 /**
874  * Print address group item for debug.
875  * \param group  Group to print.
876  * \param stream Output stream.
877  */
878 void addritem_print_item_group( ItemGroup *group, FILE *stream ) {
879         GList *node;
880         ItemPerson *person;
881         ItemEMail *item;
882         g_return_if_fail( group != NULL );
883         fprintf( stream, "Group:\n" );
884         fprintf( stream, "\tt/u: %d : '%s'\n", ADDRITEM_TYPE(group), ADDRITEM_ID(group) );
885         fprintf( stream, "\tsub: %d\n", ADDRITEM_SUBTYPE(group) );
886         fprintf( stream, "\tgrp: '%s'\n", ADDRITEM_NAME(group) );
887         fprintf( stream, "\trem: '%s'\n", group->remarks );
888         fprintf( stream, "\t---\n" );
889         node = group->listEMail;
890         while( node ) {
891                 item = node->data;
892                 person = ( ItemPerson * ) ADDRITEM_PARENT(item);
893                 if( person ) {
894                         fprintf( stream, "\t\tpid : '%s'\n", ADDRITEM_ID(person) );
895                         fprintf( stream, "\t\tcomn: '%s'\n", ADDRITEM_NAME(person) );
896                 }
897                 else {
898                         fprintf( stream, "\t\tpid : ???\n" );
899                         fprintf( stream, "\t\tcomn: ???\n" );
900                 }
901                 addritem_print_item_email( item, stream );
902                 node = g_list_next( node );
903         }
904         fprintf( stream, "\t***\n" );
905 }
906
907 /**
908  * Create new address folder.
909  * \return Initialized address folder object.
910  */
911 ItemFolder *addritem_create_item_folder( void ) {
912         ItemFolder *folder;
913         folder = g_new0( ItemFolder, 1 );
914         ADDRITEM_TYPE(folder) = ITEMTYPE_FOLDER;
915         ADDRITEM_ID(folder) = NULL;
916         ADDRITEM_NAME(folder) = NULL;
917         ADDRITEM_PARENT(folder) = NULL;
918         ADDRITEM_SUBTYPE(folder) = 0;
919         folder->remarks = NULL;
920         folder->isRoot = FALSE;
921         folder->listItems = NULL;
922         folder->listFolder = NULL;
923         folder->listPerson = NULL;
924         folder->listGroup = NULL;
925         folder->folderType = ADDRFOLDER_NONE;
926         folder->folderData = NULL;
927         folder->isHidden = FALSE;
928         return folder;
929 }
930
931 /**
932  * Copy address book folder. Note that only the folder and not its contents are
933  * copied.
934  * \param  item Folder to copy.
935  * \return A copy of the folder, or <i>NULL</i> if null argument supplied.
936  */
937 ItemFolder *addritem_copy_item_folder( ItemFolder *item ) {
938         ItemFolder *itemNew;
939
940         itemNew = g_new0( ItemFolder, 1 );
941         if( item ) {
942                 itemNew = addritem_create_item_folder();
943                 ADDRITEM_NAME(itemNew) = g_strdup( ADDRITEM_NAME(item) );
944                 itemNew->folderType = item->folderType;
945         }
946         return itemNew;
947 }
948
949 /**
950  * Specify ID to be used for folder.
951  * \param folder Folder.
952  * \param value  ID.
953  */
954 void addritem_folder_set_id( ItemFolder *folder, const gchar *value ) {
955         ADDRITEM_ID(folder) = mgu_replace_string( ADDRITEM_ID(folder), value );
956 }
957
958 /**
959  * Specify name to be used for folder.
960  * \param folder Folder.
961  * \param value  Name.
962  */
963 void addritem_folder_set_name( ItemFolder *folder, const gchar *value ) {
964         ADDRITEM_NAME(folder) = mgu_replace_string( ADDRITEM_NAME(folder), value );
965 }
966
967 /**
968  * Specify remarks to be used for folder.
969  * \param folder Folder.
970  * \param value  Remarks.
971  */
972 void addritem_folder_set_remarks( ItemFolder *folder, const gchar *value ) {
973         folder->remarks = mgu_replace_string( folder->remarks, value );
974 }
975
976 /**
977  * Specify visibility of folder.
978  * \param folder Folder.
979  * \param value  Set to <code>TRUE</code> to hide folder.
980  */
981 void addritem_folder_set_hidden( ItemFolder *folder, const gboolean value ) {
982         folder->isHidden = value;
983 }
984
985 /**
986  * Free address folder. Note: this does not free up the lists of children
987  * (folders, groups and person). This should be done prior to calling this
988  * function.
989  * \param folder Folder to free.
990  */
991 void addritem_free_item_folder( ItemFolder *folder ) {
992         g_return_if_fail( folder != NULL );
993
994         /* Free internal stuff */
995         g_free( ADDRITEM_ID(folder) );
996         g_free( ADDRITEM_NAME(folder) );
997         g_free( folder->remarks );
998         mgu_clear_list( folder->listItems );
999         g_list_free( folder->listItems );
1000
1001         ADDRITEM_TYPE(folder) = ITEMTYPE_NONE;
1002         ADDRITEM_ID(folder) = NULL;
1003         ADDRITEM_NAME(folder) = NULL;
1004         ADDRITEM_PARENT(folder) = NULL;
1005         ADDRITEM_SUBTYPE(folder) = 0;
1006         folder->isRoot = FALSE;
1007         folder->remarks = NULL;
1008         folder->listItems = NULL;
1009         folder->listFolder = NULL;
1010         folder->listGroup = NULL;
1011         folder->listPerson = NULL;
1012         folder->folderType = ADDRFOLDER_NONE;
1013         folder->folderData = NULL;
1014         folder->isHidden = FALSE;
1015
1016         g_free( folder );
1017 }
1018
1019 /**
1020  * Free up folders recursively. Note: this only frees up the lists of
1021  * children and *NOT* the children objects (folders, groups and person).
1022  * This should be done prior to calling this function.
1023  * \param parent Parent folder object to be processed.
1024  */
1025 void addritem_free_item_folder_recurse( ItemFolder *parent ) {
1026         GList *node = parent->listFolder;
1027
1028         while( node ) {
1029                 ItemFolder *folder = node->data;
1030                 addritem_free_item_folder_recurse( folder );
1031                 node = g_list_next( node );
1032         }
1033         g_list_free( parent->listPerson );
1034         g_list_free( parent->listGroup );
1035         g_list_free( parent->listFolder );
1036         parent->listPerson = NULL;
1037         parent->listGroup = NULL;
1038         parent->listFolder = NULL;
1039 }
1040
1041 /**
1042  * Free up list of person objects contained in specified folder.
1043  * \param folder Folder to process.
1044  */
1045 void addritem_folder_free_person( ItemFolder *folder ) {
1046         GList *node;
1047
1048         g_return_if_fail( folder != NULL );
1049         
1050         /* Free up folder of persons. */
1051         node = folder->listPerson;
1052         while( node ) {
1053                 ItemPerson *person = node->data;
1054                 addritem_free_item_person( person );
1055                 person = NULL;
1056                 node = g_list_next( node );
1057         }
1058 }
1059
1060 /**
1061  * Add person into folder.
1062  * \param  folder Folder.
1063  * \param  item   Person to add.
1064  * \return <i>TRUE</i> if person added.
1065  */
1066 gboolean addritem_folder_add_person( ItemFolder *folder, ItemPerson *item ) {
1067         g_return_val_if_fail( folder != NULL, FALSE );
1068         g_return_val_if_fail( item != NULL, FALSE );
1069
1070         folder->listPerson = g_list_append( folder->listPerson, item );
1071         ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
1072         return TRUE;
1073 }
1074
1075 /**
1076  * Add folder into folder.
1077  * \param  folder Folder.
1078  * \param  item   Folder to add.
1079  * \return <i>TRUE</i> if folder added.
1080  */
1081 gboolean addritem_folder_add_folder( ItemFolder *folder, ItemFolder *item ) {
1082         g_return_val_if_fail( folder != NULL, FALSE );
1083         g_return_val_if_fail( item != NULL, FALSE );
1084
1085         folder->listFolder = g_list_append( folder->listFolder, item );
1086         ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
1087         return TRUE;
1088 }
1089
1090 /**
1091  * Add group into folder.
1092  * \param  folder Folder.
1093  * \param  item   Group to add.
1094  * \return <i>TRUE</i> if group added.
1095  */
1096 gboolean addritem_folder_add_group( ItemFolder *folder, ItemGroup *item ) {
1097         g_return_val_if_fail( folder != NULL, FALSE );
1098         g_return_val_if_fail( item != NULL, FALSE );
1099
1100         folder->listGroup = g_list_append( folder->listGroup, item );
1101         ADDRITEM_PARENT(item) = ADDRITEM_OBJECT(folder);
1102         return TRUE;
1103 }
1104
1105 /**
1106  * Print address folder item contents for debug.
1107  * \param folder Folder to process.
1108  * \param stream Output stream.
1109  */
1110 void addritem_print_item_folder( ItemFolder *folder, FILE *stream ) {
1111         GList *node;
1112         /* ItemPerson *person; */
1113         ItemFolder *parent;
1114
1115         g_return_if_fail( folder != NULL );
1116
1117         fprintf( stream, "Folder:\n" );
1118         fprintf( stream, "\tt/u: %d : '%s'\n", ADDRITEM_TYPE(folder), ADDRITEM_ID(folder) );
1119         fprintf( stream, "\tsub: %d\n", ADDRITEM_SUBTYPE(folder) );
1120         fprintf( stream, "\tnam: '%s'\n", ADDRITEM_NAME(folder) );
1121         fprintf( stream, "\trem: '%s'\n", folder->remarks );
1122         fprintf( stream, "\ttyp: %d\n", folder->folderType );
1123         fprintf( stream, "\thid: %s\n", folder->isHidden ? "hidden" : "visible" );
1124         fprintf( stream, "\t---\n" );
1125         parent = ( ItemFolder * ) ADDRITEM_PARENT(folder);
1126         if( parent ) {
1127                 fprintf( stream, "\tpar: %s : %s\n", ADDRITEM_ID(parent), ADDRITEM_NAME(parent) );
1128         }
1129         else {
1130                 fprintf( stream, "\tpar: NULL\n" );
1131         }
1132         node = folder->listFolder;
1133         while( node ) {
1134                 AddrItemObject *aio = node->data;
1135                 if( aio ) {
1136                         if( aio->type == ITEMTYPE_FOLDER ) {
1137                                 ItemFolder *item = ( ItemFolder * ) aio;
1138                                 addritem_print_item_folder( item, stream );
1139                         }
1140                 }
1141                 else {
1142                         fprintf( stream, "\t\tpid : ???\n" );
1143                 }
1144
1145                 node = g_list_next( node );
1146         }
1147
1148         node = folder->listPerson;
1149         while( node ) {
1150                 AddrItemObject *aio = node->data;
1151                 if( aio ) {
1152                         if( aio->type == ITEMTYPE_PERSON ) {
1153                                 ItemPerson *item = ( ItemPerson * ) aio;
1154                                 addritem_print_item_person( item, stream );
1155                         }
1156                 }
1157                 else {
1158                         fprintf( stream, "\t\tpid : ???\n" );
1159                 }
1160
1161                 node = g_list_next( node );
1162         }
1163
1164         node = folder->listGroup;
1165         while( node ) {
1166                 AddrItemObject *aio = node->data;
1167                 if( aio ) {
1168                         if( aio->type == ITEMTYPE_GROUP ) {
1169                                 ItemGroup *item = ( ItemGroup * ) aio;
1170                                 addritem_print_item_group( item, stream );
1171                         }
1172                 }
1173                 else {
1174                         fprintf( stream, "\t\tpid : ???\n" );
1175                 }
1176                 node = g_list_next( node );
1177         }
1178         fprintf( stream, "\t###\n" );
1179 }
1180
1181 /**
1182  * Print address item for debug.
1183  * \param aio    Address item to format.
1184  * \param stream Output stream.
1185  */
1186 void addritem_print_item( AddrItemObject *aio, FILE *stream ) {
1187         g_return_if_fail( aio != NULL );
1188
1189         if( aio->type == ITEMTYPE_PERSON ) {
1190                 ItemPerson *item = ( ItemPerson * ) aio;
1191                 addritem_print_item_person( item, stream );
1192         }
1193         else if( aio->type == ITEMTYPE_EMAIL ) {
1194                 ItemEMail *item = ( ItemEMail * ) aio;
1195                 addritem_print_item_email( item, stream );
1196         }
1197         else if( aio->type == ITEMTYPE_GROUP ) {
1198                 ItemGroup *item = ( ItemGroup * ) aio;
1199                 addritem_print_item_group( item, stream );
1200         }
1201         else if( aio->type == ITEMTYPE_FOLDER ) {
1202                 ItemFolder *item = ( ItemFolder * ) aio;
1203                 addritem_print_item_folder( item, stream );
1204         }
1205 }
1206
1207 /**
1208  * Return link list of persons for specified folder. Note that the list contains
1209  * references to items and should be g_free() when done. Do *NOT* attempt to use the
1210  * addritem_free_xxx() functions... this will destroy the addressbook data!
1211  *
1212  * \param  folder Folder to process.
1213  * \return List of items, or <i>NULL</i> if none.
1214  */
1215 GList *addritem_folder_get_person_list( ItemFolder *folder ) {
1216         GList *list = NULL;
1217         GList *node = NULL;
1218
1219         g_return_val_if_fail( folder != NULL, NULL );
1220
1221         node = folder->listPerson;
1222         while( node ) {
1223                 ItemPerson *person = node->data;
1224                 list = g_list_append( list, person );
1225                 node = g_list_next( node );
1226         }
1227         return list;
1228 }
1229
1230 /**
1231  * Return link list of groups for specified folder. Note that the list contains
1232  * references to items and should be g_free() when done. Do *NOT* attempt to use the
1233  * addritem_free_xxx() functions... this will destroy the addressbook data!
1234  *
1235  * \param  folder Folder to process.
1236  * \return List of items, or <i>NULL</i> if none.
1237  */
1238 GList *addritem_folder_get_group_list( ItemFolder *folder ) {
1239         GList *list = NULL;
1240         GList *node = NULL;
1241
1242         g_return_val_if_fail( folder != NULL, NULL );
1243
1244         node = folder->listGroup;
1245         while( node ) {
1246                 ItemGroup *group = node->data;
1247                 list = g_list_append( list, group );
1248                 node = g_list_next( node );
1249         }
1250         return list;
1251 }
1252
1253 /**
1254  * Move person's email item.
1255  * \param  person     Person.
1256  * \param  itemMove   Item to move.
1257  * \param  itemTarget Target item before which to move item.
1258  * \return Reference to item that was moved, or <i>NULL</i> if null arguments
1259  *         supplied.
1260  */
1261
1262 ItemEMail *addritem_move_email_before(
1263                 ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget )
1264 {
1265         gint posT, posM;
1266
1267         g_return_val_if_fail( person != NULL, NULL );
1268
1269         if( itemTarget == NULL ) return NULL;
1270         if( itemMove == NULL ) return NULL;
1271         if( itemMove == itemTarget ) return itemMove;
1272
1273         posT = g_list_index( person->listEMail, itemTarget );
1274         if( posT < 0 ) return NULL;
1275         posM = g_list_index( person->listEMail, itemMove );
1276         if( posM < 0 ) return NULL;
1277         person->listEMail = g_list_remove( person->listEMail, itemMove );
1278         person->listEMail = g_list_insert( person->listEMail, itemMove, posT );
1279         return itemMove;
1280 }
1281
1282 /**
1283  * Move person's email item.
1284  * \param  person    Person.
1285  * \param  itemMove  Item to move.
1286  * \param  itemTarget Target item after which to move item.
1287  * \return Reference to item that was moved, or <i>NULL</i> if null arguments
1288  *         supplied.
1289  */
1290 ItemEMail *addritem_move_email_after(
1291                 ItemPerson *person, ItemEMail *itemMove, ItemEMail *itemTarget )
1292 {
1293         gint posT, posM;
1294
1295         g_return_val_if_fail( person != NULL, NULL );
1296
1297         if( itemTarget == NULL ) return NULL;
1298         if( itemMove == NULL ) return NULL;
1299         if( itemMove == itemTarget ) return itemMove;
1300
1301         posT = g_list_index( person->listEMail, itemTarget );
1302         if( posT < 0 ) return NULL;
1303         posM = g_list_index( person->listEMail, itemMove );
1304         if( posM < 0 ) return NULL;
1305         person->listEMail = g_list_remove( person->listEMail, itemMove );
1306         person->listEMail = g_list_insert( person->listEMail, itemMove, 1+posT );
1307         return itemMove;
1308 }
1309
1310 /**
1311  * Parse first and last names for person from common name.
1312  * \param person Person to process.
1313  */
1314 void addritem_parse_first_last( ItemPerson *person ) {
1315         gchar *name;
1316         gchar *fName, *lName;
1317         gchar *p;
1318         gint len, i;
1319
1320         g_return_if_fail( person != NULL );
1321
1322         name = ADDRITEM_NAME(person);
1323         if( name == NULL ) return;
1324
1325         fName = NULL;
1326         lName = NULL;
1327         p = strchr( name, ',' );
1328         if( p ) {
1329                 len = ( size_t ) ( p - name );
1330                 lName = g_strndup( name, len );
1331                 fName = g_strdup( p + 1 );
1332         }
1333         else {
1334                 /* Other way around */
1335                 i = strlen( name );
1336                 while( i >= 0 ) {
1337                         if( name[i] == ' ' ) {
1338                                 fName = g_strndup( name, i );
1339                                 lName = g_strdup( &name[i] );
1340                                 break;
1341                         }
1342                         i--;
1343                 }
1344                 if( fName == NULL ) {
1345                         fName = g_strdup( name );
1346                 }
1347         }
1348
1349         if( person->firstName ) {
1350                 g_free( person->firstName );
1351         }
1352         person->firstName = fName;
1353         if( person->firstName )
1354                 g_strstrip( person->firstName );
1355
1356         if( person->lastName ) {
1357                 g_free( person->lastName );
1358         }
1359         person->lastName = lName;
1360         if( person->lastName )
1361                 g_strstrip( person->lastName );
1362 }
1363
1364 /**
1365  * Build a path of all ancestor folders for specified folder.
1366  * \param  folder Folder.
1367  * \param  seq    Path sequence, FALSE top down, TRUE bottom up.
1368  * \return List of folders from the top down.
1369  */
1370 GList *addritem_folder_path( const ItemFolder *folder, const gboolean seq ) {
1371         GList *list;
1372         AddrItemObject *item;
1373
1374         list = NULL;
1375         item = ( AddrItemObject * ) folder;
1376         if( seq ) {
1377                 while( item ) {
1378                         list = g_list_prepend( list, item );
1379                         item = ADDRITEM_PARENT( item );
1380                 }
1381         }
1382         else {
1383                 while( item ) {
1384                         list = g_list_append( list, item );
1385                         item = ADDRITEM_PARENT( item );
1386                 }
1387         }
1388         return list;
1389 }
1390
1391 /**
1392  * Format E-Mail address.
1393  * \param email EMail item to format.
1394  * \return Formatted string. Should be freed after use.
1395  */
1396 gchar *addritem_format_email( ItemEMail *email ) {
1397         gchar *address;
1398         gchar *name;
1399         ItemPerson *person;
1400
1401         address = NULL;
1402         name = NULL;
1403         if( ADDRITEM_NAME( email ) ) {
1404                 if( strlen( ADDRITEM_NAME( email ) ) ) {
1405                         name = ADDRITEM_NAME( email );
1406                 }
1407         }
1408         if( ! name ) {
1409                 person = ( ItemPerson * ) ADDRITEM_PARENT( email );
1410                 name = ADDRITEM_NAME( person );
1411         }
1412
1413         if( name ) {
1414                 if( strchr_with_skip_quote( name, '"', ',' ) ) {
1415                         address = g_strdup_printf( "\"%s\" <%s>", name, email->address );
1416                 }
1417                 else {
1418                         address = g_strdup_printf( "%s <%s>", name, email->address );
1419                 }
1420         }
1421         else {
1422                 address = g_strdup_printf( "%s", email->address );
1423         }
1424         return address;
1425 }
1426
1427 /*
1428 * End of Source.
1429 */