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