📄 marshal.c
字号:
#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;Attach* mkattach(char*, char*, int);int readheaders(Biobuf*, int*, String**, Addr**, int);void body(Biobuf*, Biobuf*, int);char* mkboundary(void);int printdate(Biobuf*);int printfrom(Biobuf*);int printto(Biobuf*, Addr*);int printcc(Biobuf*, Addr*);int printsubject(Biobuf*, char*);int printinreplyto(Biobuf*, char*);int sendmail(Addr*, Addr*, int*, char*);void attachment(Attach*, Biobuf*);int cistrncmp(char*, char*, int);int cistrcmp(char*, char*);char* waitforsubprocs(void);int enc64(char*, int, uchar*, int);Addr* expand(int, char**);Alias* readaliases(void);Addr* expandline(String**, Addr*);void Bdrain(Biobuf*);void freeaddr(Addr *);int pgpopts(char*);int pgpfilter(int*, int, int);void readmimetypes(void);char* estrdup(char*);void* emalloc(int);void* erealloc(void*, int);void freeaddr(Addr*);void freeaddrs(Addr*);void freealias(Alias*);void freealiases(Alias*);int doublequote(Fmt*);int rfc2047fmt(Fmt*);char* mksubject(char*);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*voidusage(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");}voidfatal(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);}voidmain(int argc, char **argv){ Attach *first, **l, *a; char *subject, *type, *boundary; int flags, fd; Biobuf in, out, *b; Addr *to; Addr *cc; String *file, *hdrstring; int noinput, headersrv; int ccargc; char *ccargv[32]; noinput = 0; subject = nil; first = nil; l = &first; type = nil; hdrstring = nil; ccargc = 0; quotefmtinstall(); fmtinstall('Z', doublequote); fmtinstall('U', rfc2047fmt); ARGBEGIN{ case 't': type = ARGF(); if(type == nil) usage(); break; case 'a': flags = 0; goto aflag; case 'A': flags = 1; aflag: a = mkattach(ARGF(), 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] = ARGF(); if(ccargv[ccargc] == nil) usage(); ccargc++; break; case 'R': replymsg = ARGF(); break; case 's': subject = ARGF(); break; case 'F': Fflag = 1; // file message break; case 'r': rflag = 1; // for sendmail break; case 'd': dflag = 1; // for sendmail break; case '#': lbflag = 1; // for sendmail break; case 'x': xflag = 1; // for sendmail break; case 'n': // no standard input nflag = 1; break; case '8': // read recipients from rfc822 header eightflag = 1; break; case 'p': // pgp flag: encrypt, sign, or both if(pgpopts(ARGF()) < 0) sysfatal("bad pgp options"); 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 = nil; 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){ if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring)) fatal("write error"); s_free(hdrstring); hdrstring = nil; // read user's standard headers file = s_new(); mboxpath("headers", user, file, 0); b = Bopen(s_to_c(file), OREAD); if(b != nil){ switch(readheaders(b, &flags, &hdrstring, nil, 0)){ case Error: // error fatal("reading"); } Bterm(b); if(Bwrite(&out, s_to_c(hdrstring), s_len(hdrstring)) != s_len(hdrstring)) fatal("write error"); s_free(hdrstring); hdrstring = nil; } } // 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 stringintpgpopts(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.intreadheaders(Biobuf *in, int *fp, String **sp, Addr **top, int strict){ Addr *to; String *s, *sline; char *p; int i, seen, hdrtype; 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 user's that 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 newlinevoidbody(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.//voidbody64(Biobuf *in, Biobuf *out){ uchar buf[3*18*54]; char obuf[3*18*54*2]; int m, n; Bprint(out, "\n"); for(;;){ n = Bread(in, buf, sizeof(buf)); if(n < 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -