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
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 ",sam@foo.bar" wasn't valid.
2001-04-14 Alain Magloire
* mailbox/folder_imap.c: When calling imap_writeline () the
......
......@@ -11,14 +11,13 @@ RFC 822 for the details. "[]" pairs mean "optional", "/" means "one or
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 @@ The return value is @code{0} on success and a code number on error conditions:
@deftypefun int address_get_personal (address_t *@var{addr}, 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
......@@ -175,6 +180,24 @@ The return value is @code{0} on success and a code number on error conditions:
@end table
@end deftypefun
@deftypefun int address_is_group (address_t *@var{addr}, size_t @var{no}, size_t @var{len}, int* @var{yes})
Sets *@var{yes} to @code{1} if this address is just the name of a group,
@code{0} otherwise. This is faster than checking if the address has
a non-zero length personal, and a zero-length local_part and domain.
@var{yes} 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
@ADDRESSEINVAL
@ADDRESSENOENT
@end table
@end deftypefun
@deftypefun int address_to_string (address_t *@var{addr}, char* @var{buf}, size_t @var{len}, size_t* @var{n})
Returns the entire address list as a single RFC822 formatted address
......
Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:sroberts@[10].[1]>
Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:sroberts@[10].[1]> ;
a@b,z@y
,a@b,z@y
a@b,z@y,
,a@b,z@y,
a@b,,z@y
a@b,,,z@y
,,,a@b,,,
,a@b
a@b,
,
,,
,,,
a group: a@b,z@y ;
a group: ,a@b,z@y ;
a group: a@b,z@y, ;
a group: ,a@b,z@y, ;
a group: a@b,,z@y ;
a group: a@b,,,z@y ;
a group: ,,,a@b,,, ;
a group: ,a@b ;
a group: a@b, ;
a group: , ;
a group: ,, ;
a group: ,,, ;
Sam <@[matrix (smtp)], @[nexus: \[node 12\]]:sroberts@[10].[1]> ;
Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1];
lo@hi, Aliens: Sam <@[matrix (smtp)]: sam@sam>, sroberts@[10].[1];
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?=)
"'paul@pitbull-productions.com'" <paul@pitbull-productions.com>,
"'sam@cogent.ca'" <sam@cogent.ca>, "'sroberts@uniserve.com'"
"'sroberts\@certicom\.ca'" <sroberts@certicom.ca>
"=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" <jcmarcos@datavoicees>
"=?iso-8859-1?Q?Juan_Carlos_Marcos_Rodr=EDguez?=" <jcmarcos@datavoice.es>
"Christian Edward Gruber" <christian.edward.gruber@gmx.net>,
"D. J. Bernstein" <"djb- "@cr.yp.to>
"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?=)
=?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@cisco.com>
=?ISO-8859-1?Q?Patrik_F=E4ltstr=F6m?= <paf@cisco.com>,
=?US-ASCII?Q?gary=5Fc?= <gary_c@cunningham-lee.com>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidusnet>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidusnet>,
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= <0@pervalidusnet>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidus.net>
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E?= Meunier <0@pervalidus.net>,
=?iso-8859-1?Q?Fr=E9d=E9ric_L_=2E_W_=2E_Meunier?= <0@pervalidus.net>
=?iso-8859-1?Q?J=F8rgen_Thomsen?= <jth@postfix.jth.net>
=?iso-8859-1?Q?Jos=3F_C=2E_Garc=EDa_Sogo?= <jose@jaimedelamo.eu.org>
=?iso-8859-1?Q?Mikko_H=E4nninen?= <Mikko.Hanninen@dna.fi>
......
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
......
#include <stdio.h>
#include <errno.h>
#include <mailutils/address.h>
#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 parse(const char* str)
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;
}
......
......@@ -52,6 +52,9 @@ extern int address_get_comments
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 *));
......
......@@ -46,8 +46,7 @@ address_create (address_t *a, const char *s)
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 @@ address_get_route (address_t addr, size_t no, char *buf, size_t len, size_t *n)
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)
......
......@@ -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:
: a@b ;
"" : ;
- 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"" <sam@here> is "Sam", and for "'s@b'" <s@b>
is 's@b', 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 "sam@locahost, 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 @@ Things to consider:
- 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 @@ static int fill_mb(
(*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 parse822_address_list(address_t* a, const char* s)
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 @@ int parse822_address_list(address_t* a, const char* s)
/* anything else is a fatal error, break out */
break;
}
parse822_skip_comments(p, e);
}
if(rc) {
......@@ -595,12 +627,13 @@ int parse822_group(const char** p, const char* e, address_t* a)
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 @@ int parse822_group(const char** p, const char* e, address_t* a)
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 */
......