dirpop3d.c (5428B)
1 /* 2 3 dirpop3d -- pop3 server for w3mail 4 5 Copyright (C) 2011 Tomas Hlavaty 6 7 This program is free software: you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation, either version 3 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program. If not, see 19 <http://www.gnu.org/licenses/>. 20 21 */ 22 23 #include <stdlib.h> 24 #include <string.h> 25 #include <stdarg.h> 26 #include <unistd.h> 27 28 #include "utils.h" 29 30 struct msg { 31 int deleted; 32 int nbytes; 33 char *fname; 34 }; 35 36 struct flist { 37 int len; 38 int n; 39 int nbytes; 40 struct msg msg[]; 41 }; 42 43 static int sizeof_flist(int len) { 44 return 3 * sizeof(int) + sizeof(struct msg) * len; 45 } 46 47 static struct flist *flist_new(int len) { 48 struct flist *x = malloc(sizeof_flist(len)); 49 x->len = len; 50 x->n = 0; 51 x->nbytes = 0; 52 return x; 53 } 54 55 static void flist_free(struct flist *x) { 56 for(int i = 0; i < x->n; i++) 57 free(x->msg[i].fname); 58 free(x); 59 } 60 61 static struct flist *flist_realloc(struct flist *x, int len) { 62 if(x->len < len) { 63 struct flist *y = realloc(x, sizeof_flist(len)); 64 y->len = len; 65 return y; 66 } 67 return x; 68 } 69 70 static struct flist *flist_add(struct flist *x, char *fname, int nbytes) { 71 if(x->len <= x->n) 72 x = flist_realloc(x, x->len + 100); 73 x->nbytes += nbytes; 74 struct msg *y = &x->msg[x->n++]; 75 y->deleted = 0; 76 y->nbytes = nbytes; 77 y->fname = malloc(strlen(fname) + 1); 78 strcpy(y->fname, fname); 79 return x; 80 } 81 82 struct xlist { 83 struct flist *flist; 84 }; 85 86 static int list_cb(void *udata, char *path, char *fname, struct dirent *e, struct stat *s) { 87 struct xlist *x = udata; 88 if(S_ISREG(s->st_mode)) 89 x->flist = flist_add(x->flist, fname, s->st_size); 90 return 1; 91 } 92 93 static int line(int fd, char *buf, char *end) { 94 int n = 0; 95 while(buf < end) { 96 char c; 97 int m = read(fd, &c, sizeof(char)); 98 if(m <= 0) break; 99 if(m != sizeof(char)) exit(-1); 100 *buf++ = c; 101 n++; 102 if('\n' == c) break; 103 } 104 return n; 105 } 106 107 static inline void crlf() { 108 printf("\r\n"); 109 fflush(stdout); 110 } 111 112 static void pr(char *fmt, ...) { 113 va_list v; 114 va_start(v, fmt); 115 vprintf(fmt, v); 116 crlf(); 117 va_end(v); 118 } 119 120 int main(int argc, char *argv[]) { 121 if(argc != 2) quit(1, "Usage: %s rootdir", argv[0]); 122 char *rootdir = argv[1]; 123 pr("+OK dirpop3d ready"); 124 enum {START, USER, PASS, STAT, LIST} state = START; 125 char cmd[BLEN], usr[BLEN], pwd[BLEN]; 126 struct flist *flist = NULL; 127 char buf[BLEN]; 128 for(int n; 0 < (n = line(0, buf, buf + BLEN));) { 129 rtrim(buf); 130 sscanf(buf, "%s", cmd); 131 if(0 == strcmp("QUIT", cmd)) { 132 if(flist) { 133 for(int i = 0; i < flist->n; i++) { 134 struct msg *x = &flist->msg[i]; 135 if(x->deleted) unlink(x->fname); 136 } 137 flist_free(flist); 138 flist = NULL; 139 } 140 pr("+OK dirpop3d bye"); 141 break; 142 } 143 else if(0 == strcmp("USER", cmd)) { 144 if(state != START) pr("-ERR"); 145 else { 146 sscanf(buf, "%s %s", cmd, usr); 147 pr("+OK hi %s", usr); 148 state = USER; 149 } 150 } 151 else if(0 == strcmp("PASS", cmd)) { 152 if(state != USER) pr("-ERR"); 153 else { 154 sscanf(buf, "%s %s", cmd, pwd); 155 pr("+OK welcome %s", usr); 156 state = PASS; 157 } 158 } 159 else if(0 == strcmp("STAT", cmd)) { 160 if(state != PASS) pr("-ERR"); 161 else { 162 if(!flist) { 163 flist = flist_new(100); 164 struct xlist x = {flist}; 165 dir(&x, list_cb, "%s/%s", rootdir, usr); 166 flist = x.flist; 167 } 168 pr("+OK %d %d", flist->n, flist->nbytes); 169 state = STAT; 170 } 171 } 172 else if(0 == strcmp("LIST", cmd)) { 173 if(state != STAT) pr("-ERR"); 174 else { 175 pr("+OK %d messages (%d octets)", flist->n, flist->nbytes); 176 for(int i = 0; i < flist->n; i++) 177 pr("%d %d", i + 1, flist->msg[i].nbytes); 178 pr("."); 179 state = LIST; 180 } 181 } 182 else if(0 == strcmp("RETR", cmd)) { 183 if(state != STAT) pr("-ERR"); 184 else { 185 int n = 0; 186 sscanf(buf, "%s %d", cmd, &n); 187 if(0 < n && n <= flist->n) { 188 struct msg *x = &flist->msg[n - 1]; 189 pr("+OK %d octets", x->nbytes); 190 FILE *in = fopen(x->fname, "r"); 191 if(!in) die(1, "echo(): cannot open input file '%s'", x->fname); 192 /* Note that the served files must have lines shorter than 193 BLEN and no single dot char on its own line. Therefore 194 use base64 for the message body. */ 195 char buf[BLEN]; 196 while(fgets(buf, BLEN, in)) { 197 rtrim(buf); 198 pr("%s", buf); 199 } 200 fclose(in); 201 pr("."); 202 } 203 else pr("-ERR"); 204 } 205 } 206 else if(0 == strcmp("DELE", cmd)) { 207 if(state != STAT) pr("-ERR"); 208 else { 209 int n = 0; 210 sscanf(buf, "%s %d", cmd, &n); 211 if(0 < n && n <= flist->n) { 212 struct msg *x = &flist->msg[n - 1]; 213 x->deleted = 1; 214 pr("+OK message %d deleted", n); 215 } 216 else pr("-ERR"); 217 } 218 } 219 else pr("-ERR wrong command"); 220 } 221 }