1 #include "common.h"
2 #include "send.h"
3
4 #include "../smtp/smtp.h"
5 #include "../smtp/y.tab.h"
6
7 /* global to this file */
8 static Reprog *rfprog;
9 static Reprog *fprog;
10
11 #define VMLIMIT (64*1024)
12 #define MSGLIMIT (128*1024*1024)
13
14 int received; /* from rfc822.y */
15
16 static String* getstring(Node *p);
17 static String* getaddr(Node *p);
18
19 extern int
default_from(message * mp)20 default_from(message *mp)
21 {
22 char *cp, *lp;
23
24 cp = getenv("upasname");
25 lp = getlog();
26 if(lp == nil)
27 return -1;
28
29 if(cp && *cp)
30 s_append(mp->sender, cp);
31 else
32 s_append(mp->sender, lp);
33 s_append(mp->date, thedate());
34 return 0;
35 }
36
37 extern message *
m_new(void)38 m_new(void)
39 {
40 message *mp;
41
42 mp = (message *)mallocz(sizeof(message), 1);
43 if (mp == 0) {
44 perror("message:");
45 exit(1);
46 }
47 mp->sender = s_new();
48 mp->replyaddr = s_new();
49 mp->date = s_new();
50 mp->body = s_new();
51 mp->size = 0;
52 mp->fd = -1;
53 return mp;
54 }
55
56 extern void
m_free(message * mp)57 m_free(message *mp)
58 {
59 if(mp->fd >= 0){
60 close(mp->fd);
61 sysremove(s_to_c(mp->tmp));
62 s_free(mp->tmp);
63 }
64 s_free(mp->sender);
65 s_free(mp->date);
66 s_free(mp->body);
67 s_free(mp->havefrom);
68 s_free(mp->havesender);
69 s_free(mp->havereplyto);
70 s_free(mp->havesubject);
71 free((char *)mp);
72 }
73
74 /* read a message into a temp file, return an open fd to it in mp->fd */
75 static int
m_read_to_file(Biobuf * fp,message * mp)76 m_read_to_file(Biobuf *fp, message *mp)
77 {
78 int fd;
79 int n;
80 String *file;
81 char buf[4*1024];
82
83 file = s_new();
84 /*
85 * create temp file to be removed on close
86 */
87 abspath("mtXXXXXX", UPASTMP, file);
88 mktemp(s_to_c(file));
89 if((fd = syscreate(s_to_c(file), ORDWR|ORCLOSE, 0600))<0){
90 s_free(file);
91 return -1;
92 }
93 mp->tmp = file;
94
95 /*
96 * read the rest into the temp file
97 */
98 while((n = Bread(fp, buf, sizeof(buf))) > 0){
99 if(write(fd, buf, n) != n){
100 close(fd);
101 return -1;
102 }
103 mp->size += n;
104 if(mp->size > MSGLIMIT){
105 mp->size = -1;
106 break;
107 }
108 }
109
110 mp->fd = fd;
111 return 0;
112 }
113
114 /* get the first address from a node */
115 static String*
getaddr(Node * p)116 getaddr(Node *p)
117 {
118 for(; p; p = p->next)
119 if(p->s && p->addr)
120 return s_copy(s_to_c(p->s));
121 return nil;
122 }
123
124 /* get the text of a header line minus the field name */
125 static String*
getstring(Node * p)126 getstring(Node *p)
127 {
128 String *s;
129
130 s = s_new();
131 if(p == nil)
132 return s;
133
134 for(p = p->next; p; p = p->next){
135 if(p->s){
136 s_append(s, s_to_c(p->s));
137 }else{
138 s_putc(s, p->c);
139 s_terminate(s);
140 }
141 if(p->white)
142 s_append(s, s_to_c(p->white));
143 }
144 return s;
145 }
146
147 static char *fieldname[] =
148 {
149 [WORD-WORD] "WORD",
150 [DATE-WORD] "DATE",
151 [RESENT_DATE-WORD] "RESENT_DATE",
152 [RETURN_PATH-WORD] "RETURN_PATH",
153 [FROM-WORD] "FROM",
154 [SENDER-WORD] "SENDER",
155 [REPLY_TO-WORD] "REPLY_TO",
156 [RESENT_FROM-WORD] "RESENT_FROM",
157 [RESENT_SENDER-WORD] "RESENT_SENDER",
158 [RESENT_REPLY_TO-WORD] "RESENT_REPLY_TO",
159 [SUBJECT-WORD] "SUBJECT",
160 [TO-WORD] "TO",
161 [CC-WORD] "CC",
162 [BCC-WORD] "BCC",
163 [RESENT_TO-WORD] "RESENT_TO",
164 [RESENT_CC-WORD] "RESENT_CC",
165 [RESENT_BCC-WORD] "RESENT_BCC",
166 [REMOTE-WORD] "REMOTE",
167 [PRECEDENCE-WORD] "PRECEDENCE",
168 [MIMEVERSION-WORD] "MIMEVERSION",
169 [CONTENTTYPE-WORD] "CONTENTTYPE",
170 [MESSAGEID-WORD] "MESSAGEID",
171 [RECEIVED-WORD] "RECEIVED",
172 [MAILER-WORD] "MAILER",
173 [BADTOKEN-WORD] "BADTOKEN",
174 };
175
176 /* fix 822 addresses */
177 static void
rfc822cruft(message * mp)178 rfc822cruft(message *mp)
179 {
180 Field *f;
181 Node *p;
182 String *body, *s;
183 char *cp;
184
185 /*
186 * parse headers in in-core part
187 */
188 yyinit(s_to_c(mp->body), s_len(mp->body));
189 mp->rfc822headers = 0;
190 yyparse();
191 mp->rfc822headers = 1;
192 mp->received = received;
193
194 /*
195 * remove equivalent systems in all addresses
196 */
197 body = s_new();
198 cp = s_to_c(mp->body);
199 for(f = firstfield; f; f = f->next){
200 if(f->node->c == MIMEVERSION)
201 mp->havemime = 1;
202 if(f->node->c == FROM)
203 mp->havefrom = getaddr(f->node);
204 if(f->node->c == SENDER)
205 mp->havesender = getaddr(f->node);
206 if(f->node->c == REPLY_TO)
207 mp->havereplyto = getaddr(f->node);
208 if(f->node->c == TO)
209 mp->haveto = 1;
210 if(f->node->c == DATE)
211 mp->havedate = 1;
212 if(f->node->c == SUBJECT)
213 mp->havesubject = getstring(f->node);
214 if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
215 s = f->node->next->next->s;
216 if(s && (strcmp(s_to_c(s), "bulk") == 0
217 || strcmp(s_to_c(s), "Bulk") == 0))
218 mp->bulk = 1;
219 }
220 for(p = f->node; p; p = p->next){
221 if(p->s){
222 if(p->addr){
223 cp = skipequiv(s_to_c(p->s));
224 s_append(body, cp);
225 } else
226 s_append(body, s_to_c(p->s));
227 }else{
228 s_putc(body, p->c);
229 s_terminate(body);
230 }
231 if(p->white)
232 s_append(body, s_to_c(p->white));
233 cp = p->end+1;
234 }
235 s_append(body, "\n");
236 }
237
238 if(*s_to_c(body) == 0){
239 s_free(body);
240 return;
241 }
242
243 if(*cp != '\n')
244 s_append(body, "\n");
245 s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
246 s_terminate(body);
247
248 firstfield = 0;
249 mp->size += s_len(body) - s_len(mp->body);
250 s_free(mp->body);
251 mp->body = body;
252 }
253
254 /* read in a message, interpret the 'From' header */
255 extern message *
m_read(Biobuf * fp,int rmail,int interactive)256 m_read(Biobuf *fp, int rmail, int interactive)
257 {
258 message *mp;
259 Resub subexp[10];
260 char *line;
261 int first;
262 int n;
263
264 mp = m_new();
265
266 /* parse From lines if remote */
267 if (rmail) {
268 /* get remote address */
269 String *sender=s_new();
270
271 if (rfprog == 0)
272 rfprog = regcomp(REMFROMRE);
273 first = 1;
274 while(s_read_line(fp, s_restart(mp->body)) != 0) {
275 memset(subexp, 0, sizeof(subexp));
276 if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
277 if(first == 0)
278 break;
279 if (fprog == 0)
280 fprog = regcomp(FROMRE);
281 memset(subexp, 0, sizeof(subexp));
282 if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
283 break;
284 s_restart(mp->body);
285 append_match(subexp, s_restart(sender), SENDERMATCH);
286 append_match(subexp, s_restart(mp->date), DATEMATCH);
287 break;
288 }
289 append_match(subexp, s_restart(sender), REMSENDERMATCH);
290 append_match(subexp, s_restart(mp->date), REMDATEMATCH);
291 if(subexp[REMSYSMATCH].sp!=subexp[REMSYSMATCH].ep){
292 append_match(subexp, mp->sender, REMSYSMATCH);
293 s_append(mp->sender, "!");
294 }
295 first = 0;
296 }
297 s_append(mp->sender, s_to_c(sender));
298
299 s_free(sender);
300 }
301 if(*s_to_c(mp->sender)=='\0')
302 default_from(mp);
303
304 /* if sender address is unreturnable, treat message as bulk mail */
305 if(!returnable(s_to_c(mp->sender)))
306 mp->bulk = 1;
307
308 /* get body */
309 if(interactive && !rmail){
310 /* user typing on terminal: terminator == '.' or EOF */
311 for(;;) {
312 line = s_read_line(fp, mp->body);
313 if (line == 0)
314 break;
315 if (strcmp(".\n", line)==0) {
316 mp->body->ptr -= 2;
317 *mp->body->ptr = '\0';
318 break;
319 }
320 }
321 mp->size = mp->body->ptr - mp->body->base;
322 } else {
323 /*
324 * read up to VMLIMIT bytes (more or less) into main memory.
325 * if message is longer put the rest in a tmp file.
326 */
327 mp->size = mp->body->ptr - mp->body->base;
328 n = s_read(fp, mp->body, VMLIMIT);
329 if(n < 0){
330 perror("m_read");
331 exit(1);
332 }
333 mp->size += n;
334 if(n == VMLIMIT){
335 if(m_read_to_file(fp, mp) < 0){
336 perror("m_read");
337 exit(1);
338 }
339 }
340
341 }
342
343 /*
344 * ignore 0 length messages from a terminal
345 */
346 if (!rmail && mp->size == 0)
347 return 0;
348
349 rfc822cruft(mp);
350
351 return mp;
352 }
353
354 /* return a piece of message starting at `offset' */
355 extern int
m_get(message * mp,long offset,char ** pp)356 m_get(message *mp, long offset, char **pp)
357 {
358 static char buf[4*1024];
359
360 /*
361 * are we past eof?
362 */
363 if(offset >= mp->size)
364 return 0;
365
366 /*
367 * are we in the virtual memory portion?
368 */
369 if(offset < s_len(mp->body)){
370 *pp = mp->body->base + offset;
371 return mp->body->ptr - mp->body->base - offset;
372 }
373
374 /*
375 * read it from the temp file
376 */
377 offset -= s_len(mp->body);
378 if(mp->fd < 0)
379 return -1;
380 if(seek(mp->fd, offset, 0)<0)
381 return -1;
382 *pp = buf;
383 return read(mp->fd, buf, sizeof buf);
384 }
385
386 /* output the message body without ^From escapes */
387 static int
m_noescape(message * mp,Biobuf * fp)388 m_noescape(message *mp, Biobuf *fp)
389 {
390 long offset;
391 int n;
392 char *p;
393
394 for(offset = 0; offset < mp->size; offset += n){
395 n = m_get(mp, offset, &p);
396 if(n <= 0){
397 Bflush(fp);
398 return -1;
399 }
400 if(Bwrite(fp, p, n) < 0)
401 return -1;
402 }
403 return Bflush(fp);
404 }
405
406 /*
407 * Output the message body with '^From ' escapes.
408 * Ensures that any line starting with a 'From ' gets a ' ' stuck
409 * in front of it.
410 */
411 static int
m_escape(message * mp,Biobuf * fp)412 m_escape(message *mp, Biobuf *fp)
413 {
414 char *p, *np;
415 char *end;
416 long offset;
417 int m, n;
418 char *start;
419
420 for(offset = 0; offset < mp->size; offset += n){
421 n = m_get(mp, offset, &start);
422 if(n < 0){
423 Bflush(fp);
424 return -1;
425 }
426
427 p = start;
428 for(end = p+n; p < end; p += m){
429 np = memchr(p, '\n', end-p);
430 if(np == 0){
431 Bwrite(fp, p, end-p);
432 break;
433 }
434 m = np - p + 1;
435 if(m > 5 && strncmp(p, "From ", 5) == 0)
436 Bputc(fp, ' ');
437 Bwrite(fp, p, m);
438 }
439 }
440 Bflush(fp);
441 return 0;
442 }
443
444 static int
printfrom(message * mp,Biobuf * fp)445 printfrom(message *mp, Biobuf *fp)
446 {
447 String *s;
448 int rv;
449
450 if(!returnable(s_to_c(mp->sender)))
451 return Bprint(fp, "From: Postmaster\n");
452
453 s = username(mp->sender);
454 if(s) {
455 s_append(s, " <");
456 s_append(s, s_to_c(mp->sender));
457 s_append(s, ">");
458 } else {
459 s = s_copy(s_to_c(mp->sender));
460 }
461 s = unescapespecial(s);
462 rv = Bprint(fp, "From: %s\n", s_to_c(s));
463 s_free(s);
464 return rv;
465 }
466
467 static char *
rewritezone(char * z)468 rewritezone(char *z)
469 {
470 int mindiff;
471 char s;
472 Tm *tm;
473 static char x[7];
474
475 tm = localtime(time(0));
476 mindiff = tm->tzoff/60;
477
478 /* if not in my timezone, don't change anything */
479 if(strcmp(tm->zone, z) != 0)
480 return z;
481
482 if(mindiff < 0){
483 s = '-';
484 mindiff = -mindiff;
485 } else
486 s = '+';
487
488 sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
489 return x;
490 }
491
492 int
isutf8(String * s)493 isutf8(String *s)
494 {
495 char *p;
496
497 for(p = s_to_c(s); *p; p++)
498 if(*p&0x80)
499 return 1;
500 return 0;
501 }
502
503 void
printutf8mime(Biobuf * b)504 printutf8mime(Biobuf *b)
505 {
506 Bprint(b, "MIME-Version: 1.0\n");
507 Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
508 Bprint(b, "Content-Transfer-Encoding: 8bit\n");
509 }
510
511 /* output a message */
512 extern int
m_print(message * mp,Biobuf * fp,char * remote,int mbox)513 m_print(message *mp, Biobuf *fp, char *remote, int mbox)
514 {
515 String *date, *sender;
516 char *f[6];
517 int n;
518
519 sender = unescapespecial(s_clone(mp->sender));
520
521 if (remote != 0){
522 if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
523 s_free(sender);
524 return -1;
525 }
526 } else {
527 if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
528 s_free(sender);
529 return -1;
530 }
531 }
532 s_free(sender);
533 if(!rmail && !mp->havedate){
534 /* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
535 date = s_copy(s_to_c(mp->date));
536 n = getfields(s_to_c(date), f, 6, 1, " \t");
537 if(n == 6)
538 Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
539 f[5], f[3], rewritezone(f[4]));
540 }
541 if(!rmail && !mp->havemime && isutf8(mp->body))
542 printutf8mime(fp);
543 if(mp->to){
544 /* add the to: line */
545 if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
546 return -1;
547 /* add the from: line */
548 if (!mp->havefrom && printfrom(mp, fp) < 0)
549 return -1;
550 if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
551 if (Bprint(fp, "\n") < 0)
552 return -1;
553 } else if(!rmail){
554 /* add the from: line */
555 if (!mp->havefrom && printfrom(mp, fp) < 0)
556 return -1;
557 if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
558 if (Bprint(fp, "\n") < 0)
559 return -1;
560 }
561
562 if (!mbox)
563 return m_noescape(mp, fp);
564 return m_escape(mp, fp);
565 }
566
567 /* print just the message body */
568 extern int
m_bprint(message * mp,Biobuf * fp)569 m_bprint(message *mp, Biobuf *fp)
570 {
571 return m_noescape(mp, fp);
572 }
573