msg.c

来自「这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易」· C语言 代码 · 共 1,781 行 · 第 1/3 页

C
1,781
字号
{	int m, mm, nn;	nn = 0;	for(; n > 0; n -= m){		m = 18 * 3;		if(m > n)			m = n;		mm = enc64(out, lim - nn, in, m);		in += m;		out += mm;		*out++ = '\r';		*out++ = '\n';		nn += mm + 2;	}	return nn;}static voidbody64(int in, int out){	uchar buf[3*18*54];	char obuf[3*18*54*2];	int m, n;	for(;;){		n = read(in, buf, sizeof(buf));		if(n < 0)			return;		if(n == 0)			break;		m = enc64x18(obuf, sizeof(obuf), buf, n);		if(write(out, obuf, m) < 0)			return;	}}/* * strip all non-printable characters from a file */static voidbodystrip(int in, int out){	uchar buf[3*18*54];	int m, n, i, c;	for(;;){		n = read(in, buf, sizeof(buf));		if(n < 0)			return;		if(n == 0)			break;		m = 0;		for(i = 0; i < n; i++){			c = buf[i];			if(c > 0x1f && c < 0x7f		/* normal characters */			|| c >= 0x9 && c <= 0xd)	/* \t, \n, vertical tab, form feed, \r */				buf[m++] = c;		}		if(m && write(out, buf, m) < 0)			return;	}}/* * read in the message body to count \n without a preceding \r */static intmsgBodySize(Msg *m){	Dir *d;	char buf[BufSize + 2], *s, *se;	vlong length;	ulong size, lines, bad;	int n, fd, c;	if(m->lines != ~0UL)		return 1;	fd = msgFile(m, "rawbody");	if(fd < 0)		return 0;	d = dirfstat(fd);	if(d == nil){		close(fd);		return 0;	}	length = d->length;	free(d);	size = 0;	lines = 0;	bad = 0;	buf[0] = ' ';	for(;;){		n = read(fd, &buf[1], BufSize);		if(n <= 0)			break;		size += n;		se = &buf[n + 1];		for(s = &buf[1]; s < se; s++){			c = *s;			if(c == '\0'){				close(fd);				return msgBogus(m, BogusBody);			}			if(c != '\n')				continue;			if(s[-1] != '\r')				bad++;			lines++;		}		buf[0] = buf[n];	}	if(size != length)		bye("bad length reading rawbody");	size += bad;	m->size = size;	m->lines = lines;	close(fd);	return 1;}/* * retrieve information from the unixheader file */static intmsgUnix(Msg *m, int top){	Tm tm;	char *s, *ss;	if(m->unixDate != nil)		return 1;	if(!top){bogus:		m->unixDate = estrdup("");		m->unixFrom = unixFrom(nil);		return 1;	}	if(msgReadFile(m, "unixheader", &ss) < 0)		return 0;	s = ss;	s = strchr(s, ' ');	if(s == nil){		free(ss);		goto bogus;	}	s++;	m->unixFrom = unixFrom(s);	s = (char*)headStr;	if(date2tm(&tm, s) == nil)		s = m->info[IUnixDate];	if(s == nil){		free(ss);		goto bogus;	}	m->unixDate = estrdup(s);	free(ss);	return 1;}/* * parse the address in the unix header * last line of defence, so must return something */static MAddr *unixFrom(char *s){	MAddr *a;	char *e, *t;	if(s == nil)		return nil;	headStr = (uchar*)s;	t = emalloc(strlen(s) + 2);	e = headAddrSpec(t, nil);	if(e == nil)		a = nil;	else{		if(*e != '\0')			*e++ = '\0';		else			e = site;		a = MKZ(MAddr);		a->box = estrdup(t);		a->host = estrdup(e);	}	free(t);	return a;}/* * read in the entire header, * and parse out any existing mime headers */static intmsgHeader(Msg *m, Header *h, char *file){	char *s, *ss, *t, *te;	ulong lines, n, nn;	long ns;	int dated, c;	if(h->buf != nil)		return 1;	ns = msgReadFile(m, file, &ss);	if(ns < 0)		return 0;	s = ss;	n = ns;	/*	 * count lines ending with \n and \r\n	 * add an extra line at the end, since upas/fs headers	 * don't have a terminating \r\n	 */	lines = 1;	te = s + ns;	for(t = s; t < te; t++){		c = *t;		if(c == '\0')			return msgBogus(m, BogusHeader);		if(c != '\n')			continue;		if(t == s || t[-1] != '\r')			n++;		lines++;	}	if(t > s && t[-1] != '\n'){		if(t[-1] != '\r')			n++;		n++;	}	n += 2;	h->buf = emalloc(n + 1);	h->size = n;	h->lines = lines;	/*	 * make sure all headers end in \r\n	 */	nn = 0;	for(t = s; t < te; t++){		c = *t;		if(c == '\n'){			if(!nn || h->buf[nn - 1] != '\r')				h->buf[nn++] = '\r';			lines++;		}		h->buf[nn++] = c;	}	if(nn && h->buf[nn-1] != '\n'){		if(h->buf[nn-1] != '\r')			h->buf[nn++] = '\r';		h->buf[nn++] = '\n';	}	h->buf[nn++] = '\r';	h->buf[nn++] = '\n';	h->buf[nn] = '\0';	if(nn != n)		bye("misconverted header %d %d", nn, n);	free(s);	/*	 * and parse some mime headers	 */	headStr = (uchar*)h->buf;	dated = 0;	while(s = headAtom(headFieldStop)){		if(cistrcmp(s, "content-type") == 0)			mimeType(h);		else if(cistrcmp(s, "content-transfer-encoding") == 0)			mimeEncoding(h);		else if(cistrcmp(s, "content-id") == 0)			mimeId(h);		else if(cistrcmp(s, "content-description") == 0)			mimeDescription(h);		else if(cistrcmp(s, "content-disposition") == 0)			mimeDisposition(h);		else if(cistrcmp(s, "content-md5") == 0)			mimeMd5(h);		else if(cistrcmp(s, "content-language") == 0)			mimeLanguage(h);		else if(h == &m->head && cistrcmp(s, "from") == 0)			m->from = headMAddr(m->from);		else if(h == &m->head && cistrcmp(s, "to") == 0)			m->to = headMAddr(m->to);		else if(h == &m->head && cistrcmp(s, "reply-to") == 0)			m->replyTo = headMAddr(m->replyTo);		else if(h == &m->head && cistrcmp(s, "sender") == 0)			m->sender = headMAddr(m->sender);		else if(h == &m->head && cistrcmp(s, "cc") == 0)			m->cc = headMAddr(m->cc);		else if(h == &m->head && cistrcmp(s, "bcc") == 0)			m->bcc = headMAddr(m->bcc);		else if(h == &m->head && cistrcmp(s, "date") == 0)			dated = 1;		headSkip();		free(s);	}	if(h == &m->head){		if(m->from == nil){			m->from = m->unixFrom;			if(m->from != nil){				s = maddrStr(m->from);				msgAddHead(m, "From", s);				free(s);			}		}		if(m->sender == nil)			m->sender = m->from;		if(m->replyTo == nil)			m->replyTo = m->from;		if(infoIsNil(m->info[IDate]))			m->info[IDate] = m->unixDate;		if(!dated && m->from != nil)			msgAddDate(m);	}	return 1;}/* * prepend head: body to the cached header */static voidmsgAddHead(Msg *m, char *head, char *body){	char *s;	long size, n;	n = strlen(head) + strlen(body) + 4;	size = m->head.size + n;	s = emalloc(size + 1);	snprint(s, size + 1, "%s: %s\r\n%s", head, body, m->head.buf);	free(m->head.buf);	m->head.buf = s;	m->head.size = size;	m->head.lines++;}static voidmsgAddDate(Msg *m){	Tm tm;	char buf[64];	/* don't bother if we don't have a date */	if(infoIsNil(m->info[IDate]))		return;	date2tm(&tm, m->info[IDate]);	rfc822date(buf, sizeof(buf), &tm);	msgAddHead(m, "Date", buf);}static MimeHdr*mkMimeHdr(char *s, char *t, MimeHdr *next){	MimeHdr *mh;	mh = MK(MimeHdr);	mh->s = s;	mh->t = t;	mh->next = next;	return mh;}static voidfreeMimeHdr(MimeHdr *mh){	MimeHdr *last;	while(mh != nil){		last = mh;		mh = mh->next;		free(last->s);		free(last->t);		free(last);	}}static voidcleanupHeader(Header *h){	freeMimeHdr(h->type);	freeMimeHdr(h->id);	freeMimeHdr(h->description);	freeMimeHdr(h->encoding);	freeMimeHdr(h->md5);	freeMimeHdr(h->disposition);	freeMimeHdr(h->language);}/* * parser for rfc822 & mime header fields *//* * type		: 'content-type' ':' token '/' token params */static voidmimeType(Header *h){	char *s, *t;	if(headChar(1) != ':')		return;	s = headAtom(mimeTokenStop);	if(s == nil || headChar(1) != '/'){		free(s);		return;	}	t = headAtom(mimeTokenStop);	if(t == nil){		free(s);		return;	}	h->type = mkMimeHdr(s, t, mimeParams());}/* * params	: *		| params ';' token '=' token * 		| params ';' token '=' quoted-str */static MimeHdr*mimeParams(void){	MimeHdr head, *last;	char *s, *t;	head.next = nil;	last = &head;	for(;;){		if(headChar(1) != ';')			break;		s = headAtom(mimeTokenStop);		if(s == nil || headChar(1) != '='){			free(s);			break;		}		if(headChar(0) == '"'){			t = headQuoted('"', '"');			stripQuotes(t);		}else			t = headAtom(mimeTokenStop);		if(t == nil){			free(s);			break;		}		last->next = mkMimeHdr(s, t, nil);		last = last->next;	}	return head.next;}/* * encoding	: 'content-transfer-encoding' ':' token */static voidmimeEncoding(Header *h){	char *s;	if(headChar(1) != ':')		return;	s = headAtom(mimeTokenStop);	if(s == nil)		return;	h->encoding = mkMimeHdr(s, nil, nil);}/* * mailaddr	: ':' addresses */static MAddr*headMAddr(MAddr *old){	MAddr *a;	if(headChar(1) != ':')		return old;	if(headChar(0) == '\n')		return old;	a = headAddresses();	if(a == nil)		return old;	freeMAddr(old);	return a;}/* * addresses	: address | addresses ',' address */static MAddr*headAddresses(void){	MAddr *addr, *tail, *a;	addr = headAddress();	if(addr == nil)		return nil;	tail = addr;	while(headChar(0) == ','){		headChar(1);		a = headAddress();		if(a == nil){			freeMAddr(addr);			return nil;		}		tail->next = a;		tail = a;	}	return addr;}/* * address	: mailbox | group * group	: phrase ':' mboxes ';' | phrase ':' ';' * mailbox	: addr-spec *		| optphrase '<' addr-spec '>' *		| optphrase '<' route ':' addr-spec '>' * optphrase	: | phrase * route	: '@' domain *		| route ',' '@' domain * personal names are the phrase before '<', * or a comment before or after a simple addr-spec */static MAddr*headAddress(void){	MAddr *addr;	uchar *hs;	char *s, *e, *w, *personal;	int c;	s = emalloc(strlen((char*)headStr) + 2);	e = s;	personal = headSkipWhite(1);	c = headChar(0);	if(c == '<')		w = nil;	else{		w = headWord();		c = headChar(0);	}	if(c == '.' || c == '@' || c == ',' || c == '\n' || c == '\0'){		lastWhite = headStr;		e = headAddrSpec(s, w);		if(personal == nil){			hs = headStr;			headStr = lastWhite;			personal = headSkipWhite(1);			headStr = hs;		}	}else{		if(c != '<' || w != nil){			free(personal);			if(!headPhrase(e, w)){				free(s);				return nil;			}			/*			 * ignore addresses with groups,			 * so the only thing left if <			 */			c = headChar(1);			if(c != '<'){				free(s);				return nil;			}			personal = estrdup(s);		}else			headChar(1);		/*		 * after this point, we need to free personal before returning.		 * set e to nil to everything afterwards fails.		 *		 * ignore routes, they are useless, and heavily discouraged in rfc1123.		 * imap4 reports them up to, but not including, the terminating :		 */		e = s;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?