home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume43 / SmartList / part01 next >
Internet Message Format  |  1994-07-05  |  56KB

  1. From: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  2. Newsgroups: comp.sources.misc
  3. Subject: v43i066:  SmartList - mailinglist package v3.03, Part01/05
  4. Date: 5 Jul 1994 21:26:35 -0500
  5. Organization: Sterling Software
  6. Sender: kent@sparky.sterling.com
  7. Approved: kent@sparky.sterling.com
  8. Message-ID: <csm-v43i066=SmartList.212556@sparky.sterling.com>
  9. X-Md4-Signature: b94998e1ac18eb9d62b1a9b8d9d8d2f7
  10.  
  11. Submitted-by: berg@pool.informatik.rwth-aachen.de (Stephen R. van den Berg)
  12. Posting-number: Volume 43, Issue 66
  13. Archive-name: SmartList/part01
  14. Environment: procmail, sendmail, ZMailer, smail, /bin/mail, UNIX, POSIX
  15.  
  16. The SmartList mailinglist management package. (v3.03 1994/06/30)
  17.  
  18. The SmartList mailinglist package has been built on top of the procmail
  19. mail processing package.  In order to install it you'll need the source
  20. of the procmail package as well.
  21.  
  22. If you now have just the SmartList sources, get the procmail sources and
  23. unpack them on top of the SmartList source tree.
  24. ----------------------
  25. A recent version of both packages can be picked up at various
  26. comp.sources.misc archives.
  27. The latest versions can be obtained directly from the ftp-archive at:
  28.  
  29.         ftp.informatik.rwth-aachen.de
  30.  
  31. as (g)zipped tar files:    /pub/packages/procmail/procmail.tar.gz
  32.                            /pub/packages/procmail/SmartList.tar.gz
  33. as compressed tar files:   /pub/packages/procmail/procmail.tar.Z
  34.                            /pub/packages/procmail/SmartList.tar.Z
  35. ----------------------
  36. Summary of what SmartList provides:
  37.     + The overseeable management of an arbitrary number of mailinglists
  38.     + Convenient and simple creation of new mailinglists
  39.     + Convenient and simple removal of existing mailinglists
  40.     + Fully automated subscription/unsubscription/help-request processing
  41.       (no operator intervention needed)
  42.     + Enough intelligence to overcome the ignorance of some subscribers
  43.       (will direct subscribe and unsubscribe requests away from the
  44.       regular list and automatically onto the -request address)
  45.     + No hardwired format for (un)subscribe requests (i.e. new subscribers
  46.       need not be educated, unsubscribing users do not need to remember
  47.       any particular syntax)
  48.     + *Intelligent* autoremoval of addresses from the list that cause
  49.       too many bounces
  50.     + Submissions can be limited to people on the accept list (which could
  51.       be the current list of subscribers)
  52.     + The fully automated subscription mechanism allows for a reject list
  53.       of unwanted subscribers and a general address screening mechanism
  54.       which allows you to control exactly who is allowed to subscribe
  55.     + Optional implicit subscription upon first submission to the list
  56.     + MIME-compliant auto-digest-generation (configurable per list)
  57.     + Joint management of several mailinglists possible
  58.     + Customisation per mailinglist or mailinglist group possible (simply
  59.       remove or create the desired hardlinks)
  60.     + A listmaintainer can be assigned per list;  miscellaneous requests
  61.       that couldn't be handled by the list automatically are then
  62.       forwarded to his mail address (instead of being accumulated in
  63.       a file)
  64.     + Allows for remote maintenance of any mailinglist by a
  65.       listmaintainer
  66.     + Integrated archiving service
  67.     + Integrated diagnostic aid to give hints to the maintainer about
  68.       possible problems
  69.     + Moderated mailinglists with an arbitrary number of moderators
  70.     + Automatically eliminates duplicate submissions
  71.     + You can set up a mailinglist to function as a standalone mail
  72.       archive server
  73.     + Extended MIME support (autorecognition of well known file formats)
  74.     + The archive server can send arbitrarily long (even binary) files
  75.       in MIME-multipart mails
  76. ----------------------
  77. #! /bin/sh
  78. # This is a shell archive.  Remove anything before this line, then feed it
  79. # into a shell via "sh file" or similar.  To overwrite existing files,
  80. # type "sh file -c".
  81. # Contents:  procmail-3.03 procmail-3.03/SmartList
  82. #   procmail-3.03/SmartList/INSTALL procmail-3.03/SmartList/bin
  83. #   procmail-3.03/SmartList/etc procmail-3.03/SmartList/etc/rc.init
  84. #   procmail-3.03/SmartList/examples procmail-3.03/src
  85. #   procmail-3.03/src/hsort.h procmail-3.03/src/multigram.c
  86. # Wrapped by kent@sparky on Tue Jul  5 21:10:29 1994
  87. PATH=/bin:/usr/bin:/usr/ucb:/usr/local/bin:/usr/lbin:$PATH ; export PATH
  88. echo If this archive is complete, you will see the following message:
  89. echo '          "shar: End of archive 1 (of 5)."'
  90. if test ! -d 'procmail-3.03' ; then
  91.     echo shar: Creating directory \"'procmail-3.03'\"
  92.     mkdir 'procmail-3.03'
  93. fi
  94. if test ! -d 'procmail-3.03/SmartList' ; then
  95.     echo shar: Creating directory \"'procmail-3.03/SmartList'\"
  96.     mkdir 'procmail-3.03/SmartList'
  97. fi
  98. if test -f 'procmail-3.03/SmartList/INSTALL' -a "${1}" != "-c" ; then 
  99.   echo shar: Will not clobber existing file \"'procmail-3.03/SmartList/INSTALL'\"
  100. else
  101.   echo shar: Extracting \"'procmail-3.03/SmartList/INSTALL'\" \(10257 characters\)
  102.   sed "s/^X//" >'procmail-3.03/SmartList/INSTALL' <<'END_OF_FILE'
  103. X    SmartList Installation guide:
  104. X
  105. X        1. Prerequisites
  106. X        2. Installing the scripts
  107. X        3. Sharing the scripts on multiple architectures
  108. X        4. If you don't have a sendmail compatible mailer
  109. X        5. Upgrading from previous versions of SmartList
  110. X        6. Moving an existing list to a different directory
  111. X
  112. X                ---
  113. X
  114. X1. Prerequisites
  115. X   -------------
  116. X
  117. XIn order to make sure that SmartList works reliably for both remote and
  118. Xlocal mail, it has to be run under someone's account.
  119. XThe recommended procedure would be to create a new account and group named
  120. X"list" that is only going to be used to manage SmartList mailinglists.
  121. XYou are of course free to choose any name you'd like instead of "list".
  122. XCommon names would be "list" or "lists".  Do not pick "smartlist" because that
  123. Xname is nine characters long.  The maximum for most systems is eight.
  124. X
  125. XIf you are unable/unwilling to create a new user just for SmartList,
  126. Xthen it should be installed in a separate subdirectory.     The owner of this
  127. Xdirectory will still be able to receive regular mail.
  128. X
  129. XSmartList makes use of the -f option of sendmail.  This option makes sure
  130. Xthat the sender address on the envelope (the From_ line) contains the
  131. Xproper list address.  In order for this option to work, the user using it
  132. X(either "list" or the regular user the lists run under) must be on the
  133. Xtrusted-user-list of sendmail.    To enter the user "list" on the
  134. Xtrusted-user-list simply edit the sendmail.cf file and add a line reading:
  135. X
  136. XTlist
  137. X
  138. XRestarting sendmail is not necessary.
  139. X
  140. XSendmail 6.x and later don't need this anymore.     And even if you have an
  141. Xolder sendmail but can't put this in the sendmail.cf file, the mailinglist
  142. Xscripts will still work, albeit under less than ideal circumstances (i.e.
  143. Xautomatic bounce handling is severely impaired).
  144. X
  145. X                ---
  146. X
  147. X2. Installing the scripts
  148. X   ----------------------
  149. X
  150. XSuppose you have created this pseudo user "list" with a specified home
  151. Xdirectory of /home/list
  152. X
  153. XNow create /home/list by hand and make sure that you chown it to "list".
  154. XAlso chgrp it to "list" now if you created a group for that.  If you
  155. Xdo not have a separate group for the list, you have to make sure that you
  156. Xchange the setting of UMASK in the rc.init file.  By default the group
  157. Xto which the "list" user belongs can write in many of the files.
  158. X
  159. XNext, make sure that you are running as root (su root) and execute the
  160. X"install.sh" script present in this directory by typing something like:
  161. X
  162. X    sh install.sh /home/list
  163. X
  164. XIf you are setting up SmartList under your own account, simply creating
  165. Xa separate directory and running the install.sh script should suffice.
  166. X
  167. XThis script will then create two subdirectories (.bin and .etc) in
  168. X/home/list.  These directories will be filled with the files contained
  169. Xin the bin and etc subdirectories here.     It will also make sure that
  170. Xthe "multigram" program is compiled from the procmail*/src directory and
  171. Xcopied into /home/list/.bin.
  172. X
  173. X    The only program/binary that contains harcoded information is
  174. X    the /home/.bin/flist program.  It contains both the name of the user
  175. X    that owns the lists ("list" in this case) and the absolute path to
  176. X    the mailinglist directory (/home/list in this case).  This is
  177. X    needed for security reasons.
  178. X
  179. X    This means that if either the name of the user or the name
  180. X    of the directory changes, you'll have to reinstall the mailinglist
  181. X    scripts (or at least the flist binary).     You can treat it as if it
  182. X    were an upgrade, see paragraph 5 below for more information.
  183. X
  184. X    This also means that if you like to have several main mailinglist
  185. X    directories, you'll need a separately installed flist binary
  186. X    for each one of them.  Simply install.sh to every directory anew.
  187. X    To conserve space, or to centralise administration, you can then
  188. X    (hard)link all files in all the .bin and .etc directories with
  189. X    their counterparts (except the flist binaries and anything linked
  190. X    to them of course).
  191. X
  192. XFurthermore install.sh will link /home/list/.etc/rc.main to
  193. X/home/list/.procmailrc.     This is of course superfluous (but nondestructive) if
  194. Xyou are still using this account to receive regular mail.
  195. X
  196. XDepending on your mail configuration, if procmail has not been integrated
  197. Xin the maildelivery system (see procmail*/examples/advanced for more
  198. Xinformation on that topic) you should also create a .forward file with an
  199. Xappropriate content (see "man procmail", the NOTES section at the end).     This,
  200. Xhowever, is only necessary if you created a seperate "list" account.
  201. X
  202. X    Next, edit the /home/list/.etc/rc.init file.    Make sure that
  203. X    "domain" is set to the right value, and you can optionally specify a
  204. X    "listmaster";  also check if the PATH is correct and make sure
  205. X    that procmail and formail are in the path.
  206. X
  207. XOn some sites the aliases file is not exactly /usr/lib/aliases, or its format
  208. Xis slightly incompatible with standard sendmail.  In those cases (for
  209. Xyour own convenience) it would be wise to edit the .bin/createlist script
  210. Xto display the proper aliases to insert (so that you can cut and paste
  211. Xin the future).
  212. X
  213. XFor further instructions, you should consult the "Manual" file, it has
  214. Xbeen copied to the /home/list/.etc directory as well, and can serve as a
  215. Xquick reference.
  216. X
  217. X                ---
  218. X
  219. X3. Sharing the scripts on multiple architectures
  220. X   ---------------------------------------------
  221. X
  222. XFor people that want to run SmartList on a shared filesystem across different
  223. Xarchitectures there exists the possibility of using several .bin directories.
  224. XSimply use something like:
  225. X    sh install.sh /home/list .bin.sun4
  226. Xto install to a different .bin directory.  Repeat the complete procmail and
  227. XSmartList installation for every architecture, specifying a different .bin
  228. Xdirectory every time.
  229. X
  230. X                ---
  231. X
  232. X4. If you don't have a sendmail compatible mailer
  233. X   ----------------------------------------------
  234. X
  235. XAs is, SmartList is preconfigured to use sendmail (or a compatible
  236. Xmailer like smail) to actually transport the mails.  If your system only
  237. Xsupplies a /bin/rmail or /bin/mail to transport mails, then several of
  238. Xthe options used when invoking sendmail will not work.
  239. X
  240. XFor these cases you have to edit the /home/list/.etc/rc.init file.
  241. XLook for a SENDMAIL assignment.     Supplied with these scripts is a poor man's
  242. Xsendmail.  It was installed in the .bin directory belonging to the
  243. Xlist.  Make sure that the SENDMAIL assignment points at the "sendmails" script.
  244. XAnd, of course, don't forget to uncomment the SENDMAIL assignment and
  245. Xthe two sendmailOPT* assigments following it.
  246. X
  247. X                ---
  248. X
  249. X5. Upgrading from previous versions of SmartList
  250. X   ---------------------------------------------
  251. X
  252. XYou can simply use the install.sh script again to install directly over
  253. Xthe old installation IF you customised ONLY the master rc.init and rc.custom
  254. Xfiles.    Once you ran the install.sh script, you'll have to merge the changes
  255. Xyou made to the old rc.init file (still there) into the rc.init.new file.
  256. X
  257. XSince the install.sh script will create the .etc/rc.lock file for you,
  258. Xyou have 17 minutes to do that.     If it should take longer, touch the rc.lock
  259. Xfile every so often.  As soon as the difference between its mtime and the
  260. Xcurrent time exceeds 17 minutes, the flist programs start delivering mail to
  261. Xthe lists again.
  262. X
  263. XThen cat (yes, use `cat', because you have to preserve any hardlinks) the
  264. Xrc.init.new file into the rc.init file.
  265. X
  266. XIf there are any new entries in the new rc.custom file template, their
  267. Xomission in the old rc.custom files should not cause any problems (i.e. the
  268. Xnew rc.init already provides the new defaults).
  269. X
  270. XIf you customised more than just the rc.init or rc.custom files, you'll
  271. Xhave to make diffs between the old versions of the scripts, then install
  272. Xthe new scripts and then apply back the diffs (probably by hand).
  273. X
  274. XYou can quickly verify which files are linked to other files by
  275. Xsimply typing something like:
  276. X
  277. X    showlink rc.init
  278. X
  279. XAfter you are finished, remove the rc.lock file again.    If it is older
  280. Xthan 17 minutes it is ignored anyway, but it's cleaner this way.
  281. X
  282. X                ---
  283. X
  284. X6. Moving an existing list to a different directory
  285. X   ------------------------------------------------
  286. X
  287. XThis can be safely done by following these steps:
  288. Xa.Create the .etc/rc.lock file.
  289. Xb.Copy the tree to the new location.  Be sure to preserve hard and
  290. X  softlinks while doing this, tar can do this for you.    If you'd like to
  291. X  use tar to copy the tree, a suggested /bin/sh command line would be:
  292. X
  293. X    cd olddir; tar cf - . | (cd newdir; tar xvf -)
  294. X
  295. X  If you are moving the tree instead, make sure you create a symbolic link
  296. X  from the old location to the new immediately after moving the tree (there
  297. X  is a race condition here, mail arriving after having moved, but before having
  298. X  created the symbolic link will bounce).
  299. Xc.Create a symbolic link to the new location with (if your filesystem does not
  300. X  support symbolic links, see below):
  301. X
  302. X    ln -s newdir olddir/.etc/Moved
  303. X
  304. Xd.If the home directory of the list user needs to be changed, do it now.
  305. X  Make sure the change is in effect before proceeding with step e.
  306. X  You can buy some time by doing a "touch olddir/.etc/rc.lock" every 16
  307. X  minutes.
  308. Xe.Install SmartList from the distribution again, on top of newdir, i.e. simply
  309. X  regard it as an upgrade (you can skip this part if "flist -v" indicates that
  310. X  it is using the same path relative to the (changed) home directory of the
  311. X  list user).
  312. Xf.Update your /usr/lib/aliases (or equivalent) file, make sure the aliases run
  313. X  the new flist binary.
  314. Xg.Remove both the olddir/.etc/rc.lock and any newdir/.etc/rc.lock file.
  315. Xh.Wait at least 30 seconds before removing the olddir tree (if at all), in
  316. X  order to give the already running flists a chance to wake up and follow the
  317. X  symbolic link (if necessary, check that no old flists are running anymore).
  318. X
  319. XThe alternative way to move the lists (mandatory for filesystems that do not
  320. Xsupport symbolic links):
  321. Xa.Remove all */rc.lock files, kill the sendmail daemon (in other words: do
  322. X  whatever is necessary to prevent further invocations of the old flist binary)
  323. X  and check if any flist or procmail programs belonging to the lists are still
  324. X  running, if they do, wait until they are gone.
  325. Xb.As above.
  326. Xc.This step is irrelevant now.
  327. Xd.As above.
  328. Xe.As above.
  329. Xf.As above.
  330. Xg.This step is irrelevant now.
  331. Xh.You can remove the olddir tree without further ado (if you like).
  332. END_OF_FILE
  333.   if test 10257 -ne `wc -c <'procmail-3.03/SmartList/INSTALL'`; then
  334.     echo shar: \"'procmail-3.03/SmartList/INSTALL'\" unpacked with wrong size!
  335.   fi
  336.   # end of 'procmail-3.03/SmartList/INSTALL'
  337. fi
  338. if test ! -d 'procmail-3.03/SmartList/bin' ; then
  339.     echo shar: Creating directory \"'procmail-3.03/SmartList/bin'\"
  340.     mkdir 'procmail-3.03/SmartList/bin'
  341. fi
  342. if test ! -d 'procmail-3.03/SmartList/etc' ; then
  343.     echo shar: Creating directory \"'procmail-3.03/SmartList/etc'\"
  344.     mkdir 'procmail-3.03/SmartList/etc'
  345. fi
  346. if test -f 'procmail-3.03/SmartList/etc/rc.init' -a "${1}" != "-c" ; then 
  347.   echo shar: Will not clobber existing file \"'procmail-3.03/SmartList/etc/rc.init'\"
  348. else
  349.   echo shar: Extracting \"'procmail-3.03/SmartList/etc/rc.init'\" \(9559 characters\)
  350.   sed "s/^X//" >'procmail-3.03/SmartList/etc/rc.init' <<'END_OF_FILE'
  351. X#
  352. X# IMPORTANT variables to check/change:    PATH domain listmaster
  353. X#
  354. X
  355. X# BEFORE editing this file or any rc.* file in the .etc directory
  356. X# you should create the .etc/rc.lock file.  As long as this file
  357. X# exists, mail delivery to any list will stall (flist checks this file).
  358. X#
  359. X# Delivery can be temporarily stalled on a per list basis by creating
  360. X# the file rc.lock in the respective list's subdirectory.
  361. X
  362. X# ENVIRONMENT:
  363. X# list        contains the submitting address of the list (sans domain)
  364. X# domain    should contain the domain for the list, so that $list@$domain
  365. X#        can be used to submit to the list
  366. X# maintainer    should contain the fully qualified address of the maintainer
  367. X#        of $list;  maintainter can be left empty
  368. X# listmaster    should contain the fully qualified address of the supervising
  369. X#        list maintainer;  listmaster can be left empty
  370. X# X_ENVELOPE_TO contains the address that the incoming mail was addressed to
  371. X
  372. X# FILES:
  373. X# log        optional logfile (uncomment the LOGFILE assignment to enable
  374. X#        it)
  375. X# dist        the subscriber list, one address per line
  376. X# accept    the list of people allowed to submit messages (usually a
  377. X#        link to dist, only checked if $foreign_submit != yes)
  378. X# reject    list of people you definitely do not want to subscribe
  379. X# subscreen    program (e.g. shell script) to screen prospective subscribers
  380. X#        (see in the examples directory for a sample script)
  381. X# request    all the messages to request that could not be handled
  382. X#        automatically (only if maintainer is empty)
  383. X# help.txt    file sent to help & info requests
  384. X# info.txt    optional file sent to help & info requests as well,
  385. X#        it should contain a concise description of what the
  386. X#        list is all about
  387. X#        the various info.txt files are optionally gathered in
  388. X#        order to advertise the availability of the mailinglists
  389. X# subscribe.txt file sent to new subscribers
  390. X# subscribe.file file containing an arbitrary number of archive server commands
  391. X#        to be executed on behalf of new subscribers
  392. X# unsubscribe.txt file sent to unsubscribers
  393. X# accept.txt    file sent to people not on the accept list
  394. X# archive.txt    file sent to people requesting help from the archive server
  395. X# archive    (link to a) directory below which files can be accessed through
  396. X#        the archive server
  397. X# bounces    directory containing the bounce history files
  398. X# rc.submit    rcfile used when distributing submissions
  399. X# rc.request    rcfile used when processing requests
  400. X# rc.custom    rcfile that contains all the customisations per list
  401. X# rc.local.*    optional rcfiles for any local extensions (to be used in
  402. X#        conjunction with the RC_LOCAL_* variables which can be set
  403. X#        in rc.init or rc.custom files)
  404. X
  405. X#$Id: rc.init,v 1.37 1994/06/30 16:30:32 berg Exp $
  406. X
  407. XDELIVERED=yes    # we're in control of the mail now, tell sendmail to go away
  408. X
  409. XPATH=.:$PATH:../.bin:/bin:/usr/bin:/usr/local/bin    # setup a decent path
  410. XSHELL=/bin/sh                # to prevent surprises
  411. X
  412. Xdefaults=.etc
  413. X
  414. XLOCKTIMEOUT=3660            # set these values this high (1 hour)
  415. XTIMEOUT=3600                # to give sendmail the time to
  416. X                    # resolve big aliases
  417. X
  418. X###############################################################################
  419. X# You have to setup the following two assignments, make sure that the address
  420. X# mentioned in listmaster is *not* the address this script runs under;    if
  421. X# you would end up doing that, then assign listmaster to ""
  422. X# The same applies to the maintainer addresses used for every list, if you
  423. X# would like to alias them back to this script or to the respective -request
  424. X# addresses, then assign maintainer to "" instead.
  425. X#
  426. X# The mail-domain you have to insert below, must be the fully qualified
  427. X# mail-domain for this list;  e.g. if the preferred mail address for this
  428. X# account would be:  list@somemachine.somewhere.universe
  429. X# Then you should assign the following:
  430. X#           domain=somemachine.somewhere.universe
  431. X###############################################################################
  432. X
  433. Xdomain=INSERT.YOUR.MAIL.DOMAIN.HERE    # the common domain for all the lists
  434. Xlistmaster=
  435. X
  436. X#UMASK=077                # group can not touch the files
  437. X#UMASK=027                # group can read
  438. X UMASK=007                # group can write as well
  439. X
  440. Xdefaults=.etc                # the name of the defaults directory
  441. XLOGFILE=log
  442. XLOGABSTRACT=no
  443. X
  444. X#LOGABSTRACT=yes            # uncomment in case of emergency
  445. X#VERBOSE=yes                # uncomment in case of real emergency
  446. X#LOGFILE=../$defaults/log        # uncomment if you want centralised
  447. X                    # logging
  448. X
  449. XRC_INIT                    # clear this one from the environment
  450. X                    # so that we include this file only
  451. X                    # once
  452. X
  453. Xlistaddr=$list@$domain
  454. Xlistreq=$list-request@$domain
  455. Xlistdist=$list-dist@$domain
  456. X
  457. X                # the following constants determine the
  458. X                # behaviour of choplist, the dist-file
  459. X                # expander (zero means: no limit) which
  460. X                # calls sendmail
  461. Xminnames    =    32    # minimum number of names per call
  462. Xmindiffnames    =    8    # minimum for maxnames-minnames
  463. Xmaxnames    =    64    # maximum number of names per call
  464. Xmaxsplits    =    0    # maximum number of parts to split dist in
  465. Xmaxsize        =    200000    # maximal disk space to be taken up per mail
  466. Xmaxconcur    =    4    # maximum number of concurrent sendmail calls
  467. X
  468. Xalt_sendmail="\
  469. Xchoplist $minnames $mindiffnames $maxnames $maxsplits $maxsize $maxconcur dist"
  470. Xminnames mindiffnames maxnames maxsplits maxsize maxconcur
  471. X#alt_sendmail                # uncomment if you'd prefer sendmail
  472. X                    # to handle the $listdist expansion
  473. X
  474. XsendmailOPT="-oem -odb -oi -om -f$listreq"
  475. XsendmailOPTp=-oep
  476. XsendmailOPTq=-odq
  477. XsendmailQflush="$SENDMAIL -q"
  478. X#sendmailQflush                # uncomment if you don't want the
  479. X                    # queue to be flushed after all archive
  480. X                    # retrieval files have been queued
  481. X########################
  482. X# sendmail options used:
  483. X#
  484. X# -t    scan the header of the mail for recipients
  485. X# -f    specify the sender envelope address (requires T entry in sendmail.cf)
  486. X# -oi    do not regard a single dot on an otherwise empty line as EOF
  487. X# -om    include myself in any alias expansions
  488. X# -odb    background deliverymode command returns immediately
  489. X# -odq    queued deliverymode (put messages in the queue only)
  490. X# -q    flush the queue
  491. X# -oem    mail back error messages
  492. X# -oep    print error messages
  493. X# -onF    do not check aliases while expanding them (use it, if available)
  494. X########################
  495. X#
  496. X# If you only have /bin/mail and not some sendmail compatible mailer that
  497. X# understands the -t option of sendmail, then you have to uncomment the
  498. X# following two lines:
  499. X#
  500. X#sendmailOPT sendmailOPTp sendmailOPTq sendmailQflush
  501. X#SENDMAIL=../SedBinDir/sendmails
  502. X#
  503. X########################
  504. X
  505. Xsize_limit    =    524288        # sanity cutoff value for submissions
  506. X
  507. Xidcache_size    =    8192        # duplicate-msgid cache size in bytes
  508. X
  509. Xarchive_hist    =    2        # number of messages left archived
  510. Xarchive_log    =    $LOGFILE    # log file for archive retrievals
  511. Xsubscribe_log    =    $LOGFILE    # log file for administrivia
  512. X
  513. Xmaxhist        =    16        # bounce history limit
  514. Xminbounce    =    4        # no. of bounces before removal
  515. Xcutoff_bounce    =    256        # lines to keep in bounce processing
  516. X
  517. Xmatch_threshold =    30730        # for close matches to the list
  518. Xmedium_threshold=    28672        # for not so close matches to the list
  519. Xloose_threshold =    24476        # for loosely finding your name
  520. X
  521. Xauto_off_threshold=   $medium_threshold # for auto-unsubscribing bouncers
  522. Xoff_threshold    =      $loose_threshold # for unsubscribing
  523. Xreject_threshold=      $match_threshold # for rejecting subscriptions
  524. Xsubmit_threshold=     $medium_threshold # for permitting submissions
  525. X
  526. Xunsub_assist
  527. X#unsub_assist    =    8        # uncomment (and change perhaps) this
  528. X                    # line to enable unsubscription
  529. X                    # assistance; it specifies the no. of
  530. X                    # multigram matches an unsuccessful
  531. X                    # unsubscriber will receive back
  532. Xforeign_submit    =    yes
  533. X#foreign_submit                # uncomment this line if you
  534. X                    # want to restrict submitting to
  535. X                    # people on the accept list
  536. Xforce_subscribe
  537. X#force_subscribe=    yes        # uncomment to cause people to
  538. X                    # be autosubscribed upon first
  539. X                    # submission to the list
  540. Xauto_subscribe    =    yes
  541. X#auto_subscribe                # uncomment to disable unattended
  542. X                    # subscription handling
  543. Xauto_help
  544. X#auto_help    =    yes        # uncomment to enable default help
  545. X                    # responses to all undecipherable
  546. X                    # requests
  547. Xmoderated_flag
  548. X#moderated_flag =    yes        # uncomment this to make the list
  549. X                    # moderated (you must create a
  550. X                    # `moderators' file for this to work)
  551. Xcc_requests
  552. X#cc_requests    =    yes        # uncomment if you want subscribe
  553. X                    # and help requests to be Cc'd to
  554. X                    # the maintainer
  555. Xcc_unsubrequests
  556. X#cc_unsubrequests=    yes        # uncomment if you want unsubscribe
  557. X                    # requests to be Cc'd to the maintainer
  558. Xreply_to
  559. X#reply_to    = "Resent-Reply-To: $listaddr" # uncomment (and perhaps change
  560. X                    # it to "Reply-To") to force replies
  561. X                    # to go to the list (discouraged)
  562. Xdigest_flag
  563. X#digest_flag    =    yes        # uncomment this if you want digests
  564. Xdigest_age    =    262144        # maximum age of a digest in seconds
  565. Xdigest_size    =    32768        # maximum size of a digest in bytes
  566. X
  567. X
  568. XX_COMMAND    =    X-Command
  569. XX_COMMAND_PASSWORD =    password    # put the global password for
  570. X                    # X-Command mails here
  571. X                # this password can of course be changed/
  572. X                # customised in the per list rc.custom file
  573. X
  574. X#daemon_bias='100^0 ^From:.*daemon@ok'    # You could set "daemon_bias" to
  575. X                    # positively discriminate some
  576. X # mail address not to be from a daemon.  Either with a regexp as demonstrated
  577. X # or with more complicated recipes that simply set it to '100^0' or nothing.
  578. X
  579. X#RC_LOCAL_SUBMIT_00    = rc.local.s00    # Uncomment (and change) these to
  580. X#RC_LOCAL_SUBMIT_10    = rc.local.s10    # call up customised local scripts
  581. X#RC_LOCAL_SUBMIT_20    = rc.local.s20    # at predefined points.
  582. X#RC_LOCAL_REQUEST_00    = rc.local.r00
  583. X#RC_LOCAL_REQUEST_10    = rc.local.r10
  584. X#RC_LOCAL_REQUEST_20    = rc.local.r20
  585. X#RC_LOCAL_REQUEST_30    = rc.local.r30
  586. X
  587. XRC_CUSTOM=rc.custom
  588. END_OF_FILE
  589.   if test 9559 -ne `wc -c <'procmail-3.03/SmartList/etc/rc.init'`; then
  590.     echo shar: \"'procmail-3.03/SmartList/etc/rc.init'\" unpacked with wrong size!
  591.   fi
  592.   # end of 'procmail-3.03/SmartList/etc/rc.init'
  593. fi
  594. if test ! -d 'procmail-3.03/SmartList/examples' ; then
  595.     echo shar: Creating directory \"'procmail-3.03/SmartList/examples'\"
  596.     mkdir 'procmail-3.03/SmartList/examples'
  597. fi
  598. if test ! -d 'procmail-3.03/src' ; then
  599.     echo shar: Creating directory \"'procmail-3.03/src'\"
  600.     mkdir 'procmail-3.03/src'
  601. fi
  602. if test -f 'procmail-3.03/src/hsort.h' -a "${1}" != "-c" ; then 
  603.   echo shar: Will not clobber existing file \"'procmail-3.03/src/hsort.h'\"
  604. else
  605.   echo shar: Extracting \"'procmail-3.03/src/hsort.h'\" \(146 characters\)
  606.   sed "s/^X//" >'procmail-3.03/src/hsort.h' <<'END_OF_FILE'
  607. X/*$Id: hsort.h,v 1.1 1994/04/05 15:34:44 berg Exp $*/
  608. X
  609. Xvoid hsort Q((void*base,size_t nelem,size_t width,
  610. X int(*fcmp)(const void*,const void*)));
  611. END_OF_FILE
  612.   if test 146 -ne `wc -c <'procmail-3.03/src/hsort.h'`; then
  613.     echo shar: \"'procmail-3.03/src/hsort.h'\" unpacked with wrong size!
  614.   fi
  615.   # end of 'procmail-3.03/src/hsort.h'
  616. fi
  617. if test -f 'procmail-3.03/src/multigram.c' -a "${1}" != "-c" ; then 
  618.   echo shar: Will not clobber existing file \"'procmail-3.03/src/multigram.c'\"
  619. else
  620.   echo shar: Extracting \"'procmail-3.03/src/multigram.c'\" \(27219 characters\)
  621.   sed "s/^X//" >'procmail-3.03/src/multigram.c' <<'END_OF_FILE'
  622. X/************************************************************************
  623. X *    multigram - The human mail address reader            *
  624. X *                                    *
  625. X *    It uses multigrams to intelligently filter out mail addresses    *
  626. X *    from the garbage in any arbitrary mail.                *
  627. X *    Multigram is currently unable to pick out addresses that    *
  628. X *    contain embedded whitespace.                    *
  629. X *    This program also contains some swiss-army-knife mailinglist    *
  630. X *    support features.                        *
  631. X *                                    *
  632. X *    Most notably:    flist    A program that should be setuid root.    *
  633. X *                                    *
  634. X *    Seems to be relatively bug free.                *
  635. X *                                    *
  636. X *    Copyright (c) 1992-1994, S.R. van den Berg, The Netherlands    *
  637. X *    #include "../README"                        *
  638. X ************************************************************************/
  639. X#ifdef RCS
  640. Xstatic /*const*/char rcsid[]=
  641. X "$Id: multigram.c,v 1.57 1994/06/28 16:56:34 berg Exp $";
  642. X#endif
  643. Xstatic /*const*/char rcsdate[]="$Date: 1994/06/28 16:56:34 $";
  644. X#include "includes.h"
  645. X#include "sublib.h"
  646. X#include "hsort.h"
  647. X#include "shell.h"
  648. X#include "ecommon.h"
  649. X#include "mcommon.h"
  650. X
  651. X#include "targetdir.h"        /* see ../SmartList/install.sh2 for more details */
  652. X
  653. X#define BUFSTEP        16
  654. X#define ADDR_INCR    128
  655. X#define COPYBUF        16384
  656. X/*#define SPEEDBUF    COPYBUF           /* uncomment to get a speed increase? */
  657. X#define SCALE_WEIGHT    0x7fff
  658. X#define EXCL_THRESHOLD    30730
  659. X
  660. X#define DEFmaxgram    4
  661. X#define DEFminweight    (SCALE_WEIGHT/4)          /* sanity cutoff value */
  662. X#define DEFbest_matches 2
  663. X
  664. X#define TMPMAILFILE    "/tmp/choplist.%ld"
  665. X
  666. X#define DEFAULTS_DIR    ".etc"              /* some configurable paths */
  667. X#define GLOCKFILE    "../.etc/rc.lock"
  668. X#define RCMAIN        "./../.etc/rc.main"
  669. X#define LLOCKFILE    "rc.lock"
  670. X#define LINKMOVED    "../.etc/Moved"
  671. X#define REQUEST        "-request"
  672. X#define RCSUBMIT    "./rc.submit"
  673. X#define RCREQUEST    "./rc.request"
  674. X#define RCPOST        "./../.etc/rc.post"
  675. X#define RCINIT        "RC_INIT=rc.init"
  676. X#define XENVELOPETO    "X_ENVELOPE_TO="
  677. X#define LIST        "list="
  678. X
  679. X#define metoo_SENDMAIL        "-om"
  680. X#define nometoo_SENDMAIL    "-omF"
  681. X#define REMOV1_DELIM "(Only"
  682. X#define REMOV2_DELIM "addresses below this line can be automatically removed)"
  683. X#define NOT_METOO    "(-n)"
  684. X
  685. Xstruct string{char*text,*itext;size_t textlen,buflen;};
  686. X
  687. Xstatic remov_delim,maxgram;
  688. X
  689. Xint strnIcmp(a,b,l)const char*a,*b;size_t l;                 /* stub */
  690. X{ return strncmp(a,b,l);
  691. X}
  692. X            /* read a string from a file into a struct string buffer */
  693. Xstatic size_t readstr(file,p,linewise)FILE*const file;struct string*p;
  694. X const int linewise;
  695. X{ size_t len;int i,firstspc;
  696. X  static const char rem1str[]=REMOV1_DELIM,rem2str[]=REMOV2_DELIM;
  697. X  for(len=firstspc=0;;)
  698. X   { switch(i=getc(file))
  699. X      { case ' ':case '\t':case '\n':
  700. X       if(!len)                  /* only skip leading space */
  701. X          continue;
  702. X       if(!linewise)              /* do we need a complete line? */
  703. X          break;                       /* no, a word will do */
  704. X       if(!firstspc)                 /* already seen spaces? */
  705. X        { if(i=='\n')                 /* no, so check for EOL */
  706. X           { p->text[len]='\0';      /* terminate the first word, split */
  707. X         if(++len==p->buflen)         /* still buffer space left? */
  708. X            p->text=realloc(p->text,p->buflen+=BUFSTEP);
  709. X         break;
  710. X           }
  711. X          i='\0';firstspc=1;
  712. X        }             /* not the first word on the line, continue */
  713. X       if(i=='\n')
  714. X          break;
  715. X    default:p->text[len]=i;              /* regular character, store it */
  716. X       if(++len==p->buflen)               /* watch our buffer space */
  717. X          p->text=realloc(p->text,p->buflen+=BUFSTEP);
  718. X       continue;                       /* next character */
  719. X    case EOF:;
  720. X      }
  721. X     p->text[len]='\0';             /* terminate the buffer in any case */
  722. X     if(linewise&&len)
  723. X      { int i=0;
  724. X    for(i=0;!remov_delim&&!i;i=1)
  725. X       if(!strcmp(p->text+i,rem1str)&&
  726. X        !strcmp(p->text+sizeof rem1str+i,rem2str)) /* special delimiter? */
  727. X          remov_delim=1;
  728. X      }
  729. X     return len;
  730. X   }
  731. X}
  732. X
  733. Xstatic char*tstrdup(p)const char*const p;
  734. X{ return strcpy(malloc(strlen(p)+1),p);
  735. X}
  736. X
  737. Xstatic const char*mailfile;
  738. Xstatic int retval=EX_UNAVAILABLE;
  739. X
  740. Xstatic void sterminate P((void))
  741. X{ unlink(mailfile);exit(retval);
  742. X}
  743. X
  744. Xstatic int strIcmp(s1,s2)const char*const s1,*const s2;
  745. X{ register unsigned i,j;register const char*a,*b;
  746. X  a=s1;b=s2;
  747. X  do
  748. X   { while(*a&&*a==*b)
  749. X    a++,b++;
  750. X     if((i= *a++)-'A'<='Z'-'A')
  751. X    i+='a'-'A';
  752. X     if((j= *b++)-'A'<='Z'-'A')
  753. X    j+='a'-'A';
  754. X     if(j!=i)
  755. X    return i>j?a-s1:s1-a;
  756. X   }
  757. X  while(i&&j);
  758. X  return 0;
  759. X}
  760. X
  761. Xstatic int pstrIcmp(s1,s2)const void*const s1,*const s2;
  762. X{ return strIcmp(*(const char*const*)s1,*(const char*const*)s2);
  763. X}
  764. X
  765. Xstatic void revstr(p)register char*p;               /* reverse the string */
  766. X{ register char*q;
  767. X  for(q=strchr(p,'\0')-1;p<q;p++,q--)
  768. X   { unsigned i;
  769. X     i= *p;*p= *q;*q=i;
  770. X   }
  771. X}
  772. X
  773. Xstatic void makelow(str)register char*str;
  774. X{ for(;*str;str++)
  775. X     if((unsigned)*str-'A'<'Z'-'A')
  776. X    *str+='a'-'A';
  777. X}
  778. X
  779. Xstatic void lowcase(str)struct string*const str;       /* make lowercase */
  780. X{ makelow(str->itext=tstrdup(str->text));
  781. X}
  782. X
  783. Xstatic void elog(a)const char*const a;
  784. X{ fputs(a,stderr);
  785. X}
  786. X                            /* the program names */
  787. Xstatic const char idhash[]="idhash",flist[]="flist",senddigest[]="senddigest",
  788. X choplist[]="choplist",dirsep[]=DIRSEP;
  789. Xstatic const char*progname="multigram";
  790. X
  791. X#define ISPROGRAM(curname,refname)    \
  792. X (!strncmp(curname,refname,STRLEN(refname)))
  793. X
  794. Xvoid nlog(a)const char*const a;            /* log error with identification */
  795. X{ fprintf(stderr,"%s: %s",progname,a);
  796. X}
  797. X
  798. Xvoid logqnl(a)const char*const a;
  799. X{ fprintf(stderr," \"%s\"\n",a);
  800. X}
  801. X                         /* finds the next character */
  802. Xstatic char*lastdirsep(filename)const char*filename;
  803. X{ const char*p;                    /* following the last DIRSEP */
  804. X  while(p=strpbrk(filename,dirsep))
  805. X     filename=p+1;
  806. X  return (char*)filename;
  807. X}
  808. X                           /* check rc.lock file age */
  809. Xstatic int rclock(file,stbuf)const char*const file;struct stat*const stbuf;
  810. X{ int waited=0;
  811. X  while(!stat(file,stbuf)&&time((time_t*)0)-stbuf->st_mtime<DEFlocktimeout)
  812. X     waited=1,sleep(DEFlocksleep);             /* wait, if appropriate */
  813. X  return waited;
  814. X}
  815. X
  816. Xstatic char*argstr(first,last)const char*first,*last;        /* construct */
  817. X{ char*chp;size_t i;                   /* an argument assignment */
  818. X  strcpy(chp=malloc((i=strlen(first))+strlen(last)+1),first);
  819. X  strcpy(chp+i,last);
  820. X  return chp;
  821. X}
  822. X
  823. Xstatic void checkparens(pleft,pright,str,echp)int pleft,pright;
  824. X char*str,*const echp;
  825. X{ int parens;char*chp;
  826. X  for(chp=str,parens=0;chp=strchr(chp,pleft);chp++,parens++);
  827. X  for(chp=str;chp=strchr(chp,pright);chp++,parens--);
  828. X  if(*(chp=str)==pleft)                       /* any opening paren? */
  829. X   { if(!parens&&*echp==pright)            /* parens matched and enclosing? */
  830. X      { *echp='\0';
  831. X    goto shftleft;
  832. X      }
  833. X     if(parens>0)            /* more opening than closing parens? */
  834. Xshftleft:
  835. X    tmemmove(chp,chp+1,echp-chp+1);            /* delete left paren */
  836. X   }
  837. X  else if(parens<0&&*echp==pright)    /* more closing than opening parens? */
  838. X     *echp='\0';                       /* delete right paren */
  839. X}
  840. X
  841. Xstatic PROGID;
  842. X
  843. Xstatic int matchgram(fuzzstr,hardstr)
  844. Xconst struct string*const fuzzstr;struct string*const hardstr;
  845. X{ size_t minlen,maxlen;unsigned maxweight;int meter;
  846. X  register size_t gramsize;
  847. X  if((minlen=hardstr->textlen=strlen(hardstr->text))>(maxlen=fuzzstr->textlen))
  848. X     minlen=fuzzstr->textlen,maxlen=hardstr->textlen;
  849. X  if((gramsize=minlen-1)>maxgram)
  850. X     gramsize=maxgram;
  851. X  maxweight=SCALE_WEIGHT/(gramsize+1);
  852. X  meter=(int)((unsigned long)SCALE_WEIGHT/2*minlen/maxlen)-SCALE_WEIGHT/2;
  853. X  do                        /* reset local multigram counter */
  854. X   { register lmeter=0;size_t cmaxlen=maxlen;
  855. X     ;{ register const char*fzz,*hrd;
  856. X    fzz=fuzzstr->itext;
  857. X    do
  858. X     { for(hrd=fzz+1;hrd=strchr(hrd,*fzz);)         /* is it present in */
  859. X          if(!strncmp(++hrd,fzz+1,gramsize))          /* own string? */
  860. X           { if(cmaxlen>gramsize+1)
  861. X            cmaxlen--;
  862. X          goto dble_gram;             /* skip until it's last */
  863. X           }
  864. X       for(hrd=hardstr->itext;hrd=strchr(hrd,*fzz);)    /* otherwise */
  865. X           if(!strncmp(++hrd,fzz+1,gramsize))     /* search it in the */
  866. X        { lmeter++;                       /* dist entry */
  867. X          break;
  868. X        }
  869. Xdble_gram:;
  870. X     }
  871. X    while(*(++fzz+gramsize));                /* next gram */
  872. X      }
  873. X     if(lmeter)
  874. X      { unsigned weight;
  875. X    if(cmaxlen>minlen)
  876. X       cmaxlen=minlen;
  877. X    meter+=lmeter*(weight=maxweight/(unsigned)(cmaxlen-gramsize));
  878. X    meter-=weight*
  879. X     (unsigned long)((lmeter+=gramsize-cmaxlen)<0?-lmeter:lmeter)/cmaxlen;
  880. X      }
  881. X   }
  882. X  while(gramsize--);             /* search all gramsizes down to one */
  883. X  return meter;
  884. X}
  885. X
  886. Xmain(argc,argv)int argc;char*argv[];
  887. X{ struct string fuzzstr,hardstr,excstr,exc2str;FILE*hardfile;
  888. X  const char*addit=0;
  889. X  struct match{char*fuzz,*hard;int metric;long lentry;off_t offs1,offs2;}
  890. X   **best,*curmatch=0;
  891. X  unsigned best_matches,charoffs=0,remov=0,renam=0,incomplete=0,
  892. X   chkmetoo=(char*)progid-(char*)progid;
  893. X  int lastfrom,minweight;
  894. X  static const char cldntopen[]="Couldn't open";
  895. X  static const char usage[]=
  896. X"Usage: multigram [-cdimr] [-b nnn] [-l nnn] [-w nnn] [-ax address] filename\n"
  897. X   ;
  898. X  if(argc)                  /* sanity check, any arguments at all? */
  899. X   { char*chp;                         /* suid flist prog? */
  900. X     if(ISPROGRAM(chp=lastdirsep(argv[0]),flist))
  901. X      { struct stat stbuf;struct passwd*pass;char*arg;
  902. X    static const char request[]=REQUEST,listid[]=LISTID,
  903. X     rcrequest[]=RCREQUEST,rcpost[]=RCPOST,list[]=LIST,
  904. X     defdir[]=DEFAULTS_DIR,targetdir[]=TARGETDIR,
  905. X     *pmexec[]={PROCMAIL,RCSUBMIT,RCINIT,0,0,0,rcrequest,rcpost,0};
  906. X#define Endpmexec(i)    (pmexec[maxindex(pmexec)-(i)])
  907. X    progname=flist;*chp='\0';
  908. X    if(argc!=2)                   /* wrong number of arguments? */
  909. X     { elog("\
  910. XUsage: flist listname[-request]\n\
  911. X   Or: flist -v\n");
  912. X       return EX_USAGE;
  913. X     }
  914. X    if(!strcmp(arg=argv[1],"-v"))
  915. X     { fprintf(stderr,"%s\nUser: %s\nDirectory: %s\n",SLVERSION,listid,
  916. X        targetdir);
  917. X       return EX_OK;
  918. X     }
  919. X    ;{ uid_t euid;
  920. X       if((euid=geteuid())==ROOT_uid)
  921. X        { if(!(pass=getpwnam(listid)))
  922. X           { nlog("User \"");elog(listid);elog("\"");
  923. X         goto bailout;
  924. X           }
  925. X         /*
  926. X          * continue as the compile-time-determined list maintainer
  927. X          */
  928. X          setgid(pass->pw_gid);initgroups(listid,pass->pw_gid);
  929. X          setuid(pass->pw_uid);
  930. X        }
  931. X       else if(!(pass=getpwuid(euid)))
  932. X        { nlog("Euid");
  933. Xbailout:      elog(" unknown\n");
  934. X          return EX_NOUSER;
  935. X        }
  936. X       else
  937. X         /*
  938. X          * we weren't root, so try to get the uid and gid belonging to
  939. X          * the euid we started under
  940. X          */
  941. X        { int error;
  942. X          setrgid(pass->pw_gid);error=setgid(pass->pw_gid);setruid(euid);
  943. X          if(setuid(pass->pw_uid)&&error)
  944. X         nlog("Insufficient privileges\n");
  945. X        }
  946. X       endpwent();
  947. X       if(chdir(chp=pass->pw_dir))
  948. X          goto nochdir;
  949. X     }
  950. X    if(*(chp=(char*)targetdir)&&chdir(chp))
  951. Xnochdir: { nlog("Couldn't chdir to");logqnl(targetdir);
  952. X       return EX_NOPERM;
  953. X     }
  954. X    if(stat(defdir,&stbuf))
  955. X     { nlog("Can't find \"");elog(defdir);elog("\" in");logqnl(targetdir);
  956. X       return EX_NOINPUT;
  957. X     }
  958. X    if(pass->pw_uid!=stbuf.st_uid||pass->pw_gid!=stbuf.st_gid)
  959. X       nlog("Strange group or user id\n");           /* check for -request */
  960. X    if((chp=strchr(arg,'\0'))-arg>STRLEN(request)&&
  961. X       !strcmp(chp-=STRLEN(request),request))
  962. X       *chp='\0',pmexec[1]=rcrequest,Endpmexec(1)=0,Endpmexec(2)=rcpost;
  963. X    else
  964. X       chp=0;
  965. X    if(!strcmp(arg,chPARDIR)||strpbrk(arg,dirsep))
  966. X     { nlog("Bogus listname\n");
  967. X       return EX_NOPERM;
  968. X     }
  969. X    ;{ int foundlock;
  970. X       do
  971. X        { foundlock=0;
  972. X          if(chdir(arg))             /* goto the list's subdirectory */
  973. X         pmexec[1]=RCMAIN,Endpmexec(2)=0,chdir(defdir);
  974. X          while(rclock(GLOCKFILE,&stbuf)||rclock(LLOCKFILE,&stbuf))
  975. X         foundlock=1;                        /* stall */
  976. X        }
  977. X       while(foundlock&&!chdir(LINKMOVED));          /* did the lists move? */
  978. X     }
  979. X    Endpmexec(5)=INIT_PATH;
  980. X    Endpmexec(4)=argstr(list,arg);            /* pass on the list name */
  981. X    if(chp)                      /* was it a -request list? */
  982. X       *chp= *request;             /* then restore the leading '-' */
  983. X    Endpmexec(3)=argstr(XENVELOPETO,arg);
  984. X    execve(pmexec[0],(char*const*)pmexec,environ);nlog("Couldn't exec");
  985. X    logqnl(pmexec[0]);
  986. X    return EX_UNAVAILABLE;                        /* panic */
  987. X      }
  988. X    /*
  989. X     *    revoke any suid permissions now, since we're not flist
  990. X     */
  991. X     setgid(getgid());setuid(getuid());
  992. X     if(ISPROGRAM(chp,idhash))                  /* idhash program? */
  993. X      { unsigned long hash=0;int i;
  994. X    progname=idhash;
  995. X    if(argc!=1)
  996. X     { elog("Usage: idhash\n");
  997. X       return EX_USAGE;
  998. X     }
  999. X    while(i=fgetc(stdin),!feof(stdin))               /* hash away! */
  1000. X       hash=hash*67067L+i;
  1001. X    printf("%lx",hash);
  1002. X    return EX_OK;
  1003. X      }
  1004. X     if(ISPROGRAM(chp,senddigest))              /* senddigest program? */
  1005. X      { struct stat stbuf;
  1006. X    progname=senddigest;
  1007. X    if(argc<5)
  1008. X     { elog(
  1009. X     "Usage: senddigest maxage maxsize bodyfile trailerfile [file] ...\n");
  1010. X       return EX_USAGE;
  1011. X     }
  1012. X    if(!stat(argv[3],&stbuf))
  1013. X     { time_t newt;off_t size;
  1014. X       newt=stbuf.st_mtime;size=stbuf.st_size;
  1015. X       if(!stat(argv[argc=4],&stbuf))
  1016. X        { off_t maxsize;
  1017. X          if(stbuf.st_mtime+strtol(argv[1],(char**)0,10)<newt)
  1018. X         return EX_OK;                   /* digest too old */
  1019. X          maxsize=strtol(argv[2],(char**)0,10);
  1020. X          goto statd;
  1021. X          do
  1022. X           { if(!stat(argv[argc],&stbuf))
  1023. Xstatd:            if((size+=stbuf.st_size)>maxsize)      /* digest too big? */
  1024. X               return EX_OK;
  1025. X           }
  1026. X          while(argv[++argc]);
  1027. X        }
  1028. X     }
  1029. X    return 1;
  1030. X      }
  1031. X     hardstr.text=malloc(hardstr.buflen=BUFSTEP);
  1032. X     if(ISPROGRAM(chp,choplist))
  1033. X      { unsigned long minnames,mindiffnames,maxnames,maxsplits,maxsize,
  1034. X     maxconcur;
  1035. X    char*distfile,**nargv,**revarr;int mailfd;size_t revfilled;
  1036. X    static const char tmpmailfile[]=TMPMAILFILE;
  1037. X    char lmailfile[STRLEN(TMPMAILFILE)+8*sizeof(pid_t)*4/10+1+1];
  1038. X    progname=choplist;
  1039. X    if(argc<9)
  1040. X     { elog(
  1041. X"Usage: choplist minnames mindiffnames maxnames maxsplits maxsize maxconcur\n\
  1042. X\tdistfile sendmail [flags ...]\n");
  1043. X       return EX_USAGE;
  1044. X     }
  1045. X    minnames=strtol(argv[1],(char**)0,10);
  1046. X    mindiffnames=strtol(argv[2],(char**)0,10);
  1047. X    maxnames=strtol(argv[3],(char**)0,10);
  1048. X    maxsplits=strtol(argv[4],(char**)0,10);
  1049. X    maxsize=strtol(argv[5],(char**)0,10);
  1050. X    maxconcur=strtol(argv[6],(char**)0,10);distfile=argv[7];
  1051. X    nargv=argv+8;argc-=8;setbuf(stdin,(char*)0);setbuf(stdout,(char*)0);
  1052. X    sprintf((char*)(mailfile=lmailfile),tmpmailfile,(long)getpid());
  1053. X    qsignal(SIGTERM,sterminate);qsignal(SIGINT,sterminate);
  1054. X    qsignal(SIGHUP,sterminate);qsignal(SIGQUIT,sterminate);
  1055. X    unlink(mailfile);
  1056. X#ifndef O_CREAT
  1057. X    if(0>(mailfd=creat(mailfile,NORMperm)))
  1058. X#else
  1059. X    if(0>(mailfd=open(mailfile,O_RDWR|O_CREAT|O_EXCL,NORMperm)))
  1060. X#endif
  1061. X     { nlog("Can't create temporary file");logqnl(mailfile);
  1062. X       return EX_CANTCREAT;
  1063. X     }
  1064. X    ;{ char*buf;int i;unsigned long totsize=0;
  1065. X       buf=malloc(COPYBUF);
  1066. X       goto jin;
  1067. X       do
  1068. X        { char*a=buf;size_t len;
  1069. X          totsize+=(len=i);
  1070. X          do
  1071. X           { while(0>(i=write(mailfd,buf,(size_t)len))&&errno==EINTR);
  1072. X         if(i<0)
  1073. X          { nlog("Can't write temporary file");logqnl(mailfile);
  1074. X            retval=EX_IOERR;sterminate();
  1075. X          }
  1076. X         a+=i;
  1077. X           }
  1078. X          while(i>0&&(len-=i));
  1079. Xjin:          while(0>(i=read(STDIN,buf,(size_t)COPYBUF))&&errno==EINTR);
  1080. X        }
  1081. X       while(i>0);
  1082. X       if(!totsize)
  1083. X          nlog("Can't find the mail\n"),retval=EX_NOINPUT,sterminate();
  1084. X       free(buf);totsize=(maxsize+totsize-1)/totsize;
  1085. X       if(maxsize&&(!maxsplits||totsize<maxsplits))
  1086. X          maxsplits=totsize?totsize:1;
  1087. X     }
  1088. X    fclose(stdin);close(STDIN);
  1089. X    if(!(hardfile=fopen(chp=distfile,"r")))
  1090. X       nlog(cldntopen),logqnl(distfile),retval=EX_IOERR,sterminate();
  1091. X    ;{ size_t revlen;
  1092. X       revarr=malloc((revlen=ADDR_INCR)*sizeof*revarr);revfilled=0;
  1093. X       while(readstr(hardfile,&hardstr,1))
  1094. X        { int i=strchr((chp=strchr(hardstr.text,'\0'))+1,'\0')[-1];
  1095. X          if(*hardstr.text!='(')
  1096. X         switch(i)
  1097. X          { default:
  1098. X               goto invaddr;
  1099. X            case ')':case '\0':;
  1100. X          }
  1101. X          else                         /* comment line */
  1102. X         switch(i)
  1103. Xinvaddr:      { default:nlog("Skipping invalid address entry:");*chp=' ';
  1104. X               logqnl(hardstr.text);
  1105. X            case ')':case '\0':
  1106. X               continue;
  1107. X          }
  1108. X          if(revfilled==revlen)              /* watch our space */
  1109. X         revarr=realloc(revarr,(revlen+=ADDR_INCR)*sizeof*revarr);
  1110. X          revstr(hardstr.text);revarr[revfilled++]=tstrdup(hardstr.text);
  1111. X        }
  1112. X     }
  1113. X    free(hardstr.text);fclose(hardfile);
  1114. X    if(!revfilled)
  1115. X       retval=EX_OK,sterminate();        /* oops, no recipients, finished */
  1116. X    if(fork()>0)                        /* lose our tail */
  1117. X       return EX_OK;      /* causes procmail to release the lockfile */
  1118. X    revarr=realloc(revarr,revfilled*sizeof*revarr);        /* be modest */
  1119. X    hsort(revarr,revfilled,sizeof*revarr,pstrIcmp);          /* sort'em */
  1120. X    if(maxsplits)
  1121. X     { maxsplits=(revfilled+maxsplits-1)/maxsplits;
  1122. X       if(!minnames||minnames<maxsplits)
  1123. X        { minnames=maxsplits;
  1124. X          if(maxnames&&maxnames<minnames)
  1125. X         maxnames=minnames+mindiffnames;
  1126. X        }
  1127. X     }
  1128. X    if(!maxnames||(maxnames+argc>MAX_argc))
  1129. X       maxnames=MAX_argc-argc;
  1130. X    if(minnames>maxnames)
  1131. X       minnames=maxnames;
  1132. X    if(!minnames)
  1133. X       minnames=1;
  1134. X    ;{ int*rdist,*ip;char**nam;size_t n;
  1135. X       ip=rdist=malloc((n=revfilled)*sizeof*rdist);nam=revarr;
  1136. X       while(--n)
  1137. X        { int i,j;char*left,*right;
  1138. X          left= *nam;
  1139. X          if(!(i=strIcmp(right= *++nam,left)))
  1140. X         j=SCALE_WEIGHT;      /* identical!     don't split them up */
  1141. X          else
  1142. X         for(j=0;--i;)
  1143. X            switch(*right++)
  1144. X             { case '@':j=SCALE_WEIGHT/2;       /* domains match! */
  1145. X               case '.':j++;               /* domain borders */
  1146. X             }
  1147. X          revstr(left);*ip++=j;
  1148. X        }
  1149. X       revstr(*nam);*ip=0;nam=malloc(++argc*sizeof*argv);
  1150. X       tmemmove(nam,nargv,argc*sizeof*argv);nargv=nam;
  1151. X       ;{ unsigned long cnames,cnsize,cconcur,maxnsize;
  1152. X          char**first,**best;
  1153. X          for(maxnsize=MAX_argc*16L;*nam;maxnsize-=strlen(*nam++));
  1154. X          n=cconcur=0;
  1155. X          do
  1156. X           { int bestval;
  1157. X         cnsize=strlen(*(first=nam=revarr+n));cnames=0;
  1158. X         do
  1159. X          { if(first-nam<minnames||rdist[n]<=bestval)
  1160. X               bestval=rdist[n],best=nam;
  1161. X            cnames++;
  1162. X          }
  1163. X         while(++n<revfilled&&
  1164. X               maxnsize>=(cnsize+=strlen(*++nam)+1)&&
  1165. X               maxnames>cnames);
  1166. X         nam=(nargv=realloc(nargv,
  1167. X          ((bestval=best-first+1)+argc)*sizeof*argv))+argc-1;
  1168. X         if(maxconcur&&maxconcur<++cconcur)
  1169. X            wait((int*)0);
  1170. X         tmemmove(nam,first,bestval*sizeof*argv);nam[bestval]=0;
  1171. X         if(STDIN!=open(mailfile,O_RDONLY))
  1172. X          { nlog("Lost");logqnl(mailfile);retval=EX_NOINPUT;
  1173. X            sterminate();
  1174. X          }
  1175. X         for(;;)
  1176. X          { switch(fork())
  1177. X             { case -1:nlog("Couldn't fork, retrying\n");
  1178. X              if(wait((int*)0)==-1)
  1179. X                 sleep(DEFsuspend);
  1180. X              continue;
  1181. X               case 0:
  1182. X              execve(nargv[0],nargv,environ);
  1183. X              kill(getppid(),SIGTERM);nlog("Couldn't exec");
  1184. X              logqnl(nargv[0]);
  1185. X              return EX_UNAVAILABLE;
  1186. X             }
  1187. X            break;
  1188. X          }
  1189. X         close(STDIN);
  1190. X           }
  1191. X          while((n=best-revarr+1)<revfilled);
  1192. X        }
  1193. X     }
  1194. X    retval=EX_OK;sterminate();
  1195. X      }
  1196. X     minweight=SCALE_WEIGHT;best_matches=maxgram=0;exc2str.text=excstr.text=0;
  1197. X     while((chp= *++argv)&&*chp=='-')
  1198. X    for(chp++;;)
  1199. X     { int c;
  1200. X       switch(c= *chp++)
  1201. X        { case 'c':charoffs=1;
  1202. X         continue;
  1203. X          case 'd':remov=1;
  1204. X         continue;
  1205. X          case 'i':incomplete=1;
  1206. X         continue;
  1207. X          case 'r':renam=1;
  1208. X         continue;
  1209. X          case 'm':chkmetoo=1;
  1210. X         continue;
  1211. X          case 'a':
  1212. X         if(!*chp&&!(chp= *++argv))
  1213. X            goto usg;
  1214. X         addit=chp;
  1215. X         break;
  1216. X          case 'x':
  1217. X         if(!*chp&&!(chp= *++argv))
  1218. X            goto usg;
  1219. X         if(excstr.text)
  1220. X            exc2str.text=chp;
  1221. X         else
  1222. X            excstr.text=chp;
  1223. X         break;
  1224. X          case 'b':case 'l':case 'w':
  1225. X           { int i;
  1226. X         ;{ const char*ochp;
  1227. X            if(!*chp&&!(chp= *++argv)||
  1228. X             (i=strtol(ochp=chp,(char**)&chp,10),chp==ochp))
  1229. X               goto usg;
  1230. X          }
  1231. X         switch(c)
  1232. X          { case 'b':best_matches=i;
  1233. X               continue;
  1234. X            case 'l':minweight=i;
  1235. X               continue;
  1236. X            case 'w':maxgram=i;
  1237. X               continue;
  1238. X          }
  1239. X           }
  1240. X          case HELPOPT1:case HELPOPT2:elog(usage);
  1241. X         elog(
  1242. X "\t-a address\tadd this address to the list\
  1243. X\n\t-b nnn\t\tmaximum no. of best matches shown\
  1244. X\n\t-c\t\tdisplay offsets in characters\
  1245. X\n\t-d\t\tdelete address from list\
  1246. X\n\t-i\t\tcheck for incomplete addresses too\
  1247. X\n\t-m\t\tcheck for metoo\
  1248. X\n\t-l nnn\t\tlower bound metric\
  1249. X\n\t-r\t\trename address on list\
  1250. X\n\t-x address\texclude this address from the search (max. 2)\
  1251. X\n\t-w nnn\t\twindow width used when matching\n");
  1252. X         return EX_USAGE;
  1253. X          case '-':
  1254. X         if(!*chp)
  1255. X          { chp= *++argv;
  1256. X            goto lastopt;
  1257. X          }
  1258. X          default:
  1259. X         goto usg;
  1260. X          case '\0':;
  1261. X        }
  1262. X       break;
  1263. X     }
  1264. Xlastopt:
  1265. X     if(!chp||*++argv||renam+remov+!!addit>1)
  1266. X    goto usg;
  1267. X     if(excstr.text)
  1268. X      { excstr.textlen=strlen(excstr.text);lowcase(&excstr);
  1269. X    if(exc2str.text)
  1270. X       exc2str.textlen=strlen(exc2str.text),lowcase(&exc2str);
  1271. X      }
  1272. X     if(!(hardfile=fopen(chp,remov||renam||addit?"r+":"r")))
  1273. X      { nlog(cldntopen);logqnl(chp);
  1274. X    return EX_IOERR;
  1275. X      }
  1276. X#ifdef SPEEDBUF                   /* allocate a bigger stdio buffer */
  1277. X     setvbuf(hardfile,malloc(SPEEDBUF),_IOFBF,(size_t)SPEEDBUF);
  1278. X#endif
  1279. X   }
  1280. X  else
  1281. Xusg:
  1282. X   { elog(usage);
  1283. X     return EX_USAGE;
  1284. X   }
  1285. X  if(addit)                  /* special subfunction, to add entries */
  1286. X   { int lnl;off_t lasttell;                 /* to the dist file */
  1287. X     for(lnl=1,lasttell=0;;)
  1288. X      { switch(getc(hardfile))                /* step through the file */
  1289. X     { case '\n':
  1290. X          if(!lnl)                /* looking for trailing newlines */
  1291. X         lnl=1,lasttell=ftell(hardfile);
  1292. X          continue;
  1293. X       default:lnl=0;
  1294. X          continue;
  1295. X       case EOF:;                   /* or the end of the file */
  1296. X     }
  1297. X    break;
  1298. X      }                     /* go back there, and add the new entry */
  1299. X     fseek(hardfile,lasttell,SEEK_SET);fprintf(hardfile,"%s\n",addit);
  1300. X     printf("Added: %s\n",addit);fclose(hardfile);
  1301. X     return EX_OK;
  1302. X   }
  1303. X  if(!maxgram)
  1304. X     maxgram=DEFmaxgram;
  1305. X  maxgram--;
  1306. X  if(minweight==SCALE_WEIGHT)
  1307. X     minweight=DEFminweight;
  1308. X  if(!best_matches)
  1309. X     best_matches=DEFbest_matches;
  1310. X  fuzzstr.text=malloc(fuzzstr.buflen=BUFSTEP);
  1311. X  ;{ int i;
  1312. X     best=malloc(best_matches--*sizeof*best);i=best_matches;
  1313. X     do
  1314. X      { best[i]=malloc(sizeof**best);best[i]->hard=malloc(1);
  1315. X    best[i]->fuzz=malloc(1);best[i]->metric= -SCALE_WEIGHT;
  1316. X      }
  1317. X     while(i--);
  1318. X   }
  1319. X  for(lastfrom= -1;readstr(stdin,&fuzzstr,0);)
  1320. X   { int meter,maxmetric;long linentry;off_t offs1,offs2;
  1321. X     ;{ char*chp,*echp;
  1322. X    const static char tpunctuation[]="@\\/!#$%^&*-_=+|~`';:,.?{}";
  1323. X#define punctuation    (tpunctuation+3)
  1324. X    echp=strchr(chp=fuzzstr.text,'\0')-1;
  1325. X    while(*chp&&strchr(punctuation,*chp))    /* strip leading punctuation */
  1326. X       chp++;
  1327. X    if(*chp=='"'&&!strchr(chp+1,'"'))      /* strip leading unbalanced " */
  1328. X       chp++;
  1329. X    while(*chp&&strchr(punctuation,*chp))    /* strip leading punctuation */
  1330. X       chp++;
  1331. X    while(echp>=chp&&strchr(tpunctuation,*echp))
  1332. X       *echp--='\0';               /* strip trailing punctuation */
  1333. X    if(echp>=chp&&*echp=='"'&&strchr(chp,'"')==echp)
  1334. X       *echp--='\0';              /* strip trailing unbalanced " */
  1335. X    while(echp>=chp&&strchr(tpunctuation,*echp))
  1336. X       *echp--='\0';               /* strip trailing punctuation */
  1337. X    if(echp<chp)
  1338. X       continue;
  1339. X    if(lastfrom<=0&&      /* roughly check if it could be a mail address */
  1340. X       !strpbrk(chp,"@/")&&             /* RFC-822 or X-400 address */
  1341. X       (!strchr(chp,'!')||               /* UUCP bang path address */
  1342. X        strchr(chp,'|')||
  1343. X        strchr(chp,',')||
  1344. X        strstr(chp,".."))&&
  1345. X       !(*chp=='<'&&*echp=='>')&&          /* RFC-822 machine literal */
  1346. X       !(incomplete&&strchr(chp,'.')))              /* domain name */
  1347. X     { if(lastfrom<0)
  1348. X          lastfrom=!strcmp(SHFROM,chp);
  1349. X       continue;              /* apparently not an email address */
  1350. X     }
  1351. X    lastfrom=0;tmemmove(fuzzstr.text,chp,echp-chp+1);
  1352. X    checkparens('(',')',fuzzstr.text,echp);
  1353. X    checkparens('[',']',fuzzstr.text,strchr(fuzzstr.text,'\0'));
  1354. X    if(*(chp=fuzzstr.text)=='<'&&*(echp=strchr(chp,'\0')-1)=='>')
  1355. X     { if(chp=strstr(chp,">,<"))        /* take the first of a dense */
  1356. X          (echp=chp)[1]='\0';            /* list of addresses */
  1357. X       if(!strchr(chp=fuzzstr.text,','))          /* strip '<' and '>' ? */
  1358. X          *echp='\0',tmemmove(chp,chp+1,echp-chp);
  1359. X     }
  1360. X    if(!(fuzzstr.textlen=strlen(chp)))        /* still something left? */
  1361. X       continue;                  /* it's gone, next word please */
  1362. X    lowcase(&fuzzstr);               /* cast it into lowercase */
  1363. X    if(excstr.text&&matchgram(&fuzzstr,&excstr)>=EXCL_THRESHOLD||
  1364. X     exc2str.text&&matchgram(&fuzzstr,&exc2str)>=EXCL_THRESHOLD)
  1365. X     { free(fuzzstr.itext);
  1366. X       continue;
  1367. X     }
  1368. X    ;{ int i=0;
  1369. X       do
  1370. X        { if(best[i]->metric==-SCALE_WEIGHT&&!strcmp(best[i]->fuzz,chp))
  1371. X         break;
  1372. X          if(!strcmp(best[i]->fuzz,chp))    /* already matched this one? */
  1373. X         goto dupl_addr;
  1374. X        }
  1375. X       while(++i<=best_matches);
  1376. X     }
  1377. X    if(!curmatch)
  1378. X       curmatch=malloc(sizeof*curmatch);
  1379. X    curmatch->fuzz=tstrdup(chp);curmatch->hard=malloc(1);
  1380. X    curmatch->metric= -SCALE_WEIGHT;
  1381. X      }
  1382. X     fseek(hardfile,(off_t)0,SEEK_SET);maxmetric=best[best_matches]->metric;
  1383. X     for(remov_delim=offs2=linentry=0;
  1384. X      offs1=offs2,readstr(hardfile,&hardstr,1);)
  1385. X      { offs2=ftell(hardfile);linentry++;
  1386. X    if(*hardstr.text=='(')
  1387. X       continue;                   /* unsuitable for matches */
  1388. X    lowcase(&hardstr);meter=matchgram(&fuzzstr,&hardstr);
  1389. X    free(hardstr.itext);             /* check if we had any luck */
  1390. X    if(meter>maxmetric&&(remov_delim||!renam&&!remov))
  1391. X     { size_t hardlen;
  1392. X       curmatch->metric=maxmetric=meter;curmatch->lentry=linentry;
  1393. X       free(curmatch->hard);hardlen=hardstr.textlen+1;
  1394. X       hardlen+=strlen(hardstr.text+hardlen)+1;
  1395. X       curmatch->hard=malloc(hardlen+=strlen(hardstr.text+hardlen)+1);
  1396. X       tmemmove(curmatch->hard,hardstr.text,hardlen);
  1397. X       curmatch->offs1=offs1;curmatch->offs2=offs2;
  1398. X     }
  1399. X      }
  1400. X     free(fuzzstr.itext);     /* maybe this match can be put in the array */
  1401. X     if(curmatch->metric>-SCALE_WEIGHT)           /* of best matches so far */
  1402. X      { struct match*mp,**mmp;
  1403. X    free((mp= *(mmp=best+best_matches))->fuzz);free(mp->hard);free(mp);
  1404. X    while(--mmp>=best&&(mp= *mmp)->metric<curmatch->metric)
  1405. X       mmp[1]=mp;                       /* keep it sorted */
  1406. X    mmp[1]=curmatch;curmatch=0;
  1407. X      }
  1408. X     else
  1409. X    free(curmatch->fuzz),free(curmatch->hard);
  1410. Xdupl_addr:;
  1411. X   }
  1412. X  ;{ int i;struct match*mp;
  1413. X     for(i=0;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  1414. X    if(chkmetoo)
  1415. X       printf("%s\n",strcmp(mp->hard+strlen(mp->hard)+1,NOT_METOO)
  1416. X        ?metoo_SENDMAIL:nometoo_SENDMAIL);
  1417. X    else
  1418. X       printf("%3ld %-34s %5d %s\n",
  1419. X        charoffs?mp->offs1:mp->lentry,mp->hard,mp->metric,mp->fuzz);
  1420. X     if((mp= *best)->metric>=minweight)
  1421. X      { struct match*worse;
  1422. X    if(renam)
  1423. X     { long line;int i,w1;unsigned maxweight;
  1424. X       maxweight=SCALE_WEIGHT/(maxgram+1)>>1;;
  1425. X       for(i=1,line=mp->lentry,w1=mp->metric,worse=0;
  1426. X        i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  1427. X          if(mp->lentry==line&&mp->metric+maxweight<w1)
  1428. X         goto remv1;
  1429. X       for(i=1;i<=best_matches&&(mp=best[i++])->metric>=minweight;)
  1430. X          if(mp->metric+maxweight<w1)
  1431. Xremv1:           { worse=mp;mp= *best;
  1432. X         goto remv;
  1433. X           }
  1434. X       nlog("Couldn't find a proper address pair\n");
  1435. X       goto norenam;
  1436. X     }
  1437. X    if(remov)
  1438. Xremv:     { char*buf;off_t offs1,offs2;size_t readin;
  1439. X       buf=malloc(COPYBUF);offs1=mp->offs1;offs2=mp->offs2;
  1440. X       while(fseek(hardfile,offs2,SEEK_SET),
  1441. X        readin=fread(buf,1,COPYBUF,hardfile))
  1442. X        { offs2=ftell(hardfile);fseek(hardfile,offs1,SEEK_SET);
  1443. X          if(buf[readin-1]=='\n')      /* try and remove some empty lines */
  1444. X         while(readin>1&&buf[readin-2]=='\n')    /* at the end, since */
  1445. X            readin--;             /* every time could be the last */
  1446. X          fwrite(buf,1,readin,hardfile);offs1=ftell(hardfile);
  1447. X        }
  1448. X       free(buf);fseek(hardfile,offs1,SEEK_SET);
  1449. X       printf("Removed: %s\n",mp->hard);
  1450. X       if(renam)
  1451. X          fputs(worse->fuzz,hardfile),printf("Added: %s\n",worse->fuzz);
  1452. X       do putc('\n',hardfile);               /* erase the tail */
  1453. X       while(ftell(hardfile)<offs2);
  1454. X       fclose(hardfile);
  1455. X     }
  1456. X    return EX_OK;
  1457. X      }
  1458. X   }
  1459. X  if(remov||renam)
  1460. X   { nlog("Couldn't even find a single address\n");
  1461. X   }
  1462. Xnorenam:
  1463. X  return 1;
  1464. X}
  1465. END_OF_FILE
  1466.   if test 27219 -ne `wc -c <'procmail-3.03/src/multigram.c'`; then
  1467.     echo shar: \"'procmail-3.03/src/multigram.c'\" unpacked with wrong size!
  1468.   fi
  1469.   # end of 'procmail-3.03/src/multigram.c'
  1470. fi
  1471. echo shar: End of archive 1 \(of 5\).
  1472. cp /dev/null ark1isdone
  1473. MISSING=""
  1474. for I in 1 2 3 4 5 ; do
  1475.     if test ! -f ark${I}isdone ; then
  1476.     MISSING="${MISSING} ${I}"
  1477.     fi
  1478. done
  1479. if test "${MISSING}" = "" ; then
  1480.     echo You have unpacked all 5 archives.
  1481.     rm -f ark[1-9]isdone
  1482. else
  1483.     echo You still must unpack the following archives:
  1484.     echo "        " ${MISSING}
  1485. fi
  1486. exit 0
  1487. exit 0 # Just in case...
  1488.