1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <plumb.h> 5 #include <regexp.h> 6 #include <bio.h> 7 #include "faces.h" 8 9 static int showfd = -1; 10 static int seefd = -1; 11 static int logfd = -1; 12 static char *user; 13 static char *logtag; 14 15 char **maildirs; 16 int nmaildirs; 17 18 void 19 initplumb(void) 20 { 21 showfd = plumbopen("send", OWRITE); 22 seefd = plumbopen("seemail", OREAD); 23 24 if(seefd < 0){ 25 logfd = open("/sys/log/mail", OREAD); 26 seek(logfd, 0LL, 2); 27 user = getenv("user"); 28 if(user == nil){ 29 fprint(2, "faces: can't find user name: %r\n"); 30 exits("$user"); 31 } 32 logtag = emalloc(32+strlen(user)+1); 33 sprint(logtag, " delivered %s From ", user); 34 } 35 } 36 37 void 38 addmaildir(char *dir) 39 { 40 maildirs = erealloc(maildirs, (nmaildirs+1)*sizeof(char*)); 41 maildirs[nmaildirs++] = dir; 42 } 43 44 char* 45 attr(Face *f) 46 { 47 static char buf[128]; 48 49 if(f->str[Sdigest]){ 50 snprint(buf, sizeof buf, "digest=%s", f->str[Sdigest]); 51 return buf; 52 } 53 return nil; 54 } 55 56 void 57 showmail(Face *f) 58 { 59 char *s; 60 int n; 61 62 if(showfd<0 || f->str[Sshow]==nil || f->str[Sshow][0]=='\0') 63 return; 64 s = emalloc(128+strlen(f->str[Sshow])+1); 65 n = sprint(s, "faces\nshowmail\n/mail/fs/\ntext\n%s\n%ld\n%s", attr(f), strlen(f->str[Sshow]), f->str[Sshow]); 66 write(showfd, s, n); 67 free(s); 68 } 69 70 char* 71 value(Plumbattr *attr, char *key, char *def) 72 { 73 char *v; 74 75 v = plumblookup(attr, key); 76 if(v) 77 return v; 78 return def; 79 } 80 81 void 82 setname(Face *f, char *sender) 83 { 84 char *at, *bang; 85 char *p; 86 87 /* works with UTF-8, although it's written as ASCII */ 88 for(p=sender; *p!='\0'; p++) 89 *p = tolower(*p); 90 f->str[Suser] = sender; 91 at = strchr(sender, '@'); 92 if(at){ 93 *at++ = '\0'; 94 f->str[Sdomain] = estrdup(at); 95 return; 96 } 97 bang = strchr(sender, '!'); 98 if(bang){ 99 *bang++ = '\0'; 100 f->str[Suser] = estrdup(bang); 101 f->str[Sdomain] = sender; 102 return; 103 } 104 } 105 106 int 107 getc(void) 108 { 109 static uchar buf[512]; 110 static int nbuf = 0; 111 static int i = 0; 112 113 while(i == nbuf){ 114 i = 0; 115 nbuf = read(logfd, buf, sizeof buf); 116 if(nbuf == 0){ 117 sleep(15000); 118 continue; 119 } 120 if(nbuf < 0) 121 return -1; 122 } 123 return buf[i++]; 124 } 125 126 char* 127 getline(char *buf, int n) 128 { 129 int i, c; 130 131 for(i=0; i<n-1; i++){ 132 c = getc(); 133 if(c <= 0) 134 return nil; 135 if(c == '\n') 136 break; 137 buf[i] = c; 138 } 139 buf[i] = '\0'; 140 return buf; 141 } 142 143 static char* months[] = { 144 "jan", "feb", "mar", "apr", 145 "may", "jun", "jul", "aug", 146 "sep", "oct", "nov", "dec" 147 }; 148 149 static int 150 getmon(char *s) 151 { 152 int i; 153 154 for(i=0; i<nelem(months); i++) 155 if(cistrcmp(months[i], s) == 0) 156 return i; 157 return -1; 158 } 159 160 /* Fri Jul 23 14:05:14 EDT 1999 */ 161 ulong 162 parsedatev(char **a) 163 { 164 char *p; 165 Tm tm; 166 167 memset(&tm, 0, sizeof tm); 168 if((tm.mon=getmon(a[1])) == -1) 169 goto Err; 170 tm.mday = strtol(a[2], &p, 10); 171 if(*p != '\0') 172 goto Err; 173 tm.hour = strtol(a[3], &p, 10); 174 if(*p != ':') 175 goto Err; 176 tm.min = strtol(p+1, &p, 10); 177 if(*p != ':') 178 goto Err; 179 tm.sec = strtol(p+1, &p, 10); 180 if(*p != '\0') 181 goto Err; 182 if(strlen(a[4]) != 3) 183 goto Err; 184 strcpy(tm.zone, a[4]); 185 if(strlen(a[5]) != 4) 186 goto Err; 187 tm.year = strtol(a[5], &p, 10); 188 if(*p != '\0') 189 goto Err; 190 tm.year -= 1900; 191 return tm2sec(&tm); 192 Err: 193 return time(0); 194 } 195 196 ulong 197 parsedate(char *s) 198 { 199 char *f[10]; 200 int nf; 201 202 nf = getfields(s, f, nelem(f), 1, " "); 203 if(nf < 6) 204 return time(0); 205 return parsedatev(f); 206 } 207 208 /* achille Jul 23 14:05:15 delivered jmk From ms.com!bub Fri Jul 23 14:05:14 EDT 1999 (plan9.bell-labs.com!jmk) 1352 */ 209 /* achille Oct 26 13:45:42 remote local!rsc From rsc Sat Oct 26 13:45:41 EDT 2002 (rsc) 170 */ 210 int 211 parselog(char *s, char **sender, ulong *xtime) 212 { 213 char *f[20]; 214 int nf; 215 216 nf = getfields(s, f, nelem(f), 1, " "); 217 if(nf < 14) 218 return 0; 219 if(strcmp(f[4], "delivered") == 0 && strcmp(f[5], user) == 0) 220 goto Found; 221 if(strcmp(f[4], "remote") == 0 && strncmp(f[5], "local!", 6) == 0 && strcmp(f[5]+6, user) == 0) 222 goto Found; 223 return 0; 224 225 Found: 226 *sender = estrdup(f[7]); 227 *xtime = parsedatev(&f[8]); 228 return 1; 229 } 230 231 int 232 logrecv(char **sender, ulong *xtime) 233 { 234 char buf[4096]; 235 236 for(;;){ 237 if(getline(buf, sizeof buf) == nil) 238 return 0; 239 if(parselog(buf, sender, xtime)) 240 return 1; 241 } 242 } 243 244 char* 245 tweakdate(char *d) 246 { 247 char e[8]; 248 249 /* d, date = "Mon Aug 2 23:46:55 EDT 1999" */ 250 251 if(strlen(d) < strlen("Mon Aug 2 23:46:55 EDT 1999")) 252 return estrdup(""); 253 if(strncmp(date, d, 4+4+3) == 0) 254 snprint(e, sizeof e, "%.5s", d+4+4+3); /* 23:46 */ 255 else 256 snprint(e, sizeof e, "%.6s", d+4); /* Aug 2 */ 257 return estrdup(e); 258 } 259 260 Face* 261 nextface(void) 262 { 263 int i; 264 Face *f; 265 Plumbmsg *m; 266 char *t, *senderp, *showmailp, *digestp; 267 ulong xtime; 268 269 f = emalloc(sizeof(Face)); 270 for(;;){ 271 if(seefd >= 0){ 272 m = plumbrecv(seefd); 273 if(m == nil) 274 killall("error on seemail plumb port"); 275 t = value(m->attr, "mailtype", ""); 276 if(strcmp(t, "delete") == 0) 277 delete(m->data, value(m->attr, "digest", nil)); 278 else if(strcmp(t, "new") != 0) 279 fprint(2, "faces: unknown plumb message type %s\n", t); 280 else for(i=0; i<nmaildirs; i++) 281 if(strncmp(m->data, maildirs[i], strlen(maildirs[i])) == 0) 282 goto Found; 283 plumbfree(m); 284 continue; 285 286 Found: 287 xtime = parsedate(value(m->attr, "date", date)); 288 digestp = value(m->attr, "digest", nil); 289 if(alreadyseen(digestp)){ 290 /* duplicate upas/fs can send duplicate messages */ 291 plumbfree(m); 292 continue; 293 } 294 senderp = estrdup(value(m->attr, "sender", "???")); 295 showmailp = estrdup(m->data); 296 if(digestp) 297 digestp = estrdup(digestp); 298 plumbfree(m); 299 }else{ 300 if(logrecv(&senderp, &xtime) <= 0) 301 killall("error reading log file"); 302 showmailp = estrdup(""); 303 digestp = nil; 304 } 305 setname(f, senderp); 306 f->time = xtime; 307 f->tm = *localtime(xtime); 308 f->str[Sshow] = showmailp; 309 f->str[Sdigest] = digestp; 310 return f; 311 } 312 } 313 314 char* 315 iline(char *data, char **pp) 316 { 317 char *p; 318 319 for(p=data; *p!='\0' && *p!='\n'; p++) 320 ; 321 if(*p == '\n') 322 *p++ = '\0'; 323 *pp = p; 324 return data; 325 } 326 327 Face* 328 dirface(char *dir, char *num) 329 { 330 Face *f; 331 char *from, *date; 332 char buf[1024], pwd[1024], *info, *p, *digest; 333 int n, fd; 334 ulong len; 335 336 /* 337 * loadmbox leaves us in maildir, so we needn't 338 * walk /mail/fs/mbox for each face; this makes startup 339 * a fair bit quicker. 340 */ 341 if(getwd(pwd, sizeof pwd) != nil && strcmp(pwd, dir) == 0) 342 sprint(buf, "%s/info", num); 343 else 344 sprint(buf, "%s/%s/info", dir, num); 345 len = dirlen(buf); 346 if(len <= 0) 347 return nil; 348 fd = open(buf, OREAD); 349 if(fd < 0) 350 return nil; 351 info = emalloc(len+1); 352 n = readn(fd, info, len); 353 close(fd); 354 if(n < 0){ 355 free(info); 356 return nil; 357 } 358 info[n] = '\0'; 359 f = emalloc(sizeof(Face)); 360 from = iline(info, &p); /* from */ 361 iline(p, &p); /* to */ 362 iline(p, &p); /* cc */ 363 iline(p, &p); /* replyto */ 364 date = iline(p, &p); /* date */ 365 setname(f, estrdup(from)); 366 f->time = parsedate(date); 367 f->tm = *localtime(f->time); 368 sprint(buf, "%s/%s", dir, num); 369 f->str[Sshow] = estrdup(buf); 370 iline(p, &p); /* subject */ 371 iline(p, &p); /* mime content type */ 372 iline(p, &p); /* mime disposition */ 373 iline(p, &p); /* filename */ 374 digest = iline(p, &p); /* digest */ 375 f->str[Sdigest] = estrdup(digest); 376 free(info); 377 return f; 378 } 379