nightmaremail

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

qmail-pop3d.c (6284B)


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