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 }