diff -crN ../qmail-1.03/Makefile ./Makefile
*** ../qmail-1.03/Makefile	Mon Jun 15 05:53:16 1998
--- ./Makefile	Sun Feb 17 21:57:40 2002
***************
*** 136,141 ****
--- 136,145 ----
  compile auto_usera.c
  	./compile auto_usera.c
  
+ base64.o: \
+ compile base64.c base64.h stralloc.h substdio.h str.h
+ 	./compile base64.c
+ 
  binm1: \
  binm1.sh conf-qmail
  	cat binm1.sh \
***************
*** 1536,1547 ****
  timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
  date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
  open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
! fs.a auto_qmail.o socket.lib
  	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
  	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
  	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
  	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
! 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o  `cat \
  	socket.lib`
  
  qmail-smtpd.0: \
--- 1540,1551 ----
  timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o received.o \
  date822fmt.o now.o qmail.o cdb.a fd.a wait.a datetime.a getln.a \
  open.a sig.a case.a env.a stralloc.a alloc.a substdio.a error.a str.a \
! fs.a auto_qmail.o base64.o socket.lib
  	./load qmail-smtpd rcpthosts.o commands.o timeoutread.o \
  	timeoutwrite.o ip.o ipme.o ipalloc.o control.o constmap.o \
  	received.o date822fmt.o now.o qmail.o cdb.a fd.a wait.a \
  	datetime.a getln.a open.a sig.a case.a env.a stralloc.a \
! 	alloc.a substdio.a error.a str.a fs.a auto_qmail.o base64.o  `cat \
  	socket.lib`
  
  qmail-smtpd.0: \
***************
*** 1553,1559 ****
  substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
  error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
  substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
! exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h
  	./compile qmail-smtpd.c
  
  qmail-start: \
--- 1557,1564 ----
  substdio.h alloc.h auto_qmail.h control.h received.h constmap.h \
  error.h ipme.h ip.h ipalloc.h ip.h gen_alloc.h ip.h qmail.h \
  substdio.h str.h fmt.h scan.h byte.h case.h env.h now.h datetime.h \
! exit.h rcpthosts.h timeoutread.h timeoutwrite.h commands.h wait.h \
! fd.h base64.h
  	./compile qmail-smtpd.c
  
  qmail-start: \
diff -crN ../qmail-1.03/TARGETS ./TARGETS
*** ../qmail-1.03/TARGETS	Mon Jun 15 05:53:16 1998
--- ./TARGETS	Sun Feb 17 21:57:40 2002
***************
*** 250,255 ****
--- 250,256 ----
  qmail-qmtpd.o
  rcpthosts.o
  qmail-qmtpd
+ base64.o
  qmail-smtpd.o
  qmail-smtpd
  sendmail.o
