home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume43 / procmail / part02 < prev    next >
Encoding:
Internet Message Format  |  1994-07-05  |  53.4 KB

  1. From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  2. Newsgroups: comp.sources.misc
  3. Subject: v43i057:  procmail - mail processing package v3.03, Part02/10
  4. Date: 5 Jul 1994 20:49:35 -0500
  5. Organization: Sterling Software
  6. Sender: kent@sparky.sterling.com
  7. Approved: kent@sparky.sterling.com
  8. Message-ID: <2vd2jf$i4o@sparky.sterling.com>
  9. X-Md4-Signature: d1e1793edcda5575ee218801e2f88a43
  10.  
  11. Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  12. Posting-number: Volume 43, Issue 57
  13. Archive-name: procmail/part02
  14. Environment: sendmail, ZMailer, smail, MMDF, mailsurr, UNIX, POSIX
  15. Supersedes: procmail: Volume 38, Issue 19-31
  16.  
  17. #! /bin/sh
  18. # This is a shell archive.  Remove anything before this line, then feed it
  19. # into a shell via "sh file" or similar.  To overwrite existing files,
  20. # type "sh file -c".
  21. # Contents:  procmail-3.03/src/goodies.c procmail-3.03/src/procmail.c
  22. # Wrapped by kent@sparky on Tue Jul  5 20:42:12 1994
  23. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
  24. echo If this archive is complete, you will see the following message:
  25. echo '          "shar: End of archive 2 (of 10)."'
  26. if test -f 'procmail-3.03/src/goodies.c' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'procmail-3.03/src/goodies.c'\"
  28. else
  29.   echo shar: Extracting \"'procmail-3.03/src/goodies.c'\" \(11872 characters\)
  30.   sed "s/^X//" >'procmail-3.03/src/goodies.c' <<'END_OF_FILE'
  31. X/************************************************************************
  32. X *    Collection of library-worthy routines                *
  33. X *                                    *
  34. X *    Copyright (c) 1990-1994, S.R. van den Berg, The Netherlands    *
  35. X *    #include "../README"                        *
  36. X ************************************************************************/
  37. X#ifdef RCS
  38. Xstatic /*const*/char rcsid[]=
  39. X "$Id: goodies.c,v 1.34 1994/06/28 16:56:17 berg Exp $";
  40. X#endif
  41. X#include "procmail.h"
  42. X#include "sublib.h"
  43. X#include "robust.h"
  44. X#include "shell.h"
  45. X#include "misc.h"
  46. X#include "pipes.h"
  47. X#include "common.h"
  48. X#include "cstdio.h"
  49. X#include "goodies.h"
  50. X
  51. Xlong Stdfilled;
  52. Xconst char test[]="test";
  53. Xconst char*Tmnate,*All_args;
  54. X
  55. Xstatic const char*evalenv P((void))    /* expects the variable name in buf2 */
  56. X{ int j;
  57. X  return (unsigned)(j=(*buf2)-'0')>9?getenv(buf2):
  58. X      !j?argv0:
  59. X       j<=crestarg?restargv[j-1]:(const char*)0;
  60. X}
  61. X
  62. X#define NOTHING_YET    (-1)     /* readparse understands a very complete    */
  63. X#define SKIPPING_SPACE    0     /* subset of the standard /bin/sh syntax    */
  64. X#define NORMAL_TEXT    1     /* that includes single-, double- and back- */
  65. X#define DOUBLE_QUOTED    2     /* quotes, backslashes and $subtitutions    */
  66. X#define SINGLE_QUOTED    3
  67. X
  68. X#define fgetc() (*fpgetc)()       /* some compilers previously choked on it */
  69. X
  70. X/* sarg==0 : normal parsing, split up arguments like in /bin/sh
  71. X * sarg==1 : environment assignment parsing, parse up till first whitespace
  72. X * sarg==2 : normal parsing, split up arguments by existing whitespace
  73. X */
  74. Xvoid readparse(p,fpgetc,sarg)register char*p;int(*const fpgetc)();
  75. X const int sarg;
  76. X{ static i,skipbracelev,bracegot;int got,bracelev,qbracelev;char*startb;
  77. X  static char*skipback;static const char*oldstartb;
  78. X  bracelev=qbracelev=0;All_args=0;
  79. X  for(got=NOTHING_YET;;)            /* buf2 is used as scratch space */
  80. Xloop:
  81. X   { i=fgetc();
  82. X     if(buf+linebuf-3<p)        /* doesn't catch everything, just a hint */
  83. X      { nlog("Exceeded LINEBUF\n");p=buf+linebuf-3;
  84. X    goto ready;
  85. X      }
  86. Xnewchar:
  87. X     switch(i)
  88. X      { case EOF:    /* check sarg too to prevent warnings in the recipe- */
  89. X       if(sarg<2&&got>NORMAL_TEXT)         /* condition expansion code */
  90. Xearly_eof:    nlog(unexpeof);
  91. Xready:       if(got!=SKIPPING_SPACE||sarg)  /* not terminated yet or sarg==2 ? */
  92. X          *p++='\0';
  93. X       Tmnate=p;
  94. X       return;
  95. X    case '\\':
  96. X       if(got==SINGLE_QUOTED)
  97. X          break;
  98. X       switch(i=fgetc())
  99. X        { case EOF:
  100. X         goto early_eof;              /* can't quote EOF */
  101. X          case '\n':
  102. X         continue;                /* concatenate lines */
  103. X          case '#':
  104. X         if(got>SKIPPING_SPACE) /* escaped comment at start of word? */
  105. X            goto noesc;            /* apparently not, literally */
  106. X          case ' ':case '\t':case '\'':
  107. X         if(got==DOUBLE_QUOTED)
  108. X            goto noesc;
  109. X          case '"':case '\\':case '$':case '`':
  110. X         goto nodelim;
  111. X          case '}':
  112. X         if(got<=NORMAL_TEXT&&bracelev||
  113. X            got==DOUBLE_QUOTED&&bracelev>qbracelev)
  114. X            goto nodelim;
  115. X        }
  116. X       if(got>NORMAL_TEXT)
  117. Xnoesc:          *p++='\\';        /* nothing to escape, just echo both */
  118. X       break;
  119. X    case '`':
  120. X       if(got==SINGLE_QUOTED)
  121. X          goto nodelim;
  122. X       for(startb=p;;)                   /* mark your position */
  123. X        { switch(i=fgetc())             /* copy till next backquote */
  124. X           { case '"':
  125. X            if(got!=DOUBLE_QUOTED)     /* missing closing backquote? */
  126. X               break;
  127. Xforcebquote:     case EOF:case '`':
  128. X            if(skiprc)
  129. X               *(p=startb)='\0';
  130. X            else
  131. X             { int osh=sh;
  132. X               *p='\0';
  133. X               if(!(sh=!!strpbrk(startb,shellmetas)))
  134. X            { const char*save=sgetcp,*sAll_args;
  135. X              sgetcp=p=tstrdup(startb);sAll_args=All_args;
  136. X              readparse(startb,sgetc,0);All_args=sAll_args;
  137. X#ifndef GOT_bin_test
  138. X              if(!strcmp(test,startb))
  139. X                 strcpy(startb,p),sh=1;    /* oops, `test' found */
  140. X#endif
  141. X              free(p);sgetcp=save;               /* chopped up */
  142. X            }        /* drop source buffer, read from program */
  143. X               startb=fromprog(
  144. X            p=startb,startb,(size_t)(buf-startb+linebuf-3));
  145. X               sh=osh;                       /* restore sh */
  146. X             }
  147. X            if(got!=DOUBLE_QUOTED)
  148. X             { i=0;startb=p;
  149. X               goto simplsplit;                  /* split it up */
  150. X             }
  151. X            if(i=='"'||got<=SKIPPING_SPACE)   /* missing closing ` ? */
  152. X               got=NORMAL_TEXT;
  153. X            p=startb;
  154. X            goto loop;
  155. X         case '\\':
  156. X            switch(i=fgetc())
  157. X             { case EOF:nlog(unexpeof);
  158. X              goto forcebquote;
  159. X               case '\n':
  160. X              continue;
  161. X               case '"':
  162. X              if(got!=DOUBLE_QUOTED)
  163. X                 break;
  164. X               case '\\':case '$':case '`':
  165. X              goto escaped;
  166. X             }
  167. X            *p++='\\';
  168. X           }
  169. Xescaped:      *p++=i;
  170. X        }
  171. X    case '"':
  172. X       switch(got)
  173. X        { case DOUBLE_QUOTED:
  174. X         if(qbracelev<bracelev)           /* still inside a ${...}? */
  175. X          case SINGLE_QUOTED:
  176. X            goto nodelim;                 /* nonsense */
  177. X         got=NORMAL_TEXT;
  178. X         continue;                    /* closing " */
  179. X        }
  180. X       qbracelev=bracelev;got=DOUBLE_QUOTED;
  181. X       continue;                        /* opening " */
  182. X    case '\'':
  183. X       switch(got)
  184. X        { case DOUBLE_QUOTED:
  185. X         goto nodelim;
  186. X          case SINGLE_QUOTED:got=NORMAL_TEXT;
  187. X         continue;                    /* closing ' */
  188. X        }
  189. X       got=SINGLE_QUOTED;
  190. X       continue;                        /* opening ' */
  191. X    case '}':
  192. X       if(got<=NORMAL_TEXT&&bracelev||
  193. X          got==DOUBLE_QUOTED&&bracelev>qbracelev)
  194. X        { bracelev--;
  195. X          if(skipback&&bracelev==skipbracelev)
  196. X           { skiprc--;p=skipback;skipback=0;startb=(char*)oldstartb;
  197. X         got=bracegot;
  198. X         goto closebrace;
  199. X           }
  200. X          continue;
  201. X        }
  202. X       goto nodelim;
  203. X    case '#':
  204. X       if(got>SKIPPING_SPACE)        /* comment at start of word? */
  205. X          break;
  206. X       while((i=fgetc())!=EOF&&i!='\n');            /* skip till EOL */
  207. X       goto ready;
  208. X    case '$':
  209. X       if(got==SINGLE_QUOTED)
  210. X          break;
  211. X       startb=buf2;
  212. X       switch(i=fgetc())
  213. X        { case EOF:*p++='$';
  214. X         goto ready;
  215. X          case '@':
  216. X         if(got!=DOUBLE_QUOTED)
  217. X            goto normchar;
  218. X         if(!skiprc)          /* don't do it while skipping (braces) */
  219. X            All_args=p;
  220. X         continue;
  221. X          case '{':                          /* ${name} */
  222. X         while(EOF!=(i=fgetc())&&alphanum(i))
  223. X            *startb++=i;
  224. X         *startb='\0';startb=(char*)evalenv();
  225. X         if(numeric(*buf2)&&buf2[1])
  226. X            goto badsub;
  227. X         switch(i)
  228. X          { default:
  229. X               goto badsub;
  230. X            case ':':
  231. X               switch(i=fgetc())
  232. X            { default:
  233. Xbadsub:                 nlog("Bad substitution of");logqnl(buf2);
  234. X                 continue;
  235. X              case '-':
  236. X                 if(startb&&*startb)
  237. X                goto noalt;
  238. X                 goto doalt;
  239. X              case '+':
  240. X                 if(startb&&*startb)
  241. X                goto doalt;
  242. X                 startb=0;
  243. X            }
  244. X            case '+':
  245. X               if(startb)
  246. X              goto doalt;
  247. X               goto noalt;
  248. X            case '-':
  249. X               if(startb)
  250. Xnoalt:              if(!skiprc)
  251. X               { skiprc++;skipback=p;skipbracelev=bracelev;
  252. X                 oldstartb=startb;bracegot=got;
  253. X               }
  254. Xdoalt:               bracelev++;
  255. X               continue;
  256. X            case '}':
  257. Xclosebrace:           if(!startb)
  258. X              startb="";
  259. X          }
  260. X         goto ibreak;                      /* $$ =pid */
  261. X          case '$':ultstr(0,(unsigned long)thepid,p);
  262. X         goto ieofstr;
  263. X          case '?':ltstr(0,(long)lexitcode,p);
  264. X         goto ieofstr;
  265. X          case '#':ultstr(0,(unsigned long)crestarg,p);
  266. X         goto ieofstr;
  267. X          case '=':ltstr(0,lastscore,p);
  268. Xieofstr:     i='\0';
  269. X         goto eofstr;
  270. X          case '_':startb=incnamed?incnamed->ename:"";
  271. X         goto ibreak;
  272. X          case '-':startb=(char*)tgetenv(lastfolder); /* $- =$LASTFOLDER */
  273. Xibreak:         i='\0';break;
  274. X          default:
  275. X         if(numeric(i))               /* $n positional argument */
  276. X          { *startb++=i;i='\0';
  277. X            goto finsb;
  278. X          }
  279. X         if(alphanum(i))                    /* $name */
  280. X          { do *startb++=i;
  281. X            while(EOF!=(i=fgetc())&&alphanum(i));
  282. X            if(i==EOF)
  283. X            i='\0';
  284. Xfinsb:            *startb='\0';
  285. X            if(!(startb=(char*)evalenv()))
  286. X               startb="";
  287. X            break;
  288. X          }
  289. Xnormchar:     *p++='$';
  290. X         goto newchar;                   /* not a substitution */
  291. X        }
  292. X       if(got!=DOUBLE_QUOTED)
  293. Xsimplsplit: { if(sarg)
  294. X         goto copyit;
  295. X          for(;;startb++)          /* simply split it up in arguments */
  296. X           { switch(*startb)
  297. X          { case ' ':case '\t':case '\n':
  298. X               if(got<=SKIPPING_SPACE)
  299. X              continue;
  300. X               *p++='\0';got=SKIPPING_SPACE;
  301. X               continue;
  302. X            case '\0':
  303. X               goto eeofstr;
  304. X          }
  305. X         *p++= *startb;got=NORMAL_TEXT;
  306. X           }
  307. X        }
  308. X       else
  309. Xcopyit:        { strcpy(p,startb);                   /* simply copy it */
  310. Xeofstr:          if(got<=SKIPPING_SPACE)        /* can only occur if sarg!=0 */
  311. X         got=NORMAL_TEXT;
  312. X          p=strchr(p,'\0');
  313. X        }
  314. Xeeofstr:   if(i)                 /* already read next character? */
  315. X          goto newchar;
  316. X       continue;
  317. X    case ' ':case '\t':
  318. X       switch(got)
  319. X        { case NORMAL_TEXT:
  320. X         if(sarg==1)
  321. X            goto ready;        /* already fetched a single argument */
  322. X         got=SKIPPING_SPACE;*p++=sarg?' ':'\0';     /* space or \0 sep. */
  323. X          case NOTHING_YET:case SKIPPING_SPACE:
  324. X         continue;                       /* skip space */
  325. X        }
  326. X    case '\n':
  327. X       if(got<=NORMAL_TEXT)
  328. X          goto ready;                /* EOL means we're ready */
  329. X      }
  330. Xnodelim:
  331. X     *p++=i;                       /* ah, a normal character */
  332. X     if(got<=SKIPPING_SPACE)         /* should we bother to change mode? */
  333. X    got=NORMAL_TEXT;
  334. X   }
  335. X}
  336. X
  337. Xvoid ltstr(minwidth,val,dest)const int minwidth;const long val;char*dest;
  338. X{ if(val<0)
  339. X   { *dest=' ';ultstr(minwidth-1,-val,dest+1);
  340. X     while(*++dest==' ');             /* look for the first non-space */
  341. X     dest[-1]='-';                  /* replace it with a minus */
  342. X   }
  343. X  else
  344. X     ultstr(minwidth,val,dest);                /* business as usual */
  345. X}
  346. X
  347. Xdouble stod(str,ptr)const char*str;const char**const ptr;
  348. X{ int sign,any;unsigned i;char*chp;double acc,fracc;
  349. X  fracc=1;acc=any=sign=0;
  350. X  switch(*(chp=skpspace(str)))                     /* the sign */
  351. X   { case '-':sign=1;
  352. X     case '+':chp++;
  353. X   }
  354. X  while((i=(unsigned)*chp++-'0')<=9)         /* before the decimal point */
  355. X     acc=acc*10+i,any=1;
  356. X  switch(i)
  357. X   { case (unsigned)'.'-'0':case (unsigned)','-'0':
  358. X    while(fracc/=10,(i=(unsigned)*chp++-'0')<=9)  /* the fractional part */
  359. X       acc+=fracc*i,any=1;
  360. X   }
  361. X  if(ptr)
  362. X     *ptr=any?chp-1:str;
  363. X  return sign?-acc:acc;
  364. X}
  365. X
  366. Xstatic struct dynstring*myenv;
  367. Xstatic char**lastenv;
  368. X                  /* smart putenv, the way it was supposed to be */
  369. Xconst char*sputenv(a)const char*const a;
  370. X{ static alloced;size_t eq,i;int remove;const char*split;char**preenv;
  371. X  struct dynstring*curr,**last;
  372. X  yell("Assigning",a);remove=0;
  373. X  if(!(split=strchr(a,'=')))               /* assignment or removal? */
  374. X     remove=1,split=strchr(a,'\0');
  375. X  eq=split-a;                                /* is it */
  376. X  for(curr= *(last= &myenv);curr;curr= *(last= &curr->enext))  /* one I made */
  377. X     if(!strncmp(a,curr->ename,eq)&&((char*)curr->ename)[eq]=='=')
  378. X      { split=curr->ename;*last=curr->enext;free(curr);         /* earlier? */
  379. X    for(preenv=environ;*preenv!=split;preenv++);
  380. X    goto wipenv;
  381. X      }
  382. X  for(preenv=environ;*preenv;preenv++)            /* is it in the standard */
  383. X     if(!strncmp(a,*preenv,eq)&&(*preenv)[eq]=='=')         /* environment? */
  384. Xwipenv:
  385. X      { while(*preenv=preenv[1])   /* wipe this entry out of the environment */
  386. X       preenv++;
  387. X    break;
  388. X      }
  389. X  i=(preenv-environ+2)*sizeof*environ;
  390. X  if(alloced)           /* have we ever alloced the environ array before? */
  391. X     environ=realloc(environ,i);
  392. X  else
  393. X     alloced=1,environ=tmemmove(malloc(i),environ,i-sizeof*environ);
  394. X  if(!remove)          /* if not remove, then add it to both environments */
  395. X   { for(preenv=environ;*preenv;preenv++);
  396. X     preenv[1]=0;*(lastenv=preenv)=(char*)(split=newdynstring(&myenv,a));
  397. X     return split+eq+1;
  398. X   }
  399. X  return "";
  400. X}
  401. X                /* between calling primeStdout() and retStdout() */
  402. Xvoid primeStdout P((void))        /* *no* environment changes are allowed! */
  403. X{ char*p;
  404. X  if((p=strchr(buf,'\0'))[-1]!='=')           /* does it end in an '='? */
  405. X     *p='=',p[1]='\0';                    /* make sure it does */
  406. X  sputenv(buf);Stdout=(char*)myenv;
  407. X  Stdfilled=ioffsetof(struct dynstring,ename[0])+strlen(myenv->ename);
  408. X}
  409. X
  410. Xvoid retStdout(newmyenv)char*const newmyenv;    /* see note on primeStdout() */
  411. X{ if(newmyenv[Stdfilled-1]=='\n')           /* strip one trailing newline */
  412. X     Stdfilled--;
  413. X  newmyenv[Stdfilled]='\0';*lastenv=(myenv=(struct dynstring*)newmyenv)->ename;
  414. X  Stdout=0;
  415. X}
  416. X
  417. Xvoid postStdout P((void))         /* throw it into the keyword parser */
  418. X{ const char*p;size_t i;
  419. X  p= *lastenv;tmemmove(buf,p,i=strchr(p,'=')-p);buf[i]='\0';asenv(p+i+1);
  420. X}
  421. END_OF_FILE
  422.   if test 11872 -ne `wc -c <'procmail-3.03/src/goodies.c'`; then
  423.     echo shar: \"'procmail-3.03/src/goodies.c'\" unpacked with wrong size!
  424.   fi
  425.   # end of 'procmail-3.03/src/goodies.c'
  426. fi
  427. if test -f 'procmail-3.03/src/procmail.c' -a "${1}" != "-c" ; then 
  428.   echo shar: Will not clobber existing file \"'procmail-3.03/src/procmail.c'\"
  429. else
  430.   echo shar: Extracting \"'procmail-3.03/src/procmail.c'\" \(38724 characters\)
  431.   sed "s/^X//" >'procmail-3.03/src/procmail.c' <<'END_OF_FILE'
  432. X/************************************************************************
  433. X *    procmail - The autonomous mail processor            *
  434. X *                                    *
  435. X *    It has been designed to be able to be run suid root and (in    *
  436. X *    case your mail spool area is *not* world writable) sgid        *
  437. X *    mail (or daemon), without creating security holes.        *
  438. X *                                    *
  439. X *    Seems to be perfect.                        *
  440. X *                                    *
  441. X *    Copyright (c) 1990-1994, S.R. van den Berg, The Netherlands    *
  442. X *    #include "../README"                        *
  443. X ************************************************************************/
  444. X#ifdef RCS
  445. Xstatic /*const*/char rcsid[]=
  446. X "$Id: procmail.c,v 1.86 1994/06/30 13:52:34 berg Exp $";
  447. X#endif
  448. X#include "../patchlevel.h"
  449. X#include "procmail.h"
  450. X#include "acommon.h"
  451. X#include "sublib.h"
  452. X#include "robust.h"
  453. X#include "shell.h"
  454. X#include "misc.h"
  455. X#include "pipes.h"
  456. X#include "common.h"
  457. X#include "cstdio.h"
  458. X#include "exopen.h"
  459. X#include "regexp.h"
  460. X#include "mcommon.h"
  461. X#include "goodies.h"
  462. X#include "locking.h"
  463. X#include "mailfold.h"
  464. X
  465. Xstatic const char orgmail[]="ORGMAIL",*const nullp,From_[]=FROM,
  466. X exflags[]=RECFLAGS,drcfile[]="Rcfile:",systm_mbox[]=SYSTEM_MBOX,
  467. X pmusage[]=PM_USAGE,*etcrc=ETCRC,misrecpt[]="Missing recipient\n",
  468. X extrns[]="Extraneous ",ignrd[]=" ignored\n",conflicting[]="Conflicting ",
  469. X pardir[]=chPARDIR,suppressed[]=" suppressed\n",
  470. X insufprivs[]="Insufficient privileges\n";
  471. Xchar*buf,*buf2,*loclock,*tolock;
  472. Xconst char shell[]="SHELL",lockfile[]="LOCKFILE",newline[]="\n",binsh[]=BinSh,
  473. X unexpeof[]="Unexpected EOL\n",*const*gargv,*const*restargv= &nullp,*sgetcp,
  474. X *rcfile=PROCMAILRC,dirsep[]=DIRSEP,devnull[]=DevNull,lgname[]="LOGNAME",
  475. X executing[]="Executing",oquote[]=" \"",cquote[]="\"\n",procmailn[]="procmail",
  476. X whilstwfor[]=" whilst waiting for ",home[]="HOME",maildir[]="MAILDIR",
  477. X host[]="HOST",*defdeflock,*argv0="",errwwriting[]="Error while writing to",
  478. X slogstr[]="%s \"%s\"";
  479. Xchar*Stdout;
  480. Xint retval=EX_CANTCREAT,retvl2=EX_OK,sh,pwait,lcking,rcstate,rc= -1,ignwerr,
  481. X lexitcode=EX_OK,asgnlastf,accspooldir,crestarg,skiprc,savstdout;
  482. Xsize_t linebuf=mx(DEFlinebuf+XTRAlinebuf,STRLEN(systm_mbox)<<1);
  483. Xvolatile int nextexit;                   /* if termination is imminent */
  484. Xpid_t thepid;
  485. Xlong filled,lastscore;           /* the length of the mail, and the last score */
  486. Xchar*themail,*thebody;                /* the head and body of the mail */
  487. Xuid_t uid;
  488. Xgid_t gid,sgid;
  489. X
  490. X#if 0
  491. X#define wipetcrc()    (etcrc&&(etcrc=0,closerc(),1))
  492. X#else
  493. Xstatic int wipetcrc P((void))      /* stupid function to avoid a compiler bug */
  494. X{ if(etcrc)
  495. X   { etcrc=0;closerc();
  496. X     return 1;
  497. X   }
  498. X  return 0;
  499. X}
  500. X#endif
  501. X
  502. Xmain(argc,argv)const char*const argv[];
  503. X{ register char*chp,*chp2;register i;int suppmunreadable,mailfilter;
  504. X#if 0                /* enable this if you want to trace procmail */
  505. X  kill(getpid(),SIGSTOP);/*raise(SIGSTOP);*/
  506. X#endif
  507. X  newid();
  508. X  ;{ int presenviron,Deliverymode,override;char*fromwhom=0;
  509. X     const char*idhint=0;gid_t egid=getegid();
  510. X     Deliverymode=mailfilter=override=0;
  511. X     openlog(procmailn,LOG_PID,LOG_MAIL);          /* for the syslogd */
  512. X     if(argc)                   /* sanity check, any argument at all? */
  513. X      { Deliverymode=strncmp(lastdirsep(argv0=argv[0]),procmailn,
  514. X     STRLEN(procmailn));
  515. X    for(presenviron=argc=0;(chp2=(char*)argv[++argc])&&*chp2=='-';)
  516. X       for(;;)                       /* processing options */
  517. X        { switch(*++chp2)
  518. X           { case VERSIONOPT:elog(VERSION);
  519. X            return EX_OK;
  520. X         case HELPOPT1:case HELPOPT2:elog(pmusage);elog(PM_HELP);
  521. X            elog(PM_QREFERENCE);
  522. X            return EX_USAGE;
  523. X         case PRESERVOPT:presenviron=1;
  524. X            continue;
  525. X         case MAILFILTOPT:mailfilter=1;
  526. X            continue;
  527. X         case OVERRIDEOPT:override=1;
  528. X            continue;
  529. X         case TEMPFAILOPT:retval=EX_TEMPFAIL;
  530. X            continue;
  531. X         case FROMWHOPT:case ALTFROMWHOPT:
  532. X            if(*++chp2)
  533. X               fromwhom=chp2;
  534. X            else if(chp2=(char*)argv[argc+1])
  535. X               argc++,fromwhom=chp2;
  536. X            else
  537. X               nlog("Missing name\n");
  538. X            break;
  539. X         case ARGUMENTOPT:
  540. X          { static const char*argv1[]={"",0};
  541. X            if(*++chp2)
  542. X               goto setarg;
  543. X            else if(chp2=(char*)argv[argc+1])
  544. X             { argc++;
  545. Xsetarg:               *argv1=chp2;restargv=argv1;crestarg=1;
  546. X             }
  547. X            else
  548. X               nlog("Missing argument\n");
  549. X            break;
  550. X          }
  551. X         case DELIVEROPT:
  552. X            if(!*(chp= ++chp2)&&!(chp=(char*)argv[++argc]))
  553. X             { nlog(misrecpt);
  554. X               break;
  555. X             }
  556. X            else
  557. X             { Deliverymode=1;
  558. X               goto last_option;
  559. X             }
  560. X         case '-':
  561. X            if(!*chp2)
  562. X             { argc++;
  563. X               goto last_option;
  564. X             }
  565. X         default:nlog("Unrecognised options:");logqnl(chp2);
  566. X            elog(pmusage);elog("Processing continued\n");
  567. X         case '\0':;
  568. X           }
  569. X          break;
  570. X        }
  571. X      }
  572. X     if(Deliverymode&&!(chp=chp2))
  573. X    nlog(misrecpt),Deliverymode=0;
  574. Xlast_option:
  575. X     if(Deliverymode&&presenviron)
  576. X      { presenviron=0;                       /* -d disables -p */
  577. X    goto conflopt;
  578. X      }
  579. X     if(mailfilter)
  580. X      { if(Deliverymode)                 /* -d supersedes -m */
  581. X     { mailfilter=0;
  582. X       goto conflopt;
  583. X     }
  584. X    if(crestarg)                     /* -m will supersede -a */
  585. Xconflopt:  nlog(conflicting),elog("options\n"),elog(pmusage);
  586. X      }
  587. X     if(!Deliverymode)
  588. X    idhint=getenv(lgname);
  589. X     if(!presenviron)                     /* drop the environment */
  590. X      { const char**emax=(const char**)environ,*const*ep,*const*kp;
  591. X    static const char*const keepenv[]=KEEPENV;
  592. X    for(kp=keepenv;*kp;kp++)             /* preserve a happy few */
  593. X       for(i=strlen(*kp),ep=emax;chp2=(char*)*ep;ep++)
  594. X          if(!strncmp(*kp,chp2,(size_t)i)&&(chp2[i]=='='||chp2[i-1]=='_'))
  595. X           { *emax++=chp2;
  596. X         break;
  597. X           }
  598. X    *emax=0;                        /* drop the rest */
  599. X      }
  600. X#ifdef LD_ENV_FIX
  601. X     ;{ const char**emax=(const char**)environ,**ep;
  602. X    static const char ld_[]="LD_";
  603. X    for(ep=emax;*emax;emax++);      /* find the end of the environment */
  604. X    while(*ep)
  605. X       if(!strncmp(ld_,*ep++,STRLEN(ld_)))           /* it starts with LD_ */
  606. X          *--ep= *--emax,*emax=0;            /* copy from the end */
  607. X      }
  608. X#endif /* LD_ENV_FIX */
  609. X     ;{ const struct passwd*pass,*passinvk;struct passwd spassinvk;int privs;
  610. X    uid_t euid=geteuid();
  611. X    spassinvk.pw_name=spassinvk.pw_dir=spassinvk.pw_shell=0;
  612. X    passinvk=savepass(&spassinvk,uid=getuid());privs=1;gid=getgid();
  613. X    ;{ static const char*const trusted_ids[]=TRUSTED_IDS;
  614. X       if(*trusted_ids&&uid!=euid)
  615. X        { struct group*grp;const char*const*kp;
  616. X          if(passinvk)              /* check out the invoker's uid */
  617. X         for(chp2=passinvk->pw_name,kp=trusted_ids;*kp;)
  618. X            if(!strcmp(chp2,*kp++)) /* is it amongst the privileged? */
  619. X               goto privileged;
  620. X          if(grp=getgrgid(gid))          /* check out the invoker's gid */
  621. X         for(chp2=grp->gr_name,kp=trusted_ids;*kp;)
  622. X            if(!strcmp(chp2,*kp++))   /* is it among the privileged? */
  623. X               goto privileged;
  624. X          privs=0;
  625. X        }
  626. X     }
  627. Xprivileged:                       /* move stdout out of the way */
  628. X    endgrent();doumask(INIT_UMASK);savstdout=rdup(STDOUT);
  629. X    fclose(stdout);rclose(STDOUT);            /* just to make sure */
  630. X    if(0>opena(devnull))
  631. X     { writeerr(devnull);syslog(LOG_EMERG,slogstr,errwwriting,devnull);
  632. X       return EX_OSFILE;                 /* couldn't open stdout */
  633. X     }
  634. X#ifdef console
  635. X    opnlog(console);
  636. X#endif
  637. X    setbuf(stdin,(char*)0);buf=malloc(linebuf);buf2=malloc(linebuf);
  638. X#ifdef SIGXCPU
  639. X    signal(SIGXCPU,SIG_IGN);signal(SIGXFSZ,SIG_IGN);
  640. X#endif
  641. X#ifdef SIGLOST
  642. X    signal(SIGLOST,SIG_IGN);
  643. X#endif
  644. X#if DEFverbose
  645. X    verboff();verbon();
  646. X#else
  647. X    verbon();verboff();
  648. X#endif
  649. X    signal(SIGPIPE,SIG_IGN);qsignal(SIGTERM,srequeue);
  650. X    qsignal(SIGINT,sbounce);qsignal(SIGHUP,sbounce);
  651. X    qsignal(SIGQUIT,slose);signal(SIGALRM,(void(*)())ftimeout);
  652. X    ultstr(0,(unsigned long)uid,buf);
  653. X    chp2=!passinvk||!*passinvk->pw_name?buf:passinvk->pw_name;
  654. X    filled=0;
  655. X    ;{ const char*fwhom;size_t lfr,linv;int tstamp;
  656. X       tstamp=fromwhom&&*fromwhom==REFRESH_TIME&&!fromwhom[1];fwhom=chp2;
  657. X       if(fromwhom&&!tstamp)
  658. X        { if(!privs&&!strcmp(fromwhom,fwhom))
  659. X         privs=1; /* if -f user is the same as the invoker, allow it */
  660. X          fwhom=fromwhom;
  661. X        }
  662. X       thebody=themail=
  663. X        malloc(2*linebuf+(lfr=strlen(fwhom))+(linv=strlen(chp2)));
  664. X       if(Deliverymode||fromwhom)  /* need to peek for a leading From_ ? */
  665. X        { char*rstart;int r;static const char Fakefield[]=FAKE_FIELD;
  666. X          ;{ time_t t;                 /* the current time */
  667. X         t=time((time_t*)0);strcat(strcpy(buf2,"  "),ctime(&t));
  668. X           }
  669. X          lfr+=STRLEN(From_)+(r=strlen(buf2));
  670. X          if(tstamp)
  671. X         tstamp=r;               /* save time stamp length */
  672. X          if(privs)                     /* privileged user? */
  673. X         linv=0;         /* yes, so no need to insert >From_ */
  674. X          else
  675. X         linv+=STRLEN(Fakefield)+r;
  676. X          while(1==(r=rread(STDIN,themail,1))&&*themail=='\n');
  677. X          i=0;rstart=themail;                 /* skip garbage */
  678. X          if(r>0&&STRLEN(From_)<=(i=rread(          /* is it a From_ line? */
  679. X           STDIN,rstart+1,(int)(linebuf-2-1))+1)&&eqFrom_(themail))
  680. X           { rstart[i]='\0';
  681. X         if(!(rstart=strchr(rstart,'\n')))
  682. X          { do                     /* drop long From_ line */
  683. X             { if((i=rread(STDIN,themail,(int)(linebuf-2)))<=0)
  684. X              break;
  685. X               themail[i]='\0';          /* terminate it for strchr */
  686. X             }
  687. X            while(!(rstart=strchr(themail,'\n')));
  688. X            i=rstart?i-(++rstart-themail):0;
  689. X            goto no_from;
  690. X          }
  691. X         ;{ size_t tfrl;
  692. X            i-=tfrl= ++rstart-themail;         /* demarcate From_ line */
  693. X            if(Deliverymode&&override&&!privs)
  694. X             { if(verbose)          /* discard the bogus From_ */
  695. X              nlog(insufprivs);
  696. X               goto no_from;
  697. X             }
  698. X            if(tstamp)
  699. X               lfr=findtstamp(themail+STRLEN(From_),rstart)
  700. X            -themail+tstamp;
  701. X            else if(!fromwhom)           /* leave the From_ line alone */
  702. X               if(linv)                      /* fake alert? */
  703. X              lfr=tfrl;  /* yes, so separate From_ from the rest */
  704. X               else
  705. X              lfr=0,i+=tfrl;    /* no, tack it onto the rest */
  706. X          }
  707. X           }
  708. X          else
  709. Xno_from:       { tstamp=0;       /* no existing From_, so nothing to stamp */
  710. X         if(fromwhom&&override&&!privs)
  711. X          { if(verbose)
  712. X               nlog(insufprivs);
  713. X            fromwhom=0;                  /* ignore the bogus -f */
  714. X          }
  715. X         if(!fromwhom)                      /* no -f ? */
  716. X            linv=0;              /* then it can't be a fake */
  717. X           }
  718. X          filled=lfr+linv+i;            /* From_ + >From_ + rest */
  719. X          if(lfr||linv)         /* move read text beyond our From_ line */
  720. X           { r= *rstart;tmemmove(themail+lfr+linv,rstart,i);
  721. X         rstart=themail+lfr;          /* skip the From_ line, if any */
  722. X         if(!linv)                    /* no fake alert */
  723. X          { rstart[-tstamp]='\0';           /* where do we append */
  724. X            if(!tstamp)         /* no timestamp, so generate it all */
  725. X               strcat(strcpy(themail,From_),fwhom);    /* From user */
  726. X          }
  727. X         else
  728. X          { if(lfr)            /* did we skip a From_ line? */
  729. X               if(tstamp)     /* copy the timestamp over the tail */
  730. X              strcpy(rstart-tstamp,buf2);
  731. X               else if(fromwhom)         /* whole new From_? */
  732. X              strcat(strcat(strcpy(themail,From_),fwhom),buf2);
  733. X            strcat(strcpy(rstart,Fakefield),chp2);     /* fake alert */
  734. X          }              /* overwrite the trailing \0 again */
  735. X         strcat(themail,buf2);themail[lfr+linv]=r;
  736. X           }
  737. X        }
  738. X     }
  739. X    readmail(0,0L);                  /* read in the mail completely */
  740. X    if(Deliverymode)
  741. X       do              /* chp should point to the first recipient */
  742. X        { chp2=chp;
  743. X#ifndef NO_USER_TO_LOWERCASE_HACK
  744. X          for(;*chp;chp++)          /* kludge recipient into lowercase */
  745. X         if((unsigned)*chp-'A'<='Z'-'A')
  746. X            *chp+='a'-'A';     /* getpwnam might be case sensitive */
  747. X#endif
  748. X          if(argv[++argc])              /* more than one recipient */
  749. X         if(pidchild=sfork())
  750. X          { if(forkerr(pidchild,procmailn)||waitfor(pidchild)!=EX_OK)
  751. X               retvl2=retval;
  752. X            pidchild=0;              /* loop for the next recipient */
  753. X          }
  754. X         else
  755. X          { newid();
  756. X            while(argv[++argc]);    /* skip till end of command line */
  757. X          }
  758. X        }
  759. X       while(chp=(char*)argv[argc]);
  760. X    gargv=argv+argc;suppmunreadable=verbose; /* save it for nextrcfile() */
  761. X    if(Deliverymode)
  762. X     { if(!(pass=getpwnam(chp2)))  /* chp2 should point to the recipient */
  763. X        { static const char unkuser[]="Unknown user";
  764. X          nlog(unkuser);logqnl(chp2);
  765. X          syslog(LOG_ERR,slogstr,unkuser,chp2);
  766. X          return EX_NOUSER;
  767. X        }
  768. X       if(enoughprivs(passinvk,euid,egid,pass->pw_uid,pass->pw_gid))
  769. X          goto Setuser;
  770. X       nlog(insufprivs);
  771. X     }
  772. X    else
  773. X     { suppmunreadable=nextrcfile();
  774. X       if(presenviron)              /* preserving the environment? */
  775. X          etcrc=0;                    /* don't read etcrc then */
  776. X       if(suppmunreadable)                 /* command-line rcfile? */
  777. X          etcrc=0,scomsat=DEFcomsat;      /* forget etcrc and comsat */
  778. X       if(mailfilter)
  779. X        { if(!suppmunreadable)
  780. X           { nlog("Missing rcfile\n");
  781. X         return EX_NOINPUT;
  782. X           }
  783. X#ifdef ETCRCS
  784. X          ;{ static const char etcrcs[]=ETCRCS;
  785. X         if(!strncmp(etcrcs,rcfile,STRLEN(etcrcs)))
  786. X          { struct stat stbuf; /* path starts with /etc/procmailrcs/ */
  787. X           /*
  788. X            *    although the filename starts with /etc/procmailrcs/
  789. X            *    we will now check if it does not contain any backward
  790. X            *    references which would allow it to escape the secure
  791. X            *    tree; look for /../ sequences
  792. X            */
  793. X            for(chp=(char*)rcfile+STRLEN(etcrcs)-1;
  794. X            chp;               /* any devious embedded /../? */
  795. X            chp=strpbrk(chp,dirsep))
  796. X               if(!strncmp(pardir,++chp,STRLEN(pardir))&&
  797. X              (chp+=STRLEN(pardir),strchr(dirsep,*chp)))
  798. X              goto nospecial;      /* yes, security violation */
  799. X           /*
  800. X            *    so far, so good, now verify the credentials down to the
  801. X            *    last bit
  802. X            */
  803. X            if(presenviron||              /* -p is dangerous */
  804. X               suppmunreadable!=2||   /* so are variable assignments */
  805. X               lstat(rcfile,&stbuf)||        /* it seems to exist */
  806. X               !enoughprivs(passinvk,euid,egid,stbuf.st_uid,
  807. X            stbuf.st_gid)||           /* can we do this at all? */
  808. X               S_ISDIR(stbuf.st_mode)||          /* no directories! */
  809. X               !savepass(&spassinvk,stbuf.st_uid))   /* user exists? */
  810. Xnospecial:         { static const char densppr[]=
  811. X            "Denying special privileges for";
  812. X               nlog(densppr);logqnl(rcfile);
  813. X               syslog(LOG_ALERT,slogstr,densppr,rcfile);
  814. X             }
  815. X            else
  816. X               passinvk= &spassinvk;        /* no security violation */
  817. X          }                      /* accept new identity */
  818. X           }
  819. X#endif
  820. X        }
  821. X     }
  822. X    if(idhint&&(pass=getpwnam(idhint))&&
  823. X        passinvk&&passinvk->pw_uid==pass->pw_uid||
  824. X       (pass=passinvk))
  825. X      /*
  826. X       *    set preferred uid to the intended recipient
  827. X       */
  828. XSetuser: { gid=pass->pw_gid;uid=pass->pw_uid;
  829. X       setdef(lgname,chp= *pass->pw_name?pass->pw_name:buf);
  830. X       setdef(home,pass->pw_dir);
  831. X       if(euid==ROOT_uid)
  832. X          initgroups(chp,gid);
  833. X       endgrent();setdef(shell,*pass->pw_shell?pass->pw_shell:binsh);
  834. X     }
  835. X    else         /* user could not be found, set reasonable defaults */
  836. X      /*
  837. X       *    to prevent security holes, drop any privileges now
  838. X       */
  839. X     { setdef(lgname,buf);setdef(home,RootDir);setdef(shell,binsh);
  840. X       setids();
  841. X     }
  842. X    endpwent();
  843. X    if(passinvk)
  844. X     { free(spassinvk.pw_name);free(spassinvk.pw_dir);
  845. X       free(spassinvk.pw_shell);
  846. X     }
  847. X      }
  848. X     setdef(orgmail,systm_mbox);setdef(maildir,DEFmaildir);
  849. X     if(!presenviron||!mailfilter)
  850. X      { setdef(host,hostname());sputenv(lastfolder);sputenv(exitcode);
  851. X    initdefenv();
  852. X    ;{ const char*const*kp;static const char*const prestenv[]=PRESTENV;
  853. X       for(kp=prestenv;*kp;)    /* preset some environment variables */
  854. X        { strcpy((char*)(sgetcp=buf2),*kp++);readparse(buf,sgetc,2);
  855. X          sputenv(buf);
  856. X        }
  857. X     }
  858. X      }        /* set fdefault and find out the name of our system lockfile */
  859. X     sgetcp=fdefault;readparse(buf,sgetc,2);fdefault=tstrdup(buf);
  860. X     strcpy(chp2=strchr(strcpy(buf,chp=(char*)getenv(orgmail)),'\0'),lockext);
  861. X     defdeflock=tstrdup(buf);*chp2='\0';buf[i=lastdirsep(chp)-chp]='\0';
  862. X     ;{ struct stat stbuf;               /* strip off the basename */
  863. X    sgid=gid;                    /* presumed innocent */
  864. X       /*
  865. X    *    do we need sgidness to access the mail-spool directory/files?
  866. X    */
  867. X    if(!stat(buf,&stbuf))
  868. X     { accspooldir=!!(stbuf.st_mode&(S_IWGRP|S_IWOTH))<<1|
  869. X        uid==stbuf.st_uid;
  870. X       if((uid!=stbuf.st_uid&&
  871. X        stbuf.st_gid==egid||
  872. X           (rcstate=rc_NOSGID,0))&&
  873. X          (stbuf.st_mode&(S_IWGRP|S_IXGRP|S_IWOTH))==(S_IWGRP|S_IXGRP))
  874. X        { doumask(INIT_UMASK&~S_IRWXG);       /* make it group-writable */
  875. X          goto keepgid;
  876. X        }
  877. X       else if(stbuf.st_mode&S_ISGID)
  878. Xkeepgid:      sgid=stbuf.st_gid;   /* keep the gid from the parent directory */
  879. X     }
  880. X       /*
  881. X    *    check if the default-mailbox-lockfile is owned by the
  882. X    *    recipient, if not, mark it for further investigation, it
  883. X    *    might need to be removed
  884. X    */
  885. X    for(;;)
  886. X     { ;{ int mboxstat;
  887. X          static const char renbogus[]="Renamed bogus \"%s\" into \"%s\"",
  888. X           renfbogus[]="Couldn't rename bogus \"%s\" into \"%s\"";
  889. X          ;{ int goodlock;
  890. X         if(!(goodlock=lstat(defdeflock,&stbuf)||stbuf.st_uid==uid))
  891. X            ultoan((unsigned long)stbuf.st_ino,      /* i-node numbered */
  892. X             strchr(strcpy(buf+i,BOGUSprefix),'\0'));
  893. X        /*
  894. X         *    check if the original/default mailbox of the recipient
  895. X         *    exists, if it does, perform some security checks on it
  896. X         *    (check if it's a regular file, check if it's owned by
  897. X         *    the recipient), if something is wrong try and move the
  898. X         *    bogus mailbox out of the way, create the
  899. X         *    original/default mailbox file, and chown it to
  900. X         *    the recipient
  901. X         */
  902. X         if(lstat(chp,&stbuf))             /* stat the mailbox */
  903. X          { mboxstat= -(errno==EACCES);
  904. X            goto boglock;
  905. X          }                /* lockfile unrightful owner */
  906. X         else
  907. X          { mboxstat=1;
  908. X            if(!(stbuf.st_mode&S_IWGRP))
  909. Xboglock:           if(!goodlock)          /* try & rename bogus lockfile */
  910. X              if(rename(defdeflock,buf))       /* out of the way */
  911. X                 syslog(LOG_EMERG,renfbogus,defdeflock,buf);
  912. X              else
  913. X                 syslog(LOG_ALERT,renbogus,defdeflock,buf);
  914. X          }
  915. X           }
  916. X          if(mboxstat>0||mboxstat<0&&(setids(),!lstat(chp,&stbuf)))
  917. X         if(!(stbuf.st_mode&S_IWUSR)||         /* recipient can write? */
  918. X            S_ISLNK(stbuf.st_mode)||        /* no symbolic links */
  919. X            (S_ISDIR(stbuf.st_mode)?  /* directories, yes, hardlinks */
  920. X              !(stbuf.st_mode&S_IXUSR):stbuf.st_nlink!=1))     /* no */
  921. X            goto bogusbox;    /* can't deliver to this contraption */
  922. X         else if(stbuf.st_uid!=uid)          /* recipient not owner */
  923. Xbogusbox:      { ultoan((unsigned long)stbuf.st_ino,      /* i-node numbered */
  924. X             strchr(strcpy(buf+i,BOGUSprefix),'\0'));        /* bogus */
  925. X            nlog("Renaming bogus mailbox \"");elog(chp);
  926. X            elog("\" into");logqnl(buf);
  927. X            if(rename(chp,buf))       /* try and move it out of the way */
  928. X             { syslog(LOG_EMERG,renfbogus,chp,buf);
  929. X               goto fishy;  /* rename failed, something's fishy here */
  930. X             }
  931. X            else
  932. X               syslog(LOG_ALERT,renbogus,chp,buf);
  933. X          }                /* SysV type autoforwarding? */
  934. X         else if(Deliverymode&&stbuf.st_mode&(S_ISGID|S_ISUID))
  935. X          { nlog("Autoforwarding mailbox found\n");
  936. X            return EX_NOUSER;
  937. X          }
  938. X         else
  939. X          { if(!(stbuf.st_mode&OVERRIDE_MASK)&&stbuf.st_mode&cumask)
  940. X             { static const char enfperm[]=
  941. X            "Enforcing stricter permissions on";
  942. X               nlog(enfperm);logqnl(chp);
  943. X               syslog(LOG_NOTICE,slogstr,enfperm,chp);setids();
  944. X               chmod(chp,stbuf.st_mode&=~cumask);
  945. X             }
  946. X            break;              /* everything is just fine */
  947. X          }
  948. X        }
  949. X       if(!(accspooldir&1))         /* recipient does not own the spool dir */
  950. X        { if(!xcreat(chp,NORMperm,(time_t*)0,doCHOWN|doCHECK)) /* create */
  951. X         break;           /* mailbox... yes we could, fine, proceed */
  952. X          if(!lstat(chp,&stbuf))             /* anything in the way? */
  953. X         continue;               /* check if it could be valid */
  954. X        }
  955. X       setids();                       /* try some magic */
  956. X       if(!xcreat(chp,NORMperm,(time_t*)0,doCHECK))        /* try again */
  957. X          break;
  958. X       if(lstat(chp,&stbuf))              /* nothing in the way? */
  959. Xfishy:        { nlog("Couldn't create");logqnl(chp);sputenv(orgmail);
  960. X          if(!strcmp(chp,fdefault))            /* DEFAULT the same? */
  961. X         fdefault="";                     /* so panic */
  962. X          break;
  963. X        }
  964. X     }                    /* bad news, be conservative */
  965. X    doumask(INIT_UMASK);
  966. X      }
  967. X     while(chp=(char*)argv[argc])      /* interpret command line specs first */
  968. X       /*
  969. X    *    really change the uid now, since it would not be safe to
  970. X    *    evaluate the extra command line arguments otherwise
  971. X    */
  972. X      { setids();argc++;
  973. X    if(!asenvcpy(chp)&&mailfilter)
  974. X     { gargv= &nullp;
  975. X       for(restargv=argv+argc;restargv[crestarg];crestarg++);
  976. X       break;
  977. X     }
  978. X    resettmout();
  979. X      }
  980. X   }
  981. X  ;{ int succeed,lastcond,prevcond;struct dyna_long ifstack;
  982. X     ifstack.filled=ifstack.tspace=0;ifstack.offs=0;
  983. X     if(etcrc)          /* do we start with an /etc/procmailrc file first? */
  984. X      { if(0<=bopen(etcrc))
  985. X     { yell(drcfile,etcrc);
  986. X#if !DEFverbose
  987. X       if(rcstate!=rc_NORMAL)
  988. X          verbose=0;            /* no peeking in /etc/procmailrc */
  989. X#endif
  990. X       goto startrc;
  991. X     }
  992. X    etcrc=0;                         /* no such file */
  993. X      }
  994. X     do                         /* main rcfile interpreter loop */
  995. X      { resettmout();
  996. X    if(rc<0)                     /* open new rc file */
  997. X     { struct stat stbuf;
  998. X      /*
  999. X       *    if we happen to be still running as root, and the rcfile
  1000. X       *    is mounted on a secure NFS-partition, we might not be able
  1001. X       *    to access it, so check if we can stat it or don't need any
  1002. X       *    sgid privileges, if yes, drop all privs and set uid to
  1003. X       *    the recipient beforehand
  1004. X       */
  1005. X       goto findrc;
  1006. X       do
  1007. X        { if(suppmunreadable)      /* should we supress this message? */
  1008. Xfake_rc:     readerr(buf);
  1009. X          if(!nextrcfile())              /* not available? try the next */
  1010. X           { skiprc=0;
  1011. X         goto nomore_rc;
  1012. X           }
  1013. X          suppmunreadable=0;
  1014. Xfindrc:          i=0;            /* should we keep the current directory? */
  1015. X          if(strchr(dirsep,*rcfile)||           /* absolute path? */
  1016. X         (mailfilter||*rcfile==chCURDIR&&strchr(dirsep,rcfile[1]))&&
  1017. X         (i=1))                     /* mailfilter or ./ pfx */
  1018. X         *buf='\0';        /* do not put anything in front then */
  1019. X          else
  1020. X         cat(tgetenv(home),MCDIRSEP);      /* prepend $HOME directory */
  1021. X          if(stat(strcat(buf,rcfile),&stbuf)?          /* accessible? */
  1022. X           rcstate==rc_NOSGID:stbuf.st_mode&S_IRUSR)  /* owner-readable? */
  1023. X         setids();                /* then transmogrify */
  1024. X        }
  1025. X       while(0>bopen(buf));               /* try opening the rcfile */
  1026. X       if(i)          /* opened rcfile in the current directory? */
  1027. X        { if(!didchd)
  1028. X           { didchd=1;*(chp=strcpy(buf2,maildir)+STRLEN(maildir))='=';
  1029. X         *++chp=chCURDIR;*++chp='\0';sputenv(buf2);
  1030. X           }
  1031. X        }
  1032. X       else
  1033. X         /*
  1034. X          * OK, so now we have opened an absolute rcfile, but for security
  1035. X          * reasons we only accept it if it is owned by the recipient or
  1036. X          * root and is not world writable, and the directory it is in is
  1037. X          * not world writable or has the sticky bit set
  1038. X          */
  1039. X        { i= *(chp=lastdirsep(buf));
  1040. X          if(lstat(buf,&stbuf)||
  1041. X         ((stbuf.st_uid!=uid&&stbuf.st_uid!=ROOT_uid||
  1042. X          stbuf.st_mode&S_IWOTH)&&
  1043. X          strcmp(devnull,buf)&&          /* /dev/null is a special case */
  1044. X          (*chp='\0',stat(buf,&stbuf)||
  1045. X           (stbuf.st_mode&(S_IWOTH|S_IXOTH))==(S_IWOTH|S_IXOTH)&&
  1046. X            !(stbuf.st_mode&S_ISVTX))))
  1047. X           { static const char susprcf[]="Suspicious rcfile";
  1048. X         *chp=i;closerc();nlog(susprcf);logqnl(buf);
  1049. X         syslog(LOG_ALERT,slogstr,susprcf,buf);
  1050. X         goto fake_rc;
  1051. X           }
  1052. X          *chp=i;
  1053. X        }
  1054. X      /*
  1055. X       *    set uid back to recipient in any case, since we might just
  1056. X       *    have opened his/her .procmailrc (don't remove these, since
  1057. X       *    the rcfile might have been created after the first stat)
  1058. X       */
  1059. X       yell(drcfile,buf);setids();firstchd();
  1060. Xstartrc:   succeed=lastcond=prevcond=0;
  1061. X     }
  1062. X    unlock(&loclock);            /* unlock any local lockfile */
  1063. X    goto commint;
  1064. X    do
  1065. X     { for(;;)                /* skip the rest of the line */
  1066. X        { switch(getb())
  1067. X           { default:
  1068. X            continue;
  1069. X         case '\n':case EOF:;
  1070. X           }
  1071. X          break;
  1072. X        }
  1073. Xcommint:   do skipspace();                  /* skip whitespace */
  1074. X       while(testb('\n'));
  1075. X     }
  1076. X    while(testb('#'));                   /* no comment :-) */
  1077. X    if(testb(':'))                       /* check for a recipe */
  1078. X     { int locknext;long tobesent;char*startchar;
  1079. X       static char flags[maxindex(exflags)];
  1080. X       ;{ int nrcond,scored;double score;
  1081. X          score=scored=0;
  1082. X          readparse(buf,getb,0);
  1083. X          ;{ char*chp3;
  1084. X         nrcond=strtol(buf,&chp3,10);chp=chp3;
  1085. X           }
  1086. X          if(chp==buf)                 /* no number parsed */
  1087. X         nrcond= -1;
  1088. X          if(tolock)     /* clear temporary buffer for lockfile name */
  1089. X         free(tolock);
  1090. X          for(i=maxindex(flags);flags[i]=0,i--;);      /* clear the flags */
  1091. X          for(tolock=0,locknext=0;;)
  1092. X           { chp=skpspace(chp);
  1093. X         switch(i= *chp++)
  1094. X          { default:
  1095. X               if(!(chp2=strchr(exflags,i)))        /* a valid flag? */
  1096. X            { chp--;
  1097. X              break;
  1098. X            }
  1099. X               flags[chp2-exflags]=1;             /* set the flag */
  1100. X            case '\0':
  1101. X               if(chp!=Tmnate)        /* if not the real end, skip */
  1102. X              continue;
  1103. X               break;
  1104. X            case ':':locknext=1;    /* yep, local lockfile specified */
  1105. X               if(*chp||++chp!=Tmnate)
  1106. X              tolock=tstrdup(chp),chp=strchr(chp,'\0')+1;
  1107. X          }
  1108. X         concatenate(chp);skipped(chp);        /* display leftovers */
  1109. X         break;
  1110. X           }
  1111. X          if(flags[ERROR_DO]&&flags[ELSE_DO])
  1112. X         nlog(conflicting),elog("else-if-flag"),elog(suppressed);
  1113. X          if(flags[ERROR_DO]&&flags[ALSO_N_IF_SUCC])
  1114. X           { nlog(conflicting);elog("also-if-succeeded-flag");
  1115. X         elog(suppressed);
  1116. X           }
  1117. X          if(nrcond<0)    /* assume appropriate default nr of conditions */
  1118. X         nrcond=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC]&&
  1119. X            !flags[ELSE_DO]&&!flags[ERROR_DO];
  1120. X          startchar=themail;tobesent=thebody-themail;
  1121. X          if(flags[BODY_GREP])           /* what needs to be egrepped? */
  1122. X         if(flags[HEAD_GREP])
  1123. X            tobesent=filled;
  1124. X         else
  1125. X          { startchar=thebody;tobesent=filled-tobesent;
  1126. X            goto noconcat;
  1127. X          }
  1128. X          if(!skiprc)
  1129. X         concon(' ');
  1130. Xnoconcat:     i=flags[ERROR_DO]?prevcond&&!succeed:
  1131. X        flags[ELSE_DO]?!prevcond:
  1132. X        flags[ALSO_N_IF_SUCC]?lastcond&&succeed:
  1133. X        flags[ALSO_NEXT_RECIPE]?lastcond:1;      /* init test value */
  1134. X          if(skiprc)
  1135. X         i=0;
  1136. X          while(skipspace(),nrcond--,testb('*')||nrcond>=0)
  1137. X           { skipspace();getlline(buf2);        /* any conditions (left) */
  1138. X         for(chp=strchr(buf2,'\0');--chp>=buf2;)
  1139. X          { switch(*chp)      /* strip off whitespace at the end */
  1140. X             { case ' ':case '\t':*chp='\0';
  1141. X              continue;
  1142. X             }
  1143. X            break;
  1144. X          }
  1145. X         if(i)                 /* check out all conditions */
  1146. X          { int negate,scoreany;double weight,xponent,lscore;
  1147. X            negate=scoreany=0;lscore=score;
  1148. X            for(chp=buf2+1;;strcpy(buf2,buf))
  1149. Xcopydone:         { switch(*(sgetcp=buf2))
  1150. X            { case '0':case '1':case '2':case '3':case '4':
  1151. X              case '5':case '6':case '7':case '8':case '9':
  1152. X              case '-':case '+':case '.':case ',':
  1153. X               { char*chp3;double w;
  1154. X                 w=stod(buf2,(const char**)&chp3);chp2=chp3;
  1155. X                 if(chp2>buf2&&*(chp2=skpspace(chp2))=='^')
  1156. X                  { double x;
  1157. X                x=stod(chp2+1,(const char**)&chp3);
  1158. X                if(chp3>chp2+1)
  1159. X                 { if(score>=MAX32)
  1160. X                      goto skiptrue;
  1161. X                   chp2=skpspace(chp3);xponent=x;weight=w;
  1162. X                   scored=scoreany=1;
  1163. X                   goto copyrest;
  1164. X                 }
  1165. X                  }
  1166. X               }
  1167. X              default:chp--;     /* no special character, backup */
  1168. X              case '\\':
  1169. X               { int or_nocase;    /* case-distinction override */
  1170. X                 static const struct {const char*regkey,*regsubst;}
  1171. X                  *regsp,regs[]=
  1172. X                   { {FROMDkey,FROMDsubstitute},
  1173. X                 {TOkey,TOsubstitute},
  1174. X                 {FROMMkey,FROMMsubstitute},
  1175. X                 {0,0}
  1176. X                   };
  1177. X                 squeeze(chp);or_nocase=0;
  1178. X                 goto jinregs;
  1179. X                 do           /* find special keyword in regexp */
  1180. X                if((chp2=strstr(chp,regsp->regkey))&&
  1181. X                 (chp2==buf2||chp2[-1]!='\\'))     /* escaped? */
  1182. X                 { size_t lregs,lregk;           /* no, so */
  1183. X                   lregk=strlen(regsp->regkey); /* insert it */
  1184. X                   tmemmove(
  1185. X                    chp2+(lregs=strlen(regsp->regsubst)),
  1186. X                    chp2+lregk,strlen(chp2)-lregk+1);
  1187. X                   tmemmove(chp2,regsp->regsubst,lregs);
  1188. X                   if(regsp==regs)       /* daemon regexp? */
  1189. X                      or_nocase=1;   /* no case sensitivity! */
  1190. Xjinregs:               regsp=regs;    /* start over and look again */
  1191. X                 }
  1192. X                else
  1193. X                   regsp++;             /* next keyword */
  1194. X                 while(regsp->regkey);
  1195. X                 ;{ int igncase;
  1196. X                igncase=or_nocase||!flags[DISTINGUISH_CASE];
  1197. X                if(scoreany)
  1198. X                 { struct eps*re;long rest;
  1199. X                   re=bregcomp(chp,igncase);
  1200. X                   chp=startchar;rest=tobesent;
  1201. X                   if(negate)
  1202. X                    { if(weight&&!bregexec(re,
  1203. X                       (const uchar*)chp,(const uchar*)chp,
  1204. X                       (size_t)rest,igncase))
  1205. X                     score+=weight;
  1206. X                    }
  1207. X                   else
  1208. X                    { double oweight=weight*weight;
  1209. X                      while(weight!=0&&
  1210. X                        MIN32<score&&
  1211. X                        score<MAX32&&
  1212. X                        (chp2=
  1213. X                       bregexec(re,(const uchar*)startchar,
  1214. X                    (const uchar*)chp,(size_t)rest,
  1215. X                    igncase)))
  1216. X                       { score+=weight;weight*=xponent;
  1217. X                     if(chp>=chp2)      /* break off empty */
  1218. X                      { if(0<xponent&&xponent<1)
  1219. X                           score+=weight/(1-xponent);
  1220. X                        else if(xponent>=1&&weight!=0)
  1221. X                           score+=weight<0?MIN32:MAX32;
  1222. X                        break;        /* matches early */
  1223. X                      }
  1224. X                     ;{ double nweight;
  1225. X                        if((nweight=weight*weight)<oweight
  1226. X                           &&oweight<1)
  1227. X                           break;
  1228. X                        oweight=nweight;
  1229. X                      }
  1230. X                     rest-=chp2-chp;chp=chp2;
  1231. X                       }
  1232. X                    }
  1233. X                   free(re);
  1234. X                 }
  1235. X                else                 /* egrep for it */
  1236. X                   i=!!egrepin(chp,startchar,tobesent,
  1237. X                    !igncase)^negate;
  1238. X                  }
  1239. X                 break;
  1240. X               }
  1241. X              case '$':*buf2='"';squeeze(chp);
  1242. X                 readparse(buf,sgetc,2);strcpy(buf2,skpspace(buf));
  1243. X                 goto copydone;
  1244. X              case '!':negate^=1;chp2=skpspace(chp);
  1245. Xcopyrest:             strcpy(buf,chp2);
  1246. X                 continue;
  1247. X              case '?':pwait=2;metaparse(chp);inittmout(buf);
  1248. X                  ignwerr=1;pipin(buf,startchar,tobesent);
  1249. X                  if(scoreany&&lexitcode>=0)
  1250. X                   { int j=lexitcode;
  1251. X                 if(negate)
  1252. X                    while(--j>=0&&
  1253. X                      (score+=weight)<MAX32&&
  1254. X                      score>MIN32)
  1255. X                       weight*=xponent;
  1256. X                    else
  1257. X                       score+=j?xponent:weight;
  1258. X                   }
  1259. X                  else if(!!lexitcode^negate)
  1260. X                 i=0;
  1261. X                  strcpy(buf2,buf);
  1262. X                  break;
  1263. X              case '>':case '<':readparse(buf,sgetc,2);
  1264. X               { long pivot;
  1265. X                  ;{ char*chp3;
  1266. X                pivot=strtol(buf+1,&chp3,10);chp=chp3;
  1267. X                  }
  1268. X                 skipped(skpspace(chp));strcpy(buf2,buf);
  1269. X                 if(scoreany)
  1270. X                  { double f;
  1271. X                if((*buf=='<')^negate)
  1272. X                   if(filled)
  1273. X                      f=(double)pivot/filled;
  1274. X                   else if(pivot>0)
  1275. X                      goto plusinfty;
  1276. X                   else
  1277. X                      goto mininfty;
  1278. X                else if(pivot)
  1279. X                   f=(double)filled/pivot;
  1280. X                else
  1281. X                   goto plusinfty;
  1282. X                score+=weight*tpow(f,xponent);
  1283. X                  }
  1284. X                 else if(!((*buf=='<'?
  1285. X                     filled<pivot:
  1286. X                     filled>pivot)^
  1287. X                    negate))
  1288. X                i=0;
  1289. X               }
  1290. X            }
  1291. X               break;
  1292. X             }
  1293. X            if(score>MAX32)        /* chop off at plus infinity */
  1294. Xplusinfty:           score=MAX32;
  1295. X            if(score<=MIN32)           /* chop off at minus infinity */
  1296. Xmininfty:           score=MIN32,i=0;
  1297. X            if(verbose)         /* not entirely correct, but it will do */
  1298. X             { if(scoreany)
  1299. X            { charNUM(num,long);
  1300. X              nlog("Score: ");ltstr(7,(long)(score-lscore),num);
  1301. X              elog(num);elog(" ");
  1302. X              ;{ long iscore=score;
  1303. X                 ltstr(7,iscore,num);
  1304. X                 if(!iscore&&score>0)
  1305. X                num[7-2]='+';        /* show +0 for (0,1) */
  1306. X               }
  1307. X              elog(num);
  1308. X            }
  1309. X               else
  1310. X              nlog(i?"M":"No m"),elog("atch on");
  1311. X               if(negate)
  1312. X              elog(" !");
  1313. X               logqnl(buf2);
  1314. X             }
  1315. Xskiptrue:;      }
  1316. X           }
  1317. X          if(!(lastscore=score)&&score>0)           /* save it for $= */
  1318. X         lastscore=1;                 /* round up +0 to 1 */
  1319. X          if(scored&&i&&score<=0)
  1320. X         i=0;                     /* it was not a success */
  1321. X        }
  1322. X       if(!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC])
  1323. X          lastcond=i;           /* save the outcome for posterity */
  1324. X       if(!prevcond||!flags[ELSE_DO])
  1325. X          prevcond=i;     /* same here, for `else if' like constructs */
  1326. X       startchar=themail;tobesent=filled;
  1327. X       if(flags[PASS_HEAD])                /* body, header or both? */
  1328. X        { if(!flags[PASS_BODY])
  1329. X         tobesent=thebody-themail;
  1330. X        }
  1331. X       else if(flags[PASS_BODY])
  1332. X          tobesent-=(startchar=thebody)-themail;
  1333. X       chp=strchr(strcpy(buf,sendmail),'\0');succeed=sh=0;
  1334. X       pwait=flags[WAIT_EXIT]|flags[WAIT_EXIT_QUIET]<<1;
  1335. X       ignwerr=flags[IGNORE_WRITERR];Stdout=0;skipspace();
  1336. X       if(i)
  1337. X          zombiecollect(),concon('\n');
  1338. Xprogrm:       if(testb('!'))                 /* forward the mail */
  1339. X        { if(!i)
  1340. X         skiprc++;
  1341. X          readparse(chp+1,getb,0);
  1342. X          if(i)
  1343. X           { if(startchar==themail)
  1344. X          { startchar[filled]='\0';             /* just in case */
  1345. X            if(eqFrom_(startchar))    /* leave off any leading From_ */
  1346. X               do
  1347. X              while(i= *startchar++,--tobesent&&i!='\n');
  1348. X               while(*startchar=='>'&&eqFrom_(startchar+1));
  1349. X          }                 /* it confuses some mailers */
  1350. X         goto forward;
  1351. X           }
  1352. X          skiprc--;
  1353. X        }
  1354. X       else if(testb('|'))                    /* pipe the mail */
  1355. X        { getlline(buf2);             /* get the command to start */
  1356. X          if(i)
  1357. X           { metaparse(buf2);
  1358. X         if(!sh&&buf+1==Tmnate)              /* just a pipe symbol? */
  1359. X          { *buf='|';*(char*)(Tmnate++)='\0';
  1360. X            goto tostdout;
  1361. X          }                          /* fake it */
  1362. Xforward:     if(locknext)
  1363. X          { if(!tolock)       /* an explicit lockfile specified already */
  1364. X             { *buf2='\0';  /* find the implicit lockfile ('>>name') */
  1365. X               for(chp=buf;i= *chp++;)
  1366. X              if(i=='>'&&*chp=='>')
  1367. X               { chp=skpspace(chp+1);
  1368. X                 tmemmove(buf2,chp,i=strcspn(chp,EOFName));
  1369. X                 buf2[i]='\0';
  1370. X                 if(sh)     /* expand any environment variables */
  1371. X                  { chp=tstrdup(buf);sgetcp=buf2;
  1372. X                readparse(buf,sgetc,0);strcpy(buf2,buf);
  1373. X                strcpy(buf,chp);free(chp);
  1374. X                  }
  1375. X                 break;
  1376. X               }
  1377. X               if(!*buf2)
  1378. X            { nlog("Couldn't determine implicit lockfile from");
  1379. X              logqnl(buf);
  1380. X            }
  1381. X             }
  1382. X            lcllock();
  1383. X            if(!pwait)        /* try and protect the user from his */
  1384. X               pwait=2;               /* blissful ignorance :-) */
  1385. X          }
  1386. X         inittmout(buf);asgnlastf=1;
  1387. X         if(flags[FILTER])
  1388. X          { if(startchar==themail&&tobesent!=filled)  /* if only 'h' */
  1389. X             { if(!pipthrough(buf,startchar,tobesent))
  1390. X              readmail(1,tobesent),succeed=!pipw;
  1391. X             }
  1392. X            else if(!pipthrough(buf,startchar,tobesent))
  1393. X               filled=startchar-themail,readmail(0,0L),succeed=!pipw;
  1394. X          }
  1395. X         else if(Stdout)          /* capturing stdout again? */
  1396. X          { if(!pipthrough(buf,startchar,tobesent))
  1397. X               succeed=1,postStdout();      /* only parse if no errors */
  1398. X          }
  1399. X         else if(!pipin(buf,startchar,tobesent)&& /* regular program */
  1400. X          (succeed=1,!flags[CONTINUE]))
  1401. X            goto frmailed;
  1402. X           }
  1403. X        }
  1404. X       else if(testb(EOF))
  1405. X          nlog("Incomplete recipe\n");
  1406. X       else           /* dump the mail into a mailbox file or directory */
  1407. X        { int ofiltflag;
  1408. X          if(ofiltflag=flags[FILTER])
  1409. X         flags[FILTER]=0,nlog(extrns),elog("filter-flag"),elog(ignrd);
  1410. X          if(chp=gobenv(buf))       /* can it be an environment name? */
  1411. X           { if(skipspace())
  1412. X            chp++;           /* keep pace with argument breaks */
  1413. X         if(testb('='))              /* is it really an assignment? */
  1414. X          { int c;
  1415. X            *chp++='=';*chp='\0';
  1416. X            if(skipspace())
  1417. X               chp++;
  1418. X            ungetb(c=getb());
  1419. X            switch(c)
  1420. X             { case '!':case '|':          /* ok, it's a pipe */
  1421. X              if(i)
  1422. X                 primeStdout();
  1423. X              goto progrm;
  1424. X             }
  1425. X          }
  1426. X           }         /* find the end, start of a nesting recipe? */
  1427. X          else if((chp=strchr(buf,'\0'))==buf&&
  1428. X              testb('{')&&
  1429. X              (*chp++='{',*chp='\0',testb(' ')||
  1430. X               testb('\t')||
  1431. X               testb('\n')))
  1432. X           {  if(locknext)
  1433. X            nlog(extrns),elog("locallockfile"),elog(ignrd);
  1434. X         app_val(&ifstack,(off_t)prevcond);        /* push prevcond */
  1435. X         app_val(&ifstack,(off_t)lastcond);        /* push lastcond */
  1436. X         if(!i)                        /* no match? */
  1437. X            skiprc++;              /* increase the skipping level */
  1438. X         else
  1439. X          { if(locknext)
  1440. X             { lcllock();
  1441. X               if(!pwait)    /* try and protect the user from his */
  1442. X              pwait=2;           /* blissful ignorance :-) */
  1443. X             }
  1444. X            inittmout(procmailn);
  1445. X            if(flags[CONTINUE])
  1446. X             { yell("Forking",procmailn);onguard();
  1447. X               if(!(pidchild=sfork()))           /* clone yourself */
  1448. X            { if(loclock)          /* lockfiles are not inherited */
  1449. X                 free(loclock),loclock=0;
  1450. X              if(globlock)
  1451. X                 free(globlock),globlock=0;         /* clear up the */
  1452. X              newid();offguard();duprcs();      /* identity crisis */
  1453. X            }
  1454. X               else
  1455. X            { offguard();
  1456. X              if(forkerr(pidchild,procmailn))
  1457. X                 succeed=0;           /* Tsk, tsk, no cloning today */
  1458. X              else
  1459. X               { int excode;
  1460. X                 succeed=1;      /* wait for our significant other? */
  1461. X                 if(pwait&&(excode=waitfor(pidchild))!=EX_OK)
  1462. X                  { if(!(pwait&2)||verbose)     /* do we report it? */
  1463. X                   progerr(procmailn,excode);
  1464. X                succeed=0;
  1465. X                  }
  1466. X                 pidchild=0;skiprc++;    /* skip over the braces */
  1467. X               }
  1468. X            }
  1469. X             }
  1470. X          }
  1471. X         continue;
  1472. X           }
  1473. X          if(!i)                        /* no match? */
  1474. X         skiprc++;          /* temporarily disable subprograms */
  1475. X          readparse(chp,getb,0);
  1476. X          if(i)
  1477. X           { if(ofiltflag)     /* protect those who use bogus filter-flags */
  1478. X            startchar=themail,tobesent=filled; /* save whole message */
  1479. Xtostdout:     strcpy(buf2,buf);
  1480. X         if(locknext)
  1481. X            lcllock();             /* write to a file or directory */
  1482. X         inittmout(buf);      /* to break messed-up kernel locks */
  1483. X         if(dump(deliver(buf,strchr(buf,'\0')+1),startchar,tobesent)
  1484. X          &&!ignwerr)
  1485. X            writeerr(buf);
  1486. X         else if(succeed=1,!flags[CONTINUE])
  1487. Xfrmailed:      { if(ifstack.offs)
  1488. X               free(ifstack.offs);
  1489. X            goto mailed;
  1490. X          }
  1491. X           }
  1492. X          else
  1493. X         skiprc--;                 /* reenable subprograms */
  1494. X        }
  1495. X     }
  1496. X    else if(testb('}'))                    /* end block */
  1497. X     { if(ifstack.filled)              /* restore lastcond from stack */
  1498. X        { lastcond=ifstack.offs[--ifstack.filled];
  1499. X          prevcond=ifstack.offs[--ifstack.filled];     /* prevcond as well */
  1500. X        }
  1501. X       else
  1502. X          nlog("Closing brace unexpected\n");          /* stack empty */
  1503. X       if(skiprc)                        /* just skipping */
  1504. X          skiprc--;                       /* decrease level */
  1505. X     }
  1506. X    else                    /* then it must be an assignment */
  1507. X     { if(!(chp=gobenv(buf)))
  1508. X        { if(!*buf)                    /* skip a word first */
  1509. X         getbl(buf);                      /* then a line */
  1510. X          skipped(buf);                /* display leftovers */
  1511. X          continue;
  1512. X        }
  1513. X       skipspace();
  1514. X       if(testb('='))               /* removal or assignment? */
  1515. X          *chp='=',readparse(++chp,getb,1);
  1516. X       else
  1517. X          *++chp='\0';             /* throw in a second terminator */
  1518. X       if(!skiprc)
  1519. X          chp2=(char*)sputenv(buf),chp[-1]='\0',asenv(chp2);
  1520. X     }
  1521. X      }                            /* main interpreter loop */
  1522. X     while(rc<0||!testb(EOF)||poprc()||wipetcrc());
  1523. Xnomore_rc:
  1524. X     if(ifstack.offs)
  1525. X    free(ifstack.offs);
  1526. X   }
  1527. X  ;{ int succeed;
  1528. X     concon('\n');succeed=0;
  1529. X     if(*(chp=(char*)fdefault))                     /* DEFAULT set? */
  1530. X      { setuid(uid);firstchd();
  1531. X    if(strcmp(chp,devnull)&&strcmp(chp,"|"))  /* neither /dev/null nor | */
  1532. X     { cat(chp,lockext);
  1533. X       if(!globlock||strcmp(buf,globlock))          /* already locked? */
  1534. X          lockit(buf,&loclock);                /* implicit lock */
  1535. X     }
  1536. X    if(dump(deliver(chp,(char*)0),themail,filled))          /* default */
  1537. X       writeerr(buf);
  1538. X    else
  1539. X       succeed=1;
  1540. X      }
  1541. X     if(!succeed&&*(chp=(char*)tgetenv(orgmail)))      /* if all else failed */
  1542. X    if(dump(deliver(chp,(char*)0),themail,filled))          /* don't panic */
  1543. X       writeerr(buf);                  /* try the last resort */
  1544. X    else
  1545. X       succeed=1;
  1546. X     if(succeed)                     /* should we panic now? */
  1547. Xmailed: retval=EX_OK;              /* we're home free, mail delivered */
  1548. X   }
  1549. X  unlock(&loclock);Terminate();
  1550. X}
  1551. X
  1552. XeqFrom_(a)const char*const a;
  1553. X{ return !strncmp(a,From_,STRLEN(From_));
  1554. X}
  1555. END_OF_FILE
  1556.   if test 38724 -ne `wc -c <'procmail-3.03/src/procmail.c'`; then
  1557.     echo shar: \"'procmail-3.03/src/procmail.c'\" unpacked with wrong size!
  1558.   fi
  1559.   # end of 'procmail-3.03/src/procmail.c'
  1560. fi
  1561. echo shar: End of archive 2 \(of 10\).
  1562. cp /dev/null ark2isdone
  1563. MISSING=""
  1564. for I in 1 2 3 4 5 6 7 8 9 10 ; do
  1565.     if test ! -f ark${I}isdone ; then
  1566.     MISSING="${MISSING} ${I}"
  1567.     fi
  1568. done
  1569. if test "${MISSING}" = "" ; then
  1570.     echo You have unpacked all 10 archives.
  1571.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1572. else
  1573.     echo You still must unpack the following archives:
  1574.     echo "        " ${MISSING}
  1575. fi
  1576. exit 0
  1577. exit 0 # Just in case...
  1578.