1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488 |
- #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 doflush;
- 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 *next;
- };
- 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/rtf", "rtf", 1, 0 },
- { "text", "txt", 1, 0 },
- { "message/rfc822", "msg", 0, 0 },
- { "image/bmp", "bmp", 0, "image" },
- { "image/jpeg", "jpg", 0, "image" },
- { "image/gif", "gif", 0, "image" },
- { "application/pdf", "pdf", 0, "postscript" },
- { "application/postscript", "ps", 0, "postscript" },
- { "application/", 0, 0, 0 },
- { "image/", 0, 0, 0 },
- { "multipart/", "mul", 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* ycmd(Cmd*, Message*);
- Message* pipecmd(Cmd*, Message*);
- Message* rpipecmd(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 without flushing deleted messages" },
- { "y", 0, ycmd, "y synchronize with mail box" },
- { "=", 1, eqcmd, "= print current message number" },
- { "|", 1, pipecmd, "|cmd pipe message body to a command" },
- { "||", 1, rpipecmd, "||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;
- int longestfrom = 12;
- 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*);
- Message* flushdeleted(Message*);
- void
- usage(void)
- {
- fprint(2, "usage: %s [-nr] [-f mailfile] [-s mailfile]\n", argv0);
- fprint(2, " %s -c dir\n", argv0);
- exits("usage");
- }
- void
- catchnote(void*, char *note)
- {
- if(strstr(note, "interrupt") != nil){
- interrupted = 1;
- noted(NCONT);
- }
- noted(NDFLT);
- }
- char *
- plural(int n)
- {
- if (n == 1)
- return "";
- return "s";
- }
- void
- main(int argc, char **argv)
- {
- Message *cur, *m, *x;
- char cmdline[4*1024];
- Cmd cmd;
- Ctype *cp;
- 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);
- for(cp = ctype; cp < ctype+nelem(ctype)-1; cp++)
- cp->next = cp+1;
- 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 message%s\n", n, plural(n));
- }
- notify(catchnote);
- prompt = s_new();
- for(;;){
- s_reset(prompt);
- if(cur == &top)
- s_append(prompt, ": ");
- else {
- mkid(prompt, cur);
- s_append(prompt, ": ");
- }
- // leave space at the end of cmd line in case parsecmd needs to
- // add a space after a '|' or '!'
- if(readline(s_to_c(prompt), cmdline, sizeof(cmdline)-1) == 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);
- }
- if(doflush)
- cur = flushdeleted(cur);
- }
- 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;
- }
- void
- freemessage(Message *m)
- {
- Message *nm, *next;
- for(nm = m->child; nm != nil; nm = next){
- next = nm->next;
- freemessage(nm);
- }
- s_free(m->path);
- s_free(m->info);
- free(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 and file longest from
- i = 1;
- longestfrom = 12;
- for(m = first; m != nil; m = m->next){
- m->id = natural ? m->fileno : i++;
- n = strlen(m->from);
- if(n > longestfrom)
- longestfrom = n;
- }
- 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(Message *m)
- {
- char *p;
- char ftype[128];
- int n, pfd[2];
- Ctype *a, *cp;
- static Ctype nulltype = { "", 0, 0, 0 };
- static Ctype bintype = { "application/octet-stream", "bin", 0, 0 };
- for(cp = ctype; cp; cp = cp->next)
- if(strncmp(cp->type, m->type, strlen(cp->type)) == 0)
- return cp;
- /* use file(1) for any unknown mimetypes
- *
- * if (strcmp(m->type, bintype.type) != 0)
- * return &nulltype;
- */
- if(pipe(pfd) < 0)
- return &bintype;
- *ftype = 0;
- 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", s_to_c(extendpath(m->path, "body")), 0);
- exits(0);
- default:
- close(pfd[0]);
- n = read(pfd[1], ftype, sizeof(ftype));
- if(n > 0)
- ftype[n] = 0;
- close(pfd[1]);
- waitpid();
- break;
- }
- if (*ftype=='\0' || (p = strchr(ftype, '/')) == nil)
- return &bintype;
- *p++ = 0;
- a = mallocz(sizeof(Ctype), 1);
- a->type = strdup(ftype);
- a->ext = strdup(p);
- a->display = 0;
- a->plumbdest = strdup(ftype);
- for(cp = ctype; cp->next; cp = cp->next)
- continue;
- cp->next = a;
- a->next = nil;
- return a;
- }
- 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;
- char *p, *q;;
- // create id
- id = s_new();
- mkid(id, m);
- if(*m->from == 0){
- // no from
- snprint(buf, len, "%-3s %s %6d %s",
- s_to_c(id),
- m->type,
- m->len,
- m->filename);
- } else if(*m->subject){
- q = p = strdup(m->subject);
- while(*p == ' ')
- p++;
- if(strlen(p) > 50)
- p[50] = 0;
- cracktime(m->date, timebuf, sizeof(timebuf));
- snprint(buf, len, "%-3s %c%c%c %6d %11.11s %-*.*s %s",
- s_to_c(id),
- m->child ? 'H' : ' ',
- m->deleted ? 'd' : ' ',
- m->stored ? 's' : ' ',
- m->len,
- timebuf,
- longestfrom, longestfrom, m->from,
- p);
- free(q);
- } else {
- cracktime(m->date, timebuf, sizeof(timebuf));
- snprint(buf, len, "%-3s %c%c%c %6d %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;
- char *q;
- 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;
- }
- }
- }
- // insert a space after '!'s and '|'s
- for(q = p; *q; q++)
- if(*q != '!' && *q != '|')
- break;
- if(q != p && *q != ' '){
- memmove(q+1, q, strlen(q)+1);
- *q = ' ';
- }
- 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 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 message%s\n", i, plural(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] = "-l 40 -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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- 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);
- if(cp->display || strncmp(m->type, "multipart/", 10) == 0)
- quotecmd(nil, nm);
- }
- }
- return m;
- }
- // really delete messages
- Message*
- flushdeleted(Message *cur)
- {
- Message *m, **l;
- char buf[1024], *p, *e, *msg;
- int deld, n, fd;
- int i;
- doflush = 0;
- deld = 0;
- 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(l = &top.child; *l != nil;){
- m = *l;
- if(!m->deleted){
- l = &(*l)->next;
- continue;
- }
- // don't return a pointer to a deleted message
- if(m == cur)
- cur = m->next;
- 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++;
- // unchain and free
- *l = m->next;
- if(m->next)
- m->next->prev = m->prev;
- freemessage(m);
- }
- if(n)
- write(fd, buf, p-buf);
- close(fd);
- if(deld)
- Bprint(&out, "!%d message%s deleted\n", deld, plural(deld));
- // renumber
- i = 1;
- for(m = top.child; m != nil; m = m->next)
- m->id = natural ? m->fileno : i++;
- // if we're out of messages, go back to first
- // if no first, return the fake first
- if(cur == nil){
- if(top.child)
- return top.child;
- else
- return ⊤
- }
- return cur;
- }
- Message*
- qcmd(Cmd*, Message*)
- {
- flushdeleted(nil);
- if(didopen)
- closemb();
- Bflush(&out);
- exitfs(0);
- return nil; // not reached
- }
- Message*
- ycmd(Cmd*, Message *m)
- {
- doflush = 1;
- return icmd(nil, m);
- }
- 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;
- if(m->deleted < 0)
- Bprint(&out, "!can't undelete, already flushed\n");
- m->deleted = 0;
- return m;
- }
- Message*
- icmd(Cmd*, Message *m)
- {
- int n;
- n = dir2message(&top, reverse);
- if(n > 0)
- Bprint(&out, "%d new message%s\n", n, plural(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 in, out, rv;
- 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(sysisdir(s_to_c(file))){
- s_append(file, "/");
- if(m->filename && strchr(m->filename, '/') == nil)
- s_append(file, m->filename);
- else {
- s_append(file, "att.XXXXXXXXXXX");
- mktemp(s_to_c(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);
- s_free(file);
- 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);
- }
- // copy the message escaping what we have to ad adding newlines if we have to
- if(mbox)
- rv = appendfiletombox(in, out);
- else
- rv = appendfiletofile(in, out);
- 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;
- }
- char *specialfile[] =
- {
- "pipeto",
- "pipefrom",
- "L.mbox",
- "forward",
- "names"
- };
- // return 1 if this is a special file
- static int
- special(String *s)
- {
- char *p;
- int i;
- 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 String*
- foldername(char *rcvr)
- {
- char *p;
- int c;
- String *file;
- Dir *d;
- int scarey;
- file = s_new();
- mboxpath("f", user, file, 0);
- d = dirstat(s_to_c(file));
- // if $mail/f exists, store there, otherwise in $mail
- s_restart(file);
- if(d && d->qid.type == QTDIR){
- scarey = 0;
- s_append(file, "f/");
- } else {
- scarey = 1;
- }
- 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, "!won't overwrite %s\n", s_to_c(file));
- s_free(file);
- return nil;
- }
- return file;
- }
- Message*
- fcmd(Cmd *c, Message *m)
- {
- String *folder;
- if(c->an > 1){
- fprint(2, "!usage: f takes no arguments\n");
- return nil;
- }
- if(m == &top){
- Bprint(&out, "!address\n");
- return nil;
- }
- folder = foldername(m->from);
- if(folder == nil)
- return nil;
- if(appendtofile(m, "raw", s_to_c(folder), 1) < 0){
- s_free(folder);
- return nil;
- }
- s_free(folder);
- 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*
- xpipecmd(Cmd *c, Message *m, char *part)
- {
- 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, part);
- fd = open(s_to_c(path), OREAD);
- s_free(path);
- if(fd < 0){ // compatibility with older upas/fs
- path = extendpath(m->path, "raw");
- 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;
- }
- Message*
- pipecmd(Cmd *c, Message *m)
- {
- return xpipecmd(c, m, "body");
- }
- Message*
- rpipecmd(Cmd *c, Message *m)
- {
- return xpipecmd(c, m, "rawunix");
- }
- 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
- if (singleton && access(singleton, 0)==0
- && strncmp(singleton, "/mail/fs/", 9) == 0){
- if ((p = strchr(singleton +10, '/')) == nil){
- fprint(2, "!bad mbox name");
- return -1;
- }
- n = p-(singleton+9);
- strncpy(mbname, singleton+9, n);
- mbname[n+1] = 0;
- path = s_reset(nil);
- mboxpath(mbname, user, path, 0);
- }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);
- }
|