diff -crN ../qmail-1.03/base64.c ./base64.c
*** ../qmail-1.03/base64.c	Wed Dec 31 18:00:00 1969
--- ./base64.c	Sun Feb 17 21:57:57 2002
***************
*** 0 ****
--- 1,90 ----
+ #include "base64.h"
+ #include "stralloc.h"
+ #include "substdio.h"
+ #include "str.h"
+ 
+ static char *b64alpha =
+   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ #define B64PAD '='
+ 
+ /* returns 0 ok, 1 illegal, -1 problem */
+ 
+ int b64decode(in,l,out)
+ const unsigned char *in;
+ int l;
+ stralloc *out; /* not null terminated */
+ {
+   int i, j;
+   unsigned char a[4];
+   unsigned char b[3];
+   char *s;
+ 
+   if (l == 0)
+   {
+     if (!stralloc_copys(out,"")) return -1;
+     return 0;
+   }
+ 
+   if (!stralloc_ready(out,l + 2)) return -1; /* XXX generous */
+   s = out->s;
+ 
+   for (i = 0;i < l;i += 4) {
+     for (j = 0;j < 4;j++)
+       if ((i + j) < l && in[i + j] != B64PAD)
+       {
+         a[j] = str_chr(b64alpha,in[i + j]);
+         if (a[j] > 63) return 1;
+       }
+       else a[j] = 0;
+ 
+     b[0] = (a[0] << 2) | (a[1] >> 4);
+     b[1] = (a[1] << 4) | (a[2] >> 2);
+     b[2] = (a[2] << 6) | (a[3]);
+ 
+     *s++ = b[0];
+ 
+     if (in[i + 1] == B64PAD) break;
+     *s++ = b[1];
+ 
+     if (in[i + 2] == B64PAD) break;
+     *s++ = b[2];
+   }
+   out->len = s - out->s;
+   while (out->len && !out->s[out->len - 1]) --out->len; /* XXX avoid? */
+   return 0;
+ }
+ 
+ int b64encode(in,out)
+ stralloc *in;
+ stralloc *out; /* not null terminated */
+ {
+   unsigned char a, b, c;
+   int i;
+   char *s;
+ 
+   if (in->len == 0)
+   {
+     if (!stralloc_copys(out,"")) return -1;
+     return 0;
+   }
+ 
+   if (!stralloc_ready(out,in->len / 3 * 4 + 4)) return -1;
+   s = out->s;
+ 
+   for (i = 0;i < in->len;i += 3) {
+     a = in->s[i];
+     b = i + 1 < in->len ? in->s[i + 1] : 0;
+     c = i + 2 < in->len ? in->s[i + 2] : 0;
+ 
+     *s++ = b64alpha[a >> 2];
+     *s++ = b64alpha[((a & 3 ) << 4) | (b >> 4)];
+ 
+     if (i + 1 >= in->len) *s++ = B64PAD;
+     else *s++ = b64alpha[((b & 15) << 2) | (c >> 6)];
+ 
+     if (i + 2 >= in->len) *s++ = B64PAD;
+     else *s++ = b64alpha[c & 63];
+   }
+   out->len = s - out->s;
+   return 0;
+ }
diff -crN ../qmail-1.03/base64.h ./base64.h
*** ../qmail-1.03/base64.h	Wed Dec 31 18:00:00 1969
--- ./base64.h	Sun Feb 17 21:57:57 2002
***************
*** 0 ****
--- 1,7 ----
+ #ifndef BASE64_H
+ #define BASE64_H
+ 
+ extern int b64decode();
+ extern int b64encode();
+ 
+ #endif
diff -crN ../qmail-1.03/functions.c ./functions.c
*** ../qmail-1.03/functions.c	Wed Dec 31 18:00:00 1969
--- ./functions.c	Sun Feb 17 22:04:44 2002
***************
*** 0 ****
--- 1,457 ----
+ #include <sys/types.h>
+ #include <string.h>
+ #include <ctype.h>
+ #include <unistd.h>
+ 
+ #include "md5.h"
+ #include "functions.h"
+ 
+ #ifndef VPOPMAIL
+ char *dec2hex(unsigned char *digest)
+   {
+     static char ascii[33];
+     char *hex="0123456789abcdef";
+     int i,j,k;
+ 
+     memset(ascii,0,sizeof(ascii));
+ 
+     for (i=0; i < 16; i++) 
+       {
+         j = digest[i]/16;
+         k = digest[i]%16;
+         ascii[i*2] = hex[j];
+         ascii[(i*2)+1] = hex[k];
+       }
+ 
+     return ascii;
+   }
+ #endif
+ 
+ void HMAC_MD5(text, text_len, key, key_len, digest)
+   unsigned char  *text;                 /* pointer to data stream */
+   int             text_len;             /* length of data stream */
+   unsigned char   *key;                  /* pointer to authentication key */
+   int             key_len;              /* length of authentication key */
+   unsigned char   *digest;               /* caller digest to be filled in */
+                      
+   {
+         MD5_CTX context;
+         unsigned char k_ipad[65];     /* inner padding - key XORd with ipad */
+         unsigned char k_opad[65];     /* outer padding - key XORd with opad */
+         unsigned char tk[16];
+         int i;
+  
+         /* sanity check parameters */
+         if (!text || !key || !digest )
+                 /* most heinous, probably should log something */
+                 return;
+  
+         /* if key is longer than 64 bytes reset it to key=MD5(key) */
+         if (key_len > 64) 
+ 	  {
+  
+                 MD5_CTX      tctx;
+  
+                 MD5Init(&tctx);
+                 MD5Update(&tctx, key, key_len);
+                 MD5Final(tk, &tctx);
+  
+                 key = tk;
+                 key_len = 16;
+           }
+  
+         /*
+          * the HMAC_MD5 transform looks like:
+          *
+          * MD5(K XOR opad, MD5(K XOR ipad, text))
+          *
+          * where K is an n byte key
+          * ipad is the byte 0x36 repeated 64 times
+          * opad is the byte 0x5c repeated 64 times
+          * and text is the data being protected
+          */
+ 
+  
+         /* start out by storing key in pads */
+ 
+         bzero( k_ipad, sizeof k_ipad);
+         bzero( k_opad, sizeof k_opad);
+         bcopy( key, k_ipad, key_len);
+         bcopy( key, k_opad, key_len);
+  
+         /* XOR key with ipad and opad values */
+         for (i=0; i<64; i++) 
+ 	  {
+                 k_ipad[i] ^= 0x36;
+                 k_opad[i] ^= 0x5c;
+           }
+         /*
+          * perform inner MD5
+          */
+         MD5Init(&context);                    /* init context for 1st pass */
+         MD5Update(&context, k_ipad, 64);       /* start with inner pad */
+         MD5Update(&context, text, text_len);  /* then text of datagram */
+         MD5Final(digest, &context);           /* finish up 1st pass */
+         /*
+          * perform outer MD5
+          */
+         MD5Init(&context);                    /* init context for 2nd pass */
+         MD5Update(&context, k_opad, 64);      /* start with outer pad */
+         MD5Update(&context, digest, 16);      /* then results of 1st hash */
+         MD5Final(digest, &context);           /* finish up 2nd pass */
+   }
+ 
+ 
+ int WordCount(char *str)
+   {
+     int cnt=0;
+     char inword=1, *tmp=str, found=0;
+     
+     if(!str || !*str)
+       return 0;
+ 
+     while(isspace(*tmp))  // skipping first spaces
+       tmp++;
+     
+     while(*tmp)
+       {
+         if( isspace(*tmp) )
+ 	  {
+ 	    if(inword)
+ 	      {
+ 	        inword = 0;
+ 		cnt++;
+ 		found = 1;
+ 	      }
+ 	  }
+ 	else inword = 1;
+ 
+ 	tmp++;
+       }
+ 
+     if( (found && inword) || (cnt == 0 && inword) )
+       cnt++;
+ 
+     return cnt;
+   }
+ 
+ int GetWordN(char *Word, char *str, int n, int Max)
+   {
+     int cnt=0;
+     char *tmp = str, inword=1;
+ 
+     if(!str || !*str || n < 1 || WordCount(str) < n)
+       return 0;
+ 
+     while(isspace(*tmp))  // skipping first spaces
+       tmp++;
+ 
+     if(n > 1)
+       while(*tmp)
+         {
+           if( isspace(*tmp) )
+ 	    {
+ 	      if(inword)
+ 	        {
+ 	          inword = 0;
+ 
+ 		  if(++cnt == n-1)
+ 		    break;
+ 	        }
+ 	    }
+ 	  else inword = 1;
+ 
+ 	  tmp++;
+         }
+ 
+     cnt = 0;
+ 
+     while(isspace(*tmp))
+       tmp++;            
+ 
+     while(*tmp)
+       {
+         if(isspace(*tmp))
+ 	  break;
+ 
+ 	*Word++ = *tmp++;
+ 
+ 	if(++cnt >= Max)
+ 	  break;
+       }
+ 
+     *Word = 0;
+     DelGarbage(Word);
+     return cnt;
+   }
+ 
+ 
+ int GetLineFD(int fd, char *str, int MAX)
+   {                                          
+       char c; 
+       int key, got=0;
+                                                        
+       while( (key = read(fd, &c, 1)) > 0 && MAX-- > 0)
+         {                                      
+           if(c == '\n' || c == '\f')
+             break;                             
+ 
+           *str++ = c;
+           got++;
+         }
+         
+       if(MAX < 1)  // so we reach length limit, going to end of line (or file)
+         {
+           while((key = read(fd, &c, 1)) > 0)
+             {
+               if( !(c == '\n' || c == '\f') )
+                 continue;
+               break;
+             }
+         }
+ 
+       *str = 0;        
+       
+       return (got == 0 ? (key < 1 ? -1 : 0) : got);
+   }
+             
+ 
+ 
+ 
+ int CheckCommands(char *command)
+   {
+     char *CommandsAllowed[] = { "imap", "pop3", "auth", "" };
+     int i;
+     
+     if(command[4] && !isspace(command[4]) )
+       return(0);
+ 
+     for(i = 0; i < 3; i++)
+       if( !strncasecmp(CommandsAllowed[i], command, 4) )
+         return(1);
+ 	
+     return(0);
+   }
+ 
+ void DelGarbage(char *str)
+   {
+     char *pnt = str;
+     int j=0;
+     unsigned char buff[512];
+     
+     if(!str || !*str)
+       return;
+       
+     while( *pnt )
+       {
+         if( !isBAD(*pnt) && j < 512)
+ 	  buff[j++] = *pnt;
+ 
+ 	pnt++;
+       }
+       
+      buff[j] = 0;
+      strcpy(str, buff);
+   }      
+ 
+ int isBAD(unsigned char chr)
+   {
+     char bad = 0;
+     
+     bad = ( isspace(chr) || isEOL(chr) || (!isLetter(chr) ) );
+ 
+     if( bad )
+       bad = ( !ispunct(chr) && !isdigit(chr) );
+       
+     return (bad);  
+   }
+ 
+ int isEOL(unsigned char chr)
+   {
+     return( (chr == '\r' || chr == '\n' || chr == '\f') ? 1 : 0);
+   }
+ 
+ int isLetter(char chr)
+   {
+     return ( (chr >= 'A' && chr <= 'Z') || (chr >= 'a' && chr <= 'z') );
+   }    
+ 
+ void DelSpaces(char *str)
+   {
+     int len;
+ 
+     if(!str)
+       return;
+       
+     len = strlen(str);
+     
+     while( *str && isspace(*str) && len > 0 )
+       {
+ 	memmove(str, str+1, len+1);
+ 	len--;
+ 	continue;
+       }
+ 
+     while(len > 0 && isspace(*(str+(len-1))))
+       len--;
+     
+     *(str+len) = 0;
+   }
+   
+ 
+ void CleanIP(char *str)                                      
+   {                                                          
+     int len;                                                 
+ 	                                                               
+     if(!str || !*str)                                        
+       return;                                                
+ 		                                                                 
+     len = strlen(str);                                       
+ 			                                                             
+     while(*str)
+       {                                                      
+ 	if( !isdigit(*str) && *str != '.' && len > 0 )
+ 	  {                                                  
+ 	    memmove(str, str+1, len);                       
+ 	    len--;                                          
+ 	    continue;                                       
+ 	  }                                                  
+ 	str++;                                               
+       }
+   }
+ 
+ 
+ static unsigned char dtable[256];
+ 
+ void b64_decode(char *in, char *out)
+   {
+     int i, j;
+ 
+     for (i = 0; i < 255; i++) 
+       {
+         dtable[i] = 0x80;
+       }
+ 
+     for (i = 'A'; i <= 'Z'; i++) 
+       {
+         dtable[i] = 0 + (i - 'A');
+       }
+ 
+     for (i = 'a'; i <= 'z'; i++) 
+       {
+         dtable[i] = 26 + (i - 'a');
+       }
+ 
+     for (i = '0'; i <= '9'; i++) 
+       {
+         dtable[i] = 52 + (i - '0');
+       }
+ 
+     dtable['+'] = 62;
+     dtable['/'] = 63;
+     dtable['='] = 0;
+ 
+     while(*in) 
+       {
+         unsigned char a[4], b[4], o[3];
+ 
+         for(i = 0; i < 4; i++) 
+           {
+             int c = *in++;
+ 
+             if(c <= ' ')
+               continue;
+ 
+             if (dtable[c] & 0x80) 
+               {
+                 i--;
+                 continue;
+               }
+ 
+             a[i] = (unsigned char) c;
+             b[i] = (unsigned char) dtable[c];
+           }
+         o[0] = (b[0] << 2) | (b[1] >> 4);
+         o[1] = (b[1] << 4) | (b[2] >> 2);
+         o[2] = (b[2] << 6) | b[3];
+ 
+         i = ( (a[2] == '=') ? 1 : (a[3] == '=' ? 2 : 3) );
+ 
+         for(j=0; j < i; j++)
+           *out++ = o[j];
+ 
+         if (i < 3) 
+           break;
+       }
+     *out = 0;
+   }
+ 
+ 
+ void b64_encode(char *in, char *out)
+   {
+     int i;
+ 
+     /*  Fill dtable with character encodings.  */
+ 
+     for (i = 0; i < 26; i++)
+       {
+         dtable[i] = 'A' + i;
+         dtable[26 + i] = 'a' + i;
+       }
+ 
+     for(i = 0; i < 10; i++)
+       dtable[52 + i] = '0' + i;
+ 
+     dtable[62] = '+';
+     dtable[63] = '/';
+ 
+     while (*in)
+       {
+         unsigned char igroup[3], ogroup[4];
+         int c, n;
+ 
+         igroup[0] = igroup[1] = igroup[2] = 0;
+ 
+         for (n = 0; n < 3 && *in; n++)
+           {
+             c = *in++;
+             igroup[n] = (unsigned char) c;
+           }
+ 
+         if(n > 0) 
+ 	  {
+             ogroup[0] = dtable[igroup[0] >> 2];
+             ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
+             ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
+             ogroup[3] = dtable[igroup[2] & 0x3F];
+ 
+             /* Replace characters in output stream with "=" pad
+                characters if fewer than three characters were
+                read from the end of the input stream. */
+ 
+             if(n < 3) 
+               {
+                 ogroup[3] = '=';
+                 if (n < 2)
+                   ogroup[2] = '=';
+               }
+             for (i = 0; i < 4; i++)
+               *out++ = ogroup[i];
+           }
+       }
+ 
+     *out = 0;
+   }
+ 
+ char *md5(char *string)
+   {
+     MD5_CTX buff;
+     char digest[17];
+ 
+     MD5Init(&buff);
+     MD5Update(&buff, string, strlen(string));
+     MD5Final(digest, &buff);
+ 
+     return(dec2hex(digest));
+   }
+ 
diff -crN ../qmail-1.03/functions.h ./functions.h
*** ../qmail-1.03/functions.h	Wed Dec 31 18:00:00 1969
--- ./functions.h	Sun Feb 17 22:04:08 2002
***************
*** 0 ****
--- 1,18 ----
+ char *md5(char *);
+ void HMAC_MD5(unsigned char *text, int text_len, unsigned char *key, int key_len, unsigned char *digest);
+ 
+ char *dec2hex(unsigned char *);
+ 
+ int WordCount(char *);
+ int GetWordN(char *, char *, int, int);
+ int CheckCommands(char *);
+ void DelGarbage(char *);
+ void DelSpaces(char *);
+ int isBAD(unsigned char);
+ int isEOL(unsigned char);
+ int isLetter(char chr);
+ void CleanIP(char *);
+ int GetLineFD(int, char *, int);
+ 
+ void b64_decode(char *, char *);
+ void b64_encode(char *, char *);
diff -crN ../qmail-1.03/md5.c ./md5.c
*** ../qmail-1.03/md5.c	Wed Dec 31 18:00:00 1969
--- ./md5.c	Sun Feb 17 21:57:21 2002
***************
*** 0 ****
--- 1,251 ----
+ /*
+  * This code implements the MD5 message-digest algorithm.
+  * The algorithm is due to Ron Rivest.  This code was
+  * written by Colin Plumb in 1993, no copyright is claimed.
+  * This code is in the public domain; do with it what you wish.
+  *
+  * Modified by Andrew M. Bishop in 1998 so that it is endian
+  * independent and gives the same results on both big-endian
+  * and little-endian machines.  This meant removing a few
+  * pre-processor lines.
+  *
+  * Equivalent code is available from RSA Data Security, Inc.
+  * This code has been tested against that, and is equivalent,
+  * except that you don't need to include two pages of legalese
+  * with every copy.
+  *
+  * To compute the message digest of a chunk of bytes, declare an
+  * MD5Context structure, pass it to MD5Init, call MD5Update as
+  * needed on buffers full of bytes, and then call MD5Final, which
+  * will fill a supplied 16-byte array with the digest.
+  */
+ #include <string.h>		/* for memcpy() */
+ #include "md5.h"
+ 
+ void byteReverse(unsigned char *buf, unsigned longs);
+ 
+ /*
+  * Note: this code is harmless on little-endian machines.
+  */
+ void byteReverse(unsigned char *buf, unsigned longs)
+ {
+     uint32 t;
+     do {
+ 	t = (uint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ 	    ((unsigned) buf[1] << 8 | buf[0]);
+ 	*(uint32 *) buf = t;
+ 	buf += 4;
+     } while (--longs);
+ }
+ 
+ /*
+  * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+  * initialization constants.
+  */
+ void MD5Init(struct MD5Context *ctx)
+ {
+     ctx->buf[0] = 0x67452301;
+     ctx->buf[1] = 0xefcdab89;
+     ctx->buf[2] = 0x98badcfe;
+     ctx->buf[3] = 0x10325476;
+ 
+     ctx->bits[0] = 0;
+     ctx->bits[1] = 0;
+ }
+ 
+ /*
+  * Update context to reflect the concatenation of another buffer full
+  * of bytes.
+  */
+ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+ {
+     uint32 t;
+ 
+     /* Update bitcount */
+ 
+     t = ctx->bits[0];
+     if ((ctx->bits[0] = t + ((uint32) len << 3)) < t)
+ 	ctx->bits[1]++;		/* Carry from low to high */
+     ctx->bits[1] += len >> 29;
+ 
+     t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+ 
+     /* Handle any leading odd-sized chunks */
+ 
+     if (t) {
+ 	unsigned char *p = (unsigned char *) ctx->in + t;
+ 
+ 	t = 64 - t;
+ 	if (len < t) {
+ 	    memcpy(p, buf, len);
+ 	    return;
+ 	}
+ 	memcpy(p, buf, t);
+ 	byteReverse(ctx->in, 16);
+ 	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ 	buf += t;
+ 	len -= t;
+     }
+     /* Process data in 64-byte chunks */
+ 
+     while (len >= 64) {
+ 	memcpy(ctx->in, buf, 64);
+ 	byteReverse(ctx->in, 16);
+ 	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ 	buf += 64;
+ 	len -= 64;
+     }
+ 
+     /* Handle any remaining bytes of data. */
+ 
+     memcpy(ctx->in, buf, len);
+ }
+ 
+ /*
+  * Final wrapup - pad to 64-byte boundary with the bit pattern 
+  * 1 0* (64-bit count of bits processed, MSB-first)
+  */
+ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+ {
+     unsigned count;
+     unsigned char *p;
+ 
+     /* Compute number of bytes mod 64 */
+     count = (ctx->bits[0] >> 3) & 0x3F;
+ 
+     /* Set the first char of padding to 0x80.  This is safe since there is
+        always at least one byte free */
+     p = ctx->in + count;
+     *p++ = 0x80;
+ 
+     /* Bytes of padding needed to make 64 bytes */
+     count = 64 - 1 - count;
+ 
+     /* Pad out to 56 mod 64 */
+     if (count < 8) {
+ 	/* Two lots of padding:  Pad the first block to 64 bytes */
+ 	memset(p, 0, count);
+ 	byteReverse(ctx->in, 16);
+ 	MD5Transform(ctx->buf, (uint32 *) ctx->in);
+ 
+ 	/* Now fill the next block with 56 bytes */
+ 	memset(ctx->in, 0, 56);
+     } else {
+ 	/* Pad block to 56 bytes */
+ 	memset(p, 0, count - 8);
+     }
+     byteReverse(ctx->in, 14);
+ 
+     /* Append length in bits and transform */
+     ((uint32 *) ctx->in)[14] = ctx->bits[0];
+     ((uint32 *) ctx->in)[15] = ctx->bits[1];
+ 
+     MD5Transform(ctx->buf, (uint32 *) ctx->in);
+     byteReverse((unsigned char *) ctx->buf, 4);
+     memcpy(digest, ctx->buf, 16);
+     memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+ }
+ 
+ #ifndef ASM_MD5
+ 
+ /* The four core functions - F1 is optimized somewhat */
+ 
+ /* #define F1(x, y, z) (x & y | ~x & z) */
+ #define F1(x, y, z) (z ^ (x & (y ^ z)))
+ #define F2(x, y, z) F1(z, x, y)
+ #define F3(x, y, z) (x ^ y ^ z)
+ #define F4(x, y, z) (y ^ (x | ~z))
+ 
+ /* This is the central step in the MD5 algorithm. */
+ #define MD5STEP(f, w, x, y, z, data, s) \
+ 	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+ 
+ /*
+  * The core of the MD5 algorithm, this alters an existing MD5 hash to
+  * reflect the addition of 16 longwords of new data.  MD5Update blocks
+  * the data and converts bytes into longwords for this routine.
+  */
+ void MD5Transform(uint32 buf[4], uint32 const in[16])
+ {
+     register uint32 a, b, c, d;
+ 
+     a = buf[0];
+     b = buf[1];
+     c = buf[2];
+     d = buf[3];
+ 
+     MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+     MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+     MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+     MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+     MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+     MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+     MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+     MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+     MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+     MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+     MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+     MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+     MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+     MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+     MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+     MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+ 
+     MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+     MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+     MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+     MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+     MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+     MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+     MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+     MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+     MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+     MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+     MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+     MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+     MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+     MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+     MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+     MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+ 
+     MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+     MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+     MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+     MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+     MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+     MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+     MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+     MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+     MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+     MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+     MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+     MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+     MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+     MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+     MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+     MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+ 
+     MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+     MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+     MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+     MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+     MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+     MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+     MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+     MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+     MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+     MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+     MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+     MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+     MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+     MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+     MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+     MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+ 
+     buf[0] += a;
+     buf[1] += b;
+     buf[2] += c;
+     buf[3] += d;
+ }
+ 
+ #endif
diff -crN ../qmail-1.03/md5.h ./md5.h
*** ../qmail-1.03/md5.h	Wed Dec 31 18:00:00 1969
--- ./md5.h	Sun Feb 17 21:57:21 2002
***************
*** 0 ****
--- 1,27 ----
+ #ifndef VPOPMAIL_MD5_H
+ #define VPOPMAIL_MD5_H
+ 
+ #ifdef __alpha
+ typedef unsigned int uint32;
+ #else
+ typedef unsigned long uint32;
+ #endif
+ 
+ struct MD5Context {
+ 	uint32 buf[4];
+ 	uint32 bits[2];
+ 	unsigned char in[64];
+ };
+ 
+ void MD5Init(struct MD5Context *context);
+ void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ 	       unsigned len);
+ void MD5Final(unsigned char digest[16], struct MD5Context *context);
+ void MD5Transform(uint32 buf[4], uint32 const in[16]);
+ 
+ /*
+  * This is needed to make RSAREF happy on some MS-DOS compilers.
+  */
+ typedef struct MD5Context MD5_CTX;
+ 
+ #endif /* !MD5_H */
Binary files ../qmail-1.03/md5.o and ./md5.o differ
diff -crN ../qmail-1.03/qmail-popup.c ./qmail-popup.c
*** ../qmail-1.03/qmail-popup.c	Mon Jun 15 05:53:16 1998
--- ./qmail-popup.c	Sun Feb 17 21:57:10 2002
***************
*** 14,36 ****
  #include "timeoutread.h"
  #include "timeoutwrite.h"
  
