summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--doc/address.texi39
-rw-r--r--examples/Addrs35
-rw-r--r--examples/Makefile29
-rw-r--r--examples/addr.c112
-rw-r--r--include/mailutils/address.h3
-rw-r--r--mailbox/address.c26
-rw-r--r--mailbox/parse822.c70
8 files changed, 253 insertions, 68 deletions
diff --git a/ChangeLog b/ChangeLog
index 3051a4fe8..7ff8fdffb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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 ",sam@foo.bar" wasn't valid.
+
2001-04-14 Alain Magloire
* mailbox/folder_imap.c: When calling imap_writeline () the
diff --git a/doc/address.texi b/doc/address.texi
index 72b3024d6..2a5b8398b 100644
--- a/doc/address.texi
+++ b/doc/address.texi
@@ -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
diff --git a/examples/Addrs b/examples/Addrs
index df2479b36..9ff8412de 100644
--- a/examples/Addrs
+++ b/examples/Addrs
@@ -1,4 +1,29 @@
-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>
diff --git a/examples/Makefile b/examples/Makefile
index d23651f34..845fceb2b 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -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
+
diff --git a/examples/addr.c b/examples/addr.c
index d8bcdfb7b..5f495a052 100644
--- a/examples/addr.c
+++ b/examples/addr.c
@@ -1,48 +1,92 @@
#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;
}
diff --git a/include/mailutils/address.h b/include/mailutils/address.h
index 10fd408ee..4b4924cd5 100644
--- a/include/mailutils/address.h
+++ b/include/mailutils/address.h
@@ -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 *));
diff --git a/mailbox/address.c b/mailbox/address.c
index 1123f3ca0..b79ad3d7e 100644
--- a/mailbox/address.c
+++ b/mailbox/address.c
@@ -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)
diff --git a/mailbox/parse822.c b/mailbox/parse822.c
index bee130f32..2674778a9 100644
--- a/mailbox/parse822.c
+++ b/mailbox/parse822.c
@@ -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 */

Return to:

Send suggestions and report system problems to the System administrator.