1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  GMime
3  *  Copyright (C) 2000-2020 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 <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31 
32 #ifdef LIBIDN
33 #include <idn2.h>
34 #undef G_GNUC_UNUSED
35 #undef G_GNUC_DEPRECATED
36 #endif
37 
38 #include "internet-address.h"
39 #include "gmime-table-private.h"
40 #include "gmime-parse-utils.h"
41 #include "gmime-iconv-utils.h"
42 #include "gmime-internal.h"
43 #include "gmime-events.h"
44 #include "gmime-utils.h"
45 
46 
47 #ifdef ENABLE_WARNINGS
48 #define w(x) x
49 #else
50 #define w(x)
51 #endif /* ENABLE_WARNINGS */
52 
53 #define d(x)
54 
55 
56 /**
57  * SECTION: internet-address
58  * @title: InternetAddress
59  * @short_description: Internet addresses
60  * @see_also: #InternetAddressGroup, #InternetAddressMailbox
61  *
62  * An #InternetAddress is the base class for #InternetAddressGroup and
63  * #InternetAddressMailbox.
64  **/
65 
66 /**
67  * SECTION: internet-address-group
68  * @title: InternetAddressGroup
69  * @short_description: rfc822 'group' address
70  * @see_also: #InternetAddress
71  *
72  * An #InternetAddressGroup represents an rfc822 'group' address.
73  **/
74 
75 /**
76  * SECTION: internet-address-mailbox
77  * @title: InternetAddressMailbox
78  * @short_description: rfc822 'mailbox' address
79  * @see_also: #InternetAddress
80  *
81  * An #InternetAddressMailbox represents what is a typical "email
82  * address".
83  **/
84 
85 /**
86  * SECTION: internet-address-list
87  * @title: InternetAddressList
88  * @short_description: A list of internet addresses
89  * @see_also: #InternetAddress
90  *
91  * An #InternetAddressList is a collection of #InternetAddress
92  * objects.
93  **/
94 
95 
96 enum {
97 	INTERNET_ADDRESS_ENCODE = 1 << 0,
98 	INTERNET_ADDRESS_FOLD   = 1 << 1,
99 };
100 
101 static gboolean addrspec_parse (const char **in, const char *sentinels, char **addrspec, int *at);
102 
103 static void internet_address_class_init (InternetAddressClass *klass);
104 static void internet_address_init (InternetAddress *ia, InternetAddressClass *klass);
105 static void internet_address_finalize (GObject *object);
106 
107 
108 static GObjectClass *parent_class = NULL;
109 
110 
111 GType
internet_address_get_type(void)112 internet_address_get_type (void)
113 {
114 	static GType type = 0;
115 
116 	if (!type) {
117 		static const GTypeInfo info = {
118 			sizeof (InternetAddressClass),
119 			NULL, /* base_class_init */
120 			NULL, /* base_class_finalize */
121 			(GClassInitFunc) internet_address_class_init,
122 			NULL, /* class_finalize */
123 			NULL, /* class_data */
124 			sizeof (InternetAddress),
125 			0,    /* n_preallocs */
126 			(GInstanceInitFunc) internet_address_init,
127 		};
128 
129 		type = g_type_register_static (G_TYPE_OBJECT, "InternetAddress",
130 					       &info, G_TYPE_FLAG_ABSTRACT);
131 	}
132 
133 	return type;
134 }
135 
136 
137 static void
internet_address_class_init(InternetAddressClass * klass)138 internet_address_class_init (InternetAddressClass *klass)
139 {
140 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
141 
142 	parent_class = g_type_class_ref (G_TYPE_OBJECT);
143 
144 	object_class->finalize = internet_address_finalize;
145 
146 	klass->to_string = NULL;
147 }
148 
149 static void
internet_address_init(InternetAddress * ia,InternetAddressClass * klass)150 internet_address_init (InternetAddress *ia, InternetAddressClass *klass)
151 {
152 	ia->changed = g_mime_event_new ((GObject *) ia);
153 	ia->charset = NULL;
154 	ia->name = NULL;
155 }
156 
157 static void
internet_address_finalize(GObject * object)158 internet_address_finalize (GObject *object)
159 {
160 	InternetAddress *ia = (InternetAddress *) object;
161 
162 	g_mime_event_free (ia->changed);
163 	g_free (ia->charset);
164 	g_free (ia->name);
165 
166 	G_OBJECT_CLASS (parent_class)->finalize (object);
167 }
168 
169 
170 static void
_internet_address_set_name(InternetAddress * ia,const char * name)171 _internet_address_set_name (InternetAddress *ia, const char *name)
172 {
173 	char *buf;
174 
175 	buf = g_strdup (name);
176 	g_free (ia->name);
177 	ia->name = buf;
178 }
179 
180 
181 /**
182  * internet_address_set_name:
183  * @ia: a #InternetAddress
184  * @name: the display name for the address group or mailbox
185  *
186  * Set the display name of the #InternetAddress.
187  *
188  * Note: The @name string should be in UTF-8.
189  **/
190 void
internet_address_set_name(InternetAddress * ia,const char * name)191 internet_address_set_name (InternetAddress *ia, const char *name)
192 {
193 	g_return_if_fail (IS_INTERNET_ADDRESS (ia));
194 
195 	_internet_address_set_name (ia, name);
196 
197 	g_mime_event_emit (ia->changed, NULL);
198 }
199 
200 
201 /**
202  * internet_address_get_name:
203  * @ia: a #InternetAddress
204  *
205  * Gets the display name of the #InternetAddress.
206  *
207  * Returns: (nullable): the name of the mailbox or group in a form suitable
208  * for display if available or %NULL otherwise. If the name is available,
209  * the returned string will be in UTF-8.
210  **/
211 const char *
internet_address_get_name(InternetAddress * ia)212 internet_address_get_name (InternetAddress *ia)
213 {
214 	g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), NULL);
215 
216 	return ia->name;
217 }
218 
219 
220 /**
221  * internet_address_set_charset:
222  * @ia: a #InternetAddress
223  * @charset: (nullable): the charset to use when encoding the name or %NULL to use the defaults
224  *
225  * Set the charset to use for encoding the name of the mailbox or group.
226  **/
227 void
internet_address_set_charset(InternetAddress * ia,const char * charset)228 internet_address_set_charset (InternetAddress *ia, const char *charset)
229 {
230 	char *buf;
231 
232 	g_return_if_fail (IS_INTERNET_ADDRESS (ia));
233 
234 	buf = charset ? g_strdup (charset) : NULL;
235 	g_free (ia->charset);
236 	ia->charset = buf;
237 
238 	g_mime_event_emit (ia->changed, NULL);
239 }
240 
241 
242 /**
243  * internet_address_get_charset:
244  * @ia: a #InternetAddress
245  *
246  * Gets the charset to be used when encoding the name of the mailbox or group.
247  *
248  * Returns: (nullable): the charset to be used when encoding the name of the
249  * mailbox or group if available or %NULL otherwise.
250  **/
251 const char *
internet_address_get_charset(InternetAddress * ia)252 internet_address_get_charset (InternetAddress *ia)
253 {
254 	g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), NULL);
255 
256 	return ia->charset;
257 }
258 
259 
260 /**
261  * internet_address_to_string:
262  * @ia: Internet Address object
263  * @options: (nullable): a #GMimeFormatOptions or %NULL
264  * @encode: %TRUE if the address should be rfc2047 encoded
265  *
266  * Allocates a string containing the contents of the #InternetAddress
267  * object.
268  *
269  * Returns: the #InternetAddress object as an allocated string in
270  * rfc822 format.
271  **/
272 char *
internet_address_to_string(InternetAddress * ia,GMimeFormatOptions * options,gboolean encode)273 internet_address_to_string (InternetAddress *ia, GMimeFormatOptions *options, gboolean encode)
274 {
275 	guint32 flags = encode ? INTERNET_ADDRESS_ENCODE : 0;
276 	size_t linelen = 0;
277 	GString *string;
278 	char *str;
279 
280 	string = g_string_new ("");
281 	INTERNET_ADDRESS_GET_CLASS (ia)->to_string (ia, options, flags, &linelen, string);
282 	str = string->str;
283 
284 	g_string_free (string, FALSE);
285 
286 	return str;
287 }
288 
289 
290 static void internet_address_mailbox_class_init (InternetAddressMailboxClass *klass);
291 static void internet_address_mailbox_init (InternetAddressMailbox *mailbox, InternetAddressMailboxClass *klass);
292 static void internet_address_mailbox_finalize (GObject *object);
293 
294 static void mailbox_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 flags,
295 			       size_t *linelen, GString *out);
296 
297 
298 static GObjectClass *mailbox_parent_class = NULL;
299 
300 
301 GType
internet_address_mailbox_get_type(void)302 internet_address_mailbox_get_type (void)
303 {
304 	static GType type = 0;
305 
306 	if (!type) {
307 		static const GTypeInfo info = {
308 			sizeof (InternetAddressMailboxClass),
309 			NULL, /* base_class_init */
310 			NULL, /* base_class_finalize */
311 			(GClassInitFunc) internet_address_mailbox_class_init,
312 			NULL, /* class_finalize */
313 			NULL, /* class_data */
314 			sizeof (InternetAddressMailbox),
315 			0,    /* n_preallocs */
316 			(GInstanceInitFunc) internet_address_mailbox_init,
317 		};
318 
319 		type = g_type_register_static (INTERNET_ADDRESS_TYPE, "InternetAddressMailbox", &info, 0);
320 	}
321 
322 	return type;
323 }
324 
325 
326 static void
internet_address_mailbox_class_init(InternetAddressMailboxClass * klass)327 internet_address_mailbox_class_init (InternetAddressMailboxClass *klass)
328 {
329 	InternetAddressClass *address_class = INTERNET_ADDRESS_CLASS (klass);
330 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
331 
332 	mailbox_parent_class = g_type_class_ref (INTERNET_ADDRESS_TYPE);
333 
334 	object_class->finalize = internet_address_mailbox_finalize;
335 
336 	address_class->to_string = mailbox_to_string;
337 }
338 
339 static void
internet_address_mailbox_init(InternetAddressMailbox * mailbox,InternetAddressMailboxClass * klass)340 internet_address_mailbox_init (InternetAddressMailbox *mailbox, InternetAddressMailboxClass *klass)
341 {
342 	mailbox->idn_addr = NULL;
343 	mailbox->addr = NULL;
344 	mailbox->at = -1;
345 }
346 
347 static void
internet_address_mailbox_finalize(GObject * object)348 internet_address_mailbox_finalize (GObject *object)
349 {
350 	InternetAddressMailbox *mailbox = (InternetAddressMailbox *) object;
351 
352 	g_free (mailbox->idn_addr);
353 	g_free (mailbox->addr);
354 
355 	G_OBJECT_CLASS (mailbox_parent_class)->finalize (object);
356 }
357 
358 static InternetAddress *
_internet_address_mailbox_new(const char * name,const char * addr,int at)359 _internet_address_mailbox_new (const char *name, const char *addr, int at)
360 {
361 	InternetAddressMailbox *mailbox;
362 
363 	mailbox = g_object_new (INTERNET_ADDRESS_TYPE_MAILBOX, NULL);
364 	mailbox->addr = g_strdup (addr);
365 	mailbox->at = at;
366 
367 	_internet_address_set_name ((InternetAddress *) mailbox, name);
368 
369 	return (InternetAddress *) mailbox;
370 }
371 
372 
373 /**
374  * internet_address_mailbox_new:
375  * @name: person's name
376  * @addr: person's address
377  *
378  * Creates a new #InternetAddress object with the specified @name and
379  * @addr.
380  *
381  * Returns: a new #InternetAddressMailbox object.
382  *
383  * Note: The @name string should be in UTF-8.
384  **/
385 InternetAddress *
internet_address_mailbox_new(const char * name,const char * addr)386 internet_address_mailbox_new (const char *name, const char *addr)
387 {
388 	InternetAddressMailbox *mailbox;
389 	const char *inptr = addr;
390 
391 	g_return_val_if_fail (addr != NULL, NULL);
392 
393 	mailbox = g_object_new (INTERNET_ADDRESS_TYPE_MAILBOX, NULL);
394 
395 	if (!addrspec_parse (&inptr, "", &mailbox->addr, &mailbox->at))
396 		mailbox->addr = g_strdup (addr);
397 
398 	_internet_address_set_name ((InternetAddress *) mailbox, name);
399 
400 	return (InternetAddress *) mailbox;
401 }
402 
403 
404 /**
405  * internet_address_mailbox_set_addr:
406  * @mailbox: a #InternetAddressMailbox
407  * @addr: contact's email address
408  *
409  * Set the mailbox address.
410  **/
411 void
internet_address_mailbox_set_addr(InternetAddressMailbox * mailbox,const char * addr)412 internet_address_mailbox_set_addr (InternetAddressMailbox *mailbox, const char *addr)
413 {
414 	const char *inptr = addr;
415 
416 	g_return_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox));
417 
418 	if (mailbox->addr == addr)
419 		return;
420 
421 	g_free (mailbox->idn_addr);
422 	mailbox->idn_addr = NULL;
423 
424 	g_free (mailbox->addr);
425 
426 	if (!addrspec_parse (&inptr, "", &mailbox->addr, &mailbox->at))
427 		mailbox->addr = g_strdup (addr);
428 
429 	g_mime_event_emit (((InternetAddress *) mailbox)->changed, NULL);
430 }
431 
432 
433 /**
434  * internet_address_mailbox_get_addr:
435  * @mailbox: a #InternetAddressMailbox
436  *
437  * Gets the addr-spec of the internet address mailbox.
438  *
439  * Returns: the addr-spec string.
440  **/
441 const char *
internet_address_mailbox_get_addr(InternetAddressMailbox * mailbox)442 internet_address_mailbox_get_addr (InternetAddressMailbox *mailbox)
443 {
444 	g_return_val_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox), NULL);
445 
446 	return mailbox->addr;
447 }
448 
449 
450 /**
451  * internet_address_mailbox_get_idn_addr:
452  * @mailbox: a #InternetAddressMailbox
453  *
454  * Gets the IDN ascii-encoded addr-spec.
455  *
456  * Returns: the encoded addr-spec string.
457  **/
458 const char *
internet_address_mailbox_get_idn_addr(InternetAddressMailbox * mailbox)459 internet_address_mailbox_get_idn_addr (InternetAddressMailbox *mailbox)
460 {
461 	GString *encoded;
462 	char *ascii;
463 
464 	g_return_val_if_fail (INTERNET_ADDRESS_IS_MAILBOX (mailbox), NULL);
465 
466 #ifdef LIBIDN
467 	if (!mailbox->idn_addr && mailbox->at > 0) {
468 		const char *domain = mailbox->addr + mailbox->at + 1;
469 
470 		encoded = g_string_new ("");
471 		g_string_append_len (encoded, mailbox->addr, mailbox->at + 1);
472 		if (idn2_to_ascii_8z (domain, &ascii, 0) == IDN2_OK) {
473 			if (!g_ascii_strcasecmp (domain, ascii))
474 				g_string_append (encoded, domain);
475 			else
476 				g_string_append (encoded, ascii);
477 			idn2_free (ascii);
478 		} else {
479 			g_string_append (encoded, domain);
480 		}
481 
482 		mailbox->idn_addr = g_string_free (encoded, FALSE);
483 	}
484 
485 	return mailbox->idn_addr ? mailbox->idn_addr : mailbox->addr;
486 #else
487 	return mailbox->addr;
488 #endif
489 }
490 
491 
492 static void internet_address_group_class_init (InternetAddressGroupClass *klass);
493 static void internet_address_group_init (InternetAddressGroup *group, InternetAddressGroupClass *klass);
494 static void internet_address_group_finalize (GObject *object);
495 
496 static void group_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 flags,
497 			     size_t *linelen, GString *out);
498 
499 
500 static GObjectClass *group_parent_class = NULL;
501 
502 
503 GType
internet_address_group_get_type(void)504 internet_address_group_get_type (void)
505 {
506 	static GType type = 0;
507 
508 	if (!type) {
509 		static const GTypeInfo info = {
510 			sizeof (InternetAddressGroupClass),
511 			NULL, /* base_class_init */
512 			NULL, /* base_class_finalize */
513 			(GClassInitFunc) internet_address_group_class_init,
514 			NULL, /* class_finalize */
515 			NULL, /* class_data */
516 			sizeof (InternetAddressGroup),
517 			0,    /* n_preallocs */
518 			(GInstanceInitFunc) internet_address_group_init,
519 		};
520 
521 		type = g_type_register_static (INTERNET_ADDRESS_TYPE, "InternetAddressGroup", &info, 0);
522 	}
523 
524 	return type;
525 }
526 
527 
528 static void
internet_address_group_class_init(InternetAddressGroupClass * klass)529 internet_address_group_class_init (InternetAddressGroupClass *klass)
530 {
531 	InternetAddressClass *address_class = INTERNET_ADDRESS_CLASS (klass);
532 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
533 
534 	group_parent_class = g_type_class_ref (INTERNET_ADDRESS_TYPE);
535 
536 	object_class->finalize = internet_address_group_finalize;
537 
538 	address_class->to_string = group_to_string;
539 }
540 
541 static void
members_changed(InternetAddressList * members,gpointer args,InternetAddress * group)542 members_changed (InternetAddressList *members, gpointer args, InternetAddress *group)
543 {
544 	g_mime_event_emit (((InternetAddress *) group)->changed, NULL);
545 }
546 
547 static void
internet_address_group_init(InternetAddressGroup * group,InternetAddressGroupClass * klass)548 internet_address_group_init (InternetAddressGroup *group, InternetAddressGroupClass *klass)
549 {
550 	group->members = internet_address_list_new ();
551 
552 	g_mime_event_add (group->members->changed, (GMimeEventCallback) members_changed, group);
553 }
554 
555 static void
internet_address_group_finalize(GObject * object)556 internet_address_group_finalize (GObject *object)
557 {
558 	InternetAddressGroup *group = (InternetAddressGroup *) object;
559 
560 	g_mime_event_remove (group->members->changed, (GMimeEventCallback) members_changed, group);
561 
562 	g_object_unref (group->members);
563 
564 	G_OBJECT_CLASS (group_parent_class)->finalize (object);
565 }
566 
567 
568 /**
569  * internet_address_group_new:
570  * @name: group name
571  *
572  * Creates a new #InternetAddressGroup object with the specified
573  * @name.
574  *
575  * Returns: a new #InternetAddressGroup object.
576  *
577  * Note: The @name string should be in UTF-8.
578  **/
579 InternetAddress *
internet_address_group_new(const char * name)580 internet_address_group_new (const char *name)
581 {
582 	InternetAddress *group;
583 
584 	group = g_object_new (INTERNET_ADDRESS_TYPE_GROUP, NULL);
585 	_internet_address_set_name (group, name);
586 
587 	return group;
588 }
589 
590 
591 /**
592  * internet_address_group_set_members:
593  * @group: a #InternetAddressGroup
594  * @members: a #InternetAddressList
595  *
596  * Set the members of the internet address group.
597  **/
598 void
internet_address_group_set_members(InternetAddressGroup * group,InternetAddressList * members)599 internet_address_group_set_members (InternetAddressGroup *group, InternetAddressList *members)
600 {
601 	g_return_if_fail (INTERNET_ADDRESS_IS_GROUP (group));
602 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (members));
603 
604 	if (group->members == members)
605 		return;
606 
607 	if (group->members) {
608 		g_mime_event_remove (group->members->changed, (GMimeEventCallback) members_changed, group);
609 		g_object_unref (group->members);
610 	}
611 
612 	if (members) {
613 		g_mime_event_add (members->changed, (GMimeEventCallback) members_changed, group);
614 		g_object_ref (members);
615 	}
616 
617 	group->members = members;
618 
619 	g_mime_event_emit (((InternetAddress *) group)->changed, NULL);
620 }
621 
622 
623 /**
624  * internet_address_group_get_members:
625  * @group: a #InternetAddressGroup
626  *
627  * Gets the #InternetAddressList containing the group members of an
628  * rfc822 group address.
629  *
630  * Returns: (transfer none): a #InternetAddressList containing the
631  * members of @group.
632  **/
633 InternetAddressList *
internet_address_group_get_members(InternetAddressGroup * group)634 internet_address_group_get_members (InternetAddressGroup *group)
635 {
636 	g_return_val_if_fail (INTERNET_ADDRESS_IS_GROUP (group), NULL);
637 
638 	return group->members;
639 }
640 
641 
642 #define _internet_address_group_add_member(group,member) _internet_address_list_add (group->members, member)
643 
644 /**
645  * internet_address_group_add_member:
646  * @group: a #InternetAddressGroup
647  * @member: a #InternetAddress
648  *
649  * Add a contact to the internet address group.
650  *
651  * Returns: the index of the newly added member.
652  **/
653 int
internet_address_group_add_member(InternetAddressGroup * group,InternetAddress * member)654 internet_address_group_add_member (InternetAddressGroup *group, InternetAddress *member)
655 {
656 	g_return_val_if_fail (INTERNET_ADDRESS_IS_GROUP (group), -1);
657 	g_return_val_if_fail (IS_INTERNET_ADDRESS (member), -1);
658 
659 	return internet_address_list_add (group->members, member);
660 }
661 
662 
663 static void internet_address_list_class_init (InternetAddressListClass *klass);
664 static void internet_address_list_init (InternetAddressList *list, InternetAddressListClass *klass);
665 static void internet_address_list_finalize (GObject *object);
666 
667 
668 static GObjectClass *list_parent_class = NULL;
669 
670 
671 GType
internet_address_list_get_type(void)672 internet_address_list_get_type (void)
673 {
674 	static GType type = 0;
675 
676 	if (!type) {
677 		static const GTypeInfo info = {
678 			sizeof (InternetAddressListClass),
679 			NULL, /* base_class_init */
680 			NULL, /* base_class_finalize */
681 			(GClassInitFunc) internet_address_list_class_init,
682 			NULL, /* class_finalize */
683 			NULL, /* class_data */
684 			sizeof (InternetAddressList),
685 			0,    /* n_preallocs */
686 			(GInstanceInitFunc) internet_address_list_init,
687 		};
688 
689 		type = g_type_register_static (G_TYPE_OBJECT, "InternetAddressList", &info, 0);
690 	}
691 
692 	return type;
693 }
694 
695 
696 static void
internet_address_list_class_init(InternetAddressListClass * klass)697 internet_address_list_class_init (InternetAddressListClass *klass)
698 {
699 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
700 
701 	list_parent_class = g_type_class_ref (G_TYPE_OBJECT);
702 
703 	object_class->finalize = internet_address_list_finalize;
704 }
705 
706 static void
internet_address_list_init(InternetAddressList * list,InternetAddressListClass * klass)707 internet_address_list_init (InternetAddressList *list, InternetAddressListClass *klass)
708 {
709 	list->changed = g_mime_event_new ((GObject *) list);
710 	list->array = g_ptr_array_new ();
711 }
712 
713 static void
address_changed(InternetAddress * ia,gpointer args,InternetAddressList * list)714 address_changed (InternetAddress *ia, gpointer args, InternetAddressList *list)
715 {
716 	g_mime_event_emit (list->changed, NULL);
717 }
718 
719 static void
internet_address_list_finalize(GObject * object)720 internet_address_list_finalize (GObject *object)
721 {
722 	InternetAddressList *list = (InternetAddressList *) object;
723 	InternetAddress *ia;
724 	guint i;
725 
726 	for (i = 0; i < list->array->len; i++) {
727 		ia = (InternetAddress *) list->array->pdata[i];
728 		g_mime_event_remove (ia->changed, (GMimeEventCallback) address_changed, list);
729 		g_object_unref (ia);
730 	}
731 
732 	g_mime_event_free (list->changed);
733 
734 	g_ptr_array_free (list->array, TRUE);
735 
736 	G_OBJECT_CLASS (list_parent_class)->finalize (object);
737 }
738 
739 
740 /**
741  * internet_address_list_new:
742  *
743  * Creates a new #InternetAddressList.
744  *
745  * Returns: a new #InternetAddressList.
746  **/
747 InternetAddressList *
internet_address_list_new(void)748 internet_address_list_new (void)
749 {
750 	return g_object_new (INTERNET_ADDRESS_LIST_TYPE, NULL);
751 }
752 
753 
754 /**
755  * internet_address_list_length:
756  * @list: a #InternetAddressList
757  *
758  * Gets the length of the list.
759  *
760  * Returns: the number of #InternetAddress objects in the list.
761  **/
762 int
internet_address_list_length(InternetAddressList * list)763 internet_address_list_length (InternetAddressList *list)
764 {
765 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), -1);
766 
767 	return list->array->len;
768 }
769 
770 
771 /**
772  * internet_address_list_clear:
773  * @list: a #InternetAddressList
774  *
775  * Clears the list of addresses.
776  **/
777 void
internet_address_list_clear(InternetAddressList * list)778 internet_address_list_clear (InternetAddressList *list)
779 {
780 	InternetAddress *ia;
781 	guint i;
782 
783 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
784 
785 	for (i = 0; i < list->array->len; i++) {
786 		ia = (InternetAddress *) list->array->pdata[i];
787 		g_mime_event_remove (ia->changed, (GMimeEventCallback) address_changed, list);
788 		g_object_unref (ia);
789 	}
790 
791 	g_ptr_array_set_size (list->array, 0);
792 
793 	g_mime_event_emit (list->changed, NULL);
794 }
795 
796 
797 static int
_internet_address_list_add(InternetAddressList * list,InternetAddress * ia)798 _internet_address_list_add (InternetAddressList *list, InternetAddress *ia)
799 {
800 	int index;
801 
802 	g_mime_event_add (ia->changed, (GMimeEventCallback) address_changed, list);
803 
804 	index = list->array->len;
805 	g_ptr_array_add (list->array, ia);
806 
807 	return index;
808 }
809 
810 
811 /**
812  * internet_address_list_add:
813  * @list: a #InternetAddressList
814  * @ia: a #InternetAddress
815  *
816  * Adds an #InternetAddress to the #InternetAddressList.
817  *
818  * Returns: the index of the added #InternetAddress.
819  **/
820 int
internet_address_list_add(InternetAddressList * list,InternetAddress * ia)821 internet_address_list_add (InternetAddressList *list, InternetAddress *ia)
822 {
823 	int index;
824 
825 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), -1);
826 	g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), -1);
827 
828 	index = _internet_address_list_add (list, ia);
829 	g_object_ref (ia);
830 
831 	g_mime_event_emit (list->changed, NULL);
832 
833 	return index;
834 }
835 
836 
837 /**
838  * internet_address_list_prepend:
839  * @list: a #InternetAddressList
840  * @prepend: a #InternetAddressList
841  *
842  * Inserts all of the addresses in @prepend to the beginning of @list.
843  **/
844 void
internet_address_list_prepend(InternetAddressList * list,InternetAddressList * prepend)845 internet_address_list_prepend (InternetAddressList *list, InternetAddressList *prepend)
846 {
847 	InternetAddress *ia;
848 	char *dest, *src;
849 	guint len, i;
850 
851 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (prepend));
852 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
853 
854 	if (prepend->array->len == 0)
855 		return;
856 
857 	len = prepend->array->len;
858 	g_ptr_array_set_size (list->array, list->array->len + len);
859 
860 	src = ((char *) list->array->pdata);
861 	dest = src + (sizeof (void *) * len);
862 
863 	memmove (dest, src, (sizeof (void *) * list->array->len));
864 
865 	for (i = 0; i < prepend->array->len; i++) {
866 		ia = (InternetAddress *) prepend->array->pdata[i];
867 		g_mime_event_add (ia->changed, (GMimeEventCallback) address_changed, list);
868 		list->array->pdata[i] = ia;
869 		g_object_ref (ia);
870 	}
871 
872 	g_mime_event_emit (list->changed, NULL);
873 }
874 
875 
876 /**
877  * internet_address_list_append:
878  * @list: a #InternetAddressList
879  * @append: a #InternetAddressList
880  *
881  * Adds all of the addresses in @append to @list.
882  **/
883 void
internet_address_list_append(InternetAddressList * list,InternetAddressList * append)884 internet_address_list_append (InternetAddressList *list, InternetAddressList *append)
885 {
886 	InternetAddress *ia;
887 	guint len, i;
888 
889 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (append));
890 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
891 
892 	len = list->array->len;
893 	g_ptr_array_set_size (list->array, len + append->array->len);
894 
895 	for (i = 0; i < append->array->len; i++) {
896 		ia = (InternetAddress *) append->array->pdata[i];
897 		g_mime_event_add (ia->changed, (GMimeEventCallback) address_changed, list);
898 		list->array->pdata[len + i] = ia;
899 		g_object_ref (ia);
900 	}
901 
902 	g_mime_event_emit (list->changed, NULL);
903 }
904 
905 
906 /**
907  * internet_address_list_insert:
908  * @list: a #InternetAddressList
909  * @index: index to insert at
910  * @ia: a #InternetAddress
911  *
912  * Inserts an #InternetAddress into the #InternetAddressList at the
913  * specified index.
914  **/
915 void
internet_address_list_insert(InternetAddressList * list,int index,InternetAddress * ia)916 internet_address_list_insert (InternetAddressList *list, int index, InternetAddress *ia)
917 {
918 	char *dest, *src;
919 	size_t n;
920 
921 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
922 	g_return_if_fail (IS_INTERNET_ADDRESS (ia));
923 	g_return_if_fail (index >= 0);
924 
925 	g_mime_event_add (ia->changed, (GMimeEventCallback) address_changed, list);
926 	g_object_ref (ia);
927 
928 	if ((guint) index < list->array->len) {
929 		g_ptr_array_set_size (list->array, list->array->len + 1);
930 
931 		dest = ((char *) list->array->pdata) + (sizeof (void *) * (index + 1));
932 		src = ((char *) list->array->pdata) + (sizeof (void *) * index);
933 		n = list->array->len - index - 1;
934 
935 		memmove (dest, src, (sizeof (void *) * n));
936 		list->array->pdata[index] = ia;
937 	} else {
938 		/* the easy case */
939 		g_ptr_array_add (list->array, ia);
940 	}
941 
942 	g_mime_event_emit (list->changed, NULL);
943 }
944 
945 
946 /**
947  * internet_address_list_remove:
948  * @list: a #InternetAddressList
949  * @ia: a #InternetAddress
950  *
951  * Removes an #InternetAddress from the #InternetAddressList.
952  *
953  * Returns: %TRUE if the specified #InternetAddress was removed or
954  * %FALSE otherwise.
955  **/
956 gboolean
internet_address_list_remove(InternetAddressList * list,InternetAddress * ia)957 internet_address_list_remove (InternetAddressList *list, InternetAddress *ia)
958 {
959 	int index;
960 
961 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), FALSE);
962 	g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), FALSE);
963 
964 	if ((index = internet_address_list_index_of (list, ia)) == -1)
965 		return FALSE;
966 
967 	internet_address_list_remove_at (list, index);
968 
969 	return TRUE;
970 }
971 
972 
973 /**
974  * internet_address_list_remove_at:
975  * @list: a #InternetAddressList
976  * @index: index to remove
977  *
978  * Removes an #InternetAddress from the #InternetAddressList at the
979  * specified index.
980  *
981  * Returns: %TRUE if an #InternetAddress was removed or %FALSE
982  * otherwise.
983  **/
984 gboolean
internet_address_list_remove_at(InternetAddressList * list,int index)985 internet_address_list_remove_at (InternetAddressList *list, int index)
986 {
987 	InternetAddress *ia;
988 
989 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), FALSE);
990 	g_return_val_if_fail (index >= 0, FALSE);
991 
992 	if ((guint) index >= list->array->len)
993 		return FALSE;
994 
995 	ia = list->array->pdata[index];
996 	g_mime_event_remove (ia->changed, (GMimeEventCallback) address_changed, list);
997 	g_object_unref (ia);
998 
999 	g_ptr_array_remove_index (list->array, index);
1000 
1001 	g_mime_event_emit (list->changed, NULL);
1002 
1003 	return TRUE;
1004 }
1005 
1006 
1007 /**
1008  * internet_address_list_contains:
1009  * @list: a #InternetAddressList
1010  * @ia: a #InternetAddress
1011  *
1012  * Checks whether or not the specified #InternetAddress is contained
1013  * within the #InternetAddressList.
1014  *
1015  * Returns: %TRUE if the specified #InternetAddress is contained
1016  * within the specified #InternetAddressList or %FALSE otherwise.
1017  **/
1018 gboolean
internet_address_list_contains(InternetAddressList * list,InternetAddress * ia)1019 internet_address_list_contains (InternetAddressList *list, InternetAddress *ia)
1020 {
1021 	return internet_address_list_index_of (list, ia) != -1;
1022 }
1023 
1024 
1025 /**
1026  * internet_address_list_index_of:
1027  * @list: a #InternetAddressList
1028  * @ia: a #InternetAddress
1029  *
1030  * Gets the index of the specified #InternetAddress inside the
1031  * #InternetAddressList.
1032  *
1033  * Returns: the index of the requested #InternetAddress within the
1034  * #InternetAddressList or %-1 if it is not contained within the
1035  * #InternetAddressList.
1036  **/
1037 int
internet_address_list_index_of(InternetAddressList * list,InternetAddress * ia)1038 internet_address_list_index_of (InternetAddressList *list, InternetAddress *ia)
1039 {
1040 	guint i;
1041 
1042 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), -1);
1043 	g_return_val_if_fail (IS_INTERNET_ADDRESS (ia), -1);
1044 
1045 	for (i = 0; i < list->array->len; i++) {
1046 		if (list->array->pdata[i] == ia)
1047 			return i;
1048 	}
1049 
1050 	return -1;
1051 }
1052 
1053 
1054 /**
1055  * internet_address_list_get_address:
1056  * @list: a #InternetAddressList
1057  * @index: index of #InternetAddress to get
1058  *
1059  * Gets the #InternetAddress at the specified index.
1060  *
1061  * Returns: (transfer none): the #InternetAddress at the specified
1062  * index or %NULL if the index is out of range.
1063  **/
1064 InternetAddress *
internet_address_list_get_address(InternetAddressList * list,int index)1065 internet_address_list_get_address (InternetAddressList *list, int index)
1066 {
1067 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), NULL);
1068 	g_return_val_if_fail (index >= 0, NULL);
1069 
1070 	if ((guint) index >= list->array->len)
1071 		return NULL;
1072 
1073 	return list->array->pdata[index];
1074 }
1075 
1076 
1077 /**
1078  * internet_address_list_set_address:
1079  * @list: a #InternetAddressList
1080  * @index: index of #InternetAddress to set
1081  * @ia: a #InternetAddress
1082  *
1083  * Sets the #InternetAddress at the specified index to @ia.
1084  **/
1085 void
internet_address_list_set_address(InternetAddressList * list,int index,InternetAddress * ia)1086 internet_address_list_set_address (InternetAddressList *list, int index, InternetAddress *ia)
1087 {
1088 	InternetAddress *old;
1089 
1090 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
1091 	g_return_if_fail (IS_INTERNET_ADDRESS (ia));
1092 	g_return_if_fail (index >= 0);
1093 
1094 	if ((guint) index > list->array->len)
1095 		return;
1096 
1097 	if ((guint) index == list->array->len) {
1098 		internet_address_list_add (list, ia);
1099 		return;
1100 	}
1101 
1102 	if ((old = list->array->pdata[index]) == ia)
1103 		return;
1104 
1105 	g_mime_event_remove (old->changed, (GMimeEventCallback) address_changed, list);
1106 	g_object_unref (old);
1107 
1108 	g_mime_event_add (ia->changed, (GMimeEventCallback) address_changed, list);
1109 	list->array->pdata[index] = ia;
1110 	g_object_ref (ia);
1111 
1112 	g_mime_event_emit (list->changed, NULL);
1113 }
1114 
1115 
1116 static char *
encoded_name(GMimeFormatOptions * options,const char * raw,gboolean rfc2047_encode,const char * charset)1117 encoded_name (GMimeFormatOptions *options, const char *raw, gboolean rfc2047_encode, const char *charset)
1118 {
1119 	char *name;
1120 
1121 	g_return_val_if_fail (raw != NULL, NULL);
1122 
1123 	if (rfc2047_encode) {
1124 		name = g_mime_utils_header_encode_phrase (options, raw, charset);
1125 	} else {
1126 		name = g_mime_utils_quote_string (raw);
1127 	}
1128 
1129 	return name;
1130 }
1131 
1132 static void
linewrap(GString * str,const char * newline)1133 linewrap (GString *str, const char *newline)
1134 {
1135 	if (str->len > 0 && str->str[str->len - 1] == ' ') {
1136 		str->str[str->len - 1] = newline[0];
1137 
1138 		if (newline[1])
1139 			g_string_append_c (str, newline[1]);
1140 	} else {
1141 		g_string_append (str, newline);
1142 	}
1143 
1144 	g_string_append_c (str, '\t');
1145 }
1146 
1147 static void
append_folded_name(GString * str,size_t * linelen,const char * name,const char * newline)1148 append_folded_name (GString *str, size_t *linelen, const char *name, const char *newline)
1149 {
1150 	const char *word, *lwsp;
1151 	size_t len;
1152 
1153 	word = name;
1154 
1155 	while (*word) {
1156 		lwsp = word;
1157 
1158 		if (*word == '"') {
1159 			/* quoted string, don't break these up */
1160 			lwsp++;
1161 
1162 			while (*lwsp && *lwsp != '"') {
1163 				if (*lwsp == '\\')
1164 					lwsp++;
1165 
1166 				if (*lwsp)
1167 					lwsp++;
1168 			}
1169 
1170 			if (*lwsp == '"')
1171 				lwsp++;
1172 		} else {
1173 			/* normal word */
1174 			while (*lwsp && !is_lwsp (*lwsp))
1175 				lwsp++;
1176 		}
1177 
1178 		len = lwsp - word;
1179 		if (*linelen > 1 && (*linelen + len) > GMIME_FOLD_LEN) {
1180 			linewrap (str, newline);
1181 			*linelen = 1;
1182 		}
1183 
1184 		g_string_append_len (str, word, len);
1185 		*linelen += len;
1186 
1187 		word = lwsp;
1188 		while (*word && is_lwsp (*word))
1189 			word++;
1190 
1191 		if (*word && is_lwsp (*lwsp)) {
1192 			g_string_append_c (str, ' ');
1193 			(*linelen)++;
1194 		}
1195 	}
1196 }
1197 
1198 static void
mailbox_to_string(InternetAddress * ia,GMimeFormatOptions * options,guint32 flags,size_t * linelen,GString * str)1199 mailbox_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 flags, size_t *linelen, GString *str)
1200 {
1201 	InternetAddressMailbox *mailbox = (InternetAddressMailbox *) ia;
1202 	gboolean encode = flags & INTERNET_ADDRESS_ENCODE;
1203 	gboolean fold = flags & INTERNET_ADDRESS_FOLD;
1204 	const char *newline, *addr;
1205 	char *name;
1206 	size_t len;
1207 
1208 	newline = g_mime_format_options_get_newline (options);
1209 
1210 	addr = internet_address_mailbox_get_idn_addr (mailbox);
1211 
1212 	if (ia->name && *ia->name) {
1213 		name = encoded_name (options, ia->name, encode, ia->charset);
1214 		len = strlen (name);
1215 
1216 		if (fold && (*linelen + len) > GMIME_FOLD_LEN) {
1217 			if (len > GMIME_FOLD_LEN) {
1218 				/* we need to break up the name */
1219 				append_folded_name (str, linelen, name, newline);
1220 			} else {
1221 				/* the name itself is short enough to fit on a single
1222 				 * line, but only if we write it on a line by itself */
1223 				if (*linelen > 1) {
1224 					linewrap (str, newline);
1225 					*linelen = 1;
1226 				}
1227 
1228 				g_string_append_len (str, name, len);
1229 				*linelen += len;
1230 			}
1231 		} else {
1232 			/* we can safely fit the name on this line */
1233 			g_string_append_len (str, name, len);
1234 			*linelen += len;
1235 		}
1236 
1237 		g_free (name);
1238 
1239 		len = strlen (addr);
1240 
1241 		if (fold && (*linelen + len + 3) >= GMIME_FOLD_LEN) {
1242 			g_string_append (str, newline);
1243 			g_string_append_len (str, "\t<", 2);
1244 			*linelen = 2;
1245 		} else {
1246 			g_string_append_len (str, " <", 2);
1247 			*linelen += 2;
1248 		}
1249 
1250 		g_string_append_len (str, addr, len);
1251 		g_string_append_c (str, '>');
1252 		*linelen += len + 1;
1253 	} else {
1254 		len = strlen (addr);
1255 
1256 		if (fold && (*linelen + len) > GMIME_FOLD_LEN) {
1257 			linewrap (str, newline);
1258 			*linelen = 1;
1259 		}
1260 
1261 		g_string_append_len (str, addr, len);
1262 		*linelen += len;
1263 	}
1264 }
1265 
1266 static void
_internet_address_list_to_string(const InternetAddressList * list,GMimeFormatOptions * options,guint32 flags,size_t * linelen,GString * str)1267 _internet_address_list_to_string (const InternetAddressList *list, GMimeFormatOptions *options, guint32 flags,
1268 				  size_t *linelen, GString *str)
1269 {
1270 	InternetAddress *ia;
1271 	guint i;
1272 
1273 	for (i = 0; i < list->array->len; i++) {
1274 		ia = (InternetAddress *) list->array->pdata[i];
1275 
1276 		INTERNET_ADDRESS_GET_CLASS (ia)->to_string (ia, options, flags, linelen, str);
1277 
1278 		if (i + 1 < list->array->len) {
1279 			g_string_append (str, ", ");
1280 			*linelen += 2;
1281 		}
1282 	}
1283 }
1284 
1285 static void
group_to_string(InternetAddress * ia,GMimeFormatOptions * options,guint32 flags,size_t * linelen,GString * str)1286 group_to_string (InternetAddress *ia, GMimeFormatOptions *options, guint32 flags, size_t *linelen, GString *str)
1287 {
1288 	InternetAddressGroup *group = (InternetAddressGroup *) ia;
1289 	gboolean encode = flags & INTERNET_ADDRESS_ENCODE;
1290 	gboolean fold = flags & INTERNET_ADDRESS_FOLD;
1291 	const char *newline;
1292 	char *name = NULL;
1293 	size_t len = 0;
1294 
1295 	newline = g_mime_format_options_get_newline (options);
1296 
1297 	if (ia->name != NULL) {
1298 		name = encoded_name (options, ia->name, encode, ia->charset);
1299 		len = strlen (name);
1300 
1301 		if (fold && *linelen > 1 && (*linelen + len + 1) > GMIME_FOLD_LEN) {
1302 			linewrap (str, newline);
1303 			*linelen = 1;
1304 		}
1305 
1306 		g_string_append_len (str, name, len);
1307 	}
1308 
1309 	g_string_append_len (str, ": ", 2);
1310 	*linelen += len + 2;
1311 	g_free (name);
1312 
1313 	_internet_address_list_to_string (group->members, options, flags, linelen, str);
1314 	g_string_append_c (str, ';');
1315 	*linelen += 1;
1316 }
1317 
1318 
1319 /**
1320  * internet_address_list_to_string:
1321  * @list: list of internet addresses
1322  * @options: (nullable): a #GMimeFormatOptions or %NULL
1323  * @encode: %TRUE if the address should be rfc2047 encoded
1324  *
1325  * Allocates a string buffer containing the rfc822 formatted addresses
1326  * in @list.
1327  *
1328  * Returns: (nullable): a string containing the list of addresses in rfc822
1329  * format or %NULL if no addresses are contained in the list.
1330  **/
1331 char *
internet_address_list_to_string(InternetAddressList * list,GMimeFormatOptions * options,gboolean encode)1332 internet_address_list_to_string (InternetAddressList *list, GMimeFormatOptions *options, gboolean encode)
1333 {
1334 	guint32 flags = encode ? INTERNET_ADDRESS_ENCODE : 0;
1335 	size_t linelen = 0;
1336 	GString *str;
1337 
1338 	g_return_val_if_fail (IS_INTERNET_ADDRESS_LIST (list), NULL);
1339 
1340 	if (list->array->len == 0)
1341 		return NULL;
1342 
1343 	str = g_string_new ("");
1344 	_internet_address_list_to_string (list, options, flags, &linelen, str);
1345 
1346 	return g_string_free (str, FALSE);
1347 }
1348 
1349 
1350 /**
1351  * internet_address_list_encode:
1352  * @list: list of internet addresses
1353  * @options: (nullable): a #GMimeFormatOptions or %NULL
1354  * @str: string to write to
1355  *
1356  * Writes the rfc2047-encoded rfc822 formatted addresses in @list to
1357  * @str, folding appropriately.
1358  **/
1359 void
internet_address_list_encode(InternetAddressList * list,GMimeFormatOptions * options,GString * str)1360 internet_address_list_encode (InternetAddressList *list, GMimeFormatOptions *options, GString *str)
1361 {
1362 	guint32 flags = INTERNET_ADDRESS_ENCODE | INTERNET_ADDRESS_FOLD;
1363 	const char *newline;
1364 	size_t linelen;
1365 
1366 	g_return_if_fail (IS_INTERNET_ADDRESS_LIST (list));
1367 	g_return_if_fail (str != NULL);
1368 
1369 	newline = g_mime_format_options_get_newline (options);
1370 	linelen = str->len;
1371 
1372 	_internet_address_list_to_string (list, options, flags, &linelen, str);
1373 
1374 	g_string_append (str, newline);
1375 }
1376 
1377 
1378 static char *
decode_name(GMimeParserOptions * options,const char * name,size_t len,const char ** charset,gint64 offset)1379 decode_name (GMimeParserOptions *options, const char *name, size_t len, const char **charset, gint64 offset)
1380 {
1381 	char *value, *buf = NULL;
1382 
1383 	if (!g_utf8_validate (name, len, NULL)) {
1384 		/* A (broken) mailer has sent us raw 8bit/multibyte text data... */
1385 		buf = g_mime_utils_decode_8bit (options, name, len);
1386 	} else {
1387 		buf = g_strndup (name, len);
1388 	}
1389 
1390 	/* decode the phrase */
1391 	g_mime_utils_unquote_string (buf);
1392 	value = _g_mime_utils_header_decode_phrase (options, buf, charset, offset);
1393 	g_strstrip (value);
1394 	g_free (buf);
1395 
1396 	return value;
1397 }
1398 
1399 
1400 typedef enum {
1401 	ALLOW_MAILBOX = 1 << 0,
1402 	ALLOW_GROUP   = 1 << 1,
1403 	ALLOW_ANY     = ALLOW_MAILBOX | ALLOW_GROUP
1404 } AddressParserFlags;
1405 
1406 static gboolean
decode_route(const char ** in)1407 decode_route (const char **in)
1408 {
1409 	const char *inptr = *in;
1410 	GString *route;
1411 
1412 	route = g_string_new ("");
1413 
1414 	do {
1415 		inptr++;
1416 
1417 		g_string_append_c (route, '@');
1418 		if (!decode_domain (&inptr, route)) {
1419 			g_string_free (route, TRUE);
1420 			goto error;
1421 		}
1422 
1423 		skip_cfws (&inptr);
1424 		if (*inptr == ',') {
1425 			g_string_append_c (route, ',');
1426 			inptr++;
1427 			skip_cfws (&inptr);
1428 
1429 			/* obs-domain-lists allow commas with nothing between them... */
1430 			while (*inptr == ',') {
1431 				inptr++;
1432 				skip_cfws (&inptr);
1433 			}
1434 		}
1435 	} while (*inptr == '@');
1436 
1437 	g_string_free (route, TRUE);
1438 	skip_cfws (&inptr);
1439 
1440 	if (*inptr != ':') {
1441 		w(g_warning ("Invalid route domain-list, missing ':': %.*s", inptr - start, start));
1442 		goto error;
1443 	}
1444 
1445 	*in = inptr;
1446 
1447 	return TRUE;
1448 
1449  error:
1450 
1451 	while (*inptr && *inptr != ':' && *inptr != '>')
1452 		inptr++;
1453 
1454 	*in = inptr;
1455 
1456 	return FALSE;
1457 }
1458 
1459 static gboolean
localpart_parse(GString * localpart,const char ** in)1460 localpart_parse (GString *localpart, const char **in)
1461 {
1462 	const char *inptr = *in;
1463 	const char *word;
1464 
1465 	do {
1466 		word = inptr;
1467 
1468 		if (!skip_word (&inptr))
1469 			goto error;
1470 
1471 		if (!g_utf8_validate (word, (size_t) (inptr - word), NULL))
1472 			goto error;
1473 
1474 		g_string_append_len (localpart, word, (size_t) (inptr - word));
1475 
1476 		if (!skip_cfws (&inptr))
1477 			goto error;
1478 
1479 		if (*inptr != '.')
1480 			break;
1481 
1482 		g_string_append_c (localpart, *inptr++);
1483 
1484 		if (!skip_cfws (&inptr))
1485 			goto error;
1486 
1487 		if (*inptr == '\0')
1488 			goto error;
1489 	} while (TRUE);
1490 
1491 	*in = inptr;
1492 
1493 	return TRUE;
1494 
1495  error:
1496 	*in = inptr;
1497 
1498 	return FALSE;
1499 }
1500 
1501 #define COMMA_GREATER_THAN_OR_SEMICOLON ",>;"
1502 
1503 static gboolean
dotatom_parse(GString * str,const char ** in,const char * sentinels)1504 dotatom_parse (GString *str, const char **in, const char *sentinels)
1505 {
1506 	const char *atom, *comment;
1507 	const char *inptr = *in;
1508 	GString *domain = str;
1509 
1510 	do {
1511 		if (!is_atom (*inptr))
1512 			goto error;
1513 
1514 		atom = inptr;
1515 		while (is_atom (*inptr))
1516 			inptr++;
1517 
1518 		if (!g_utf8_validate (atom, (size_t) (inptr - atom), NULL))
1519 			goto error;
1520 
1521 #if LIBIDN
1522 		if (domain == str && !strncmp (atom, "xn--", 4)) {
1523 			/* from here on out, we'll use a temp domain buffer so that
1524 			 * we can decode it once we're done parsing it */
1525 			domain = g_string_new ("");
1526 		}
1527 #endif
1528 
1529 		g_string_append_len (domain, atom, (size_t) (inptr - atom));
1530 
1531 		comment = inptr;
1532 		if (!skip_cfws (&inptr))
1533 			goto error;
1534 
1535 		if (*inptr != '.') {
1536 			inptr = comment;
1537 			break;
1538 		}
1539 
1540 		/* skip over the '.' */
1541 		inptr++;
1542 
1543 		if (!skip_cfws (&inptr))
1544 			goto error;
1545 
1546 		/* allow domains to end with a '.', but strip it off */
1547 		if (*inptr == '\0' || strchr (sentinels, *inptr))
1548 			break;
1549 
1550 		g_string_append_c (domain, '.');
1551 	} while (TRUE);
1552 
1553 #ifdef LIBIDN
1554 	if (domain != str) {
1555 		char *unicode;
1556 
1557 		if (idn2_to_unicode_8z8z (domain->str, &unicode, 0) == IDN2_OK) {
1558 			g_string_append (str, unicode);
1559 			free (unicode);
1560 		} else {
1561 			g_string_append_len (str, domain->str, domain->len);
1562 		}
1563 
1564 		g_string_free (domain, TRUE);
1565 	}
1566 #endif
1567 
1568 	*in = inptr;
1569 
1570 	return TRUE;
1571 
1572  error:
1573 #ifdef LIBIDN
1574 	if (domain != str) {
1575 		g_string_append_len (str, domain->str, domain->len);
1576 		g_string_free (domain, TRUE);
1577 	}
1578 #endif
1579 
1580 	*in = inptr;
1581 
1582 	return FALSE;
1583 }
1584 
1585 static gboolean
domain_literal_parse(GString * str,const char ** in)1586 domain_literal_parse (GString *str, const char **in)
1587 {
1588 	const char *inptr = *in;
1589 
1590 	g_string_append_c (str, '[');
1591 	inptr++;
1592 
1593 	skip_lwsp (&inptr);
1594 
1595 	do {
1596 		while (*inptr && is_dtext (*inptr))
1597 			g_string_append_c (str, *inptr++);
1598 
1599 		skip_lwsp (&inptr);
1600 
1601 		if (*inptr == '\0')
1602 			goto error;
1603 
1604 		if (*inptr == ']')
1605 			break;
1606 
1607 		if (!is_dtext (*inptr))
1608 			goto error;
1609 	} while (TRUE);
1610 
1611 	g_string_append_c (str, ']');
1612 	*in = inptr + 1;
1613 
1614 	return TRUE;
1615 
1616  error:
1617 	*in = inptr;
1618 
1619 	return FALSE;
1620 }
1621 
1622 static gboolean
domain_parse(GString * str,const char ** in,const char * sentinels)1623 domain_parse (GString *str, const char **in, const char *sentinels)
1624 {
1625 	if (**in == '[')
1626 		return domain_literal_parse (str, in);
1627 
1628 	return dotatom_parse (str, in, sentinels);
1629 }
1630 
1631 static gboolean
addrspec_parse(const char ** in,const char * sentinels,char ** addrspec,int * at)1632 addrspec_parse (const char **in, const char *sentinels, char **addrspec, int *at)
1633 {
1634 	const char *inptr = *in;
1635 	GString *str;
1636 
1637 	str = g_string_new ("");
1638 
1639 	if (!localpart_parse (str, &inptr))
1640 		goto error;
1641 
1642 	if (*inptr == '\0' || strchr (sentinels, *inptr)) {
1643 		*addrspec = g_string_free (str, FALSE);
1644 		*in = inptr;
1645 		*at = -1;
1646 		return TRUE;
1647 	}
1648 
1649 	if (*inptr != '@')
1650 		goto error;
1651 
1652 	*at = str->len;
1653 	g_string_append_c (str, *inptr++);
1654 
1655 	if (*inptr == '\0')
1656 		goto error;
1657 
1658 	if (!skip_cfws (&inptr))
1659 		goto error;
1660 
1661 	if (*inptr == '\0')
1662 		goto error;
1663 
1664 	if (!domain_parse (str, &inptr, sentinels))
1665 		goto error;
1666 
1667 	*addrspec = g_string_free (str, FALSE);
1668 	*in = inptr;
1669 
1670 	return TRUE;
1671 
1672  error:
1673 	g_string_free (str, TRUE);
1674 	*addrspec = NULL;
1675 	*in = inptr;
1676 	*at = -1;
1677 
1678 	return FALSE;
1679 }
1680 
1681 // TODO: rename to angleaddr_parse??
1682 static gboolean
mailbox_parse(GMimeParserOptions * options,const char ** in,const char * name,InternetAddress ** address)1683 mailbox_parse (GMimeParserOptions *options, const char **in, const char *name, InternetAddress **address)
1684 {
1685 	GMimeRfcComplianceMode mode = g_mime_parser_options_get_address_compliance_mode (options);
1686 	const char *inptr = *in;
1687 	char *addrspec = NULL;
1688 	int at;
1689 
1690 	/* skip over the '<' */
1691 	inptr++;
1692 
1693 	/* Note: check for excessive angle brackets like the example described in section 7.1.2 of rfc7103... */
1694 	if (*inptr == '<') {
1695 		if (mode != GMIME_RFC_COMPLIANCE_LOOSE)
1696 			goto error;
1697 
1698 		do {
1699 			inptr++;
1700 		} while (*inptr == '<');
1701 	}
1702 
1703 	if (*inptr == '\0')
1704 		goto error;
1705 
1706 	if (!skip_cfws (&inptr))
1707 		goto error;
1708 
1709 	if (*inptr == '@') {
1710 		if (!decode_route (&inptr))
1711 			goto error;
1712 
1713 		if (*inptr != ':')
1714 			goto error;
1715 
1716 		inptr++;
1717 
1718 		if (!skip_cfws (&inptr))
1719 			goto error;
1720 	}
1721 
1722 	// Note: The only syntactically correct sentinel token here is the '>', but alas... to deal with the first
1723 	// example in section 7.1.5 of rfc7103, we need to at least handle ',' as a sentinel and might as well handle
1724 	// ';' as well in case the mailbox is within a group address.
1725 	//
1726 	// Example: <third@example.net, fourth@example.net>
1727 	if (!addrspec_parse (&inptr, COMMA_GREATER_THAN_OR_SEMICOLON, &addrspec, &at))
1728 		goto error;
1729 
1730 	if (!skip_cfws (&inptr))
1731 		goto error;
1732 
1733 	if (*inptr != '>') {
1734 		if (mode != GMIME_RFC_COMPLIANCE_LOOSE)
1735 			goto error;
1736 	} else {
1737 		/* skip over the '>' */
1738 		inptr++;
1739 
1740 		/* Note: check for excessive angle brackets like the example described in section 7.1.2 of rfc7103... */
1741 		if (*inptr == '>') {
1742 			if (mode != GMIME_RFC_COMPLIANCE_LOOSE)
1743 				goto error;
1744 
1745 			do {
1746 				inptr++;
1747 			} while (*inptr == '>');
1748 		}
1749 	}
1750 
1751 	*address = _internet_address_mailbox_new (name, addrspec, at);
1752 	g_free (addrspec);
1753 	*in = inptr;
1754 
1755 	return TRUE;
1756 
1757  error:
1758 	g_free (addrspec);
1759 	*address = NULL;
1760 	*in = inptr;
1761 
1762 	return FALSE;
1763 }
1764 
1765 static gboolean address_list_parse (InternetAddressList *list, GMimeParserOptions *options, const char **in, gboolean is_group, gint64 offset);
1766 
1767 static gboolean
group_parse(InternetAddressGroup * group,GMimeParserOptions * options,const char ** in,gint64 offset)1768 group_parse (InternetAddressGroup *group, GMimeParserOptions *options, const char **in, gint64 offset)
1769 {
1770 	const char *inptr = *in;
1771 
1772 	/* skip over the ':' */
1773 	inptr++;
1774 
1775 	/* ignore superfluous colons (and whitespace) */
1776 	while (*inptr == ':' || is_lwsp (*inptr))
1777 		inptr++;
1778 
1779 	if (*inptr != '\0') {
1780 		address_list_parse (group->members, options, &inptr, TRUE, offset);
1781 
1782 		if (*inptr != ';') {
1783 			while (*inptr && *inptr != ';')
1784 				inptr++;
1785 		} else {
1786 			inptr++;
1787 		}
1788 	}
1789 
1790 	*in = inptr;
1791 
1792 	return TRUE;
1793 }
1794 
1795 static gboolean
address_parse(GMimeParserOptions * options,AddressParserFlags flags,const char ** in,const char ** charset,InternetAddress ** address,gint64 offset)1796 address_parse (GMimeParserOptions *options, AddressParserFlags flags, const char **in, const char **charset, InternetAddress **address, gint64 offset)
1797 {
1798 	GMimeRfcComplianceMode mode = g_mime_parser_options_get_address_compliance_mode (options);
1799 	int min_words = g_mime_parser_options_get_allow_addresses_without_domain (options) ? 1 : 0;
1800 	gboolean trim_leading_quote = FALSE;
1801 	const char *inptr = *in;
1802 	const char *start;
1803 	size_t length;
1804 	int words = 0;
1805 
1806 	if (!skip_cfws (&inptr) || *inptr == '\0')
1807 		goto error;
1808 
1809 	/* keep track of the start & length of the phrase */
1810 	start = inptr;
1811 	length = 0;
1812 
1813 	while (*inptr) {
1814 		if (mode != GMIME_RFC_COMPLIANCE_LOOSE) {
1815 			if (!skip_word (&inptr))
1816 				break;
1817 		} else if (*inptr == '"') {
1818 			const char *qstring = inptr;
1819 
1820 			if (!skip_quoted (&inptr)) {
1821 				inptr = qstring + 1;
1822 
1823 				skip_lwsp (&inptr);
1824 
1825 				if (!skip_atom (&inptr))
1826 					break;
1827 
1828 				if (start == qstring)
1829 					trim_leading_quote = TRUE;
1830 			}
1831 		} else {
1832 			if (!skip_atom (&inptr))
1833 				break;
1834 		}
1835 
1836 		length = (size_t) (inptr - start);
1837 
1838 		do {
1839 			if (!skip_cfws (&inptr))
1840 				goto error;
1841 
1842 			/* Note: some clients don't quote dots in the name */
1843 			if (*inptr != '.')
1844 				break;
1845 
1846 			inptr++;
1847 
1848 			length = (size_t) (inptr - start);
1849 		} while (TRUE);
1850 
1851 		words++;
1852 
1853 		/* Note: some clients don't quote commas in the name */
1854 		if (*inptr == ',' && words > min_words) {
1855 			inptr++;
1856 
1857 			length = (size_t) (inptr - start);
1858 
1859 			if (!skip_cfws (&inptr))
1860 				goto error;
1861 		}
1862 	}
1863 
1864 	if (!skip_cfws (&inptr))
1865 		goto error;
1866 
1867 	// specials    =  "(" / ")" / "<" / ">" / "@"  ; Must be in quoted-
1868 	//             /  "," / ";" / ":" / "\" / <">  ;  string, to use
1869 	//             /  "." / "[" / "]"              ;  within a word.
1870 
1871 	if (*inptr == '\0' || *inptr == ',' || *inptr == '>' || *inptr == ';') {
1872 		/* we've completely gobbled up an addr-spec w/o a domain */
1873 		char sentinel = *inptr != '\0' ? *inptr : ',';
1874 		char sentinels[2] = { sentinel, 0 };
1875 		char *name, *addrspec;
1876 		int at;
1877 
1878 		/* rewind back to the beginning of the local-part */
1879 		inptr = start;
1880 
1881 		if (!(flags & ALLOW_MAILBOX))
1882 			goto error;
1883 
1884 		if (!addrspec_parse (&inptr, sentinels, &addrspec, &at))
1885 			goto error;
1886 
1887 		skip_lwsp (&inptr);
1888 
1889 		if (*inptr == '(') {
1890 			const char *comment = inptr;
1891 
1892 			if (!skip_comment (&inptr))
1893 				goto error;
1894 
1895 			comment++;
1896 
1897 			name = decode_name (options, comment, (size_t) ((inptr - 1) - comment), charset, offset);
1898 		} else {
1899 			name = g_strdup ("");
1900 		}
1901 
1902 		if (*inptr == '>') {
1903 			if (mode != GMIME_RFC_COMPLIANCE_LOOSE)
1904 				goto error;
1905 
1906 			inptr++;
1907 		}
1908 
1909 		*address = _internet_address_mailbox_new (name, addrspec, at);
1910 		g_free (addrspec);
1911 		g_free (name);
1912 		*in = inptr;
1913 
1914 		return TRUE;
1915 	}
1916 
1917 	if (*inptr == ':') {
1918 		/* rfc2822 group address */
1919 		InternetAddressGroup *group;
1920 		const char *phrase = start;
1921 		gboolean retval;
1922 		char *name;
1923 
1924 		if (!(flags & ALLOW_GROUP))
1925 			goto error;
1926 
1927 		if (trim_leading_quote) {
1928 			phrase++;
1929 			length--;
1930 		}
1931 
1932 		if (length > 0) {
1933 			name = decode_name (options, phrase, length, charset, offset);
1934 		} else {
1935 			name = g_strdup ("");
1936 		}
1937 
1938 		group = (InternetAddressGroup *) internet_address_group_new (name);
1939 		*address = (InternetAddress *) group;
1940 		g_free (name);
1941 
1942 		retval = group_parse (group, options, &inptr, offset);
1943 		*in = inptr;
1944 
1945 		return retval;
1946 	}
1947 
1948 	if (!(flags & ALLOW_MAILBOX))
1949 		goto error;
1950 
1951 	if (*inptr == '@') {
1952 		/* we're either in the middle of an addr-spec token or we completely gobbled up
1953 		 * an addr-spec w/o a domain */
1954 		char *name, *addrspec;
1955 		int at;
1956 
1957 		/* rewind back to the beginning of the local-part */
1958 		inptr = start;
1959 
1960 		if (!addrspec_parse (&inptr, COMMA_GREATER_THAN_OR_SEMICOLON, &addrspec, &at))
1961 			goto error;
1962 
1963 		skip_lwsp (&inptr);
1964 
1965 		if (*inptr == '(') {
1966 			const char *comment = inptr;
1967 
1968 			if (!skip_comment (&inptr))
1969 				goto error;
1970 
1971 			comment++;
1972 
1973 			name = decode_name (options, comment, (size_t) ((inptr - 1) - comment), charset, offset);
1974 		} else {
1975 			name = g_strdup ("");
1976 		}
1977 
1978 		if (!skip_cfws (&inptr)) {
1979 			g_free (addrspec);
1980 			g_free (name);
1981 			goto error;
1982 		}
1983 
1984 		if (*inptr == '\0') {
1985 			*address = _internet_address_mailbox_new (name, addrspec, at);
1986 			g_free (addrspec);
1987 			g_free (name);
1988 			*in = inptr;
1989 
1990 			return TRUE;
1991 		}
1992 
1993 		if (*inptr == '<') {
1994 			/* We have an address like "user@example.com <user@example.com>"; i.e. the name
1995 			 * is an unquoted string with an '@'. */
1996 			const char *end;
1997 
1998 			if (mode != GMIME_RFC_COMPLIANCE_LOOSE)
1999 				goto error;
2000 
2001 			end = inptr;
2002 			while (end > start && is_lwsp (*(end - 1)))
2003 				end--;
2004 
2005 			length = (size_t) (end - start);
2006 			g_free (addrspec);
2007 			g_free (name);
2008 
2009 			/* fall through to the rfc822 angle-addr token case... */
2010 		} else {
2011 			/* Note: since there was no '<', there should not be a '>'... but we handle it
2012 			 * anyway in order to deal with the second Unbalanced Angle Brackets example in
2013 			 * section 7.1.3: second@example.org> */
2014 			if (*inptr == '>') {
2015 				if (mode != GMIME_RFC_COMPLIANCE_LOOSE)
2016 					goto error;
2017 
2018 				inptr++;
2019 			}
2020 
2021 			*address = _internet_address_mailbox_new (name, addrspec, at);
2022 			g_free (addrspec);
2023 			g_free (name);
2024 			*in = inptr;
2025 
2026 			return TRUE;
2027 		}
2028 	}
2029 
2030 	if (*inptr == '<') {
2031 		/* rfc2822 angle-addr token */
2032 		const char *phrase = start;
2033 		gboolean retval;
2034 		char *name;
2035 
2036 		if (trim_leading_quote) {
2037 			phrase++;
2038 			length--;
2039 		}
2040 
2041 		if (length > 0) {
2042 			name = decode_name (options, phrase, length, charset, offset);
2043 		} else {
2044 			name = g_strdup ("");
2045 		}
2046 
2047 		retval = mailbox_parse (options, &inptr, name, address);
2048 		g_free (name);
2049 		*in = inptr;
2050 
2051 		return retval;
2052 	}
2053 
2054  error:
2055 	if (g_mime_parser_options_get_warning_callback (options) != NULL)
2056 		_g_mime_parser_options_warn (options, offset, GMIME_WARN_INVALID_ADDRESS_LIST, *in);
2057 
2058 	*address = NULL;
2059 	*in = inptr;
2060 
2061 	return FALSE;
2062 }
2063 
2064 static gboolean
address_list_parse(InternetAddressList * list,GMimeParserOptions * options,const char ** in,gboolean is_group,gint64 offset)2065 address_list_parse (InternetAddressList *list, GMimeParserOptions *options, const char **in, gboolean is_group, gint64 offset)
2066 {
2067 	gboolean can_warn = g_mime_parser_options_get_warning_callback (options) != NULL;
2068 	InternetAddress *address;
2069 	const char *charset;
2070 	const char *inptr;
2071 
2072 	if (!skip_cfws (in))
2073 		return FALSE;
2074 
2075 	inptr = *in;
2076 
2077 	if (*inptr == '\0')
2078 		return FALSE;
2079 
2080 	while (*inptr) {
2081 		gboolean separator_between_addrs = FALSE;
2082 
2083 		if (is_group && *inptr ==  ';')
2084 			break;
2085 
2086 		charset = NULL;
2087 
2088 		if (!address_parse (options, ALLOW_ANY, &inptr, &charset, &address, offset)) {
2089 			/* skip this address... */
2090 			while (*inptr && *inptr != ',' && (!is_group || *inptr != ';'))
2091 				inptr++;
2092 		} else {
2093 			_internet_address_list_add (list, address);
2094 
2095 			if (charset)
2096 				address->charset = g_strdup (charset);
2097 
2098 			if (INTERNET_ADDRESS_IS_GROUP(address))
2099 				separator_between_addrs = TRUE;
2100 		}
2101 
2102 		/* Note: we loop here in case there are any null addresses between commas */
2103 		do {
2104 			if (!skip_cfws (&inptr)) {
2105 				*in = inptr;
2106 
2107 				return FALSE;
2108 			}
2109 
2110 			if (*inptr != ',')
2111 				break;
2112 
2113 			separator_between_addrs = TRUE;
2114 			inptr++;
2115 		} while (TRUE);
2116 
2117 		if (can_warn && !(separator_between_addrs || (*inptr == '\0') || (is_group && *inptr == ';')))
2118 			_g_mime_parser_options_warn (options, offset, GMIME_WARN_INVALID_ADDRESS_LIST, *in);
2119 	}
2120 
2121 	*in = inptr;
2122 
2123 	return TRUE;
2124 }
2125 
2126 
2127 /**
2128  * internet_address_list_parse:
2129  * @options: (nullable): a #GMimeParserOptions or %NULL
2130  * @str: a string containing internet addresses
2131  *
2132  * Construct a list of internet addresses from the given string.
2133  *
2134  * Returns: (nullable) (transfer full): a #InternetAddressList or %NULL if the
2135  * input string does not contain any addresses.
2136  **/
2137 InternetAddressList *
internet_address_list_parse(GMimeParserOptions * options,const char * str)2138 internet_address_list_parse (GMimeParserOptions *options, const char *str)
2139 {
2140 	return _internet_address_list_parse (options, str, -1);
2141 }
2142 
2143 InternetAddressList *
_internet_address_list_parse(GMimeParserOptions * options,const char * str,gint64 offset)2144 _internet_address_list_parse (GMimeParserOptions *options, const char *str, gint64 offset)
2145 {
2146 	InternetAddressList *list;
2147 	const char *inptr = str;
2148 
2149 	g_return_val_if_fail (str != NULL, NULL);
2150 
2151 	list = internet_address_list_new ();
2152 	if (!address_list_parse (list, options, &inptr, FALSE, offset) || list->array->len == 0) {
2153 		g_object_unref (list);
2154 		return NULL;
2155 	}
2156 
2157 	return list;
2158 }
2159