Index: ChangeLog =================================================================== RCS file: /cvs/mailutils/ChangeLog,v retrieving revision 1.87 diff -u -r1.87 ChangeLog --- ChangeLog 2001/04/14 22:09:55 1.87 +++ ChangeLog 2001/04/15 00:07:19 @@ -1,3 +1,10 @@ +2001-04-14 Sam Roberts + * 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 ",address@hidden" wasn't valid. + 2001-04-14 Alain Magloire * mailbox/folder_imap.c: When calling imap_writeline () the Index: doc/address.texi =================================================================== RCS file: /cvs/mailutils/doc/address.texi,v retrieving revision 1.3 diff -u -r1.3 address.texi --- doc/address.texi 2001/04/13 23:29:44 1.3 +++ doc/address.texi 2001/04/15 00:07:20 @@ -11,14 +11,13 @@ the other", and double-quoted characters are literals. @example -address-list = address ["," address-list] -address = mailbox / group -mailbox = addr-spec ["(" personal ")"] / - [personal] "<" [route] addr-spec ">" addr-spec = local-part "@" domain -group = phrase ":" mailbox-list ";" - +mailbox = addr-spec ["(" display-name ")"] / + [display-name] "<" [route] addr-spec ">" mailbox-list = mailbox ["," mailbox-list] +group = display-name ":" [mailbox-list] ";" +address = mailbox / group +address-list = address ["," address-list] @end example Several address functions have a set of common arguments with consistent @@ -98,12 +97,18 @@ @deftypefun int address_get_personal (address_t address@hidden, size_t @var{no}, char* @var{buf}, size_t @var{len}, size_t* @var{n}) -Acesses the personal phrase describing the @var{no}th email address. This -personal is optional, so may not be present. If it is not present, but +Acesses the display-name describing the @var{no}th email address. This +display-name is optional, so may not be present. If it is not present, but there is an RFC822 comment after the address, that comment will be returned as the personal phrase, as this is a common usage of the comment even though it is not defined in the internet mail standard. +A group is a kind of a special case. It has a display-name, followed +by an optional mailbox-list. The display-name will be allocated an address +all it's own, but all the other elements (local-part, domain, etc.) will +be zero-length. So "a group: ;" is valid, will have a count of 1, but +address_get_email(), and all the rest, will return zero-length output. + The return value is @code{0} on success and a code number on error conditions: @table @code @ADDRESSEINVAL @@ -167,6 +172,24 @@ parsing the @var{no}th email address. This is a rarely used RFC822 address syntax, but is legal in SMTP as well. The entire route is returned as a string, those wishing to parse it should look at . + +The return value is @code{0} on success and a code number on error conditions: address@hidden @code address@hidden address@hidden address@hidden table address@hidden deftypefun + address@hidden int address_is_group (address_t address@hidden, size_t @var{no}, size_t @var{len}, int* @var{yes}) + +Sets address@hidden to @code{1} if this address is just the name of a group, address@hidden otherwise. This is faster than checking if the address has +a non-zero length personal, and a zero-length local_part and domain. + address@hidden can be @code{nul}, though that doesn't serve much purpose other +than determining that @var{no} refers to an address. + +Currently, there is no way to determine the end of the group. The return value is @code{0} on success and a code number on error conditions: @table @code Index: examples/Addrs =================================================================== RCS file: /cvs/mailutils/examples/Addrs,v retrieving revision 1.1 diff -u -r1.1 Addrs --- examples/Addrs 2001/04/10 05:17:43 1.1 +++ examples/Addrs 2001/04/15 00:07:21 @@ -1,4 +1,29 @@ -Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:address@hidden> +Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:address@hidden> ; address@hidden,address@hidden +,address@hidden,address@hidden address@hidden,address@hidden, +,address@hidden,address@hidden, address@hidden,,address@hidden address@hidden,,,address@hidden +,,,address@hidden,,, +,address@hidden address@hidden, +, +,, +,,, +a group: address@hidden,address@hidden ; +a group: ,address@hidden,address@hidden ; +a group: address@hidden,address@hidden, ; +a group: ,address@hidden,address@hidden, ; +a group: address@hidden,,address@hidden ; +a group: address@hidden,,,address@hidden ; +a group: ,,,address@hidden,,, ; +a group: ,address@hidden ; +a group: address@hidden, ; +a group: , ; +a group: ,, ; +a group: ,,, ; +Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:address@hidden> ; Aliens: Sam <@[matrix (smtp)]: address@hidden>, address@hidden; address@hidden, Aliens: Sam <@[matrix (smtp)]: address@hidden>, address@hidden; Aliens: Sam <@[matrix (smtp)]: address@hidden>, address@hidden;, address@hidden @@ -15,7 +40,7 @@ "'address@hidden'" , "'address@hidden'" , "'address@hidden'" "'address@hidden'" -"=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" +"=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" "Christian Edward Gruber" , "D. J. Bernstein" <"djb- "@cr.yp.to> "D. J. Bernstein" , address@hidden @@ -58,9 +83,9 @@ =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= =?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= , =?US-ASCII?Q?gary=5Fc?= -=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier -=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier , -=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= +=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier +=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier , +=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= =?iso-8859-1?Q?J=F8rgen_Thomsen?= =?iso-8859-1?Q?Jos=3F_C=2E_Garc=EDa_Sogo?= =?iso-8859-1?Q?Mikko_H=E4nninen?= Index: examples/Makefile =================================================================== RCS file: /cvs/mailutils/examples/Makefile,v retrieving revision 1.3 diff -u -r1.3 Makefile --- examples/Makefile 2001/04/10 05:17:26 1.3 +++ examples/Makefile 2001/04/15 00:07:21 @@ -1,12 +1,31 @@ -CFLAGS = -Wall -pedantic -g -INCLUDES = -I../libmailbox -LIBS = ../libmailbox/.libs/libmailbox.al +# Makefile +CFLAGS = -g -I../include +LDFLAGS = -g -static +LIBS = ../mailbox/.libs/libmailbox.a ../lib/libmailutils.a + +default: addr + +# showmail + showmail: showmail.c $(LIBS) - $(CC) $(CFLAGS) $(INCLUDES) -o showmail showmail.c $(LIBS) + $(CC) $(CFLAGS) -o $@ $< $(LIBS) + +# addr example and test +test: addr + ./addr < Addrs > Addrs.test + diff -u Addrs.good Addrs.test + @echo "---- There should be no differences! ----" + addr: addr.c $(LIBS) - $(CC) $(CFLAGS) $(INCLUDES) -o addr addr.c $(LIBS) + $(CC) $(CFLAGS) -o $@ $< $(LIBS) + +# clean and empty clean: + rm -f *.o + +empty: clean rm -f addr showmail + Index: examples/addr.c =================================================================== RCS file: /cvs/mailutils/examples/addr.c,v retrieving revision 1.2 diff -u -r1.2 addr.c --- examples/addr.c 2001/04/10 05:17:26 1.2 +++ examples/addr.c 2001/04/15 00:07:23 @@ -1,48 +1,92 @@ #include +#include #include +#define EPARSE ENOENT + +static const char* err_name(int e) +{ + struct { + int e; + const char* s; + } map[] = { +#define E(e) { e, #e }, + E(ENOENT) + E(EINVAL) + E(ENOMEM) +#undef E + { 0, NULL } + }; + static char s[sizeof(int) * 8 + 3]; + int i; + + for(i = 0; map[i].s; i++) { + if(map[i].e == e) + return map[i].s; + } + sprintf(s, "[%d]", e); + + return s; +} + static int parse(const char* str) { size_t no = 0; - size_t pcount; + size_t pcount = 0; + int status; char buf[BUFSIZ]; address_t address = NULL; - address_create(&address, str); + status = address_create(&address, str); address_get_count(address, &pcount); - printf("%s=> pcount %d\n", str, pcount); + if(status) { + printf("%s=> error %s\n\n", str, err_name(status)); + return 0; + } else { + printf("%s=> pcount %d\n", str, pcount); + } for(no = 1; no <= pcount; no++) { - size_t got = 0; - printf("%d ", no); + size_t got = 0; + int isgroup; + + address_is_group(address, no, &isgroup); + + printf("%d ", no); + + if(isgroup) { + address_get_personal(address, no, buf, sizeof(buf), &got); - address_get_email(address, no, buf, sizeof(buf), 0); + printf("group <%s>\n", buf); + } else { + address_get_email(address, no, buf, sizeof(buf), 0); - printf("email <%s>\n", buf); + printf("email <%s>\n", buf); + } - address_get_personal(address, no, buf, sizeof(buf), &got); + address_get_personal(address, no, buf, sizeof(buf), &got); - if(got) printf(" personal <%s>\n", buf); + if(got && !isgroup) printf(" personal <%s>\n", buf); - address_get_comments(address, no, buf, sizeof(buf), &got); + address_get_comments(address, no, buf, sizeof(buf), &got); - if(got) printf(" comments <%s>\n", buf); + if(got) printf(" comments <%s>\n", buf); - address_get_local_part(address, no, buf, sizeof(buf), &got); + address_get_local_part(address, no, buf, sizeof(buf), &got); - if(got) printf(" local-part <%s>", buf); + if(got) printf(" local-part <%s>", buf); - address_get_domain(address, no, buf, sizeof(buf), &got); + address_get_domain(address, no, buf, sizeof(buf), &got); - if(got) printf(" domain <%s>\n", buf); + if(got) printf(" domain <%s>\n", buf); - address_get_route(address, no, buf, sizeof(buf), &got); + address_get_route(address, no, buf, sizeof(buf), &got); - if(got) printf(" route <%s>\n", buf); + if(got) printf(" route <%s>\n", buf); } address_destroy(&address); @@ -53,30 +97,30 @@ static int parseinput(void) { - char buf[BUFSIZ]; + char buf[BUFSIZ]; - while(fgets(buf, sizeof(buf), stdin) != 0) { - buf[strlen(buf) - 1] = 0; - parse(buf); - } + while(fgets(buf, sizeof(buf), stdin) != 0) { + buf[strlen(buf) - 1] = 0; + parse(buf); + } - return 0; + return 0; } int main(int argc, const char *argv[]) { - argc = 1; + argc = 1; - if(!argv[argc]) { - return parseinput(); - } - for(; argv[argc]; argc++) { - if(strcmp(argv[argc], "-") == 0) { - parseinput(); - } else { - parse(argv[argc]); - } + if(!argv[argc]) { + return parseinput(); + } + for(; argv[argc]; argc++) { + if(strcmp(argv[argc], "-") == 0) { + parseinput(); + } else { + parse(argv[argc]); } + } - return 0; + return 0; } Index: include/mailutils/address.h =================================================================== RCS file: /cvs/mailutils/include/mailutils/address.h,v retrieving revision 1.4 diff -u -r1.4 address.h --- include/mailutils/address.h 2001/03/13 23:53:49 1.4 +++ include/mailutils/address.h 2001/04/15 00:07:23 @@ -52,6 +52,9 @@ extern int address_get_route __P ((address_t, size_t, char *, size_t, size_t *)); +extern int address_is_group + __P ((address_t, size_t, int*)); + extern int address_to_string __P ((address_t, char *, size_t, size_t *)); extern int address_get_count __P ((address_t, size_t *)); Index: mailbox/address.c =================================================================== RCS file: /cvs/mailutils/mailbox/address.c,v retrieving revision 1.8 diff -u -r1.8 address.c --- mailbox/address.c 2001/04/10 05:13:50 1.8 +++ mailbox/address.c 2001/04/15 00:07:24 @@ -46,8 +46,7 @@ status = parse822_address_list (a, (char*) s); if (status == 0) { - /* There was a group that got parsed correctly, but had - * no addresses... + /* And address-list may contain 0 addresses but parse correctly. */ if (!*a) return ENOENT; @@ -220,6 +219,29 @@ return status; } +int +address_is_group (address_t addr, size_t no, int* yes) +{ + size_t j; + int status = ENOENT; + if(addr == NULL) + return EINVAL; + for (j = 1; addr; addr = addr->next, j++) + { + if (j == no) + { + status = 0; + if(yes) + { + *yes = 0; + if(addr->personal && !addr->local_part && !addr->domain) + *yes = 1; + } + break; + } + } + return status; +} int address_to_string (address_t addr, char *buf, size_t len, size_t *n) Index: mailbox/parse822.c =================================================================== RCS file: /cvs/mailutils/mailbox/parse822.c,v retrieving revision 1.5 diff -u -r1.5 parse822.c --- mailbox/parse822.c 2001/04/14 00:31:59 1.5 +++ mailbox/parse822.c 2001/04/15 00:07:26 @@ -18,18 +18,41 @@ /* Things to consider: - - A group should create an address node with a group, accessable - with address_get_group(). + - A group should create an address node for a group, accessable + with address_get_personal(). Perhaps an is_group() would be + useful? Test that a zero-length phrase is rejected! So these + are invalid: + : address@hidden ; + "" : ; + - When parsing phrase, should I ignore non-ascii, or replace with a + '?' character? Right now parsing fails. + - Make domain optional in addr-spec, for parsing address lists - provided to local mail utilities. + provided to local mail utilities, but NOT in the addr-spec of a + route-addr. + + - Are comments allowed in domain-literals? + + - Need a way to mark the *end* of a group. Maybe add a field to _address, + int group_end;, so if you care, you can search for the end of + a group with address_is_group_end(); + + - Need a way to parse "<>", it's a valid SMTP address... + + - Need a way to parse ",,,", it's a valid address-list, it just doesn't + have any addresses. + + - Functions for forming email addresses, quoting display-name, etc. + - The personal for ""Sam"" is "Sam", and for "'address@hidden'" + is 'address@hidden', should I strip those outside parentheses, or is that + too intrusive? Maybe an apps business if it wants to? + - Should we do best effort parsing, so parsing "address@hidden, foo@" gets one address, or just say it is or it isn't in RFC format? Right now we're strict, we'll see how it goes. - - quote local-part when generating email field of address_t. - - parse field names and bodies? - parse dates? - parse Received: field? @@ -37,6 +60,7 @@ - test for memory leaks on malloc failure - fix the realloc, try a struct _string { char* b, size_t sz }; + - get example mail from drums, and from the perl code. */ #ifdef HAVE_CONFIG_H @@ -499,9 +523,12 @@ (*a)->comments = comments; (*a)->personal = personal; - /* this is wrong, local must be quoted */ do { /* loop exists only to break out of */ + if(!local || !domain) { + /* no email to construct */ + break; + } if((rc = parse822_quote_local_part(&(*a)->email, local))) break; if((rc = str_append(&(*a)->email, "@"))) @@ -531,13 +558,20 @@ int rc = EOK; address_t* n = a; /* the next address we'll be creating */ - if((rc = parse822_address(p, e, n))) - return rc; - - parse822_skip_comments(p, e); + rc = parse822_address(p, e, n); + /* A list may start with a leading <,>, we'll find out if + * that's not the case at the top of the while, but give + * this a conditional OK unless there was some other kind + * of error. + */ + if(rc != EOK && rc != EPARSE) { + return rc; + } while(*p < e) { + parse822_skip_comments(p, e); + /* An address can contain a group, so an entire * list of addresses may have been appended, or no * addresses at all. Walk to the end. @@ -565,8 +599,6 @@ /* anything else is a fatal error, break out */ break; } - - parse822_skip_comments(p, e); } if(rc) { @@ -595,12 +627,13 @@ const char* save = *p; address_t* asave = a; /* so we can destroy these if parsing fails */ int rc; + char* phrase = 0; parse822_skip_comments(p, e); *p = save; - if((rc = parse822_phrase(p, e, 0))) { + if((rc = parse822_phrase(p, e, &phrase))) { return rc; } @@ -611,11 +644,20 @@ return rc; } + /* fake up an address node for the group's descriptive phrase, if + * it fails, clean-up will happen after the loop + */ + if((rc = fill_mb(a, 0, phrase, 0, 0)) == EOK) { + a = &(*a)->next; + } else { + str_free(&phrase); + } + /* Basically, on each loop, we may find a mailbox, but we must find * a comma after the mailbox, otherwise we've popped off the end * of the list. */ - for(;;) { + while(!rc) { parse822_skip_comments(p, e); /* it's ok not be a mailbox, but other errors are fatal */