debian/rules: Use `git' potty wrapper.
[qmail] / qmail-pop3d.c
index c3ff0a6..51976c2 100644 (file)
@@ -1,60 +1,69 @@
 #include <sys/types.h>
 #include <sys/stat.h>
-#include "direntry.h"
+#include "commands.h"
 #include "sig.h"
 #include "getln.h"
 #include "stralloc.h"
 #include "substdio.h"
 #include "alloc.h"
-#include "datetime.h"
-#include "prot.h"
 #include "open.h"
 #include "prioq.h"
 #include "scan.h"
 #include "fmt.h"
-#include "error.h"
 #include "str.h"
 #include "exit.h"
-#include "now.h"
+#include "maildir.h"
 #include "readwrite.h"
+#include "timeoutread.h"
+#include "timeoutwrite.h"
 
-int timeout = 1200;
+void die() { _exit(0); }
 
-char ssoutbuf[1024];
-substdio ssout = SUBSTDIO_FDBUF(write,1,ssoutbuf,sizeof(ssoutbuf));
+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 timeoutread(fd,buf,n) int fd; char *buf; int n;
+int safewrite(fd,buf,len) int fd; char *buf; int len;
 {
- int r; int saveerrno;
- alarm(timeout);
- r = read(fd,buf,n); saveerrno = errno;
- alarm(0);
- errno = saveerrno; return r;
+  int r;
+  r = timeoutwrite(1200,fd,buf,len);
+  if (r <= 0) die();
+  return r;
 }
 
-char ssinbuf[128];
-substdio ssin = SUBSTDIO_FDBUF(timeoutread,0,ssinbuf,sizeof(ssinbuf));
+char ssoutbuf[1024];
+substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
 
+char ssinbuf[128];
+substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
 
-void die() { _exit(0); }
+void put(buf,len) char *buf; int len;
+{
+  substdio_put(&ssout,buf,len);
+}
 void puts(s) char *s;
 {
if (substdio_puts(&ssout,s) == -1) die();
 substdio_puts(&ssout,s);
 }
 void flush()
 {
if (substdio_flush(&ssout) == -1) die();
 substdio_flush(&ssout);
 }
 void err(s) char *s;
 {
- puts("-ERR ");
- puts(s);
- puts("\r\n");
if (substdio_flush(&ssout) == -1) die();
 puts("-ERR ");
 puts(s);
 puts("\r\n");
 flush();
 }
+
 void die_nomem() { err("out of memory"); die(); }
-void die_prot() { err("protection problem"); die(); }
 void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
+void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
 
 void err_syntax() { err("syntax error"); }
 void err_unimpl() { err("unimplemented"); }
@@ -65,333 +74,232 @@ void err_nosuch() { err("unable to open that message"); }
 void err_nounlink() { err("unable to unlink all deleted messages"); }
 
 void okay() { puts("+OK \r\n"); flush(); }
-void pop3_last() { puts("+OK 0\r\n"); flush(); }
 
+void printfn(fn) char *fn;
+{
+  fn += 4;
+  put(fn,str_chr(fn,':'));
+}
 
-stralloc dataline = {0};
+char strnum[FMT_ULONG];
+stralloc line = {0};
+
+void blast(ssfrom,limit)
+substdio *ssfrom;
+unsigned long limit;
+{
+  int match;
+  int inheaders = 1;
+  for (;;) {
+    if (getln(ssfrom,&line,&match,'\n') != 0) die();
+    if (!match && !line.len) break;
+    if (match) --line.len; /* no way to pass this info over POP */
+    if (limit) if (!inheaders) if (!--limit) break;
+    if (!line.len)
+      inheaders = 0;
+    else
+      if (line.s[0] == '.')
+        put(".",1);
+    put(line.s,line.len);
+    put("\r\n",2);
+    if (!match) break;
+  }
+  put("\r\n.\r\n",5);
+  flush();
+}
 
 stralloc filenames = {0};
 prioq pq = {0};
-stralloc newname = {0};
 
-struct message
- {
+struct message {
   int flagdeleted;
   unsigned long size;
   char *fn;
- }
-*m;
+} *m;
 int numm;
 
-substdio ssmsg; char ssmsgbuf[1024];
-
-
-void blast(ssfrom,limit)
-substdio *ssfrom;
-unsigned long limit;
-{
- int match;
- int inheaders = 1;
-
- for (;;)
-  {
-   if (getln(ssfrom,&dataline,&match,'\n') != 0) die();
-   if (!match && !dataline.len) break;
-   if (match) --dataline.len; /* no way to pass this info over POP */
-   if (limit) if (!inheaders) if (!--limit) break;
-   if (!dataline.len)
-     inheaders = 0;
-   else
-     if (dataline.s[0] == '.')
-       substdio_put(&ssout,".",1);
-   if (substdio_put(&ssout,dataline.s,dataline.len) == -1) die();
-   if (substdio_put(&ssout,"\r\n",2) == -1) die();
-   if (!match) break;
-  }
- if (substdio_put(&ssout,"\r\n.\r\n",5) == -1) die();
- if (substdio_flush(&ssout) == -1) die();
-}
+int last = 0;
 
 void getlist()
 {
- unsigned long pos;
- datetime_sec time;
- DIR *dir;
- direntry *d;
- struct prioq_elt pe;
- struct stat st;
- int i;
-
- numm = 0;
-
- time = now();
-
- if (dir = opendir("tmp"))
-  {
-   while (d = readdir(dir))
-    {
-     if (str_equal(d->d_name,".")) continue;
-     if (str_equal(d->d_name,"..")) continue;
-     if (!stralloc_copys(&newname,"tmp/")) die_nomem();
-     if (!stralloc_cats(&newname,d->d_name)) die_nomem();
-     if (!stralloc_0(&newname)) die_nomem();
-     if (stat(newname.s,&st) == 0)
-       if (time > st.st_atime + 129600)
-        unlink(newname.s);
-    }
-   closedir(dir);
-  }
-
- if (!stralloc_copys(&filenames,"")) die_nomem();
-
- if (dir = opendir("new"))
-  {
-   while (d = readdir(dir))
-    {
-     if (str_equal(d->d_name,".")) continue;
-     if (str_equal(d->d_name,"..")) continue;
-     pos = filenames.len;
-     if (!stralloc_cats(&filenames,"new/")) die_nomem();
-     if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
-     if (!stralloc_0(&filenames)) die_nomem();
-     if (stat(filenames.s + pos,&st) == 0)
-      {
-       pe.dt = st.st_mtime;
-       pe.id = pos;
-       if (!prioq_insert(&pq,&pe)) die_nomem();
-       ++numm;
-      }
-    }
-   closedir(dir);
-  }
-
- if (dir = opendir("cur"))
-  {
-   while (d = readdir(dir))
-    {
-     if (str_equal(d->d_name,".")) continue;
-     if (str_equal(d->d_name,"..")) continue;
-     pos = filenames.len;
-     if (!stralloc_cats(&filenames,"cur/")) die_nomem();
-     if (!stralloc_cats(&filenames,d->d_name)) die_nomem();
-     if (!stralloc_0(&filenames)) die_nomem();
-     if (stat(filenames.s + pos,&st) == 0)
-      {
-       pe.dt = st.st_mtime;
-       pe.id = pos;
-       if (!prioq_insert(&pq,&pe)) die_nomem();
-       ++numm;
-      }
-    }
-   closedir(dir);
-  }
-
- m = (struct message *) alloc(numm * sizeof(struct message));
- if (!m) die_nomem();
-
- for (i = 0;i < numm;++i)
-  {
-   if (!prioq_min(&pq,&pe)) { numm = i; break; }
-   prioq_delmin(&pq);
-   m[i].fn = filenames.s + pe.id;
-   m[i].flagdeleted = 0;
-   if (stat(m[i].fn,&st) == -1)
-     m[i].size = 0;
-   else
-     m[i].size = st.st_size;
+  struct prioq_elt pe;
+  struct stat st;
+  int i;
+  maildir_clean(&line);
+  if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan();
+  numm = pq.p ? pq.len : 0;
+  m = (struct message *) alloc(numm * sizeof(struct message));
+  if (!m) die_nomem();
+  for (i = 0;i < numm;++i) {
+    if (!prioq_min(&pq,&pe)) { numm = i; break; }
+    prioq_delmin(&pq);
+    m[i].fn = filenames.s + pe.id;
+    m[i].flagdeleted = 0;
+    if (stat(m[i].fn,&st) == -1)
+      m[i].size = 0;
+    else
+      m[i].size = st.st_size;
   }
 }
 
-char foo[FMT_ULONG];
-
-void printint(u) unsigned int u;
-{
- foo[fmt_uint(foo,u)] = 0;
- puts(foo);
- puts(" ");
-}
-
-void printlong(u) unsigned long u;
-{
- foo[fmt_uint(foo,u)] = 0;
- puts(foo);
- puts("\r\n");
-}
-
-void printfn(fn) char *fn;
+void pop3_stat()
 {
- puts(fn + 4);
- puts("\r\n");
+  int i;
+  unsigned long total;
+  total = 0;
+  for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
+  puts("+OK ");
+  put(strnum,fmt_uint(strnum,numm));
+  puts(" ");
+  put(strnum,fmt_ulong(strnum,total));
+  puts("\r\n");
+  flush();
 }
 
-void pop3_stat()
+void pop3_rset()
 {
- int i;
- unsigned long total;
-
- total = 0;
- for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
- puts("+OK ");
- printint(numm);
- printlong(total);
- flush();
+  int i;
+  for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
+  last = 0;
+  okay();
 }
 
-void pop3_rset()
+void pop3_last()
 {
- int i;
- for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
- okay();
+  puts("+OK ");
+  put(strnum,fmt_uint(strnum,last));
+  puts("\r\n");
+  flush();
 }
 
 void pop3_quit()
 {
- int i;
- for (i = 0;i < numm;++i)
-   if (m[i].flagdeleted)
-     if (unlink(m[i].fn) == -1) err_nounlink();
- okay();
- die();
+  int i;
+  for (i = 0;i < numm;++i)
+    if (m[i].flagdeleted) {
+      if (unlink(m[i].fn) == -1) err_nounlink();
+    }
+    else
+      if (str_start(m[i].fn,"new/")) {
+       if (!stralloc_copys(&line,"cur/")) die_nomem();
+       if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem();
+       if (!stralloc_cats(&line,":2,")) die_nomem();
+       if (!stralloc_0(&line)) die_nomem();
+       rename(m[i].fn,line.s); /* if it fails, bummer */
+      }
+  okay();
+  die();
 }
 
 int msgno(arg) char *arg;
 {
- unsigned long u;
- if (!arg) { err_syntax(); return -1; }
- if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
- if (!u) { err_nozero(); return -1; }
- --u;
- if (u >= numm) { err_toobig(); return -1; }
- if (m[u].flagdeleted) { err_deleted(); return -1; }
- return u;
+  unsigned long u;
+  if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
+  if (!u) { err_nozero(); return -1; }
+  --u;
+  if (u >= numm) { err_toobig(); return -1; }
+  if (m[u].flagdeleted) { err_deleted(); return -1; }
+  return u;
 }
 
 void pop3_dele(arg) char *arg;
 {
- int i;
+  int i;
+  i = msgno(arg);
+  if (i == -1) return;
+  m[i].flagdeleted = 1;
+  if (i + 1 > last) last = i + 1;
+  okay();
+}
 
- i = msgno(arg);
- if (i == -1) return;
- m[i].flagdeleted = 1;
- okay();
+void list(i,flaguidl)
+int i;
+int flaguidl;
+{
+  put(strnum,fmt_uint(strnum,i + 1));
+  puts(" ");
+  if (flaguidl) printfn(m[i].fn);
+  else put(strnum,fmt_ulong(strnum,m[i].size));
+  puts("\r\n");
 }
 
 void dolisting(arg,flaguidl) char *arg; int flaguidl;
 {
- unsigned int i;
-
- if (arg)
-  {
-   i = msgno(arg);
-   if (i == -1) return;
-   puts("+OK ");
-   printint(i + 1);
-   if (flaguidl) printfn(m[i].fn); else printlong(m[i].size);
+  unsigned int i;
+  if (*arg) {
+    i = msgno(arg);
+    if (i == -1) return;
+    puts("+OK ");
+    list(i,flaguidl);
   }
- else
-  {
-   okay();
-
-   for (i = 0;i < numm;++i)
-     if (!m[i].flagdeleted)
-      {
-       printint(i + 1);
-       if (flaguidl) printfn(m[i].fn); else printlong(m[i].size);
-      }
-   puts(".\r\n");
+  else {
+    okay();
+    for (i = 0;i < numm;++i)
+      if (!m[i].flagdeleted)
+       list(i,flaguidl);
+    puts(".\r\n");
   }
- flush();
 flush();
 }
 
 void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
 void pop3_list(arg) char *arg; { dolisting(arg,0); }
 
+substdio ssmsg; char ssmsgbuf[1024];
+
 void pop3_top(arg) char *arg;
 {
- int i;
- unsigned long limit;
- int fd;
-
- i = msgno(arg);
- if (i == -1) return;
-
- arg += scan_ulong(arg,&limit);
- while (*arg == ' ') ++arg;
- if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
-
- fd = open_read(m[i].fn);
- if (fd == -1) { err_nosuch(); return; }
- okay();
- substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
- blast(&ssmsg,limit);
- close(fd);
 int i;
 unsigned long limit;
 int fd;
 i = msgno(arg);
 if (i == -1) return;
 arg += scan_ulong(arg,&limit);
 while (*arg == ' ') ++arg;
 if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
 fd = open_read(m[i].fn);
 if (fd == -1) { err_nosuch(); return; }
 okay();
 substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
 blast(&ssmsg,limit);
 close(fd);
 }
 
-static struct { void (*fun)(); char *text; } pop3cmd[] = {
-  { pop3_quit, "quit" }
-, { pop3_stat, "stat" }
-, { pop3_list, "list" }
-, { pop3_uidl, "uidl" }
-, { pop3_dele, "dele" }
-, { pop3_top, "retr" }
-, { pop3_rset, "rset" }
-, { pop3_last, "last" }
-, { pop3_top, "top" }
-, { okay, "noop" }
-, { 0, 0 }
-};
-
-void doit(cmd)
-char *cmd;
-{
- int i;
- int j;
- char ch;
-
- for (i = 0;pop3cmd[i].fun;++i)
-  {
-   for (j = 0;ch = pop3cmd[i].text[j];++j)
-     if ((cmd[j] != ch) && (cmd[j] != ch - 32))
-       break;
-   if (!ch)
-     if (!cmd[j] || (cmd[j] == ' '))
-      {
-       while (cmd[j] == ' ') ++j;
-       if (!cmd[j])
-         pop3cmd[i].fun((char *) 0);
-       else
-         pop3cmd[i].fun(cmd + j);
-       return;
-      }
-  }
- err_unimpl();
-}
+struct commands pop3commands[] = {
+  { "quit", pop3_quit, 0 }
+, { "stat", pop3_stat, 0 }
+, { "list", pop3_list, 0 }
+, { "uidl", pop3_uidl, 0 }
+, { "dele", pop3_dele, 0 }
+, { "retr", pop3_top, 0 }
+, { "rset", pop3_rset, 0 }
+, { "last", pop3_last, 0 }
+, { "top", pop3_top, 0 }
+, { "noop", okay, 0 }
+, { 0, err_unimpl, 0 }
+} ;
 
 void main(argc,argv)
 int argc;
 char **argv;
 {
- static stralloc cmd = {0};
- int match;
-
- sig_alarmcatch(die);
- sig_pipeignore();
-
- if (!argv[1]) die_nomaildir();
- if (chdir(argv[1]) == -1) die_nomaildir();
-
- getlist();
-
- okay();
-
- for (;;)
-  {
-   if (getln(&ssin,&cmd,&match,'\n') == -1) die();
-   if (!match) die();
-   if (cmd.len == 0) die();
-   if (cmd.s[--cmd.len] != '\n') die();
-   if ((cmd.len > 0) && (cmd.s[cmd.len - 1] == '\r')) --cmd.len;
-   cmd.s[cmd.len++] = 0;
-   doit(cmd.s);
-  }
+  sig_alarmcatch(die);
+  sig_pipeignore();
+  if (!argv[1]) die_nomaildir();
+  if (chdir(argv[1]) == -1) die_nomaildir();
+  getlist();
+
+  okay();
+  commands(&ssin,pop3commands);
+  die();
 }