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