! void die() { _exit(1); }
  
! int saferead(fd,buf,len) int fd; char *buf; int len;
! {
!   int r;
!   r = timeoutread(1200,fd,buf,len);
!   if (r <= 0) die();
!   return r;
! }
! 
! int safewrite(fd,buf,len) int fd; char *buf; int len;
! {
!   int r;
!   r = timeoutwrite(1200,fd,buf,len);
!   if (r <= 0) die();
!   return r;
! }
  
  char ssoutbuf[128];
  substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
--- 14,50 ----
  #include "timeoutread.h"
  #include "timeoutwrite.h"
  
! #include "md5.c"
  
! void die_badauth(void);
! char *main(int, char **);
! 
! void die() 
!   { 
!     _exit(1); 
!   }
! 
! int saferead(int fd, char *buf, int len)
!   {
!     int r;
!     r = timeoutread(1200, fd, buf, len);
! 
!     if (r <= 0) 
!       die();
! 
!     return r;
!   }
! 
! int safewrite(int fd, char *buf, int len)
!   {
!     int r;
!     r = timeoutwrite(1200, fd, buf, len);
! 
!     if (r <= 0) 
!       die();
! 
!     return r;
!   }
  
  char ssoutbuf[128];
  substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
***************
*** 38,58 ****
  char ssinbuf[128];
  substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
  
