URI:
       tmain.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tmain.c (11855B)
       ---
            1 #include "common.h"
            2 #include "send.h"
            3 
            4 /* globals to all files */
            5 int rmail;
            6 char *thissys, *altthissys;
            7 int nflg;
            8 int xflg;
            9 int debug;
           10 int rflg;
           11 int iflg = 1;
           12 int nosummary;
           13 
           14 /* global to this file */
           15 static String *errstring;
           16 static message *mp;
           17 static int interrupt;
           18 static int savemail;
           19 static Biobuf in;
           20 static int forked;
           21 static int add822headers = 1;
           22 static String *arglist;
           23 
           24 /* predeclared */
           25 static int        send(dest *, message *, int);
           26 static void        lesstedious(void);
           27 static void        save_mail(message *);
           28 static int        complain_mail(dest *, message *);
           29 static int        pipe_mail(dest *, message *);
           30 static void        appaddr(String *, dest *);
           31 static void        mkerrstring(String *, message *, dest *, dest *, char *, int);
           32 static int        replymsg(String *, message *, dest *);
           33 static int        catchint(void*, char*);
           34 
           35 void
           36 usage(void)
           37 {
           38         fprint(2, "usage: mail [-birtx] list-of-addresses\n");
           39         exits("usage");
           40 }
           41 
           42 void
           43 main(int argc, char *argv[])
           44 {
           45         dest *dp=0;
           46         int checkforward;
           47         char *base;
           48         int rv;
           49 
           50         /* process args */
           51         ARGBEGIN{
           52         case '#':
           53                 nflg = 1;
           54                 break;
           55         case 'b':
           56                 add822headers = 0;
           57                 break;
           58         case 'x':
           59                 nflg = 1;
           60                 xflg = 1;
           61                 break;
           62         case 'd':
           63                 debug = 1;
           64                 break;
           65         case 'i':
           66                 iflg = 0;
           67                 break;
           68         case 'r':
           69                 rflg = 1;
           70                 break;
           71         default:
           72                 usage();
           73         }ARGEND
           74 
           75         while(*argv){
           76                 if(shellchars(*argv)){
           77                         fprint(2, "illegal characters in destination\n");
           78                         exits("syntax");
           79                 }
           80                 d_insert(&dp, d_new(s_copy(*argv++)));
           81         }
           82 
           83         if (dp == 0)
           84                 usage();
           85         arglist = d_to(dp);
           86 
           87         /*
           88          * get context:
           89          *        - whether we're rmail or mail
           90          */
           91         base = basename(argv0);
           92         checkforward = rmail = (strcmp(base, "rmail")==0) | rflg;
           93         thissys = sysname_read();
           94         altthissys = alt_sysname_read();
           95         if(rmail)
           96                 add822headers = 0;
           97 
           98         /*
           99          *  read the mail.  If an interrupt occurs while reading, save in
          100          *  dead.letter
          101          */
          102         if (!nflg) {
          103                 Binit(&in, 0, OREAD);
          104                 if(!rmail)
          105                         atnotify(catchint, 1);
          106                 mp = m_read(&in, rmail, !iflg);
          107                 if (mp == 0)
          108                         exit(0);
          109                 if (interrupt != 0) {
          110                         save_mail(mp);
          111                         exit(1);
          112                 }
          113         } else {
          114                 mp = m_new();
          115                 if(default_from(mp) < 0){
          116                         fprint(2, "%s: can't determine login name\n", argv0);
          117                         exit(1);
          118                 }
          119         }
          120         errstring = s_new();
          121         getrules();
          122 
          123         /*
          124          *  If this is a gateway, translate the sender address into a local
          125          *  address.  This only happens if mail to the local address is
          126          *  forwarded to the sender.
          127          */
          128         gateway(mp);
          129 
          130         /*
          131          *  Protect against shell characters in the sender name for
          132          *  security reasons.
          133          */
          134         mp->sender = escapespecial(mp->sender);
          135         if (shellchars(s_to_c(mp->sender)))
          136                 mp->replyaddr = s_copy("postmaster");
          137         else
          138                 mp->replyaddr = s_clone(mp->sender);
          139 
          140         /*
          141          *  reject messages that have been looping for too long
          142          */
          143         if(mp->received > 32)
          144                 exit(refuse(dp, mp, "possible forward loop", 0, 0));
          145 
          146         /*
          147          *  reject messages that are too long.  We don't do it earlier
          148          *  in m_read since we haven't set up enough things yet.
          149          */
          150         if(mp->size < 0)
          151                 exit(refuse(dp, mp, "message too long", 0, 0));
          152 
          153         rv = send(dp, mp, checkforward);
          154         if(savemail)
          155                 save_mail(mp);
          156         if(mp)
          157                 m_free(mp);
          158         exit(rv);
          159 }
          160 
          161 /* send a message to a list of sites */
          162 static int
          163 send(dest *destp, message *mp, int checkforward)
          164 {
          165         dest *dp;                /* destination being acted upon */
          166         dest *bound;                /* bound destinations */
          167         int errors=0;
          168 
          169         /* bind the destinations to actions */
          170         bound = up_bind(destp, mp, checkforward);
          171         if(add822headers && mp->haveto == 0){
          172                 if(nosummary)
          173                         mp->to = d_to(bound);
          174                 else
          175                         mp->to = arglist;
          176         }
          177 
          178         /* loop through and execute commands */
          179         for (dp = d_rm(&bound); dp != 0; dp = d_rm(&bound)) {
          180                 switch (dp->status) {
          181                 case d_cat:
          182                         errors += cat_mail(dp, mp);
          183                         break;
          184                 case d_pipeto:
          185                 case d_pipe:
          186                         if (!rmail && !nflg && !forked) {
          187                                 forked = 1;
          188                                 lesstedious();
          189                         }
          190                         errors += pipe_mail(dp, mp);
          191                         break;
          192                 default:
          193                         errors += complain_mail(dp, mp);
          194                         break;
          195                 }
          196         }
          197 
          198         return errors;
          199 }
          200 
          201 /* avoid user tedium (as Mike Lesk said in a previous version) */
          202 static void
          203 lesstedious(void)
          204 {
          205         int i;
          206 
          207         if(debug)
          208                 return;
          209 
          210         switch(fork()){
          211         case -1:
          212                 break;
          213         case 0:
          214                 sysdetach();
          215                 for(i=0; i<3; i++)
          216                         close(i);
          217                 savemail = 0;
          218                 break;
          219         default:
          220                 exit(0);
          221         }
          222 }
          223 
          224 
          225 /* save the mail */
          226 static void
          227 save_mail(message *mp)
          228 {
          229         Biobuf *fp;
          230         String *file;
          231 
          232         file = s_new();
          233         deadletter(file);
          234         fp = sysopen(s_to_c(file), "cAt", 0660);
          235         if (fp == 0)
          236                 return;
          237         m_bprint(mp, fp);
          238         sysclose(fp);
          239         fprint(2, "saved in %s\n", s_to_c(file));
          240         s_free(file);
          241 }
          242 
          243 /* remember the interrupt happened */
          244 
          245 static int
          246 catchint(void *a, char *msg)
          247 {
          248         USED(a);
          249         if(strstr(msg, "interrupt") || strstr(msg, "hangup")) {
          250                 interrupt = 1;
          251                 return 1;
          252         }
          253         return 0;
          254 }
          255 
          256 /* dispose of incorrect addresses */
          257 static int
          258 complain_mail(dest *dp, message *mp)
          259 {
          260         char *msg;
          261 
          262         switch (dp->status) {
          263         case d_undefined:
          264                 msg = "Invalid address"; /* a little different, for debugging */
          265                 break;
          266         case d_syntax:
          267                 msg = "invalid address";
          268                 break;
          269         case d_unknown:
          270                 msg = "unknown user";
          271                 break;
          272         case d_eloop:
          273         case d_loop:
          274                 msg = "forwarding loop";
          275                 break;
          276         case d_noforward:
          277                 if(dp->pstat && *s_to_c(dp->repl2))
          278                         return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0);
          279                 else
          280                         msg = "destination unknown or forwarding disallowed";
          281                 break;
          282         case d_pipe:
          283                 msg = "broken pipe";
          284                 break;
          285         case d_cat:
          286                 msg = "broken cat";
          287                 break;
          288         case d_translate:
          289                 if(dp->pstat && *s_to_c(dp->repl2))
          290                         return refuse(dp, mp, s_to_c(dp->repl2), dp->pstat, 0);
          291                 else
          292                         msg = "name translation failed";
          293                 break;
          294         case d_alias:
          295                 msg = "broken alias";
          296                 break;
          297         case d_badmbox:
          298                 msg = "corrupted mailbox";
          299                 break;
          300         case d_resource:
          301                 return refuse(dp, mp, "out of some resource.  Try again later.", 0, 1);
          302         default:
          303                 msg = "unknown d_";
          304                 break;
          305         }
          306         if (nflg) {
          307                 print("%s: %s\n", msg, s_to_c(dp->addr));
          308                 return 0;
          309         }
          310         return refuse(dp, mp, msg, 0, 0);
          311 }
          312 
          313 /* dispose of remote addresses */
          314 static int
          315 pipe_mail(dest *dp, message *mp)
          316 {
          317         dest *next, *list=0;
          318         String *cmd;
          319         process *pp;
          320         int status;
          321         char *none;
          322         String *errstring=s_new();
          323 
          324         if (dp->status == d_pipeto)
          325                 none = "none";
          326         else
          327                 none = 0;
          328         /*
          329          *  collect the arguments
          330          */
          331         next = d_rm_same(&dp);
          332         if(xflg)
          333                 cmd = s_new();
          334         else
          335                 cmd = s_clone(next->repl1);
          336         for(; next != 0; next = d_rm_same(&dp)){
          337                 if(xflg){
          338                         s_append(cmd, s_to_c(next->addr));
          339                         s_append(cmd, "\n");
          340                 } else {
          341                         if (next->repl2 != 0) {
          342                                 s_append(cmd, " ");
          343                                 s_append(cmd, s_to_c(next->repl2));
          344                         }
          345                 }
          346                 d_insert(&list, next);
          347         }
          348 
          349         if (nflg) {
          350                 if(xflg)
          351                         print("%s", s_to_c(cmd));
          352                 else
          353                         print("%s\n", s_to_c(cmd));
          354                 s_free(cmd);
          355                 return 0;
          356         }
          357 
          358         /*
          359          *  run the process
          360          */
          361         pp = proc_start(s_to_c(cmd), instream(), 0, outstream(), 1, none);
          362         if(pp==0 || pp->std[0]==0 || pp->std[2]==0)
          363                 return refuse(list, mp, "out of processes, pipes, or memory", 0, 1);
          364         pipesig(0);
          365         m_print(mp, pp->std[0]->fp, thissys, 0);
          366         pipesigoff();
          367         stream_free(pp->std[0]);
          368         pp->std[0] = 0;
          369         while(s_read_line(pp->std[2]->fp, errstring))
          370                 ;
          371         status = proc_wait(pp);
          372         proc_free(pp);
          373         s_free(cmd);
          374 
          375         /*
          376          *  return status
          377          */
          378         if (status != 0)
          379                 return refuse(list, mp, s_to_c(errstring), status, 0);
          380         loglist(list, mp, "remote");
          381         return 0;
          382 }
          383 
          384 static void
          385 appaddr(String *sp, dest *dp)
          386 {
          387         dest *parent;
          388         String *s;
          389 
          390         if (dp->parent != 0) {
          391                 for(parent=dp->parent; parent->parent!=0; parent=parent->parent)
          392                         ;
          393                 s = unescapespecial(s_clone(parent->addr));
          394                 s_append(sp, s_to_c(s));
          395                 s_free(s);
          396                 s_append(sp, "' alias `");
          397         }
          398         s = unescapespecial(s_clone(dp->addr));
          399         s_append(sp, s_to_c(s));
          400         s_free(s);
          401 }
          402 
          403 /*
          404  *  reject delivery
          405  *
          406  *  returns        0        - if mail has been disposed of
          407  *                other        - if mail has not been disposed
          408  */
          409 int
          410 refuse(dest *list, message *mp, char *cp, int status, int outofresources)
          411 {
          412         String *errstring=s_new();
          413         dest *dp;
          414         int rv;
          415 
          416         dp = d_rm(&list);
          417         mkerrstring(errstring, mp, dp, list, cp, status);
          418 
          419         /*
          420          *  log first in case we get into trouble
          421          */
          422         logrefusal(dp, mp, s_to_c(errstring));
          423 
          424         /*
          425          *  bulk mail is never replied to, if we're out of resources,
          426          *  let the sender try again
          427          */
          428         if(rmail){
          429                 /* accept it or request a retry */
          430                 if(outofresources){
          431                         fprint(2, "Mail %s\n", s_to_c(errstring));
          432                         rv = 1;                                        /* try again later */
          433                 } else if(mp->bulk)
          434                         rv = 0;                                        /* silently discard bulk */
          435                 else
          436                         rv = replymsg(errstring, mp, dp);        /* try later if we can't reply */
          437         } else {
          438                 /* aysnchronous delivery only happens if !rmail */
          439                 if(forked){
          440                         /*
          441                          *  if spun off for asynchronous delivery, we own the mail now.
          442                          *  return it or dump it on the floor.  rv really doesn't matter.
          443                          */
          444                         rv = 0;
          445                         if(!outofresources && !mp->bulk)
          446                                 replymsg(errstring, mp, dp);
          447                 } else {
          448                         fprint(2, "Mail %s\n", s_to_c(errstring));
          449                         savemail = 1;
          450                         rv = 1;
          451                 }
          452         }
          453 
          454         s_free(errstring);
          455         return rv;
          456 }
          457 
          458 /* make the error message */
          459 static void
          460 mkerrstring(String *errstring, message *mp, dest *dp, dest *list, char *cp, int status)
          461 {
          462         dest *next;
          463         char smsg[64];
          464         String *sender;
          465 
          466         sender = unescapespecial(s_clone(mp->sender));
          467 
          468         /* list all aliases */
          469         s_append(errstring, " from '");
          470         s_append(errstring, s_to_c(sender));
          471         s_append(errstring, "'\nto '");
          472         appaddr(errstring, dp);
          473         for(next = d_rm(&list); next != 0; next = d_rm(&list)) {
          474                 s_append(errstring, "'\nand '");
          475                 appaddr(errstring, next);
          476                 d_insert(&dp, next);
          477         }
          478         s_append(errstring, "'\nfailed with error '");
          479         s_append(errstring, cp);
          480         s_append(errstring, "'.\n");
          481 
          482         /* >> and | deserve different flavored messages */
          483         switch(dp->status) {
          484         case d_pipe:
          485                 s_append(errstring, "The mailer `");
          486                 s_append(errstring, s_to_c(dp->repl1));
          487                 sprint(smsg, "' returned error status %x.\n\n", status);
          488                 s_append(errstring, smsg);
          489                 break;
          490         }
          491 
          492         s_free(sender);
          493 }
          494 
          495 /*
          496  *  create a new boundary
          497  */
          498 static String*
          499 mkboundary(void)
          500 {
          501         char buf[32];
          502         int i;
          503         static int already;
          504 
          505         if(already == 0){
          506                 srand((time(0)<<16)|getpid());
          507                 already = 1;
          508         }
          509         strcpy(buf, "upas-");
          510         for(i = 5; i < sizeof(buf)-1; i++)
          511                 buf[i] = 'a' + nrand(26);
          512         buf[i] = 0;
          513         return s_copy(buf);
          514 }
          515 
          516 /*
          517  *  reply with up to 1024 characters of the
          518  *  original message
          519  */
          520 static int
          521 replymsg(String *errstring, message *mp, dest *dp)
          522 {
          523         message *refp = m_new();
          524         dest *ndp;
          525         char *rcvr;
          526         int rv;
          527         String *boundary;
          528 
          529         boundary = mkboundary();
          530 
          531         refp->bulk = 1;
          532         refp->rfc822headers = 1;
          533         rcvr = dp->status==d_eloop ? "postmaster" : s_to_c(mp->replyaddr);
          534         ndp = d_new(s_copy(rcvr));
          535         s_append(refp->sender, "postmaster");
          536         s_append(refp->replyaddr, "/dev/null");
          537         s_append(refp->date, thedate());
          538         refp->haveto = 1;
          539         s_append(refp->body, "To: ");
          540         s_append(refp->body, rcvr);
          541         s_append(refp->body, "\n");
          542         s_append(refp->body, "Subject: bounced mail\n");
          543         s_append(refp->body, "MIME-Version: 1.0\n");
          544         s_append(refp->body, "Content-Type: multipart/mixed;\n");
          545         s_append(refp->body, "\tboundary=\"");
          546         s_append(refp->body, s_to_c(boundary));
          547         s_append(refp->body, "\"\n");
          548         s_append(refp->body, "Content-Disposition: inline\n");
          549         s_append(refp->body, "\n");
          550         s_append(refp->body, "This is a multi-part message in MIME format.\n");
          551         s_append(refp->body, "--");
          552         s_append(refp->body, s_to_c(boundary));
          553         s_append(refp->body, "\n");
          554         s_append(refp->body, "Content-Disposition: inline\n");
          555         s_append(refp->body, "Content-Type: text/plain; charset=\"US-ASCII\"\n");
          556         s_append(refp->body, "Content-Transfer-Encoding: 7bit\n");
          557         s_append(refp->body, "\n");
          558         s_append(refp->body, "The attached mail");
          559         s_append(refp->body, s_to_c(errstring));
          560         s_append(refp->body, "--");
          561         s_append(refp->body, s_to_c(boundary));
          562         s_append(refp->body, "\n");
          563         s_append(refp->body, "Content-Type: message/rfc822\n");
          564         s_append(refp->body, "Content-Disposition: inline\n\n");
          565         s_append(refp->body, s_to_c(mp->body));
          566         s_append(refp->body, "--");
          567         s_append(refp->body, s_to_c(boundary));
          568         s_append(refp->body, "--\n");
          569 
          570         refp->size = s_len(refp->body);
          571         rv = send(ndp, refp, 0);
          572         m_free(refp);
          573         d_free(ndp);
          574         return rv;
          575 }