diff -ru -I Id: -x CVS -N dbmail/Makefile.concept dbmail.working/Makefile.concept
--- dbmail/Makefile.concept	2002-12-05 11:39:50.000000000 +1300
+++ dbmail.working/Makefile.concept	2002-12-05 10:48:52.000000000 +1300
@@ -11,18 +11,18 @@
 DBOBJECT = $(__DBTYPE__)/db$(__DBTYPE__).o
 DBSOURCE = $(__DBTYPE__)/db$(__DBTYPE__).c
 
-SMTP_OBJECTS = list.o debug.o pipe.o mime.o $(DBOBJECT) dbmd5.o md5.o bounce.o forward.o memblock.o \
+SMTP_OBJECTS = list.o debug.o pipe.o mime.o check.o rfc822.o $(DBOBJECT) dbmd5.o md5.o bounce.o forward.o memblock.o \
 $(AUTHOBJECT) config.o
-INJECTOR_OBJECTS = list.o debug.o $(DBOBJECT) dbmd5.o md5.o $(AUTHOBJECT) mime.o config.o
-UNIONE_OBJECTS = list.o debug.o $(DBOBJECT) dbmd5.o md5.o $(AUTHOBJECT) mime.o config.o
-MINI_OBJECTS = debug.o $(DBOBJECT) list.o dbmd5.o md5.o $(AUTHOBJECT) mime.o config.o
-POP_OBJECTS = pop3.o list.o debug.o $(DBOBJECT) serverchild.o server.o dbmd5.o md5.o mime.o misc.o memblock.o $(AUTHOBJECT) config.o
+INJECTOR_OBJECTS = list.o debug.o $(DBOBJECT) dbmd5.o md5.o $(AUTHOBJECT) mime.o check.o rfc822.o config.o
+UNIONE_OBJECTS = list.o debug.o $(DBOBJECT) dbmd5.o md5.o $(AUTHOBJECT) mime.o check.o rfc822.o config.o
+MINI_OBJECTS = debug.o $(DBOBJECT) list.o dbmd5.o md5.o $(AUTHOBJECT) mime.o check.o rfc822.o config.o
+POP_OBJECTS = pop3.o list.o debug.o $(DBOBJECT) serverchild.o server.o dbmd5.o md5.o mime.o check.o rfc822.o misc.o memblock.o $(AUTHOBJECT) config.o
 IMAP_OBJECTS = imap4.o debug.o $(DBOBJECT) serverchild.o server.o config.o list.o dbmd5.o md5.o imaputil.o \
-imapcommands.o mime.o misc.o memblock.o rfcmsg.o $(MSGBUFOBJECT) $(SEARCHOBJECT) $(AUTHOBJECT)
-MAINTENANCE_OBJECTS = debug.o list.o dbmd5.o md5.o $(DBOBJECT) mime.o memblock.o $(AUTHOBJECT) config.o
-CONFIG_OBJECTS = $(DBOBJECT) list.o md5.o debug.o dbmd5.o mime.o memblock.o $(AUTHOBJECT) config.o
-USER_OBJECTS = debug.o list.o dbmd5.o md5.o $(DBOBJECT) mime.o memblock.o $(AUTHOBJECT) config.o
-VUTCONV_OBJECTS = debug.o list.o dbmd5.o md5.o mime.o $(DBOBJECT) $(AUTHOBJECT) config.o
+imapcommands.o mime.o check.o rfc822.o misc.o memblock.o rfcmsg.o $(MSGBUFOBJECT) $(SEARCHOBJECT) $(AUTHOBJECT)
+MAINTENANCE_OBJECTS = debug.o list.o dbmd5.o md5.o $(DBOBJECT) mime.o check.o rfc822.o memblock.o $(AUTHOBJECT) config.o
+CONFIG_OBJECTS = $(DBOBJECT) list.o md5.o debug.o dbmd5.o mime.o check.o rfc822.o memblock.o $(AUTHOBJECT) config.o
+USER_OBJECTS = debug.o list.o dbmd5.o md5.o $(DBOBJECT) mime.o check.o rfc822.o memblock.o $(AUTHOBJECT) config.o
+VUTCONV_OBJECTS = debug.o list.o dbmd5.o md5.o mime.o check.o rfc822.o $(DBOBJECT) $(AUTHOBJECT) config.o
 CC = cc
 
 DIRS = -L$(__LIBDIR__) -I$(__INCDIR__)
@@ -80,7 +80,9 @@
 serverchild.o: serverchild.h debug.h config.h list.h
 pipe.o: pipe.h config.h debug.h
 forward.o: forward.h config.h debug.h
-mime.o: mime.h config.h debug.h
+mime.o: mime.h debug.h check.h
+check.o: check.h rfc822.h
+rfc822.o: rfc822.h
 misc.o:misc.h config.h debug.h
 pop3.o:pop3.h config.h debug.h dbmailtypes.h
 dbmd5.o:dbmd5.h md5.h debug.h