! void puts(s) char *s;
! {
!   substdio_puts(&ssout,s);
! }
  void flush()
! {
!   substdio_flush(&ssout);
! }
! void err(s) char *s;
! {
!   puts("-ERR ");
!   puts(s);
!   puts("\r\n");
!   flush();
! }
  
  void die_usage() { err("usage: popup hostname subprogram"); die(); }
  void die_nomem() { err("out of memory"); die(); }
--- 52,90 ----
  char ssinbuf[128];
  substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
  
! void puts(char *s)
!   {
!     substdio_puts(&ssout, s);
!   }
! 
  void flush()
!   {
!     substdio_flush(&ssout);
!   }
! 
! char APOP_FLAG[2] = "\0\0";
! 
! void pop3_capa(char *arg)
!   {
!     puts("+OK Capability list follows\r\n");
!     puts("TOP\r\n");
!     puts("UIDL\r\n");
!     puts("LAST\r\n");
!     puts("USER\r\n");
!     puts("APOP\r\n");
!     puts("SASL PLAIN CRAM-MD5\r\n");    
!     puts(".\r\n");
!     flush();
!   }
! 
! 
! void err(char *s)
!   {
!     puts("-ERR ");
!     puts(s);
!     puts("\r\n");
!     flush();
!   }
  
  void die_usage() { err("usage: popup hostname subprogram"); die(); }
  void die_nomem() { err("out of memory"); die(); }
