12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255 |
- #include "common.h"
- #include <ctype.h>
- #include <plumb.h>
- typedef struct Message Message;
- typedef struct Ctype Ctype;
- typedef struct Cmd Cmd;
- char root[Pathlen];
- char mbname[Elemlen];
- int rootlen;
- int didopen;
- char *user;
- char wd[2048];
- String *mbpath;
- int natural;
- int interrupted;
- struct Message {
- Message *next;
- Message *prev;
- Message *cmd;
- Message *child;
- Message *parent;
- String *path;
- int id;
- int len;
- int fileno; // number of directory
- String *info;
- char *from;
- char *to;
- char *cc;
- char *replyto;
- char *date;
- char *subject;
- char *type;
- char *disposition;
- char *filename;
- char deleted;
- char stored;
- };
- Message top;
- struct Ctype {
- char *type;
- char *ext;
- int display;
- char *plumbdest;
- };
- Ctype ctype[] = {
- { "text/plain", "txt", 1, 0 },
- { "text/html", "htm", 1, 0 },
- { "text/html", "html", 1, 0 },
- { "text/tab-separated-values", "tsv", 1, 0 },
- { "text/richtext", "rtx", 1, 0 },
- { "text", "txt", 1, 0 },
- { "message/rfc822", "msg", 0, 0 },
- { "image/jpeg", "jpg", 0, "image" },
- { "image/gif", "gif", 0, "image" },
- { "application/octet-stream", "bin", 0, 0 },
- { "application/pdf", "pdf", 0, "" },
- { "application/postscript", "ps", 0, "" },
- { "application/", 0, 0, 0 },
- { "image/", 0, 0, 0 },
- { "multipart/", "mul", 0, 0 },
- { "", 0, 0, 0 },
- };
- Message* acmd(Cmd*, Message*);
- Message* bcmd(Cmd*, Message*);
- Message* dcmd(Cmd*, Message*);
- Message* eqcmd(Cmd*, Message*);
- Message* hcmd(Cmd*, Message*);
- Message* Hcmd(Cmd*, Message*);
- Message* helpcmd(Cmd*, Message*);
- Message* icmd(Cmd*, Message*);
- Message* pcmd(Cmd*, Message*);
- Message* qcmd(Cmd*, Message*);
- Message* rcmd(Cmd*, Message*);
- Message* scmd(Cmd*, Message*);
- Message* ucmd(Cmd*, Message*);
- Message* wcmd(Cmd*, Message*);
- Message* xcmd(Cmd*, Message*);
- Message* pipecmd(Cmd*, Message*);
- Message* bangcmd(Cmd*, Message*);
- Message* Pcmd(Cmd*, Message*);
- Message* mcmd(Cmd*, Message*);
- Message* fcmd(Cmd*, Message*);
- Message* quotecmd(Cmd*, Message*);
- struct {
- char *cmd;
- int args;
- Message* (*f)(Cmd*, Message*);
- char *help;
- } cmdtab[] = {
- { "a", 1, acmd, "a reply to sender and recipients" },
- { "A", 1, acmd, "A reply to sender and recipients with copy" },
- { "b", 0, bcmd, "b print the next 10 headers" },
- { "d", 0, dcmd, "d mark for deletion" },
- { "f", 0, fcmd, "f file message by from address" },
- { "h", 0, hcmd, "h print elided message summary (,h for all)" },
- { "help", 0, helpcmd, "help print this info" },
- { "H", 0, Hcmd, "H print message's MIME structure " },
- { "i", 0, icmd, "i incorporate new mail" },
- { "m", 1, mcmd, "m addr forward mail" },
- { "M", 1, mcmd, "M addr forward mail with message" },
- { "p", 0, pcmd, "p print the processed message" },
- { "P", 0, Pcmd, "P print the raw message" },
- { "\"", 0, quotecmd, "\" print a quoted version of msg" },
- { "q", 0, qcmd, "q exit and remove all deleted mail" },
- { "r", 1, rcmd, "r [addr] reply to sender plus any addrs specified" },
- { "rf", 1, rcmd, "rf [addr]file message and reply" },
- { "R", 1, rcmd, "R [addr] reply including copy of message" },
- { "Rf", 1, rcmd, "Rf [addr]file message and reply with copy" },
- { "s", 1, scmd, "s file append raw message to file" },
- { "u", 0, ucmd, "u remove deletion mark" },
- { "w", 1, wcmd, "w file store message contents as file" },
- { "x", 0, xcmd, "x exit with mailbox unchanged" },
- { "=", 1, eqcmd, "= print current message number" },
- { "|", 1, pipecmd, "|cmd pipe raw message to a command" },
- { "!", 1, bangcmd, "!cmd run a command" },
- { nil, 0, nil, nil },
- };
- enum
- {
- NARG= 32,
- };
- struct Cmd {
- Message *msgs;
- Message *(*f)(Cmd*, Message*);
- int an;
- char *av[NARG];
- int delete;
- };
- Biobuf out;
- int startedfs;
- int reverse;
- String* file2string(String*, char*);
- int dir2message(Message*, int);
- int filelen(String*, char*);
- String* extendpath(String*, char*);
- void snprintheader(char*, int, Message*);
- void cracktime(char*, char*, int);
- int cistrncmp(char*, char*, int);
- int cistrcmp(char*, char*);
- Reprog* parsesearch(char**);
- char* parseaddr(char**, Message*, Message*, Message*, Message**);
- char* parsecmd(char*, Cmd*, Message*, Message*);
- char* readline(char*, char*, int);
- void messagecount(Message*);
- void system(char*, char**, int);
- void mkid(String*, Message*);
- int switchmb(char*, char*);
- void closemb(void);
- int lineize(char*, char**, int);
- int rawsearch(Message*, Reprog*);
- Message* dosingleton(Message*, char*);
- String* rooted(String*);
- int plumb(Message*, Ctype*);
- String* addrecolon(char*);
- void exitfs(char*);
- void
- usage(void)
- {
- fprint(2, "usage: %s [-cr] [-f mboxdir] [-s singleton]\n", argv0);
- exits("usage");
- }
- void
- catchnote(void*, char *note)
- {
- if(strstr(note, "interrupt") != nil){
- interrupted = 1;
- noted(NCONT);
- }
- noted(NDFLT);
- }
- void
- main(int argc, char **argv)
- {
- Message *cur, *m, *x;
- char cmdline[4*1024];
- Cmd cmd;
- char *err;
- int n, cflag;
- char *av[4];
- String *prompt;
- char *file, *singleton;
- Binit(&out, 1, OWRITE);
- file = nil;
- singleton = nil;
- reverse = 1;
- cflag = 0;
- ARGBEGIN {
- case 'c':
- cflag = 1;
- break;
- case 'f':
- file = EARGF(usage());
- break;
- case 's':
- singleton = EARGF(usage());
- break;
- case 'r':
- reverse = 0;
- break;
- case 'n':
- natural = 1;
- reverse = 0;
- break;
- default:
- usage();
- break;
- } ARGEND;
- user = getlog();
- if(user == nil || *user == 0)
- sysfatal("can't read user name");
- if(cflag){
- if(argc > 0)
- creatembox(user, argv[0]);
- else
- creatembox(user, nil);
- exits(0);
- }
- if(argc)
- usage();
- if(access("/mail/fs/ctl", 0) < 0){
- startedfs = 1;
- av[0] = "fs";
- av[1] = "-p";
- av[2] = 0;
- system("/bin/upas/fs", av, -1);
- }
- switchmb(file, singleton);
- top.path = s_copy(root);
- if(singleton != nil){
- cur = dosingleton(&top, singleton);
- if(cur == nil){
- Bprint(&out, "no message\n");
- exitfs(0);
- }
- pcmd(nil, cur);
- } else {
- cur = ⊤
- n = dir2message(&top, reverse);
- if(n < 0)
- sysfatal("can't read %s", s_to_c(top.path));
- Bprint(&out, "%d messages\n", n);
- }
- notify(catchnote);
- prompt = s_new();
- for(;;){
- s_reset(prompt);
- if(cur == &top)
- s_append(prompt, ": ");
- else {
- mkid(prompt, cur);
- s_append(prompt, ": ");
- }
- if(readline(s_to_c(prompt), cmdline, sizeof(cmdline)) == nil)
- break;
- err = parsecmd(cmdline, &cmd, top.child, cur);
- if(err != nil){
- Bprint(&out, "!%s\n", err);
- continue;
- }
- if(singleton != nil && cmd.f == icmd){
- Bprint(&out, "!illegal command\n");
- continue;
- }
- interrupted = 0;
- if(cmd.msgs == nil || cmd.msgs == &top){
- x = (*cmd.f)(&cmd, &top);
- if(x != nil)
- cur = x;
- } else for(m = cmd.msgs; m != nil; m = m->cmd){
- x = m;
- if(cmd.delete){
- dcmd(&cmd, x);
- // dp acts differently than all other commands
- // since its an old lesk idiom that people love.
- // it deletes the current message, moves the current
- // pointer ahead one and prints.
- if(cmd.f == pcmd){
- if(x->next == nil){
- Bprint(&out, "!address\n");
- cur = x;
- break;
- } else
- x = x->next;
- }
- }
- x = (*cmd.f)(&cmd, x);
- if(x != nil)
- cur = x;
- if(interrupted)
- break;
- if(singleton != nil && (cmd.delete || cmd.f == dcmd))
- qcmd(nil, nil);
- }
- }
- qcmd(nil, nil);
- }
- //
- // read the message info
- //
- Message*
- file2message(Message *parent, char *name)
- {
- Message *m;
- String *path;
- char *f[10];
- m = mallocz(sizeof(Message), 1);
- if(m == nil)
- return nil;
- m->path = path = extendpath(parent->path, name);
- m->fileno = atoi(name);
- m->info = file2string(path, "info");
- lineize(s_to_c(m->info), f, nelem(f));
- m->from = f[0];
- m->to = f[1];
- m->cc = f[2];
- m->replyto = f[3];
- m->date = f[4];
- m->subject = f[5];
- m->type = f[6];
- m->disposition = f[7];
- m->filename = f[8];
- m->len = filelen(path, "raw");
- if(strstr(m->type, "multipart") != nil || strcmp(m->type, "message/rfc822") == 0)
- dir2message(m, 0);
- m->parent = parent;
- return m;
- }
- //
- // read a directory into a list of messages
- //
- int
- dir2message(Message *parent, int reverse)
- {
- int i, n, fd, highest, newmsgs;
- Dir *d;
- Message *first, *last, *m;
- fd = open(s_to_c(parent->path), OREAD);
- if(fd < 0)
- return -1;
- // count current entries
- first = parent->child;
- highest = newmsgs = 0;
- for(last = parent->child; last != nil && last->next != nil; last = last->next)
- if(last->fileno > highest)
- highest = last->fileno;
- if(last != nil)
- if(last->fileno > highest)
- highest = last->fileno;
- n = dirreadall(fd, &d);
- for(i = 0; i < n; i++){
- if((d[i].qid.type & QTDIR) == 0)
- continue;
- if(atoi(d[i].name) <= highest)
- continue;
- m = file2message(parent, d[i].name);
- if(m == nil)
- break;
- newmsgs++;
- if(reverse){
- m->next = first;
- if(first != nil)
- first->prev = m;
- first = m;
- } else {
- if(first == nil)
- first = m;
- else
- last->next = m;
- m->prev = last;
- last = m;
- }
- }
- free(d);
- close(fd);
- parent->child = first;
- // renumber
- i = 1;
- for(m = first; m != nil; m = m->next)
- m->id = natural ? m->fileno : i++;
- return newmsgs;
- }
- //
- // point directly to a message
- //
- Message*
- dosingleton(Message *parent, char *path)
- {
- char *p, *np;
- Message *m;
- // walk down to message and read it
- if(strlen(path) < rootlen)
- return nil;
- if(path[rootlen] != '/')
- return nil;
- p = path+rootlen+1;
- np = strchr(p, '/');
- if(np != nil)
- *np = 0;
- m = file2message(parent, p);
- if(m == nil)
- return nil;
- parent->child = m;
- m->id = 1;
- // walk down to requested component
- while(np != nil){
- *np = '/';
- np = strchr(np+1, '/');
- if(np != nil)
- *np = 0;
- for(m = m->child; m != nil; m = m->next)
- if(strcmp(path, s_to_c(m->path)) == 0)
- return m;
- if(m == nil)
- return nil;
- }
- return m;
- }
- //
- // read a file into a string
- //
- String*
- file2string(String *dir, char *file)
- {
- String *s;
- int fd, n, m;
- s = extendpath(dir, file);
- fd = open(s_to_c(s), OREAD);
- s_grow(s, 512); /* avoid multiple reads on info files */
- s_reset(s);
- if(fd < 0)
- return s;
- for(;;){
- n = s->end - s->ptr;
- if(n == 0){
- s_grow(s, 128);
- continue;
- }
- m = read(fd, s->ptr, n);
- if(m <= 0)
- break;
- s->ptr += m;
- if(m < n)
- break;
- }
- s_terminate(s);
- close(fd);
- return s;
- }
- //
- // get the length of a file
- //
- int
- filelen(String *dir, char *file)
- {
- String *path;
- Dir *d;
- int rv;
- path = extendpath(dir, file);
- d = dirstat(s_to_c(path));
- if(d == nil){
- s_free(path);
- return -1;
- }
- s_free(path);
- rv = d->length;
- free(d);
- return rv;
- }
- //
- // walk the path name an element
- //
- String*
- extendpath(String *dir, char *name)
- {
- String *path;
- if(strcmp(s_to_c(dir), ".") == 0)
- path = s_new();
- else {
- path = s_copy(s_to_c(dir));
- s_append(path, "/");
- }
- s_append(path, name);
- return path;
- }
- 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;
- }
- char*
- nosecs(char *t)
- {
- char *p;
- p = strchr(t, ':');
- if(p == nil)
- return t;
- p = strchr(p+1, ':');
- if(p != nil)
- *p = 0;
- return t;
- }
- char *months[12] =
- {
- "jan", "feb", "mar", "apr", "may", "jun",
- "jul", "aug", "sep", "oct", "nov", "dec"
- };
- int
- month(char *m)
- {
- int i;
- for(i = 0; i < 12; i++)
- if(cistrcmp(m, months[i]) == 0)
- return i+1;
- return 1;
- }
- enum
- {
- Yearsecs= 365*24*60*60
- };
- void
- cracktime(char *d, char *out, int len)
- {
- char in[64];
- char *f[6];
- int n;
- Tm tm;
- long now, then;
- char *dtime;
- *out = 0;
- if(d == nil)
- return;
- strncpy(in, d, sizeof(in));
- in[sizeof(in)-1] = 0;
- n = getfields(in, f, 6, 1, " \t\r\n");
- if(n != 6){
- // unknown style
- snprint(out, 16, "%10.10s", d);
- return;
- }
- now = time(0);
- memset(&tm, 0, sizeof tm);
- if(strchr(f[0], ',') != nil && strchr(f[4], ':') != nil){
- // 822 style
- tm.year = atoi(f[3])-1900;
- tm.mon = month(f[2]);
- tm.mday = atoi(f[1]);
- dtime = nosecs(f[4]);
- then = tm2sec(&tm);
- } else if(strchr(f[3], ':') != nil){
- // unix style
- tm.year = atoi(f[5])-1900;
- tm.mon = month(f[1]);
- tm.mday = atoi(f[2]);
- dtime = nosecs(f[3]);
- then = tm2sec(&tm);
- } else {
- then = now;
- tm = *localtime(now);
- dtime = "";
- }
- if(now - then < Yearsecs/2)
- snprint(out, len, "%2d/%2.2d %s", tm.mon, tm.mday, dtime);
- else
- snprint(out, len, "%2d/%2.2d %4d", tm.mon, tm.mday, tm.year+1900);
- }
- Ctype*
- findctype(char *t)
- {
- Ctype *cp;
- for(cp = ctype; ; cp++)
- if(strncmp(cp->type, t, strlen(cp->type)) == 0)
- break;
- return cp;
- }
- void
- mkid(String *s, Message *m)
- {
- char buf[32];
- if(m->parent != &top){
- mkid(s, m->parent);
- s_append(s, ".");
- }
- sprint(buf, "%d", m->id);
- s_append(s, buf);
- }
- void
- snprintheader(char *buf, int len, Message *m)
- {
- char timebuf[32];
- String *id;
- // create id
- id = s_new();
- mkid(id, m);
- if(*m->from == 0){
- // no from
- snprint(buf, len, "%-3s %s %5d %s",
- s_to_c(id),
- m->type,
- m->len,
- m->filename);
- } else if(*m->subject){
- cracktime(m->date, timebuf, sizeof(timebuf));
- snprint(buf, len, "%-3s %c%c%c %5d %11.11s %s %s",
- s_to_c(id),
- m->child ? 'H' : ' ',
- m->deleted ? 'd' : ' ',
- m->stored ? 's' : ' ',
- m->len,
- timebuf,
- m->from,
- m->subject);
- } else {
- cracktime(m->date, timebuf, sizeof(timebuf));
- snprint(buf, len, "%-3s %c%c%c %5d %11.11s %s",
- s_to_c(id),
- m->child ? 'H' : ' ',
- m->deleted ? 'd' : ' ',
- m->stored ? 's' : ' ',
- m->len,
- timebuf,
- m->from);
- }
- s_free(id);
- }
- char *spaces = " ";
- void
- snprintHeader(char *buf, int len, int indent, Message *m)
- {
- String *id;
- char typeid[64];
- char *p, *e;
- // create id
- id = s_new();
- mkid(id, m);
- e = buf + len;
- snprint(typeid, sizeof typeid, "%s %s", s_to_c(id), m->type);
- if(indent < 6)
- p = seprint(buf, e, "%-32s %-6d ", typeid, m->len);
- else
- p = seprint(buf, e, "%-64s %-6d ", typeid, m->len);
- if(m->filename && *m->filename)
- p = seprint(p, e, "(file,%s)", m->filename);
- if(m->from && *m->from)
- p = seprint(p, e, "(from,%s)", m->from);
- if(m->subject && *m->subject)
- seprint(p, e, "(subj,%s)", m->subject);
- s_free(id);
- }
- char sstring[256];
- // cmd := range cmd ' ' arg-list ;
- // range := address
- // | address ',' address
- // | 'g' search ;
- // address := msgno
- // | search ;
- // msgno := number
- // | number '/' msgno ;
- // search := '/' string '/'
- // | '%' string '%' ;
- //
- Reprog*
- parsesearch(char **pp)
- {
- char *p, *np;
- int c, n;
- p = *pp;
- c = *p++;
- np = strchr(p, c);
- if(np != nil){
- *np++ = 0;
- *pp = np;
- } else {
- n = strlen(p);
- *pp = p + n;
- }
- if(*p == 0)
- p = sstring;
- else{
- strncpy(sstring, p, sizeof(sstring));
- sstring[sizeof(sstring)-1] = 0;
- }
- return regcomp(p);
- }
- char*
- parseaddr(char **pp, Message *first, Message *cur, Message *unspec, Message **mp)
- {
- int n;
- Message *m;
- char *p;
- Reprog *prog;
- int c, sign;
- char buf[256];
- *mp = nil;
- p = *pp;
- if(*p == '+'){
- sign = 1;
- p++;
- *pp = p;
- } else if(*p == '-'){
- sign = -1;
- p++;
- *pp = p;
- } else
- sign = 0;
- switch(*p){
- default:
- if(sign){
- n = 1;
- goto number;
- }
- *mp = unspec;
- break;
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
- n = strtoul(p, pp, 10);
- if(n == 0){
- if(sign)
- *mp = cur;
- else
- *mp = ⊤
- break;
- }
- number:
- m = nil;
- switch(sign){
- case 0:
- for(m = first; m != nil; m = m->next)
- if(m->id == n)
- break;
- break;
- case -1:
- if(cur != &top)
- for(m = cur; m != nil && n > 0; n--)
- m = m->prev;
- break;
- case 1:
- if(cur == &top){
- n--;
- cur = first;
- }
- for(m = cur; m != nil && n > 0; n--)
- m = m->next;
- break;
- }
- if(m == nil)
- return "address";
- *mp = m;
- break;
- case '%':
- case '/':
- case '?':
- c = *p;
- prog = parsesearch(pp);
- if(prog == nil)
- return "badly formed regular expression";
- m = nil;
- switch(c){
- case '%':
- for(m = cur == &top ? first : cur->next; m != nil; m = m->next){
- if(rawsearch(m, prog))
- break;
- }
- break;
- case '/':
- for(m = cur == &top ? first : cur->next; m != nil; m = m->next){
- snprintheader(buf, sizeof(buf), m);
- if(regexec(prog, buf, nil, 0))
- break;
- }
- break;
- case '?':
- for(m = cur == &top ? nil : cur->prev; m != nil; m = m->prev){
- snprintheader(buf, sizeof(buf), m);
- if(regexec(prog, buf, nil, 0))
- break;
- }
- break;
- }
- if(m == nil)
- return "search";
- *mp = m;
- free(prog);
- break;
- case '$':
- for(m = first; m != nil && m->next != nil; m = m->next)
- ;
- *mp = m;
- *pp = p+1;
- break;
- case '.':
- *mp = cur;
- *pp = p+1;
- break;
- case ',':
- *mp = first;
- *pp = p;
- break;
- }
- if(*mp != nil && **pp == '.'){
- (*pp)++;
- if((*mp)->child == nil)
- return "no sub parts";
- return parseaddr(pp, (*mp)->child, (*mp)->child, (*mp)->child, mp);
- }
- if(**pp == '+' || **pp == '-' || **pp == '/' || **pp == '%')
- return parseaddr(pp, first, *mp, *mp, mp);
- return nil;
- }
- //
- // search a message for a regular expression match
- //
- int
- rawsearch(Message *m, Reprog *prog)
- {
- char buf[4096+1];
- int i, fd, rv;
- String *path;
- path = extendpath(m->path, "raw");
- fd = open(s_to_c(path), OREAD);
- if(fd < 0)
- return 0;
- // march through raw message 4096 bytes at a time
- // with a 128 byte overlap to chain the re search.
- rv = 0;
- for(;;){
- i = read(fd, buf, sizeof(buf)-1);
- if(i <= 0)
- break;
- buf[i] = 0;
- if(regexec(prog, buf, nil, 0)){
- rv = 1;
- break;
- }
- if(i < sizeof(buf)-1)
- break;
- if(seek(fd, -128LL, 1) < 0)
- break;
- }
- close(fd);
- s_free(path);
- return rv;
- }
- char*
- parsecmd(char *p, Cmd *cmd, Message *first, Message *cur)
- {
- Reprog *prog;
- Message *m, *s, *e, **l, *last;
- char buf[256];
- char *err;
- int i, c;
- static char errbuf[Errlen];
- cmd->delete = 0;
- l = &cmd->msgs;
- *l = nil;
- // eat white space
- while(*p == ' ')
- p++;
- // null command is a special case (advance and print)
- if(*p == 0){
- if(cur == &top){
- // special case
- m = first;
- } else {
- // walk to the next message even if we have to go up
- m = cur->next;
- while(m == nil && cur->parent != nil){
- cur = cur->parent;
- m = cur->next;
- }
- }
- if(m == nil)
- return "address";
- *l = m;
- m->cmd = nil;
- cmd->an = 0;
- cmd->f = pcmd;
- return nil;
- }
- // global search ?
- if(*p == 'g'){
- p++;
- // no search string means all messages
- if(*p != '/' && *p != '%'){
- for(m = first; m != nil; m = m->next){
- *l = m;
- l = &m->cmd;
- *l = nil;
- }
- } else {
- // mark all messages matching this search string
- c = *p;
- prog = parsesearch(&p);
- if(prog == nil)
- return "badly formed regular expression";
- if(c == '%'){
- for(m = first; m != nil; m = m->next){
- if(rawsearch(m, prog)){
- *l = m;
- l = &m->cmd;
- *l = nil;
- }
- }
- } else {
- for(m = first; m != nil; m = m->next){
- snprintheader(buf, sizeof(buf), m);
- if(regexec(prog, buf, nil, 0)){
- *l = m;
- l = &m->cmd;
- *l = nil;
- }
- }
- }
- free(prog);
- }
- } else {
-
- // parse an address
- s = e = nil;
- err = parseaddr(&p, first, cur, cur, &s);
- if(err != nil)
- return err;
- if(*p == ','){
- // this is an address range
- if(s == &top)
- s = first;
- p++;
- for(last = s; last != nil && last->next != nil; last = last->next)
- ;
- err = parseaddr(&p, first, cur, last, &e);
- if(err != nil)
- return err;
-
- // select all messages in the range
- for(; s != nil; s = s->next){
- *l = s;
- l = &s->cmd;
- *l = nil;
- if(s == e)
- break;
- }
- if(s == nil)
- return "null address range";
- } else {
- // single address
- if(s != &top){
- *l = s;
- s->cmd = nil;
- }
- }
- }
- cmd->an = getfields(p, cmd->av, nelem(cmd->av) - 1, 1, " \t\r\n");
- if(cmd->an == 0 || *cmd->av[0] == 0)
- cmd->f = pcmd;
- else {
- // hack to avoid space after '|' or '!'
- if((*p == '!' || *p == '|') && *(p+1) != 0){
- for(i = cmd->an; i > 0 ; i--)
- cmd->av[i] = cmd->av[i-1];
- cmd->av[0] = *p == '!' ? "!" : "|";
- cmd->av[1]++;
- cmd->an++;
- }
- // hack to allow all messages to start with 'd'
- if(*(cmd->av[0]) == 'd' && *(cmd->av[0]+1) != 0){
- cmd->delete = 1;
- cmd->av[0]++;
- }
- // search command table
- for(i = 0; cmdtab[i].cmd != nil; i++)
- if(strcmp(cmd->av[0], cmdtab[i].cmd) == 0)
- break;
- if(cmdtab[i].cmd == nil)
- return "illegal command";
- if(cmdtab[i].args == 0 && cmd->an > 1){
- snprint(errbuf, sizeof(errbuf), "%s doesn't take an argument", cmdtab[i].cmd);
- return errbuf;
- }
- cmd->f = cmdtab[i].f;
- }
- return nil;
- }
- // inefficient read from standard input
- char*
- readline(char *prompt, char *line, int len)
- {
- char *p, *e;
- int n;
- retry:
- interrupted = 0;
- Bprint(&out, "%s", prompt);
- Bflush(&out);
- e = line + len;
- for(p = line; p < e; p++){
- n = read(0, p, 1);
- if(n < 0){
- if(interrupted)
- goto retry;
- return nil;
- }
- if(n == 0)
- return nil;
- if(*p == '\n')
- break;
- }
- *p = 0;
- return line;
- }
- void
- messagecount(Message *m)
- {
- int i;
- i = 0;
- for(; m != nil; m = m->next)
- i++;
- Bprint(&out, "%d messages\n", i);
- }
- Message*
- aichcmd(Message *m, int indent)
- {
- char hdr[256];
- if(m == &top)
- return nil;
- snprintHeader(hdr, sizeof(hdr), indent, m);
- Bprint(&out, "%s\n", hdr);
- for(m = m->child; m != nil; m = m->next)
- aichcmd(m, indent+1);
- return nil;
- }
- Message*
- Hcmd(Cmd*, Message *m)
- {
- if(m == &top)
- return nil;
- aichcmd(m, 0);
- return nil;
- }
- Message*
- hcmd(Cmd*, Message *m)
- {
- char hdr[256];
- if(m == &top)
- return nil;
- snprintheader(hdr, sizeof(hdr), m);
- Bprint(&out, "%s\n", hdr);
- return nil;
- }
- Message*
- bcmd(Cmd*, Message *m)
- {
- int i;
- Message *om = m;
- if(m == &top)
- m = top.child;
- for(i = 0; i < 10 && m != nil; i++){
- hcmd(nil, m);
- om = m;
- m = m->next;
- }
- return om;
- }
- Message*
- ncmd(Cmd*, Message *m)
- {
- if(m == &top)
- return m->child;
- return m->next;
- }
- int
- printpart(String *s, char *part)
- {
- char buf[4096];
- int n, fd, tot;
- String *path;
- path = extendpath(s, part);
- fd = open(s_to_c(path), OREAD);
- s_free(path);
- if(fd < 0){
- fprint(2, "!message dissappeared\n");
- return 0;
- }
- tot = 0;
- while((n = read(fd, buf, sizeof(buf))) > 0){
- if(interrupted)
- break;
- if(Bwrite(&out, buf, n) <= 0)
- break;
- tot += n;
- }
- close(fd);
- return tot;
- }
- int
- printhtml(Message *m)
- {
- Cmd c;
- c.an = 3;
- c.av[1] = "/bin/htmlfmt";
- c.av[2] = "-cutf-8";
- Bprint(&out, "!%s\n", c.av[1]);
- Bflush(&out);
- pipecmd(&c, m);
- return 0;
- }
- Message*
- Pcmd(Cmd*, Message *m)
- {
- if(m == &top)
- return ⊤
- if(m->parent == &top)
- printpart(m->path, "unixheader");
- printpart(m->path, "raw");
- return m;
- }
- void
- compress(char *p)
- {
- char *np;
- int last;
- last = ' ';
- for(np = p; *p; p++){
- if(*p != ' ' || last != ' '){
- last = *p;
- *np++ = last;
- }
- }
- *np = 0;
- }
- Message*
- pcmd(Cmd*, Message *m)
- {
- Message *nm;
- Ctype *cp;
- String *s;
- char buf[128];
- if(m == &top)
- return ⊤
- if(m->parent == &top)
- printpart(m->path, "unixheader");
- if(printpart(m->path, "header") > 0)
- Bprint(&out, "\n");
- cp = findctype(m->type);
- if(cp->display){
- if(strcmp(m->type, "text/html") == 0)
- printhtml(m);
- else
- printpart(m->path, "body");
- } else if(strcmp(m->type, "multipart/alternative") == 0){
- for(nm = m->child; nm != nil; nm = nm->next){
- cp = findctype(nm->type);
- if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0)
- break;
- }
- if(nm == nil)
- for(nm = m->child; nm != nil; nm = nm->next){
- cp = findctype(nm->type);
- if(cp->display)
- break;
- }
- if(nm != nil)
- pcmd(nil, nm);
- else
- hcmd(nil, m);
- } else if(strncmp(m->type, "multipart/", 10) == 0){
- nm = m->child;
- if(nm != nil){
- // always print first part
- pcmd(nil, nm);
- for(nm = nm->next; nm != nil; nm = nm->next){
- s = rooted(s_clone(nm->path));
- cp = findctype(nm->type);
- snprintHeader(buf, sizeof buf, -1, nm);
- compress(buf);
- if(strcmp(nm->disposition, "inline") == 0){
- if(cp->ext != nil)
- Bprint(&out, "\n--- %s %s/body.%s\n\n",
- buf, s_to_c(s), cp->ext);
- else
- Bprint(&out, "\n--- %s %s/body\n\n",
- buf, s_to_c(s));
- pcmd(nil, nm);
- } else {
- if(cp->ext != nil)
- Bprint(&out, "\n!--- %s %s/body.%s\n",
- buf, s_to_c(s), cp->ext);
- else
- Bprint(&out, "\n!--- %s %s/body\n",
- buf, s_to_c(s));
- }
- s_free(s);
- }
- } else {
- hcmd(nil, m);
- }
- } else if(strcmp(m->type, "message/rfc822") == 0){
- pcmd(nil, m->child);
- } else if(plumb(m, cp) >= 0)
- Bprint(&out, "\n!--- using plumber to display message of type %s\n", m->type);
- else
- Bprint(&out, "\n!--- cannot display messages of type %s\n", m->type);
-
- return m;
- }
- void
- printpartindented(String *s, char *part, char *indent)
- {
- char *p;
- String *path;
- Biobuf *b;
- path = extendpath(s, part);
- b = Bopen(s_to_c(path), OREAD);
- s_free(path);
- if(b == nil){
- fprint(2, "!message dissappeared\n");
- return;
- }
- while((p = Brdline(b, '\n')) != nil){
- if(interrupted)
- break;
- p[Blinelen(b)-1] = 0;
- if(Bprint(&out, "%s%s\n", indent, p) <= 0)
- break;
- }
- Bprint(&out, "\n");
- Bterm(b);
- }
- Message*
- quotecmd(Cmd*, Message *m)
- {
- Message *nm;
- Ctype *cp;
- if(m == &top)
- return ⊤
- Bprint(&out, "\n");
- if(m->from != nil && *m->from)
- Bprint(&out, "On %s, %s wrote:\n", m->date, m->from);
- cp = findctype(m->type);
- if(cp->display){
- printpartindented(m->path, "body", "> ");
- } else if(strcmp(m->type, "multipart/alternative") == 0){
- for(nm = m->child; nm != nil; nm = nm->next){
- cp = findctype(nm->type);
- if(cp->ext != nil && strncmp(cp->ext, "txt", 3) == 0)
- break;
- }
- if(nm == nil)
- for(nm = m->child; nm != nil; nm = nm->next){
- cp = findctype(nm->type);
- if(cp->display)
- break;
- }
- if(nm != nil)
- quotecmd(nil, nm);
- } else if(strncmp(m->type, "multipart/", 10) == 0){
- nm = m->child;
- if(nm != nil){
- cp = findctype(nm->type);
- if(cp->display || strncmp(m->type, "multipart/", 10) == 0)
- quotecmd(nil, nm);
- }
- }
- return m;
- }
- Message*
- qcmd(Cmd*, Message*)
- {
- Message *m;
- char buf[1024], *p, *e, *msg;
- int deld, n, fd;
- deld = 0;
- // really delete messages
- fd = open("/mail/fs/ctl", ORDWR);
- if(fd < 0){
- fprint(2, "!can't delete mail, opening /mail/fs/ctl: %r\n");
- exitfs(0);
- }
- e = &buf[sizeof(buf)];
- p = seprint(buf, e, "delete %s", mbname);
- n = 0;
- for(m = top.child; m != nil; m = m->next)
- if(m->deleted){
- deld++;
- msg = strrchr(s_to_c(m->path), '/');
- if(msg == nil)
- msg = s_to_c(m->path);
- else
- msg++;
- if(e-p < 10){
- write(fd, buf, p-buf);
- n = 0;
- p = seprint(buf, e, "delete %s", mbname);
- }
- p = seprint(p, e, " %s", msg);
- n++;
- }
- if(n)
- write(fd, buf, p-buf);
- close(fd);
- if(didopen)
- closemb();
- switch(deld){
- case 0:
- break;
- case 1:
- Bprint(&out, "!1 message deleted\n");
- break;
- default:
- Bprint(&out, "!%d messages deleted\n", deld);
- break;
- }
- Bflush(&out);
- exitfs(0);
- return nil; // not reached
- }
- Message*
- xcmd(Cmd*, Message*)
- {
- exitfs(0);
- return nil; // not reached
- }
- Message*
- eqcmd(Cmd*, Message *m)
- {
- if(m == &top)
- Bprint(&out, "0\n");
- else
- Bprint(&out, "%d\n", m->id);
- return nil;
- }
- Message*
- dcmd(Cmd*, Message *m)
- {
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- while(m->parent != &top)
- m = m->parent;
- m->deleted = 1;
- return m;
- }
- Message*
- ucmd(Cmd*, Message *m)
- {
- if(m == &top)
- return nil;
- while(m->parent != &top)
- m = m->parent;
- m->deleted = 0;
- return m;
- }
- Message*
- icmd(Cmd*, Message *m)
- {
- int n;
- n = dir2message(&top, reverse);
- if(n > 0)
- Bprint(&out, "%d new messages\n", n);
- return m;
- }
- Message*
- helpcmd(Cmd*, Message *m)
- {
- int i;
- Bprint(&out, "Commands are of the form [<range>] <command> [args]\n");
- Bprint(&out, "<range> := <addr> | <addr>','<addr>| 'g'<search>\n");
- Bprint(&out, "<addr> := '.' | '$' | '^' | <number> | <search> | <addr>'+'<addr> | <addr>'-'<addr>\n");
- Bprint(&out, "<search> := '/'<regexp>'/' | '?'<regexp>'?' | '%%'<regexp>'%%'\n");
- Bprint(&out, "<command> :=\n");
- for(i = 0; cmdtab[i].cmd != nil; i++)
- Bprint(&out, "%s\n", cmdtab[i].help);
- return m;
- }
- int
- tomailer(char **av)
- {
- Waitmsg *w;
- int pid, i;
- // start the mailer and get out of the way
- switch(pid = fork()){
- case -1:
- fprint(2, "can't fork: %r\n");
- return -1;
- case 0:
- Bprint(&out, "!/bin/upas/marshal");
- for(i = 1; av[i]; i++){
- if(strchr(av[i], ' ') != nil)
- Bprint(&out, " '%s'", av[i]);
- else
- Bprint(&out, " %s", av[i]);
- }
- Bprint(&out, "\n");
- Bflush(&out);
- av[0] = "marshal";
- chdir(wd);
- exec("/bin/upas/marshal", av);
- fprint(2, "couldn't exec /bin/upas/marshal\n");
- exits(0);
- default:
- w = wait();
- if(w == nil){
- if(interrupted)
- postnote(PNPROC, pid, "die");
- waitpid();
- return -1;
- }
- if(w->msg[0]){
- fprint(2, "mailer failed: %s\n", w->msg);
- free(w);
- return -1;
- }
- free(w);
- Bprint(&out, "!\n");
- break;
- }
- return 0;
- }
- //
- // like tokenize but obey "" quoting
- //
- int
- tokenize822(char *str, char **args, int max)
- {
- int na;
- int intok = 0, inquote = 0;
- if(max <= 0)
- return 0;
- for(na=0; ;str++)
- switch(*str) {
- case ' ':
- case '\t':
- if(inquote)
- goto Default;
- /* fall through */
- case '\n':
- *str = 0;
- if(!intok)
- continue;
- intok = 0;
- if(na < max)
- continue;
- /* fall through */
- case 0:
- return na;
- case '"':
- inquote ^= 1;
- /* fall through */
- Default:
- default:
- if(intok)
- continue;
- args[na++] = str;
- intok = 1;
- }
- return 0; /* can't get here; silence compiler */
- }
- Message*
- rcmd(Cmd *c, Message *m)
- {
- char *av[128];
- int i, ai = 1;
- Message *nm;
- char *addr;
- String *path = nil;
- String *rpath;
- String *subject = nil;
- String *from;
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- addr = nil;
- for(nm = m; nm != ⊤ nm = nm->parent){
- if(*nm->replyto != 0){
- addr = nm->replyto;
- break;
- }
- }
- if(addr == nil){
- Bprint(&out, "!no reply address\n");
- return nil;
- }
- if(nm == &top){
- print("!noone to reply to\n");
- return nil;
- }
- for(nm = m; nm != ⊤ nm = nm->parent){
- if(*nm->subject){
- av[ai++] = "-s";
- subject = addrecolon(nm->subject);
- av[ai++] = s_to_c(subject);;
- break;
- }
- }
- av[ai++] = "-R";
- rpath = rooted(s_clone(m->path));
- av[ai++] = s_to_c(rpath);
- if(strchr(c->av[0], 'f') != nil){
- fcmd(c, m);
- av[ai++] = "-F";
- }
- if(strchr(c->av[0], 'R') != nil){
- av[ai++] = "-t";
- av[ai++] = "message/rfc822";
- av[ai++] = "-A";
- path = rooted(extendpath(m->path, "raw"));
- av[ai++] = s_to_c(path);
- }
- for(i = 1; i < c->an && ai < nelem(av)-1; i++)
- av[ai++] = c->av[i];
- from = s_copy(addr);
- ai += tokenize822(s_to_c(from), &av[ai], nelem(av) - ai);
- av[ai] = 0;
- if(tomailer(av) < 0)
- m = nil;
- s_free(path);
- s_free(rpath);
- s_free(subject);
- s_free(from);
- return m;
- }
- Message*
- mcmd(Cmd *c, Message *m)
- {
- char **av;
- int i, ai;
- String *path;
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- if(c->an < 2){
- fprint(2, "!usage: M list-of addresses\n");
- return nil;
- }
- ai = 1;
- av = malloc(sizeof(char*)*(c->an + 8));
- av[ai++] = "-t";
- if(m->parent == &top)
- av[ai++] = "message/rfc822";
- else
- av[ai++] = "mime";
- av[ai++] = "-A";
- path = rooted(extendpath(m->path, "raw"));
- av[ai++] = s_to_c(path);
- if(strchr(c->av[0], 'M') == nil)
- av[ai++] = "-n";
- for(i = 1; i < c->an; i++)
- av[ai++] = c->av[i];
- av[ai] = 0;
- if(tomailer(av) < 0)
- m = nil;
- if(path != nil)
- s_free(path);
- free(av);
- return m;
- }
- Message*
- acmd(Cmd *c, Message *m)
- {
- char *av[128];
- int i, ai;
- String *from, *to, *cc, *path = nil, *subject = nil;
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- ai = 1;
- if(*m->subject){
- av[ai++] = "-s";
- subject = addrecolon(m->subject);
- av[ai++] = s_to_c(subject);
- }
- if(strchr(c->av[0], 'A') != nil){
- av[ai++] = "-t";
- av[ai++] = "message/rfc822";
- av[ai++] = "-A";
- path = rooted(extendpath(m->path, "raw"));
- av[ai++] = s_to_c(path);
- }
- for(i = 1; i < c->an && ai < nelem(av)-1; i++)
- av[ai++] = c->av[i];
- from = s_copy(m->from);
- ai += tokenize822(s_to_c(from), &av[ai], nelem(av) - ai);
- to = s_copy(m->to);
- ai += tokenize822(s_to_c(to), &av[ai], nelem(av) - ai);
- cc = s_copy(m->cc);
- ai += tokenize822(s_to_c(cc), &av[ai], nelem(av) - ai);
- av[ai] = 0;
- if(tomailer(av) < 0)
- return nil;
- s_free(from);
- s_free(to);
- s_free(cc);
- s_free(subject);
- s_free(path);
- return m;
- }
- String *
- relpath(char *path, String *to)
- {
- if (*path=='/' || strncmp(path, "./", 2) == 0
- || strncmp(path, "../", 3) == 0) {
- to = s_append(to, path);
- } else if(mbpath) {
- to = s_append(to, s_to_c(mbpath));
- to->ptr = strrchr(to->base, '/')+1;
- s_append(to, path);
- }
- return to;
- }
- int
- appendtofile(Message *m, char *part, char *base, int mbox)
- {
- String *file, *h;
- int n, in, out, rv;
- char buf[4096];
- file = extendpath(m->path, part);
- in = open(s_to_c(file), OREAD);
- if(in < 0){
- fprint(2, "!message disappeared\n");
- return -1;
- }
- s_reset(file);
- relpath(base, file);
- if(mbox)
- out = open(s_to_c(file), OWRITE);
- else
- out = open(s_to_c(file), OWRITE|OTRUNC);
- if(out < 0){
- out = create(s_to_c(file), OWRITE, 0666);
- if(out < 0){
- fprint(2, "!can't open %s: %r\n", s_to_c(file));
- close(in);
- return -1;
- }
- }
- if(mbox)
- seek(out, 0, 2);
- // put on a 'From ' line
- if(mbox){
- while(m->parent != &top)
- m = m->parent;
- h = file2string(m->path, "unixheader");
- fprint(out, "%s", s_to_c(h));
- s_free(h);
- }
- rv = 0;
- for(;;){
- n = read(in, buf, sizeof(buf));
- if(n < 0){
- fprint(2, "!error reading file: %r\n");
- rv = -1;
- break;
- }
- if(n == 0)
- break;
- if(write(out, buf, n) != n){
- fprint(2, "!error writing file: %r\n");
- rv = -1;
- break;
- }
- }
- if(mbox)
- write(out, "\n", 1);
- close(in);
- close(out);
- if(rv >= 0)
- print("!saved in %s\n", s_to_c(file));
- s_free(file);
- return rv;
- }
- Message*
- scmd(Cmd *c, Message *m)
- {
- char *file;
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- switch(c->an){
- case 1:
- file = "stored";
- break;
- case 2:
- file = c->av[1];
- break;
- default:
- fprint(2, "!usage: s filename\n");
- return nil;
- }
- if(appendtofile(m, "raw", file, 1) < 0)
- return nil;
- m->stored = 1;
- return m;
- }
- Message*
- wcmd(Cmd *c, Message *m)
- {
- char *file;
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- switch(c->an){
- case 2:
- file = c->av[1];
- break;
- case 1:
- if(*m->filename == 0){
- fprint(2, "!usage: w filename\n");
- return nil;
- }
- file = strrchr(m->filename, '/');
- if(file != nil)
- file++;
- else
- file = m->filename;
- break;
- default:
- fprint(2, "!usage: w filename\n");
- return nil;
- }
- if(appendtofile(m, "body", file, 0) < 0)
- return nil;
- m->stored = 1;
- return m;
- }
- // find the recipient account name
- static void
- foldername(char *folder, char *rcvr)
- {
- char *p;
- char *e = folder+Elemlen-1;
- p = strrchr(rcvr, '!');
- if(p != nil)
- rcvr = p+1;
- while(folder < e && *rcvr && *rcvr != '@')
- *folder++ = *rcvr++;
- *folder = 0;
- }
- Message*
- fcmd(Cmd *c, Message *m)
- {
- char folder[Elemlen];
- if(c->an > 1){
- fprint(2, "!usage: f takes no arguments\n");
- return nil;
- }
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- foldername(folder, m->from);
- if(appendtofile(m, "raw", folder, 1) < 0)
- return nil;
- m->stored = 1;
- return m;
- }
- void
- system(char *cmd, char **av, int in)
- {
- int pid;
- switch(pid=fork()){
- case -1:
- return;
- case 0:
- if(in >= 0){
- close(0);
- dup(in, 0);
- close(in);
- }
- if(wd[0] != 0)
- chdir(wd);
- exec(cmd, av);
- fprint(2, "!couldn't exec %s\n", cmd);
- exits(0);
- default:
- if(in >= 0)
- close(in);
- while(waitpid() < 0){
- if(!interrupted)
- break;
- postnote(PNPROC, pid, "die");
- continue;
- }
- break;
- }
- }
- Message*
- bangcmd(Cmd *c, Message *m)
- {
- char cmd[4*1024];
- char *p, *e;
- char *av[4];
- int i;
- cmd[0] = 0;
- p = cmd;
- e = cmd+sizeof(cmd);
- for(i = 1; i < c->an; i++)
- p = seprint(p, e, "%s ", c->av[i]);
- av[0] = "rc";
- av[1] = "-c";
- av[2] = cmd;
- av[3] = 0;
- system("/bin/rc", av, -1);
- Bprint(&out, "!\n");
- return m;
- }
- Message*
- pipecmd(Cmd *c, Message *m)
- {
- char cmd[128];
- char *p, *e;
- char *av[4];
- String *path;
- int i, fd;
- if(c->an < 2){
- Bprint(&out, "!usage: | cmd\n");
- return nil;
- }
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- path = extendpath(m->path, "body");
- fd = open(s_to_c(path), OREAD);
- s_free(path);
- if(fd < 0){
- fprint(2, "!message disappeared\n");
- return nil;
- }
- p = cmd;
- e = cmd+sizeof(cmd);
- cmd[0] = 0;
- for(i = 1; i < c->an; i++)
- p = seprint(p, e, "%s ", c->av[i]);
- av[0] = "rc";
- av[1] = "-c";
- av[2] = cmd;
- av[3] = 0;
- system("/bin/rc", av, fd); /* system closes fd */
- Bprint(&out, "!\n");
- return m;
- }
- void
- closemb(void)
- {
- int fd;
- fd = open("/mail/fs/ctl", ORDWR);
- if(fd < 0)
- sysfatal("can't open /mail/fs/ctl: %r");
- // close current mailbox
- if(*mbname && strcmp(mbname, "mbox") != 0)
- fprint(fd, "close %s", mbname);
- close(fd);
- }
- int
- switchmb(char *file, char *singleton)
- {
- char *p;
- int n, fd;
- String *path;
- char buf[256];
- // if the user didn't say anything and there
- // is an mbox mounted already, use that one
- // so that the upas/fs -fdefault default is honored.
- if(file
- || (singleton && access(singleton, 0)<0)
- || (!singleton && access("/mail/fs/mbox", 0)<0)){
- if(file == nil)
- file = "mbox";
- // close current mailbox
- closemb();
- didopen = 1;
- fd = open("/mail/fs/ctl", ORDWR);
- if(fd < 0)
- sysfatal("can't open /mail/fs/ctl: %r");
-
- path = s_new();
-
- // get an absolute path to the mail box
- if(strncmp(file, "./", 2) == 0){
- // resolve path here since upas/fs doesn't know
- // our working directory
- if(getwd(buf, sizeof(buf)-strlen(file)) == nil){
- fprint(2, "!can't get working directory: %s\n", buf);
- return -1;
- }
- s_append(path, buf);
- s_append(path, file+1);
- } else {
- mboxpath(file, user, path, 0);
- }
-
- // make up a handle to use when talking to fs
- p = strrchr(file, '/');
- if(p == nil){
- // if its in the mailbox directory, just use the name
- strncpy(mbname, file, sizeof(mbname));
- mbname[sizeof(mbname)-1] = 0;
- } else {
- // make up a mailbox name
- p = strrchr(s_to_c(path), '/');
- p++;
- if(*p == 0){
- fprint(2, "!bad mbox name");
- return -1;
- }
- strncpy(mbname, p, sizeof(mbname));
- mbname[sizeof(mbname)-1] = 0;
- n = strlen(mbname);
- if(n > Elemlen-12)
- n = Elemlen-12;
- sprint(mbname+n, "%ld", time(0));
- }
-
- if(fprint(fd, "open %s %s", s_to_c(path), mbname) < 0){
- fprint(2, "!can't 'open %s %s': %r\n", file, mbname);
- s_free(path);
- return -1;
- }
- close(fd);
- }else{
- path = s_reset(nil);
- mboxpath("mbox", user, path, 0);
- strcpy(mbname, "mbox");
- }
- sprint(root, "/mail/fs/%s", mbname);
- if(getwd(wd, sizeof(wd)) == 0)
- wd[0] = 0;
- if(singleton == nil && chdir(root) >= 0)
- strcpy(root, ".");
- rootlen = strlen(root);
- if(mbpath != nil)
- s_free(mbpath);
- mbpath = path;
- return 0;
- }
- // like tokenize but for into lines
- int
- lineize(char *s, char **f, int n)
- {
- int i;
- for(i = 0; *s && i < n; i++){
- f[i] = s;
- s = strchr(s, '\n');
- if(s == nil)
- break;
- *s++ = 0;
- }
- return i;
- }
- String*
- rooted(String *s)
- {
- static char buf[256];
- if(strcmp(root, ".") != 0)
- return s;
- snprint(buf, sizeof(buf), "/mail/fs/%s/%s", mbname, s_to_c(s));
- s_free(s);
- return s_copy(buf);
- }
- int
- plumb(Message *m, Ctype *cp)
- {
- String *s;
- Plumbmsg *pm;
- static int fd = -2;
- if(cp->plumbdest == nil)
- return -1;
- if(fd < -1)
- fd = plumbopen("send", OWRITE);
- if(fd < 0)
- return -1;
- pm = mallocz(sizeof(Plumbmsg), 1);
- pm->src = strdup("mail");
- if(*cp->plumbdest)
- pm->dst = strdup(cp->plumbdest);
- pm->wdir = nil;
- pm->type = strdup("text");
- pm->ndata = -1;
- s = rooted(extendpath(m->path, "body"));
- if(cp->ext != nil){
- s_append(s, ".");
- s_append(s, cp->ext);
- }
- pm->data = strdup(s_to_c(s));
- s_free(s);
- plumbsend(fd, pm);
- plumbfree(pm);
- return 0;
- }
- void
- regerror(char*)
- {
- }
- String*
- addrecolon(char *s)
- {
- String *str;
- if(cistrncmp(s, "re:", 3) != 0){
- str = s_copy("Re: ");
- s_append(str, s);
- } else
- str = s_copy(s);
- return str;
- }
- void
- exitfs(char *rv)
- {
- if(startedfs)
- unmount(nil, "/mail/fs");
- chdir("/sys/src/cmd/upas/ned");
- exits(rv);
- }
|