home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume29 / procmail / part05 < prev    next >
Encoding:
Text File  |  1992-05-12  |  52.9 KB  |  1,431 lines

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