1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912 |
- /*
- * marshal - gather mail message for transmission
- */
- #include "common.h"
- #include <ctype.h>
- typedef struct Attach Attach;
- typedef struct Alias Alias;
- typedef struct Addr Addr;
- typedef struct Ctype Ctype;
- struct Attach {
- Attach *next;
- char *path;
- char *type;
- int ainline;
- Ctype *ctype;
- };
- struct Alias
- {
- Alias *next;
- int n;
- Addr *addr;
- };
- struct Addr
- {
- Addr *next;
- char *v;
- };
- enum {
- Hfrom,
- Hto,
- Hcc,
- Hbcc,
- Hsender,
- Hreplyto,
- Hinreplyto,
- Hdate,
- Hsubject,
- Hmime,
- Hpriority,
- Hmsgid,
- Hcontent,
- Hx,
- Hprecedence,
- Nhdr,
- };
- enum {
- PGPsign = 1,
- PGPencrypt = 2,
- };
- char *hdrs[Nhdr] = {
- [Hfrom] "from:",
- [Hto] "to:",
- [Hcc] "cc:",
- [Hbcc] "bcc:",
- [Hreplyto] "reply-to:",
- [Hinreplyto] "in-reply-to:",
- [Hsender] "sender:",
- [Hdate] "date:",
- [Hsubject] "subject:",
- [Hpriority] "priority:",
- [Hmsgid] "message-id:",
- [Hmime] "mime-",
- [Hcontent] "content-",
- [Hx] "x-",
- [Hprecedence] "precedence",
- };
- struct Ctype {
- char *type;
- char *ext;
- int display;
- };
- Ctype ctype[] = {
- { "text/plain", "txt", 1, },
- { "text/html", "html", 1, },
- { "text/html", "htm", 1, },
- { "text/tab-separated-values", "tsv", 1, },
- { "text/richtext", "rtx", 1, },
- { "message/rfc822", "txt", 1, },
- { "", 0, 0, },
- };
- Ctype *mimetypes;
- int pid = -1;
- int pgppid = -1;
- void Bdrain(Biobuf*);
- void attachment(Attach*, Biobuf*);
- void body(Biobuf*, Biobuf*, int);
- int cistrcmp(char*, char*);
- int cistrncmp(char*, char*, int);
- int doublequote(Fmt*);
- void* emalloc(int);
- int enc64(char*, int, uchar*, int);
- void* erealloc(void*, int);
- char* estrdup(char*);
- Addr* expand(int, char**);
- Addr* expandline(String**, Addr*);
- void freeaddr(Addr*);
- void freeaddr(Addr *);
- void freeaddrs(Addr*);
- void freealias(Alias*);
- void freealiases(Alias*);
- Attach* mkattach(char*, char*, int);
- char* mkboundary(void);
- char* mksubject(char*);
- int pgpfilter(int*, int, int);
- int pgpopts(char*);
- int printcc(Biobuf*, Addr*);
- int printdate(Biobuf*);
- int printfrom(Biobuf*);
- int printinreplyto(Biobuf*, char*);
- int printsubject(Biobuf*, char*);
- int printto(Biobuf*, Addr*);
- Alias* readaliases(void);
- int readheaders(Biobuf*, int*, String**, Addr**, int);
- void readmimetypes(void);
- int rfc2047fmt(Fmt*);
- int sendmail(Addr*, Addr*, int*, char*);
- char* waitforsubprocs(void);
- int rflag, lbflag, xflag, holding, nflag, Fflag, eightflag, dflag;
- int pgpflag = 0;
- char *user;
- char *login;
- Alias *aliases;
- int rfc822syntaxerror;
- char lastchar;
- char *replymsg;
- enum
- {
- Ok = 0,
- Nomessage = 1,
- Nobody = 2,
- Error = -1,
- };
- #pragma varargck type "Z" char*
- #pragma varargck type "U" char*
- void
- usage(void)
- {
- fprint(2, "usage: %s [-Fr#xn] [-s subject] [-c ccrecipient] [-t type]"
- " [-aA attachment] [-p[es]] [-R replymsg] -8 | recipient-list\n",
- argv0);
- exits("usage");
- }
- void
- fatal(char *fmt, ...)
- {
- char buf[1024];
- va_list arg;
- if(pid >= 0)
- postnote(PNPROC, pid, "die");
- if(pgppid >= 0)
- postnote(PNPROC, pgppid, "die");
- va_start(arg, fmt);
- vseprint(buf, buf+sizeof(buf), fmt, arg);
- va_end(arg);
- fprint(2, "%s: %s\n", argv0, buf);
- holdoff(holding);
- exits(buf);
- }
- static void
- bwritesfree(Biobuf *bp, String **str)
- {
- if(Bwrite(bp, s_to_c(*str), s_len(*str)) != s_len(*str))
- fatal("write error");
- s_free(*str);
- *str = nil;
- }
- void
- main(int argc, char **argv)
- {
- int ccargc, flags, fd, noinput, headersrv;
- char *subject, *type, *boundary;
- char *ccargv[32];
- Addr *cc, *to;
- Attach *first, **l, *a;
- Biobuf in, out, *b;
- String *file, *hdrstring;
- noinput = 0;
- subject = nil;
- first = nil;
- l = &first;
- type = nil;
- hdrstring = nil;
- ccargc = 0;
- quotefmtinstall();
- fmtinstall('Z', doublequote);
- fmtinstall('U', rfc2047fmt);
- ARGBEGIN{
- case 'a':
- flags = 0;
- goto aflag;
- case 'A':
- flags = 1;
- aflag:
- a = mkattach(EARGF(usage()), type, flags);
- if(a == nil)
- exits("bad args");
- type = nil;
- *l = a;
- l = &a->next;
- break;
- case 'C':
- if(ccargc >= nelem(ccargv)-1)
- sysfatal("too many cc's");
- ccargv[ccargc++] = EARGF(usage());
- break;
- case 'd':
- dflag = 1; /* for sendmail */
- break;
- case 'F':
- Fflag = 1; /* file message */
- break;
- case 'n': /* no standard input */
- nflag = 1;
- break;
- case 'p': /* pgp flag: encrypt, sign, or both */
- if(pgpopts(EARGF(usage())) < 0)
- sysfatal("bad pgp options");
- break;
- case 'r':
- rflag = 1; /* for sendmail */
- break;
- case 'R':
- replymsg = EARGF(usage());
- break;
- case 's':
- subject = EARGF(usage());
- break;
- case 't':
- type = EARGF(usage());
- break;
- case 'x':
- xflag = 1; /* for sendmail */
- break;
- case '8': /* read recipients from rfc822 header */
- eightflag = 1;
- break;
- case '#':
- lbflag = 1; /* for sendmail */
- break;
- default:
- usage();
- break;
- }ARGEND;
- login = getlog();
- user = getenv("upasname");
- if(user == nil || *user == 0)
- user = login;
- if(user == nil || *user == 0)
- sysfatal("can't read user name");
- if(Binit(&in, 0, OREAD) < 0)
- sysfatal("can't Binit 0: %r");
- if(nflag && eightflag)
- sysfatal("can't use both -n and -8");
- if(eightflag && argc >= 1)
- usage();
- else if(!eightflag && argc < 1)
- usage();
- aliases = readaliases();
- if(!eightflag){
- to = expand(argc, argv);
- cc = expand(ccargc, ccargv);
- } else
- to = cc = nil;
- flags = 0;
- headersrv = Nomessage;
- if(!nflag && !xflag && !lbflag &&!dflag) {
- /*
- * pass through headers, keeping track of which we've seen,
- * perhaps building to list.
- */
- holding = holdon();
- headersrv = readheaders(&in, &flags, &hdrstring,
- eightflag? &to: nil, 1);
- if(rfc822syntaxerror){
- Bdrain(&in);
- fatal("rfc822 syntax error, message not sent");
- }
- if(to == nil){
- Bdrain(&in);
- fatal("no addresses found, message not sent");
- }
- switch(headersrv){
- case Error: /* error */
- fatal("reading");
- break;
- case Nomessage: /* no message, just exit mimicking old behavior */
- noinput = 1;
- if(first == nil)
- exits(0);
- break;
- }
- }
- fd = sendmail(to, cc, &pid, Fflag ? argv[0] : nil);
- if(fd < 0)
- sysfatal("execing sendmail: %r\n:");
- if(xflag || lbflag || dflag){
- close(fd);
- exits(waitforsubprocs());
- }
- if(Binit(&out, fd, OWRITE) < 0)
- fatal("can't Binit 1: %r");
- if(!nflag)
- bwritesfree(&out, &hdrstring);
- /* read user's standard headers */
- file = s_new();
- mboxpath("headers", user, file, 0);
- b = Bopen(s_to_c(file), OREAD);
- if(b != nil){
- if (readheaders(b, &flags, &hdrstring, nil, 0) == Error)
- fatal("reading");
- Bterm(b);
- bwritesfree(&out, &hdrstring);
- }
- /* add any headers we need */
- if((flags & (1<<Hdate)) == 0)
- if(printdate(&out) < 0)
- fatal("writing");
- if((flags & (1<<Hfrom)) == 0)
- if(printfrom(&out) < 0)
- fatal("writing");
- if((flags & (1<<Hto)) == 0)
- if(printto(&out, to) < 0)
- fatal("writing");
- if((flags & (1<<Hcc)) == 0)
- if(printcc(&out, cc) < 0)
- fatal("writing");
- if((flags & (1<<Hsubject)) == 0 && subject != nil)
- if(printsubject(&out, subject) < 0)
- fatal("writing");
- if(replymsg != nil)
- if(printinreplyto(&out, replymsg) < 0)
- fatal("writing");
- Bprint(&out, "MIME-Version: 1.0\n");
- if(pgpflag){
- /* interpose pgp process between us and sendmail to handle body */
- Bflush(&out);
- Bterm(&out);
- fd = pgpfilter(&pgppid, fd, pgpflag);
- if(Binit(&out, fd, OWRITE) < 0)
- fatal("can't Binit 1: %r");
- }
- /* if attachments, stick in multipart headers */
- boundary = nil;
- if(first != nil){
- boundary = mkboundary();
- Bprint(&out, "Content-Type: multipart/mixed;\n");
- Bprint(&out, "\tboundary=\"%s\"\n\n", boundary);
- Bprint(&out, "This is a multi-part message in MIME format.\n");
- Bprint(&out, "--%s\n", boundary);
- Bprint(&out, "Content-Disposition: inline\n");
- }
- if(!nflag){
- if(!noinput && headersrv == Ok)
- body(&in, &out, 1);
- } else
- Bprint(&out, "\n");
- holdoff(holding);
- Bflush(&out);
- for(a = first; a != nil; a = a->next){
- if(lastchar != '\n')
- Bprint(&out, "\n");
- Bprint(&out, "--%s\n", boundary);
- attachment(a, &out);
- }
- if(first != nil){
- if(lastchar != '\n')
- Bprint(&out, "\n");
- Bprint(&out, "--%s--\n", boundary);
- }
- Bterm(&out);
- close(fd);
- exits(waitforsubprocs());
- }
- /* evaluate pgp option string */
- int
- pgpopts(char *s)
- {
- if(s == nil || s[0] == '\0')
- return -1;
- while(*s){
- switch(*s++){
- case 's': case 'S':
- pgpflag |= PGPsign;
- break;
- case 'e': case 'E':
- pgpflag |= PGPencrypt;
- break;
- default:
- return -1;
- }
- }
- return 0;
- }
- /*
- * read headers from stdin into a String, expanding local aliases,
- * keep track of which headers are there, which addresses we have
- * remove Bcc: line.
- */
- int
- readheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict)
- {
- int i, seen, hdrtype;
- char *p;
- Addr *to;
- String *s, *sline;
- s = s_new();
- sline = nil;
- to = nil;
- hdrtype = -1;
- seen = 0;
- for(;;) {
- if((p = Brdline(in, '\n')) != nil) {
- seen = 1;
- p[Blinelen(in)-1] = 0;
- /* coalesce multiline headers */
- if((*p == ' ' || *p == '\t') && sline){
- s_append(sline, "\n");
- s_append(sline, p);
- p[Blinelen(in)-1] = '\n';
- continue;
- }
- }
- /* process the current header, it's all been read */
- if(sline) {
- assert(hdrtype != -1);
- if(top){
- switch(hdrtype){
- case Hto:
- case Hcc:
- case Hbcc:
- to = expandline(&sline, to);
- break;
- }
- }
- if(hdrtype == Hsubject){
- s_append(s, mksubject(s_to_c(sline)));
- s_append(s, "\n");
- }else if(top==nil || hdrtype!=Hbcc){
- s_append(s, s_to_c(sline));
- s_append(s, "\n");
- }
- s_free(sline);
- sline = nil;
- }
- if(p == nil)
- break;
- /* if no :, it's not a header, seek back and break */
- if(strchr(p, ':') == nil){
- p[Blinelen(in)-1] = '\n';
- Bseek(in, -Blinelen(in), 1);
- break;
- }
- sline = s_copy(p);
- /*
- * classify the header. If we don't recognize it, break.
- * This is to take care of users who start messages with
- * lines that contain ':'s but that aren't headers.
- * This is a bit hokey. Since I decided to let users type
- * headers, I need some way to distinguish. Therefore,
- * marshal tries to know all likely headers and will indeed
- * screw up if the user types an unlikely one. -- presotto
- */
- hdrtype = -1;
- for(i = 0; i < nelem(hdrs); i++){
- if(cistrncmp(hdrs[i], p, strlen(hdrs[i])) == 0){
- *fp |= 1<<i;
- hdrtype = i;
- break;
- }
- }
- if(strict){
- if(hdrtype == -1){
- p[Blinelen(in)-1] = '\n';
- Bseek(in, -Blinelen(in), 1);
- break;
- }
- } else
- hdrtype = 0;
- p[Blinelen(in)-1] = '\n';
- }
- *sp = s;
- if(top)
- *top = to;
- if(seen == 0){
- if(Blinelen(in) == 0)
- return Nomessage;
- else
- return Ok;
- }
- if(p == nil)
- return Nobody;
- return Ok;
- }
- /* pass the body to sendmail, make sure body starts and ends with a newline */
- void
- body(Biobuf *in, Biobuf *out, int docontenttype)
- {
- char *buf, *p;
- int i, n, len;
- n = 0;
- len = 16*1024;
- buf = emalloc(len);
- /* first char must be newline */
- i = Bgetc(in);
- if(i > 0){
- if(i != '\n')
- buf[n++] = '\n';
- buf[n++] = i;
- } else
- buf[n++] = '\n';
- /* read into memory */
- if(docontenttype){
- while(docontenttype){
- if(n == len){
- len += len >> 2;
- buf = realloc(buf, len);
- if(buf == nil)
- sysfatal("%r");
- }
- p = buf+n;
- i = Bread(in, p, len - n);
- if(i < 0)
- fatal("input error2");
- if(i == 0)
- break;
- n += i;
- for(; i > 0; i--)
- if((*p++ & 0x80) && docontenttype){
- Bprint(out, "Content-Type: text/plain; charset=\"UTF-8\"\n");
- Bprint(out, "Content-Transfer-Encoding: 8bit\n");
- docontenttype = 0;
- break;
- }
- }
- if(docontenttype){
- Bprint(out, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
- Bprint(out, "Content-Transfer-Encoding: 7bit\n");
- }
- }
- /* write what we already read */
- if(Bwrite(out, buf, n) < 0)
- fatal("output error");
- if(n > 0)
- lastchar = buf[n-1];
- else
- lastchar = '\n';
- /* pass the rest */
- for(;;){
- n = Bread(in, buf, len);
- if(n < 0)
- fatal("input error2");
- if(n == 0)
- break;
- if(Bwrite(out, buf, n) < 0)
- fatal("output error");
- lastchar = buf[n-1];
- }
- }
- /*
- * pass the body to sendmail encoding with base64
- *
- * the size of buf is very important to enc64. Anything other than
- * a multiple of 3 will cause enc64 to output a termination sequence.
- * To ensure that a full buf corresponds to a multiple of complete lines,
- * we make buf a multiple of 3*18 since that's how many enc64 sticks on
- * a single line. This avoids short lines in the output which is pleasing
- * but not necessary.
- */
- void
- body64(Biobuf *in, Biobuf *out)
- {
- int m, n;
- uchar buf[3*18*54];
- char obuf[3*18*54*2];
- Bprint(out, "\n");
- for(;;){
- n = Bread(in, buf, sizeof(buf));
- if(n < 0)
- fatal("input error");
- if(n == 0)
- break;
- m = enc64(obuf, sizeof(obuf), buf, n);
- if(Bwrite(out, obuf, m) < 0)
- fatal("output error");
- }
- lastchar = '\n';
- }
- /* pass message to sendmail, make sure body starts with a newline */
- void
- copy(Biobuf *in, Biobuf *out)
- {
- int n;
- char buf[4*1024];
- for(;;){
- n = Bread(in, buf, sizeof(buf));
- if(n < 0)
- fatal("input error");
- if(n == 0)
- break;
- if(Bwrite(out, buf, n) < 0)
- fatal("output error");
- }
- }
- void
- attachment(Attach *a, Biobuf *out)
- {
- Biobuf *f;
- char *p;
- /* if it's already mime encoded, just copy */
- if(strcmp(a->type, "mime") == 0){
- f = Bopen(a->path, OREAD);
- if(f == nil){
- /*
- * hack: give marshal time to stdin, before we kill it
- * (for dead.letter)
- */
- sleep(500);
- postnote(PNPROC, pid, "interrupt");
- sysfatal("opening %s: %r", a->path);
- }
- copy(f, out);
- Bterm(f);
- }
- /* if it's not already mime encoded ... */
- if(strcmp(a->type, "text/plain") != 0)
- Bprint(out, "Content-Type: %s\n", a->type);
- if(a->ainline)
- Bprint(out, "Content-Disposition: inline\n");
- else {
- p = strrchr(a->path, '/');
- if(p == nil)
- p = a->path;
- else
- p++;
- Bprint(out, "Content-Disposition: attachment; filename=%Z\n", p);
- }
- f = Bopen(a->path, OREAD);
- if(f == nil){
- /*
- * hack: give marshal time to stdin, before we kill it
- * (for dead.letter)
- */
- sleep(500);
- postnote(PNPROC, pid, "interrupt");
- sysfatal("opening %s: %r", a->path);
- }
- /* dump our local 'From ' line when passing along mail messages */
- if(strcmp(a->type, "message/rfc822") == 0){
- p = Brdline(f, '\n');
- if(strncmp(p, "From ", 5) != 0)
- Bseek(f, 0, 0);
- }
- if(a->ctype->display)
- body(f, out, strcmp(a->type, "text/plain") == 0);
- else {
- Bprint(out, "Content-Transfer-Encoding: base64\n");
- body64(f, out);
- }
- Bterm(f);
- }
- char *ascwday[] =
- {
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
- };
- char *ascmon[] =
- {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
- };
- int
- printdate(Biobuf *b)
- {
- int tz;
- Tm *tm;
- tm = localtime(time(0));
- tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
- return Bprint(b, "Date: %s, %d %s %d %2.2d:%2.2d:%2.2d %s%.4d\n",
- ascwday[tm->wday], tm->mday, ascmon[tm->mon], 1900 + tm->year,
- tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz);
- }
- int
- printfrom(Biobuf *b)
- {
- return Bprint(b, "From: %s\n", user);
- }
- int
- printto(Biobuf *b, Addr *a)
- {
- int i;
- if(Bprint(b, "To: %s", a->v) < 0)
- return -1;
- i = 0;
- for(a = a->next; a != nil; a = a->next)
- if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
- return -1;
- if(Bprint(b, "\n") < 0)
- return -1;
- return 0;
- }
- int
- printcc(Biobuf *b, Addr *a)
- {
- int i;
- if(a == nil)
- return 0;
- if(Bprint(b, "CC: %s", a->v) < 0)
- return -1;
- i = 0;
- for(a = a->next; a != nil; a = a->next)
- if(Bprint(b, "%s%s", ((i++ & 7) == 7)?",\n\t":", ", a->v) < 0)
- return -1;
- if(Bprint(b, "\n") < 0)
- return -1;
- return 0;
- }
- int
- printsubject(Biobuf *b, char *subject)
- {
- return Bprint(b, "Subject: %U\n", subject);
- }
- int
- printinreplyto(Biobuf *out, char *dir)
- {
- int fd, n;
- char buf[256];
- String *s = s_copy(dir);
- s_append(s, "/messageid");
- fd = open(s_to_c(s), OREAD);
- s_free(s);
- if(fd < 0)
- return 0;
- n = read(fd, buf, sizeof(buf)-1);
- close(fd);
- if(n <= 0)
- return 0;
- buf[n] = 0;
- return Bprint(out, "In-Reply-To: %s\n", buf);
- }
- Attach*
- mkattach(char *file, char *type, int ainline)
- {
- int n, pfd[2];
- char *p;
- char ftype[64];
- Attach *a;
- Ctype *c;
- if(file == nil)
- return nil;
- if(access(file, 4) == -1){
- fprint(2, "%s: %s can't read file\n", argv0, file);
- return nil;
- }
- a = emalloc(sizeof(*a));
- a->path = file;
- a->next = nil;
- a->type = type;
- a->ainline = ainline;
- a->ctype = nil;
- if(type != nil){
- for(c = ctype; ; c++)
- if(strncmp(type, c->type, strlen(c->type)) == 0){
- a->ctype = c;
- break;
- }
- return a;
- }
- /* pick a type depending on extension */
- p = strchr(file, '.');
- if(p != nil)
- p++;
- /* check the builtin extensions */
- if(p != nil){
- for(c = ctype; c->ext != nil; c++)
- if(strcmp(p, c->ext) == 0){
- a->type = c->type;
- a->ctype = c;
- return a;
- }
- }
- /* try the mime types file */
- if(p != nil){
- if(mimetypes == nil)
- readmimetypes();
- for(c = mimetypes; c != nil && c->ext != nil; c++)
- if(strcmp(p, c->ext) == 0){
- a->type = c->type;
- a->ctype = c;
- return a;
- }
- }
- /* run file to figure out the type */
- a->type = "application/octet-stream"; /* safest default */
- if(pipe(pfd) < 0)
- return a;
- switch(fork()){
- case -1:
- break;
- case 0:
- close(pfd[1]);
- close(0);
- dup(pfd[0], 0);
- close(1);
- dup(pfd[0], 1);
- execl("/bin/file", "file", "-m", file, nil);
- exits(0);
- default:
- close(pfd[0]);
- n = read(pfd[1], ftype, sizeof(ftype));
- if(n > 0){
- ftype[n-1] = 0;
- a->type = estrdup(ftype);
- }
- close(pfd[1]);
- waitpid();
- break;
- }
- for(c = ctype; ; c++)
- if(strncmp(a->type, c->type, strlen(c->type)) == 0){
- a->ctype = c;
- break;
- }
- return a;
- }
- char*
- mkboundary(void)
- {
- int i;
- char buf[32];
- srand((time(0)<<16)|getpid());
- strcpy(buf, "upas-");
- for(i = 5; i < sizeof(buf)-1; i++)
- buf[i] = 'a' + nrand(26);
- buf[i] = 0;
- return estrdup(buf);
- }
- /* copy types to two fd's */
- static void
- tee(int in, int out1, int out2)
- {
- int n;
- char buf[8*1024];
- while ((n = read(in, buf, sizeof buf)) > 0)
- if (write(out1, buf, n) != n ||
- write(out2, buf, n) != n)
- break;
- }
- /* print the unix from line */
- int
- printunixfrom(int fd)
- {
- int tz;
- Tm *tm;
- tm = localtime(time(0));
- tz = (tm->tzoff/3600)*100 + (tm->tzoff/60)%60;
- return fprint(fd, "From %s %s %s %d %2.2d:%2.2d:%2.2d %s%.4d %d\n",
- user,
- ascwday[tm->wday], ascmon[tm->mon], tm->mday,
- tm->hour, tm->min, tm->sec, tz>=0?"+":"", tz, 1900 + tm->year);
- }
- char *specialfile[] =
- {
- "pipeto",
- "pipefrom",
- "L.mbox",
- "forward",
- "names"
- };
- /* return 1 if this is a special file */
- static int
- special(String *s)
- {
- int i;
- char *p;
- p = strrchr(s_to_c(s), '/');
- if(p == nil)
- p = s_to_c(s);
- else
- p++;
- for(i = 0; i < nelem(specialfile); i++)
- if(strcmp(p, specialfile[i]) == 0)
- return 1;
- return 0;
- }
- /* open the folder using the recipients account name */
- static int
- openfolder(char *rcvr)
- {
- int c, fd, scarey;
- char *p;
- Dir *d;
- String *file;
- file = s_new();
- mboxpath("f", user, file, 0);
- /* if $mail/f exists, store there, otherwise in $mail */
- d = dirstat(s_to_c(file));
- if(d == nil || d->qid.type != QTDIR){
- scarey = 1;
- file->ptr -= 1;
- } else {
- s_putc(file, '/');
- scarey = 0;
- }
- free(d);
- p = strrchr(rcvr, '!');
- if(p != nil)
- rcvr = p+1;
- while(*rcvr && *rcvr != '@'){
- c = *rcvr++;
- if(c == '/')
- c = '_';
- s_putc(file, c);
- }
- s_terminate(file);
- if(scarey && special(file)){
- fprint(2, "%s: won't overwrite %s\n", argv0, s_to_c(file));
- s_free(file);
- return -1;
- }
- fd = open(s_to_c(file), OWRITE);
- if(fd < 0)
- fd = create(s_to_c(file), OWRITE, 0660);
- s_free(file);
- return fd;
- }
- /* start up sendmail and return an fd to talk to it with */
- int
- sendmail(Addr *to, Addr *cc, int *pid, char *rcvr)
- {
- int ac, fd;
- int pfd[2];
- char **av, **v;
- Addr *a;
- String *cmd;
- fd = -1;
- if(rcvr != nil)
- fd = openfolder(rcvr);
- ac = 0;
- for(a = to; a != nil; a = a->next)
- ac++;
- for(a = cc; a != nil; a = a->next)
- ac++;
- v = av = emalloc(sizeof(char*)*(ac+20));
- ac = 0;
- v[ac++] = "sendmail";
- if(xflag)
- v[ac++] = "-x";
- if(rflag)
- v[ac++] = "-r";
- if(lbflag)
- v[ac++] = "-#";
- if(dflag)
- v[ac++] = "-d";
- for(a = to; a != nil; a = a->next)
- v[ac++] = a->v;
- for(a = cc; a != nil; a = a->next)
- v[ac++] = a->v;
- v[ac] = 0;
- if(pipe(pfd) < 0)
- fatal("%r");
- switch(*pid = rfork(RFFDG|RFREND|RFPROC|RFENVG)){
- case -1:
- fatal("%r");
- break;
- case 0:
- if(holding)
- close(holding);
- close(pfd[1]);
- dup(pfd[0], 0);
- close(pfd[0]);
- if(rcvr != nil){
- if(pipe(pfd) < 0)
- fatal("%r");
- switch(fork()){
- case -1:
- fatal("%r");
- break;
- case 0:
- close(pfd[0]);
- seek(fd, 0, 2);
- printunixfrom(fd);
- tee(0, pfd[1], fd);
- write(fd, "\n", 1);
- exits(0);
- default:
- close(fd);
- close(pfd[1]);
- dup(pfd[0], 0);
- break;
- }
- }
- if(replymsg != nil)
- putenv("replymsg", replymsg);
- cmd = mboxpath("pipefrom", login, s_new(), 0);
- exec(s_to_c(cmd), av);
- exec("/bin/myupassend", av);
- exec("/bin/upas/send", av);
- fatal("execing: %r");
- break;
- default:
- if(rcvr != nil)
- close(fd);
- close(pfd[0]);
- break;
- }
- return pfd[1];
- }
- /*
- * start up pgp process and return an fd to talk to it with.
- * its standard output will be the original fd, which goes to sendmail.
- */
- int
- pgpfilter(int *pid, int fd, int pgpflag)
- {
- int ac;
- int pfd[2];
- char **av, **v;
- v = av = emalloc(sizeof(char*)*8);
- ac = 0;
- v[ac++] = "pgp";
- v[ac++] = "-fat"; /* operate as a filter, generate text */
- if(pgpflag & PGPsign)
- v[ac++] = "-s";
- if(pgpflag & PGPencrypt)
- v[ac++] = "-e";
- v[ac] = 0;
- if(pipe(pfd) < 0)
- fatal("%r");
- switch(*pid = fork()){
- case -1:
- fatal("%r");
- break;
- case 0:
- close(pfd[1]);
- dup(pfd[0], 0);
- close(pfd[0]);
- dup(fd, 1);
- close(fd);
- /* add newline to avoid confusing pgp output with 822 headers */
- write(1, "\n", 1);
- exec("/bin/pgp", av);
- fatal("execing: %r");
- break;
- default:
- close(pfd[0]);
- break;
- }
- close(fd);
- return pfd[1];
- }
- /* wait for sendmail and pgp to exit; exit here if either failed */
- char*
- waitforsubprocs(void)
- {
- Waitmsg *w;
- char *err;
- err = nil;
- while((w = wait()) != nil){
- if(w->pid == pid || w->pid == pgppid)
- if(w->msg[0] != 0)
- err = estrdup(w->msg);
- free(w);
- }
- if(err)
- exits(err);
- return nil;
- }
- int
- cistrncmp(char *a, char *b, int n)
- {
- while(n-- > 0)
- if(tolower(*a++) != tolower(*b++))
- return -1;
- return 0;
- }
- int
- cistrcmp(char *a, char *b)
- {
- for(;;){
- if(tolower(*a) != tolower(*b++))
- return -1;
- if(*a++ == 0)
- break;
- }
- return 0;
- }
- static uchar t64d[256];
- static char t64e[64];
- static void
- init64(void)
- {
- int c, i;
- memset(t64d, 255, 256);
- memset(t64e, '=', 64);
- i = 0;
- for(c = 'A'; c <= 'Z'; c++){
- t64e[i] = c;
- t64d[c] = i++;
- }
- for(c = 'a'; c <= 'z'; c++){
- t64e[i] = c;
- t64d[c] = i++;
- }
- for(c = '0'; c <= '9'; c++){
- t64e[i] = c;
- t64d[c] = i++;
- }
- t64e[i] = '+';
- t64d['+'] = i++;
- t64e[i] = '/';
- t64d['/'] = i;
- }
- int
- enc64(char *out, int lim, uchar *in, int n)
- {
- int i;
- ulong b24;
- char *start = out;
- char *e = out + lim;
- if(t64e[0] == 0)
- init64();
- for(i = 0; i < n/3; i++){
- b24 = (*in++)<<16;
- b24 |= (*in++)<<8;
- b24 |= *in++;
- if(out + 5 >= e)
- goto exhausted;
- *out++ = t64e[(b24>>18)];
- *out++ = t64e[(b24>>12)&0x3f];
- *out++ = t64e[(b24>>6)&0x3f];
- *out++ = t64e[(b24)&0x3f];
- if((i%18) == 17)
- *out++ = '\n';
- }
- switch(n%3){
- case 2:
- b24 = (*in++)<<16;
- b24 |= (*in)<<8;
- if(out + 4 >= e)
- goto exhausted;
- *out++ = t64e[(b24>>18)];
- *out++ = t64e[(b24>>12)&0x3f];
- *out++ = t64e[(b24>>6)&0x3f];
- break;
- case 1:
- b24 = (*in)<<16;
- if(out + 4 >= e)
- goto exhausted;
- *out++ = t64e[(b24>>18)];
- *out++ = t64e[(b24>>12)&0x3f];
- *out++ = '=';
- break;
- case 0:
- if((i%18) != 0)
- *out++ = '\n';
- *out = 0;
- return out - start;
- }
- exhausted:
- *out++ = '=';
- *out++ = '\n';
- *out = 0;
- return out - start;
- }
- void
- freealias(Alias *a)
- {
- freeaddrs(a->addr);
- free(a);
- }
- void
- freealiases(Alias *a)
- {
- Alias *next;
- while(a != nil){
- next = a->next;
- freealias(a);
- a = next;
- }
- }
- /*
- * read alias file
- */
- Alias*
- readaliases(void)
- {
- Addr *addr, **al;
- Alias *a, **l, *first;
- Sinstack *sp;
- String *file, *line, *token;
- static int already;
- first = nil;
- file = s_new();
- line = s_new();
- token = s_new();
- /* open and get length */
- mboxpath("names", login, file, 0);
- sp = s_allocinstack(s_to_c(file));
- if(sp == nil)
- goto out;
- l = &first;
- /* read a line at a time. */
- while(s_rdinstack(sp, s_restart(line))!=nil) {
- s_restart(line);
- a = emalloc(sizeof(Alias));
- al = &a->addr;
- while(s_parse(line, s_restart(token)) != 0) {
- addr = emalloc(sizeof(Addr));
- addr->v = strdup(s_to_c(token));
- addr->next = 0;
- *al = addr;
- al = &addr->next;
- }
- if(a->addr == nil || a->addr->next == nil){
- freealias(a);
- continue;
- }
- a->next = nil;
- *l = a;
- l = &a->next;
- }
- s_freeinstack(sp);
- out:
- s_free(file);
- s_free(line);
- s_free(token);
- return first;
- }
- Addr*
- newaddr(char *name)
- {
- Addr *a;
- a = emalloc(sizeof(*a));
- a->next = nil;
- a->v = estrdup(name);
- if(a->v == nil)
- sysfatal("%r");
- return a;
- }
- /*
- * expand personal aliases since the names are meaningless in
- * other contexts
- */
- Addr*
- _expand(Addr *old, int *changedp)
- {
- Addr *first, *next, **l, *a;
- Alias *al;
- *changedp = 0;
- first = nil;
- l = &first;
- for(;old != nil; old = next){
- next = old->next;
- for(al = aliases; al != nil; al = al->next){
- if(strcmp(al->addr->v, old->v) == 0){
- for(a = al->addr->next; a != nil; a = a->next){
- *l = newaddr(a->v);
- if(*l == nil)
- sysfatal("%r");
- l = &(*l)->next;
- *changedp = 1;
- }
- break;
- }
- }
- if(al != nil){
- freeaddr(old);
- continue;
- }
- *l = old;
- old->next = nil;
- l = &(*l)->next;
- }
- return first;
- }
- Addr*
- rexpand(Addr *old)
- {
- int i, changed;
- changed = 0;
- for(i = 0; i < 32; i++){
- old = _expand(old, &changed);
- if(changed == 0)
- break;
- }
- return old;
- }
- Addr*
- unique(Addr *first)
- {
- Addr *a, **l, *x;
- for(a = first; a != nil; a = a->next){
- for(l = &a->next; *l != nil;){
- if(strcmp(a->v, (*l)->v) == 0){
- x = *l;
- *l = x->next;
- freeaddr(x);
- } else
- l = &(*l)->next;
- }
- }
- return first;
- }
- Addr*
- expand(int ac, char **av)
- {
- int i;
- Addr *first, **l;
- first = nil;
- /* make a list of the starting addresses */
- l = &first;
- for(i = 0; i < ac; i++){
- *l = newaddr(av[i]);
- if(*l == nil)
- sysfatal("%r");
- l = &(*l)->next;
- }
- /* recurse till we don't change any more */
- return unique(rexpand(first));
- }
- Addr*
- concataddr(Addr *a, Addr *b)
- {
- Addr *oa;
- if(a == nil)
- return b;
- oa = a;
- for(; a->next; a=a->next)
- ;
- a->next = b;
- return oa;
- }
- void
- freeaddr(Addr *ap)
- {
- free(ap->v);
- free(ap);
- }
- void
- freeaddrs(Addr *ap)
- {
- Addr *next;
- for(; ap; ap=next) {
- next = ap->next;
- freeaddr(ap);
- }
- }
- String*
- s_copyn(char *s, int n)
- {
- return s_nappend(s_reset(nil), s, n);
- }
- /*
- * fetch the next token from an RFC822 address string
- * we assume the header is RFC822-conformant in that
- * we recognize escaping anywhere even though it is only
- * supposed to be in quoted-strings, domain-literals, and comments.
- *
- * i'd use yylex or yyparse here, but we need to preserve
- * things like comments, which i think it tosses away.
- *
- * we're not strictly RFC822 compliant. we misparse such nonsense as
- *
- * To: gre @ (Grace) plan9 . (Emlin) bell-labs.com
- *
- * make sure there's no whitespace in your addresses and
- * you'll be fine.
- */
- enum {
- Twhite,
- Tcomment,
- Twords,
- Tcomma,
- Tleftangle,
- Trightangle,
- Terror,
- Tend,
- };
- // char *ty82[] = {"white", "comment", "words", "comma", "<", ">", "err", "end"};
- #define ISWHITE(p) ((p)==' ' || (p)=='\t' || (p)=='\n' || (p)=='\r')
- int
- get822token(String **tok, char *p, char **pp)
- {
- int type, quoting;
- char *op;
- op = p;
- switch(*p){
- case '\0':
- *tok = nil;
- *pp = nil;
- return Tend;
- case ' ': /* get whitespace */
- case '\t':
- case '\n':
- case '\r':
- type = Twhite;
- while(ISWHITE(*p))
- p++;
- break;
- case '(': /* get comment */
- type = Tcomment;
- for(p++; *p && *p != ')'; p++)
- if(*p == '\\') {
- if(*(p+1) == '\0') {
- *tok = nil;
- return Terror;
- }
- p++;
- }
- if(*p != ')') {
- *tok = nil;
- return Terror;
- }
- p++;
- break;
- case ',':
- type = Tcomma;
- p++;
- break;
- case '<':
- type = Tleftangle;
- p++;
- break;
- case '>':
- type = Trightangle;
- p++;
- break;
- default: /* bunch of letters, perhaps quoted strings tossed in */
- type = Twords;
- quoting = 0;
- for (; *p && (quoting ||
- (!ISWHITE(*p) && *p != '>' && *p != '<' && *p != ',')); p++) {
- if(*p == '"')
- quoting = !quoting;
- if(*p == '\\') {
- if(*(p+1) == '\0') {
- *tok = nil;
- return Terror;
- }
- p++;
- }
- }
- break;
- }
- if(pp)
- *pp = p;
- *tok = s_copyn(op, p-op);
- return type;
- }
- /*
- * expand local aliases in an RFC822 mail line
- * add list of expanded addresses to to.
- */
- Addr*
- expandline(String **s, Addr *to)
- {
- int tok, inangle, hadangle, nword;
- char *p;
- Addr *na, *nto, *ap;
- String *os, *ns, *stok, *lastword, *sinceword;
- os = s_copy(s_to_c(*s));
- p = strchr(s_to_c(*s), ':');
- assert(p != nil);
- p++;
- ns = s_copyn(s_to_c(*s), p-s_to_c(*s));
- stok = nil;
- nto = nil;
- /*
- * the only valid mailbox namings are word
- * and word* < addr >
- * without comments this would be simple.
- * we keep the following:
- * lastword - current guess at the address
- * sinceword - whitespace and comment seen since lastword
- */
- lastword = s_new();
- sinceword = s_new();
- inangle = 0;
- nword = 0;
- hadangle = 0;
- for(;;) {
- stok = nil;
- switch(tok = get822token(&stok, p, &p)){
- default:
- abort();
- case Tcomma:
- case Tend:
- if(inangle)
- goto Error;
- if(nword != 1)
- goto Error;
- na = rexpand(newaddr(s_to_c(lastword)));
- s_append(ns, na->v);
- s_append(ns, s_to_c(sinceword));
- for(ap=na->next; ap; ap=ap->next) {
- s_append(ns, ", ");
- s_append(ns, ap->v);
- }
- nto = concataddr(na, nto);
- if(tok == Tcomma){
- s_append(ns, ",");
- s_free(stok);
- }
- if(tok == Tend)
- goto Break2;
- inangle = 0;
- nword = 0;
- hadangle = 0;
- s_reset(sinceword);
- s_reset(lastword);
- break;
- case Twhite:
- case Tcomment:
- s_append(sinceword, s_to_c(stok));
- s_free(stok);
- break;
- case Trightangle:
- if(!inangle)
- goto Error;
- inangle = 0;
- hadangle = 1;
- s_append(sinceword, s_to_c(stok));
- s_free(stok);
- break;
- case Twords:
- case Tleftangle:
- if(hadangle)
- goto Error;
- if(tok != Tleftangle && inangle && s_len(lastword))
- goto Error;
- if(tok == Tleftangle) {
- inangle = 1;
- nword = 1;
- }
- s_append(ns, s_to_c(lastword));
- s_append(ns, s_to_c(sinceword));
- s_reset(sinceword);
- if(tok == Tleftangle) {
- s_append(ns, "<");
- s_reset(lastword);
- } else {
- s_free(lastword);
- lastword = stok;
- }
- if(!inangle)
- nword++;
- break;
- case Terror: /* give up, use old string, addrs */
- Error:
- ns = os;
- os = nil;
- freeaddrs(nto);
- nto = nil;
- werrstr("rfc822 syntax error");
- rfc822syntaxerror = 1;
- goto Break2;
- }
- }
- Break2:
- s_free(*s);
- s_free(os);
- *s = ns;
- nto = concataddr(nto, to);
- return nto;
- }
- void
- Bdrain(Biobuf *b)
- {
- char buf[8192];
- while(Bread(b, buf, sizeof buf) > 0)
- ;
- }
- void
- readmimetypes(void)
- {
- char *p;
- char type[256];
- char *f[6];
- Biobuf *b;
- static int alloced, inuse;
- if(mimetypes == 0){
- alloced = 256;
- mimetypes = emalloc(alloced*sizeof(Ctype));
- mimetypes[0].ext = "";
- }
- b = Bopen("/sys/lib/mimetype", OREAD);
- if(b == nil)
- return;
- for(;;){
- p = Brdline(b, '\n');
- if(p == nil)
- break;
- p[Blinelen(b)-1] = 0;
- if(tokenize(p, f, 6) < 4)
- continue;
- if (strcmp(f[0], "-") == 0 || strcmp(f[1], "-") == 0 ||
- strcmp(f[2], "-") == 0)
- continue;
- if(inuse + 1 >= alloced){
- alloced += 256;
- mimetypes = erealloc(mimetypes, alloced*sizeof(Ctype));
- }
- snprint(type, sizeof(type), "%s/%s", f[1], f[2]);
- mimetypes[inuse].type = estrdup(type);
- mimetypes[inuse].ext = estrdup(f[0]+1);
- mimetypes[inuse].display = !strcmp(type, "text/plain");
- inuse++;
- /* always make sure there's a terminator */
- mimetypes[inuse].ext = 0;
- }
- Bterm(b);
- }
- char*
- estrdup(char *x)
- {
- x = strdup(x);
- if(x == nil)
- fatal("memory");
- return x;
- }
- void*
- emalloc(int n)
- {
- void *x;
- x = malloc(n);
- if(x == nil)
- fatal("%r");
- return x;
- }
- void*
- erealloc(void *x, int n)
- {
- x = realloc(x, n);
- if(x == nil)
- fatal("%r");
- return x;
- }
- /*
- * Formatter for %"
- * Use double quotes to protect white space, frogs, \ and "
- */
- enum
- {
- Qok = 0,
- Qquote,
- Qbackslash,
- };
- static int
- needtoquote(Rune r)
- {
- if(r >= Runeself)
- return Qquote;
- if(r <= ' ')
- return Qquote;
- if(r=='\\' || r=='"')
- return Qbackslash;
- return Qok;
- }
- int
- doublequote(Fmt *f)
- {
- int w, quotes;
- char *s, *t;
- Rune r;
- s = va_arg(f->args, char*);
- if(s == nil || *s == '\0')
- return fmtstrcpy(f, "\"\"");
- quotes = 0;
- for(t = s; *t; t += w){
- w = chartorune(&r, t);
- quotes |= needtoquote(r);
- }
- if(quotes == 0)
- return fmtstrcpy(f, s);
- fmtrune(f, '"');
- for(t = s; *t; t += w){
- w = chartorune(&r, t);
- if(needtoquote(r) == Qbackslash)
- fmtrune(f, '\\');
- fmtrune(f, r);
- }
- return fmtrune(f, '"');
- }
- int
- rfc2047fmt(Fmt *fmt)
- {
- char *s, *p;
- s = va_arg(fmt->args, char*);
- if(s == nil)
- return fmtstrcpy(fmt, "");
- for(p=s; *p; p++)
- if((uchar)*p >= 0x80)
- goto hard;
- return fmtstrcpy(fmt, s);
- hard:
- fmtprint(fmt, "=?utf-8?q?");
- for(p = s; *p; p++){
- if(*p == ' ')
- fmtrune(fmt, '_');
- else if(*p == '_' || *p == '\t' || *p == '=' || *p == '?' ||
- (uchar)*p >= 0x80)
- fmtprint(fmt, "=%.2uX", (uchar)*p);
- else
- fmtrune(fmt, (uchar)*p);
- }
- fmtprint(fmt, "?=");
- return 0;
- }
- char*
- mksubject(char *line)
- {
- char *p, *q;
- static char buf[1024];
- p = strchr(line, ':') + 1;
- while(*p == ' ')
- p++;
- for(q = p; *q; q++)
- if((uchar)*q >= 0x80)
- goto hard;
- return line;
- hard:
- snprint(buf, sizeof buf, "Subject: %U", p);
- return buf;
- }
|