bounce-resender.pl
上传用户:xu_441
上传日期:2007-01-04
资源大小:1640k
文件大小:8k
源码类别:

Email客户端

开发平台:

Unix_Linux

  1. #!/usr/local/bin/perl -w
  2. #
  3. # bounce-resender: constructs mail queue from bounce spool for
  4. #  subsequent reprocessing by sendmail
  5. #
  6. # usage: given a mail spool full of (only) bounced mail called "bounces":
  7. #        # mkdir -m0700 bqueue; cd bqueue && bounce-resender < ../bounces
  8. #        # cd ..
  9. #        # chown -R root bqueue; chmod 600 bqueue/*
  10. #        # /usr/lib/sendmail -bp -oQ`pwd`/bqueue | more   # does it look OK?
  11. #        # /usr/lib/sendmail -q -oQ`pwd`/bqueue -oT99d &  # run the queue
  12. #
  13. # ** also read messages at end! **
  14. #
  15. # Brian R. Gaeke <brg@EECS.Berkeley.EDU> Thu Feb 18 13:40:10 PST 1999
  16. #
  17. #############################################################################
  18. # This script has NO WARRANTY, NO BUG FIXES, and NO SUPPORT.  You will
  19. # need to modify it for your site and for your operating system, unless
  20. # you are in the EECS Instructional group at UC Berkeley. (Search forward
  21. # for two occurrences of "FIXME".)
  22. #
  23. $state = "MSG_START";
  24. $ctr = 0;
  25. $lineno = 0;
  26. $getnrl = 0;
  27. $nrl = "";
  28. $uname = "PhilOS";  # You don't want to change this here.
  29. $myname = $0;
  30. $myname =~ s,.*/([^/]*),$1,;
  31. chomp($hostname = `hostname`);
  32. chomp($uname = `uname`);
  33. # FIXME: Define the functions "major" and "minor" for your OS.
  34. if ($uname eq "SunOS") {
  35. # from h2ph < /usr/include/sys/sysmacros.h on 
  36. # SunOS torus.CS.Berkeley.EDU 5.6 Generic_105182-11 i86pc i386 i86pc
  37.     eval 'sub O_BITSMINOR () {8;}' unless defined(&O_BITSMINOR);
  38.     eval 'sub O_MAXMAJ () {0x7f;}' unless defined(&O_MAXMAJ);
  39.     eval 'sub O_MAXMIN () {0xff;}' unless defined(&O_MAXMIN);
  40. eval 'sub major {
  41.     local($x) = @_;
  42.     eval "((($x) >>  &O_BITSMINOR)   &O_MAXMAJ)";
  43. }' unless defined(&major);
  44. eval 'sub minor {
  45.     local($x) = @_;
  46.     eval "(($x)   &O_MAXMIN)";
  47. }' unless defined(&minor);
  48. } else {
  49. die "How do you calculate major and minor device numbers on $uname?n";
  50. }
  51. sub ignorance { $ignored{$state}++; }
  52. sub unmunge {
  53. my($addr) = @_;
  54. $addr =~ s/_FNORD_/ /g;
  55. # remove (Real Name)
  56. $addr =~ s/^(.*)([^)]*)(.*)$/$1$2/
  57. if $addr =~ /^.*([^)]*).*$/;
  58. # extract <user@host> if it appears
  59. $addr =~ s/^.*<([^>]*)>.*$/$1/
  60. if $addr =~ /^.*<[^>]*>.*$/;
  61. # strip leading, trailing blanks
  62. $addr =~ s/^s*(.*)s*/$1/;
  63. # nuke local domain
  64.     # FIXME: Add a regular expression for your local domain here.
  65. $addr =~
  66.  s/@(cory|po|pasteur|torus|parker|cochise|franklin).(ee)?cs.berkeley.edu//i;
  67. return $addr;
  68. }
  69. print STDERR "$0: running on $hostname ($uname)n";
  70. open(INPUT,$ARGV[0]) || die "$ARGV[0]: $!n";
  71. sub working { 
  72. my($now);
  73. $now = localtime;
  74. print STDERR "$myname: Working... $nown";
  75. }
  76. &working();
  77. while (! eof INPUT) {
  78. # get a new line
  79. if ($state eq "IN_MESSAGE_HEADER") {
  80. # handle multi-line headers
  81. if ($nrl ne "" || $getnrl != 0) {
  82. $_ = $nrl;
  83. $getnrl = 0;
  84. $nrl = "";
  85. } else {
  86. $_ = <INPUT>; $lineno++;
  87. }
  88. unless ($_ =~ /^s*$/) {
  89. while ($nrl eq "") {
  90. $nrl = <INPUT>; $lineno++;
  91. if ($nrl =~ /^s+[^s].*$/) { # continuation line
  92. chomp($_);
  93. $_ .= "_FNORD_" . $nrl;
  94. $nrl = "";
  95. } elsif ($nrl =~ /^s*$/) { # end of headers
  96. $getnrl++;
  97. last;
  98. }
  99. }
  100. }
  101. } else {
  102. # normal single line
  103. if ($nrl ne "") {
  104. $_ = $nrl; $nrl = "";
  105. } else {
  106. $_ = <INPUT>; $lineno++;
  107. }
  108. }
  109. if ($state eq "WAIT_FOR_FROM") {
  110. if (/^From S+.*$/) {
  111. $state = "MSG_START";
  112. } else {
  113. &ignorance();
  114. }
  115. } elsif ($state eq "MSG_START") {
  116. if (/^s+boundary="([^"]*)".*$/) {
  117. $boundary = $1;
  118. $state = "GOT_BOUNDARY";
  119. $ctr++;
  120. } else {
  121. &ignorance();
  122. }
  123. } elsif ($state eq "GOT_BOUNDARY") {
  124. if (/^--$boundary/) {
  125. $next = <INPUT>; $lineno++;
  126. if ($next =~ /^Content-Type: message/rfc822/) {
  127. $hour = (localtime)[2];
  128. $char = chr(ord("A") + $hour);
  129. $ident = sprintf("%sAA%05d",$char,99999 - $ctr);
  130. $qf = "qf$ident";
  131. $df = "df$ident";
  132. @rcpt = ();
  133. open(MSGHDR,">$qf") || die "Can't write to $qf: $!n";
  134. open(MSGBODY,">$df") || die "Can't write to $df: $!n";
  135. chmod(0600, $qf, $df);
  136. $state = "IN_MESSAGE_HEADER";
  137. $header = $body = "";
  138. $messageid = "bounce-resender-$ctr";
  139. $fromline = "MAILER-DAEMON";
  140. $ctencod = "7BIT";
  141. # skip a bit, brother maynard (boundary is separated from
  142. #  the header by a blank line)
  143. $next = <INPUT>; $lineno++;
  144. unless ($next =~ /^s*$/) {
  145. print MSGHDR $next;
  146. }
  147. }
  148. } else {
  149. &ignorance();
  150. }
  151. $next = $char = $hour = undef;
  152. } elsif ($state eq "IN_MESSAGE_HEADER") {
  153. if (!(/^--$boundary/ || /^s*$/)) {
  154. if (/^Message-[iI][dD]:s+<([^@]+)@[^>]*>.*$/) {
  155. $messageid = $1;
  156. } elsif (/^From:s+(.*)$/) {
  157. $fromline = $sender = $1;
  158. $fromline = unmunge($fromline);
  159. } elsif (/^Content-[Tt]ransfer-[Ee]ncoding:s+(.*)$/) {
  160. $ctencod = $1;
  161. } elsif (/^(To|[Cc][Cc]):s+(.*)$/) {
  162. $toaddrs = $2;
  163. foreach $toaddr (split(/,/,$toaddrs)) {
  164. $toaddr = unmunge($toaddr);
  165. push(@rcpt,$toaddr);
  166. }
  167. }
  168. $headerline = $_; 
  169. # escape special chars
  170. # (Perhaps not. It doesn't seem to be necessary (yet)).
  171.             #$headerline =~ s/([()<>@,;:\".[]])/\$1/g;
  172. # purely heuristic ;-)
  173.             $headerline =~ s/Return-Path:/?P?Return-Path:/g;
  174. # save H-line to write to qf, later
  175. $header .= "H$headerline";
  176. $headerline = $toaddr = $toaddrs = undef;
  177. } elsif (/^s*$/) {
  178. # write to qf
  179. ($dev, $ino) = (stat($df))[0 .. 1];
  180. ($maj, $min) = (major($dev), minor($dev));
  181. $time = time();
  182. print MSGHDR "V2n";
  183. print MSGHDR "B$ctencodn";
  184. print MSGHDR "S$sendern";
  185. print MSGHDR "I$maj/$min/$inon";
  186. print MSGHDR "K$timen";
  187. print MSGHDR "T$timen";
  188. print MSGHDR "D$dfn";
  189. print MSGHDR "N1n";
  190. print MSGHDR "MDeferred: manually-requeued bounced messagen";
  191. foreach $r (@rcpt) {
  192. print MSGHDR "RP:$rn";
  193. }
  194. $header =~ s/_FNORD_/n/g;
  195. print MSGHDR $header;
  196. print MSGHDR "HMessage-ID: <$messageid@$hostname>n"
  197. if ($messageid =~ /bounce-resender/);
  198. print MSGHDR ".n";
  199. close MSGHDR;
  200. # jump to state waiting for message body
  201. $state = "IN_MESSAGE_BODY";
  202. $dev = $ino = $maj = $min = $r = $time = undef;
  203. } elsif (/^--$boundary/) {
  204. # signal an error
  205. print "$myname: Header without message! Line $lineno qf $qfn";
  206. # write to qf anyway (SAME AS ABOVE, SHOULD BE A PROCEDURE)
  207. ($dev, $ino) = (stat($df))[0 .. 1];
  208. ($maj, $min) = (major($dev), minor($dev));
  209. $time = time();
  210. print MSGHDR "V2n";
  211. print MSGHDR "B$ctencodn";
  212. print MSGHDR "S$sendern";
  213. print MSGHDR "I$maj/$min/$inon";
  214. print MSGHDR "K$timen";
  215. print MSGHDR "T$timen";
  216. print MSGHDR "D$dfn";
  217. print MSGHDR "N1n";
  218. print MSGHDR "MDeferred: manually-requeued bounced messagen";
  219. foreach $r (@rcpt) {
  220. print MSGHDR "RP:$rn";
  221. }
  222. $header =~ s/_FNORD_/n/g;
  223. print MSGHDR $header;
  224. print MSGHDR "HMessage-ID: <$messageid@$hostname>n"
  225. if ($messageid =~ /bounce-resender/);
  226. print MSGHDR ".n";
  227. close MSGHDR;
  228. # jump to state waiting for next bounce message
  229. $state = "WAIT_FOR_FROM";
  230. $dev = $ino = $maj = $min = $r = $time = undef;
  231. } else {
  232. # never got here
  233. &ignorance();
  234. }
  235. } elsif ($state eq "IN_MESSAGE_BODY") {
  236. if (/^--$boundary/) {
  237. print MSGBODY $body;
  238. close MSGBODY;
  239. $state = "WAIT_FOR_FROM";
  240. } else {
  241. $body .= $_;
  242. }
  243. }
  244. if ($lineno % 1900 == 0) { &working(); }
  245. }
  246. close INPUT;
  247. foreach $x (keys %ignored) {
  248. print STDERR
  249. "$myname: ignored $ignored{$x} lines of bounce spool in state $xn";
  250. }
  251. print STDERR
  252. "$myname: processed $lineno lines of input and wrote $ctr messagesn";
  253. print STDERR
  254. "$myname: remember to chown the queue files to root before running:n";
  255. chomp($pwd = `pwd`);
  256. print STDERR "$myname:      # sendmail -q -oQ$pwd -oT99d &n";
  257. print STDERR "$myname: to test the newly generated queue:n";
  258. print STDERR "$myname:      # sendmail -bp -oQ$pwd | moren";
  259. exit 0;