***************
*** 60,66 ****
  void die_write() { err("unable to write pipe"); die(); }
  void die_fork() { err("unable to fork"); die(); }
  void die_childcrashed() { err("aack, child crashed"); }
! void die_badauth() { err("authorization failed"); }
  
  void err_syntax() { err("syntax error"); }
  void err_wantuser() { err("USER first"); }
--- 92,98 ----
  void die_write() { err("unable to write pipe"); die(); }
  void die_fork() { err("unable to fork"); die(); }
  void die_childcrashed() { err("aack, child crashed"); }
! 
  
  void err_syntax() { err("syntax error"); }
  void err_wantuser() { err("USER first"); }
***************
*** 77,183 ****
  char **childargs;
  substdio ssup;
  char upbuf[128];
  
  
! void doanddie(user,userlen,pass)
! char *user;
! unsigned int userlen; /* including 0 byte */
! char *pass;
! {
!   int child;
!   int wstat;
!   int pi[2];
   
!   if (fd_copy(2,1) == -1) die_pipe();
!   close(3);
!   if (pipe(pi) == -1) die_pipe();
!   if (pi[0] != 3) die_pipe();
!   switch(child = fork()) {
!     case -1:
!       die_fork();
!     case 0:
!       close(pi[1]);
!       sig_pipedefault();
!       execvp(*childargs,childargs);
!       _exit(1);
!   }
!   close(pi[0]);
!   substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
!   if (substdio_put(&ssup,user,userlen) == -1) die_write();
!   if (substdio_put(&ssup,pass,str_len(pass) + 1) == -1) die_write();
!   if (substdio_puts(&ssup,"<") == -1) die_write();
!   if (substdio_puts(&ssup,unique) == -1) die_write();
!   if (substdio_puts(&ssup,hostname) == -1) die_write();
!   if (substdio_put(&ssup,">",2) == -1) die_write();
!   if (substdio_flush(&ssup) == -1) die_write();
!   close(pi[1]);
!   byte_zero(pass,str_len(pass));
!   byte_zero(upbuf,sizeof upbuf);
!   if (wait_pid(&wstat,child) == -1) die();
!   if (wait_crashed(wstat)) die_childcrashed();
!   if (wait_exitcode(wstat)) die_badauth();
!   die();
! }
  void pop3_greet()
! {
!   char *s;
!   s = unique;
!   s += fmt_uint(s,getpid());
!   *s++ = '.';
!   s += fmt_ulong(s,(unsigned long) now());
!   *s++ = '@';
!   *s++ = 0;
!   puts("+OK <");
!   puts(unique);
!   puts(hostname);
!   puts(">\r\n");
!   flush();
! }
  void pop3_user(arg) char *arg;
! {
!   if (!*arg) { err_syntax(); return; }
!   okay();
!   seenuser = 1;
!   if (!stralloc_copys(&username,arg)) die_nomem(); 
!   if (!stralloc_0(&username)) die_nomem(); 
! }
! void pop3_pass(arg) char *arg;
! {
!   if (!seenuser) { err_wantuser(); return; }
!   if (!*arg) { err_syntax(); return; }
!   doanddie(username.s,username.len,arg);
! }
! void pop3_apop(arg) char *arg;
! {
!   char *space;
!   space = arg + str_chr(arg,' ');
!   if (!*space) { err_syntax(); return; }
!   *space++ = 0;
!   doanddie(arg,space - arg,space);
! }
! 
! struct commands pop3commands[] = {
!   { "user", pop3_user, 0 }
! , { "pass", pop3_pass, 0 }
! , { "apop", pop3_apop, 0 }
! , { "quit", pop3_quit, 0 }
! , { "noop", okay, 0 }
! , { 0, err_authoriz, 0 }
! } ;
! 
! void main(argc,argv)
! int argc;
! char **argv;
! {
!   sig_alarmcatch(die);
!   sig_pipeignore();
   
!   hostname = argv[1];
!   if (!hostname) die_usage();
!   childargs = argv + 2;
!   if (!*childargs) die_usage();
   
!   pop3_greet();
!   commands(&ssin,pop3commands);
!   die();
! }
--- 109,446 ----
  char **childargs;
  substdio ssup;
  char upbuf[128];
