home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume31 / procmail / part05 < prev    next >
Encoding:
Text File  |  1992-07-15  |  55.9 KB  |  1,590 lines

  1. Newsgroups: comp.sources.misc
  2. From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  3. Subject:  v31i044:  procmail - mail processing program v2.71, Part05/05
  4. Message-ID: <1992Jul16.204918.21050@sparky.imd.sterling.com>
  5. X-Md4-Signature: f2914d125faef2d09b4b634127b57154
  6. Date: Thu, 16 Jul 1992 20:49:18 GMT
  7. Approved: kent@sparky.imd.sterling.com
  8.  
  9. Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  10. Posting-number: Volume 31, Issue 44
  11. Archive-name: procmail/part05
  12. Environment: UNIX, sendmail, smail, MMDF
  13. Supersedes: procmail: Volume 29, Issue 90-94
  14.  
  15. #! /bin/sh
  16. # This is a shell archive.  Remove anything before this line, then unpack
  17. # it by saving it into a file and typing "sh file".  To overwrite existing
  18. # files, type "sh file -c".  You can also feed this as standard input via
  19. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  20. # will see the following message at the end:
  21. #        "End of archive 5 (of 5)."
  22. # Contents:  procmail/examples/1rmail procmail/formail.c
  23. #   procmail/man/lockfile.man procmail/procmail.c
  24. # Wrapped by berg@minipicc on Thu Jul 16 14:34:21 1992
  25. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  26. if test -f 'procmail/examples/1rmail' -a "${1}" != "-c" ; then 
  27.   echo shar: Will not clobber existing file \"'procmail/examples/1rmail'\"
  28. else
  29. echo shar: Extracting \"'procmail/examples/1rmail'\" \(400 characters\)
  30. sed "s/^X//" >'procmail/examples/1rmail' <<'END_OF_FILE'
  31. X#!/bin/sh
  32. X#
  33. X# specify the mailbox file you want to read on the command line
  34. X#
  35. XMAILDIR=$HOME/Mail
  36. Xcd $MAILDIR
  37. XLOCKFILE=$HOME/.lockmail
  38. Xif lockfile -! -r1 $LOCKFILE
  39. Xthen
  40. X echo Mail is currently arriving, please wait...
  41. X while
  42. X   lockfile -! -4 -r2 $LOCKFILE
  43. X do
  44. X echo Mail is still arriving...
  45. X done
  46. Xfi
  47. Xtrap "rm -f $LOCKFILE;exit 0" 0 1 2 3 15
  48. X#
  49. X# Call you favourite mailer here.
  50. X#
  51. X/usr/ucb/mail -f $*
  52. END_OF_FILE
  53. if test 400 -ne `wc -c <'procmail/examples/1rmail'`; then
  54.     echo shar: \"'procmail/examples/1rmail'\" unpacked with wrong size!
  55. fi
  56. # end of 'procmail/examples/1rmail'
  57. fi
  58. if test -f 'procmail/formail.c' -a "${1}" != "-c" ; then 
  59.   echo shar: Will not clobber existing file \"'procmail/formail.c'\"
  60. else
  61. echo shar: Extracting \"'procmail/formail.c'\" \(23879 characters\)
  62. sed "s/^X//" >'procmail/formail.c' <<'END_OF_FILE'
  63. X/************************************************************************
  64. X *    formail.c    a mail (re)formatter                *
  65. X *                                    *
  66. X *    Seems to be relatively bug free.                *
  67. X *                                    *
  68. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  69. X *    The sources can be freely copied for non-commercial use.    *
  70. X *    #include "README"                        *
  71. X *                                    *
  72. X ************************************************************************/
  73. X#ifdef RCS
  74. Xstatic char rcsid[]="$Id: formail.c,v 2.26 1992/06/03 13:17:41 berg Rel $";
  75. X#endif
  76. Xstatic char rcsdate[]="$Date: 1992/06/03 13:17:41 $";
  77. X#include "config.h"                      /* slight overkill */
  78. X#include "includes.h"
  79. X#include "strpbrk.h"
  80. X#include <ctype.h>
  81. X
  82. Xchar*pstrspn();
  83. X
  84. X#define BSIZE        128
  85. X
  86. X#define NAMEPREFIX    "formail: "
  87. X#define HEAD_DELIMITER    ':'
  88. X
  89. X#define Re        (re+1)
  90. X#define putssn(a,l)    tputssn(a,(size_t)(l))
  91. X#define putcs(a)    (errout=putc(a,mystdout))
  92. X#define PRDO        poutfd[0]
  93. X#define PWRO        poutfd[1]
  94. X#define FLD_HEADSIZ    ((size_t)offsetof(struct field,fld_text[0]))
  95. X
  96. Xstatic const char couldntw[]="Couldn't write to stdout",unknown[]=UNKNOWN,
  97. X outofmem[]="Out of memory\n",re[]=" Re:",OldP[]=OLD_PREFIX,fmusage[]=FM_USAGE,
  98. X From_[]=        FROM,                /* VNIX 'From ' line */
  99. X path[]=        "Path:",                   /* USENET */
  100. X article[]=        "Article ",                   /* USENET */
  101. X returnpath[]=        "Return-Path:",                  /* RFC 822 */
  102. X received[]=        "Received:",                /* ditto ... */
  103. X replyto[]=        "Reply-To:",
  104. X Fromm[]=        "From:",
  105. X sender[]=        "Sender:",
  106. X res_replyto[]=        "Resent-Reply-To:",
  107. X res_from[]=        "Resent-From:",
  108. X res_sender[]=        "Resent-Sender:",
  109. X date[]=        "Date:",
  110. X res_date[]=        "Resent-Date:",
  111. X to[]=            "To:",
  112. X res_to[]=        "Resent-To:",
  113. X cc[]=            "Cc:",
  114. X res_cc[]=        "Resent-Cc:",
  115. X bcc[]=            "Bcc:",
  116. X res_bcc[]=        "Resent-Bcc:",
  117. X messageid[]=        "Message-ID:",
  118. X res_messageid[]=    "Resent-Message-ID:",
  119. X inreplyto[]=        "In-Reply-To:",
  120. X references[]=        "References:",
  121. X keywords[]=        "Keywords:",
  122. X subject[]=        "Subject:",
  123. X scomments[]=        "Comments:",
  124. X encrypted[]=        "Encrypted:",
  125. X errorsto[]=        "Errors-To:",               /* sendmail extension */
  126. X retreceiptto[]=    "Return-Receipt-To:",            /* ditto ... */
  127. X precedence[]=        "Precedence:",
  128. X priority[]=        "Priority:",                /* ELM extension */
  129. X summary[]=        "Summary:",             /* USENET extension */
  130. X organisation[]=    "Organisation:",            /* ditto ... */
  131. X aorganization[]=    "Organization:",
  132. X newsgroups[]=        "Newsgroups:",
  133. X lines[]=        "Lines:",
  134. X originator[]=        "Originator:",
  135. X nntppostinghost[]=    "Nntp-Posting-Host:",
  136. X status[]=        "Status:",             /* mailer extension */
  137. X x_[]=            "X-";                /* general extension */
  138. X#define ssl(str)    str,STRLEN(str)
  139. X#define bsl(str)    {ssl(str)}
  140. Xconst char binsh[]=BinSh;
  141. X/*
  142. X *    sender determination fields in order of importance reliability
  143. X *    reply-address determination fields (wrepl specifies the weight)
  144. X */
  145. Xstatic const struct {const char*head;int len,wrepl;}sest[]=
  146. X{ {ssl(errorsto),5},{ssl(retreceiptto),6},{ssl(sender),0},{ssl(replyto),4},
  147. X  {ssl(Fromm),2},{ssl(returnpath),1}
  148. X};
  149. X/*
  150. X *    digest splitting fields (if you need to add one, send me a mail, I
  151. X *    might be interested in including it in the next release)
  152. X */
  153. Xstatic const struct {const char*hedr;int lnr;}cdigest[]=
  154. X{ bsl(path),bsl(article),bsl(returnpath),bsl(received),bsl(replyto),bsl(Fromm),
  155. X  bsl(sender),bsl(res_replyto),bsl(res_from),bsl(res_sender),bsl(date),
  156. X  bsl(res_date),bsl(to),bsl(res_to),bsl(cc),bsl(res_cc),bsl(bcc),bsl(res_bcc),
  157. X  bsl(messageid),bsl(res_messageid),bsl(inreplyto),bsl(references),
  158. X  bsl(keywords),bsl(subject),bsl(scomments),bsl(encrypted),bsl(errorsto),
  159. X  bsl(precedence),bsl(retreceiptto),bsl(summary),bsl(organisation),
  160. X  bsl(aorganization),bsl(newsgroups),bsl(status),bsl(lines),bsl(originator),
  161. X  bsl(nntppostinghost),bsl(priority)
  162. X};
  163. X
  164. Xstatic struct saved{const char*const headr;const int lenr;int rexl;char*rexp;}
  165. X rex[]={bsl(subject),bsl(references),bsl(messageid),bsl(date)};
  166. X#define subj    (rex+0)
  167. X#define refr    (rex+1)
  168. X#define msid    (rex+2)
  169. X#define hdate    (rex+3)
  170. X
  171. X#ifdef sMAILBOX_SEPARATOR
  172. X#define emboxsep    smboxsep
  173. X#define MAILBOX_SEPARATOR
  174. Xstatic const char smboxsep[]=sMAILBOX_SEPARATOR;
  175. X#endif /* sMAILBOX_SEPARATOR */
  176. X#ifdef eMAILBOX_SEPARATOR
  177. X#ifdef emboxsep
  178. X#undef emboxsep
  179. X#else
  180. X#define MAILBOX_SEPARATOR
  181. X#endif
  182. Xstatic const char emboxsep[]=eMAILBOX_SEPARATOR;
  183. X#endif /* eMAILBOX_SEPARATOR */
  184. X
  185. Xstatic errout,oldstdout,quiet,buflast;
  186. Xstatic pid_t child= -1;
  187. Xstatic FILE*mystdout;
  188. Xstatic size_t nrskip,nrtotal= -1,buflen,buffilled;
  189. Xstatic char*buf;
  190. Xstatic struct field{size_t id_len;size_t tot_len;struct field*fld_next;
  191. X char fld_text[255];}*rdheader,*iheader,*Iheader,*aheader,*Aheader,*xheader,
  192. X    *nheader;
  193. X
  194. Xstruct field*findf(p,hdr)const struct field*const p,*hdr;
  195. X{ size_t i;char*chp;        /* find a field in the linked list of fileds */
  196. X  for(i=p->id_len,chp=(char*)p->fld_text;hdr;hdr=hdr->fld_next)
  197. X     if(i==hdr->id_len&&!strnicmp(chp,hdr->fld_text,i))     /* case insensitive */
  198. X    return(struct field*)hdr;
  199. X  return(struct field*)0;
  200. X}
  201. X
  202. Xvoid*tmalloc(len)const size_t len;
  203. X{ void*p;
  204. X  if(p=malloc(len))
  205. X     return p;
  206. X  nlog(outofmem);exit(EX_OSERR);
  207. X}
  208. X
  209. Xvoid*trealloc(old,len)void*old;const size_t len;
  210. X{ if(old=realloc(old,len))
  211. X     return old;
  212. X  nlog(outofmem);exit(EX_OSERR);
  213. X}
  214. X
  215. Xtfree(a)void*a;
  216. X{ free(a);
  217. X}
  218. X
  219. X#include "shell.h"
  220. X                         /* skips an RFC 822 address */
  221. Xchar*skipwords(start,end)const char*start,*const end;
  222. X{ int delim='>',firstch;
  223. X  if((firstch= *start)=='<')
  224. X     goto machref;                 /* machine-usable reference */
  225. X  do
  226. X   { switch(*start)
  227. X      { default:                          /* normal word */
  228. X       if(firstch!='(')         /* if it did *not* start with a comment */
  229. X        { const char*p;
  230. Xnotend:          if(p=strpbrk(start,"([\"<,; \t\n"))    /* find next special */
  231. X         switch(*p)                 /* is it a big address? */
  232. X          { case '(':case '[':case '"':start=p;continue;
  233. X            default:return(char*)p;        /* address delimiter */
  234. X          }
  235. X          start=strchr(start,'\0')+1;goto notend; /* it can't be the end */
  236. X        }
  237. X    case '(':delim=')';break;                  /* comment */
  238. X    case '[':delim=']';break;               /* domain-literal */
  239. X    case '"':delim='"';
  240. X      }
  241. Xmachref:
  242. X    {int i;
  243. X     do
  244. X    if((i= *start++)==delim)         /* corresponding delimiter? */
  245. X       break;
  246. X    else if(i=='\\'&&*start)            /* skip quoted character */
  247. X       ++start;
  248. X     while(start<end);                        /* anything? */
  249. X    }
  250. X   }
  251. X  while(start<end);
  252. X  return(char*)end;
  253. X}
  254. X
  255. Xmain(lastm,argv)const char*const argv[];
  256. X{ int i,j,split=0,force=0,bogus=1,every=0,areply=0,trust=0,digest=0,
  257. X    nowait=0,keepb=0,minfields=0;
  258. X  size_t lnl;char*chp,*namep;struct field*fldp,*fp2,**afldp,*fdate;
  259. X  while(chp=(char*)*++argv)
  260. X   { if((lastm= *chp++)==FM_SKIP)
  261. X    goto number;
  262. X     else if(lastm!=FM_TOTAL)
  263. X    goto usg;
  264. X     for(;;)
  265. X      { switch(lastm= *chp++)
  266. X     { case FM_TRUST:trust=1;continue;
  267. X       case FM_REPLY:areply=1;continue;
  268. X       case FM_FORCE:force=1;continue;
  269. X       case FM_EVERY:every=1;continue;
  270. X       case FM_DIGEST:digest=1;continue;
  271. X       case FM_NOWAIT:nowait=1;continue;
  272. X       case FM_KEEPB:keepb=1;continue;
  273. X       case FM_QUIET:quiet=1;continue;
  274. X       case FM_SPLIT:split=1;
  275. X          if(!*chp&&*++argv)
  276. X         goto parsedoptions;
  277. X          goto usg;
  278. X       case HELPOPT1:case HELPOPT2:log(fmusage);log(FM_HELP);goto xusg;
  279. X       case FM_MINFIELDS:
  280. X         if(!*chp&&!(chp=(char*)*++argv))    /* concatenated or seperate? */
  281. X        goto usg;
  282. Xnumber:       default:
  283. X          if(*chp-'0'>(unsigned)9)
  284. Xusg:           { log(fmusage);
  285. Xxusg:         return EX_USAGE;
  286. X           }
  287. X          i=strtol(chp,(char**)0,10);
  288. X          switch(lastm)            /* where does the number go? */
  289. X           { case FM_SKIP:nrskip=i;break;
  290. X         case FM_TOTAL:nrtotal=i;break;
  291. X         default:minfields=i;
  292. X           }
  293. X          break;
  294. X       case FM_BOGUS:bogus=0;continue;
  295. X       case FM_ADD_IFNOT:case FM_ADD_ALWAYS:case FM_REN_INSERT:
  296. X       case FM_DEL_INSERT:case FM_EXTRACT:
  297. X          if(!*chp&&!(chp=(char*)*++argv))    /* concatenated or seperate? */
  298. X         goto usg;
  299. X          if(!breakfield(chp,i=strlen(chp)))
  300. X           { nlog("Invalid field-name: \"");log(chp);log("\"\n");goto usg;
  301. X           }
  302. X          chp[i++]='\n';                   /* terminate the line */
  303. X          addfield(lastm==FM_REN_INSERT?&iheader:lastm==FM_DEL_INSERT?
  304. X           &Iheader:lastm==FM_ADD_IFNOT?&aheader:lastm==FM_ADD_ALWAYS?
  305. X           &Aheader:&xheader,chp,i);
  306. X       case '\0':;
  307. X     }
  308. X    break;
  309. X      }
  310. X   }
  311. Xparsedoptions:
  312. X  mystdout=stdout;signal(SIGPIPE,SIG_IGN);
  313. X  if(split)
  314. X   { oldstdout=dup(STDOUT);fclose(stdout);startprog(argv);
  315. X     if(!minfields)                   /* no user specified minimum? */
  316. X    minfields=DEFminfields;                 /* take our default */
  317. X   }
  318. X  else if(every||digest||minfields)          /* these combinations are only */
  319. X     goto usg;                  /* valid in combination with split */
  320. X  if(!areply&&keepb)
  321. X     goto usg;
  322. X  namep=malloc(1);buf=malloc(buflen=BSIZE);           /* prime some buffers */
  323. X  i=maxindex(rex);
  324. X  do rex[i].rexp=malloc(1);
  325. X  while(i--);
  326. X  fdate=0;addfield(&fdate,date,STRLEN(date)); /* fdate is only for searching */
  327. X  while((buflast=getchar())=='\n');             /* skip leading garbage */
  328. X#ifdef sMAILBOX_SEPARATOR
  329. X  if(!readhead())                      /* check for a leading */
  330. X   { if(!strncmp(smboxsep,buf,STRLEN(smboxsep)))    /* mailbox separator */
  331. X      { buffilled=0;goto startover;                  /* skip it */
  332. X      }
  333. X   }
  334. X  else
  335. X#endif
  336. Xstartover:
  337. X     while(readhead());                 /* read in the whole header */
  338. X {size_t namel=0;size_t lenparkedbuf;void*parkedbuf;
  339. X  i=maxindex(rex);
  340. X  do rex[i].rexl=0;
  341. X  while(i--);                     /* all state has been reset */
  342. X  for(fldp=rdheader;fldp;fldp=fldp->fld_next)        /* go through the linked */
  343. X   { int nowm;                        /* list of header-fields */
  344. X     chp=fldp->fld_text;
  345. X     if((j=fldp->id_len)==STRLEN(From_)&&fldp==rdheader&&eqFrom_(chp))
  346. X      { nowm=trust?1:3/*wreply*/;goto foundfrom;        /* leading From_ */
  347. X      }
  348. X     concatenate(fldp);i=maxindex(sest);      /* look for other "sender" */
  349. X     while((sest[i].len!=j||strnicmp(sest[i].head,chp,j))&&i--);   /* fields */
  350. X     if(i>=0)                          /* found anything? */
  351. X      { const char*saddr,*end;
  352. X    nowm=areply?keepb&&sest[i].head==replyto?    /* determine the weight */
  353. X     maxindex(sest)+1:sest[i].wrepl:i;             /* of this find */
  354. Xfoundfrom:
  355. X    saddr=end=chp+fldp->tot_len-1;chp+=j;
  356. X    for(;;chp=skipwords(chp,end))            /* skip RFC 822 wise */
  357. X     { switch(*(chp=pstrspn(chp,",; \t")))
  358. X        { default:
  359. X         if(saddr==end)           /* if we haven't got anything yet */
  360. X            saddr=chp;            /* this might be the address */
  361. X          case '(':continue;          /* a comment, don't bother */
  362. X          case '<':saddr=chp;          /* hurray, machine useable */
  363. X          case '\n':;
  364. X        }
  365. X       break;
  366. X     }           /* check if the address has any length and extract it */
  367. X    if((i=skipwords(saddr,end)-saddr)&&(!namel||nowm>lastm))
  368. X     { tmemmove(namep=realloc(namep,i+1),saddr,namel=i);
  369. X       lastm=mystrstr(namep,".UUCP",end)?nowm-maxindex(sest)-1:nowm;
  370. X     }
  371. X      }                       /* save headers for later perusal */
  372. X     i=maxindex(rex);chp=fldp->fld_text;j=fldp->id_len;      /* e.g. for areply */
  373. X     while((rex[i].lenr!=j||strnicmp(rex[i].headr,chp,j))&&i--);
  374. X     chp+=j;
  375. X     if(i>=0&&(j=fldp->tot_len-j)>1)              /* found anything? */
  376. X    tmemmove(rex[i].rexp=realloc(rex[i].rexp,rex[i].rexl=j),chp,j);
  377. X   }
  378. X  tmemmove(parkedbuf=malloc(buffilled),buf,lenparkedbuf=buffilled);
  379. X  buffilled=0;          /* move the contents of buf out of the way temporarily */
  380. X  if(areply)              /* autoreply requested, we clean up the header */
  381. X   { for(fldp= *(afldp= &rdheader);fldp;)
  382. X    if(!(fp2=findf(fldp,iheader))||fp2->id_len<fp2->tot_len-1)
  383. X       *afldp=fldp->fld_next,free(fldp),fldp= *afldp;      /* remove all */
  384. X    else                    /* except the ones mentioned */
  385. X       fldp= *(afldp= &fldp->fld_next);               /* as -i ...: */
  386. X     loadbuf(to,STRLEN(to));loadchar(' ');       /* generate the To: field */
  387. X     if(namel)               /* did we find a valid return address at all? */
  388. X    loadbuf(namep,namel);                  /* then insert it here */
  389. X     else
  390. X    loadbuf(unknown,STRLEN(unknown));        /* or insert our default */
  391. X     loadchar('\n');addbuf();                   /* add it to rdheader */
  392. X     if(subj->rexl)                      /* any Subject: found? */
  393. X      { loadbuf(subject,STRLEN(subject));      /* sure, check for leading */
  394. X    if(strnicmp(pstrspn(chp=subj->rexp," \t"),Re,STRLEN(Re)))     /* Re: */
  395. X       loadbuf(re,STRLEN(re));           /* no Re: , add one ourselves */
  396. X    loadsaved(subj);addbuf();
  397. X      }
  398. X     if(refr->rexl||msid->rexl)           /* any References: or Message-ID: */
  399. X      { loadbuf(references,STRLEN(references));      /* yes, insert References: */
  400. X    if(refr->rexl)
  401. X     { if(msid->rexl)        /* if we're going to append a Message-ID */
  402. X          --refr->rexl;            /* suppress the trailing newline */
  403. X       loadsaved(refr);
  404. X     }
  405. X    if(msid->rexl)
  406. X       loadsaved(msid);               /* here's our missing newline */
  407. X    addbuf();
  408. X      }
  409. X     if(msid->rexl)             /* do we add an In-Reply-To: field? */
  410. X    loadbuf(inreplyto,STRLEN(inreplyto)),loadsaved(msid),addbuf();
  411. X   }                       /* are we allowed to add From_ lines? */
  412. X  else if(!force&&(!rdheader||!eqFrom_(rdheader->fld_text))) /* was missing? */
  413. X   { struct field*old;time_t t;             /* insert a From_ line up front */
  414. X     t=time((time_t*)0);old=rdheader;rdheader=0;loadbuf(From_,STRLEN(From_));
  415. X     if(namel)                  /* we found a valid return address */
  416. X    loadbuf(namep,namel);
  417. X     else
  418. X    loadbuf(unknown,STRLEN(unknown));
  419. X     if(!hdate->rexl||findf(fdate,iheader)||findf(fdate,Iheader))   /* Date: */
  420. X    loadchar(' '),chp=ctime(&t),loadbuf(chp,strlen(chp));    /* no Date:, */
  421. X     else                     /* we generate it ourselves */
  422. X    loadsaved(hdate);          /* yes, found Date:, then copy from it */
  423. X     addbuf();rdheader->fld_next=old;
  424. X   }
  425. X  for(fldp=aheader;fldp;fldp=fldp->fld_next)
  426. X     if(!findf(fldp,rdheader))               /* only add what didn't exist */
  427. X    addfield(&nheader,fldp->fld_text,fldp->tot_len);
  428. X  for(fldp= *(afldp= &rdheader);fldp;)
  429. X   { i=fldp->tot_len;
  430. X     if(findf(fldp,xheader))                   /* extract fields */
  431. X    putssn(fldp->fld_text+fldp->id_len,i-fldp->id_len);
  432. X     if(fp2=findf(fldp,Iheader))                /* delete fields */
  433. X      { *afldp=fldp->fld_next;free(fldp);fldp= *afldp;continue;
  434. X      }
  435. X     else if((fp2=findf(fldp,iheader))&&!(areply&&fp2->id_len==fp2->tot_len-1))
  436. X      { *afldp=fldp=realloc(fldp,FLD_HEADSIZ+(fldp->tot_len=i+STRLEN(OldP)));
  437. X    tmemmove(fldp->fld_text+STRLEN(OldP),fldp->fld_text,i);
  438. X    tmemmove(fldp->fld_text,OldP,STRLEN(OldP));        /* rename fields */
  439. X      }
  440. X     fldp= *(afldp= &fldp->fld_next);
  441. X   }                    /* restore the saved contents of buf */
  442. X  tmemmove(buf,parkedbuf,buffilled=lenparkedbuf);free(parkedbuf);
  443. X }
  444. X  if(xheader)                     /* we're just extracting fields */
  445. X     clearfield(&rdheader),clearfield(&nheader);        /* throw it away */
  446. X  else                 /* otherwise, display the new & improved header */
  447. X   { flushfield(&rdheader);flushfield(&nheader);dispfield(Aheader);
  448. X     dispfield(iheader);dispfield(Iheader);putcs('\n');      /* make sure it is */
  449. X   }                        /* followed by an empty line */
  450. X  if(areply&&!keepb||xheader)          /* decision time, do we keep the rest? */
  451. X   { if(split)
  452. X    closemine();
  453. X     opensink();                     /* discard the body */
  454. X   }
  455. X  lnl=1;                      /* last line was a newline */
  456. X  if(buffilled==1)           /* the header really ended with a newline */
  457. X     buffilled=0;          /* throw it away, since we already inserted it */
  458. X  do                     /* continue the quest, line by line */
  459. X   { if(!buffilled)                      /* is it really empty? */
  460. X    readhead();                      /* read the next field */
  461. X     if(!rdheader&&eqFrom_(buf))         /* special case, From_ line */
  462. X    addbuf();           /* add it manually, in case readhead() didn't */
  463. X     if(rdheader)            /* anything looking like a header found? */
  464. X    if(eqFrom_(chp=rdheader->fld_text))          /* check if it's From_ */
  465. X     { register size_t k;
  466. X       if(split&&(lnl||every)&&    /* more thorough check for a postmark */
  467. X        (k=strcspn(chp=pstrspn(chp+STRLEN(From_)," \t")," \t\n"))&&
  468. X        *pstrspn(chp+k," \t")!='\n')
  469. X          goto accuhdr;             /* ok, postmark found, split it */
  470. X       if(bogus)                           /* disarm */
  471. X          putcs(ESCAP);
  472. X     }
  473. X    else if(split&&digest&&(lnl||every)&&digheadr())      /* digest? */
  474. Xaccuhdr: { for(i=minfields;--i&&readhead()&&digheadr(););   /* found enough? */
  475. X       if(!i)                       /* then split it! */
  476. Xsplitit:    { if(!lnl)        /* did the previous mail end with an empty line? */
  477. X         putcs('\n');                  /* but now it does :-) */
  478. X          if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
  479. X         nlog(couldntw),log(", continuing...\n"),split= -1;
  480. X          if(!nowait)
  481. X         waitforit();         /* wait till the child has finished */
  482. X          startprog(argv);goto startover;        /* and there we go again */
  483. X        }
  484. X     }
  485. X#ifdef MAILBOX_SEPARATOR
  486. X     if(!strncmp(emboxsep,buf,STRLEN(emboxsep)))         /* end of mail? */
  487. X      { if(split)               /* gobble up the next start separator */
  488. X     { buffilled=0;
  489. X#ifdef sMAILBOX_SEPARATOR
  490. X       getline();buffilled=0;         /* but only if it's defined */
  491. X#endif
  492. X       if(buflast!=EOF)                       /* if any */
  493. X          goto splitit;
  494. X       break;
  495. X     }
  496. X    else if(bogus)
  497. X       goto putsp;                   /* escape it with a space */
  498. X      }
  499. X     else if(!strncmp(smboxsep,buf,STRLEN(smboxsep)&&bogus))
  500. Xputsp:    putcs(' ');
  501. X#endif /* MAILBOX_SEPARATOR */
  502. X     lnl=buffilled==1;              /* check if we just read an empty line */
  503. X     if(areply&&bogus)                      /* escape the body */
  504. X    if(fldp=rdheader)          /* we already read some "valid" fields */
  505. X     { register char*p;
  506. X       rdheader=0;
  507. X       do                   /* careful, then can contain newlines */
  508. X        { fp2=fldp->fld_next;chp=fldp->fld_text;
  509. X          do putcs(ESCAP),putssn(chp,(p=strchr(chp,'\n')+1)-chp);
  510. X          while((chp=p)<fldp->fld_text+fldp->tot_len);
  511. X          free(fldp);                    /* delete it */
  512. X        }
  513. X       while(fldp=fp2);               /* escape all fields we found */
  514. X     }
  515. X    else
  516. X     { if(buffilled>1)      /* we don't escape empty lines, looks neat */
  517. X          putcs(ESCAP);
  518. X       goto flbuf;
  519. X     }
  520. X     else if(rdheader)
  521. X    flushfield(&rdheader); /* beware, after this buf can still be filled */
  522. X     else
  523. Xflbuf:    putssn(buf,buffilled),buffilled=0;
  524. X   }                   /* make sure the mail ends with an empty line */
  525. X  while(buffilled||!lnl||buflast!=EOF);
  526. X  closemine();child= -1;waitforit();            /* wait for everyone */
  527. X  return split<0?EX_IOERR:EX_OK;
  528. X}
  529. X
  530. Xconcatenate(fldp)struct field*const fldp;   /* concatenate a continued field */
  531. X{ register char*p;register size_t l;
  532. X  l=fldp->tot_len;p=fldp->fld_text;
  533. X  while(l--)
  534. X     if(*p++=='\n'&&l)         /* by substituting all newlines except the last */
  535. X    p[-1]=' ';
  536. X}
  537. X
  538. XeqFrom_(a)const char*const a;
  539. X{ return!strncmp(a,From_,STRLEN(From_));
  540. X}
  541. X     /* checks if the last field in rdheader looks like a know digest header */
  542. Xdigheadr()
  543. X{ char*chp;int i,j;struct field*fp;
  544. X  for(fp=rdheader;fp->fld_next;fp=fp->fld_next);     /* skip to the last */
  545. X  i=maxindex(cdigest);chp=fp->fld_text;j=fp->id_len;
  546. X  while((cdigest[i].lnr!=j||strnicmp(cdigest[i].hedr,chp,j))&&i--);
  547. X  return i>=0||j>STRLEN(x_)&&!strnicmp(x_,chp,STRLEN(x_));
  548. X}
  549. X
  550. Xbreakfield(line,len)const char*const line;size_t len;       /* look where the */
  551. X{ const char*p=line;               /* fieldname ends (RFC 822 specs) */
  552. X  if(eqFrom_(line))                      /* special case, From_ */
  553. X     return STRLEN(From_);
  554. X  while(len--&&!iscntrl(*p))            /* no control characters allowed */
  555. X   { switch(*p++)
  556. X      { default:continue;
  557. X    case HEAD_DELIMITER:len=p-line;return len==1?0:len;      /* eureka! */
  558. X    case ' ':;                    /* no spaces allowed */
  559. X      }
  560. X     break;
  561. X   }
  562. X  return 0;            /* sorry, does not seem to be a legitimate field */
  563. X}
  564. X
  565. Xaddfield(pointer,text,totlen)register struct field**pointer;      /* add new */
  566. X const char*const text;const size_t totlen;       /* field to a linked list */
  567. X{ register struct field*p;
  568. X  while(*pointer)                  /* skip to the end of the list */
  569. X     pointer= &(*pointer)->fld_next;
  570. X  (*pointer=p=malloc(FLD_HEADSIZ+totlen))->fld_next=0;     /* create the field */
  571. X  p->id_len=breakfield(text,totlen);
  572. X  tmemmove(p->fld_text,text,p->tot_len=totlen);        /* copy the contents */
  573. X}
  574. X
  575. Xclearfield(pointer)register struct field**pointer;     /* delete the whole */
  576. X{ register struct field*p,*q;                /* linked list of fields */
  577. X  for(p= *pointer,*pointer=0;p;p=q)
  578. X     q=p->fld_next,free(p);
  579. X}
  580. X
  581. Xflushfield(pointer)register struct field**pointer;     /* delete and print */
  582. X{ register struct field*p,*q;                   /* them as you go */
  583. X  for(p= *pointer,*pointer=0;p;p=q)
  584. X     q=p->fld_next,putssn(p->fld_text,p->tot_len),free(p);
  585. X}
  586. X
  587. Xdispfield(p)const register struct field*p;   /* print list non-destructively */
  588. X{ for(;p;p=p->fld_next)
  589. X     if(p->id_len<p->tot_len-1)             /* any contents to display? */
  590. X    putssn(p->fld_text,p->tot_len);
  591. X}
  592. X
  593. Xreadhead()        /* try and append one valid field to rdheader from stdin */
  594. X{ getline();
  595. X  if(!eqFrom_(buf))                    /* it's not a From_ line */
  596. X   { if(!breakfield(buf,buffilled))       /* not the start of a valid field */
  597. X    return 0;
  598. X     for(;;getline())              /* get the rest of the continued field */
  599. X      { switch(buflast)                 /* will this line be continued? */
  600. X     { case ' ':case '\t':continue;              /* yep, it sure is */
  601. X     }
  602. X    break;
  603. X      }
  604. X   }
  605. X  else if(rdheader)
  606. X     return 0;                       /* the From_ line was a fake! */
  607. X  addbuf();return 1;          /* phew, got the field, add it to rdheader */
  608. X}
  609. X
  610. Xaddbuf()
  611. X{ addfield(&rdheader,buf,buffilled);buffilled=0;
  612. X}
  613. X
  614. Xgetline()                   /* read a newline-terminated line */
  615. X{ if(buflast!=EOF)                 /* do we still have a leftover? */
  616. X     loadchar(buflast);                  /* load it into the buffer */
  617. X  if(buflast!='\n')
  618. X   { int ch;
  619. X     while((ch=getchar())!=EOF&&ch!='\n')
  620. X    loadchar(ch);                /* load the rest of the line */
  621. X     loadchar('\n');            /* make sure (!), it ends with a newline */
  622. X   }        /* (some code in formail.c depends on a terminating newline) */
  623. X  return buflast=getchar();            /* look ahead, one character */
  624. X}
  625. X
  626. Xloadsaved(sp)const struct saved*const sp;       /* load a some saved text */
  627. X{ switch(*sp->rexp)
  628. X   { default:loadchar(' ');           /* make sure it has leading whitspace */
  629. X     case ' ':case '\t':;
  630. X   }
  631. X  loadbuf(sp->rexp,sp->rexl);
  632. X}
  633. X
  634. Xloadbuf(text,len)const char*const text;const size_t len;    /* append to buf */
  635. X{ if(buffilled+len>buflen)              /* buf can't hold the text */
  636. X     buf=realloc(buf,buflen+=BSIZE);
  637. X  tmemmove(buf+buffilled,text,len);buffilled+=len;
  638. X}
  639. X
  640. Xloadchar(c)const int c;                  /* append one character to buf */
  641. X{ if(buffilled==buflen)
  642. X     buf=realloc(buf,buflen+=BSIZE);
  643. X  buf[buffilled++]=c;
  644. X}
  645. X
  646. Xlog(a)const char*const a;                     /* error output */
  647. X{ fputs(a,stderr);
  648. X}
  649. X
  650. Xtputssn(a,l)const char*a;size_t l;
  651. X{ while(l--)
  652. X     putcs(*a++);
  653. X}
  654. X
  655. Xstartprog(argv)const char*const*const argv;
  656. X{ int poutfd[2];
  657. X  if(!nrtotal)                    /* no more mails to display? */
  658. X     goto squelch;
  659. X  if(nrskip)                  /* should we still skip this mail? */
  660. X   { --nrskip;                             /* count it */
  661. Xsquelch:
  662. X     opensink();return;
  663. X   }
  664. X  if(nrtotal>0)
  665. X     --nrtotal;                             /* count it */
  666. X  dup(oldstdout);pipe(poutfd);
  667. X  if(!(child=fork()))    /* DON'T fclose(stdin) here, provokes a bug on HP/UX */
  668. X   { close(STDIN);close(oldstdout);close(PWRO);dup(PRDO);close(PRDO);
  669. X     shexec(argv);
  670. X   }
  671. X  close(STDOUT);close(PRDO);
  672. X  if(STDOUT!=dup(PWRO)||!(mystdout=fdopen(STDOUT,"a")))
  673. X     nofild();
  674. X  close(PWRO);
  675. X  if(-1==child)
  676. X     nlog("Can't fork\n"),exit(EX_OSERR);
  677. X}
  678. X
  679. Xnofild()
  680. X{ nlog("File table full\n");exit(EX_OSERR);
  681. X}
  682. X
  683. Xwaitforit()
  684. X{ int i;pid_t j;
  685. X  while(child!=(j=wait(&i))||WIFSTOPPED(i))
  686. X    if(-1==j)
  687. X       return;
  688. X}
  689. X
  690. Xnlog(a)const char*const a;
  691. X{ log(NAMEPREFIX);log(a);
  692. X}
  693. X
  694. Xlogqnl(a)const char*const a;
  695. X{ log(" \"");log(a);log("\"\n");
  696. X}
  697. X
  698. Xclosemine()
  699. X{ if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
  700. X     nlog(couldntw),log("\n"),exit(EX_IOERR);
  701. X}
  702. X
  703. Xopensink()
  704. X{ if(!(mystdout=fopen(DevNull,"a")))
  705. X     nofild();
  706. X}
  707. X
  708. Xstrnicmp(a,b,l)register const char*a,*b;register unsigned l;
  709. X{ int i,j;
  710. X  if(l)                         /* case insensitive strncmp */
  711. X     do
  712. X      { while(*a&&*a==*b&&--l)
  713. X       ++a,++b;
  714. X    if(!l)
  715. X       break;
  716. X    if((i= *a++)>='A'&&i<='Z')
  717. X       i+='a'-'A';
  718. X    if((j= *b++)>='A'&&j<='Z')
  719. X       j+='a'-'A';
  720. X    if(j!=i)
  721. X       return i>j?1:-1;
  722. X      }
  723. X     while(i&&j&&--l);
  724. X  return 0;
  725. X}
  726. X
  727. Xmystrstr(whole,part,end)const char*whole,*const part,*end;
  728. X{ size_t i;
  729. X  for(end-=(i=strlen(part))+1;--end>=whole;)
  730. X     if(!strncmp(end,part,i))
  731. X    return 1;
  732. X  return 0;
  733. X}
  734. END_OF_FILE
  735. if test 23879 -ne `wc -c <'procmail/formail.c'`; then
  736.     echo shar: \"'procmail/formail.c'\" unpacked with wrong size!
  737. fi
  738. # end of 'procmail/formail.c'
  739. fi
  740. if test -f 'procmail/man/lockfile.man' -a "${1}" != "-c" ; then 
  741.   echo shar: Will not clobber existing file \"'procmail/man/lockfile.man'\"
  742. else
  743. echo shar: Extracting \"'procmail/man/lockfile.man'\" \(3439 characters\)
  744. sed "s/^X//" >'procmail/man/lockfile.man' <<'END_OF_FILE'
  745. X.de Id
  746. X.ds Rv \\$3
  747. X.ds Dt \\$4
  748. X..
  749. X.Id $Id: lockfile.man,v 2.7 1992/06/30 16:43:23 berg Rel $
  750. X.de Sh
  751. X.br
  752. X.ne 11
  753. X.SH "\\$1"
  754. X..
  755. X.de Ss
  756. X.br
  757. X.ne 10
  758. X.SS "\\$1"
  759. X..
  760. X.de Tp
  761. X.br
  762. X.ne 9
  763. X.TP \\$1
  764. X..
  765. X.de Rs
  766. X.na
  767. X.nf
  768. X.RS
  769. X..
  770. X.de Re
  771. X.RE
  772. X.fi
  773. X.ad
  774. X..
  775. X.TH LOCKFILE 1 \*(Dt BuGless
  776. X.SH NAME
  777. X.na
  778. Xlockfile \- conditional semaphore-file creator
  779. X.SH SYNOPSIS
  780. X.B lockfile
  781. X.I "\fB\-\fPsleeptime"
  782. X|
  783. X.I "\fB\-r\fPretries"
  784. X|
  785. X.B "\-!"
  786. X|
  787. X.if n .ti +0.5i
  788. X.I "\fB\-l\fPlocktimeout"
  789. X|
  790. X.I "\fB\-s\fPsuspend"
  791. X|
  792. X.B "\-ml"
  793. X|
  794. X.B "\-mu"
  795. X|
  796. X.I filename
  797. X\&.\|.\|.
  798. X.ad
  799. X.Sh DESCRIPTION
  800. X.B lockfile
  801. Xcan be used to create one or more
  802. X.I semaphore
  803. X.IR files .
  804. XIf lockfile can't create all the specified files (in the specified order),
  805. Xit waits
  806. X.I sleeptime
  807. X(defaults to 8) seconds and retries the last file that didn't succeed.
  808. XYou can specify the number of
  809. X.I retries
  810. Xto do until failure is returned.
  811. XIf the number of
  812. X.I retries
  813. Xis 0 (default) lockfile will retry forever.
  814. X.PP
  815. XIf the number of
  816. X.I retries
  817. Xexpires before all files have been created, lockfile returns failure and
  818. Xremoves all the files it created up till that point.
  819. X.PP
  820. XThe return value of lockfile can be easily inverted by specifying
  821. X.B \-!
  822. Xas an argument (comes in handy in shell scripts).
  823. X.PP
  824. XAll flags can be specified anywhere on the command line, they will be
  825. Xprocessed when encountered.  The command line is simply parsed from
  826. Xleft to right.
  827. X.PP
  828. XAll files created by lockfile will have access permission 0, and therefore
  829. Xwill have to be removed with
  830. X.B rm
  831. X.BR \-f .
  832. X.PP
  833. XIf you specify a
  834. X.I locktimeout
  835. Xthen a lockfile will be removed by force after locktimeout seconds have
  836. Xpassed since the lockfile was last modified/created.  Lockfile is clock skew
  837. Ximmune.  After a lockfile has been removed by force, a suspension of
  838. X.I suspend
  839. Xseconds (defaults to 16) is taken into account, in order to prevent the
  840. Xinadvertent immediate removal of any newly created lockfile by another program
  841. X(compare
  842. X.BR SUSPEND
  843. Xin
  844. X.BR procmail (1)).
  845. X.Ss "Mailbox locks"
  846. XIf the permissions on the system mail spool directory allow it, or if lockfile
  847. Xis suitably setuid or setgid, it will be able to lock and unlock your system
  848. Xmailbox by using the options
  849. X.B "\-ml"
  850. Xand
  851. X.B "\-mu"
  852. Xrespectively.
  853. X.Sh FILES
  854. X.Tp 2.3i
  855. X.B  /etc/passwd
  856. Xto get the invoker's loginname
  857. X.B +SYSTEM_MBOX++DEFlockext+
  858. Xlockfile for the system mailbox
  859. X.Sh "SEE ALSO"
  860. X.na
  861. X.BR rm (1),
  862. X.BR mail (1),
  863. X.BR binmail (1),
  864. X.BR sendmail (8),
  865. X.BR procmail (1)
  866. X.ad
  867. X.Sh DIAGNOSTICS
  868. X.Tp 2.3i
  869. XForcing lock on "x"
  870. XSpecified lockfile is going to be removed by force because of a timeout
  871. X(compare
  872. X.BR LOCKTIMEOUT
  873. Xin
  874. X.BR procmail (1)).
  875. X.Tp
  876. XSorry, giving up
  877. XThe
  878. X.I retries
  879. Xlimit has been reached or a signal was received.
  880. X.Sh BUGS
  881. Xlockfile is only a simple program and can't process concatenated options.
  882. X.Sh NOTES
  883. XCalling up lockfile with the \-+HELPOPT1+ or \-+HELPOPT2+ options will cause
  884. Xit to display a command-line help page.
  885. X.PP
  886. XMultiple
  887. X.B \-!
  888. Xflags will toggle the return status.
  889. X.PP
  890. XSince flags can occur anywhere on the command line, any filename starting
  891. Xwith a '-' has to be preceded by './'.
  892. X.PP
  893. XThe number of
  894. X.I retries
  895. Xis global.  That is, it is not reset when a new file is being created.
  896. XIt can, however, be reset by specifying
  897. X.RI \-r newretries
  898. Xafter every file on the command line.
  899. X.PP
  900. Xlockfile performs the locking in an NFS-secure way.
  901. X.Sh AUTHOR
  902. XStephen R. van den Berg at RWTH-Aachen, Germany
  903. X.Rs
  904. Xberg@pool.informatik.rwth-aachen.de
  905. X.br
  906. Xberg@physik.tu-muenchen.de
  907. X.Re
  908. END_OF_FILE
  909. if test 3439 -ne `wc -c <'procmail/man/lockfile.man'`; then
  910.     echo shar: \"'procmail/man/lockfile.man'\" unpacked with wrong size!
  911. fi
  912. # end of 'procmail/man/lockfile.man'
  913. fi
  914. if test -f 'procmail/procmail.c' -a "${1}" != "-c" ; then 
  915.   echo shar: Will not clobber existing file \"'procmail/procmail.c'\"
  916. else
  917. echo shar: Extracting \"'procmail/procmail.c'\" \(24484 characters\)
  918. sed "s/^X//" >'procmail/procmail.c' <<'END_OF_FILE'
  919. X/************************************************************************
  920. X *    procmail.c    an autonomous mail processor            *
  921. X *                                    *
  922. X *    It has been designed to be able to be run suid root and (in    *
  923. X *    case your mail spool area is *not* world writeable) sgid    *
  924. X *    mail (or daemon), without creating security holes.        *
  925. X *                                    *
  926. X *    Seems to be perfect.                        *
  927. X *                                    *
  928. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  929. X *    The sources can be freely copied for non-commercial use.    *
  930. X *    #include "README"                        *
  931. X *                                    *
  932. X ************************************************************************/
  933. X#ifdef RCS
  934. Xstatic char rcsid[]="$Id: procmail.c,v 2.37 1992/07/01 12:47:38 berg Rel $";
  935. X#endif
  936. X#include "config.h"
  937. X#define MAIN
  938. X#include "procmail.h"
  939. X#include "shell.h"
  940. X#include "patchlevel.h"
  941. X
  942. Xchar*buf,*buf2,*globlock,*loclock,*tolock,*lastfolder;
  943. Xconst char shellflags[]="SHELLFLAGS",shell[]="SHELL",
  944. X shellmetas[]="SHELLMETAS",lockext[]="LOCKEXT",newline[]="\n",binsh[]=BinSh,
  945. X unexpeof[]="Unexpected EOL\n",*const*gargv,*sgetcp,*rcfile=PROCMAILRC,
  946. X dirsep[]=DIRSEP,msgprefix[]="MSGPREFIX",devnull[]=DevNull,
  947. X executing[]="Executing",oquote[]=" \"",cquote[]="\"\n",procmailn[]="procmail",
  948. X whilstwfor[]=" whilst waiting for ",sdelivered[]="DELIVERED";
  949. Xstatic const char slinebuf[]="LINEBUF",tokey[]=TOkey,eumask[]="UMASK",
  950. X tosubstitute[]=TOsubstitute,lockfile[]="LOCKFILE",defaultf[]="DEFAULT",
  951. X maildir[]="MAILDIR",couldnread[]="Couldn't read",logfile[]="LOGFILE",
  952. X orgmail[]="ORGMAIL",user[]="USER",tmp[]=Tmp,home[]="HOME",sfolder[]=FOLDER,
  953. X sendmail[]="SENDMAIL",host[]="HOST",Log[]="LOG",From[]=FROM,
  954. X exflags[]=RECFLAGS,systm_mbox[]=SYSTEM_MBOX,cldchd[]="Couldn't chdir to",
  955. X pmusage[]=PM_USAGE;
  956. Xstruct varval strenvvar[]={{"LOCKSLEEP",DEFlocksleep},
  957. X {"LOCKTIMEOUT",DEFlocktimeout},{"SUSPEND",DEFsuspend},
  958. X {"NORESRETRY",DEFnoresretry},{"TIMEOUT",DEFtimeout}};
  959. Xlong lastdump;             /* the no. of bytes dumped (saved) recently */
  960. Xint retval=EX_CANTCREAT,retvl2=EX_OK,sh,pwait,lcking,locknext,verbose,rc= -2,
  961. X tofolder,tofile,ignwerr,fakedelivery,
  962. X linebuf=mx(DEFlinebuf,STRLEN(systm_mbox)<<1);
  963. Xvolatile int nextexit;                   /* if termination is imminent */
  964. Xvolatile time_t alrmtime;
  965. Xpid_t thepid,pidchild;
  966. Xstatic volatile mailread;    /* if the mail is completely read in already */
  967. Xstatic long filled;                   /* the length of the mail */
  968. Xstatic char*themail,*thebody;            /* the head and body of the mail */
  969. X
  970. Xmain(argc,argv)const char*const argv[];
  971. X{ static char flags[maxindex(exflags)];
  972. X  static const char*const keepenv[]=KEEPENV,*const prestenv[]=PRESTENV,
  973. X   *const trusted_ids[]=TRUSTED_IDS;
  974. X  char*chp,*startchar,*chp2,*fromwhom=0;long tobesent;
  975. X  int i,lastcond,succeed;uid_t uid;gid_t gid;
  976. X#define Deliverymode    lastcond
  977. X#define Presenviron    i
  978. X#define Privileged    succeed
  979. X  Deliverymode=strcmp(lastdirsep(argv[0]),procmailn);
  980. X  for(Presenviron=argc=0;(chp=(char*)argv[++argc])&&*chp=='-';)
  981. X     for(;;)                           /* processing options */
  982. X      { switch(*++chp)
  983. X     { case VERSIONOPT:log(VERSION);return EX_OK;
  984. X       case HELPOPT1:case HELPOPT2:log(pmusage);log(PM_HELP);
  985. X          log(PM_QREFERENCE);return EX_USAGE;
  986. X       case PRESERVOPT:Presenviron=1;continue;
  987. X       case TEMPFAILOPT:retval=EX_TEMPFAIL;continue;
  988. X       case FROMWHOPT:case ALTFROMWHOPT:
  989. X          if(*++chp)
  990. X         fromwhom=chp;
  991. X          else if(chp=(char*)argv[argc+1])
  992. X         ++argc,fromwhom=chp;
  993. X          else
  994. X         log("Missing name\n");
  995. X          break;
  996. X       case DELIVEROPT:Deliverymode=1;++chp;goto last_option;
  997. X       default:log("Unrecognised options:");logqnl(chp);
  998. X          log(pmusage);log("Processing continued\n");
  999. X       case '\0':;
  1000. X     }
  1001. X    break;
  1002. X      }
  1003. Xlast_option:
  1004. X  if(!Presenviron)                     /* drop the environment */
  1005. X   { const char**emax=(const char**)environ,*const*ep,*const*kp;
  1006. X     for(kp=keepenv;*kp;++kp)                 /* preserve a happy few */
  1007. X    for(i=strlen(*kp),ep=emax;chp2=(char*)*ep;++ep)
  1008. X       if(!strncmp(*kp,chp2,i)&&chp2[i]=='=')
  1009. X        { *emax++=chp2;break;
  1010. X        }
  1011. X     *emax=0;                            /* drop the rest */
  1012. X   }
  1013. X#ifdef LD_ENV_FIX
  1014. X {const char**emax=(const char**)environ,**ep;static const char ld_[]="LD_";
  1015. X  for(ep=emax;*emax;++emax);          /* find the end of the environment */
  1016. X  while(*ep)
  1017. X     if(!strncmp(ld_,*ep++,STRLEN(ld_)))           /* it starts with LD_ */
  1018. X    *--ep= *--emax,*emax=0;                /* copy from the end */
  1019. X }
  1020. X#endif /* LD_ENV_FIX */
  1021. X  if(Deliverymode&&(!chp||(!*chp&&!(chp=(char*)argv[++argc]))))
  1022. X     Deliverymode=0,log("Missing recipient\n");
  1023. X {struct passwd*pass,*passinvk;
  1024. X  passinvk=getpwuid(uid=getuid());Privileged=1;
  1025. X  if(*trusted_ids&&uid!=geteuid())
  1026. X   { struct group*grp;const char*const*kp;
  1027. X     if(passinvk)                  /* check out the invoker's uid */
  1028. X    for(chp2=passinvk->pw_name,kp=trusted_ids;*kp;)
  1029. X       if(!strcmp(chp2,*kp++))          /* is it among the privileged? */
  1030. X        { endpwent();goto privileged;
  1031. X        }
  1032. X     endpwent();
  1033. X     if(grp=getgrgid(getgid()))              /* check out the invoker's gid */
  1034. X       for(chp2=grp->gr_name,kp=trusted_ids;*kp;)
  1035. X      if(!strcmp(chp2,*kp++))          /* is it among the privileged? */
  1036. X       { endgrent();goto privileged;
  1037. X       }
  1038. X    endgrent();Privileged=0;
  1039. X    if(Deliverymode)
  1040. X       fromwhom=0;
  1041. X  }
  1042. Xprivileged:
  1043. X  umask(INIT_UMASK);fclose(stdout);fclose(stderr);rclose(STDOUT);
  1044. X  rclose(STDERR);
  1045. X  if(0>opena(devnull)||0>opena(vconsole)&&0>opena(devnull))
  1046. X     return EX_OSFILE;              /* couldn't open stdout and stderr */
  1047. X  setbuf(stdin,(char*)0);buf=malloc(linebuf);buf2=malloc(linebuf);
  1048. X  thepid=getpid();
  1049. X#ifdef SIGXCPU
  1050. X  signal(SIGXCPU,SIG_IGN);signal(SIGXFSZ,SIG_IGN);
  1051. X#endif
  1052. X  signal(SIGPIPE,SIG_IGN);signal(SIGTERM,srequeue);signal(SIGINT,sbounce);
  1053. X  signal(SIGHUP,sbounce);signal(SIGQUIT,slose);signal(SIGALRM,ftimeout);
  1054. X  ultstr(0,(unsigned long)uid,buf);
  1055. X  chp2=fromwhom?fromwhom:!passinvk||!*passinvk->pw_name?buf:passinvk->pw_name;
  1056. X  {time_t t;
  1057. X  t=time((time_t*)0);startchar=ctime(&t);         /* the current time */
  1058. X  }
  1059. X  strncpy(buf2,chp2,i=linebuf-strlen(startchar)-2);buf2[i]='\0';
  1060. X  strcat(strcat(buf2," "),startchar);
  1061. X  thebody=themail=malloc((tobesent=STRLEN(From)+strlen(buf2))+STRLEN(From));
  1062. X  filled=0;
  1063. X  if(Deliverymode||fromwhom)     /* do we need to peek for a leading From_ ? */
  1064. X   { int r;
  1065. X     while(1==(r=rread(STDIN,themail,1))&&*themail=='\n');   /* skip garbage */
  1066. X     if(STRLEN(From)-1==(i=rread(STDIN,themail+1,STRLEN(From)-1))&&
  1067. X      !strncmp(From,themail,STRLEN(From)))          /* is it a From_ line? */
  1068. X      { if(fromwhom||!Privileged)
  1069. X     { char a;
  1070. X       while(1==rread(STDIN,&a,1)&&a!='\n');   /* discard the From_ line */
  1071. X       i=0;goto Frominserted;
  1072. X     }
  1073. X    filled=STRLEN(From);               /* leave the From_ line alone */
  1074. X      }
  1075. X     else           /* move the read-ahead text beyond our From_ line */
  1076. X      { tmemmove(themail+tobesent,themail,i=r<=0?0:i>0?i+1:1);
  1077. X    strcpy(themail,From);              /* insert From_ of our own */
  1078. XFrominserted:
  1079. X    tmemmove(themail+STRLEN(From),buf2,tobesent-STRLEN(From));
  1080. X    filled=tobesent+i;
  1081. X      }
  1082. X   }
  1083. X  readmail(0,0L);chdir(tmp);              /* read in the mail completely */
  1084. X  if(Deliverymode)
  1085. X     do
  1086. X      { chp2=chp;
  1087. X#ifndef NO_USER_TO_LOWERCASE_HACK
  1088. X    for(;*chp;chp++)
  1089. X       if(*chp>='A'&&*chp<='Z')      /* kludge recipient into lowercase */
  1090. X          *chp+='a'-'A';     /* because getpwnam might be case sensitive */
  1091. X#endif
  1092. X    if(argv[++argc])              /* more than one recipient */
  1093. X       if(pidchild=sfork())
  1094. X        { if(forkerr(pidchild,procmailn)||waitfor(pidchild)!=EX_OK)
  1095. X         retvl2=retval;
  1096. X          pidchild=0;              /* loop for the next recipient */
  1097. X        }
  1098. X       else
  1099. X        { thepid=getpid();
  1100. X          while(argv[++argc]);        /* skip till end of command line */
  1101. X        }
  1102. X      }
  1103. X     while(chp=(char*)argv[argc]);
  1104. X  gargv=argv+argc;                 /* save it for nextrcfile() */
  1105. X  if(geteuid()==ROOT_uid&&Deliverymode&&(pass=getpwnam(chp2))||
  1106. X   (pass=passinvk))
  1107. X    /*
  1108. X     *    set preferred uid to the intended recipient
  1109. X     */
  1110. X   { gid=pass->pw_gid;uid=pass->pw_uid;setdef(home,pass->pw_dir);
  1111. X     chdir(pass->pw_dir);setdef(user,*pass->pw_name?pass->pw_name:buf);
  1112. X     setdef(shell,pass->pw_shell);
  1113. X   }
  1114. X  else             /* user could not be found, set reasonable defaults */
  1115. X    /*
  1116. X     *    set preferred uid to nobody, in case we are running as root
  1117. X     */
  1118. X   { setdef(home,tmp);setdef(user,buf);setdef(shell,binsh);
  1119. X     setgid(gid=NOBODY_gid);setuid(uid=NOBODY_uid);
  1120. X   }
  1121. X  endpwent();
  1122. X }
  1123. X  setdef(orgmail,systm_mbox);setdef(shellmetas,DEFshellmetas);
  1124. X  setdef(shellflags,DEFshellflags);setdef(maildir,DEFmaildir);
  1125. X  setdef(defaultf,DEFdefault);setdef(sendmail,DEFsendmail);
  1126. X  setdef(lockext,DEFlockext);setdef(msgprefix,DEFmsgprefix);
  1127. X {const char*const*kp;
  1128. X  for(kp=prestenv;*kp;)        /* preset or wipe selected environment variables */
  1129. X     strcpy((char*)(sgetcp=buf2),*kp++),readparse(buf,sgetc,2),sputenv(buf);
  1130. X }
  1131. X  if(chdir(chp=(char*)getenv(maildir)))
  1132. X     log(cldchd),logqnl(chp);
  1133. X /*
  1134. X  *    check if the original/default mailbox of the recipient exists, if
  1135. X  *    does, perform some security checks on it (check if it's a regular
  1136. X  *    file, check if it's owned by the recipient), if something is wrong
  1137. X  *    try and move the bogus mailbox out of the way,    create the
  1138. X  *    original/default mailbox file, and chown it to the recipient
  1139. X  */
  1140. X  chp=(char*)getenv(orgmail);strncpy(buf,chp,i=lastdirsep(chp)-chp);
  1141. X {struct stat stbuf; /* check if the recipient's system mailbox is a link */
  1142. X  if(!lstat(chp,&stbuf))
  1143. X     if(!(stbuf.st_mode&S_IWUSR)||S_ISLNK(stbuf.st_mode)||
  1144. X      (S_ISDIR(stbuf.st_mode)?!(stbuf.st_mode&S_IXUSR):stbuf.st_nlink!=1))
  1145. X    goto bogusbox;         /* we only deliver to real files (security) */
  1146. X     else if(stbuf.st_uid!=uid)             /* the recipient doesn't own it */
  1147. Xbogusbox:                         /* bogus mailbox found! */
  1148. X      { ultoan((unsigned long)stbuf.st_ino,          /* i-node numbered */
  1149. X     strchr(strcpy(buf+i,BOGUSprefix),'\0'));
  1150. X    if(rename(chp,buf))           /* try and move it out of the way */
  1151. X       goto fishy;         /* couldn't rename, something is fishy here */
  1152. X      }
  1153. X     else
  1154. X    goto notfishy;                       /* everything is fine */
  1155. X  buf[i]='\0';
  1156. X  if(!stat(buf,&stbuf)&&
  1157. X   (stbuf.st_mode&(S_IWGRP|S_IXGRP|S_IWOTH))==(S_IWGRP|S_IXGRP)&&
  1158. X   stbuf.st_gid==(gid_t)(tobesent=getegid()))
  1159. X     umask(INIT_UMASK&~S_IRWXG);                /* keep the gid? */
  1160. X  else
  1161. X     tobesent=gid;
  1162. X /*
  1163. X  *    try and create the file, check if it can be chowned to the recipient
  1164. X  *    if not, then we're either not root or accessing a secure NFS-partition
  1165. X  *    in the latter case, the created file is owned by nobody, not good, so
  1166. X  *    we unlink it again, set our uid to the recipient and try again
  1167. X  */
  1168. X  if(NFSxopen(chp,NORMperm,(time_t*)0))
  1169. X     goto fishy;
  1170. X  if(chown(chp,uid,(gid_t)tobesent)&&
  1171. X   (unlink(chp),setgid(gid),setuid(uid),NFSxopen(chp,NORMperm,(time_t*)0)))
  1172. Xfishy:
  1173. X     sputenv(orgmail),sputenv(defaultf);
  1174. X  umask(INIT_UMASK);
  1175. Xnotfishy:
  1176. X  cat(chp,getenv(lockext));               /* remove bogus lockfiles */
  1177. X  if(!lstat(strcpy(buf2,buf),&stbuf)&&stbuf.st_uid!=uid)
  1178. X   { ultoan((unsigned long)stbuf.st_ino,          /* i-node numbered */
  1179. X      strchr(strcpy(buf+i,BOGUSprefix),'\0'));
  1180. X     rename(buf2,buf);               /* try and move it out of the way */
  1181. X   }
  1182. X }
  1183. X  if(!Deliverymode)                   /* not explicit delivery mode */
  1184. X    /*
  1185. X     *    really change the uid now, since we are not in explicit
  1186. X     *    delivery mode
  1187. X     */
  1188. X     setgid(gid),setuid(uid),nextrcfile();
  1189. X  do                         /* main rcfile interpreter loop */
  1190. X   { alarm((unsigned)(alrmtime=0));                /* reset timeout */
  1191. X     while(chp=(char*)argv[argc])      /* interpret command line specs first */
  1192. X      { argc++;
  1193. Xlikearg:
  1194. X    strcpy(buf,chp);
  1195. X    if(chp=strchr(buf,'='))
  1196. X     { strcpy((char*)(sgetcp=buf2),++chp);readparse(chp,sgetc,2);
  1197. X       goto argenv;
  1198. X     }
  1199. X      }
  1200. X     if(rc<0)                         /* open new rc file */
  1201. X      { struct stat stbuf;
  1202. X       /*
  1203. X    *    if we happen to be still running as root, and the rcfile
  1204. X    *    is mounted on a secure NFS-partition, we might not be able
  1205. X    *    to access it, so check if we can stat it, if yes, drop
  1206. X    *    all rights and set uid to the recipient beforehand
  1207. X    */
  1208. X    while(*buf='\0',stat(strcat(
  1209. X     strchr(dirsep,*rcfile)?buf:cat(tgetenv(home),MCDIRSEP),rcfile),
  1210. X     &stbuf)||!(stbuf.st_mode&S_IRUSR)?0:(setgid(gid),setuid(uid)),
  1211. X     0>bopen(buf))
  1212. Xfake_rc: { log(couldnread);logqnl(buf);
  1213. X       if(!nextrcfile())              /* not available? try the next */
  1214. X        { bopen(devnull);goto nomore_rc;
  1215. X        }
  1216. X     }
  1217. X       /*
  1218. X    *    OK, so now we have opened an rcfile, but for security reasons
  1219. X    *    we only accept it if it is owned by the recipient or if the
  1220. X    *    the directory it is in, is not world writeable
  1221. X    */
  1222. X    i= *(chp=lastdirsep(buf));
  1223. X    if(lstat(buf,&stbuf)||
  1224. X     (stbuf.st_uid!=uid&&(*chp='\0',stat(buf,&stbuf)||
  1225. X     (stbuf.st_mode&(S_IWOTH|S_IXOTH))==(S_IWOTH|S_IXOTH))))
  1226. X     { rclose(rc);log("Suspicious rcfile\n");*chp=i;goto fake_rc;
  1227. X     }
  1228. X       /*
  1229. X    *    set uid back to recipient in any case, since we might just
  1230. X    *    have opened his/her .procmailrc (don't remove these, since
  1231. X    *    the rcfile might have been created after the first stat)
  1232. X    */
  1233. X    succeed=lastcond=0;setgid(gid);setuid(uid);
  1234. X      }
  1235. X     unlock(&loclock);                /* unlock any local lockfile */
  1236. X     do skipspace();                      /* skip whitespace */
  1237. X     while(testb('\n'));
  1238. X     if(testb(':'))                       /* check for a recipe */
  1239. X      { readparse(buf,getb,0);sh=strtol(buf,&chp,10);
  1240. X    if(chp==buf)                     /* no number parsed */
  1241. X       sh= -1;
  1242. X    if(tolock)         /* clear temporary buffer for lockfile name */
  1243. X       free(tolock);
  1244. X    for(i=maxindex(flags);flags[i]=0,i--;);          /* clear the flags */
  1245. X    for(tolock=0,locknext=0;;)
  1246. X     { switch(i= *chp++)
  1247. X        { default:
  1248. X         if(!(chp2=strchr(exflags,i)))       /* check for a valid flag */
  1249. X          { --chp;break;
  1250. X          }
  1251. X         flags[chp2-exflags]=1;                 /* set the flag */
  1252. X          case ' ':case '\t':continue;
  1253. X          case '\0':
  1254. X         if(*chp!=TMNATE)        /* if not the real end, skip */
  1255. X            continue;
  1256. X         break;
  1257. X          case ':':locknext=1;        /* yep, local lockfile specified */
  1258. X         if(*chp||*++chp!=TMNATE)
  1259. X            tolock=tstrdup(chp),chp=strchr(chp,'\0')+1;
  1260. X        }
  1261. X       if(concatenate(chp))
  1262. X          skipped(chp);                /* display any leftovers */
  1263. X       break;
  1264. X     }
  1265. X    if(sh<0)      /* assume the appropriate default nr of conditions */
  1266. X       sh=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC];
  1267. X    startchar=themail;tobesent=thebody-themail;
  1268. X    if(flags[BODY_GREP])               /* what needs to be egrepped? */
  1269. X       if(flags[HEAD_GREP])
  1270. X          tobesent=filled;
  1271. X       else
  1272. X          startchar=thebody,tobesent=filled-tobesent;
  1273. X    i=flags[ALSO_NEXT_RECIPE]?lastcond:1;          /* init test value */
  1274. X    if(flags[ALSO_N_IF_SUCC])
  1275. X       i=lastcond&&succeed;        /* only if the last recipe succeeded */
  1276. X    while(sh--)                    /* any conditions (left) */
  1277. X     { skipspace();getbl(buf2);
  1278. X       if(!strncmp(buf2,tokey,STRLEN(tokey)))         /* magic TOkey? */
  1279. X          cat(tosubstitute,buf2+STRLEN(tokey));
  1280. X       else if(*buf=='!'&&!strncmp(buf2+1,tokey,STRLEN(tokey)))  /* yes! */
  1281. X          strcat(cat("!",tosubstitute),buf2+1+STRLEN(tokey));
  1282. X       else
  1283. X          strcpy(buf,buf2);
  1284. X       if(i)                 /* check out all conditions */
  1285. X        { chp=buf+1;
  1286. Xsubstituted:  strcpy((char*)(sgetcp=buf2),buf);
  1287. X          switch(*buf)
  1288. X           { default:--chp;             /* no special character, backup */
  1289. X         case '!':case '\\':
  1290. X            i=!!egrepin(chp,startchar,tobesent,          /* grep it */
  1291. X             flags[DISTINGUISH_CASE])^*buf=='!';       /* invert it? */
  1292. X            break;
  1293. X         case '$':*buf2='"';readparse(buf,sgetc,2);goto substituted;
  1294. X         case '>':case '<':readparse(buf,sgetc,2);
  1295. X            i=strtol(buf+1,&chp,10);
  1296. X            i='<'==*buf?filled<i:filled>i;       /* compare length */
  1297. X            while(*chp==' ')
  1298. X               ++chp;
  1299. X            if(*chp)                    /* any leftover? */
  1300. X               skipped(chp);
  1301. X           }
  1302. X          if(verbose)
  1303. X         log(i?"M":"No m"),log("atch on"),logqnl(buf);
  1304. X        }
  1305. X     }
  1306. X    if(!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC])
  1307. X       lastcond=i;               /* save the outcome for posterity */
  1308. X    startchar=themail;tobesent=filled;        /* body, header or both? */
  1309. X    if(flags[PASS_HEAD])
  1310. X     { if(!flags[PASS_BODY])
  1311. X          tobesent=thebody-themail;
  1312. X     }
  1313. X    else if(flags[PASS_BODY])
  1314. X       tobesent-=(startchar=thebody)-themail;
  1315. X    chp=strchr(strcpy(buf,tgetenv(sendmail)),'\0');succeed=sh=0;
  1316. X    pwait=flags[WAIT_EXIT]|flags[WAIT_EXIT_QUIET]<<1;
  1317. X    ignwerr=flags[IGNORE_WRITERR];skipspace();
  1318. X    if(testb('!'))                     /* forward the mail */
  1319. X     { readparse(chp+1,getb,0);
  1320. X       if(i)
  1321. X          goto forward;
  1322. X     }
  1323. X    else if(testb('|'))                    /* pipe the mail */
  1324. X     { getbl(buf2);
  1325. X       for(chp=buf2;*(chp=strchr(chp,'\0')-1)=='\\'&&getbl(chp););
  1326. X       if(i)
  1327. X        { if(sh=!!strpbrk(buf2,tgetenv(shellmetas)))
  1328. X         strcpy(buf,buf2);     /* copy literally, shell will parse */
  1329. X          else
  1330. X         sgetcp=buf2,readparse(buf,sgetc,0);    /* parse it yourself */
  1331. Xforward:      if(!tolock)       /* an explicit lockfile specified already */
  1332. X           { chp=buf;*buf2='\0';
  1333. X         while(i= *chp)        /* find the implicit lockfile ('>>name') */
  1334. X            if(chp++,i=='>'&&*chp=='>')
  1335. X             { chp=pstrspn(chp+1," \t");
  1336. X               tmemmove(buf2,chp,i=strcspn(chp,EOFName));buf2[i]='\0';
  1337. X               if(sh)         /* expand any environment variables */
  1338. X            { chp=tstrdup(buf);sgetcp=buf2;readparse(buf,sgetc,0);
  1339. X              strcpy(buf2,buf);strcpy(buf,chp);free(chp);
  1340. X            }
  1341. X               break;
  1342. X             }
  1343. X           }
  1344. X          lcllock();inittmout(buf);
  1345. X          if(flags[FILTER])
  1346. X           { if(startchar==themail&&tobesent!=filled)     /* if only 'h' */
  1347. X          { if(!pipthrough(buf,startchar,tobesent))
  1348. X               succeed=1,readmail(1,tobesent);
  1349. X          }
  1350. X         else if(!pipthrough(buf,startchar,tobesent))
  1351. X            succeed=1,filled=startchar-themail,readmail(0,0L);
  1352. X           }
  1353. X          else if(!pipin(buf,startchar,tobesent)&&
  1354. X           (succeed=1,!flags[CONTINUE]))
  1355. X         goto mailed;
  1356. X        }
  1357. X     }
  1358. X    else           /* dump the mail into a mailbox file or directory */
  1359. X     { readparse(buf,getb,0);
  1360. X       if(concatenate(chp=strchr(buf,'\0')+1))
  1361. X          skipped(chp);                 /* report any leftovers */
  1362. X       if(i)
  1363. X        { strcpy(buf2,buf);lcllock();strcpy(buf2,buf);tofile=1;
  1364. X          if(dump(deliver(buf2),startchar,tobesent))
  1365. X         writeerr(buf);
  1366. X          else if(succeed=1,!flags[CONTINUE])
  1367. X         goto mailed;
  1368. X          tofile=tofolder=0;
  1369. X        }
  1370. X     }
  1371. X      }
  1372. X     else if(testb('#'))                   /* no comment :-) */
  1373. X    getbl(buf);
  1374. X     else                    /* then it must be an assignment */
  1375. X      { for(*(chp=buf)='\0';;)                /* get the variable name */
  1376. X     { switch(i=getb())
  1377. X        { case ' ':case '\t':skipspace();i=testb('=')?'=':0;
  1378. X          case '\n':case '=':case EOF:*chp='\0';goto eofvarname;
  1379. X        }
  1380. X       if(!alphanum(*chp++=i))
  1381. X          for(;;*chp++=i)             /* it was garbage after all */
  1382. X         switch(i=getb())
  1383. X          { case ' ':case '\t':case '\n':case EOF:*chp='\0';
  1384. X               skipped(buf);goto mainloop;
  1385. X          }
  1386. X     }
  1387. Xeofvarname:
  1388. X    if(i!='=')                   /* removal or assignment? */
  1389. X       *++chp='\0';
  1390. X    else
  1391. X       *chp='=',readparse(++chp,getb,1);
  1392. Xargenv: sputenv(buf);chp[-1]='\0';
  1393. X    if(!strcmp(buf,slinebuf))
  1394. X     { if((linebuf=renvint(0L,chp)+XTRAlinebuf)<MINlinebuf+XTRAlinebuf)
  1395. X          linebuf=MINlinebuf+XTRAlinebuf;           /* check minimum size */
  1396. X       free(buf);free(buf2);buf=malloc(linebuf);buf2=malloc(linebuf);
  1397. X     }
  1398. X    else if(!strcmp(buf,maildir))
  1399. X     { if(chdir(chp))
  1400. X          log(cldchd),logqnl(chp);
  1401. X     }
  1402. X    else if(!strcmp(buf,logfile))
  1403. X     { close(STDERR);
  1404. X       if(verbose=DEBUGPREFIX==*chp)         /* turn on diagnostics? */
  1405. X          chp++;
  1406. X       if(0>opena(chp))
  1407. X          if(0>opena(vconsole))
  1408. X         retval=EX_OSFILE;      /* bad news, but can't tell anyone */
  1409. X          else
  1410. X         writeerr(chp);
  1411. X     }
  1412. X    else if(!strcmp(buf,Log))
  1413. X       log(chp);
  1414. X    else if(!strcmp(buf,sdelivered))            /* fake delivery */
  1415. X     { lcking|=lck_LOCKFILE;        /* just to prevent interruptions */
  1416. X       if((thepid=sfork())>0)
  1417. X        { nextexit=2;lcking&=~lck_LOCKFILE;return retvl2;
  1418. X        }                    /* signals may cause trouble */
  1419. X       else
  1420. X        { if(!forkerr(thepid,procmailn))
  1421. X         fakedelivery=1;
  1422. X          thepid=getpid();lcking&=~lck_LOCKFILE;
  1423. X          if(nextexit)             /* signals occurred so far? */
  1424. X         log(newline),terminate();
  1425. X        }
  1426. X     }
  1427. X    else if(!strcmp(buf,lockfile))
  1428. X       lockit(chp,&globlock),chown(chp,uid,gid);
  1429. X    else if(!strcmp(buf,eumask))
  1430. X       umask((int)strtol(chp,(char**)0,8));
  1431. X    else if(!strcmp(buf,host))
  1432. X     { if(strncmp(chp,chp2=(char*)hostname(),HOSTNAMElen))
  1433. X        { yell("HOST mismatched",chp2);
  1434. X          if(rc<0||!nextrcfile())          /* if no rcfile opened yet */
  1435. X         retval=EX_OK,terminate();      /* exit gracefully as well */
  1436. X          rclose(rc);rc= -1;
  1437. X        }
  1438. X     }
  1439. X    else
  1440. X     { i=MAXvarvals;
  1441. X       do                      /* several numeric assignments */
  1442. X          if(!strcmp(buf,strenvvar[i].name))
  1443. X           { strenvvar[i].val=renvint(strenvvar[i].val,chp);break;
  1444. X           }
  1445. X       while(i--);
  1446. X     }
  1447. X      }
  1448. Xmainloop:;
  1449. X   }
  1450. X  while(rc<0||!testb(EOF));                /* main interpreter loop */
  1451. Xnomore_rc:
  1452. X  if(tofile!=2)
  1453. X   { tofile=2;setuid(uid);chp=DEFdefaultlock;goto likearg;
  1454. X   }
  1455. X  if(dump(deliver(tgetenv(defaultf)),themail,filled))          /* default */
  1456. X   { writeerr(buf);        /* if it fails, don't panic, try the last resort */
  1457. X     if(dump(deliver(tgetenv(orgmail)),themail,filled))
  1458. X    writeerr(buf);goto mailerr;            /* now you can panic */
  1459. X   }
  1460. Xmailed:
  1461. X  retval=EX_OK;                  /* we're home free, mail delivered */
  1462. Xmailerr:
  1463. X  unlock(&loclock);terminate();
  1464. X}
  1465. X
  1466. Xlogabstract()
  1467. X{ char*chp,*chp2;int i;
  1468. X  if(mailread)                  /* is the mail completely read in? */
  1469. X   { *thebody='\0';               /* terminate the header, just in case */
  1470. X     if(!strncmp(From,chp=themail,STRLEN(From)))       /* any "From " header */
  1471. X      { if(chp=strchr(themail,'\n'))
  1472. X       *chp++='\0';
  1473. X    else
  1474. X       chp=thebody;
  1475. X    log(themail);log(newline);   /* preserve mailbox format (any length) */
  1476. X      }
  1477. X     if(!(lcking&lck_ALLOCLIB)&&        /* don't reenter malloc/free */
  1478. X      (chp=egrepin(NSUBJECT,chp,(long)(thebody-chp),0)))
  1479. X      { for(chp2= --chp;*--chp2!='\n'&&*chp2;);
  1480. X    if(chp-++chp2>MAXSUBJECTSHOW)            /* keep it within bounds */
  1481. X       chp2[MAXSUBJECTSHOW]='\0';
  1482. X    *chp='\0';detab(chp2);log(" ");log(chp2);log(newline);
  1483. X      }
  1484. X   }
  1485. X  log(sfolder);i=strlen(strncpy(buf,lastfolder,MAXfoldlen))+STRLEN(sfolder);
  1486. X  buf[MAXfoldlen]='\0';detab(buf);log(buf);i-=i%TABWIDTH;    /* last dump */
  1487. X  do log(TABCHAR);
  1488. X  while((i+=TABWIDTH)<LENoffset);
  1489. X  ultstr(7,lastdump,buf);log(buf);log(newline);
  1490. X}
  1491. X
  1492. Xreadmail(onlyhead,tobesent)const int onlyhead;const long tobesent;
  1493. X{ char*chp,*chp2,*pastend,*realstart;int firstchar;
  1494. X  mailread=0;
  1495. X  if(onlyhead)
  1496. X   { long dfilled=0;
  1497. X     chp=readdyn(malloc(1),&dfilled);filled-=tobesent;
  1498. X     if(tobesent<dfilled)           /* adjust buffer size (grow only) */
  1499. X    themail=realloc(themail,dfilled+filled);
  1500. X     tmemmove(themail+dfilled,thebody,filled);
  1501. X     tmemmove(themail,chp,dfilled);free(chp);
  1502. X     themail=realloc(themail,1+(filled+=dfilled));
  1503. X   }
  1504. X  else
  1505. X     themail=readdyn(themail,&filled);             /* read in the mail */
  1506. X  pastend=filled+(thebody=themail);
  1507. X  while(thebody<pastend&&*thebody++=='\n');         /* skip leading garbage */
  1508. X  realstart=thebody;
  1509. X  while(thebody=egrepin("[^\n]\n[\n\t ]",thebody,(long)(pastend-thebody),1))
  1510. X     if(*--thebody!='\n')
  1511. X    thebody[-1]=' ';            /* concatenate continuated lines */
  1512. X     else
  1513. X    goto eofheader;               /* empty line marks end of header */
  1514. X  thebody=pastend;          /* provide a default, in case there is no body */
  1515. Xeofheader:
  1516. X  firstchar= *realstart;
  1517. X  for(*(chp=realstart)='\0';chp=egrepin(FROM_EXPR,chp,(long)(pastend-chp),1);)
  1518. X   { while(*--chp!='\n');               /* where did this line start? */
  1519. X     ++chp;tmemmove(chp+1,chp,pastend++-chp);*chp=ESCAP;       /* bogus! */
  1520. X     themail=realloc(chp2=themail,++filled+1);
  1521. X#define ADJUST(x)    ((x)=themail+((x)-chp2))
  1522. X     ADJUST(thebody);ADJUST(pastend);ADJUST(chp);ADJUST(realstart);
  1523. X   }
  1524. X  *realstart=firstchar;mailread=1;
  1525. X}
  1526. X
  1527. Xdirmail()                /* buf should contain directory name */
  1528. X{ char*chp;struct stat stbuf;
  1529. X  if((chp=strchr(buf,'\0')-1)-1>=buf&&chp[-1]==*MCDIRSEP&&*chp=='.')
  1530. X     *chp='\0',strcpy(buf2,buf);               /* it ended in /. */
  1531. X  else
  1532. X     chp=0,strcpy(buf2,strcat(buf,MCDIRSEP));
  1533. X  if(unique(buf2,strchr(buf2,'\0'),NORMperm))
  1534. X   { if(chp)
  1535. X      { long i=0;             /* first let us try to prime i with the */
  1536. X#ifndef NOopendir             /* highest MH folder number we can find */
  1537. X    long j;DIR*dirp;struct dirent*dp;char*chp2;
  1538. X    *chp='\0';yell("Opening directory",buf);
  1539. X    if(dirp=opendir(buf))
  1540. X     { while(dp=readdir(dirp))    /* there still are directory entries */
  1541. X          if((j=strtol(dp->d_name,&chp2,10))>i&&!*chp2)
  1542. X         i=j;                /* yep, we found a higher number */
  1543. X       closedir(dirp);                 /* aren't we neat today */
  1544. X     }
  1545. X    else
  1546. X       log(couldnread),logqnl(buf);
  1547. X#endif /* NOopendir */
  1548. X    do ultstr(0,++i,chp);               /* find first empty MH folder */
  1549. X    while(link(buf2,buf)&&errno==EEXIST);
  1550. X    unlink(buf2);goto opn;
  1551. X      }
  1552. X     stat(buf2,&stbuf);
  1553. X     ultoan((unsigned long)stbuf.st_ino,      /* filename with i-node number */
  1554. X      strchr(strcat(buf,tgetenv(msgprefix)),'\0'));
  1555. X     if(!myrename(buf2,buf))           /* rename it, we need the same i-node */
  1556. Xopn:    return opena(buf);
  1557. X   }
  1558. X  return -1;
  1559. X}
  1560. END_OF_FILE
  1561. if test 24484 -ne `wc -c <'procmail/procmail.c'`; then
  1562.     echo shar: \"'procmail/procmail.c'\" unpacked with wrong size!
  1563. fi
  1564. # end of 'procmail/procmail.c'
  1565. fi
  1566. echo shar: End of archive 5 \(of 5\).
  1567. cp /dev/null ark5isdone
  1568. MISSING=""
  1569. for I in 1 2 3 4 5 ; do
  1570.     if test ! -f ark${I}isdone ; then
  1571.     MISSING="${MISSING} ${I}"
  1572.     fi
  1573. done
  1574. if test "${MISSING}" = "" ; then
  1575.     echo You have unpacked all 5 archives.
  1576.     rm -f ark[1-9]isdone
  1577. else
  1578.     echo You still need to unpack the following archives:
  1579.     echo "        " ${MISSING}
  1580. fi
  1581. ##  End of shell archive.
  1582. exit 0
  1583. -- 
  1584. Sincerely,                                  berg@pool.informatik.rwth-aachen.de
  1585.            Stephen R. van den Berg (AKA BuGless).    berg@physik.tu-muenchen.de
  1586.  
  1587. He did a quarter of the work in *half* the time!
  1588.  
  1589. exit 0 # Just in case...
  1590.