Blob


1 #include "common.h"
2 #include "send.h"
4 #include "../smtp/smtp.h"
5 #include "../smtp/rfc822.tab.h"
7 /* global to this file */
8 static Reprog *rfprog;
9 static Reprog *fprog;
11 #define VMLIMIT (64*1024)
12 #define MSGLIMIT (128*1024*1024)
14 int received; /* from rfc822.y */
16 static String* getstring(Node *p);
17 static String* getaddr(Node *p);
19 extern int
20 default_from(message *mp)
21 {
22 char *cp, *lp;
24 cp = getenv("upasname");
25 lp = getlog();
26 if(lp == nil)
27 return -1;
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 }
37 extern message *
38 m_new(void)
39 {
40 message *mp;
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 }
56 extern void
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 }
74 /* read a message into a temp file , return an open fd to it */
75 static int
76 m_read_to_file(Biobuf *fp, message *mp)
77 {
78 int fd;
79 int n;
80 String *file;
81 char buf[4*1024];
83 file = s_new();
84 /*
85 * create temp file to be remove 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;
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;
103 mp->size += n;
104 if(mp->size > MSGLIMIT){
105 mp->size = -1;
106 break;
110 mp->fd = fd;
111 return 0;
114 /* get the first address from a node */
115 static String*
116 getaddr(Node *p)
118 for(; p; p = p->next)
119 if(p->s && p->addr)
120 return s_copy(s_to_c(p->s));
121 return nil;
124 /* get the text of a header line minus the field name */
125 static String*
126 getstring(Node *p)
128 String *s;
130 s = s_new();
131 if(p == nil)
132 return s;
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);
141 if(p->white)
142 s_append(s, s_to_c(p->white));
144 return s;
147 #if 0
148 static char *fieldname[] =
150 [WORD-WORD] "WORD",
151 [DATE-WORD] "DATE",
152 [RESENT_DATE-WORD] "RESENT_DATE",
153 [RETURN_PATH-WORD] "RETURN_PATH",
154 [FROM-WORD] "FROM",
155 [SENDER-WORD] "SENDER",
156 [REPLY_TO-WORD] "REPLY_TO",
157 [RESENT_FROM-WORD] "RESENT_FROM",
158 [RESENT_SENDER-WORD] "RESENT_SENDER",
159 [RESENT_REPLY_TO-WORD] "RESENT_REPLY_TO",
160 [SUBJECT-WORD] "SUBJECT",
161 [TO-WORD] "TO",
162 [CC-WORD] "CC",
163 [BCC-WORD] "BCC",
164 [RESENT_TO-WORD] "RESENT_TO",
165 [RESENT_CC-WORD] "RESENT_CC",
166 [RESENT_BCC-WORD] "RESENT_BCC",
167 [REMOTE-WORD] "REMOTE",
168 [PRECEDENCE-WORD] "PRECEDENCE",
169 [MIMEVERSION-WORD] "MIMEVERSION",
170 [CONTENTTYPE-WORD] "CONTENTTYPE",
171 [MESSAGEID-WORD] "MESSAGEID",
172 [RECEIVED-WORD] "RECEIVED",
173 [MAILER-WORD] "MAILER",
174 [BADTOKEN-WORD] "BADTOKEN"
175 };
176 #endif
178 /* fix 822 addresses */
179 static void
180 rfc822cruft(message *mp)
182 Field *f;
183 Node *p;
184 String *body, *s;
185 char *cp;
187 /*
188 * parse headers in in-core part
189 */
190 yyinit(s_to_c(mp->body), s_len(mp->body));
191 mp->rfc822headers = 0;
192 yyparse();
193 mp->rfc822headers = 1;
194 mp->received = received;
196 /*
197 * remove equivalent systems in all addresses
198 */
199 body = s_new();
200 cp = s_to_c(mp->body);
201 for(f = firstfield; f; f = f->next){
202 if(f->node->c == MIMEVERSION)
203 mp->havemime = 1;
204 if(f->node->c == FROM)
205 mp->havefrom = getaddr(f->node);
206 if(f->node->c == SENDER)
207 mp->havesender = getaddr(f->node);
208 if(f->node->c == REPLY_TO)
209 mp->havereplyto = getaddr(f->node);
210 if(f->node->c == TO)
211 mp->haveto = 1;
212 if(f->node->c == DATE)
213 mp->havedate = 1;
214 if(f->node->c == SUBJECT)
215 mp->havesubject = getstring(f->node);
216 if(f->node->c == PRECEDENCE && f->node->next && f->node->next->next){
217 s = f->node->next->next->s;
218 if(s && (strcmp(s_to_c(s), "bulk") == 0
219 || strcmp(s_to_c(s), "Bulk") == 0))
220 mp->bulk = 1;
222 for(p = f->node; p; p = p->next){
223 if(p->s){
224 if(p->addr){
225 cp = skipequiv(s_to_c(p->s));
226 s_append(body, cp);
227 } else
228 s_append(body, s_to_c(p->s));
229 }else{
230 s_putc(body, p->c);
231 s_terminate(body);
233 if(p->white)
234 s_append(body, s_to_c(p->white));
235 cp = p->end+1;
237 s_append(body, "\n");
240 if(*s_to_c(body) == 0){
241 s_free(body);
242 return;
245 if(*cp != '\n')
246 s_append(body, "\n");
247 s_memappend(body, cp, s_len(mp->body) - (cp - s_to_c(mp->body)));
248 s_terminate(body);
250 firstfield = 0;
251 mp->size += s_len(body) - s_len(mp->body);
252 s_free(mp->body);
253 mp->body = body;
256 /* read in a message, interpret the 'From' header */
257 extern message *
258 m_read(Biobuf *fp, int rmail, int interactive)
260 message *mp;
261 Resub subexp[10];
262 char *line;
263 int first;
264 int n;
266 mp = m_new();
268 /* parse From lines if remote */
269 if (rmail) {
270 /* get remote address */
271 String *sender=s_new();
273 if (rfprog == 0)
274 rfprog = regcomp(REMFROMRE);
275 first = 1;
276 while(s_read_line(fp, s_restart(mp->body)) != 0) {
277 memset(subexp, 0, sizeof(subexp));
278 if (regexec(rfprog, s_to_c(mp->body), subexp, 10) == 0){
279 if(first == 0)
280 break;
281 if (fprog == 0)
282 fprog = regcomp(FROMRE);
283 memset(subexp, 0, sizeof(subexp));
284 if(regexec(fprog, s_to_c(mp->body), subexp,10) == 0)
285 break;
286 s_restart(mp->body);
287 append_match(subexp, s_restart(sender), SENDERMATCH);
288 append_match(subexp, s_restart(mp->date), DATEMATCH);
289 break;
291 append_match(subexp, s_restart(sender), REMSENDERMATCH);
292 append_match(subexp, s_restart(mp->date), REMDATEMATCH);
293 if(subexp[REMSYSMATCH].s.sp!=subexp[REMSYSMATCH].e.ep){
294 append_match(subexp, mp->sender, REMSYSMATCH);
295 s_append(mp->sender, "!");
297 first = 0;
299 s_append(mp->sender, s_to_c(sender));
301 s_free(sender);
303 if(*s_to_c(mp->sender)=='\0')
304 default_from(mp);
306 /* if sender address is unreturnable, treat message as bulk mail */
307 if(!returnable(s_to_c(mp->sender)))
308 mp->bulk = 1;
310 /* get body */
311 if(interactive && !rmail){
312 /* user typing on terminal: terminator == '.' or EOF */
313 for(;;) {
314 line = s_read_line(fp, mp->body);
315 if (line == 0)
316 break;
317 if (strcmp(".\n", line)==0) {
318 mp->body->ptr -= 2;
319 *mp->body->ptr = '\0';
320 break;
323 mp->size = mp->body->ptr - mp->body->base;
324 } else {
325 /*
326 * read up to VMLIMIT bytes (more or less) into main memory.
327 * if message is longer put the rest in a tmp file.
328 */
329 mp->size = mp->body->ptr - mp->body->base;
330 n = s_read(fp, mp->body, VMLIMIT);
331 if(n < 0){
332 perror("m_read");
333 exit(1);
335 mp->size += n;
336 if(n == VMLIMIT){
337 if(m_read_to_file(fp, mp) < 0){
338 perror("m_read_to_file");
339 exit(1);
344 /*
345 * ignore 0 length messages from a terminal
346 */
347 if (!rmail && mp->size == 0)
348 return 0;
350 rfc822cruft(mp);
352 return mp;
355 /* return a piece of message starting at `offset' */
356 extern int
357 m_get(message *mp, long offset, char **pp)
359 static char buf[4*1024];
361 /*
362 * are we past eof?
363 */
364 if(offset >= mp->size)
365 return 0;
367 /*
368 * are we in the virtual memory portion?
369 */
370 if(offset < s_len(mp->body)){
371 *pp = mp->body->base + offset;
372 return mp->body->ptr - mp->body->base - offset;
375 /*
376 * read it from the temp file
377 */
378 offset -= s_len(mp->body);
379 if(mp->fd < 0)
380 return -1;
381 if(seek(mp->fd, offset, 0)<0)
382 return -1;
383 *pp = buf;
384 return read(mp->fd, buf, sizeof buf);
387 /* output the message body without ^From escapes */
388 static int
389 m_noescape(message *mp, Biobuf *fp)
391 long offset;
392 int n;
393 char *p;
395 for(offset = 0; offset < mp->size; offset += n){
396 n = m_get(mp, offset, &p);
397 if(n <= 0){
398 Bflush(fp);
399 return -1;
401 if(Bwrite(fp, p, n) < 0)
402 return -1;
404 return Bflush(fp);
407 /*
408 * Output the message body with '^From ' escapes.
409 * Ensures that any line starting with a 'From ' gets a ' ' stuck
410 * in front of it.
411 */
412 static int
413 m_escape(message *mp, Biobuf *fp)
415 char *p, *np;
416 char *end;
417 long offset;
418 int m, n;
419 char *start;
421 for(offset = 0; offset < mp->size; offset += n){
422 n = m_get(mp, offset, &start);
423 if(n < 0){
424 Bflush(fp);
425 return -1;
428 p = start;
429 for(end = p+n; p < end; p += m){
430 np = memchr(p, '\n', end-p);
431 if(np == 0){
432 Bwrite(fp, p, end-p);
433 break;
435 m = np - p + 1;
436 if(m > 5 && strncmp(p, "From ", 5) == 0)
437 Bputc(fp, ' ');
438 Bwrite(fp, p, m);
441 Bflush(fp);
442 return 0;
445 static int
446 printfrom(message *mp, Biobuf *fp)
448 String *s;
449 int rv;
451 if(!returnable(s_to_c(mp->sender)))
452 return Bprint(fp, "From: Postmaster\n");
454 s = username(mp->sender);
455 if(s) {
456 s_append(s, " <");
457 s_append(s, s_to_c(mp->sender));
458 s_append(s, ">");
459 } else {
460 s = s_copy(s_to_c(mp->sender));
462 s = unescapespecial(s);
463 rv = Bprint(fp, "From: %s\n", s_to_c(s));
464 s_free(s);
465 return rv;
468 static char *
469 rewritezone(char *z)
471 int mindiff;
472 char s;
473 Tm *tm;
474 static char x[7];
476 tm = localtime(time(0));
477 mindiff = tm->tzoff/60;
479 /* if not in my timezone, don't change anything */
480 if(strcmp(tm->zone, z) != 0)
481 return z;
483 if(mindiff < 0){
484 s = '-';
485 mindiff = -mindiff;
486 } else
487 s = '+';
489 sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
490 return x;
493 int
494 isutf8(String *s)
496 char *p;
498 for(p = s_to_c(s); *p; p++)
499 if(*p&0x80)
500 return 1;
501 return 0;
504 void
505 printutf8mime(Biobuf *b)
507 Bprint(b, "MIME-Version: 1.0\n");
508 Bprint(b, "Content-Type: text/plain; charset=\"UTF-8\"\n");
509 Bprint(b, "Content-Transfer-Encoding: 8bit\n");
512 /* output a message */
513 extern int
514 m_print(message *mp, Biobuf *fp, char *remote, int mbox)
516 String *date, *sender;
517 char *f[6];
518 int n;
520 sender = unescapespecial(s_clone(mp->sender));
522 if (remote != 0){
523 if(print_remote_header(fp,s_to_c(sender),s_to_c(mp->date),remote) < 0){
524 s_free(sender);
525 return -1;
527 } else {
528 if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
529 s_free(sender);
530 return -1;
533 s_free(sender);
534 if(!rmail && !mp->havedate){
535 /* add a date: line Date: Sun, 19 Apr 1998 12:27:52 -0400 */
536 date = s_copy(s_to_c(mp->date));
537 n = getfields(s_to_c(date), f, 6, 1, " \t");
538 if(n == 6)
539 Bprint(fp, "Date: %s, %s %s %s %s %s\n", f[0], f[2], f[1],
540 f[5], f[3], rewritezone(f[4]));
542 if(!rmail && !mp->havemime && isutf8(mp->body))
543 printutf8mime(fp);
544 if(mp->to){
545 /* add the to: line */
546 if (Bprint(fp, "%s\n", s_to_c(mp->to)) < 0)
547 return -1;
548 /* add the from: line */
549 if (!mp->havefrom && printfrom(mp, fp) < 0)
550 return -1;
551 if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
552 if (Bprint(fp, "\n") < 0)
553 return -1;
554 } else if(!rmail){
555 /* add the from: line */
556 if (!mp->havefrom && printfrom(mp, fp) < 0)
557 return -1;
558 if(!mp->rfc822headers && *s_to_c(mp->body) != '\n')
559 if (Bprint(fp, "\n") < 0)
560 return -1;
563 if (!mbox)
564 return m_noescape(mp, fp);
565 return m_escape(mp, fp);
568 /* print just the message body */
569 extern int
570 m_bprint(message *mp, Biobuf *fp)
572 return m_noescape(mp, fp);