diff -ru -I Id: -x CVS -N dbmail/check.c dbmail.working/check.c
--- dbmail/check.c	1970-01-01 12:00:00.000000000 +1200
+++ dbmail.working/check.c	2002-12-05 11:24:20.000000000 +1300
@@ -0,0 +1,131 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "debug.h"
+#include "rfc822.h"
+#include "check.h"
+
+/* Checks addresses
+ * Accepts: string pointer
+ *   if there are any errors the string is updated to
+ *   be on a 'correct' mail address format.
+ * Returns: number of parse errors
+ */
+
+int check_address(char *line)
+{
+   ADDRESS *adr, *adr_ptr;
+   char *line_cpy;
+   char *line_ptr=line;
+   int errors=0;
+   int first=0, personal=0;
+   int cnt=0;
+   line_cpy=(char *)my_malloc( strlen(line)+1);
+   strcpy (line_cpy, line);
+
+   adr = mail_newaddr ();
+   errors = rfc822_parse_adrlist(&adr, line_cpy, BADHOST);
+   adr_ptr = adr;
+
+   /* if errors, then try to set the from string to be an ok mailaddress */
+   if(errors>0)
+   {
+      while(adr) 
+      {
+         if(adr->mailbox && adr->host) 
+         {
+            if(adr->personal && first==0) 
+            {
+               cnt=sprintf(line, "%s <", remove_chr(adr->personal, '"'));
+               line+=cnt;
+               personal=1;
+            }
+            if(first==1) {
+               cnt=sprintf(line, ",");
+               line+=cnt;
+            }
+            cnt=sprintf(line, "%s@%s", adr->mailbox, adr->host);
+            line+=cnt;
+            first=1;
+         }               
+         adr = adr->next;
+      }
+      if(personal==1) {
+         cnt=sprintf(line, ">");
+         line+=cnt;
+      }
+      trace(TRACE_INFO, "check_address(): updated: [%s] > [%s]\n", line_cpy, line_ptr);
+   }
+   /* Remove '"' characters */
+   else
+   {
+      line = remove_chr(line, '"');
+   }
+   free(line_cpy);
+
+   return 0;
+}
+
+/* Compare two unsigned longs
+ * Accepts: first value
+ *	    second value
+ * Returns: -1 if l1 < l2, 0 if l1 == l2, 1 if l1 > l2
+ */
+
+int compare_ulong (unsigned long l1,unsigned long l2)
+{
+  if (l1 < l2) return -1;
+  if (l1 > l2) return 1;
+  return 0;
+}
+
+
+/* Compare two case-independent strings
+ * Accepts: first string
+ *	    second string
+ * Returns: -1 if s1 < s2, 0 if s1 == s2, 1 if s1 > s2
+ */
+
+int compare_cstring (char *s1,char *s2)
+{
+  int i;
+  if (!s1) return s2 ? -1 : 0;	/* empty string cases */
+  else if (!s2) return 1;
+  for (; *s1 && *s2; s1++,s2++)
+    if ((i = (compare_ulong (isupper (*s1) ? tolower (*s1) : *s1,
+			    isupper (*s2) ? tolower (*s2) : *s2))))
+      return i;			/* found a difference */
+  if (*s1) return 1;		/* first string is longer */
+  return *s2 ? -1 : 0;		/* second string longer : strings identical */
+}
+
+/* Parse a string and remove all c characters
+ * Accepts: string  string to be parsed
+ *          chr     character to remove
+ * Returns: Pointer to the parsed string
+ */
+char *remove_chr(char *string, int chr)
+{
+   char *ptr = string, *first = string;
+   int term=0;
+   
+   // step through the string
+   for (; *ptr; ptr++)
+   {
+      // when no match, just copy the character
+      if(*ptr!=chr)
+      {
+         *string=*ptr;
+         string++;
+      }
+      else
+         term++;
+   }
+   for(; term>0; term--)
+   {
+      *string='\0';
+      string++;
+   }
+   
+   return first;
+}
diff -ru -I Id: -x CVS -N dbmail/check.h dbmail.working/check.h
--- dbmail/check.h	1970-01-01 12:00:00.000000000 +1200
+++ dbmail.working/check.h	2002-12-05 10:49:44.000000000 +1300
@@ -0,0 +1,5 @@
+
+int check_address(char *line);
+int compare_ulong (unsigned long l1,unsigned long l2);
+int compare_cstring (char *s1,char *s2);
+char *remove_chr(char *string, int chr);
diff -ru -I Id: -x CVS -N dbmail/mime.c dbmail.working/mime.c
--- dbmail/mime.c	2002-12-05 11:39:50.000000000 +1300
+++ dbmail.working/mime.c	2002-12-05 11:37:54.000000000 +1300
@@ -6,6 +6,7 @@
 
 #include "mime.h"
 #include "debug.h"
+#include "check.h"
 #include <ctype.h>
 #include <stdlib.h>
 #include <string.h>
@@ -298,8 +299,17 @@
 /*	  strncpy(mr->field, startptr, MIME_FIELD_MAX);
 	  strncpy(mr->value, &delimiter[idx], MIME_VALUE_MAX);
 */
-/*	  trace (TRACE_DEBUG,"mime_readheader(): mimepair found: [%s] [%s] \n",mr->field, mr->value); 
+/*	  trace (TRACE_DEBUG,"mime_readheader(): mimepair found: [%s] [%s] \n",mr->field, mr->value);
 */