+ char IsCRAM = 0, IsAPOP=0;
  
  
! void doanddie(char *user, unsigned int userlen, char *pass)
!   {
!     int child;
!     int wstat;
!     int pi[2];
   
!     if(fd_copy(2, 1) == -1) 
!       die_pipe();
! 
!     close(3);
! 
!     if (pipe(pi) == -1) 
!       die_pipe();
! 
!     if (pi[0] != 3) 
!       die_pipe();
! 
!     switch(child = fork()) 
!       {
!         case -1:
!           die_fork();
! 
!         case 0:
!           close(pi[1]);
!           sig_pipedefault();
!           execvp(*childargs, childargs);
!           _exit(1);
!       }
! 
!     close(pi[0]);
!     substdio_fdbuf(&ssup, write, pi[1], upbuf, sizeof(upbuf));
! 
!     if(substdio_put(&ssup, user, userlen) == -1) 
!       die_write();
! 
!     if(substdio_put(&ssup, pass, str_len(pass) + 1) == -1)
!       die_write();
! 
!     if(!IsCRAM)
!       {
!         if(substdio_puts(&ssup,"<") == -1) 
!           die_write();
! 
!         if(substdio_puts(&ssup,unique) == -1)
!           die_write();
! 
!         if (substdio_puts(&ssup,hostname) == -1) 
!           die_write();
! 
!         if (substdio_put(&ssup,">",2) == -1) 
!           die_write();
!       
! 
!         if (substdio_put(&ssup, APOP_FLAG, sizeof(APOP_FLAG)) == -1)   // my hack to determine AUTH method
!           die_write();                                                 // so the forth line is 1 if APOP
!       }	 
! 
!     if (substdio_flush(&ssup) == -1) 
!       die_write();
! 
!     close(pi[1]);
! 
!     byte_zero(pass, str_len(pass));
!     byte_zero(user, str_len(user));
!     byte_zero(upbuf, sizeof(upbuf));
! 
!     if(wait_pid(&wstat, child) == -1) 
!       die();
! 
!     if(wait_crashed(wstat)) 
!       die_childcrashed();
! 
!     if(wait_exitcode(wstat)) 
!       die_badauth();
! 
!     die();
!   }
! 
  void pop3_greet()
!   {
!     char *s;
! 
!     s = unique;
!     s += fmt_uint(s, getpid());
!     *s++ = '.';
!     s += fmt_ulong(s,(unsigned long) now());
!     *s++ = '@';
!     *s++ = 0;
! 
!     puts("+OK <");
!     puts(unique);
!     puts(hostname);
!     puts(">\r\n");
! 
!     flush();
!   }
! 
  void pop3_user(arg) char *arg;
!   {
!     if (!*arg) 
!       { 
!         err_syntax(); 
! 	return; 
!       }
! 
!     okay();
! 
!     seenuser = 1;
! 
!     if (!stralloc_copys(&username, arg)) 
!       die_nomem(); 
! 
!     if (!stralloc_0(&username)) 
!       die_nomem(); 
!   }
! 
! void pop3_pass(char *arg)
!   {
!     if (!seenuser) 
!       { 
!         err_wantuser(); 
! 	return; 
!       }
! 
!     if(!*arg) 
!       { 
!         err_syntax(); 
! 	return; 
!       }
! 
!     APOP_FLAG[0] = 'n';
!     doanddie(username.s, username.len, arg);
!   }
! 
! void pop3_apop(char *arg)
!   {
!     char *space;
! 
!     space = arg + str_chr(arg,' ');
! 
!     if (!*space) 
!       { 
!         err_syntax(); 
! 	return; 
!       }
! 
!     *space++ = 0;
!     APOP_FLAG[0] = 'y';
!     IsAPOP = 1;
! 
!     doanddie(arg, (space - arg), space);
!   }
! 
! int GetString(char *str, int MAX)
!   {                                                
!     char c;                                        
!     int cnt = 0;                                   
! 	                                                     
!     while(read(1, &c, 1) > 0 && cnt < MAX)         
!       {
!         if(c == '\0' || c == '\r')
! 	  continue;
! 	  
! 	if(c == '\n')
! 	  break;
! 	  
! 	*str++ = c;
! 	if(++cnt >= MAX)
! 	  break;
!       }
! 
!     *str = 0;
! 
!     return(cnt);
!   }                                                
! 
! 
! #include "functions.c"
! 
! void pop3_auth(char *arg)
!   {
!     char tmp[256], stamp[128];
!     char b64[256], Command[64];
!     int i, NWords;
! 
! 
!     if( (NWords = WordCount(arg)) > 0)
!       GetWordN(Command, arg, 1, 256);
! 
!     if(NWords == 0)
!       {
!         // guess request from buggie MS clients with SPA
!     
!         puts("+ \r\nNTLM\r\n.\r\n");
! 	
! 
! /*        strcpy(stamp, "+ <");
!         strcat(stamp, unique);
!         strcat(stamp, hostname);
!         strcat(stamp, ">");
! 
! 	b_encode(stamp, b64);
! 	puts(b64);
! 	puts("\r\n");  */
! 
! 	flush();
! 
!         i = GetString(tmp, 256);
! 
! 	if(i < 9 || strncasecmp(tmp, "auth ntlm", 9))
! 	  {
! 	    puts("-ERR incorrect answer\r\n");
! 	    flush();
! 	    return;
! 	  }
! 
!         puts("+OK\r\n.\r\n");
!         flush(); 
! 	      
!         i = GetString(tmp, 256);
! 	
! 	strcpy(stamp, "auth ntlm ");
! 	strcat(stamp, tmp);
! 	strcat(stamp, " ");
! 
! 	doanddie(stamp, strlen(stamp), tmp);
!       }
!     else if(NWords == 1)
!       {
! 	if( !strcasecmp(Command, "cram-md5") )
! 	  {
!             strcpy(stamp, "<");
!             strcat(stamp, unique);
!             strcat(stamp, hostname);
!             strcat(stamp, ">");
!     
!             b64_encode(stamp, b64);
! 
!             puts("+ ");
!             puts(b64);
!             puts("\r\n");
!             flush();
! 
!             i = GetString(tmp, 256);
! 
!             if(i < 1)
!               {
!                 puts("-ERR incorrect data for this schema\r\n");
! 	        flush();
!                 return;      
!               }
! 
!             strcpy(Command, "auth ");
!             strcat(Command, arg);
!             strcat(Command, " ");    
!             strcat(Command, b64);
!             strcat(Command, " ");
!     
!             IsCRAM = 1;
!             doanddie(Command, strlen(Command), tmp);
! 	  }
! 	else
! 	  {
! 	    puts("-ERR unsupported method\r\n");
! 	    flush();
! 	    return;
! 	  }
!       }
!     else if(NWords == 3 && !strcasecmp(Command, "login") )
!       {
!         strcpy(Command, "auth login ");
! 
! 	GetWordN(tmp, arg, 2, 128);
! 	strcat(Command, tmp);
! 	strcat(Command, " ");
! 
! 	GetWordN(tmp, arg, 3, 128);
! 
! 	doanddie(Command, strlen(Command), tmp);
!       }
!     else
!       {
!         puts("-ERR invalid request syntax\r\n");
! 	flush();
!       }
!   }
! 
! struct commands pop3commands[] = 
!   {
!     { "user", pop3_user, 0 },
!     { "pass", pop3_pass, 0 },
!     { "apop", pop3_apop, 0 },
!     { "auth", pop3_auth, 0 },    
!     { "quit", pop3_quit, 0 },
!     { "capa", pop3_capa, 0 },
!     { "noop", okay, 0 },
!     { 0, err_authoriz, 0 }
!   };
! 
! 
! void die_badauth() 
!   { 
!     if(IsCRAM || IsAPOP)
!       { 
!          puts("-ERR login failure\r\n");
! 	 flush();
! 	 commands(&ssin, pop3commands);
! 	 die();
!       }	 
!      else	 
!        err("authorization failed"); 
!   }
! 
! 
! char *main(int argc, char **argv)
!   {
!     sig_alarmcatch(die);
!     sig_pipeignore();
   
!     hostname = argv[1];
!     if (!hostname || !*hostname)
!       die_usage();
! 
!     childargs = argv + 2;
!     if (!*childargs) 
!       die_usage();
   
!     pop3_greet();
!     commands(&ssin, pop3commands);
! 
!     die();
!   }
diff -crN ../qmail-1.03/qmail-smtpd.8 ./qmail-smtpd.8
*** ../qmail-1.03/qmail-smtpd.8	Mon Jun 15 05:53:16 1998
--- ./qmail-smtpd.8	Sun Feb 17 21:57:40 2002
***************
*** 3,8 ****
--- 3,13 ----
  qmail-smtpd \- receive mail via SMTP
  .SH SYNOPSIS
  .B qmail-smtpd
