home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / misc / volume35 / procmail / part10 < prev    next >
Encoding:
Text File  |  1993-02-04  |  45.2 KB  |  1,187 lines

  1. Newsgroups: comp.sources.misc
  2. From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  3. Subject: v35i031:  procmail - mail processing package v2.80, Part10/11
  4. Message-ID: <1993Feb5.020733.16935@sparky.imd.sterling.com>
  5. X-Md4-Signature: a4a052d61af3e1757a814cf94d4f1c76
  6. Date: Fri, 5 Feb 1993 02:07:33 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 35, Issue 31
  11. Archive-name: procmail/part10
  12. Environment: sendmail, smail, MMDF, mailsurr, UNIX, POSIX
  13. Supersedes: procmail: Volume 31, Issue 40-44
  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 10 (of 11)."
  22. # Contents:  procmail280/src/formail.c procmail280/src/procmail.c
  23. # Wrapped by berg@hathi on Thu Feb  4 15:28:01 1993
  24. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  25. if test -f 'procmail280/src/formail.c' -a "${1}" != "-c" ; then 
  26.   echo shar: Will not clobber existing file \"'procmail280/src/formail.c'\"
  27. else
  28. echo shar: Extracting \"'procmail280/src/formail.c'\" \(18801 characters\)
  29. sed "s/^X//" >'procmail280/src/formail.c' <<'END_OF_FILE'
  30. X/************************************************************************
  31. X *    formail - The mail (re)formatter                *
  32. X *                                    *
  33. X *    Seems to be relatively bug free.                *
  34. X *                                    *
  35. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  36. X *    #include "README"                        *
  37. X ************************************************************************/
  38. X#ifdef RCS
  39. Xstatic /*const*/char rcsid[]=
  40. X "$Id: formail.c,v 1.21 1993/02/02 15:27:07 berg Exp $";
  41. X#endif
  42. Xstatic /*const*/char rcsdate[]="$Date: 1993/02/02 15:27:07 $";
  43. X#include "includes.h"
  44. X#include <ctype.h>        /* iscntrl() */
  45. X#include "formail.h"
  46. X#include "sublib.h"
  47. X#include "shell.h"
  48. X#include "common.h"
  49. X#include "fields.h"
  50. X#include "ecommon.h"
  51. X#include "formisc.h"
  52. X
  53. Xstatic const char unknown[]=UNKNOWN,re[]=" Re:",fmusage[]=FM_USAGE,
  54. X From_[]=        FROM,                /* VNIX 'From ' line */
  55. X Article_[]=        "Article ",           /* USENET 'Article ' line */
  56. X x_[]=            "X-",                /* general extension */
  57. X old_[]=        OLD_PREFIX;                 /* my extension */
  58. X#define ssl(str)        str,STRLEN(str)
  59. X#define bsl(str)        {ssl(str)}
  60. X#define sslbar(str,bar1,bar2)    {ssl(str),STRLEN(bar1)-1,STRLEN(bar2)-1}
  61. X#include "header.h"
  62. X/*
  63. X *    sender determination fields in order of importance/reliability
  64. X *    reply-address determination fields (wrepl specifies the weight for
  65. X *    for regular replies, wtrepl specifies the weight for trusted users)
  66. X *
  67. X *    I bet this is the first time you see a bar graph in C-source-code :-)
  68. X */
  69. Xstatic const struct {const char*head;int len,wrepl,wtrepl;}sest[]=
  70. X{ sslbar(replyto    ,"******"    ,"********"    ),
  71. X  sslbar(Fromm        ,"*"        ,"****"        ),
  72. X  sslbar(retreceiptto    ,"********"    ,"*******"    ),
  73. X  sslbar(sender        ,"*****"    ,"******"    ),
  74. X  sslbar(res_replyto    ,"***********"    ,"***********"    ),
  75. X  sslbar(res_from    ,"***foo***"    ,"***bar***"    ),
  76. X  sslbar(res_sender    ,"**********"    ,"**********"    ),
  77. X  sslbar(errorsto    ,"*******"    ,"*****"    ),
  78. X  sslbar(path        ,"**"        ,"*"        ),
  79. X  sslbar(returnpath    ,"***"        ,"***"        ),
  80. X  sslbar(From_        ,"****"        ,"**"        )
  81. X};
  82. X
  83. Xstatic struct saved rex[]=
  84. X{ bsl(subject),bsl(references),bsl(messageid),bsl(date)
  85. X};
  86. X#define subj    (rex+0)
  87. X#define refr    (rex+1)
  88. X#define msid    (rex+2)
  89. X#define hdate    (rex+3)
  90. X
  91. X#ifdef sMAILBOX_SEPARATOR
  92. X#define emboxsep    smboxsep
  93. X#define MAILBOX_SEPARATOR
  94. Xstatic const char smboxsep[]=sMAILBOX_SEPARATOR;
  95. X#endif /* sMAILBOX_SEPARATOR */
  96. X#ifdef eMAILBOX_SEPARATOR
  97. X#ifdef emboxsep
  98. X#undef emboxsep
  99. X#else
  100. X#define MAILBOX_SEPARATOR
  101. X#endif
  102. Xstatic const char emboxsep[]=eMAILBOX_SEPARATOR;
  103. X#endif /* eMAILBOX_SEPARATOR */
  104. X
  105. Xconst char binsh[]=BinSh,sfolder[]=FOLDER,
  106. X couldntw[]="Couldn't write to stdout";
  107. Xint errout,oldstdout,quiet,buflast;
  108. Xpid_t child= -1;
  109. XFILE*mystdout;
  110. Xsize_t nrskip,nrtotal= -1,buflen,buffilled;
  111. Xlong totallen;
  112. Xchar*buf,*logsummary;
  113. Xstruct field*rdheader;
  114. Xstatic struct field*iheader,*Iheader,*aheader,*Aheader,*xheader,*Xheader,
  115. X *Rheader,*nheader;
  116. X
  117. Xstatic void logfolder P((void))     /* estimate the no. of characters needed to */
  118. X{ size_t i;char num[8*sizeof totallen*4/10+1];           /* represent totallen */
  119. X  static const char tabchar[]=TABCHAR;
  120. X  if(logsummary)
  121. X   { putssn(sfolder,STRLEN(sfolder));i=strlen(logsummary)+STRLEN(sfolder);
  122. X     i-=i%TABWIDTH;
  123. X     do putssn(tabchar,STRLEN(tabchar));
  124. X     while((i+=TABWIDTH)<LENoffset);
  125. X     ultstr(7,totallen,num);putssn(num,strlen(num));putcs('\n');
  126. X   }
  127. X}
  128. X    /* checks if the last field in rdheader looks like a known digest header */
  129. Xstatic digheadr P((void))
  130. X{ char*chp;int i;size_t j;struct field*fp;
  131. X  for(fp=rdheader;fp->fld_next;fp=fp->fld_next);     /* skip to the last */
  132. X  i=maxindex(cdigest);chp=fp->fld_text;j=fp->id_len;
  133. X  while((cdigest[i].lnr!=j||strnIcmp(cdigest[i].hedr,chp,j))&&i--);
  134. X  return i>=0||j>STRLEN(old_)&&!strnIcmp(old_,chp,STRLEN(old_))||
  135. X   j>STRLEN(x_)&&!strnIcmp(x_,chp,STRLEN(x_));
  136. X}
  137. X
  138. Xstatic artheadr P((void))         /* could it be the start of an article? */
  139. X{ if(!rdheader&&!strncmp(buf,Article_,STRLEN(Article_)))
  140. X   { addbuf();rdheader->id_len=STRLEN(Article_);return 1;
  141. X   }
  142. X  return 0;
  143. X}
  144. X
  145. Xstatic PROGID;
  146. X
  147. Xmain(lastm,argv)const char*const argv[];
  148. X{ int i,split=0,force=0,bogus=1,every=0,areply=0,trust=0,digest=0,nowait=0,
  149. X   keepb=0,minfields=(char*)progid-(char*)progid,conctenate=0;
  150. X  size_t j,lnl;char*chp,*namep;struct field*fldp,*fp2,**afldp,*fdate;
  151. X  if(lastm)                   /* sanity check, any argument at all? */
  152. X#define Qnext_arg()    if(!*chp&&!(chp=(char*)*++argv))goto usg
  153. X     while(chp=(char*)*++argv)
  154. X      { if((lastm= *chp++)==FM_SKIP)
  155. X       goto number;
  156. X    else if(lastm!=FM_TOTAL)
  157. X       goto usg;
  158. X    for(;;)
  159. X     { switch(lastm= *chp++)
  160. X        { case FM_TRUST:trust=1;continue;
  161. X          case FM_REPLY:areply=1;continue;
  162. X          case FM_FORCE:force=1;continue;
  163. X          case FM_EVERY:every=1;continue;
  164. X          case FM_DIGEST:digest=1;continue;
  165. X          case FM_NOWAIT:nowait=1;continue;
  166. X          case FM_KEEPB:keepb=1;continue;
  167. X          case FM_CONCATENATE:conctenate=1;continue;
  168. X          case FM_QUIET:quiet=1;continue;
  169. X          case FM_LOGSUMMARY:Qnext_arg();
  170. X         if(strlen(logsummary=chp)>MAXfoldlen)
  171. X            chp[MAXfoldlen]='\0';
  172. X         detab(chp);break;
  173. X          case FM_SPLIT:split=1;
  174. X         if(!*chp&&*++argv)
  175. X            goto parsedoptions;
  176. X         goto usg;
  177. X          case HELPOPT1:case HELPOPT2:elog(fmusage);elog(FM_HELP);
  178. X         goto xusg;
  179. X          case FM_MINFIELDS:Qnext_arg();chp++;
  180. X          default:chp--;
  181. Xnumber:         if(*chp-'0'>(unsigned)9)        /* the number is not >=0 */
  182. X            goto usg;
  183. X         i=strtol(chp,&chp,10);
  184. X         switch(lastm)            /* where does the number go? */
  185. X          { case FM_SKIP:nrskip=i;break;
  186. X            case FM_MINFIELDS:minfields=i;break;
  187. X            default:nrtotal=i;
  188. X          }
  189. X         continue;
  190. X          case FM_BOGUS:bogus=0;continue;
  191. X          case FM_ADD_IFNOT:case FM_ADD_ALWAYS:case FM_REN_INSERT:
  192. X          case FM_DEL_INSERT:case FM_EXTRACT:case FM_EXTRC_KEEP:
  193. X          case FM_ReNAME:Qnext_arg();
  194. X         if(!breakfield(chp,lnl=strlen(chp)))
  195. X            goto invfield;
  196. X         chp[lnl]='\n';                   /* terminate the line */
  197. X         afldp=addfield(lastm==FM_REN_INSERT?&iheader:
  198. X          lastm==FM_DEL_INSERT?&Iheader:lastm==FM_ADD_IFNOT?&aheader:
  199. X          lastm==FM_ADD_ALWAYS?&Aheader:lastm==FM_EXTRACT?&xheader:
  200. X          lastm==FM_EXTRC_KEEP?&Xheader:&Rheader,chp,++lnl);
  201. X         if(lastm==FM_ReNAME)          /* then we need a second field */
  202. X          { int copied=0;
  203. X            for(namep=(chp=(fldp= *afldp)->fld_text)+lnl,
  204. X             chp+=lnl=fldp->id_len;chp<namep;++chp)
  205. X             { switch(*chp)              /* skip whitespace */
  206. X            { case ' ':case '\t':case '\n':continue;
  207. X            }
  208. X               break;
  209. X             }                   /* second field attached? */
  210. X            if(i=breakfield(chp,(size_t)(namep-chp)))  /* squeeze on */
  211. X               tmemmove(fldp->fld_text+lnl,chp,i),copied=1;
  212. X            else if(!(chp=(char*)*++argv)||     /* look at next arg */
  213. X             !(i=breakfield(chp,strlen(chp))))        /* no field? */
  214. Xinvfield:         { nlog("Invalid field-name:");logqnl(chp?chp:"");
  215. X               goto usg;
  216. X             }
  217. X            *afldp=fldp=
  218. X             realloc(fldp,FLD_HEADSIZ+(fldp->tot_len=lnl+i));
  219. X            if(!copied)               /* if not squeezed on yet */
  220. X               tmemmove(fldp->fld_text+lnl,chp,i);    /* squeeze now */
  221. X          }
  222. X          case '\0':;
  223. X        }
  224. X       break;
  225. X     }
  226. X      }
  227. Xparsedoptions:
  228. X  mystdout=stdout;signal(SIGPIPE,SIG_IGN);
  229. X  if(split)
  230. X   { oldstdout=dup(STDOUT);fclose(stdout);startprog((const char*Const*)argv);
  231. X     if(!minfields)                   /* no user specified minimum? */
  232. X    minfields=DEFminfields;                 /* take our default */
  233. X   }
  234. X  else if(every||digest||minfields)          /* these combinations are only */
  235. X     goto usg;                  /* valid in combination with split */
  236. X  if((xheader||Xheader)&&logsummary||keepb&&!(areply||xheader))
  237. Xusg:                             /* options sanity check */
  238. X   { elog(fmusage);                       /* impossible mix */
  239. Xxusg:
  240. X     return EX_USAGE;
  241. X   }
  242. X  buf=malloc(buflen=BSIZE);totallen=0;i=maxindex(rex); /* prime some buffers */
  243. X  do rex[i].rexp=malloc(1);
  244. X  while(i--);
  245. X  fdate=0;addfield(&fdate,date,STRLEN(date)); /* fdate is only for searching */
  246. X  while((buflast=getchar())=='\n');             /* skip leading garbage */
  247. X  if(!readhead())                        /* start looking */
  248. X   {
  249. X#ifdef sMAILBOX_SEPARATOR                  /* check for a leading */
  250. X     if(!strncmp(smboxsep,buf,STRLEN(smboxsep)))    /* mailbox separator */
  251. X      { buffilled=0;goto startover;                  /* skip it */
  252. X      }
  253. X#endif
  254. X     if(digest&&artheadr())
  255. X    goto startover;
  256. X   }
  257. X  else
  258. Xstartover:
  259. X     while(readhead());                 /* read in the whole header */
  260. X  ;{ size_t lenparkedbuf;void*parkedbuf;
  261. X     if(rdheader&&!strncmp(rdheader->fld_text,Article_,STRLEN(Article_)))
  262. X    rdheader->fld_text[STRLEN(Article_)-1]=HEAD_DELIMITER;       /* proper */
  263. X     namep=0;totallen=0;i=maxindex(rex);                /* field */
  264. X     do rex[i].rexl=0;
  265. X     while(i--);                 /* all state has been reset */
  266. X     for(fldp=rdheader;fldp;fldp=fldp->fld_next)    /* go through the linked */
  267. X      { int nowm;                    /* list of header-fields */
  268. X    if(conctenate)
  269. X       concatenate(fldp);             /* look for `sender' fields */
  270. X    chp=fldp->fld_text;j=fldp->id_len;i=maxindex(sest);
  271. X    while((sest[i].len!=j||strnIcmp(sest[i].head,chp,j))&&i--);
  272. X    if(i>=0&&(i!=maxindex(sest)||fldp==rdheader))      /* found anything? */
  273. X     { char*saddr;char*tmp;                 /* determine the weight */
  274. X       nowm=trust?sest[i].wtrepl:areply?i:sest[i].wrepl;chp+=j;
  275. X       saddr=tmp=malloc(j=fldp->tot_len-j);tmemmove(tmp,chp,j);
  276. X       tmp[j-1]='\0';chp=pstrspn(tmp," \t\n");
  277. X       for(saddr=0;;chp=skipwords(chp))        /* skip RFC 822 wise */
  278. X        { switch(*chp)
  279. X           { default:
  280. X            if(!saddr)           /* if we haven't got anything yet */
  281. X               saddr=chp;        /* this might be the address */
  282. X            continue;
  283. X         case '<':skipwords(saddr=chp);      /* hurray, machine useable */
  284. X         case '\0':;
  285. X           }
  286. X          break;
  287. X        }
  288. X       if(saddr)                /* any useful mailaddress found? */
  289. X        { if(*saddr)              /* did it have any length? */
  290. X           { if(strstr(saddr,".UUCP"))
  291. X            nowm-=(maxindex(sest)+2)*3;     /* depreciate .UUCP address */
  292. X         else if(!strpbrk(saddr,"@!/"))
  293. X            nowm-=(maxindex(sest)+2)*2;        /* depreciate "user" */
  294. X         else if(strchr(saddr,'@')&&!strchr(saddr,'.'))
  295. X            nowm-=maxindex(sest)+2;         /* depreciate user@host */
  296. X         if(!namep||nowm>lastm)        /* better than previous ones */
  297. X          { saddr=strcpy(malloc(strlen(saddr)+1),saddr);lastm=nowm;
  298. X            goto newnamep;
  299. X          }
  300. X           }
  301. X          else if(sest[i].head==returnpath)        /* nill Return-Path: */
  302. X           { saddr=0;nowm=maxindex(sest)+2;             /* override */
  303. Xnewnamep:     if(namep)
  304. X            free(namep);
  305. X         namep=saddr;
  306. X           }
  307. X        }
  308. X       free(tmp);
  309. X     }                   /* save headers for later perusal */
  310. X    i=maxindex(rex);chp=fldp->fld_text;j=fldp->id_len;    /* e.g. areply */
  311. X    while((rex[i].lenr!=j||strnIcmp(rex[i].headr,chp,j))&&i--);
  312. X    chp+=j;
  313. X    if(i>=0&&(j=fldp->tot_len-j)>1)              /* found anything? */
  314. X       tmemmove(rex[i].rexp=realloc(rex[i].rexp,rex[i].rexl=j),chp,j);
  315. X      }
  316. X     tmemmove(parkedbuf=malloc(buffilled),buf,lenparkedbuf=buffilled);
  317. X     buffilled=0;    /* moved the contents of buf out of the way temporarily */
  318. X     if(areply)              /* autoreply requested, we clean up the header */
  319. X      { for(fldp= *(afldp= &rdheader);fldp;)
  320. X       if(!(fp2=findf(fldp,iheader))||fp2->id_len<fp2->tot_len-1)
  321. X          *afldp=fldp->fld_next,free(fldp),fldp= *afldp;   /* remove all */
  322. X       else                    /* except the ones mentioned */
  323. X          fldp= *(afldp= &fldp->fld_next);               /* as -i ...: */
  324. X    loadbuf(to,STRLEN(to));loadchar(' ');       /* generate the To: field */
  325. X    if(namep)           /* did we find a valid return address at all? */
  326. X       loadbuf(namep,strlen(namep));          /* then insert it here */
  327. X    else
  328. X       loadbuf(unknown,STRLEN(unknown));        /* or insert our default */
  329. X    loadchar('\n');addbuf();               /* add it to rdheader */
  330. X    if(subj->rexl)                      /* any Subject: found? */
  331. X     { loadbuf(subject,STRLEN(subject));      /* sure, check for leading */
  332. X       if(strnIcmp(pstrspn(chp=subj->rexp," \t"),Re,STRLEN(Re)))  /* Re: */
  333. X          loadbuf(re,STRLEN(re));           /* no Re: , add one ourselves */
  334. X       loadsaved(subj);addbuf();
  335. X     }
  336. X    if(refr->rexl||msid->rexl)       /* any References: or Message-ID: */
  337. X     { loadbuf(references,STRLEN(references)); /* yes insert References: */
  338. X       if(refr->rexl)
  339. X        { if(msid->rexl)        /* if we're going to append a Message-ID */
  340. X         --refr->rexl;            /* suppress the trailing newline */
  341. X          loadsaved(refr);
  342. X        }
  343. X       if(msid->rexl)
  344. X          loadsaved(msid);               /* here's our missing newline */
  345. X       addbuf();
  346. X     }
  347. X    if(msid->rexl)             /* do we add an In-Reply-To: field? */
  348. X       loadbuf(inreplyto,STRLEN(inreplyto)),loadsaved(msid),addbuf();
  349. X      }                       /* are we allowed to add From_ lines? */
  350. X     else if(!force&&(!rdheader||!eqFrom_(rdheader->fld_text)))     /* missing? */
  351. X      { struct field*old;time_t t;         /* insert a From_ line up front */
  352. X    t=time((time_t*)0);old=rdheader;rdheader=0;
  353. X    loadbuf(From_,STRLEN(From_));
  354. X    if(namep)              /* we found a valid return address */
  355. X       loadbuf(namep,strlen(namep));
  356. X    else
  357. X       loadbuf(unknown,STRLEN(unknown));                /* Date: */
  358. X    if(!hdate->rexl||!findf(fdate,aheader))
  359. X       loadchar(' '),chp=ctime(&t),loadbuf(chp,strlen(chp)); /* no Date: */
  360. X    else                     /* we generate it ourselves */
  361. X       loadsaved(hdate);          /* yes, found Date:, then copy from it */
  362. X    addbuf();rdheader->fld_next=old;
  363. X      }
  364. X     for(fldp=aheader;fldp;fldp=fldp->fld_next)
  365. X    if(!findf(fldp,rdheader))           /* only add what didn't exist */
  366. X       addfield(&nheader,fldp->fld_text,fldp->tot_len);
  367. X     if((fldp= *(afldp= &rdheader))&&logsummary&&eqFrom_(fldp->fld_text))
  368. X    concatenate(fldp),putssn(fldp->fld_text,fldp->tot_len);
  369. X     while(fldp)
  370. X      { lnl=fldp->id_len;chp=fldp->fld_text;
  371. X    if(logsummary)
  372. X     { if(lnl==STRLEN(subject)&&!strnIcmp(chp,subject,lnl))
  373. X        { concatenate(fldp);chp[i=fldp->tot_len-1]='\0';detab(chp);
  374. X          putcs(' ');putssn(chp,i>=MAXSUBJECTSHOW?MAXSUBJECTSHOW:i);
  375. X          putcs('\n');
  376. X        }
  377. X     }
  378. X    else if(findf(fldp,xheader))           /* extract field contents */
  379. X       putssn(chp+lnl,fldp->tot_len-lnl);
  380. X    else if(findf(fldp,Xheader))               /* extract fields */
  381. X       putssn(chp,fldp->tot_len);
  382. X    if(findf(fldp,Iheader))                    /* delete fields */
  383. X     { *afldp=fldp->fld_next,free(fldp);fldp= *afldp;continue;
  384. X     }
  385. X    else if(fp2=findf(fldp,Rheader))      /* explicitly rename field */
  386. X       renfield(afldp,lnl,fp2->fld_text+lnl,fp2->tot_len-lnl);
  387. X    else if((fp2=findf(fldp,iheader))&&!(areply&&lnl==fp2->tot_len-1))
  388. X       renfield(afldp,(size_t)0,old_,STRLEN(old_)); /* implicitly rename */
  389. X    fldp= *(afldp= &(*afldp)->fld_next);
  390. X      }                    /* restore the saved contents of buf */
  391. X     tmemmove(buf,parkedbuf,buffilled=lenparkedbuf);free(parkedbuf);
  392. X   }
  393. X  if(xheader||Xheader)                 /* we're just extracting fields */
  394. X     clearfield(&rdheader),clearfield(&nheader);        /* throw it away */
  395. X  else                 /* otherwise, display the new & improved header */
  396. X   { flushfield(&rdheader);flushfield(&nheader);dispfield(Aheader);
  397. X     dispfield(iheader);dispfield(Iheader);lputcs('\n');  /* make sure it is */
  398. X   }                        /* followed by an empty line */
  399. X  if(namep)
  400. X     free(namep);
  401. X  if(!keepb&&(areply||xheader||Xheader))            /* decision time */
  402. X   { logfolder();                   /* we throw away the rest */
  403. X     if(split)
  404. X    closemine();
  405. X     opensink();                     /* discard the body */
  406. X   }
  407. X  lnl=1;                      /* last line was a newline */
  408. X  if(buffilled==1)           /* the header really ended with a newline */
  409. X     buffilled=0;          /* throw it away, since we already inserted it */
  410. X  while(buffilled||!lnl||buflast!=EOF)     /* continue the quest, line by line */
  411. X   { if(!buffilled)                      /* is it really empty? */
  412. X    readhead();                      /* read the next field */
  413. X     if(rdheader)            /* anything looking like a header found? */
  414. X      { if(eqFrom_(chp=rdheader->fld_text))          /* check if it's From_ */
  415. Xfromanyway:
  416. X     { register size_t k;
  417. X       if(split&&(lnl||every)&&    /* more thorough check for a postmark */
  418. X        (k=strcspn(chp=pstrspn(chp+STRLEN(From_)," \t")," \t\n"))&&
  419. X        *pstrspn(chp+k," \t")!='\n')
  420. X          goto accuhdr;             /* ok, postmark found, split it */
  421. X       if(bogus)                           /* disarm */
  422. X          lputcs(ESCAP);
  423. X     }
  424. X    else if(split&&digest&&(lnl||every)&&digheadr())      /* digest? */
  425. Xaccuhdr: { for(i=minfields;--i&&readhead()&&digheadr(););   /* found enough? */
  426. X       if(!i)                       /* then split it! */
  427. Xsplitit:    { if(!lnl)        /* did the previous mail end with an empty line? */
  428. X         lputcs('\n');                  /* but now it does :-) */
  429. X          logfolder();
  430. X          if((fclose(mystdout)==EOF||errout==EOF)&&!quiet)
  431. X         nlog(couldntw),elog(", continuing...\n"),split= -1;
  432. X          if(!nowait)
  433. X         waitforit();         /* wait till the child has finished */
  434. X          startprog((const char*Const*)argv);goto startover;
  435. X        }                        /* and there we go again */
  436. X     }
  437. X      }
  438. X     else if(eqFrom_(buf))             /* special case, From_ line */
  439. X      { addbuf();goto fromanyway;      /* add it manually, readhead() didn't */
  440. X      }
  441. X     else if(split&&digest&&(lnl||every)&&artheadr())
  442. X    goto accuhdr;
  443. X#ifdef MAILBOX_SEPARATOR
  444. X     if(!strncmp(emboxsep,buf,STRLEN(emboxsep)))         /* end of mail? */
  445. X      { if(split)               /* gobble up the next start separator */
  446. X     { buffilled=0;
  447. X#ifdef sMAILBOX_SEPARATOR
  448. X       getline();buffilled=0;         /* but only if it's defined */
  449. X#endif
  450. X       if(buflast!=EOF)                       /* if any */
  451. X          goto splitit;
  452. X       break;
  453. X     }
  454. X    else if(bogus)
  455. X       goto putsp;                   /* escape it with a space */
  456. X      }
  457. X     else if(!strncmp(smboxsep,buf,STRLEN(smboxsep)&&bogus))
  458. Xputsp:    lputcs(' ');
  459. X#endif /* MAILBOX_SEPARATOR */
  460. X     lnl=buffilled==1;              /* check if we just read an empty line */
  461. X     if(areply&&bogus)                      /* escape the body */
  462. X    if(fldp=rdheader)          /* we already read some "valid" fields */
  463. X     { register char*p;
  464. X       rdheader=0;
  465. X       do                   /* careful, they can contain newlines */
  466. X        { fp2=fldp->fld_next;chp=fldp->fld_text;
  467. X          do lputcs(ESCAP),lputssn(chp,(p=strchr(chp,'\n')+1)-chp);
  468. X          while((chp=p)<fldp->fld_text+fldp->tot_len);
  469. X          free(fldp);                    /* delete it */
  470. X        }
  471. X       while(fldp=fp2);               /* escape all fields we found */
  472. X     }
  473. X    else
  474. X     { if(buffilled>1)      /* we don't escape empty lines, looks neat */
  475. X          lputcs(ESCAP);
  476. X       goto flbuf;
  477. X     }
  478. X     else if(rdheader)
  479. X    flushfield(&rdheader); /* beware, after this buf can still be filled */
  480. X     else
  481. Xflbuf:    lputssn(buf,buffilled),buffilled=0;
  482. X   }                   /* make sure the mail ends with an empty line */
  483. X  logfolder();closemine();child= -1;waitforit();    /* wait for everyone */
  484. X  return split<0?EX_IOERR:EX_OK;
  485. X}
  486. X
  487. XeqFrom_(a)const char*const a;
  488. X{ return!strncmp(a,From_,STRLEN(From_));
  489. X}
  490. X
  491. Xbreakfield(line,len)const char*const line;size_t len;       /* look where the */
  492. X{ const char*p=line;               /* fieldname ends (RFC 822 specs) */
  493. X  if(eqFrom_(line))                      /* special case, From_ */
  494. X     return STRLEN(From_);
  495. X  while(len--&&!iscntrl(*p))            /* no control characters allowed */
  496. X   { switch(*p++)
  497. X      { default:continue;
  498. X    case HEAD_DELIMITER:len=p-line;return len==1?0:len;      /* eureka! */
  499. X    case ' ':;                    /* no spaces allowed */
  500. X      }
  501. X     break;
  502. X   }
  503. X  return 0;            /* sorry, does not seem to be a legitimate field */
  504. X}
  505. END_OF_FILE
  506. if test 18801 -ne `wc -c <'procmail280/src/formail.c'`; then
  507.     echo shar: \"'procmail280/src/formail.c'\" unpacked with wrong size!
  508. fi
  509. # end of 'procmail280/src/formail.c'
  510. fi
  511. if test -f 'procmail280/src/procmail.c' -a "${1}" != "-c" ; then 
  512.   echo shar: Will not clobber existing file \"'procmail280/src/procmail.c'\"
  513. else
  514. echo shar: Extracting \"'procmail280/src/procmail.c'\" \(23640 characters\)
  515. sed "s/^X//" >'procmail280/src/procmail.c' <<'END_OF_FILE'
  516. X/************************************************************************
  517. X *    procmail - The autonomous mail processor            *
  518. X *                                    *
  519. X *    It has been designed to be able to be run suid root and (in    *
  520. X *    case your mail spool area is *not* world writeable) sgid    *
  521. X *    mail (or daemon), without creating security holes.        *
  522. X *                                    *
  523. X *    Seems to be perfect.                        *
  524. X *                                    *
  525. X *    Copyright (c) 1990-1992, S.R. van den Berg, The Netherlands    *
  526. X *    #include "README"                        *
  527. X ************************************************************************/
  528. X#ifdef RCS
  529. Xstatic /*const*/char rcsid[]=
  530. X "$Id: procmail.c,v 1.25 1993/02/02 15:27:23 berg Exp $";
  531. X#endif
  532. X#include "../patchlevel.h"
  533. X#include "procmail.h"
  534. X#include "sublib.h"
  535. X#include "robust.h"
  536. X#include "shell.h"
  537. X#include "misc.h"
  538. X#include "pipes.h"
  539. X#include "common.h"
  540. X#include "cstdio.h"
  541. X#include "exopen.h"
  542. X#include "goodies.h"
  543. X#include "locking.h"
  544. X#include "mailfold.h"
  545. X
  546. Xstatic const char fdefault[]="DEFAULT",orgmail[]="ORGMAIL",
  547. X sendmail[]="SENDMAIL",From_[]=FROM,exflags[]=RECFLAGS,
  548. X systm_mbox[]=SYSTEM_MBOX,pmusage[]=PM_USAGE;
  549. Xchar*buf,*buf2,*globlock,*loclock,*tolock,*lastfolder;
  550. Xconst char shellflags[]="SHELLFLAGS",shell[]="SHELL",lockfile[]="LOCKFILE",
  551. X shellmetas[]="SHELLMETAS",lockext[]="LOCKEXT",newline[]="\n",binsh[]=BinSh,
  552. X unexpeof[]="Unexpected EOL\n",*const*gargv,*sgetcp,*rcfile=PROCMAILRC,
  553. X dirsep[]=DIRSEP,msgprefix[]="MSGPREFIX",devnull[]=DevNull,lgname[]="LOGNAME",
  554. X executing[]="Executing",oquote[]=" \"",cquote[]="\"\n",procmailn[]="procmail",
  555. X whilstwfor[]=" whilst waiting for ",home[]="HOME",maildir[]="MAILDIR";
  556. Xchar*Stdout;
  557. Xint retval=EX_CANTCREAT,retvl2=EX_OK,sh,pwait,lcking,rc=rc_INIT,
  558. X ignwerr,lexitcode=EX_OK,asgnlastf;
  559. Xsize_t linebuf=mx(DEFlinebuf+XTRAlinebuf,STRLEN(systm_mbox)<<1);
  560. Xvolatile int nextexit;                   /* if termination is imminent */
  561. Xpid_t thepid;
  562. Xlong filled;                       /* the length of the mail */
  563. Xchar*themail,*thebody;                /* the head and body of the mail */
  564. Xuid_t uid;
  565. Xgid_t gid,sgid;
  566. X
  567. Xmain(argc,argv)const char*const argv[];
  568. X{ static char flags[maxindex(exflags)];
  569. X  static const char*const keepenv[]=KEEPENV,*const prestenv[]=PRESTENV,
  570. X   *const trusted_ids[]=TRUSTED_IDS;
  571. X  char*chp,*startchar,*chp2,*fromwhom=0;long tobesent;
  572. X  int i,lastcond,succeed,suppmunreadable;
  573. X#define Deliverymode    lastcond
  574. X#define Presenviron    i
  575. X#define Privileged    succeed
  576. X  Deliverymode=0;
  577. X  if(argc)                   /* sanity check, any argument at all? */
  578. X   { Deliverymode=strcmp(lastdirsep(argv[0]),procmailn);
  579. X     for(Presenviron=argc=0;(chp2=(char*)argv[++argc])&&*chp2=='-';)
  580. X    for(;;)                           /* processing options */
  581. X     { switch(*++chp2)
  582. X        { case VERSIONOPT:elog(VERSION);return EX_OK;
  583. X          case HELPOPT1:case HELPOPT2:elog(pmusage);elog(PM_HELP);
  584. X         elog(PM_QREFERENCE);return EX_USAGE;
  585. X          case PRESERVOPT:Presenviron=1;continue;
  586. X          case TEMPFAILOPT:retval=EX_TEMPFAIL;continue;
  587. X          case FROMWHOPT:case ALTFROMWHOPT:
  588. X         if(*++chp2)
  589. X            fromwhom=chp2;
  590. X         else if(chp2=(char*)argv[argc+1])
  591. X            argc++,fromwhom=chp2;
  592. X         else
  593. X            nlog("Missing name\n");
  594. X         break;
  595. X          case DELIVEROPT:
  596. X         if(!*(chp= ++chp2)&&!(chp=(char*)argv[++argc]))
  597. X          { nlog("Missing recipient\n");break;
  598. X          }
  599. X         else
  600. X          { Deliverymode=1;goto last_option;
  601. X          }
  602. X          case '-':
  603. X         if(!*chp2)
  604. X          { argc++;goto last_option;
  605. X          }
  606. X          default:nlog("Unrecognised options:");logqnl(chp2);
  607. X         elog(pmusage);elog("Processing continued\n");
  608. X          case '\0':;
  609. X        }
  610. X       break;
  611. X     }
  612. X   }
  613. Xlast_option:
  614. X  if(!Presenviron)                     /* drop the environment */
  615. X   { const char**emax=(const char**)environ,*const*ep,*const*kp;
  616. X     for(kp=keepenv;*kp;kp++)                 /* preserve a happy few */
  617. X    for(i=strlen(*kp),ep=emax;chp2=(char*)*ep;ep++)
  618. X       if(!strncmp(*kp,chp2,i)&&(chp2[i]=='='||chp2[i-1]=='_'))
  619. X        { *emax++=chp2;break;
  620. X        }
  621. X     *emax=0;                            /* drop the rest */
  622. X   }
  623. X#ifdef LD_ENV_FIX
  624. X  ;{ const char**emax=(const char**)environ,**ep;static const char ld_[]="LD_";
  625. X     for(ep=emax;*emax;emax++);          /* find the end of the environment */
  626. X     while(*ep)
  627. X    if(!strncmp(ld_,*ep++,STRLEN(ld_)))           /* it starts with LD_ */
  628. X       *--ep= *--emax,*emax=0;            /* copy from the end */
  629. X   }
  630. X#endif /* LD_ENV_FIX */
  631. X  ;{ struct passwd*pass,*passinvk,spassinvk;uid_t euid=geteuid();
  632. X     if(passinvk=getpwuid(uid=getuid()))        /* save it by copying it */
  633. X    tmemmove(&spassinvk,passinvk,sizeof spassinvk),passinvk= &spassinvk;
  634. X     Privileged=1;gid=getgid();
  635. X     if(*trusted_ids&&uid!=euid)
  636. X      { struct group*grp;const char*const*kp;
  637. X    if(passinvk)                  /* check out the invoker's uid */
  638. X       for(chp2=passinvk->pw_name,kp=trusted_ids;*kp;)
  639. X          if(!strcmp(chp2,*kp++))          /* is it among the privileged? */
  640. X        goto privileged;
  641. X    if(grp=getgrgid(gid))              /* check out the invoker's gid */
  642. X      for(chp2=grp->gr_name,kp=trusted_ids;*kp;)
  643. X         if(!strcmp(chp2,*kp++))          /* is it among the privileged? */
  644. X        goto privileged;
  645. X    Privileged=0;
  646. X    if(Deliverymode)
  647. X      fromwhom=0;
  648. X      }
  649. Xprivileged:
  650. X     endgrent();endpwent();umask(INIT_UMASK);fclose(stdout);rclose(STDOUT);
  651. X     if(0>opena(devnull))
  652. X      { writeerr(devnull);return EX_OSFILE;         /* couldn't open stdout */
  653. X      }
  654. X#ifdef console
  655. X     openlog(console);
  656. X#endif
  657. X     setbuf(stdin,(char*)0);buf=malloc(linebuf);buf2=malloc(linebuf);
  658. X     lastfolder=cstr(lastfolder,"");thepid=getpid();
  659. X#ifdef SIGXCPU
  660. X     signal(SIGXCPU,SIG_IGN);signal(SIGXFSZ,SIG_IGN);
  661. X#endif
  662. X#ifdef SIGLOST
  663. X     signal(SIGLOST,SIG_IGN);
  664. X#endif
  665. X     signal(SIGPIPE,SIG_IGN);signal(SIGTERM,(void(*)())srequeue);
  666. X     signal(SIGINT,(void(*)())sbounce);signal(SIGHUP,(void(*)())sbounce);
  667. X     signal(SIGQUIT,(void(*)())slose);signal(SIGALRM,(void(*)())ftimeout);
  668. X     ultstr(0,(unsigned long)uid,buf);
  669. X     chp2=fromwhom?
  670. X      fromwhom:!passinvk||!*passinvk->pw_name?buf:passinvk->pw_name;
  671. X     ;{ time_t t;
  672. X    t=time((time_t*)0);startchar=ctime(&t);         /* the current time */
  673. X      }
  674. X     strncpy(buf2,chp2,i=linebuf-strlen(startchar)-2);buf2[i]='\0';
  675. X     strcat(strcat(buf2," "),startchar);
  676. X     thebody=themail=
  677. X      malloc((tobesent=STRLEN(From_)+strlen(buf2))+STRLEN(From_));
  678. X     filled=0;
  679. X     if(Deliverymode||fromwhom)     /* do we need to peek for a leading From_ ? */
  680. X      { int r;                             /* skip garbage */
  681. X    while(1==(r=rread(STDIN,themail,1))&&*themail=='\n');
  682. X    if(STRLEN(From_)-1==(i=rread(STDIN,themail+1,STRLEN(From_)-1))&&
  683. X     eqFrom_(themail))                  /* is it a From_ line? */
  684. X     { if(fromwhom||!Privileged)
  685. X        { char a;
  686. X          while(1==rread(STDIN,&a,1)&&a!='\n');    /* discard From_ line */
  687. X          i=0;goto Frominserted;
  688. X        }
  689. X       filled=STRLEN(From_);           /* leave the From_ line alone */
  690. X     }
  691. X    else           /* move the read-ahead text beyond our From_ line */
  692. X     { tmemmove(themail+tobesent,themail,i=r<=0?0:i>0?i+1:1);
  693. X       strcpy(themail,From_);          /* insert From_ of our own */
  694. XFrominserted:
  695. X       tmemmove(themail+STRLEN(From_),buf2,tobesent-STRLEN(From_));
  696. X       filled=tobesent+i;
  697. X     }
  698. X      }
  699. X     readmail(0,0L);                  /* read in the mail completely */
  700. X     if(Deliverymode)
  701. X    do
  702. X     { chp2=chp;
  703. X#ifndef NO_USER_TO_LOWERCASE_HACK
  704. X       for(;*chp;chp++)          /* kludge recipient into lowercase */
  705. X          if((unsigned)*chp-'A'<='Z'-'A')
  706. X         *chp+='a'-'A';     /* because getpwnam might be case sensitive */
  707. X#endif
  708. X       if(argv[++argc])              /* more than one recipient */
  709. X          if(pidchild=sfork())
  710. X           { if(forkerr(pidchild,procmailn)||waitfor(pidchild)!=EX_OK)
  711. X            retvl2=retval;
  712. X         pidchild=0;              /* loop for the next recipient */
  713. X           }
  714. X          else
  715. X           { thepid=getpid();
  716. X         while(argv[++argc]);        /* skip till end of command line */
  717. X           }
  718. X     }
  719. X    while(chp=(char*)argv[argc]);
  720. X     gargv=argv+argc;                 /* save it for nextrcfile() */
  721. X     if(Deliverymode)
  722. X      { if(!(pass=getpwnam(chp2)))
  723. X     { nlog("Unknown user");logqnl(chp2);return EX_NOUSER;
  724. X     }
  725. X    if(passinvk&&passinvk->pw_uid==pass->pw_uid||euid==ROOT_uid)
  726. X       goto Setuser;
  727. X      }
  728. X     if(pass=passinvk)
  729. XSetuser:
  730. X       /*
  731. X    *    set preferred uid to the intended recipient
  732. X    */
  733. X      { gid=pass->pw_gid;uid=pass->pw_uid;setdef(home,pass->pw_dir);
  734. X    setdef(lgname,chp= *pass->pw_name?pass->pw_name:buf);
  735. X    if(euid==ROOT_uid)
  736. X       initgroups(chp,gid);
  737. X    endgrent();setdef(shell,*pass->pw_shell?pass->pw_shell:binsh);
  738. X      }
  739. X     else         /* user could not be found, set reasonable defaults */
  740. X       /*
  741. X    *    to prevent security holes, drop any privileges now
  742. X    */
  743. X      { setdef(home,RootDir);setdef(lgname,buf);setdef(shell,binsh);
  744. X    setgid(gid);setuid(uid);
  745. X      }
  746. X     endpwent();
  747. X   }
  748. X  setdef(orgmail,systm_mbox);setdef(shellmetas,DEFshellmetas);
  749. X  setdef(shellflags,DEFshellflags);setdef(maildir,DEFmaildir);
  750. X  setdef(fdefault,DEFdefault);setdef(sendmail,DEFsendmail);
  751. X  setdef(lockext,DEFlockext);setdef(msgprefix,DEFmsgprefix);
  752. X  ;{ const char*const*kp;
  753. X     for(kp=prestenv;*kp;)  /* preset or wipe selected environment variables */
  754. X    strcpy((char*)(sgetcp=buf2),*kp++),readparse(buf,sgetc,2),sputenv(buf);
  755. X   }
  756. X  strcpy(buf2,strcpy(buf,chp=(char*)getenv(orgmail)));
  757. X  buf[i=lastdirsep(chp)-chp]='\0';           /* strip off the basename */
  758. X  ;{ struct stat stbuf;
  759. X     sgid=gid;                        /* presumed innocent */
  760. X    /*
  761. X     *    do we need sgidness to access the mail-spool directory/files?
  762. X     */
  763. X     if(!stat(buf,&stbuf))
  764. X      { if((uid!=stbuf.st_uid&&stbuf.st_gid==getegid()||(rc=rc_NOSGID,0))&&
  765. X     (stbuf.st_mode&(S_IWGRP|S_IXGRP|S_IWOTH))==(S_IWGRP|S_IXGRP))
  766. X     { umask(INIT_UMASK&~S_IRWXG);goto keepgid;      /* group-writeable */
  767. X     }
  768. X    else if(stbuf.st_mode&S_ISGID)
  769. Xkeepgid:   sgid=stbuf.st_gid;       /* keep the gid from the parent directory */
  770. X      }
  771. X    /*
  772. X     *    check if the default-mailbox-lockfile is owned by the recipient, if
  773. X     *    not, mark it for further investigation, it might need to be removed
  774. X     */
  775. X     if(!(Privileged=lstat(strcat(buf2,getenv(lockext)),&stbuf)||
  776. X      stbuf.st_uid==uid))            /* check for bogus lockfiles */
  777. X    ultoan((unsigned long)stbuf.st_ino,          /* i-node numbered */
  778. X     strchr(strcpy(buf+i,BOGUSprefix),'\0'));
  779. X    /*
  780. X     *    check if the original/default mailbox of the recipient exists, if it
  781. X     *    does, perform some security checks on it (check if it's a regular
  782. X     *    file, check if it's owned by the recipient), if something is wrong
  783. X     *    try and move the bogus mailbox out of the way,    create the
  784. X     *    original/default mailbox file, and chown it to the recipient
  785. X     */
  786. X     if(lstat(chp,&stbuf))                 /* stat the mailbox */
  787. X      { succeed= -(errno==EACCES);goto boglock;
  788. X      }
  789. X     if(!Privileged&&!(stbuf.st_mode&S_IWGRP))    /* lockfile unrightful owner */
  790. X      { succeed=1;
  791. Xboglock:
  792. X    if(!Privileged)      /* try to rename the bogus lockfile out of the way */
  793. X       rename(buf2,buf);
  794. X      }
  795. X     else
  796. X    succeed=1;                 /* mailbox a symbolic link? */
  797. X     if(succeed>0||succeed<0&&(setgid(gid),setuid(uid),!lstat(chp,&stbuf)))
  798. X    if(!(stbuf.st_mode&S_IWUSR)||S_ISLNK(stbuf.st_mode)||
  799. X     (S_ISDIR(stbuf.st_mode)?!(stbuf.st_mode&S_IXUSR):stbuf.st_nlink!=1))
  800. X       goto bogusbox;     /* we only deliver to real files (security) */
  801. X    else if(stbuf.st_uid!=uid)         /* the recipient doesn't own it */
  802. Xbogusbox:                         /* bogus mailbox found! */
  803. X     { ultoan((unsigned long)stbuf.st_ino,          /* i-node numbered */
  804. X        strchr(strcpy(buf+i,BOGUSprefix),'\0'));
  805. X       if(rename(chp,buf))           /* try and move it out of the way */
  806. X          goto fishy;     /* couldn't rename, something is fishy here */
  807. X     }                    /* SysV type autoforwarding? */
  808. X    else if(Deliverymode&&stbuf.st_mode&(S_ISGID|S_ISUID))
  809. X     { nlog("Autoforwarding mailbox found\n");return EX_NOUSER;
  810. X     }
  811. X    else
  812. X       goto notfishy;                   /* everything is fine */
  813. X     if(!xcreat(chp,NORMperm,(time_t*)0,&i))   /* try and create the mailbox */
  814. X    if(i)                           /* could we chown it? */
  815. X       unlink(chp);            /* no we couldn't, NFS-mount or not root */
  816. X    else
  817. X       goto notfishy;              /* yes we could, fine, proceed */
  818. X     setgid(gid);setuid(uid);                   /* try some magic */
  819. X     if(xcreat(chp,NORMperm,(time_t*)0,(int*)0))   /* try to create it again */
  820. Xfishy:                        /* bad news, be conservative */
  821. X    nlog("Couldn't create"),logqnl(chp),sputenv(orgmail),sputenv(fdefault);
  822. Xnotfishy:
  823. X     umask(INIT_UMASK);
  824. X   }
  825. X  suppmunreadable=!verbose;
  826. X  if(!Deliverymode)                   /* not explicit delivery mode */
  827. X    /*
  828. X     *    really change the uid now, since we are not in explicit
  829. X     *    delivery mode
  830. X     */
  831. X   { setgid(gid);setuid(uid);
  832. X     if(suppmunreadable=nextrcfile())      /* any rcfile on the command-line? */
  833. X#ifndef NO_COMSAT
  834. X    if(!getenv(scomsat))
  835. X       setdef(scomsat,DEFcomsat)           /* turn off comsat by default */
  836. X#endif
  837. X     ;
  838. X     while(chp=(char*)argv[argc])      /* interpret command line specs first */
  839. X    argc++,asenvcpy(chp);
  840. X   }
  841. X  do                         /* main rcfile interpreter loop */
  842. X   { alarm((unsigned)(alrmtime=0));                /* reset timeout */
  843. X     if(rc<0)                         /* open new rc file */
  844. X      { struct stat stbuf;
  845. X       /*
  846. X    *    if we happen to be still running as root, and the rcfile
  847. X    *    is mounted on a secure NFS-partition, we might not be able
  848. X    *    to access it, so check if we can stat it or don't need any
  849. X    *    sgid privileges, if yes, drop all privs and set uid to
  850. X    *    the recipient beforehand
  851. X    */
  852. X    goto findrc;
  853. X    do
  854. X     { if(suppmunreadable)          /* should we supress this message? */
  855. Xfake_rc:      readerr(buf);
  856. X       if(!nextrcfile())              /* not available? try the next */
  857. X          goto nomore_rc;
  858. Xfindrc:       suppmunreadable=i=0;        /* should we keep the current directory? */
  859. X       if(strchr(dirsep,*rcfile)||               /* absolute path? */
  860. X        *rcfile==chCURDIR&&strchr(dirsep,rcfile[1])&&(i=1)) /* ./ prefix */
  861. X          *buf='\0';        /* do not put anything in front then */
  862. X       else
  863. X          cat(tgetenv(home),MCDIRSEP);      /* prepend $HOME directory */
  864. X       if(stat(strcat(buf,rcfile),&stbuf)?              /* accessible? */
  865. X        rc==rc_NOSGID:stbuf.st_mode&S_IRUSR)      /* and owner-readable? */
  866. X          setgid(gid),setuid(uid);            /* then transmogrify */
  867. X     }
  868. X    while(0>bopen(buf));               /* try opening the rcfile */
  869. X    if(i&&!didchd)          /* opened rcfile in the current directory? */
  870. X     { didchd=1;*(chp=strcpy(buf2,maildir)+STRLEN(maildir))='=';
  871. X       *++chp=chCURDIR;*++chp='\0';sputenv(buf2);
  872. X     }
  873. X       /*
  874. X    *    OK, so now we have opened an rcfile, but for security reasons
  875. X    *    we only accept it if it is owned by the recipient or if the
  876. X    *    the directory it is in, is not world writeable
  877. X    */
  878. X    i= *(chp=lastdirsep(buf));
  879. X    if(lstat(buf,&stbuf)||
  880. X     (stbuf.st_uid!=uid&&(*chp='\0',stat(buf,&stbuf)||
  881. X     (stbuf.st_mode&(S_IWOTH|S_IXOTH))==(S_IWOTH|S_IXOTH))))
  882. X     { *chp=i;rclose(rc);nlog("Suspicious rcfile\n");goto fake_rc;
  883. X     }
  884. X       /*
  885. X    *    set uid back to recipient in any case, since we might just
  886. X    *    have opened his/her .procmailrc (don't remove these, since
  887. X    *    the rcfile might have been created after the first stat)
  888. X    */
  889. X    *chp=i;yell("Rcfile:",buf);succeed=lastcond=0;setgid(gid);setuid(uid);
  890. X    firstchd();
  891. X      }
  892. X     unlock(&loclock);                /* unlock any local lockfile */
  893. X     do skipspace();                      /* skip whitespace */
  894. X     while(testb('\n'));
  895. X     if(testb(':'))                       /* check for a recipe */
  896. X      { int locknext;
  897. X    ;{ int nrcond;
  898. X       readparse(buf,getb,0);nrcond=strtol(buf,&chp,10);
  899. X       if(chp==buf)                     /* no number parsed */
  900. X          nrcond= -1;
  901. X       if(tolock)         /* clear temporary buffer for lockfile name */
  902. X          free(tolock);
  903. X       for(i=maxindex(flags);flags[i]=0,i--;);      /* clear the flags */
  904. X       for(tolock=0,locknext=0;;)
  905. X        { chp=skpspace(chp);
  906. X          switch(i= *chp++)
  907. X           { default:
  908. X            if(!(chp2=strchr(exflags,i)))  /* check for a valid flag */
  909. X             { chp--;break;
  910. X             }
  911. X            flags[chp2-exflags]=1;             /* set the flag */
  912. X         case '\0':
  913. X            if(*chp!=TMNATE)        /* if not the real end, skip */
  914. X               continue;
  915. X            break;
  916. X         case ':':locknext=1;        /* yep, local lockfile specified */
  917. X            if(*chp||*++chp!=TMNATE)
  918. X               tolock=tstrdup(chp),chp=strchr(chp,'\0')+1;
  919. X           }
  920. X          concatenate(chp);skipped(chp);break;  /* display any leftovers */
  921. X        }
  922. X       if(nrcond<0)      /* assume the appropriate default nr of conditions */
  923. X          nrcond=!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC];
  924. X       startchar=themail;tobesent=thebody-themail;
  925. X       if(flags[BODY_GREP])               /* what needs to be egrepped? */
  926. X          if(flags[HEAD_GREP])
  927. X         tobesent=filled;
  928. X          else
  929. X           { startchar=thebody;tobesent=filled-tobesent;goto noconcat;
  930. X           }
  931. X       concon(' ');
  932. Xnoconcat:  i=flags[ALSO_NEXT_RECIPE]?lastcond:1;      /* init test value */
  933. X       if(flags[ALSO_N_IF_SUCC])
  934. X          i=lastcond&&succeed;    /* only if the last recipe succeeded */
  935. X       while(nrcond--)                /* any conditions (left) */
  936. X        { skipspace();getlline(buf2);
  937. X          for(chp=strchr(buf2,'\0');--chp>=buf2;)
  938. X           { switch(*chp)          /* strip off whitespace at the end */
  939. X          { case ' ':case '\t':*chp='\0';continue;
  940. X          }
  941. X         break;
  942. X           }
  943. X          if(i)                 /* check out all conditions */
  944. X           { int negate=0;
  945. X         for(chp=buf2+1;;strcpy(buf2,buf))
  946. X          { switch(*(sgetcp=buf2))
  947. X             { default:chp--;         /* no special character, backup */
  948. X               case '\\':
  949. X            { int or_nocase;    /* case-distinction override */
  950. X              static const struct {const char*regkey,*regsubst;}
  951. X               *regsp,regs[]=
  952. X                { {FROMDkey,FROMDsubstitute},
  953. X                  {TOkey,TOsubstitute},
  954. X                  {0,0}
  955. X                };
  956. X              ;{ char*tg;
  957. X                 for(or_nocase=0,tg=chp2=chp;;tg++,chp2++)
  958. X                  { switch(*tg= *chp2)
  959. X                 { case '\n':
  960. X                      if(or_nocase==1)
  961. X                     tg-=2;         /* throw out \ \n pairs */
  962. X                      or_nocase=2;continue;
  963. X                   case '\\':or_nocase=1;continue;
  964. X                   case ' ':case '\t':
  965. X                      if(or_nocase==2)         /* skip leading */
  966. X                       { tg--;continue;           /* whitespace */
  967. X                       }
  968. X                   default:or_nocase=0;continue;
  969. X                   case '\0':;
  970. X                 }
  971. X                break;
  972. X                  }
  973. X               }
  974. X              or_nocase=0;goto jinregs;
  975. X              do           /* find special keyword in regexp */
  976. X                 if((chp2=strstr(chp,regsp->regkey))&&
  977. X                  (chp2==buf2||chp2[-1]!='\\'))  /* not escaped? */
  978. X                  { size_t lregs,lregk;
  979. X                lregk=strlen(regsp->regkey);  /* shove it in */
  980. X                tmemmove(chp2+(lregs=strlen(regsp->regsubst)),
  981. X                 chp2+lregk,strlen(chp2)-lregk+1);
  982. X                tmemmove(chp2,regsp->regsubst,lregs);
  983. X                if(regsp==regs)      /* check for daemon regexp */
  984. X                   or_nocase=1;         /* no case sensitivity! */
  985. Xjinregs:            regsp=regs;    /* start over and look again */
  986. X                  }
  987. X                 else
  988. X                regsp++;             /* next keyword */
  989. X              while(regsp->regkey);
  990. X              i=!!egrepin(chp,startchar,tobesent,     /* egrep it */
  991. X               or_nocase?0:flags[DISTINGUISH_CASE]);
  992. X              break;
  993. X            }
  994. X               case '$':*buf2='"';readparse(buf,sgetc,2);continue;
  995. X               case '!':negate^=1;strcpy(buf,chp);continue;
  996. X               case '?':pwait=2;metaparse(chp);inittmout(buf);
  997. X               ignwerr=1;i=!pipin(buf,startchar,tobesent);
  998. X               strcpy(buf2,buf);break;
  999. X               case '>':case '<':readparse(buf,sgetc,2);
  1000. X              i=strtol(buf+1,&chp,10);
  1001. X              i='<'==*buf?filled<i:filled>i;skipped(skpspace(chp));
  1002. X              strcpy(buf2,buf);            /* leftovers */
  1003. X             }
  1004. X            break;
  1005. X          }
  1006. X         i^=negate;
  1007. X         if(verbose)         /* not entirely correct, but it will do */
  1008. X          { nlog(i?"M":"No m");elog("atch on");
  1009. X            if(negate)
  1010. X               elog(" !");
  1011. X            logqnl(buf2);
  1012. X          }
  1013. X           }
  1014. X        }
  1015. X     }
  1016. X    if(!flags[ALSO_NEXT_RECIPE]&&!flags[ALSO_N_IF_SUCC])
  1017. X       lastcond=i;               /* save the outcome for posterity */
  1018. X    startchar=themail;tobesent=filled;        /* body, header or both? */
  1019. X    if(flags[PASS_HEAD])
  1020. X     { if(!flags[PASS_BODY])
  1021. X          tobesent=thebody-themail;
  1022. X     }
  1023. X    else if(flags[PASS_BODY])
  1024. X       tobesent-=(startchar=thebody)-themail;
  1025. X    chp=strchr(strcpy(buf,tgetenv(sendmail)),'\0');succeed=sh=0;
  1026. X    pwait=flags[WAIT_EXIT]|flags[WAIT_EXIT_QUIET]<<1;asgnlastf=1;
  1027. X    ignwerr=flags[IGNORE_WRITERR];Stdout=0;skipspace();
  1028. X    if(i)
  1029. X       concon('\n');
  1030. Xprogrm: if(testb('!'))                     /* forward the mail */
  1031. X     { readparse(chp+1,getb,0);
  1032. X       if(i)
  1033. X          goto forward;
  1034. X     }
  1035. X    else if(testb('|'))                    /* pipe the mail */
  1036. X     { getlline(buf2);             /* get the command to start */
  1037. X       if(i)
  1038. X        { metaparse(buf2);
  1039. Xforward:      if(locknext)
  1040. X           { if(!tolock)       /* an explicit lockfile specified already */
  1041. X          { *buf2='\0';        /* find the implicit lockfile ('>>name') */
  1042. X            for(chp=buf;i= *chp++;)
  1043. X               if(i=='>'&&*chp=='>')
  1044. X            { chp=pstrspn(chp+1," \t");
  1045. X              tmemmove(buf2,chp,i=strcspn(chp,EOFName));
  1046. X              buf2[i]='\0';
  1047. X              if(sh)     /* expand any environment variables */
  1048. X               { chp=tstrdup(buf);sgetcp=buf2;
  1049. X                 readparse(buf,sgetc,0);strcpy(buf2,buf);
  1050. X                 strcpy(buf,chp);free(chp);
  1051. X               }
  1052. X              break;
  1053. X            }
  1054. X            if(!*buf2)
  1055. X             { nlog("Couldn't determine implicit lockfile from");
  1056. X               logqnl(buf);
  1057. X             }
  1058. X          }
  1059. X         lcllock();
  1060. X         if(!pwait)        /* try and protect the user from his */
  1061. X            pwait=2;               /* blissful ignorance :-) */
  1062. X           }
  1063. X          inittmout(buf);
  1064. X          if(flags[FILTER])
  1065. X           { if(startchar==themail&&tobesent!=filled)     /* if only 'h' */
  1066. X          { if(!pipthrough(buf,startchar,tobesent))
  1067. X               succeed=1,readmail(1,tobesent);
  1068. X          }
  1069. X         else if(!pipthrough(buf,startchar,tobesent))
  1070. X            succeed=1,filled=startchar-themail,readmail(0,0L);
  1071. X           }
  1072. X          else if(Stdout)              /* capturing stdout again? */
  1073. X           { if(!pipthrough(buf,startchar,tobesent))
  1074. X            succeed=1,postStdout();      /* only parse if no errors */
  1075. X           }
  1076. X          else if(!pipin(buf,startchar,tobesent)&&      /* regular program */
  1077. X           (succeed=1,!flags[CONTINUE]))
  1078. X         goto mailed;
  1079. X        }
  1080. X     }
  1081. X    else           /* dump the mail into a mailbox file or directory */
  1082. X     { if(flags[FILTER])
  1083. X          flags[FILTER]=0,nlog("Extraneous filter-flag ignored\n");
  1084. X       if(chp=gobenv(buf))           /* can it be an environment name? */
  1085. X        { if(skipspace())
  1086. X         chp++;               /* keep pace with argument breaks */
  1087. X          if(testb('='))              /* is it really an assignment? */
  1088. X           { int c;
  1089. X         *chp++='=';*chp='\0';
  1090. X         if(skipspace())
  1091. X            chp++;
  1092. X         ungetb(c=getb());
  1093. X         switch(c)
  1094. X          { case '!':case '|':              /* ok, it's a pipe */
  1095. X               if(i)
  1096. X              primeStdout();
  1097. X               goto progrm;
  1098. X          }
  1099. X           }
  1100. X        }
  1101. X       else
  1102. X          chp=strchr(buf,'\0');                 /* find the end */
  1103. X       readparse(chp,getb,0);concatenate(chp=strchr(buf,'\0')+1);
  1104. X       skipped(chp);                 /* report any leftovers */
  1105. X       if(i)
  1106. X        { strcpy(buf2,buf);
  1107. X          if(locknext)
  1108. X         lcllock();
  1109. X          strcpy(buf2,buf);             /* write to a file or directory */
  1110. X          if(dump(deliver(buf2),startchar,tobesent)&&!ignwerr)
  1111. X         writeerr(buf);
  1112. X          else if(succeed=1,!flags[CONTINUE])
  1113. X         goto mailed;
  1114. X        }
  1115. X     }
  1116. X      }
  1117. X     else if(testb('#'))                   /* no comment :-) */
  1118. X    getbl(buf);
  1119. X     else                    /* then it must be an assignment */
  1120. X      { if(!(chp=gobenv(buf)))
  1121. X     { if(!*buf)                    /* skip a word first */
  1122. X          getbl(buf);                      /* then a line */
  1123. X       skipped(buf);continue;            /* display leftovers */
  1124. X     }
  1125. X    skipspace();
  1126. X    if(testb('='))                   /* removal or assignment? */
  1127. X       *chp='=',readparse(++chp,getb,1);
  1128. X    else
  1129. X       *++chp='\0';                 /* throw in a second terminator */
  1130. X    sputenv(buf);chp[-1]='\0';asenv(chp);
  1131. X      }
  1132. X   }
  1133. X  while(rc<0||!testb(EOF)||poprc());            /* main interpreter loop */
  1134. Xnomore_rc:
  1135. X  concon('\n');succeed=0;
  1136. X  if(*(chp=(char*)tgetenv(fdefault)))                 /* DEFAULT set? */
  1137. X   { setuid(uid);firstchd();asenvcpy(DEFdefaultlock);        /* implicit lock */
  1138. X     if(dump(deliver(chp),themail,filled))              /* default */
  1139. X    writeerr(buf);
  1140. X     else
  1141. X    succeed=1;
  1142. X   }
  1143. X  if(!succeed&&*(chp=(char*)tgetenv(orgmail)))           /* if all else failed */
  1144. X     if(dump(deliver(chp),themail,filled))    /* don't panic, try the last */
  1145. X    writeerr(buf);                           /* resort */
  1146. X     else
  1147. X    succeed=1;
  1148. X  if(succeed)                         /* should we panic now? */
  1149. Xmailed:
  1150. X     retval=EX_OK;              /* we're home free, mail delivered */
  1151. X  unlock(&loclock);terminate();
  1152. X}
  1153. X
  1154. XeqFrom_(a)const char*const a;
  1155. X{ return!strncmp(a,From_,STRLEN(From_));
  1156. X}
  1157. END_OF_FILE
  1158. if test 23640 -ne `wc -c <'procmail280/src/procmail.c'`; then
  1159.     echo shar: \"'procmail280/src/procmail.c'\" unpacked with wrong size!
  1160. fi
  1161. # end of 'procmail280/src/procmail.c'
  1162. fi
  1163. echo shar: End of archive 10 \(of 11\).
  1164. cp /dev/null ark10isdone
  1165. MISSING=""
  1166. for I in 1 2 3 4 5 6 7 8 9 10 11 ; do
  1167.     if test ! -f ark${I}isdone ; then
  1168.     MISSING="${MISSING} ${I}"
  1169.     fi
  1170. done
  1171. if test "${MISSING}" = "" ; then
  1172.     echo You have unpacked all 11 archives.
  1173.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1174. else
  1175.     echo You still need to unpack the following archives:
  1176.     echo "        " ${MISSING}
  1177. fi
  1178. ##  End of shell archive.
  1179. exit 0
  1180. -- 
  1181. Sincerely,                                  berg@pool.informatik.rwth-aachen.de
  1182.            Stephen R. van den Berg (AKA BuGless).    berg@physik.tu-muenchen.de
  1183.  
  1184. "Be spontaneous!"
  1185.  
  1186. exit 0 # Just in case...
  1187.