+     /* check the address fields, for address parse errors /Klas Handel */
+	  if(compare_cstring(mr->field, "to") == 0 ||
+	     compare_cstring(mr->field, "cc") == 0 ||
+	     compare_cstring(mr->field, "bcc") == 0 ||
+	     compare_cstring(mr->field, "from") == 0 ||
+	     compare_cstring(mr->field, "sender") == 0 ||
+	     compare_cstring(mr->field, "reply-to") == 0 )
+         check_address(mr->value);
+
 	  el = list_nodeadd(mimelist,mr,sizeof (*mr));
 	  if (!el)
 	    {
diff -ru -I Id: -x CVS -N dbmail/rfc822.c dbmail.working/rfc822.c
--- dbmail/rfc822.c	1970-01-01 12:00:00.000000000 +1200
+++ dbmail.working/rfc822.c	2002-12-05 10:49:44.000000000 +1300
@@ -0,0 +1,703 @@
+#include <stddef.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <syslog.h>
+#include "debug.h"
+#include "rfc822.h"
+
+char *errhst = ERRHOST;		/* syntax error host string */
+
+/* Once upon a time, CSnet had a mailer which assigned special semantics to
+ * dot in e-mail addresses.  For the sake of that mailer, dot was added to
+ * the RFC-822 definition of `specials', even though it had numerous bad side
+ * effects:
+ *   1)	It broke mailbox names on systems which had dots in user names, such as
+ *	Multics and TOPS-20.  RFC-822's syntax rules require that `Admin . MRC'
+ *	be considered equivalent to `Admin.MRC'.  Fortunately, few people ever
+ *	tried this in practice.
+ *   2) It required that all personal names with an initial be quoted, a widely
+ *	detested user interface misfeature.
+ *   3)	It made the parsing of host names be non-atomic for no good reason.
+ * To work around these problems, the following alternate specials lists are
+ * defined.  wspecials is used in lieu of rspecials, and ptspecials is used in
+ * lieu of tspecials.  These alternate specials lists make the parser work a
+ * lot better in the real world.  It ain't politically correct, but it lets
+ * the users get their job done!
+ */
+				
+/* parse-word specials */
+/* deleted '\"' from the original wspecials /Klas Handel
+ * otherwise addresses like: <"C:`CommercialAdzYenYen.txt"@smtp.orcon.net.nz>
+ *                           <"Undisclosed-Recipient:;"@smtp.orcon.net.nz>
+ * is returned to the mail client (e.g Outlook Express) but can't be read
+ */
+const char *wspecials = " ()<>@,;:\\[]\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\177";
+
+
+/* Skips RFC822 comment
+ * Accepts: pointer to string pointer
+ *	    trim flag
+ * Returns: pointer to first non-blank character of comment
+ */
+char *rfc822_skip_comment (char **s,long trim, int *errors)
+{
+  char *ret;
+  char *s1 = *s;
+  char *t = NIL;
+				/* skip past whitespace */
+  for (ret = ++s1; *ret == ' '; ret++);
+  do switch (*s1) {		/* get character of comment */
+  case '(':			/* nested comment? */
+    if (!rfc822_skip_comment (&s1,(long) NIL,errors)) return NIL;
+    t = --s1;			/* last significant char at end of comment */
+    break;
+  case ')':			/* end of comment? */
+    *s = ++s1;			/* skip past end of comment */
+    if (trim) {			/* if level 0, must trim */
+      if (t) t[1] = '\0';	/* tie off comment string */
+      else *ret = '\0';		/* empty comment */
+    }
+    return ret;
+  case '\\':			/* quote next character? */
+    if (*++s1) {		/* next character non-null? */
+      t = s1;			/* update last significant character pointer */
+      break;			/* all OK */
+    }
+  case '\0':			/* end of string */
+    trace(TRACE_INFO, "rfc822_skip_comment(): Unterminated comment: %.80s", *s);
+    ++*errors;
+    **s = '\0';			/* nuke duplicate messages in case reparse */
+    return NIL;			/* this is wierd if it happens */
+  case ' ':			/* whitespace isn't significant */
+    break;
+  default:			/* random character */
+    t = s1;			/* update last significant character pointer */
+    break;
+  } while (s1++);
+  return NIL;			/* impossible, but pacify lint et al */
+}
+
+/* Skips RFC822 whitespace
+ * Accepts: pointer to string pointer
+ */
+
+void rfc822_skipws (char **s, int *errors)
+{
+  while (T) {
+    if (**s == ' ') ++*s;	/* skip space */
+    else if ((**s != '(') || !rfc822_skip_comment (s,(long) NIL,errors)) return;
+  }
+}
+
+/* Default block notify routine
+ * Accepts: reason for calling
+ *	    data
+ * Returns: data
+ */
+
+void *mm_blocknotify (int reason, void *data)
+{
+  void *ret = data;
+  switch (reason) {
+  case BLOCK_SENSITIVE:		/* entering sensitive code */
+    ret = (void *) alarm (0);
+    break;
+  case BLOCK_NONSENSITIVE:	/* exiting sensitive code */
+    if ((unsigned int) data) alarm ((unsigned int) data);
+    break;
+  default:			/* ignore all other reasons */
+    break;
+  }
+  return ret;
+}
+
+/* Get a block of free storage
+ * Accepts: size of desired block
+ * Returns: free storage block
+ */
+void *fs_get (size_t size)
+{
+  void *data = mm_blocknotify (BLOCK_SENSITIVE,NIL);
+  void *block = malloc (size ? size : (size_t) 1);
+  if (!block)
+    trace (TRACE_FATAL, "fs_resize: Out of memory");
+  mm_blocknotify (BLOCK_NONSENSITIVE, data);
+  return (block);
+}
+
+
+/* Resize a block of free storage
+ * Accepts: ** pointer to current block
+ *	    new size
+ */
+
+void fs_resize (void **block,size_t size)
+{
+  void *data = mm_blocknotify (BLOCK_SENSITIVE,NIL);  
+  if (!(*block = realloc (*block,size ? size : (size_t) 1)))
+    trace (TRACE_FATAL, "fs_resize: Can't resize memory");
+  mm_blocknotify (BLOCK_NONSENSITIVE,data);
+}
+
+
+/* Return a block of free storage
+ * Accepts: ** pointer to free storage block
+ */
+
+void fs_give (void **block)
+{
+  void *data = mm_blocknotify (BLOCK_SENSITIVE,NIL);
+  free (*block);
+  *block = NIL;
+  mm_blocknotify (BLOCK_NONSENSITIVE,data);
+}
+
+/* Copy string to free storage
+ * Accepts: source string
+ * Returns: free storage copy of string
+ */
+
+char *cpystr (const char *string)
+{
+  return string ? strcpy ((char *) fs_get (1 + strlen (string)),string) : NIL;
+}
+
+/* Mail instantiate address
+ * Returns: new address
+ */
+
+ADDRESS *mail_newaddr (void)
+{
+  return (ADDRESS *) memset (fs_get (sizeof (ADDRESS)),0,sizeof (ADDRESS));
+}
+
+/* Copy an RFC822 format string
+ * Accepts: string
+ * Returns: copy of string
+ */
+
+char *rfc822_cpy (char *src)
+{
+				/* copy and unquote */
+  return rfc822_quote (cpystr (src));
+}
+
+
+/* Unquote an RFC822 format string
+ * Accepts: string
+ * Returns: string
+ */
+
+char *rfc822_quote (char *src)
+{
+  char *ret = src;
+  if (strpbrk (src,"\\\"")) {	/* any quoting in string? */
+    char *dst = ret;
+    while (*src) {		/* copy string */
+      if (*src == '\"') src++;	/* skip double quote entirely */
+      else {
+	if (*src == '\\') src++;/* skip over single quote, copy next always */
+	*dst++ = *src++;	/* copy character */
+      }
+    }
+    *dst = '\0';		/* tie off string */
+  }
+  return ret;			/* return our string */
+}
+
+/* Parse RFC822 route-address
+ * Accepts: string pointer
+ *	    pointer to string pointer to update
+ * Returns: address
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost, int *errors)
+{
+  char tmp[MAILTMPLEN];
+  ADDRESS *adr;
+  char *s,*t,*adl;
+  size_t adllen,i;
+  if (!string) return NIL;
+  rfc822_skipws (&string,errors);	/* flush leading whitespace */
+				/* must start with open broket */
+  if (*string != '<') return NIL;
+  t = ++string;			/* see if A-D-L there */
+  rfc822_skipws (&t,errors);		/* flush leading whitespace */
+  for (adl = NIL,adllen = 0;	/* parse possible A-D-L */
+       (*t == '@') && (s = rfc822_parse_domain (t+1,&t,errors));) {
+    i = strlen (s) + 2;		/* @ plus domain plus delimiter or NUL */
+    if (adl) {			/* have existing A-D-L? */
+      fs_resize ((void **) &adl,adllen + i);
+      sprintf (adl + adllen - 1,",@%s",s);
+    }
+				/* write initial A-D-L */
+    else sprintf (adl = (char *) fs_get (i),"@%s",s);
+    adllen += i;		/* new A-D-L length */
+    fs_give ((void **) &s);	/* don't need domain any more */
+    rfc822_skipws (&t,errors);		/* skip WS */
+    if (*t != ',') break;	/* put if not comma */
+    t++;			/* skip the comma */
+    rfc822_skipws (&t,errors);		/* skip WS */
+  }
+  if (adl) {			/* got an A-D-L? */
+    if (*t != ':') {		/* make sure syntax good */
+      trace(TRACE_INFO, "rfc822_parse_routeaddr(): Unterminated at-domain-list: %.80s%.80s", adl, t);
+      ++*errors;
+    }
+    else string = ++t;		/* continue parse from this point */
+  }
+				/* parse address spec */
+  if (!(adr = rfc822_parse_addrspec (string,ret,defaulthost,errors))) {
+    if (adl) fs_give ((void **) &adl);
+    return NIL;
+  }
+  if (adl) adr->adl = adl;	/* have an A-D-L? */
+  if (*ret) if (**ret == '>') {	/* make sure terminated OK */
+    ++*ret;			/* skip past the broket */
+    rfc822_skipws (ret,errors);	/* flush trailing WS */
+    if (!**ret) *ret = NIL;	/* wipe pointer if at end of string */
+    return adr;			/* return the address */
+  }
+  sprintf (tmp,"Unterminated mailbox: %.80s@%.80s",adr->mailbox,
+	   *adr->host == '@' ? "<null>" : adr->host);
+  trace(TRACE_INFO, "rfc822_parse_routeaddr(): %s", tmp);
+  ++*errors;
+  adr->next = mail_newaddr ();
+  adr->next->mailbox = cpystr ("MISSING_MAILBOX_TERMINATOR");
+  adr->next->host = cpystr (errhst);
+  return adr;			/* return the address */
+}
+
+
+/* Parse RFC822 word
+ * Accepts: string pointer
+ *	    delimiter (or NIL for phrase word parsing)
+ * Returns: pointer to end of word
+ */
+
+char *rfc822_parse_word (char *s,const char *delimiters, int *errors)
+{
+  char *st,*str;
+  if (!s) return NIL;		/* no string */
+  rfc822_skipws (&s,errors);		/* flush leading whitespace */
+  if (!*s) return NIL;		/* empty string */
+  str = s;			/* hunt pointer for strpbrk */
+  while (T) {			/* look for delimiter, return if none */
+    if (!(st = strpbrk (str,delimiters ? delimiters : wspecials)))
+      return str + strlen (str);
+				/* ESC in phrase */
+    if (!delimiters && (*st == I2C_ESC)) {
+      str = ++st;		/* always skip past ESC */
+      switch (*st) {		/* special hack for RFC 1468 (ISO-2022-JP) */
+      case I2C_MULTI:		/* multi byte sequence */
+	switch (*++st) {
+	case I2CS_94x94_JIS_OLD:/* old JIS (1978) */
+	case I2CS_94x94_JIS_NEW:/* new JIS (1983) */
+	  str = ++st;		/* skip past the shift to JIS */
+	  while ((st = strchr (st,I2C_ESC)))
+	    if ((*++st == I2C_G0_94) && ((st[1] == I2CS_94_ASCII) ||
+					 (st[1] == I2CS_94_JIS_ROMAN) ||
+					 (st[1] == I2CS_94_JIS_BUGROM))) {
+	      str = st += 2;	/* skip past the shift back to ASCII */
+	      break;
+	    }
+				/* eats entire text if no shift back */
+	  if (!st || !*st) return str + strlen (str);
+	}
+	break;
+      case I2C_G0_94:		/* single byte sequence */
+	switch (st[1]) {
+	case I2CS_94_ASCII:	/* shift to ASCII */
+	case I2CS_94_JIS_ROMAN:	/* shift to JIS-Roman */
+	case I2CS_94_JIS_BUGROM:/* old buggy definition of JIS-Roman */
+	  str = st + 2;		/* skip past the shift */
+	  break;
+	}
+      }
+    }
+    else switch (*st) {		/* dispatch based on delimiter */
+    case '"':			/* quoted string */
+				/* look for close quote */
+      while (*++st != '"') switch (*st) {
+      case '\0':		/* unbalanced quoted string */
+	return NIL;		/* sick sick sick */
+      case '\\':		/* quoted character */
+	if (!*++st) return NIL;	/* skip the next character */
+      default:			/* ordinary character */
+	break;			/* no special action */
+      }
+      str = ++st;		/* continue parse */
+      break;
+    case '\\':			/* quoted character */
+      /* This is wrong; a quoted-pair can not be part of a word.  However,
+       * domain-literal is parsed as a word and quoted-pairs can be used
+       * *there*.  Either way, it's pretty pathological.
+       */
+      if (st[1]) {		/* not on NUL though... */
+	str = st + 2;		/* skip quoted character and go on */
+	break;
+      }
+    default:			/* found a word delimiter */
+      return (st == s) ? NIL : st;
+    }
+  }
+}
+
+
+/* Parse RFC822 phrase
+ * Accepts: string pointer
+ * Returns: pointer to end of phrase
+ */
+
+char *rfc822_parse_phrase (char *s, int *errors)
+{
+  char *curpos;
+  if (!s) return NIL;		/* no-op if no string */
+				/* find first word of phrase */
+  curpos = rfc822_parse_word (s,NIL,errors);
+  if (!curpos) return NIL;	/* no words means no phrase */
+  if (!*curpos) return curpos;	/* check if string ends with word */
+  s = curpos;			/* sniff past the end of this word and WS */
+  rfc822_skipws (&s,errors);		/* skip whitespace */
+				/* recurse to see if any more */
+  return (s = rfc822_parse_phrase (s,errors)) ? s : curpos;
+}
+
+/* Parse RFC822 domain
+ * Accepts: string pointer
+ *	    pointer to return end of domain
+ * Returns: domain name or NIL if failure
+ */
+
+char *rfc822_parse_domain (char *string,char **end, int *errors)
+{
+  char *ret = NIL;
+  char c,*s,*t,*v;
+  rfc822_skipws (&string,errors);	/* skip whitespace */
+  if (*string == '[') {		/* domain literal? */
+    if (!(*end = rfc822_parse_word (string + 1,"]\\",errors)))
+    {
+      trace(TRACE_INFO, "rfc822_parse_domain(): Empty domain literal");
+      ++*errors;
+    }    
+    else if (**end != ']') 
+    {
+      trace(TRACE_INFO, "rfc822_parse_domain(): Unterminated domain literal");
+      ++*errors;
+    }
+    else {
+      size_t len = ++*end - string;
+      strncpy (ret = (char *) fs_get (len + 1),string,len);
+      ret[len] = '\0';		/* tie off literal */
+    }
+  }
+				/* search for end of host */
+  else if ((t = rfc822_parse_word (string,wspecials,errors))) {
+    c = *t;			/* remember delimiter */
+    *t = '\0';			/* tie off host */
+    ret = rfc822_cpy (string);	/* copy host */
+    *t = c;			/* restore delimiter */
+    *end = t;			/* remember end of domain */
+    rfc822_skipws (&t,errors);		/* skip WS after host */
+    while (*t == '.') {		/* some cretin taking RFC 822 too seriously? */
+      string = ++t;		/* skip past the dot and any WS */
+      rfc822_skipws (&string,errors);
+      if ((string = rfc822_parse_domain (string,&t,errors))) {
+	*end = t;		/* remember new end of domain */
+	c = *t;			/* remember delimiter */
+	*t = '\0';		/* tie off host */
+	s = rfc822_cpy (string);/* copy successor part */
+	*t = c;			/* restore delimiter */
+				/* build new domain */
+	sprintf (v = (char *) fs_get (strlen (ret) + strlen (s) + 2),
+		 "%s.%s",ret,s);
+	fs_give ((void **) &ret);
+	ret = v;		/* new host name */
+	rfc822_skipws (&t,errors);	/* skip WS after domain */
+      }
+      else {			/* barf */
+         trace(TRACE_INFO, "rfc822_parse_domain(): Invalid domain part after .");
+         ++*errors;
+	      break;
+      }
+    }
+  }
+  else {
+    trace(TRACE_INFO, "rfc822_parse_domain(): Missing or invalid host name after @");
+    ++*errors;
+  }    
+  return ret;
+}
+
+/* Parse RFC822 address-spec
+ * Accepts: string pointer
+ *	    pointer to string pointer to update
+ *	    default host
+ * Returns: address
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost, int *errors)
+{
+  ADDRESS *adr;
+  char c,*s,*t,*v,*end;
+  if (!string) return NIL;	/* no string */
+  rfc822_skipws (&string,errors);	/* flush leading whitespace */
+  if (!*string) return NIL;	/* empty string */
+				/* find end of mailbox */
+  if (!(t = rfc822_parse_word (string,wspecials,errors))) return NIL;
+  adr = mail_newaddr ();	/* create address block */
+  c = *t;			/* remember delimiter */
+  *t = '\0';			/* tie off mailbox */
+				/* copy mailbox */
+  adr->mailbox = rfc822_cpy (string);
+  *t = c;			/* restore delimiter */
+  end = t;			/* remember end of mailbox */
+  rfc822_skipws (&t,errors);		/* skip whitespace */
+  while (*t == '.') {		/* some cretin taking RFC 822 too seriously? */
+    string = ++t;		/* skip past the dot and any WS */
+    rfc822_skipws (&string,errors);
+				/* get next word of mailbox */
+    if ((t = rfc822_parse_word (string,wspecials,errors))) {
+      end = t;			/* remember new end of mailbox */
+      c = *t;			/* remember delimiter */
+      *t = '\0';		/* tie off word */
+      s = rfc822_cpy (string);	/* copy successor part */
+      *t = c;			/* restore delimiter */
+				/* build new mailbox */
+      sprintf (v = (char *) fs_get (strlen (adr->mailbox) + strlen (s) + 2),
+	       "%s.%s",adr->mailbox,s);
+      fs_give ((void **) &adr->mailbox);
+      adr->mailbox = v;		/* new host name */
+      rfc822_skipws (&t,errors);	/* skip WS after mailbox */
+    }
+    else {			/* barf */
+      trace(TRACE_INFO, "rfc822_parse_addrspec(): Invalid mailbox part after .");
+      ++*errors;
+      break;
+    }
+  }
+  t = end;			/* remember delimiter in case no host */
+  rfc822_skipws (&end,errors);		/* sniff ahead at what follows */
+  if (*end != '@') end = t;	/* host name missing */
+				/* otherwise parse host name */
+  else if (!(adr->host = rfc822_parse_domain (++end,&end,errors)))
+    adr->host = cpystr (errhst);
+				/* default host if missing */
+  if (!adr->host) adr->host = cpystr (defaulthost);
+  if (end && !adr->personal) {	/* try person name in comments if missing */
+    while (*end == ' ') ++end;	/* see if we can find a person name here */
+    if ((*end == '(') && (s = rfc822_skip_comment (&end,LONGT,errors)) && strlen (s))
+      adr->personal = rfc822_cpy (s);
+    rfc822_skipws (&end,errors);	/* skip any other WS in the normal way */
+  }
+				/* set return to end pointer */
+  *ret = (end && *end) ? end : NIL;
+  return adr;			/* return the address we got */
+}
+
+
+/* Parse RFC822 mailbox
+ * Accepts: pointer to string pointer
+ *	    default host
+ * Returns: address list
+ *
+ * Updates string pointer
+ */
+
+ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost, int *errors)
+{
+  ADDRESS *adr = NIL;
+  char *s,*end;
+/*  parsephrase_t pp = (parsephrase_t) mail_parameters (NIL,GET_PARSEPHRASE,NIL); */
+  if (!*string) return NIL;	/* no string */
+  rfc822_skipws (string,errors);	/* flush leading whitespace */
+  if (!**string) return NIL;	/* empty string */
+  if (*(s = *string) == '<') 	/* note start, handle case of phraseless RA */
+    adr = rfc822_parse_routeaddr (s,string,defaulthost,errors);
+				/* otherwise, expect at least one word */
+  else if ((end = rfc822_parse_phrase (s,errors))) {
+    if ((adr = rfc822_parse_routeaddr (end,string,defaulthost,errors))) {
+				/* phrase is a personal name */
+      if (adr->personal) fs_give ((void **) &adr->personal);
+      *end = '\0';		/* tie off phrase */
+      adr->personal = rfc822_cpy (s);
+    }
+    else adr = rfc822_parse_addrspec (s,string,defaulthost,errors);
+  }
+  return adr;			/* return the address */
+}
+
+
+/* Parse RFC822 group
+ * Accepts: address list to write to
+ *	    pointer to tail of address list
+ *	    pointer to input string
+ *	    default host name
+ *	    group nesting depth
+ */
+
+ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
+			     char *defaulthost,unsigned long depth, int *errors)
+{
+  char *p,*s;
+  ADDRESS *adr;
+  if (depth > MAXGROUPDEPTH) {	/* excessively deep recursion? */
+    trace(TRACE_INFO, "rfc822_parse_group(): Ignoring excessively deep group recursion");
+    ++*errors;
+    return NIL;			/* probably abusive */
+  }
+  if (!*string) return NIL;	/* no string */
+  rfc822_skipws (string,errors);	/* skip leading WS */
+  if (!**string ||		/* trailing whitespace or not group */
+      ((*(p = *string) != ':') && !(p = rfc822_parse_phrase (*string,errors))))
+    return NIL;
+  s = p;			/* end of candidate phrase */
+  rfc822_skipws (&s,errors);		/* find delimiter */
+  if (*s != ':') return NIL;	/* not really a group */
+  *p = '\0';			/* tie off group name */
+  p = ++s;			/* continue after the delimiter */
+  rfc822_skipws (&p,errors);		/* skip subsequent whitespace */
+				/* write as address */
+  (adr = mail_newaddr ())->mailbox = rfc822_cpy (*string);
+  if (!*lst) *lst = adr;	/* first time through? */
+  else last->next = adr;	/* no, append to the list */
+  last = adr;			/* set for subsequent linking */
+  *string = p;			/* continue after this point */
+  while (*string && **string && (**string != ';')) {
+    if ((adr = rfc822_parse_address (lst,last,string,defaulthost,depth+1,errors))) {
+      last = adr;		/* new tail address */
+      if (*string) {		/* anything more? */
+	rfc822_skipws (string,errors);	/* skip whitespace */
+	switch (**string) {	/* see what follows */
+	case ',':		/* another address? */
+	  ++*string;		/* yes, skip past the comma */
+	case ';':		/* end of group? */
+	case '\0':		/* end of string */
+	  break;
+	default:
+     trace(TRACE_INFO, "rfc822_parse_group(): Unexpected characters after address in group: %.80s", *string);
+     ++*errors;
+	  *string = NIL;	/* cancel remainder of parse */
+	  last = last->next = mail_newaddr ();
+	  last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS_IN_GROUP");
+	  last->host = cpystr (errhst);
+	}
+      }
+    }
+    else {			/* bogon */
+      trace(TRACE_INFO, "rfc822_parse_group(): Invalid group mailbox list: %.80s", *string);
+      ++*errors;
+      *string = NIL;		/* cancel remainder of parse */
+      (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS_IN_GROUP");
+      adr->host = cpystr (errhst);
+      last = last->next = adr;
+    }
+  }
+  if (*string) {		/* skip close delimiter */
+    if (**string == ';') ++*string;
+    rfc822_skipws (string,errors);
+  }
+				/* append end of address mark to the list */
+  last->next = (adr = mail_newaddr ());
+  last = adr;			/* set for subsequent linking */
+  return last;			/* return the tail */
+}
+
+
+/* Parse RFC822 address
+ * Accepts: address list to write to
+ *	    tail of address list
+ *	    pointer to input string
+ *	    default host name
+ *	    group nesting depth
+ * Returns: new list tail
+ */
+
+ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
+			       char *defaulthost,unsigned long depth, int *errors)
+{
+  ADDRESS *adr;
+  if (!*string) return NIL;	/* no string */
+  rfc822_skipws (string,errors);	/* skip leading WS */
+  if (!**string) return NIL;	/* empty string */
+  if ((adr = rfc822_parse_group (lst,last,string,defaulthost,depth,errors))) 
+    last = adr;
+				/* got an address? */
+  else if ((adr = rfc822_parse_mailbox (string,defaulthost,errors))) {
+    if (!*lst) *lst = adr;	/* yes, first time through? */
+    else last->next = adr;	/* no, append to the list */
+				/* set for subsequent linking */
+    for (last = adr; last->next; last = last->next);
+  }
+  else if (*string) return NIL;
+  return last;
+}
+
+
+/* Parse RFC822 address list
+ * Accepts: address list to write to
+ *	    input string
+ *	    default host name
+ */
+int rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host)
+{
+  int errors = 0;
+  char c,*s,tmp[MAILTMPLEN];
+  ADDRESS *last = *lst;
+  ADDRESS *adr;
+  if (!string) return errors;		/* no string */
+  rfc822_skipws (&string,&errors);	/* skip leading WS */
+  if (!*string) return errors;		/* empty string */
+				/* run to tail of list */
+  if (last) while (last->next) last = last->next;
+  while (string) {		/* loop until string exhausted */
+				/* got an address? */
+    if ((adr = rfc822_parse_address (lst,last,&string,host,0,&errors))) {
+      last = adr;		/* new tail address */
+      if (string) {		/* analyze what follows */
+	rfc822_skipws (&string,&errors);
+	switch (c = *string) {
+	case ',':		/* comma? */
+	  ++string;		/* then another address follows */
+	  break;
+	default:
+	  s = isalnum (c) ? "Must use comma to separate addresses: %.80s" :
+	    "Unexpected characters at end of address: %.80s";
+	  sprintf (tmp,s,string);
+     trace(TRACE_INFO, "rfc822_parse_adrlist(): %s", tmp);
+     ++errors;
+	  last = last->next = mail_newaddr ();
+	  last->mailbox = cpystr ("UNEXPECTED_DATA_AFTER_ADDRESS");
+	  last->host = cpystr (errhst);
+				/* falls through */
+	case '\0':		/* null-specified address? */
+	  string = NIL;		/* punt remainder of parse */
+	  break;
+	}
+      }
+    }
+    else if (string) {		/* bad mailbox */
+      rfc822_skipws (&string,&errors);	/* skip WS */
+      if (!*string) 
+        strcpy (tmp,"Missing address after comma");
+      else 
+        sprintf (tmp,"Invalid mailbox list: %.80s",string);
+      trace(TRACE_INFO, "rfc822_parse_adrlist(): %s", tmp);
+      ++errors;
+      string = NIL;
+      (adr = mail_newaddr ())->mailbox = cpystr ("INVALID_ADDRESS");
+      adr->host = cpystr (errhst);
+      if (last) last = last->next = adr;
+      else *lst = last = adr;
+      break;
+    }
+  }
+  return errors;
+}
diff -ru -I Id: -x CVS -N dbmail/rfc822.h dbmail.working/rfc822.h
--- dbmail/rfc822.h	1970-01-01 12:00:00.000000000 +1200
+++ dbmail.working/rfc822.h	2002-12-05 10:49:44.000000000 +1300
@@ -0,0 +1,61 @@
+#define MAXGROUPDEPTH 50   /* RFC 822 doesn't allow nesting at all */
+#define MAILTMPLEN 1024    /* size of a temporary buffer */
+
+#define BADHOST ".MISSING-HOST-NAME."     /* default for unqualified addresses */
+#define ERRHOST ".SYNTAX-ERROR."          /* default for syntax errors in addresses */
+
+#define NIL 0              /* convenient name */
+#define T 1                /* opposite of NIL */
+#define LONGT (long) 1     /* long T */
+
+#define BLOCK_SENSITIVE 1     /* sensitive code, disallow alarms */
+#define BLOCK_NONSENSITIVE 2  /* non-sensitive code, allow alarms */
+#define LOG_ALERT 1           /* action must be taken immediately */
+
+/* ISO-2022 octet definitions */
+#define I2C_ESC 0x1b       /* ESCape */
+#define I2C_MULTI 0x24     /* multi-byte character set */
+/* 94x94 character sets */
+#define I2CS_94x94_JIS_OLD 0x40  /* 4/0 JIS X 0208-1978 */
+#define I2CS_94x94_JIS_NEW 0x42  /* 4/2 JIS X 0208-1983 */
+#define I2C_G0_94 0x28           /* G0 94-character set */
+#define I2CS_94_ASCII 0x42       /* 4/2 ISO 646 USA (ASCII) */
+#define I2CS_94_JIS_ROMAN 0x4a   /* 4/a JIS X 0201-1976 left half */
+#define I2CS_94_JIS_BUGROM 0x48  /* 4/8 some buggy software does this */
+
+#define ADDRESS struct mail_address
+ADDRESS {
+   char *personal;   /* personal name phrase */
+   char *adl;        /* at-domain-list source route */
+   char *mailbox;    /* mailbox name */
+   char *host;       /* domain name of mailbox's host */
+   char *error;      /* error in address from SMTP module */
+   ADDRESS *next;    /* pointer to next address in list */
+};
+
+int rfc822_parse_adrlist (ADDRESS **lst,char *string,char *host);
+
+
+ADDRESS *mail_newaddr (void);
+ADDRESS *rfc822_parse_address (ADDRESS **lst,ADDRESS *last,char **string,
+char *defaulthost,unsigned long depth, int *errors);
+ADDRESS *rfc822_parse_group (ADDRESS **lst,ADDRESS *last,char **string,
+char *defaulthost,unsigned long depth, int *errors);
+ADDRESS *rfc822_parse_mailbox (char **string,char *defaulthost, int *errors);
+ADDRESS *rfc822_parse_addrspec (char *string,char **ret,char *defaulthost, int *errors);
+ADDRESS *rfc822_parse_routeaddr (char *string,char **ret,char *defaulthost, int *errors);
+
+char *rfc822_parse_domain (char *string,char **end, int *errors);
+char *rfc822_parse_phrase (char *s,int *errors);
+char *rfc822_parse_word (char *s,const char *delimiters, int *errors);
+char *rfc822_quote (char *src);
+char *rfc822_cpy (char *src);
+void rfc822_skipws (char **s, int *errors);
+char *rfc822_skip_comment (char **s,long trim, int *errors);
+
+char *cpystr (const char *string);
+
+void fs_give (void **block);
+void fs_resize (void **block,size_t size);
+void *fs_get (size_t size);
+void *mm_blocknotify (int reason,void *data);