+ [
+ .I hostname
+ .I checkprogram
+ .I subprogram
+ ]
  .SH DESCRIPTION
  .B qmail-smtpd
  receives mail messages via the Simple Mail Transfer Protocol (SMTP)
***************
*** 23,29 ****
  header fields.
  
  .B qmail-smtpd
! supports ESMTP, including the 8BITMIME and PIPELINING options.
  .SH TRANSPARENCY
  .B qmail-smtpd
  converts the SMTP newline convention into the UNIX newline convention
--- 28,56 ----
  header fields.
  
  .B qmail-smtpd
! supports ESMTP, including the 8BITMIME, PIPELINING, and AUTH options.
! 
! .B qmail-smtpd
! can accept LOGIN, PLAIN, and CRAM-MD5 AUTH types.  It invokes
! .IR checkprogram ,
! which reads on file descriptor 3 the username, a 0 byte, the password
! or challenge derived from
! .IR hostname ,
! another 0 byte, a CRAM-MD5 response (if applicable to the AUTH type),
! and a final 0 byte.
! .I checkprogram
! invokes
! .I subprogram
! upon successful authentication, which should in turn return 0 to
! .BR qmail-smtpd ,
! effectively setting the environment variables RELAYCLIENT and TCPREMOTEINFO
! (any supplied value replaced with the authenticated username).
! .B qmail-smtpd
! will reject the authentication attempt if it receives a nonzero return
! value from
! .I checkprogram
! or
! .IR subprogram .
  .SH TRANSPARENCY
  .B qmail-smtpd
  converts the SMTP newline convention into the UNIX newline convention
***************
*** 177,179 ****
--- 204,209 ----
  qmail-newmrh(8),
  qmail-queue(8),
  qmail-remote(8)
+ .SH "HISTORY"
+ The patch enabling the ESMTP AUTH option is not part of the standard
+ qmail-1.03 distribution.
diff -crN ../qmail-1.03/qmail-smtpd.c ./qmail-smtpd.c
*** ../qmail-1.03/qmail-smtpd.c	Mon Jun 15 05:53:16 1998
--- ./qmail-smtpd.c	Sun Feb 17 22:02:18 2002
***************
*** 23,32 ****
--- 23,36 ----
  #include "timeoutread.h"
  #include "timeoutwrite.h"
  #include "commands.h"
+ #include "wait.h"
+ #include "fd.h"
  
+ #define AUTHCRAM
  #define MAXHOPS 100
  unsigned int databytes = 0;
  int timeout = 1200;
+ char IsCRAM=0;
  
  int safewrite(fd,buf,len) int fd; char *buf; int len;
  {
***************
*** 59,64 ****
--- 63,77 ----
  void err_vrfy() { out("252 send some mail, i'll try my best\r\n"); }
  void err_qqt() { out("451 qqt failure (#4.3.0)\r\n"); }
  
+ int err_child() { out("454 oops, problem with child and I can't auth (#4.3.0)\r\n"); return -1; }
+ int err_fork() { out("454 oops, child won't start and I can't auth (#4.3.0)\r\n"); return -1; }
+ int err_pipe() { out("454 oops, unable to open pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+ int err_write() { out("454 oops, unable to write pipe and I can't auth (#4.3.0)\r\n"); return -1; }
+ void err_authd() { out("503 you're already authenticated (#5.5.0)\r\n"); }
+ void err_authmail() { out("503 no auth during mail transaction (#5.5.0)\r\n"); }
+ int err_noauth() { out("504 auth type unimplemented (#5.5.1)\r\n"); return -1; }
+ int err_authabrt() { out("501 auth exchange cancelled (#5.0.0)\r\n"); return -1; }
+ int err_input() { out("501 malformed auth input (#5.5.4)\r\n"); return -1; }
  
  stralloc greeting = {0};
  
***************
*** 229,235 ****
  }
  void smtp_ehlo(arg) char *arg;
  {
!   smtp_greet("250-"); out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
    seenmail = 0; dohelo(arg);
  }
  void smtp_rset()
--- 242,256 ----
  }
  void smtp_ehlo(arg) char *arg;
  {
!   smtp_greet("250-");
! #ifdef AUTHCRAM
!   out("\r\n250-AUTH LOGIN CRAM-MD5 PLAIN");
!   out("\r\n250-AUTH=LOGIN CRAM-MD5 PLAIN");
! #else
!   out("\r\n250-AUTH LOGIN PLAIN");
!   out("\r\n250-AUTH=LOGIN PLAIN");
! #endif
!   out("\r\n250-PIPELINING\r\n250 8BITMIME\r\n");
    seenmail = 0; dohelo(arg);
  }
  void smtp_rset()
***************
*** 394,403 ****
--- 415,642 ----
    out("\r\n");
  }
  
