11 #define inline _inline
13 typedef struct Attach Attach;
14 typedef struct Alias Alias;
15 typedef struct Addr Addr;
16 typedef struct Ctype Ctype;
69 [Hreplyto] "reply-to:",
70 [Hinreplyto] "in-reply-to:",
73 [Hsubject] "subject:",
74 [Hpriority] "priority:",
75 [Hmsgid] "message-id:",
77 [Hcontent] "content-",
79 [Hprecedence] "precedence"
89 { "text/plain", "txt", 1, },
90 { "text/html", "html", 1, },
91 { "text/html", "htm", 1, },
92 { "text/tab-separated-values", "tsv", 1, },
93 { "text/richtext", "rtx", 1, },
94 { "message/rfc822", "txt", 1, },
103 Attach* mkattach(char*, char*, int);
104 int readheaders(Biobuf*, int*, String**, Addr**, int);
105 void body(Biobuf*, Biobuf*, int);
106 char* mkboundary(void);
107 int printdate(Biobuf*);
108 int printfrom(Biobuf*);
109 int printto(Biobuf*, Addr*);
110 int printcc(Biobuf*, Addr*);
111 int printsubject(Biobuf*, char*);
112 int printinreplyto(Biobuf*, char*);
113 int sendmail(Addr*, Addr*, int*, char*);
114 void attachment(Attach*, Biobuf*);
115 int cistrncmp(char*, char*, int);
116 int cistrcmp(char*, char*);
117 char* waitforsubprocs(void);
118 int enc64(char*, int, uchar*, int);
119 Addr* expand(int, char**);
120 Alias* readaliases(void);
121 Addr* expandline(String**, Addr*);
122 void Bdrain(Biobuf*);
123 void freeaddr(Addr *);
125 int pgpfilter(int*, int, int);
126 void readmimetypes(void);
127 char* estrdup(char*);
129 void* erealloc(void*, int);
130 void freeaddr(Addr*);
131 void freeaddrs(Addr*);
132 void freealias(Alias*);
133 void freealiases(Alias*);
134 int doublequote(Fmt*);
137 int rfc2047fmt(Fmt*);
138 char* mksubject(char*);
140 int rflag, lbflag, xflag, holding, nflag, Fflag, eightflag, dflag;
145 int rfc822syntaxerror;
159 #pragma varargck type "Z" char*
164 fprint(2, "usage: %s [-Fr#xn] [-s subject] [-c ccrecipient] [-t type] [-aA attachment] [-p[es]] [-R replymsg] -8 | recipient-list\n",
166 threadexitsall("usage");
170 fatal(char *fmt, ...)
176 postnote(PNPROC, pid, "die");
178 postnote(PNPROC, pgppid, "die");
181 vseprint(buf, buf+sizeof(buf), fmt, arg);
183 fprint(2, "%s: %s\n", argv0, buf);
189 threadmain(int argc, char **argv)
191 Attach *first, **l, *a;
192 char *subject, *type, *boundary;
197 String *file, *hdrstring;
198 int noinput, headersrv;
211 fmtinstall('Z', doublequote);
212 fmtinstall('U', rfc2047fmt);
217 type = EARGF(usage());
225 a = mkattach(EARGF(usage()), type, flags);
227 threadexitsall("bad args");
233 if(ccargc >= nelem(ccargv)-1)
234 sysfatal("too many cc's");
235 ccargv[ccargc] = ARGF();
236 if(ccargv[ccargc] == nil)
241 replymsg = EARGF(usage());
244 subject = EARGF(usage());
247 Fflag = 1; /* file message */
250 rflag = 1; /* for sendmail */
253 dflag = 1; /* for sendmail */
256 lbflag = 1; /* for sendmail */
259 xflag = 1; /* for sendmail */
261 case 'n': /* no standard input */
264 case '8': /* read recipients from rfc822 header */
267 case 'p': /* pgp flag: encrypt, sign, or both */
268 if(pgpopts(EARGF(usage())) < 0)
269 sysfatal("bad pgp options");
277 user = getenv("upasname");
278 if(user == nil || *user == 0)
280 if(user == nil || *user == 0)
281 sysfatal("can't read user name");
283 if(Binit(&in, 0, OREAD) < 0)
284 sysfatal("can't Binit 0: %r");
286 if(nflag && eightflag)
287 sysfatal("can't use both -n and -8");
288 if(eightflag && argc >= 1)
290 else if(!eightflag && argc < 1)
293 aliases = readaliases();
295 to = expand(argc, argv);
296 cc = expand(ccargc, ccargv);
303 headersrv = Nomessage;
304 if(!nflag && !xflag && !lbflag &&!dflag) {
305 /* pass through headers, keeping track of which we've seen, */
306 /* perhaps building to list. */
308 headersrv = readheaders(&in, &flags, &hdrstring, eightflag ? &to : nil, 1);
309 if(rfc822syntaxerror){
311 fatal("rfc822 syntax error, message not sent");
315 fatal("no addresses found, message not sent");
319 case Error: /* error */
322 case Nomessage: /* no message, just exit mimicking old behavior */
330 fd = sendmail(to, cc, &pid, Fflag ? argv[0] : nil);
332 sysfatal("execing sendmail: %r\n:");
333 if(xflag || lbflag || dflag){
335 threadexitsall(waitforsubprocs());
338 if(Binit(&out, fd, OWRITE) < 0)
339 fatal("can't Binit 1: %r");
342 if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring))
343 fatal("write error");
347 /* read user's standard headers */
349 mboxpath("headers", user, file, 0);
350 b = Bopen(s_to_c(file), OREAD);
352 switch(readheaders(b, &flags, &hdrstring, nil, 0)){
353 case Error: /* error */
357 if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring))
358 fatal("write error");
364 /* add any headers we need */
365 if((flags & (1<<Hdate)) == 0)
366 if(printdate(&out) < 0)
368 if((flags & (1<<Hfrom)) == 0)
369 if(printfrom(&out) < 0)
371 if((flags & (1<<Hto)) == 0)
372 if(printto(&out, to) < 0)
374 if((flags & (1<<Hcc)) == 0)
375 if(printcc(&out, cc) < 0)
377 if((flags & (1<<Hsubject)) == 0 && subject != nil)
378 if(printsubject(&out, subject) < 0)
381 printinreplyto(&out, replymsg); /* ignore errors */
382 Bprint(&out, "MIME-Version: 1.0\n");
384 if(pgpflag){ /* interpose pgp process between us and sendmail to handle body */
387 fd = pgpfilter(&pgppid, fd, pgpflag);
388 if(Binit(&out, fd, OWRITE) < 0)
389 fatal("can't Binit 1: %r");
392 /* if attachments, stick in multipart headers */
395 boundary = mkboundary();
396 Bprint(&out, "Content-Type: multipart/mixed;\n");
397 Bprint(&out, "\tboundary=\"%s\"\n\n", boundary);
398 Bprint(&out, "This is a multi-part message in MIME format.\n");
399 Bprint(&out, "--%s\n", boundary);
400 Bprint(&out, "Content-Disposition: inline\n");
404 if(!noinput && headersrv == Ok){
412 for(a = first; a != nil; a = a->next){
415 Bprint(&out, "--%s\n", boundary);
422 Bprint(&out, "--%s--\n", boundary);
427 threadexitsall(waitforsubprocs());
430 /* evaluate pgp option string */
434 if(s == nil || s[0] == '\0')
442 pgpflag |= PGPencrypt;
451 /* read headers from stdin into a String, expanding local aliases, */
452 /* keep track of which headers are there, which addresses we have */
453 /* remove Bcc: line. */
455 readheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict)
460 int i, seen, hdrtype;
468 if((p = Brdline(in, '\n')) != nil) {
470 p[Blinelen(in)-1] = 0;
472 /* coalesce multiline headers */
473 if((*p == ' ' || *p == '\t') && sline){
474 s_append(sline, "\n");
476 p[Blinelen(in)-1] = '\n';
481 /* process the current header, it's all been read */
483 assert(hdrtype != -1);
489 to = expandline(&sline, to);
493 if(hdrtype == Hsubject){
494 s_append(s, mksubject(s_to_c(sline)));
496 }else if(top==nil || hdrtype!=Hbcc){
497 s_append(s, s_to_c(sline));
507 /* if no :, it's not a header, seek back and break */
508 if(strchr(p, ':') == nil){
509 p[Blinelen(in)-1] = '\n';
510 Bseek(in, -Blinelen(in), 1);
516 /* classify the header. If we don't recognize it, break. This is */
517 /* to take care of user's that start messages with lines that contain */
518 /* ':'s but that aren't headers. This is a bit hokey. Since I decided */
519 /* to let users type headers, I need some way to distinguish. Therefore, */
520 /* marshal tries to know all likely headers and will indeed screw up if */
521 /* the user types an unlikely one. -- presotto */
523 for(i = 0; i < nelem(hdrs); i++){
524 if(cistrncmp(hdrs[i], p, strlen(hdrs[i])) == 0){
532 p[Blinelen(in)-1] = '\n';
533 Bseek(in, -Blinelen(in), 1);
538 p[Blinelen(in)-1] = '\n';
546 if(Blinelen(in) == 0)
556 /* pass the body to sendmail, make sure body starts and ends with a newline */
558 body(Biobuf *in, Biobuf *out, int docontenttype)
567 /* first char must be newline */
577 /* read into memory */
579 while(docontenttype){
582 buf = realloc(buf, len);
587 i = Bread(in, p, len - n);
589 fatal("input error2");
594 if((*p++ & 0x80) && docontenttype){
595 Bprint(out, "Content-Type: text/plain; charset=\"UTF-8\"\n");
596 Bprint(out, "Content-Transfer-Encoding: 8bit\n");
602 Bprint(out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
603 Bprint(out, "Content-Transfer-Encoding: 7bit\n");
607 /* write what we already read */
608 if(Bwrite(out, buf, n) < 0)
609 fatal("output error");
618 n = Bread(in, buf, len);
620 fatal("input error2");
623 if(Bwrite(out, buf, n) < 0)
624 fatal("output error");
629 /* pass the body to sendmail encoding with base64 */
631 /* the size of buf is very important to enc64. Anything other than */
632 /* a multiple of 3 will cause enc64 to output a termination sequence. */
633 /* To ensure that a full buf corresponds to a multiple of complete lines, */
634 /* we make buf a multiple of 3*18 since that's how many enc64 sticks on */
635 /* a single line. This avoids short lines in the output which is pleasing */
636 /* but not necessary. */
639 body64(Biobuf *in, Biobuf *out)
642 char obuf[3*18*54*2];
647 n = Bread(in, buf, sizeof(buf));
649 fatal("input error");
652 m = enc64(obuf, sizeof(obuf), buf, n);
653 if((n=Bwrite(out, obuf, m)) < 0)
654 fatal("output error");
659 /* pass message to sendmail, make sure body starts with a newline */
661 copy(Biobuf *in, Biobuf *out)
667 n = Bread(in, buf, sizeof(buf));
669 fatal("input error");
672 if(Bwrite(out, buf, n) < 0)
673 fatal("output error");
678 attachment(Attach *a, Biobuf *out)
683 f = emalloc(sizeof *f);
684 Binit(f, a->fd, OREAD);
685 /* if it's already mime encoded, just copy */
686 if(strcmp(a->type, "mime") == 0){
693 /* if it's not already mime encoded ... */
694 if(strcmp(a->type, "text/plain") != 0)
695 Bprint(out, "Content-Type: %s\n", a->type);
698 Bprint(out, "Content-Disposition: inline\n");
700 p = strrchr(a->path, '/');
705 Bprint(out, "Content-Disposition: attachment; filename=%Z\n", p);
708 /* dump our local 'From ' line when passing along mail messages */
709 if(strcmp(a->type, "message/rfc822") == 0){
710 p = Brdline(f, '\n');
711 if(strncmp(p, "From ", 5) != 0)
714 if(a->ctype->display){
715 body(f, out, strcmp(a->type, "text/plain") == 0);
717 Bprint(out, "Content-Transfer-Encoding: base64\n");
726 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
731 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
732 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
741 tm = localtime(time(0));
742 tz = (tm->tzoff/3600)*100 + ((tm->tzoff/60)%60);
744 return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n",
745 ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900+tm->year,
746 tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz);
752 return Bprint(b, "From: %s\n", user);
756 printto(Biobuf *b, Addr *a)
760 if(Bprint(b, "To: %s", a->v) < 0)
763 for(a = a->next; a != nil; a = a->next)
764 if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
766 if(Bprint(b, "\n") < 0)
772 printcc(Biobuf *b, Addr *a)
778 if(Bprint(b, "CC: %s", a->v) < 0)
781 for(a = a->next; a != nil; a = a->next)
782 if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
784 if(Bprint(b, "\n") < 0)
790 printsubject(Biobuf *b, char *subject)
792 return Bprint(b, "Subject: %s\n", subject);
796 printinreplyto(Biobuf *out, char *dir)
805 if(strncmp(dir, "Mail/", 5) != 0)
808 s_append(s, "/messageid");
809 fd = fsopenfd(mailfs, s_to_c(s), OREAD);
813 n = readn(fd, buf, sizeof(buf)-1);
818 return Bprint(out, "In-Reply-To: %s\n", buf);
822 mopen(char *file, int mode)
826 if((fd = open(file, mode)) >= 0)
828 if(strncmp(file, "Mail/", 5) == 0 && mountmail() >= 0 && (fd = fsopenfd(mailfs, file+5, mode)) >= 0)
834 mkattach(char *file, char *type, int inline)
840 int fd, n, pfd[2], xfd[3];
844 if((fd = mopen(file, OREAD)) < 0)
846 a = emalloc(sizeof(*a));
854 for(c = ctype; ; c++)
855 if(strncmp(type, c->type, strlen(c->type)) == 0){
862 /* pick a type depending on extension */
863 p = strchr(file, '.');
867 /* check the builtin extensions */
869 for(c = ctype; c->ext != nil; c++)
870 if(strcmp(p, c->ext) == 0){
877 /* try the mime types file */
881 for(c = mimetypes; c != nil && c->ext != nil; c++)
882 if(strcmp(p, c->ext) == 0){
889 /* run file to figure out the type */
890 a->type = "application/octet-stream"; /* safest default */
894 xfd[0] = mopen(file, OREAD);
897 if((pid=threadspawnl(xfd, unsharp("#9/bin/file"), "file", "-m", nil)) < 0){
903 /* threadspawnl closed pfd[0] */
905 n = readn(pfd[1], ftype, sizeof(ftype));
908 a->type = estrdup(ftype);
913 for(c = ctype; ; c++)
914 if(strncmp(a->type, c->type, strlen(c->type)) == 0){
928 srand((time(0)<<16)|getpid());
929 strcpy(buf, "upas-");
930 for(i = 5; i < sizeof(buf)-1; i++)
931 buf[i] = 'a' + nrand(26);
936 /* copy types to two fd's */
938 tee(int in, int out1, int out2)
944 n = read(in, buf, sizeof(buf));
947 if(write(out1, buf, n) < 0)
949 if(write(out2, buf, n) < 0)
960 tee(a[0], a[1], a[2]);
961 write(a[2], "\n", 1);
964 /* print the unix from line */
966 printunixfrom(int fd)
971 tm = localtime(time(0));
972 tz = (tm->tzoff/3600)*100 + ((tm->tzoff/60)%60);
974 return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n",
976 ascwday[tm->wday], ascmon[tm->mon], tm->mday,
977 tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900+tm->year);
980 char *specialfile[] =
989 /* return 1 if this is a special file */
996 p = strrchr(s_to_c(s), '/');
1001 for(i = 0; i < nelem(specialfile); i++)
1002 if(strcmp(p, specialfile[i]) == 0)
1007 /* open the folder using the recipients account name */
1009 openfolder(char *rcvr)
1019 mboxpath("f", user, file, 0);
1021 /* if $mail/f exists, store there, otherwise in $mail */
1022 d = dirstat(s_to_c(file));
1023 if(d == nil || d->qid.type != QTDIR){
1032 p = strrchr(rcvr, '!');
1036 while(*rcvr && *rcvr != '@'){
1044 if(scarey && special(file)){
1045 fprint(2, "%s: won't overwrite %s\n", argv0, s_to_c(file));
1050 fd = open(s_to_c(file), OWRITE);
1052 fd = create(s_to_c(file), OWRITE, 0660);
1058 /* start up sendmail and return an fd to talk to it with */
1060 sendmail(Addr *to, Addr *cc, int *pid, char *rcvr)
1064 int pfd[2], sfd, xfd[3];
1071 fd = openfolder(rcvr);
1074 for(a = to; a != nil; a = a->next)
1076 for(a = cc; a != nil; a = a->next)
1078 v = av = emalloc(sizeof(char*)*(ac+20));
1080 v[ac++] = "sendmail";
1089 for(a = to; a != nil; a = a->next)
1091 for(a = cc; a != nil; a = a->next)
1099 xfd[1] = dup(1, -1);
1100 xfd[2] = dup(2, -1);
1103 putenv("replymsg", replymsg);
1104 cmd = mboxpath("pipefrom", login, s_new(), 0);
1106 if((*pid = threadspawn(xfd, x=s_to_c(cmd), av)) < 0
1107 && (*pid = threadspawn(xfd, x="myupassend", av)) < 0
1108 && (*pid = threadspawn(xfd, x=unsharp("#9/bin/upas/send"), av)) < 0)
1110 /* threadspawn closed pfd[0] (== xfd[0]) */
1118 targ = emalloc(3*sizeof targ[0]);
1122 proccreate(teeproc, targ, STACK);
1129 /* start up pgp process and return an fd to talk to it with. */
1130 /* its standard output will be the original fd, which goes to sendmail. */
1132 pgpfilter(int *pid, int fd, int pgpflag)
1138 v = av = emalloc(sizeof(char*)*8);
1141 v[ac++] = "-fat"; /* operate as a filter, generate text */
1142 if(pgpflag & PGPsign)
1144 if(pgpflag & PGPencrypt)
1150 switch(*pid = fork()){
1160 /* add newline to avoid confusing pgp output with 822 headers */
1164 fatal("execing: %r");
1174 /* wait for sendmail and pgp to exit; exit here if either failed */
1176 waitforsubprocs(void)
1182 if(pgppid >= 0 && (w=procwait(pgppid)) && w->msg[0])
1184 if(pid >= 0 && (w=procwait(pid)) && w->msg[0])
1190 cistrncmp(char *a, char *b, int n)
1193 if(tolower(*a++) != tolower(*b++))
1200 cistrcmp(char *a, char *b)
1203 if(tolower(*a) != tolower(*b++))
1211 static uchar t64d[256];
1212 static char t64e[64];
1219 memset(t64d, 255, 256);
1220 memset(t64e, '=', 64);
1222 for(c = 'A'; c <= 'Z'; c++){
1226 for(c = 'a'; c <= 'z'; c++){
1230 for(c = '0'; c <= '9'; c++){
1241 enc64(char *out, int lim, uchar *in, int n)
1246 char *e = out + lim;
1250 for(i = 0; i < n/3; i++){
1256 *out++ = t64e[(b24>>18)];
1257 *out++ = t64e[(b24>>12)&0x3f];
1258 *out++ = t64e[(b24>>6)&0x3f];
1259 *out++ = t64e[(b24)&0x3f];
1270 *out++ = t64e[(b24>>18)];
1271 *out++ = t64e[(b24>>12)&0x3f];
1272 *out++ = t64e[(b24>>6)&0x3f];
1278 *out++ = t64e[(b24>>18)];
1279 *out++ = t64e[(b24>>12)&0x3f];
1303 freealiases(Alias *a)
1315 /* read alias file */
1320 Alias *a, **l, *first;
1322 String *file, *line, *token;
1330 /* open and get length */
1331 mboxpath("names", login, file, 0);
1332 sp = s_allocinstack(s_to_c(file));
1338 /* read a line at a time. */
1339 while(s_rdinstack(sp, s_restart(line))!=nil) {
1341 a = emalloc(sizeof(Alias));
1344 if(s_parse(line, s_restart(token))==0)
1346 addr = emalloc(sizeof(Addr));
1347 addr->v = strdup(s_to_c(token));
1352 if(a->addr == nil || a->addr->next == nil){
1374 a = emalloc(sizeof(*a));
1376 a->v = estrdup(name);
1383 /* expand personal aliases since the names are meaningless in */
1384 /* other contexts */
1387 _expand(Addr *old, int *changedp)
1390 Addr *first, *next, **l, *a;
1395 for(;old != nil; old = next){
1397 for(al = aliases; al != nil; al = al->next){
1398 if(strcmp(al->addr->v, old->v) == 0){
1399 for(a = al->addr->next; a != nil; a = a->next){
1426 for(i=0; i<32; i++){
1427 old = _expand(old, &changed);
1439 for(a = first; a != nil; a = a->next){
1440 for(l = &a->next; *l != nil;){
1441 if(strcmp(a->v, (*l)->v) == 0){
1453 expand(int ac, char **av)
1460 /* make a list of the starting addresses */
1462 for(i = 0; i < ac; i++){
1463 *l = newaddr(av[i]);
1469 /* recurse till we don't change any more */
1470 return unique(rexpand(first));
1474 concataddr(Addr *a, Addr *b)
1482 for(; a->next; a=a->next)
1500 for(; ap; ap=next) {
1507 s_copyn(char *s, int n)
1509 return s_nappend(s_reset(nil), s, n);
1512 /* fetch the next token from an RFC822 address string */
1513 /* we assume the header is RFC822-conformant in that */
1514 /* we recognize escaping anywhere even though it is only */
1515 /* supposed to be in quoted-strings, domain-literals, and comments. */
1517 /* i'd use yylex or yyparse here, but we need to preserve */
1518 /* things like comments, which i think it tosses away. */
1520 /* we're not strictly RFC822 compliant. we misparse such nonsense as */
1522 /* To: gre @ (Grace) plan9 . (Emlin) bell-labs.com */
1524 /* make sure there's no whitespace in your addresses and */
1525 /* you'll be fine. */
1537 /*char *ty82[] = {"white", "comment", "words", "comma", "<", ">", "err", "end"}; */
1538 #define ISWHITE(p) ((p)==' ' || (p)=='\t' || (p)=='\n' || (p)=='\r')
1540 get822token(String **tok, char *p, char **pp)
1553 case ' ': /* get whitespace */
1562 case '(': /* get comment */
1564 for(p++; *p && *p != ')'; p++)
1566 if(*(p+1) == '\0') {
1591 default: /* bunch of letters, perhaps quoted strings tossed in */
1594 for(; *p && (quoting || (!ISWHITE(*p) && *p != '>' && *p != '<' && *p != ',')); p++) {
1598 if(*(p+1) == '\0') {
1610 *tok = s_copyn(op, p-op);
1614 /* expand local aliases in an RFC822 mail line */
1615 /* add list of expanded addresses to to. */
1617 expandline(String **s, Addr *to)
1619 Addr *na, *nto, *ap;
1621 int tok, inangle, hadangle, nword;
1622 String *os, *ns, *stok, *lastword, *sinceword;
1624 os = s_copy(s_to_c(*s));
1625 p = strchr(s_to_c(*s), ':');
1629 ns = s_copyn(s_to_c(*s), p-s_to_c(*s));
1633 /* the only valid mailbox namings are word */
1634 /* and word* < addr > */
1635 /* without comments this would be simple. */
1636 /* we keep the following: */
1637 /* lastword - current guess at the address */
1638 /* sinceword - whitespace and comment seen since lastword */
1641 sinceword = s_new();
1647 switch(tok = get822token(&stok, p, &p)){
1656 na = rexpand(newaddr(s_to_c(lastword)));
1657 s_append(ns, na->v);
1658 s_append(ns, s_to_c(sinceword));
1659 for(ap=na->next; ap; ap=ap->next) {
1661 s_append(ns, ap->v);
1663 nto = concataddr(na, nto);
1678 s_append(sinceword, s_to_c(stok));
1686 s_append(sinceword, s_to_c(stok));
1693 if(tok != Tleftangle && inangle && s_len(lastword))
1695 if(tok == Tleftangle) {
1699 s_append(ns, s_to_c(lastword));
1700 s_append(ns, s_to_c(sinceword));
1702 if(tok == Tleftangle) {
1712 case Terror: /* give up, use old string, addrs */
1718 werrstr("rfc822 syntax error");
1719 rfc822syntaxerror = 1;
1727 nto = concataddr(nto, to);
1736 while(Bread(b, buf, sizeof buf) > 0)
1747 static int alloced, inuse;
1751 mimetypes = emalloc(alloced*sizeof(Ctype));
1752 mimetypes[0].ext = "";
1755 b = Bopen(unsharp("#9/lib/mimetype"), OREAD);
1759 p = Brdline(b, '\n');
1762 p[Blinelen(b)-1] = 0;
1763 if(tokenize(p, f, 6) < 4)
1765 if(strcmp(f[0], "-") == 0 || strcmp(f[1], "-") == 0 || strcmp(f[2], "-") == 0)
1767 if(inuse + 1 >= alloced){
1769 mimetypes = erealloc(mimetypes, alloced*sizeof(Ctype));
1771 snprint(type, sizeof(type), "%s/%s", f[1], f[2]);
1772 mimetypes[inuse].type = estrdup(type);
1773 mimetypes[inuse].ext = estrdup(f[0]+1);
1774 mimetypes[inuse].display = !strcmp(type, "text/plain");
1777 /* always make sure there's a terminator */
1778 mimetypes[inuse].ext = 0;
1804 erealloc(void *x, int n)
1813 /* Formatter for %" */
1814 /* Use double quotes to protect white space, frogs, \ and " */
1830 if(r=='\\' || r=='"')
1842 s = va_arg(f->args, char*);
1843 if(s == nil || *s == '\0')
1844 return fmtstrcpy(f, "\"\"");
1848 w = chartorune(&r, t);
1849 quotes |= needtoquote(r);
1852 return fmtstrcpy(f, s);
1856 w = chartorune(&r, t);
1857 if(needtoquote(r) == Qbackslash)
1861 return fmtrune(f, '"');
1869 if((mailfs = nsmount("mail", nil)) == nil)
1875 rfc2047fmt(Fmt *fmt)
1879 s = va_arg(fmt->args, char*);
1881 return fmtstrcpy(fmt, "");
1883 if((uchar)*p >= 0x80)
1885 return fmtstrcpy(fmt, s);
1888 fmtprint(fmt, "=?utf-8?q?");
1892 else if(*p == '_' || *p == '\t' || *p == '=' || *p == '?' || (uchar)*p >= 0x80)
1893 fmtprint(fmt, "=%.2uX", (uchar)*p);
1895 fmtrune(fmt, (uchar)*p);
1897 fmtprint(fmt, "?=");
1902 mksubject(char *line)
1905 static char buf[1024];
1907 p = strchr(line, ':')+1;
1911 if((uchar)*q >= 0x80)
1916 snprint(buf, sizeof buf, "Subject: %U", p);