Initialize the gmime for upstream
[platform/upstream/gmime.git] / gmime / internet-address.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2012 Jeffrey Stedfast
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public License
7  *  as published by the Free Software Foundation; either version 2.1
8  *  of the License, or (at your option) any later version.
9  *
10  *  This library 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 GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free
17  *  Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
18  *  02110-1301, USA.
19  */
20
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <ctype.h>
29 #include <errno.h>
30
31 #include "internet-address.h"
32 #include "gmime-table-private.h"
33 #include "gmime-parse-utils.h"
34 #include "gmime-iconv-utils.h"
35 #include "gmime-events.h"
36 #include "gmime-utils.h"
37 #include "list.h"
38
39
40 #ifdef ENABLE_WARNINGS
41 #define w(x) x
42 #else
43 #define w(x)
44 #endif /* ENABLE_WARNINGS */
45
46 #define d(x)
47
48
49 /**
50  * SECTION: internet-address
51  * @title: InternetAddress
52  * @short_description: Internet addresses
53  * @see_also: #InternetAddressGroup, #InternetAddressMailbox
54  *
55  * An #InternetAddress is the base class for #InternetAddressGroup and
56  * #InternetAddressMailbox.
57  **/
58
59 /**
60  * SECTION: internet-address-group
61  * @title: InternetAddressGroup
62  * @short_description: rfc822 'group' address
63  * @see_also: #InternetAddress
64  *
65  * An #InternetAddressGroup represents an rfc822 'group' address.
66  **/
67
68 /**
69  * SECTION: internet-address-mailbox
70  * @title: InternetAddressMailbox
71  * @short_description: rfc822 'mailbox' address
72  * @see_also: #InternetAddress
73  *
74  * An #InternetAddressMailbox represents what is a typical "email
75  * address".
76  **/
77
78 /**
79  * SECTION: internet-address-list
80  * @title: InternetAddressList
81  * @short_description: A list of internet addresses
82  * @see_also: #InternetAddress
83  *
84  * An #InternetAddressList is a collection of #InternetAddress
85  * objects.
86  **/
87
88
89 enum {
90         INTERNET_ADDRESS_ENCODE = 1 << 0,
91         INTERNET_ADDRESS_FOLD   = 1 << 1,
92 };
93
94
95 static void internet_address_class_init (InternetAddressClass *klass);
96 static void internet_address_init (InternetAddress *ia, InternetAddressClass *klass);
97 static void internet_address_finalize (GObject *object);
98
99
100 static GObjectClass *parent_class = NULL;
101
102
103 GType
104 internet_address_get_type (void)
105 {
106         static GType type = 0;
107         
108         if (!type) {
109                 static const GTypeInfo info = {
110                         sizeof (InternetAddressClass),
111                         NULL, /* base_class_init */
112                         NULL, /* base_class_finalize */
113                         (GClassInitFunc) internet_address_class_init,
114                         NULL, /* class_finalize */
115                         NULL, /* class_data */
116                         sizeof (InternetAddress),
117                         0,    /* n_preallocs */
118                         (GInstanceInitFunc) internet_address_init,
119                 };
120                 
121                 type = g_type_register_static (G_TYPE_OBJECT, "InternetAddress",
122                                                &info, G_TYPE_FLAG_ABSTRACT);
123         }
124         
125         return type;
126 }
127
128
129 static void
130 internet_address_class_init (InternetAddressClass *klass)
131 {
132         GObjectClass *object_class = G_OBJECT_CLASS (klass);
133         
134         parent_class = g_type_class_ref (G_TYPE_OBJECT);
135         
136         object_class->finalize = internet_address_finalize;
137         
138         klass->to_string = NULL;
139 }
140
141 static void
142 internet_address_init (InternetAddress *ia, InternetAddressClass *klass)
143 {
144         ia->priv = g_mime_event_new ((GObject *) ia);
145         ia->name = NULL;
146 }
147
148 static void
149 internet_address_finalize (GObject *object)
150 {
151         InternetAddress *ia = (InternetAddress *) object;
152         
153         g_mime_event_destroy (ia->priv);
154         g_free (ia->name);
155         
156         G_OBJECT_CLASS (parent_class)->finalize (object);
157 }
158
159
160 static void
161 _internet_address_set_name (InternetAddress *ia, const char *name)
162 {
163         char *buf;
164         
165         buf = g_strdup (name);
166         g_free (ia->name);
167         ia->name = buf;
168 }
169
170 /**
171  * internet_address_set_name:
172  * @ia: a #InternetAddress
173  * @name: the display name for the address group or mailbox
174  *
175  * Set the display name of the #InternetAddress.
176  **/
177 void
178 internet_address_set_name (InternetAddress *ia, const char *name)
179 {
180         g_return_if_fail (IS_INTERNET_ADDRESS (ia));
181         
182         _internet_address_set_name (ia, name);
183         
184         g_mime_event_emit (ia->priv, NULL);
185 }
186
187
188 /**
189  * internet_address_get_name:
190  * @ia: a #InternetAddress
191  *
192  * Gets the display name of the #InternetAddress.
193  *
194  * Returns: the display name of @ia.
195  **/
196 const char *
197 internet_address_get_name (InternetAddress *ia)
198 {
199         g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), NULL);
200         
201         return ia->name;
202 }
203
204
205 /**
206  * internet_address_to_string:
207  * @ia: Internet Address object
208  * @encode: %TRUE if the address should be rfc2047 encoded
209  *
210  * Allocates a string containing the contents of the #InternetAddress
211  * object.
212  * 
213  * Returns: the #InternetAddress object as an allocated string in
214  * rfc822 format.
215  **/
216 char *
217 internet_address_to_string (InternetAddress *ia, gboolean encode)
218 {
219         guint32 flags = encode ? INTERNET_ADDRESS_ENCODE : 0;
220         size_t linelen = 0;
221         GString *string;
222         char *str;
223         
224         string = g_string_new ("");
225         INTERNET_ADDRESS_GET_CLASS (ia)->to_string (ia, flags, &linelen, string);
226         str = string->str;
227         
228         g_string_free (string, FALSE);
229         
230         return str;
231 }
232
233
234 static void internet_address_mailbox_class_init (InternetAddressMailboxClass *klass);
235 static void internet_address_mailbox_init (InternetAddressMailbox *mailbox, InternetAddressMailboxClass *klass);
236 static void internet_address_mailbox_finalize (GObject *object);
237
238 static void mailbox_to_string (InternetAddress *ia, guint32 flags, size_t *linelen, GString *out);
239
240
241 static GObjectClass *mailbox_parent_class = NULL;
242
243
244 GType
245 internet_address_mailbox_get_type (void)
246 {
247         static GType type = 0;
248         
249         if (!type) {
250                 static const GTypeInfo info = {
251                         sizeof (InternetAddressMailboxClass),
252                         NULL, /* base_class_init */
253                         NULL, /* base_class_finalize */
254                         (GClassInitFunc) internet_address_mailbox_class_init,
255                         NULL, /* class_finalize */
256                         NULL, /* class_data */
257                         sizeof (InternetAddressMailbox),
258                         0,    /* n_preallocs */
259                         (GInstanceInitFunc) internet_address_mailbox_init,
260                 };
261                 
262                 type = g_type_register_static (INTERNET_ADDRESS_TYPE, "InternetAddressMailbox", &info, 0);
263         }
264         
265         return type;
266 }
267
268
269 static void
270 internet_address_mailbox_class_init (InternetAddressMailboxClass *klass)
271 {
272         InternetAddressClass *address_class = INTERNET_ADDRESS_CLASS (klass);
273         GObjectClass *object_class = G_OBJECT_CLASS (klass);
274         
275         mailbox_parent_class = g_type_class_ref (INTERNET_ADDRESS_TYPE);
276         
277         object_class->finalize = internet_address_mailbox_finalize;
278         
279         address_class->to_string = mailbox_to_string;
280 }
281
282 static void
283 internet_address_mailbox_init (InternetAddressMailbox *mailbox, InternetAddressMailboxClass *klass)
284 {
285         mailbox->addr = NULL;
286 }
287
288 static void
289 internet_address_mailbox_finalize (GObject *object)
290 {
291         InternetAddressMailbox *mailbox = (InternetAddressMailbox *) object;
292         
293         g_free (mailbox->addr);
294         
295         G_OBJECT_CLASS (mailbox_parent_class)->finalize (object);
296 }
297
298
299 /**
300  * internet_address_mailbox_new:
301  * @name: person's name
302  * @addr: person's address
303  *
304  * Creates a new #InternetAddress object with name @name and address
305  * @addr.
306  * 
307  * Returns: a new #InternetAddressMailbox object.
308  **/
309 InternetAddress *
310 internet_address_mailbox_new (const char *name, const char *addr)
311 {
312         InternetAddressMailbox *mailbox;
313         
314         g_return_val_if_fail (addr != NULL, NULL);
315         
316         mailbox = g_object_newv (INTERNET_ADDRESS_TYPE_MAILBOX, 0, NULL);
317         mailbox->addr = g_strdup (addr);
318         
319         _internet_address_set_name ((InternetAddress *) mailbox, name);
320         
321         return (InternetAddress *) mailbox;
322 }
323
324
325 /**
326  * internet_address_mailbox_set_addr:
327  * @mailbox: a #InternetAddressMailbox
328  * @addr: contact's email address
329  *
330  * Set the mailbox address.
331  **/
332 void
333 internet_address_mailbox_set_addr (InternetAddressMailbox *mailbox, const char *addr)
334 {
335         g_return_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox));
336         
337         if (mailbox->addr == addr)
338                 return;
339         
340         g_free (mailbox->addr);
341         mailbox->addr = g_strdup (addr);
342         
343         g_mime_event_emit (((InternetAddress *) mailbox)->priv, NULL);
344 }
345
346
347 /**
348  * internet_address_mailbox_get_addr:
349  * @mailbox: a #InternetAddressMailbox
350  *
351  * Gets the addr-spec of the internet address mailbox.
352  *
353  * Returns: the address of the mailbox.
354  **/
355 const char *
356 internet_address_mailbox_get_addr (InternetAddressMailbox *mailbox)
357 {
358         g_return_val_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox), NULL);
359         
360         return mailbox->addr;
361 }
362
363
364 static void internet_address_group_class_init (InternetAddressGroupClass *klass);
365 static void internet_address_group_init (InternetAddressGroup *group, InternetAddressGroupClass *klass);
366 static void internet_address_group_finalize (GObject *object);
367
368 static void group_to_string (InternetAddress *ia, guint32 flags, size_t *linelen, GString *out);
369
370
371 static GObjectClass *group_parent_class = NULL;
372
373
374 GType
375 internet_address_group_get_type (void)
376 {
377         static GType type = 0;
378         
379         if (!type) {
380                 static const GTypeInfo info = {
381                         sizeof (InternetAddressGroupClass),
382                         NULL, /* base_class_init */
383                         NULL, /* base_class_finalize */
384                         (GClassInitFunc) internet_address_group_class_init,
385                         NULL, /* class_finalize */
386                         NULL, /* class_data */
387                         sizeof (InternetAddressGroup),
388                         0,    /* n_preallocs */
389                         (GInstanceInitFunc) internet_address_group_init,
390                 };
391                 
392                 type = g_type_register_static (INTERNET_ADDRESS_TYPE, "InternetAddressGroup", &info, 0);
393         }
394         
395         return type;
396 }
397
398
399 static void
400 internet_address_group_class_init (InternetAddressGroupClass *klass)
401 {
402         InternetAddressClass *address_class = INTERNET_ADDRESS_CLASS (klass);
403         GObjectClass *object_class = G_OBJECT_CLASS (klass);
404         
405         group_parent_class = g_type_class_ref (INTERNET_ADDRESS_TYPE);
406         
407         object_class->finalize = internet_address_group_finalize;
408         
409         address_class->to_string = group_to_string;
410 }
411
412 static void
413 members_changed (InternetAddressList *members, gpointer args, InternetAddress *group)
414 {
415         g_mime_event_emit (((InternetAddress *) group)->priv, NULL);
416 }
417
418 static void
419 internet_address_group_init (InternetAddressGroup *group, InternetAddressGroupClass *klass)
420 {
421         group->members = internet_address_list_new ();
422         
423         g_mime_event_add (group->members->priv, (GMimeEventCallback) members_changed, group);
424 }
425
426 static void
427 internet_address_group_finalize (GObject *object)
428 {
429         InternetAddressGroup *group = (InternetAddressGroup *) object;
430         
431         g_mime_event_remove (group->members->priv, (GMimeEventCallback) members_changed, group);
432         
433         g_object_unref (group->members);
434         
435         G_OBJECT_CLASS (group_parent_class)->finalize (object);
436 }
437
438
439 /**
440  * internet_address_group_new:
441  * @name: group name
442  *
443  * Creates a new #InternetAddressGroup object with a display name of
444  * @name.
445  * 
446  * Returns: a new #InternetAddressGroup object.
447  **/
448 InternetAddress *
449 internet_address_group_new (const char *name)
450 {
451         InternetAddress *group;
452         
453         group = g_object_newv (INTERNET_ADDRESS_TYPE_GROUP, 0, NULL);
454         _internet_address_set_name (group, name);
455         
456         return group;
457 }
458
459
460 /**
461  * internet_address_group_set_members:
462  * @group: a #InternetAddressGroup
463  * @members: a #InternetAddressList
464  *
465  * Set the members of the internet address group.
466  **/
467 void
468 internet_address_group_set_members (InternetAddressGroup *group, InternetAddressList *members)
469 {
470         g_return_if_fail (INTERNET_ADDRESS_IS_GROUP (group));
471         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (members));
472         
473         if (group->members == members)
474                 return;
475         
476         if (group->members) {
477                 g_mime_event_remove (group->members->priv, (GMimeEventCallback) members_changed, group);
478                 g_object_unref (group->members);
479         }
480         
481         if (members) {
482                 g_mime_event_add (members->priv, (GMimeEventCallback) members_changed, group);
483                 g_object_ref (members);
484         }
485         
486         group->members = members;
487         
488         g_mime_event_emit (((InternetAddress *) group)->priv, NULL);
489 }
490
491
492 /**
493  * internet_address_group_get_members:
494  * @group: a #InternetAddressGroup
495  *
496  * Gets the #InternetAddressList containing the group members of an
497  * rfc822 group address.
498  *
499  * Returns: a #InternetAddressList containing the members of @group.
500  **/
501 InternetAddressList *
502 internet_address_group_get_members (InternetAddressGroup *group)
503 {
504         g_return_val_if_fail (INTERNET_ADDRESS_IS_GROUP (group), NULL);
505         
506         return group->members;
507 }
508
509
510 #define _internet_address_group_add_member(group,member) _internet_address_list_add (group->members, member)
511
512 /**
513  * internet_address_group_add_member:
514  * @group: a #InternetAddressGroup
515  * @member: a #InternetAddress
516  *
517  * Add a contact to the internet address group.
518  *
519  * Returns: the index of the newly added member.
520  **/
521 int
522 internet_address_group_add_member (InternetAddressGroup *group, InternetAddress *member)
523 {
524         g_return_val_if_fail (INTERNET_ADDRESS_IS_GROUP (group), -1);
525         g_return_val_if_fail (IS_INTERNET_ADDRESS (member), -1);
526         
527         return internet_address_list_add (group->members, member);
528 }
529
530
531 static void internet_address_list_class_init (InternetAddressListClass *klass);
532 static void internet_address_list_init (InternetAddressList *list, InternetAddressListClass *klass);
533 static void internet_address_list_finalize (GObject *object);
534
535
536 static GObjectClass *list_parent_class = NULL;
537
538
539 GType
540 internet_address_list_get_type (void)
541 {
542         static GType type = 0;
543         
544         if (!type) {
545                 static const GTypeInfo info = {
546                         sizeof (InternetAddressListClass),
547                         NULL, /* base_class_init */
548                         NULL, /* base_class_finalize */
549                         (GClassInitFunc) internet_address_list_class_init,
550                         NULL, /* class_finalize */
551                         NULL, /* class_data */
552                         sizeof (InternetAddressList),
553                         0,    /* n_preallocs */
554                         (GInstanceInitFunc) internet_address_list_init,
555                 };
556                 
557                 type = g_type_register_static (G_TYPE_OBJECT, "InternetAddressList", &info, 0);
558         }
559         
560         return type;
561 }
562
563
564 static void
565 internet_address_list_class_init (InternetAddressListClass *klass)
566 {
567         GObjectClass *object_class = G_OBJECT_CLASS (klass);
568         
569         list_parent_class = g_type_class_ref (G_TYPE_OBJECT);
570         
571         object_class->finalize = internet_address_list_finalize;
572 }
573
574 static void
575 internet_address_list_init (InternetAddressList *list, InternetAddressListClass *klass)
576 {
577         list->priv = g_mime_event_new ((GObject *) list);
578         list->array = g_ptr_array_new ();
579 }
580
581 static void
582 address_changed (InternetAddress *ia, gpointer args, InternetAddressList *list)
583 {
584         g_mime_event_emit (list->priv, NULL);
585 }
586
587 static void
588 internet_address_list_finalize (GObject *object)
589 {
590         InternetAddressList *list = (InternetAddressList *) object;
591         InternetAddress *ia;
592         guint i;
593         
594         for (i = 0; i < list->array->len; i++) {
595                 ia = (InternetAddress *) list->array->pdata[i];
596                 g_mime_event_remove (ia->priv, (GMimeEventCallback) address_changed, list);
597                 g_object_unref (ia);
598         }
599         
600         g_mime_event_destroy (list->priv);
601         
602         g_ptr_array_free (list->array, TRUE);
603         
604         G_OBJECT_CLASS (list_parent_class)->finalize (object);
605 }
606
607
608 /**
609  * internet_address_list_new:
610  *
611  * Creates a new #InternetAddressList.
612  *
613  * Returns: a new #InternetAddressList.
614  **/
615 InternetAddressList *
616 internet_address_list_new (void)
617 {
618         return g_object_newv (INTERNET_ADDRESS_LIST_TYPE, 0, NULL);
619 }
620
621
622 /**
623  * internet_address_list_length:
624  * @list: a #InternetAddressList
625  *
626  * Gets the length of the list.
627  *
628  * Returns: the number of #InternetAddress objects in the list.
629  **/
630 int
631 internet_address_list_length (InternetAddressList *list)
632 {
633         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), -1);
634         
635         return list->array->len;
636 }
637
638
639 /**
640  * internet_address_list_clear:
641  * @list: a #InternetAddressList
642  *
643  * Clears the list of addresses.
644  **/
645 void
646 internet_address_list_clear (InternetAddressList *list)
647 {
648         InternetAddress *ia;
649         guint i;
650         
651         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
652         
653         for (i = 0; i < list->array->len; i++) {
654                 ia = (InternetAddress *) list->array->pdata[i];
655                 g_mime_event_remove (ia->priv, (GMimeEventCallback) address_changed, list);
656                 g_object_unref (ia);
657         }
658         
659         g_ptr_array_set_size (list->array, 0);
660         
661         g_mime_event_emit (list->priv, NULL);
662 }
663
664
665 static int
666 _internet_address_list_add (InternetAddressList *list, InternetAddress *ia)
667 {
668         int index;
669         
670         g_mime_event_add (ia->priv, (GMimeEventCallback) address_changed, list);
671         
672         index = list->array->len;
673         g_ptr_array_add (list->array, ia);
674         
675         return index;
676 }
677
678
679 /**
680  * internet_address_list_add:
681  * @list: a #InternetAddressList
682  * @ia: a #InternetAddress
683  *
684  * Adds an #InternetAddress to the #InternetAddressList.
685  *
686  * Returns: the index of the added #InternetAddress.
687  **/
688 int
689 internet_address_list_add (InternetAddressList *list, InternetAddress *ia)
690 {
691         int index;
692         
693         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), -1);
694         g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), -1);
695         
696         index = _internet_address_list_add (list, ia);
697         g_object_ref (ia);
698         
699         g_mime_event_emit (list->priv, NULL);
700         
701         return index;
702 }
703
704
705 /**
706  * internet_address_list_prepend:
707  * @list: a #InternetAddressList
708  * @prepend: a #InternetAddressList
709  *
710  * Inserts all of the addresses in @prepend to the beginning of @list.
711  **/
712 void
713 internet_address_list_prepend (InternetAddressList *list, InternetAddressList *prepend)
714 {
715         InternetAddress *ia;
716         char *dest, *src;
717         guint len, i;
718         
719         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (prepend));
720         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
721         
722         if (prepend->array->len == 0)
723                 return;
724         
725         len = prepend->array->len;
726         g_ptr_array_set_size (list->array, list->array->len + len);
727         
728         src = ((char *) list->array->pdata);
729         dest = src + (sizeof (void *) * len);
730         
731         g_memmove (dest, src, (sizeof (void *) * list->array->len));
732         
733         for (i = 0; i < prepend->array->len; i++) {
734                 ia = (InternetAddress *) prepend->array->pdata[i];
735                 g_mime_event_add (ia->priv, (GMimeEventCallback) address_changed, list);
736                 list->array->pdata[i] = ia;
737                 g_object_ref (ia);
738         }
739         
740         g_mime_event_emit (list->priv, NULL);
741 }
742
743
744 /**
745  * internet_address_list_append:
746  * @list: a #InternetAddressList
747  * @append: a #InternetAddressList
748  *
749  * Adds all of the addresses in @append to @list.
750  **/
751 void
752 internet_address_list_append (InternetAddressList *list, InternetAddressList *append)
753 {
754         InternetAddress *ia;
755         guint len, i;
756         
757         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (append));
758         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
759         
760         len = list->array->len;
761         g_ptr_array_set_size (list->array, len + append->array->len);
762         
763         for (i = 0; i < append->array->len; i++) {
764                 ia = (InternetAddress *) append->array->pdata[i];
765                 g_mime_event_add (ia->priv, (GMimeEventCallback) address_changed, list);
766                 list->array->pdata[len + i] = ia;
767                 g_object_ref (ia);
768         }
769         
770         g_mime_event_emit (list->priv, NULL);
771 }
772
773
774 /**
775  * internet_address_list_insert:
776  * @list: a #InternetAddressList
777  * @index: index to insert at
778  * @ia: a #InternetAddress
779  *
780  * Inserts an #InternetAddress into the #InternetAddressList at the
781  * specified index.
782  **/
783 void
784 internet_address_list_insert (InternetAddressList *list, int index, InternetAddress *ia)
785 {
786         char *dest, *src;
787         size_t n;
788         
789         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
790         g_return_if_fail (IS_INTERNET_ADDRESS (ia));
791         g_return_if_fail (index >= 0);
792         
793         g_mime_event_add (ia->priv, (GMimeEventCallback) address_changed, list);
794         g_object_ref (ia);
795         
796         if ((guint) index < list->array->len) {
797                 g_ptr_array_set_size (list->array, list->array->len + 1);
798                 
799                 dest = ((char *) list->array->pdata) + (sizeof (void *) * (index + 1));
800                 src = ((char *) list->array->pdata) + (sizeof (void *) * index);
801                 n = list->array->len - index - 1;
802                 
803                 g_memmove (dest, src, (sizeof (void *) * n));
804                 list->array->pdata[index] = ia;
805         } else {
806                 /* the easy case */
807                 g_ptr_array_add (list->array, ia);
808         }
809         
810         g_mime_event_emit (list->priv, NULL);
811 }
812
813
814 /**
815  * internet_address_list_remove:
816  * @list: a #InternetAddressList
817  * @ia: a #InternetAddress
818  *
819  * Removes an #InternetAddress from the #InternetAddressList.
820  *
821  * Returns: %TRUE if the specified #InternetAddress was removed or
822  * %FALSE otherwise.
823  **/
824 gboolean
825 internet_address_list_remove (InternetAddressList *list, InternetAddress *ia)
826 {
827         int index;
828         
829         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), FALSE);
830         g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), FALSE);
831         
832         if ((index = internet_address_list_index_of (list, ia)) == -1)
833                 return FALSE;
834         
835         internet_address_list_remove_at (list, index);
836         
837         return TRUE;
838 }
839
840
841 /**
842  * internet_address_list_remove_at:
843  * @list: a #InternetAddressList
844  * @index: index to remove
845  *
846  * Removes an #InternetAddress from the #InternetAddressList at the
847  * specified index.
848  *
849  * Returns: %TRUE if an #InternetAddress was removed or %FALSE
850  * otherwise.
851  **/
852 gboolean
853 internet_address_list_remove_at (InternetAddressList *list, int index)
854 {
855         InternetAddress *ia;
856         
857         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), FALSE);
858         g_return_val_if_fail (index >= 0, FALSE);
859         
860         if ((guint) index >= list->array->len)
861                 return FALSE;
862         
863         ia = list->array->pdata[index];
864         g_mime_event_remove (ia->priv, (GMimeEventCallback) address_changed, list);
865         g_object_unref (ia);
866         
867         g_ptr_array_remove_index (list->array, index);
868         
869         g_mime_event_emit (list->priv, NULL);
870         
871         return TRUE;
872 }
873
874
875 /**
876  * internet_address_list_contains:
877  * @list: a #InternetAddressList
878  * @ia: a #InternetAddress
879  *
880  * Checks whether or not the specified #InternetAddress is contained
881  * within the #InternetAddressList.
882  *
883  * Returns: %TRUE if the specified #InternetAddress is contained
884  * within the specified #InternetAddressList or %FALSE otherwise.
885  **/
886 gboolean
887 internet_address_list_contains (InternetAddressList *list, InternetAddress *ia)
888 {
889         return internet_address_list_index_of (list, ia) != -1;
890 }
891
892
893 /**
894  * internet_address_list_index_of:
895  * @list: a #InternetAddressList
896  * @ia: a #InternetAddress
897  *
898  * Gets the index of the specified #InternetAddress inside the
899  * #InternetAddressList.
900  *
901  * Returns: the index of the requested #InternetAddress within the
902  * #InternetAddressList or %-1 if it is not contained within the
903  * #InternetAddressList.
904  **/
905 int
906 internet_address_list_index_of (InternetAddressList *list, InternetAddress *ia)
907 {
908         guint i;
909         
910         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), -1);
911         g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), -1);
912         
913         for (i = 0; i < list->array->len; i++) {
914                 if (list->array->pdata[i] == ia)
915                         return i;
916         }
917         
918         return -1;
919 }
920
921
922 /**
923  * internet_address_list_get_address:
924  * @list: a #InternetAddressList
925  * @index: index of #InternetAddress to get
926  *
927  * Gets the #InternetAddress at the specified index.
928  *
929  * Returns: the #InternetAddress at the specified index or %NULL if
930  * the index is out of range.
931  **/
932 InternetAddress *
933 internet_address_list_get_address (InternetAddressList *list, int index)
934 {
935         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), NULL);
936         g_return_val_if_fail (index >= 0, NULL);
937         
938         if ((guint) index >= list->array->len)
939                 return NULL;
940         
941         return list->array->pdata[index];
942 }
943
944
945 /**
946  * internet_address_list_set_address:
947  * @list: a #InternetAddressList
948  * @index: index of #InternetAddress to set
949  * @ia: a #InternetAddress
950  *
951  * Sets the #InternetAddress at the specified index to @ia.
952  **/
953 void
954 internet_address_list_set_address (InternetAddressList *list, int index, InternetAddress *ia)
955 {
956         InternetAddress *old;
957         
958         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
959         g_return_if_fail (IS_INTERNET_ADDRESS (ia));
960         g_return_if_fail (index >= 0);
961         
962         if ((guint) index > list->array->len)
963                 return;
964         
965         if ((guint) index == list->array->len) {
966                 internet_address_list_add (list, ia);
967                 return;
968         }
969         
970         if ((old = list->array->pdata[index]) == ia)
971                 return;
972         
973         g_mime_event_remove (old->priv, (GMimeEventCallback) address_changed, list);
974         g_object_unref (old);
975         
976         g_mime_event_add (ia->priv, (GMimeEventCallback) address_changed, list);
977         list->array->pdata[index] = ia;
978         g_object_ref (ia);
979         
980         g_mime_event_emit (list->priv, NULL);
981 }
982
983
984 static char *
985 encoded_name (const char *raw, gboolean rfc2047_encode)
986 {
987         char *name;
988         
989         g_return_val_if_fail (raw != NULL, NULL);
990         
991         if (rfc2047_encode) {
992                 name = g_mime_utils_header_encode_phrase (raw);
993         } else {
994                 name = g_mime_utils_quote_string (raw);
995         }
996         
997         return name;
998 }
999
1000 static void
1001 linewrap (GString *string)
1002 {
1003         if (string->len > 0 && string->str[string->len - 1] == ' ') {
1004                 string->str[string->len - 1] = '\n';
1005                 g_string_append_c (string, '\t');
1006         } else {
1007                 g_string_append (string, "\n\t");
1008         }
1009 }
1010
1011 static void
1012 append_folded_name (GString *string, size_t *linelen, const char *name)
1013 {
1014         const char *word, *lwsp;
1015         size_t len;
1016         
1017         word = name;
1018         
1019         while (*word) {
1020                 lwsp = word;
1021                 
1022                 if (*word == '"') {
1023                         /* quoted string, don't break these up */
1024                         lwsp++;
1025                         
1026                         while (*lwsp && *lwsp != '"') {
1027                                 if (*lwsp == '\\')
1028                                         lwsp++;
1029                                 
1030                                 if (*lwsp)
1031                                         lwsp++;
1032                         }
1033                         
1034                         if (*lwsp == '"')
1035                                 lwsp++;
1036                 } else {
1037                         /* normal word */
1038                         while (*lwsp && !is_lwsp (*lwsp))
1039                                 lwsp++;
1040                 }
1041                 
1042                 len = lwsp - word;
1043                 if (*linelen > 1 && (*linelen + len) > GMIME_FOLD_LEN) {
1044                         linewrap (string);
1045                         *linelen = 1;
1046                 }
1047                 
1048                 g_string_append_len (string, word, len);
1049                 *linelen += len;
1050                 
1051                 word = lwsp;
1052                 while (*word && is_lwsp (*word))
1053                         word++;
1054                 
1055                 if (*word && is_lwsp (*lwsp)) {
1056                         g_string_append_c (string, ' ');
1057                         (*linelen)++;
1058                 }
1059         }
1060 }
1061
1062 static void
1063 mailbox_to_string (InternetAddress *ia, guint32 flags, size_t *linelen, GString *string)
1064 {
1065         InternetAddressMailbox *mailbox = (InternetAddressMailbox *) ia;
1066         gboolean encode = flags & INTERNET_ADDRESS_ENCODE;
1067         gboolean fold = flags & INTERNET_ADDRESS_FOLD;
1068         char *name;
1069         size_t len;
1070         
1071         if (ia->name && *ia->name) {
1072                 name = encoded_name (ia->name, encode);
1073                 len = strlen (name);
1074                 
1075                 if (fold && (*linelen + len) > GMIME_FOLD_LEN) {
1076                         if (len > GMIME_FOLD_LEN) {
1077                                 /* we need to break up the name */
1078                                 append_folded_name (string, linelen, name);
1079                         } else {
1080                                 /* the name itself is short enough to fit on a single
1081                                  * line, but only if we write it on a line by itself */
1082                                 if (*linelen > 1) {
1083                                         linewrap (string);
1084                                         *linelen = 1;
1085                                 }
1086                                 
1087                                 g_string_append_len (string, name, len);
1088                                 *linelen += len;
1089                         }
1090                 } else {
1091                         /* we can safely fit the name on this line */
1092                         g_string_append_len (string, name, len);
1093                         *linelen += len;
1094                 }
1095                 
1096                 g_free (name);
1097                 
1098                 len = strlen (mailbox->addr);
1099                 
1100                 if (fold && (*linelen + len + 3) >= GMIME_FOLD_LEN) {
1101                         g_string_append_len (string, "\n\t<", 3);
1102                         *linelen = 2;
1103                 } else {
1104                         g_string_append_len (string, " <", 2);
1105                         *linelen += 2;
1106                 }
1107                 
1108                 g_string_append_len (string, mailbox->addr, len);
1109                 g_string_append_c (string, '>');
1110                 *linelen += len + 1;
1111         } else {
1112                 len = strlen (mailbox->addr);
1113                 
1114                 if (fold && (*linelen + len) > GMIME_FOLD_LEN) {
1115                         linewrap (string);
1116                         *linelen = 1;
1117                 }
1118                 
1119                 g_string_append_len (string, mailbox->addr, len);
1120                 *linelen += len;
1121         }
1122 }
1123
1124 static void
1125 _internet_address_list_to_string (const InternetAddressList *list, guint32 flags, size_t *linelen, GString *string)
1126 {
1127         InternetAddress *ia;
1128         guint i;
1129         
1130         for (i = 0; i < list->array->len; i++) {
1131                 ia = (InternetAddress *) list->array->pdata[i];
1132                 
1133                 INTERNET_ADDRESS_GET_CLASS (ia)->to_string (ia, flags, linelen, string);
1134                 
1135                 if (i + 1 < list->array->len) {
1136                         g_string_append (string, ", ");
1137                         *linelen += 2;
1138                 }
1139         }
1140 }
1141
1142 static void
1143 group_to_string (InternetAddress *ia, guint32 flags, size_t *linelen, GString *string)
1144 {
1145         InternetAddressGroup *group = (InternetAddressGroup *) ia;
1146         gboolean encode = flags & INTERNET_ADDRESS_ENCODE;
1147         gboolean fold = flags & INTERNET_ADDRESS_FOLD;
1148         char *name = NULL;
1149         size_t len = 0;
1150         
1151         if (ia->name != NULL) {
1152                 name = encoded_name (ia->name, encode);
1153                 len = strlen (name);
1154                 
1155                 if (fold && *linelen > 1 && (*linelen + len + 1) > GMIME_FOLD_LEN) {
1156                         linewrap (string);
1157                         *linelen = 1;
1158                 }
1159                 
1160                 g_string_append_len (string, name, len);
1161         }
1162         
1163         g_string_append_len (string, ": ", 2);
1164         *linelen += len + 2;
1165         g_free (name);
1166         
1167         _internet_address_list_to_string (group->members, flags, linelen, string);
1168         g_string_append_c (string, ';');
1169         *linelen += 1;
1170 }
1171
1172
1173 /**
1174  * internet_address_list_to_string:
1175  * @list: list of internet addresses
1176  * @encode: %TRUE if the address should be rfc2047 encoded
1177  *
1178  * Allocates a string buffer containing the rfc822 formatted addresses
1179  * in @list.
1180  *
1181  * Returns: a string containing the list of addresses in rfc822 format
1182  * or %NULL if no addresses are contained in the list.
1183  **/
1184 char *
1185 internet_address_list_to_string (InternetAddressList *list, gboolean encode)
1186 {
1187         guint32 flags = encode ? INTERNET_ADDRESS_ENCODE : 0;
1188         size_t linelen = 0;
1189         GString *string;
1190         char *str;
1191         
1192         g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), NULL);
1193         
1194         if (list->array->len == 0)
1195                 return NULL;
1196         
1197         string = g_string_new ("");
1198         _internet_address_list_to_string (list, flags, &linelen, string);
1199         str = string->str;
1200         
1201         g_string_free (string, FALSE);
1202         
1203         return str;
1204 }
1205
1206
1207 /**
1208  * internet_address_list_writer:
1209  * @list: list of internet addresses
1210  * @str: string to write to
1211  *
1212  * Writes the rfc2047-encoded rfc822 formatted addresses in @list to
1213  * @string, folding appropriately.
1214  **/
1215 void
1216 internet_address_list_writer (InternetAddressList *list, GString *str)
1217 {
1218         guint32 flags = INTERNET_ADDRESS_ENCODE | INTERNET_ADDRESS_FOLD;
1219         size_t linelen = str->len;
1220         
1221         g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
1222         g_return_if_fail (str != NULL);
1223         
1224         _internet_address_list_to_string (list, flags, &linelen, str);
1225 }
1226
1227 static void
1228 _internet_address_decode_name (InternetAddress *ia, GString *name)
1229 {
1230         char *value, *buf = NULL;
1231         char *phrase;
1232         
1233         if (!g_utf8_validate (name->str, name->len, NULL)) {
1234                 /* A (broken) mailer has sent us raw 8bit/multibyte text data... */
1235                 buf = g_mime_utils_decode_8bit (name->str, name->len);
1236                 phrase = buf;
1237         } else {
1238                 phrase = name->str;
1239         }
1240         
1241         /* decode the phrase */
1242         g_mime_utils_unquote_string (phrase);
1243         value = g_mime_utils_header_decode_phrase (phrase);
1244         g_free (ia->name);
1245         ia->name = value;
1246         g_free (buf);
1247 }
1248
1249 static InternetAddress *decode_address (const char **in);
1250
1251 static void
1252 skip_lwsp (const char **in)
1253 {
1254         register const char *inptr = *in;
1255         
1256         while (*inptr && is_lwsp (*inptr))
1257                 inptr++;
1258         
1259         *in = inptr;
1260 }
1261
1262 static InternetAddress *
1263 decode_addrspec (const char **in)
1264 {
1265         InternetAddress *mailbox = NULL;
1266         const char *start, *inptr, *word;
1267         gboolean got_local = FALSE;
1268         GString *addr;
1269         size_t len;
1270         
1271         addr = g_string_new ("");
1272         inptr = *in;
1273         
1274         decode_lwsp (&inptr);
1275         
1276         /* some spam bots set their addresses to stuff like: ).ORHH@em.ca */
1277         while (*inptr && !(*inptr == '"' || is_atom (*inptr)))
1278                 inptr++;
1279         
1280         start = inptr;
1281         
1282         /* extract the first word of the local-part */
1283         if ((word = decode_word (&inptr))) {
1284                 g_string_append_len (addr, word, (size_t) (inptr - word));
1285                 decode_lwsp (&inptr);
1286                 got_local = TRUE;
1287         }
1288         
1289         /* extract the rest of the local-part */
1290         while (word && *inptr == '.') {
1291                 /* Note: According to the spec, only a single '.' is
1292                  * allowed between word tokens in the local-part of an
1293                  * addr-spec token, but according to Evolution bug
1294                  * #547969, some Japanese cellphones have email
1295                  * addresses that look like x..y@somewhere.jp */
1296                 do {
1297                         inptr++;
1298                         decode_lwsp (&inptr);
1299                         g_string_append_c (addr, '.');
1300                 } while (*inptr == '.');
1301                 
1302                 if ((word = decode_word (&inptr)))
1303                         g_string_append_len (addr, word, (size_t) (inptr - word));
1304                 
1305                 decode_lwsp (&inptr);
1306         }
1307         
1308         if (*inptr == '@') {
1309                 len = addr->len;
1310                 
1311                 g_string_append_c (addr, '@');
1312                 inptr++;
1313                 
1314                 if (!decode_domain (&inptr, addr)) {
1315                         /* drop the @domain and continue as if it weren't there */
1316                         w(g_warning ("Missing domain in addr-spec: %.*s",
1317                                      inptr - start, start));
1318                         g_string_truncate (addr, len);
1319                 }
1320         } else if (got_local) {
1321                 w(g_warning ("Missing '@' and domain in addr-spec: %.*s",
1322                              inptr - start, start));
1323         }
1324         
1325         *in = inptr;
1326         
1327         if (!got_local) {
1328                 w(g_warning ("Invalid addr-spec, missing local-part: %.*s",
1329                              inptr - start, start));
1330                 g_string_free (addr, TRUE);
1331                 return NULL;
1332         }
1333         
1334         mailbox = g_object_newv (INTERNET_ADDRESS_TYPE_MAILBOX, 0, NULL);
1335         ((InternetAddressMailbox *) mailbox)->addr = addr->str;
1336         g_string_free (addr, FALSE);
1337         
1338         return mailbox;
1339 }
1340
1341 static InternetAddress *
1342 decode_group (const char **in)
1343 {
1344         InternetAddressGroup *group;
1345         InternetAddress *addr;
1346         const char *inptr;
1347         
1348         inptr = *in;
1349         
1350         addr = internet_address_group_new (NULL);
1351         group = (InternetAddressGroup *) addr;
1352         
1353         decode_lwsp (&inptr);
1354         while (*inptr && *inptr != ';') {
1355                 InternetAddress *member;
1356                 
1357                 if ((member = decode_address (&inptr)))
1358                         _internet_address_group_add_member (group, member);
1359                 
1360                 decode_lwsp (&inptr);
1361                 while (*inptr == ',') {
1362                         inptr++;
1363                         decode_lwsp (&inptr);
1364                         if ((member = decode_address (&inptr)))
1365                                 _internet_address_group_add_member (group, member);
1366                         
1367                         decode_lwsp (&inptr);
1368                 }
1369         }
1370         
1371         *in = inptr;
1372         
1373         return addr;
1374 }
1375
1376 static InternetAddress *
1377 decode_address (const char **in)
1378 {
1379         const char *inptr, *start, *word, *comment = NULL;
1380         InternetAddress *addr = NULL;
1381         gboolean has_lwsp = FALSE;
1382         gboolean is_word;
1383         GString *name;
1384         
1385         decode_lwsp (in);
1386         start = inptr = *in;
1387         
1388         name = g_string_new ("");
1389         
1390         /* Both groups and mailboxes can begin with a phrase (denoting
1391          * the display name for the address). Collect all of the
1392          * tokens that make up this name phrase.
1393          */
1394         while (*inptr) {
1395                 if ((word = decode_word (&inptr))) {
1396                         g_string_append_len (name, word, (size_t) (inptr - word));
1397                         
1398                 check_lwsp:
1399                         word = inptr;
1400                         skip_lwsp (&inptr);
1401                         
1402                         /* is the next token a word token? */
1403                         is_word = *inptr == '"' || is_atom (*inptr);
1404                         
1405                         if (inptr > word && is_word) {
1406                                 g_string_append_c (name, ' ');
1407                                 has_lwsp = TRUE;
1408                         }
1409                         
1410                         if (is_word)
1411                                 continue;
1412                 }
1413                 
1414                 /* specials    =  "(" / ")" / "<" / ">" / "@"  ; Must be in quoted-
1415                  *             /  "," / ";" / ":" / "\" / <">  ;  string, to use
1416                  *             /  "." / "[" / "]"              ;  within a word.
1417                  */
1418                 if (*inptr == ':') {
1419                         /* group */
1420                         inptr++;
1421                         addr = decode_group (&inptr);
1422                         decode_lwsp (&inptr);
1423                         if (*inptr != ';')
1424                                 w(g_warning ("Invalid group spec, missing closing ';': %.*s",
1425                                              inptr - start, start));
1426                         else
1427                                 inptr++;
1428                         break;
1429                 } else if (*inptr == '<') {
1430                         /* mailbox route-addr */
1431                         inptr++;
1432                         addr = decode_addrspec (&inptr);
1433                         decode_lwsp (&inptr);
1434                         if (*inptr != '>') {
1435                                 w(g_warning ("Invalid route-addr, missing closing '>': %.*s",
1436                                              inptr - start, start));
1437                                 
1438                                 while (*inptr && *inptr != '>' && *inptr != ',')
1439                                         inptr++;
1440                                 
1441                                 if (*inptr == '>')
1442                                         inptr++;
1443                         } else
1444                                 inptr++;
1445                         
1446                         /* if comment is non-NULL, we can check for a comment containing a name */
1447                         comment = inptr;
1448                         break;
1449                 } else if (*inptr == '(') {
1450                         /* beginning of a comment, use decode_lwsp() to skip past it */
1451                         decode_lwsp (&inptr);
1452                 } else if (*inptr && strchr ("@,;", *inptr)) {
1453                         if (name->len == 0) {
1454                                 if (*inptr == '@') {
1455                                         GString *domain;
1456                                         
1457                                         w(g_warning ("Unexpected address: %s: skipping.", start));
1458                                         
1459                                         /* skip over @domain? */
1460                                         inptr++;
1461                                         domain = g_string_new ("");
1462                                         decode_domain (&inptr, domain);
1463                                         g_string_free (domain, TRUE);
1464                                 } else {
1465                                         /* empty address */
1466                                 }
1467                                 break;
1468                         } else if (has_lwsp) {
1469                                 /* assume this is just an unquoted special that we should
1470                                    treat as part of the name */
1471                                 w(g_warning ("Unquoted '%c' in address name: %s: ignoring.", *inptr, start));
1472                                 g_string_append_c (name, *inptr);
1473                                 inptr++;
1474                                 
1475                                 goto check_lwsp;
1476                         }
1477                         
1478                 addrspec:
1479                         /* what we thought was a name was actually an addrspec? */
1480                         g_string_truncate (name, 0);
1481                         inptr = start;
1482                         
1483                         addr = decode_addrspec (&inptr);
1484                         
1485                         /* if comment is non-NULL, we can check for a comment containing a name */
1486                         comment = inptr;
1487                         break;
1488                 } else if (*inptr == '.') {
1489                         /* This would normally signify that we are
1490                          * decoding the local-part of an addr-spec,
1491                          * but sadly, it is common for broken mailers
1492                          * to forget to quote/encode .'s in the name
1493                          * phrase. */
1494                         g_string_append_c (name, *inptr);
1495                         inptr++;
1496                         
1497                         goto check_lwsp;
1498                 } else if (*inptr) {
1499                         /* Technically, these are all invalid tokens
1500                          * but in the interest of being liberal in
1501                          * what we accept, we'll ignore them. */
1502                         w(g_warning ("Unexpected char '%c' in address: %s: ignoring.", *inptr, start));
1503                         g_string_append_c (name, *inptr);
1504                         inptr++;
1505                         
1506                         goto check_lwsp;
1507                 } else {
1508                         goto addrspec;
1509                 }
1510         }
1511         
1512         /* Note: will also skip over any comments */
1513         decode_lwsp (&inptr);
1514         
1515         if (name->len == 0 && comment && inptr > comment) {
1516                 /* missing a name, look for a trailing comment */
1517                 if ((comment = memchr (comment, '(', inptr - comment))) {
1518                         const char *cend;
1519                         
1520                         /* find the end of the comment */
1521                         cend = inptr - 1;
1522                         while (cend > comment && is_lwsp (*cend))
1523                                 cend--;
1524                         
1525                         if (*cend == ')')
1526                                 cend--;
1527                         
1528                         g_string_append_len (name, comment + 1, (size_t) (cend - comment));
1529                 }
1530         }
1531         
1532         if (addr && name->len > 0)
1533                 _internet_address_decode_name (addr, name);
1534         
1535         g_string_free (name, TRUE);
1536         
1537         *in = inptr;
1538         
1539         return addr;
1540 }
1541
1542
1543 /**
1544  * internet_address_list_parse_string:
1545  * @str: a string containing internet addresses
1546  *
1547  * Construct a list of internet addresses from the given string.
1548  *
1549  * Returns: a #InternetAddressList or %NULL if the input string does
1550  * not contain any addresses.
1551  **/
1552 InternetAddressList *
1553 internet_address_list_parse_string (const char *str)
1554 {
1555         InternetAddressList *addrlist;
1556         const char *inptr = str;
1557         
1558         addrlist = internet_address_list_new ();
1559         
1560         while (inptr && *inptr) {
1561                 InternetAddress *addr;
1562                 const char *start;
1563                 
1564                 start = inptr;
1565                 
1566                 if ((addr = decode_address (&inptr))) {
1567                         _internet_address_list_add (addrlist, addr);
1568                 } else {
1569                         w(g_warning ("Invalid or incomplete address: %.*s",
1570                                      inptr - start, start));
1571                 }
1572                 
1573                 decode_lwsp (&inptr);
1574                 if (*inptr == ',') {
1575                         inptr++;
1576                 } else if (*inptr) {
1577                         w(g_warning ("Parse error at '%s': expected ','", inptr));
1578                         /* try skipping to the next address */
1579                         if ((inptr = strchr (inptr, ',')))
1580                                 inptr++;
1581                 }
1582         }
1583         
1584         if (addrlist->array->len == 0) {
1585                 g_object_unref (addrlist);
1586                 addrlist = NULL;
1587         }
1588         
1589         return addrlist;
1590 }