+ 
+ char unique[FMT_ULONG + FMT_ULONG + 3];
+ static stralloc authin = {0};
+ static stralloc user = {0};
+ static stralloc pass = {0};
+ static stralloc resp = {0};
+ static stralloc slop = {0};
+ char *hostname;
+ char **childargs;
+ substdio ssup;
+ char upbuf[128];
+ int authd = 0;
+ 
+ int authgetl(void) {
+   int i;
+ 
+   if (!stralloc_copys(&authin, "")) die_nomem();
+ 
+   for (;;) {
+     if (!stralloc_readyplus(&authin,1)) die_nomem(); /* XXX */
+     i = substdio_get(&ssin,authin.s + authin.len,1);
+     if (i != 1) die_read();
+     if (authin.s[authin.len] == '\n') break;
+     ++authin.len;
+   }
+ 
+   if (authin.len > 0) if (authin.s[authin.len - 1] == '\r') --authin.len;
+   authin.s[authin.len] = 0;
+ 
+   if (*authin.s == '*' && *(authin.s + 1) == 0) { return err_authabrt(); }
+   if (authin.len == 0) { return err_input(); }
+   return authin.len;
+ }
+ 
+ int authenticate(void)
+ {
+   int child;
+   int wstat;
+   int pi[2];
+   char tmp[2]="s";
+ 
+   if (!stralloc_0(&user)) die_nomem();
+   if (!stralloc_0(&pass)) die_nomem();
+   if (!stralloc_0(&resp)) die_nomem();
+ 
+   if (fd_copy(2,1) == -1) return err_pipe();
+   close(3);
+   if (pipe(pi) == -1) return err_pipe();
+   if (pi[0] != 3) return err_pipe();
+   switch(child = fork()) {
+     case -1:
+       return err_fork();
+     case 0:
+       close(pi[1]);
+       sig_pipedefault();
+       execvp(*childargs, childargs);
+       _exit(1);
+   }
+   close(pi[0]);
+ 
+   substdio_fdbuf(&ssup,write,pi[1],upbuf,sizeof upbuf);
+   if (substdio_put(&ssup,user.s,user.len) == -1) return err_write();
+   if (substdio_put(&ssup,pass.s,pass.len) == -1) return err_write();
+   if (substdio_put(&ssup,resp.s,resp.len) == -1) return err_write();
+   if(IsCRAM) tmp[0] = 'c';
+   if(substdio_put(&ssup, tmp, 2) == -1) return err_write();
+   if (substdio_flush(&ssup) == -1) return err_write();
+ 
+   close(pi[1]);
+   byte_zero(pass.s,pass.len);
+   byte_zero(upbuf,sizeof upbuf);
+   if (wait_pid(&wstat,child) == -1) return err_child();
+   if (wait_crashed(wstat)) return err_child();
+   if (wait_exitcode(wstat)) { sleep(5); return 1; } /* no */
+   return 0; /* yes */
+ }
+ 
+ int auth_login(arg) char *arg;
+ {
+   int r;
+ 
+   if (*arg) {
+     if (r = b64decode(arg,str_len(arg),&user) == 1) return err_input();
+   }
+   else {
+     out("334 VXNlcm5hbWU6\r\n"); flush(); /* Username: */
+     if (authgetl() < 0) return -1;
+     if (r = b64decode(authin.s,authin.len,&user) == 1) return err_input();
+   }
+   if (r == -1) die_nomem();
+ 
+   out("334 UGFzc3dvcmQ6\r\n"); flush(); /* Password: */
+ 
+   if (authgetl() < 0) return -1;
+   if (r = b64decode(authin.s,authin.len,&pass) == 1) return err_input();
+   if (r == -1) die_nomem();
+ 
+   if (!user.len || !pass.len) return err_input();
+   return authenticate();  
+ }
+ 
+ int auth_plain(arg) char *arg;
+ {
+   int r, id = 0;
+ 
+   if (*arg) {
+     if (r = b64decode(arg,str_len(arg),&slop) == 1) return err_input();
+   }
+   else {
+     out("334 ok, go on\r\n"); flush();
+     if (authgetl() < 0) return -1;
+     if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
+   }
+   if (r == -1 || !stralloc_0(&slop)) die_nomem();
+   while (slop.s[id]) id++; /* ignore authorize-id */
+ 
+   if (slop.len > id + 1)
+     if (!stralloc_copys(&user,slop.s + id + 1)) die_nomem();
+   if (slop.len > id + user.len + 2)
+     if (!stralloc_copys(&pass,slop.s + id + user.len + 2)) die_nomem();
+ 
+   if (!user.len || !pass.len) return err_input();
+   return authenticate();
+ }
+ 
+ #ifdef AUTHCRAM
+ int auth_cram()
+ {
+   int i, r;
+   char *s;
+ 
+   s = unique;
+   s += fmt_uint(s,getpid());
+   *s++ = '.';
+   s += fmt_ulong(s,(unsigned long) now());
+   *s++ = '@';
+   *s++ = 0;
+ 
+   if (!stralloc_copys(&pass,"<")) die_nomem();
+   if (!stralloc_cats(&pass,unique)) die_nomem();
+   if (!stralloc_cats(&pass,hostname)) die_nomem();
+   if (!stralloc_cats(&pass,">")) die_nomem();
+   if (b64encode(&pass,&slop) < 0) die_nomem();
+   if (!stralloc_0(&slop)) die_nomem();
+ 
+   out("334 ");
+   out(slop.s);
+   out("\r\n");
+   flush();
+ 
+   if (authgetl() < 0) return -1;
+   if (r = b64decode(authin.s,authin.len,&slop) == 1) return err_input();
+   if (r == -1 || !stralloc_0(&slop)) die_nomem();
+ 
+   i = str_chr(slop.s,' ');
+   s = slop.s + i;
+   while (*s == ' ') ++s;
+   slop.s[i] = 0;
+   if (!stralloc_copys(&user,slop.s)) die_nomem();
+   if (!stralloc_copys(&resp,s)) die_nomem();
+ 
+   if (!user.len || !resp.len) return err_input();
+   IsCRAM = 1;
+   return authenticate();
+ }
+ #endif
+ 
+ struct authcmd {
+   char *text;
+   int (*fun)();
+ } authcmds[] = {
+   { "login", auth_login }
+ , { "plain", auth_plain }
+ #ifdef AUTHCRAM
+ , { "cram-md5", auth_cram }
+ #endif
+ , { 0, err_noauth }
+ };
+ 
+ void smtp_auth(arg)
+ char *arg;
+ {
+   int i;
+   char *cmd = arg;
+ 
+   if (!hostname || !*childargs)
+   {
+     out("503 auth not available (#5.3.3)\r\n");
+     return;
+   }
+   if (authd) { err_authd(); return; }
+   if (seenmail) { err_authmail(); return; }
+ 
+   if (!stralloc_copys(&user,"")) die_nomem();
+   if (!stralloc_copys(&pass,"")) die_nomem();
+   if (!stralloc_copys(&resp,"")) die_nomem();
+ 
+   i = str_chr(cmd,' ');   
+   arg = cmd + i;
+   while (*arg == ' ') ++arg;
+   cmd[i] = 0;
+ 
+   for (i = 0;authcmds[i].text;++i)
+     if (case_equals(authcmds[i].text,cmd)) break;
+ 
+   switch (authcmds[i].fun(arg)) {
+     case 0:
+       authd = 1;
+       relayclient = "";
+       remoteinfo = user.s;
+       out("235 ok, go ahead (#2.0.0)\r\n");
+       break;
+     case 1:
+       out("535 authorization failed (#5.7.0)\r\n");
+   }
+ }
+ 
  struct commands smtpcommands[] = {
    { "rcpt", smtp_rcpt, 0 }
  , { "mail", smtp_mail, 0 }
  , { "data", smtp_data, flush }
+ , { "auth", smtp_auth, flush }
  , { "quit", smtp_quit, flush }
  , { "helo", smtp_helo, flush }
  , { "ehlo", smtp_ehlo, flush }
***************
*** 408,415 ****
  , { 0, err_unimpl, flush }
  } ;
  
! void main()
  {
    sig_pipeignore();
    if (chdir(auto_qmail) == -1) die_control();
    setup();
--- 647,659 ----
  , { 0, err_unimpl, flush }
  } ;
  
! void main(argc,argv)
! int argc;
! char **argv;
  {
+   hostname = argv[1];
+   childargs = argv + 2;
+ 
    sig_pipeignore();
    if (chdir(auto_qmail) == -1) die_control();
    setup();
