--- ../qmail-1.03-unpatched/qmail-popup.c	1998-06-15 12:53:16.000000000 +0200
+++ qmail-popup.c	2004-03-08 19:08:13.000000000 +0100
@@ -14,6 +14,9 @@
 #include "timeoutread.h"
 #include "timeoutwrite.h"
 
+#include "case.h"
+#include "base64.h"
+
 void die() { _exit(1); }
 
 int saferead(fd,buf,len) int fd; char *buf; int len;
@@ -64,15 +67,20 @@
 
 void err_syntax() { err("syntax error"); }
 void err_wantuser() { err("USER first"); }
-void err_authoriz() { err("authorization first"); }
+void err_authoriz(arg) char *arg; { err("authorization first"); }
 
-void okay() { puts("+OK \r\n"); flush(); }
-void pop3_quit() { okay(); die(); }
+void okay(arg) char *arg; { puts("+OK \r\n"); flush(); }
+void pop3_quit(arg) char *arg; { okay(0); die(); }
 
 
 char unique[FMT_ULONG + FMT_ULONG + 3];
 char *hostname;
-stralloc username = {0};
+stralloc authin = {0};   /* input from POP3 client */
+stralloc username = {0}; /* username */
+stralloc chal = {0};     /* challenge */
+stralloc slop = {0};     /* b64 challenge */
+stralloc resp = {0};     /* b64 response */
+
 int seenuser = 0;
 char **childargs;
 substdio ssup;
@@ -133,10 +141,23 @@
   puts(">\r\n");
   flush();
 }
+void pop3_capa(arg) 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 CRAM-MD5\r\n");    
+  puts(".\r\n");
+  flush();
+}
+
 void pop3_user(arg) char *arg;
 {
   if (!*arg) { err_syntax(); return; }
-  okay();
+  okay(0);
   seenuser = 1;
   if (!stralloc_copys(&username,arg)) die_nomem(); 
   if (!stralloc_0(&username)) die_nomem(); 
@@ -156,11 +177,100 @@
   doanddie(arg,space - arg,space);
 }
 
+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();
+    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) { die_badauth(); }
+  if (authin.len == 0) { die_badauth(); }
+  return authin.len;
+}
+
+
+void pop3_auth_cram(arg) char *arg;
+{
+  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(&chal,"<")) die_nomem();          /* generate challenge */
+  if (!stralloc_cats(&chal,unique)) die_nomem();
+  if (!stralloc_cats(&chal,hostname)) die_nomem();
+  if (!stralloc_cats(&chal,">")) die_nomem();
+  if (b64encode(&chal,&slop) < 0) die_nomem();
+  if (!stralloc_0(&slop)) die_nomem();
+
+  puts("+ ");
+  puts(slop.s);
+  puts("\r\n");
+  flush();
+
+  if (authgetl() < 0) die_badauth();                        /* got response */
+
+  if (r = b64decode(authin.s,authin.len,&resp) == 1) die_badauth();
+
+  /* if (r == -1 || !stralloc_0(&resp)) die_nomem(); */
+  if (r == -1) die_nomem();
+
+  i = str_chr(resp.s,' ');
+  s = resp.s + i;
+  while (*s == ' ') ++s;
+  resp.s[i] = 0;
+
+  if (!stralloc_copys(&username,resp.s)) die_nomem();       /* userid */
+  if (!stralloc_0(&username)) die_nomem();
+  if (!username.len || !s) die_badauth();
+
+  doanddie(username.s,username.len,s);
+}
+struct authcmd {
+  char *text;
+  void (*fun)();
+} authcmds[] = {
+  { "cram-md5",pop3_auth_cram }
+,  { 0,die_badauth }
+};
+
+void pop3_auth(arg) char *arg;
+{
+  int i;
+  char *cmd = arg;
+
+  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)) authcmds[i].fun(arg);
+
+  puts("-ERR unsupported method\r\n"); flush(); return;
+}
+
 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 }
 } ;
