plan9port

fork of plan9port with libvec, libstr and libsdb
Log | Files | Refs | README | LICENSE

message.c (11103B)


      1 #include "common.h"
      2 #include "send.h"
      3 
      4 #include "../smtp/smtp.h"
      5 #include "../smtp/rfc822.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
     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 *
     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
     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 */
     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];
     82 
     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;
     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*
    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*
    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 #if 0
    148 static char *fieldname[] =
    149 {
    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
    177 
    178 /* fix 822 addresses */
    179 static void
    180 rfc822cruft(message *mp)
    181 {
    182 	Field *f;
    183 	Node *p;
    184 	String *body, *s;
    185 	char *cp;
    186 
    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;
    195 
    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;
    221 		}
    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);
    232 			}
    233 			if(p->white)
    234 				s_append(body, s_to_c(p->white));
    235 			cp = p->end+1;
    236 		}
    237 		s_append(body, "\n");
    238 	}
    239 
    240 	if(*s_to_c(body) == 0){
    241 		s_free(body);
    242 		return;
    243 	}
    244 
    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);
    249 
    250 	firstfield = 0;
    251 	mp->size += s_len(body) - s_len(mp->body);
    252 	s_free(mp->body);
    253 	mp->body = body;
    254 }
    255 
    256 /* read in a message, interpret the 'From' header */
    257 extern message *
    258 m_read(Biobuf *fp, int rmail, int interactive)
    259 {
    260 	message *mp;
    261 	Resub subexp[10];
    262 	char *line;
    263 	int first;
    264 	int n;
    265 
    266 	mp = m_new();
    267 
    268 	/* parse From lines if remote */
    269 	if (rmail) {
    270 		/* get remote address */
    271 		String *sender=s_new();
    272 
    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;
    290 			}
    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, "!");
    296 			}
    297 			first = 0;
    298 		}
    299 		s_append(mp->sender, s_to_c(sender));
    300 
    301 		s_free(sender);
    302 	}
    303 	if(*s_to_c(mp->sender)=='\0')
    304 		default_from(mp);
    305 
    306 	/* if sender address is unreturnable, treat message as bulk mail */
    307 	if(!returnable(s_to_c(mp->sender)))
    308 		mp->bulk = 1;
    309 
    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;
    321 			}
    322 		}
    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);
    334 		}
    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);
    340 			}
    341 		}
    342 	}
    343 
    344 	/*
    345 	 *  ignore 0 length messages from a terminal
    346 	 */
    347 	if (!rmail && mp->size == 0)
    348 		return 0;
    349 
    350 	rfc822cruft(mp);
    351 
    352 	return mp;
    353 }
    354 
    355 /* return a piece of message starting at `offset' */
    356 extern int
    357 m_get(message *mp, long offset, char **pp)
    358 {
    359 	static char buf[4*1024];
    360 
    361 	/*
    362 	 *  are we past eof?
    363 	 */
    364 	if(offset >= mp->size)
    365 		return 0;
    366 
    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;
    373 	}
    374 
    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);
    385 }
    386 
    387 /* output the message body without ^From escapes */
    388 static int
    389 m_noescape(message *mp, Biobuf *fp)
    390 {
    391 	long offset;
    392 	int n;
    393 	char *p;
    394 
    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;
    400 		}
    401 		if(Bwrite(fp, p, n) < 0)
    402 			return -1;
    403 	}
    404 	return Bflush(fp);
    405 }
    406 
    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)
    414 {
    415 	char *p, *np;
    416 	char *end;
    417 	long offset;
    418 	int m, n;
    419 	char *start;
    420 
    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;
    426 		}
    427 
    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;
    434 			}
    435 			m = np - p + 1;
    436 			if(m > 5 && strncmp(p, "From ", 5) == 0)
    437 				Bputc(fp, ' ');
    438 			Bwrite(fp, p, m);
    439 		}
    440 	}
    441 	Bflush(fp);
    442 	return 0;
    443 }
    444 
    445 static int
    446 printfrom(message *mp, Biobuf *fp)
    447 {
    448 	String *s;
    449 	int rv;
    450 
    451 	if(!returnable(s_to_c(mp->sender)))
    452 		return Bprint(fp, "From: Postmaster\n");
    453 
    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));
    461 	}
    462 	s = unescapespecial(s);
    463 	rv = Bprint(fp, "From: %s\n", s_to_c(s));
    464 	s_free(s);
    465 	return rv;
    466 }
    467 
    468 static char *
    469 rewritezone(char *z)
    470 {
    471 	int mindiff;
    472 	char s;
    473 	Tm *tm;
    474 	static char x[7];
    475 
    476 	tm = localtime(time(0));
    477 	mindiff = tm->tzoff/60;
    478 
    479 	/* if not in my timezone, don't change anything */
    480 	if(strcmp(tm->zone, z) != 0)
    481 		return z;
    482 
    483 	if(mindiff < 0){
    484 		s = '-';
    485 		mindiff = -mindiff;
    486 	} else
    487 		s = '+';
    488 
    489 	sprint(x, "%c%.2d%.2d", s, mindiff/60, mindiff%60);
    490 	return x;
    491 }
    492 
    493 int
    494 isutf8(String *s)
    495 {
    496 	char *p;
    497 
    498 	for(p = s_to_c(s);  *p; p++)
    499 		if(*p&0x80)
    500 			return 1;
    501 	return 0;
    502 }
    503 
    504 void
    505 printutf8mime(Biobuf *b)
    506 {
    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");
    510 }
    511 
    512 /* output a message */
    513 extern int
    514 m_print(message *mp, Biobuf *fp, char *remote, int mbox)
    515 {
    516 	String *date, *sender;
    517 	char *f[6];
    518 	int n;
    519 
    520 	sender = unescapespecial(s_clone(mp->sender));
    521 
    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;
    526 		}
    527 	} else {
    528 		if(print_header(fp, s_to_c(sender), s_to_c(mp->date)) < 0){
    529 			s_free(sender);
    530 			return -1;
    531 		}
    532 	}
    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]));
    541 	}
    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;
    561 	}
    562 
    563 	if (!mbox)
    564 		return m_noescape(mp, fp);
    565 	return m_escape(mp, fp);
    566 }
    567 
    568 /* print just the message body */
    569 extern int
    570 m_bprint(message *mp, Biobuf *fp)
    571 {
    572 	return m_noescape(mp, fp);
    573 }