qmail-pop3d.c
changeset 0 068428edee47
equal deleted inserted replaced
-1:000000000000 0:068428edee47
       
     1 #include <sys/types.h>
       
     2 #include <sys/stat.h>
       
     3 #include "commands.h"
       
     4 #include "sig.h"
       
     5 #include "getln.h"
       
     6 #include "stralloc.h"
       
     7 #include "substdio.h"
       
     8 #include "alloc.h"
       
     9 #include "open.h"
       
    10 #include "prioq.h"
       
    11 #include "scan.h"
       
    12 #include "fmt.h"
       
    13 #include "str.h"
       
    14 #include "exit.h"
       
    15 #include "maildir.h"
       
    16 #include "readwrite.h"
       
    17 #include "timeoutread.h"
       
    18 #include "timeoutwrite.h"
       
    19 
       
    20 void die() { _exit(0); }
       
    21 
       
    22 int saferead(fd,buf,len) int fd; char *buf; int len;
       
    23 {
       
    24   int r;
       
    25   r = timeoutread(1200,fd,buf,len);
       
    26   if (r <= 0) die();
       
    27   return r;
       
    28 }
       
    29 
       
    30 int safewrite(fd,buf,len) int fd; char *buf; int len;
       
    31 {
       
    32   int r;
       
    33   r = timeoutwrite(1200,fd,buf,len);
       
    34   if (r <= 0) die();
       
    35   return r;
       
    36 }
       
    37 
       
    38 char ssoutbuf[1024];
       
    39 substdio ssout = SUBSTDIO_FDBUF(safewrite,1,ssoutbuf,sizeof ssoutbuf);
       
    40 
       
    41 char ssinbuf[128];
       
    42 substdio ssin = SUBSTDIO_FDBUF(saferead,0,ssinbuf,sizeof ssinbuf);
       
    43 
       
    44 void put(buf,len) char *buf; int len;
       
    45 {
       
    46   substdio_put(&ssout,buf,len);
       
    47 }
       
    48 void puts(s) char *s;
       
    49 {
       
    50   substdio_puts(&ssout,s);
       
    51 }
       
    52 void flush()
       
    53 {
       
    54   substdio_flush(&ssout);
       
    55 }
       
    56 void err(s) char *s;
       
    57 {
       
    58   puts("-ERR ");
       
    59   puts(s);
       
    60   puts("\r\n");
       
    61   flush();
       
    62 }
       
    63 
       
    64 void die_nomem() { err("out of memory"); die(); }
       
    65 void die_nomaildir() { err("this user has no $HOME/Maildir"); die(); }
       
    66 void die_scan() { err("unable to scan $HOME/Maildir"); die(); }
       
    67 
       
    68 void err_syntax() { err("syntax error"); }
       
    69 void err_unimpl() { err("unimplemented"); }
       
    70 void err_deleted() { err("already deleted"); }
       
    71 void err_nozero() { err("messages are counted from 1"); }
       
    72 void err_toobig() { err("not that many messages"); }
       
    73 void err_nosuch() { err("unable to open that message"); }
       
    74 void err_nounlink() { err("unable to unlink all deleted messages"); }
       
    75 
       
    76 void okay() { puts("+OK \r\n"); flush(); }
       
    77 
       
    78 void printfn(fn) char *fn;
       
    79 {
       
    80   fn += 4;
       
    81   put(fn,str_chr(fn,':'));
       
    82 }
       
    83 
       
    84 char strnum[FMT_ULONG];
       
    85 stralloc line = {0};
       
    86 
       
    87 void blast(ssfrom,limit)
       
    88 substdio *ssfrom;
       
    89 unsigned long limit;
       
    90 {
       
    91   int match;
       
    92   int inheaders = 1;
       
    93  
       
    94   for (;;) {
       
    95     if (getln(ssfrom,&line,&match,'\n') != 0) die();
       
    96     if (!match && !line.len) break;
       
    97     if (match) --line.len; /* no way to pass this info over POP */
       
    98     if (limit) if (!inheaders) if (!--limit) break;
       
    99     if (!line.len)
       
   100       inheaders = 0;
       
   101     else
       
   102       if (line.s[0] == '.')
       
   103         put(".",1);
       
   104     put(line.s,line.len);
       
   105     put("\r\n",2);
       
   106     if (!match) break;
       
   107   }
       
   108   put("\r\n.\r\n",5);
       
   109   flush();
       
   110 }
       
   111 
       
   112 stralloc filenames = {0};
       
   113 prioq pq = {0};
       
   114 
       
   115 struct message {
       
   116   int flagdeleted;
       
   117   unsigned long size;
       
   118   char *fn;
       
   119 } *m;
       
   120 int numm;
       
   121 
       
   122 int last = 0;
       
   123 
       
   124 void getlist()
       
   125 {
       
   126   struct prioq_elt pe;
       
   127   struct stat st;
       
   128   int i;
       
   129  
       
   130   maildir_clean(&line);
       
   131   if (maildir_scan(&pq,&filenames,1,1) == -1) die_scan();
       
   132  
       
   133   numm = pq.p ? pq.len : 0;
       
   134   m = (struct message *) alloc(numm * sizeof(struct message));
       
   135   if (!m) die_nomem();
       
   136  
       
   137   for (i = 0;i < numm;++i) {
       
   138     if (!prioq_min(&pq,&pe)) { numm = i; break; }
       
   139     prioq_delmin(&pq);
       
   140     m[i].fn = filenames.s + pe.id;
       
   141     m[i].flagdeleted = 0;
       
   142     if (stat(m[i].fn,&st) == -1)
       
   143       m[i].size = 0;
       
   144     else
       
   145       m[i].size = st.st_size;
       
   146   }
       
   147 }
       
   148 
       
   149 void pop3_stat()
       
   150 {
       
   151   int i;
       
   152   unsigned long total;
       
   153  
       
   154   total = 0;
       
   155   for (i = 0;i < numm;++i) if (!m[i].flagdeleted) total += m[i].size;
       
   156   puts("+OK ");
       
   157   put(strnum,fmt_uint(strnum,numm));
       
   158   puts(" ");
       
   159   put(strnum,fmt_ulong(strnum,total));
       
   160   puts("\r\n");
       
   161   flush();
       
   162 }
       
   163 
       
   164 void pop3_rset()
       
   165 {
       
   166   int i;
       
   167   for (i = 0;i < numm;++i) m[i].flagdeleted = 0;
       
   168   last = 0;
       
   169   okay();
       
   170 }
       
   171 
       
   172 void pop3_last()
       
   173 {
       
   174   puts("+OK ");
       
   175   put(strnum,fmt_uint(strnum,last));
       
   176   puts("\r\n");
       
   177   flush();
       
   178 }
       
   179 
       
   180 void pop3_quit()
       
   181 {
       
   182   int i;
       
   183   for (i = 0;i < numm;++i)
       
   184     if (m[i].flagdeleted) {
       
   185       if (unlink(m[i].fn) == -1) err_nounlink();
       
   186     }
       
   187     else
       
   188       if (str_start(m[i].fn,"new/")) {
       
   189 	if (!stralloc_copys(&line,"cur/")) die_nomem();
       
   190 	if (!stralloc_cats(&line,m[i].fn + 4)) die_nomem();
       
   191 	if (!stralloc_cats(&line,":2,")) die_nomem();
       
   192 	if (!stralloc_0(&line)) die_nomem();
       
   193 	rename(m[i].fn,line.s); /* if it fails, bummer */
       
   194       }
       
   195   okay();
       
   196   die();
       
   197 }
       
   198 
       
   199 int msgno(arg) char *arg;
       
   200 {
       
   201   unsigned long u;
       
   202   if (!scan_ulong(arg,&u)) { err_syntax(); return -1; }
       
   203   if (!u) { err_nozero(); return -1; }
       
   204   --u;
       
   205   if (u >= numm) { err_toobig(); return -1; }
       
   206   if (m[u].flagdeleted) { err_deleted(); return -1; }
       
   207   return u;
       
   208 }
       
   209 
       
   210 void pop3_dele(arg) char *arg;
       
   211 {
       
   212   int i;
       
   213   i = msgno(arg);
       
   214   if (i == -1) return;
       
   215   m[i].flagdeleted = 1;
       
   216   if (i + 1 > last) last = i + 1;
       
   217   okay();
       
   218 }
       
   219 
       
   220 void list(i,flaguidl)
       
   221 int i;
       
   222 int flaguidl;
       
   223 {
       
   224   put(strnum,fmt_uint(strnum,i + 1));
       
   225   puts(" ");
       
   226   if (flaguidl) printfn(m[i].fn);
       
   227   else put(strnum,fmt_ulong(strnum,m[i].size));
       
   228   puts("\r\n");
       
   229 }
       
   230 
       
   231 void dolisting(arg,flaguidl) char *arg; int flaguidl;
       
   232 {
       
   233   unsigned int i;
       
   234   if (*arg) {
       
   235     i = msgno(arg);
       
   236     if (i == -1) return;
       
   237     puts("+OK ");
       
   238     list(i,flaguidl);
       
   239   }
       
   240   else {
       
   241     okay();
       
   242     for (i = 0;i < numm;++i)
       
   243       if (!m[i].flagdeleted)
       
   244 	list(i,flaguidl);
       
   245     puts(".\r\n");
       
   246   }
       
   247   flush();
       
   248 }
       
   249 
       
   250 void pop3_uidl(arg) char *arg; { dolisting(arg,1); }
       
   251 void pop3_list(arg) char *arg; { dolisting(arg,0); }
       
   252 
       
   253 substdio ssmsg; char ssmsgbuf[1024];
       
   254 
       
   255 void pop3_top(arg) char *arg;
       
   256 {
       
   257   int i;
       
   258   unsigned long limit;
       
   259   int fd;
       
   260  
       
   261   i = msgno(arg);
       
   262   if (i == -1) return;
       
   263  
       
   264   arg += scan_ulong(arg,&limit);
       
   265   while (*arg == ' ') ++arg;
       
   266   if (scan_ulong(arg,&limit)) ++limit; else limit = 0;
       
   267  
       
   268   fd = open_read(m[i].fn);
       
   269   if (fd == -1) { err_nosuch(); return; }
       
   270   okay();
       
   271   substdio_fdbuf(&ssmsg,read,fd,ssmsgbuf,sizeof(ssmsgbuf));
       
   272   blast(&ssmsg,limit);
       
   273   close(fd);
       
   274 }
       
   275 
       
   276 struct commands pop3commands[] = {
       
   277   { "quit", pop3_quit, 0 }
       
   278 , { "stat", pop3_stat, 0 }
       
   279 , { "list", pop3_list, 0 }
       
   280 , { "uidl", pop3_uidl, 0 }
       
   281 , { "dele", pop3_dele, 0 }
       
   282 , { "retr", pop3_top, 0 }
       
   283 , { "rset", pop3_rset, 0 }
       
   284 , { "last", pop3_last, 0 }
       
   285 , { "top", pop3_top, 0 }
       
   286 , { "noop", okay, 0 }
       
   287 , { 0, err_unimpl, 0 }
       
   288 } ;
       
   289 
       
   290 void main(argc,argv)
       
   291 int argc;
       
   292 char **argv;
       
   293 {
       
   294   sig_alarmcatch(die);
       
   295   sig_pipeignore();
       
   296  
       
   297   if (!argv[1]) die_nomaildir();
       
   298   if (chdir(argv[1]) == -1) die_nomaildir();
       
   299  
       
   300   getlist();
       
   301 
       
   302   okay();
       
   303   commands(&ssin,pop3commands);
       
   304   die();
       
   305 }