Commit 5bed8190 5bed8190e1dacb3be3516bb41d10cbc3d1a357cd by Alain Magloire

From Sam.

	* examples/{Makefile,Addrs,addr.c,Addrs.good}: address test f/w.
	* include/mailutils/address.h,mailbox/{address.c,parse822.c}: now
	  stuff a group name into an _address, and added a function to do
	  a quick check if it is a group.
	* mailbox/parse822.c: fixed bug where ",sam@foo.bar" wasn't valid.
1 parent fadb4826
1 2001-04-14 Sam Roberts
2 * examples/{Makefile,Addrs,addr.c,Addrs.good}: address test f/w.
3 * include/mailutils/address.h,mailbox/{address.c,parse822.c}: now
4 stuff a group name into an _address, and added a function to do
5 a quick check if it is a group.
6 * mailbox/parse822.c: fixed bug where ",sam@foo.bar" wasn't valid.
7
1 2001-04-14 Alain Magloire 8 2001-04-14 Alain Magloire
2 9
3 * mailbox/folder_imap.c: When calling imap_writeline () the 10 * mailbox/folder_imap.c: When calling imap_writeline () the
......
...@@ -11,14 +11,13 @@ RFC 822 for the details. "[]" pairs mean "optional", "/" means "one or ...@@ -11,14 +11,13 @@ RFC 822 for the details. "[]" pairs mean "optional", "/" means "one or
11 the other", and double-quoted characters are literals. 11 the other", and double-quoted characters are literals.
12 12
13 @example 13 @example
14 address-list = address ["," address-list]
15 address = mailbox / group
16 mailbox = addr-spec ["(" personal ")"] /
17 [personal] "<" [route] addr-spec ">"
18 addr-spec = local-part "@" domain 14 addr-spec = local-part "@" domain
19 group = phrase ":" mailbox-list ";" 15 mailbox = addr-spec ["(" display-name ")"] /
20 16 [display-name] "<" [route] addr-spec ">"
21 mailbox-list = mailbox ["," mailbox-list] 17 mailbox-list = mailbox ["," mailbox-list]
18 group = display-name ":" [mailbox-list] ";"
19 address = mailbox / group
20 address-list = address ["," address-list]
22 @end example 21 @end example
23 22
24 Several address functions have a set of common arguments with consistent 23 Several address functions have a set of common arguments with consistent
...@@ -98,12 +97,18 @@ The return value is @code{0} on success and a code number on error conditions: ...@@ -98,12 +97,18 @@ The return value is @code{0} on success and a code number on error conditions:
98 97
99 @deftypefun int address_get_personal (address_t *@var{addr}, size_t @var{no}, char* @var{buf}, size_t @var{len}, size_t* @var{n}) 98 @deftypefun int address_get_personal (address_t *@var{addr}, size_t @var{no}, char* @var{buf}, size_t @var{len}, size_t* @var{n})
100 99
101 Acesses the personal phrase describing the @var{no}th email address. This 100 Acesses the display-name describing the @var{no}th email address. This
102 personal is optional, so may not be present. If it is not present, but 101 display-name is optional, so may not be present. If it is not present, but
103 there is an RFC822 comment after the address, that comment will be 102 there is an RFC822 comment after the address, that comment will be
104 returned as the personal phrase, as this is a common usage of the comment 103 returned as the personal phrase, as this is a common usage of the comment
105 even though it is not defined in the internet mail standard. 104 even though it is not defined in the internet mail standard.
106 105
106 A group is a kind of a special case. It has a display-name, followed
107 by an optional mailbox-list. The display-name will be allocated an address
108 all it's own, but all the other elements (local-part, domain, etc.) will
109 be zero-length. So "a group: ;" is valid, will have a count of 1, but
110 address_get_email(), and all the rest, will return zero-length output.
111
107 The return value is @code{0} on success and a code number on error conditions: 112 The return value is @code{0} on success and a code number on error conditions:
108 @table @code 113 @table @code
109 @ADDRESSEINVAL 114 @ADDRESSEINVAL
...@@ -175,6 +180,24 @@ The return value is @code{0} on success and a code number on error conditions: ...@@ -175,6 +180,24 @@ The return value is @code{0} on success and a code number on error conditions:
175 @end table 180 @end table
176 @end deftypefun 181 @end deftypefun
177 182
183 @deftypefun int address_is_group (address_t *@var{addr}, size_t @var{no}, size_t @var{len}, int* @var{yes})
184
185 Sets *@var{yes} to @code{1} if this address is just the name of a group,
186 @code{0} otherwise. This is faster than checking if the address has
187 a non-zero length personal, and a zero-length local_part and domain.
188
189 @var{yes} can be @code{nul}, though that doesn't serve much purpose other
190 than determining that @var{no} refers to an address.
191
192 Currently, there is no way to determine the end of the group.
193
194 The return value is @code{0} on success and a code number on error conditions:
195 @table @code
196 @ADDRESSEINVAL
197 @ADDRESSENOENT
198 @end table
199 @end deftypefun
200
178 @deftypefun int address_to_string (address_t *@var{addr}, char* @var{buf}, size_t @var{len}, size_t* @var{n}) 201 @deftypefun int address_to_string (address_t *@var{addr}, char* @var{buf}, size_t @var{len}, size_t* @var{n})
179 202
180 Returns the entire address list as a single RFC822 formatted address 203 Returns the entire address list as a single RFC822 formatted address
......
1 Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:sroberts@[10].[1]> 1 Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:sroberts@[10].[1]> ;
2 a@b,z@y
3 ,a@b,z@y
4 a@b,z@y,
5 ,a@b,z@y,
6 a@b,,z@y
7 a@b,,,z@y
8 ,,,a@b,,,
9 ,a@b
10 a@b,
11 ,
12 ,,
13 ,,,
14 a group: a@b,z@y ;
15 a group: ,a@b,z@y ;
16 a group: a@b,z@y, ;
17 a group: ,a@b,z@y, ;
18 a group: a@b,,z@y ;
19 a group: a@b,,,z@y ;
20 a group: ,,,a@b,,, ;
21 a group: ,a@b ;
22 a group: a@b, ;
23 a group: , ;
24 a group: ,, ;
25 a group: ,,, ;
26 Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:sroberts@[10].[1]> ;
2 Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1]; 27 Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1];
3 lo@hi, Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1]; 28 lo@hi, Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1];
4 Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1];, hi@lo 29 Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1];, hi@lo
...@@ -15,7 +40,7 @@ list-ietf-wg-apps-drums@faerber.muc.de (=?ISO-8859-1?Q?Claus_F=E4rber?=) ...@@ -15,7 +40,7 @@ list-ietf-wg-apps-drums@faerber.muc.de (=?ISO-8859-1?Q?Claus_F=E4rber?=)
15 "'paul@pitbull-productions.com'" <paul@pitbull-productions.com>, 40 "'paul@pitbull-productions.com'" <paul@pitbull-productions.com>,
16 "'sam@cogent.ca'" <sam@cogent.ca>, "'sroberts@uniserve.com'" 41 "'sam@cogent.ca'" <sam@cogent.ca>, "'sroberts@uniserve.com'"
17 "'sroberts\@certicom\.ca'" <sroberts@certicom.ca> 42 "'sroberts\@certicom\.ca'" <sroberts@certicom.ca>
18 "=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" <jcmarcos@datavoicees> 43 "=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" <jcmarcos@datavoice.es>
19 "Christian Edward Gruber" <christian.edward.gruber@gmx.net>, 44 "Christian Edward Gruber" <christian.edward.gruber@gmx.net>,
20 "D. J. Bernstein" <"djb- "@cr.yp.to> 45 "D. J. Bernstein" <"djb- "@cr.yp.to>
21 "D. J. Bernstein" <djb@cr.yp.to>, drums@cs.utk.edu 46 "D. J. Bernstein" <djb@cr.yp.to>, drums@cs.utk.edu
...@@ -58,9 +83,9 @@ list-ietf-wg-apps-drums@faerber.muc.de (=?ISO-8859-1?Q?Claus_F=E4rber?=) ...@@ -58,9 +83,9 @@ list-ietf-wg-apps-drums@faerber.muc.de (=?ISO-8859-1?Q?Claus_F=E4rber?=)
58 =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@cisco.com> 83 =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@cisco.com>
59 =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@cisco.com>, 84 =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@cisco.com>,
60 =?US-ASCII?Q?gary=5Fc?= <gary_c@cunningham-lee.com> 85 =?US-ASCII?Q?gary=5Fc?= <gary_c@cunningham-lee.com>
61 =?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidusnet> 86 =?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidus.net>
62 =?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidusnet>, 87 =?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidus.net>,
63 =?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= <0@pervalidusnet> 88 =?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= <0@pervalidus.net>
64 =?iso-8859-1?Q?J=F8rgen_Thomsen?= <jth@postfix.jth.net> 89 =?iso-8859-1?Q?J=F8rgen_Thomsen?= <jth@postfix.jth.net>
65 =?iso-8859-1?Q?Jos=3F_C=2E_Garc=EDa_Sogo?= <jose@jaimedelamo.eu.org> 90 =?iso-8859-1?Q?Jos=3F_C=2E_Garc=EDa_Sogo?= <jose@jaimedelamo.eu.org>
66 =?iso-8859-1?Q?Mikko_H=E4nninen?= <Mikko.Hanninen@dna.fi> 91 =?iso-8859-1?Q?Mikko_H=E4nninen?= <Mikko.Hanninen@dna.fi>
......
1 CFLAGS = -Wall -pedantic -g 1 # Makefile
2 INCLUDES = -I../libmailbox 2
3 LIBS = ../libmailbox/.libs/libmailbox.al 3 CFLAGS = -g -I../include
4 LDFLAGS = -g -static
5 LIBS = ../mailbox/.libs/libmailbox.a ../lib/libmailutils.a
6
7 default: addr
8
9 # showmail
4 10
5 showmail: showmail.c $(LIBS) 11 showmail: showmail.c $(LIBS)
6 $(CC) $(CFLAGS) $(INCLUDES) -o showmail showmail.c $(LIBS) 12 $(CC) $(CFLAGS) -o $@ $< $(LIBS)
13
14 # addr example and test
15
16 test: addr
17 ./addr < Addrs > Addrs.test
18 diff -u Addrs.good Addrs.test
19 @echo "---- There should be no differences! ----"
7 20
8 addr: addr.c $(LIBS) 21 addr: addr.c $(LIBS)
9 $(CC) $(CFLAGS) $(INCLUDES) -o addr addr.c $(LIBS) 22 $(CC) $(CFLAGS) -o $@ $< $(LIBS)
23
24 # clean and empty
10 25
11 clean: 26 clean:
27 rm -f *.o
28
29 empty: clean
12 rm -f addr showmail 30 rm -f addr showmail
31
......
1 #include <stdio.h> 1 #include <stdio.h>
2 #include <errno.h>
2 #include <mailutils/address.h> 3 #include <mailutils/address.h>
3 4
5 #define EPARSE ENOENT
6
7 static const char* err_name(int e)
8 {
9 struct {
10 int e;
11 const char* s;
12 } map[] = {
13 #define E(e) { e, #e },
14 E(ENOENT)
15 E(EINVAL)
16 E(ENOMEM)
17 #undef E
18 { 0, NULL }
19 };
20 static char s[sizeof(int) * 8 + 3];
21 int i;
22
23 for(i = 0; map[i].s; i++) {
24 if(map[i].e == e)
25 return map[i].s;
26 }
27 sprintf(s, "[%d]", e);
28
29 return s;
30 }
31
4 static int parse(const char* str) 32 static int parse(const char* str)
5 { 33 {
6 size_t no = 0; 34 size_t no = 0;
7 size_t pcount; 35 size_t pcount = 0;
36 int status;
8 37
9 char buf[BUFSIZ]; 38 char buf[BUFSIZ];
10 39
11 address_t address = NULL; 40 address_t address = NULL;
12 41
13 address_create(&address, str); 42 status = address_create(&address, str);
14 43
15 address_get_count(address, &pcount); 44 address_get_count(address, &pcount);
16 45
46 if(status) {
47 printf("%s=> error %s\n\n", str, err_name(status));
48 return 0;
49 } else {
17 printf("%s=> pcount %d\n", str, pcount); 50 printf("%s=> pcount %d\n", str, pcount);
51 }
18 52
19 for(no = 1; no <= pcount; no++) { 53 for(no = 1; no <= pcount; no++) {
20 size_t got = 0; 54 size_t got = 0;
55 int isgroup;
56
57 address_is_group(address, no, &isgroup);
58
21 printf("%d ", no); 59 printf("%d ", no);
22 60
61 if(isgroup) {
62 address_get_personal(address, no, buf, sizeof(buf), &got);
63
64 printf("group <%s>\n", buf);
65 } else {
23 address_get_email(address, no, buf, sizeof(buf), 0); 66 address_get_email(address, no, buf, sizeof(buf), 0);
24 67
25 printf("email <%s>\n", buf); 68 printf("email <%s>\n", buf);
69 }
26 70
27 address_get_personal(address, no, buf, sizeof(buf), &got); 71 address_get_personal(address, no, buf, sizeof(buf), &got);
28 72
29 if(got) printf(" personal <%s>\n", buf); 73 if(got && !isgroup) printf(" personal <%s>\n", buf);
30 74
31 address_get_comments(address, no, buf, sizeof(buf), &got); 75 address_get_comments(address, no, buf, sizeof(buf), &got);
32 76
......
...@@ -52,6 +52,9 @@ extern int address_get_comments ...@@ -52,6 +52,9 @@ extern int address_get_comments
52 extern int address_get_route 52 extern int address_get_route
53 __P ((address_t, size_t, char *, size_t, size_t *)); 53 __P ((address_t, size_t, char *, size_t, size_t *));
54 54
55 extern int address_is_group
56 __P ((address_t, size_t, int*));
57
55 extern int address_to_string __P ((address_t, char *, size_t, size_t *)); 58 extern int address_to_string __P ((address_t, char *, size_t, size_t *));
56 extern int address_get_count __P ((address_t, size_t *)); 59 extern int address_get_count __P ((address_t, size_t *));
57 60
......
...@@ -46,8 +46,7 @@ address_create (address_t *a, const char *s) ...@@ -46,8 +46,7 @@ address_create (address_t *a, const char *s)
46 status = parse822_address_list (a, (char*) s); 46 status = parse822_address_list (a, (char*) s);
47 if (status == 0) 47 if (status == 0)
48 { 48 {
49 /* There was a group that got parsed correctly, but had 49 /* And address-list may contain 0 addresses but parse correctly.
50 * no addresses...
51 */ 50 */
52 if (!*a) 51 if (!*a)
53 return ENOENT; 52 return ENOENT;
...@@ -220,6 +219,29 @@ address_get_route (address_t addr, size_t no, char *buf, size_t len, size_t *n) ...@@ -220,6 +219,29 @@ address_get_route (address_t addr, size_t no, char *buf, size_t len, size_t *n)
220 return status; 219 return status;
221 } 220 }
222 221
222 int
223 address_is_group (address_t addr, size_t no, int* yes)
224 {
225 size_t j;
226 int status = ENOENT;
227 if(addr == NULL)
228 return EINVAL;
229 for (j = 1; addr; addr = addr->next, j++)
230 {
231 if (j == no)
232 {
233 status = 0;
234 if(yes)
235 {
236 *yes = 0;
237 if(addr->personal && !addr->local_part && !addr->domain)
238 *yes = 1;
239 }
240 break;
241 }
242 }
243 return status;
244 }
223 245
224 int 246 int
225 address_to_string (address_t addr, char *buf, size_t len, size_t *n) 247 address_to_string (address_t addr, char *buf, size_t len, size_t *n)
......
...@@ -18,18 +18,41 @@ ...@@ -18,18 +18,41 @@
18 /* 18 /*
19 Things to consider: 19 Things to consider:
20 20
21 - A group should create an address node with a group, accessable 21 - A group should create an address node for a group, accessable
22 with address_get_group(). 22 with address_get_personal(). Perhaps an is_group() would be
23 useful? Test that a zero-length phrase is rejected! So these
24 are invalid:
25 : a@b ;
26 "" : ;
27
28 - When parsing phrase, should I ignore non-ascii, or replace with a
29 '?' character? Right now parsing fails.
23 30
24 - Make domain optional in addr-spec, for parsing address lists 31 - Make domain optional in addr-spec, for parsing address lists
25 provided to local mail utilities. 32 provided to local mail utilities, but NOT in the addr-spec of a
33 route-addr.
34
35 - Are comments allowed in domain-literals?
36
37 - Need a way to mark the *end* of a group. Maybe add a field to _address,
38 int group_end;, so if you care, you can search for the end of
39 a group with address_is_group_end();
40
41 - Need a way to parse "<>", it's a valid SMTP address...
42
43 - Need a way to parse ",,,", it's a valid address-list, it just doesn't
44 have any addresses.
45
46 - Functions for forming email addresses, quoting display-name, etc.
47
48 - The personal for ""Sam"" <sam@here> is "Sam", and for "'s@b'" <s@b>
49 is 's@b', should I strip those outside parentheses, or is that
50 too intrusive? Maybe an apps business if it wants to?
26 51
27 - Should we do best effort parsing, so parsing "sam@locahost, foo@" 52 - Should we do best effort parsing, so parsing "sam@locahost, foo@"
28 gets one address, or just say it is or it isn't in RFC format? 53 gets one address, or just say it is or it isn't in RFC format?
29 Right now we're strict, we'll see how it goes. 54 Right now we're strict, we'll see how it goes.
30 55
31 - quote local-part when generating email field of address_t.
32
33 - parse field names and bodies? 56 - parse field names and bodies?
34 - parse dates? 57 - parse dates?
35 - parse Received: field? 58 - parse Received: field?
...@@ -37,6 +60,7 @@ Things to consider: ...@@ -37,6 +60,7 @@ Things to consider:
37 - test for memory leaks on malloc failure 60 - test for memory leaks on malloc failure
38 - fix the realloc, try a struct _string { char* b, size_t sz }; 61 - fix the realloc, try a struct _string { char* b, size_t sz };
39 62
63 - get example mail from drums, and from the perl code.
40 */ 64 */
41 65
42 #ifdef HAVE_CONFIG_H 66 #ifdef HAVE_CONFIG_H
...@@ -499,9 +523,12 @@ static int fill_mb( ...@@ -499,9 +523,12 @@ static int fill_mb(
499 (*a)->comments = comments; 523 (*a)->comments = comments;
500 (*a)->personal = personal; 524 (*a)->personal = personal;
501 525
502 /* this is wrong, local must be quoted */
503 do { 526 do {
504 /* loop exists only to break out of */ 527 /* loop exists only to break out of */
528 if(!local || !domain) {
529 /* no email to construct */
530 break;
531 }
505 if((rc = parse822_quote_local_part(&(*a)->email, local))) 532 if((rc = parse822_quote_local_part(&(*a)->email, local)))
506 break; 533 break;
507 if((rc = str_append(&(*a)->email, "@"))) 534 if((rc = str_append(&(*a)->email, "@")))
...@@ -531,13 +558,20 @@ int parse822_address_list(address_t* a, const char* s) ...@@ -531,13 +558,20 @@ int parse822_address_list(address_t* a, const char* s)
531 int rc = EOK; 558 int rc = EOK;
532 address_t* n = a; /* the next address we'll be creating */ 559 address_t* n = a; /* the next address we'll be creating */
533 560
534 if((rc = parse822_address(p, e, n))) 561 rc = parse822_address(p, e, n);
535 return rc;
536
537 parse822_skip_comments(p, e);
538 562
563 /* A list may start with a leading <,>, we'll find out if
564 * that's not the case at the top of the while, but give
565 * this a conditional OK unless there was some other kind
566 * of error.
567 */
568 if(rc != EOK && rc != EPARSE) {
569 return rc;
570 }
539 while(*p < e) 571 while(*p < e)
540 { 572 {
573 parse822_skip_comments(p, e);
574
541 /* An address can contain a group, so an entire 575 /* An address can contain a group, so an entire
542 * list of addresses may have been appended, or no 576 * list of addresses may have been appended, or no
543 * addresses at all. Walk to the end. 577 * addresses at all. Walk to the end.
...@@ -565,8 +599,6 @@ int parse822_address_list(address_t* a, const char* s) ...@@ -565,8 +599,6 @@ int parse822_address_list(address_t* a, const char* s)
565 /* anything else is a fatal error, break out */ 599 /* anything else is a fatal error, break out */
566 break; 600 break;
567 } 601 }
568
569 parse822_skip_comments(p, e);
570 } 602 }
571 603
572 if(rc) { 604 if(rc) {
...@@ -595,12 +627,13 @@ int parse822_group(const char** p, const char* e, address_t* a) ...@@ -595,12 +627,13 @@ int parse822_group(const char** p, const char* e, address_t* a)
595 const char* save = *p; 627 const char* save = *p;
596 address_t* asave = a; /* so we can destroy these if parsing fails */ 628 address_t* asave = a; /* so we can destroy these if parsing fails */
597 int rc; 629 int rc;
630 char* phrase = 0;
598 631
599 parse822_skip_comments(p, e); 632 parse822_skip_comments(p, e);
600 633
601 *p = save; 634 *p = save;
602 635
603 if((rc = parse822_phrase(p, e, 0))) { 636 if((rc = parse822_phrase(p, e, &phrase))) {
604 return rc; 637 return rc;
605 } 638 }
606 639
...@@ -611,11 +644,20 @@ int parse822_group(const char** p, const char* e, address_t* a) ...@@ -611,11 +644,20 @@ int parse822_group(const char** p, const char* e, address_t* a)
611 return rc; 644 return rc;
612 } 645 }
613 646
647 /* fake up an address node for the group's descriptive phrase, if
648 * it fails, clean-up will happen after the loop
649 */
650 if((rc = fill_mb(a, 0, phrase, 0, 0)) == EOK) {
651 a = &(*a)->next;
652 } else {
653 str_free(&phrase);
654 }
655
614 /* Basically, on each loop, we may find a mailbox, but we must find 656 /* Basically, on each loop, we may find a mailbox, but we must find
615 * a comma after the mailbox, otherwise we've popped off the end 657 * a comma after the mailbox, otherwise we've popped off the end
616 * of the list. 658 * of the list.
617 */ 659 */
618 for(;;) { 660 while(!rc) {
619 parse822_skip_comments(p, e); 661 parse822_skip_comments(p, e);
620 662
621 /* it's ok not be a mailbox, but other errors are fatal */ 663 /* it's ok not be a mailbox, but other errors are fatal */
......