~ubuntu-branches/ubuntu/natty/amavisd-new/natty

« back to all changes in this revision

Viewing changes to amavisd-new-courier-old.patch

  • Committer: Bazaar Package Importer
  • Author(s): Christian Perrier
  • Date: 2007-02-24 19:27:53 UTC
  • mfrom: (3.1.7 feisty)
  • Revision ID: james.westby@ubuntu.com-20070224192753-fvvima53q1jrp34x
Tags: 1:2.4.2-6.1
* Non-maintainer upload to fix pending l10n issues.
* Debconf translations
  - Remove extra debian/po/de.po~
  - Convert all translation files to UTF-8
  - Russian. Closes: #405243
  - Spanish. Closes: #408734
  - Italian. Closes: #409831
* Add very simple LSB headers to init scripts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
--- amavisd.ori Tue Jun 27 13:22:56 2006
 
2
+++ amavisd     Tue Jun 27 13:23:43 2006
 
3
@@ -96,5 +96,5 @@
 
4
 #  Amavis::In::AMCL
 
5
 #  Amavis::In::SMTP
 
6
-#( Amavis::In::Courier )
 
7
+#  Amavis::In::Courier
 
8
 #  Amavis::Out::SMTP
 
9
 #  Amavis::Out::Pipe
 
10
@@ -140,5 +140,5 @@
 
11
   fetch_modules('REQUIRED BASIC MODULES', 1, qw(
 
12
     Exporter POSIX Fcntl Socket Errno Carp Time::HiRes
 
13
-    IO::Handle IO::File IO::Socket IO::Socket::UNIX IO::Socket::INET
 
14
+    IO::Handle IO::File IO::Select IO::Socket IO::Socket::UNIX IO::Socket::INET
 
15
     IO::Wrap IO::Stringy Digest::MD5 Unix::Syslog File::Basename
 
16
     Mail::Field Mail::Address Mail::Header Mail::Internet Compress::Zlib
 
17
@@ -6376,4 +6376,5 @@
 
18
 use POSIX qw(locale_h);
 
19
 use IO::File ();
 
20
+use IO::Select;
 
21
 use Time::HiRes ();
 
22
 # body digest for caching, either SHA1 or MD5
 
23
@@ -6868,4 +6869,31 @@
 
24
 
 
25
 ### Net::Server hook
 
26
+### This hook takes place immediately after the "->run()" method is called.
 
27
+### This hook allows for setting up the object before any built in configuration
 
28
+### takes place.  This allows for custom configurability.
 
29
+sub configure_hook {
 
30
+  my($self) = @_;
 
31
+  if ($courierfilter_shutdown) {
 
32
+    # Duplicate the courierfilter pipe to another fd since STDIN is closed if we
 
33
+    # daemonize
 
34
+    $self->{courierfilter_pipe} = IO::File->new('<&STDIN')
 
35
+      or die "Can't duplicate courierfilter shutdown pipe: $!";
 
36
+    $self->{courierfilter_select} = IO::Select->new($self->{courierfilter_pipe});
 
37
+  }
 
38
+}
 
39
+
 
40
+### Net::Server hook
 
41
+### This hook occurs just after the bind process and just before any
 
42
+### chrooting, change of user, or change of group occurs.  At this point
 
43
+### the process will still be running as the user who started the server.
 
44
+sub post_bind_hook {
 
45
+  my ($self) = @_;
 
46
+  if (c('protocol') eq 'COURIER') {
 
47
+    # Allow courier to write to the socket
 
48
+    chmod(0660, $unix_socketname);
 
49
+  }
 
50
+}
 
51
+
 
52
+### Net::Server hook
 
53
 ### This hook occurs in the parent (master) process after chroot,
 
54
 ### change of user, and change of group has occured. It allows
 
55
@@ -6908,4 +6936,15 @@
 
56
     }
 
57
     Amavis::SpamControl::init_pre_fork()  if $extra_code_antispam;
 
58
+    if ($courierfilter_shutdown) {
 
59
+      # Tell courierfilter we have finished initialisation by closing fd 3
 
60
+      # But make sure it's a pipe (and not the courierfilter shutdown pipe)
 
61
+      # first: if we have been started using filterctl (i.e. not when
 
62
+      # courierfilter itself starts) then there is no initial pipe on fd 3 so
 
63
+      # it could be assigned to another file
 
64
+      open(my $fh3, '<&3');
 
65
+      if (-p $fh3 && $self->{courierfilter_pipe}->fileno() != 3) {
 
66
+        POSIX::close(3);
 
67
+      }
 
68
+    }
 
69
   };
 
70
   if ($@ ne '') {
 
71
@@ -7195,5 +7234,7 @@
 
72
     if ($sock->NS_proto eq 'UNIX') {     # traditional amavis helper program
 
73
       if ($suggested_protocol eq 'COURIER') {
 
74
-        die "unavailable support for protocol: $suggested_protocol";
 
75
+        # courierfilter client
 
76
+        $courier_in_obj = Amavis::In::Courier->new  if !$courier_in_obj;
 
77
+        $courier_in_obj->process_courier_request($sock, $conn, \&check_mail);
 
78
       } elsif ($suggested_protocol eq 'AM.PDP') {
 
79
         $amcl_in_obj = Amavis::In::AMCL->new  if !$amcl_in_obj;
 
80
@@ -7285,4 +7326,14 @@
 
81
 }
 
82
 
 
83
+### Net::Server::PreForkSimple hook
 
84
+### Is run by the master process every 10 seconds if $courierfilter_shutdown is set
 
85
+sub run_dequeue {
 
86
+  my($self) = @_;
 
87
+  if ($self->{courierfilter_select}->can_read(0)) {
 
88
+    do_log(0, "Instructed by courierfilter to shutdown");
 
89
+    $self->server_close();
 
90
+  }
 
91
+}
 
92
+
 
93
 ### Child is about to be terminated
 
94
 ### user customizable Net::Server hook
 
95
@@ -9656,4 +9707,8 @@
 
96
     log_level  => $DEBUG ? 4 : 2,
 
97
     log_file   => undef,  # will be overridden to call do_log()
 
98
+    # 9 to ensure it runs EVERY 10 seconds
 
99
+    # (Net::Server::PreForkSimple only checks every 10 seconds)
 
100
+    check_for_dequeue => $courierfilter_shutdown ? 9 : undef,
 
101
+    max_dequeue => $courierfilter_shutdown ? 1 : undef,
 
102
   },
 
103
 }, 'Amavis';
 
104
@@ -12151,5 +12206,413 @@
 
105
 no warnings 'uninitialized';
 
106
 
 
107
-BEGIN { die "Code not available for module Amavis::In::Courier" }
 
108
+BEGIN {
 
109
+  use Exporter ();
 
110
+  use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
 
111
+  $VERSION = '2.044';
 
112
+  @ISA = qw(Exporter);
 
113
+}
 
114
+
 
115
+use IO::File;
 
116
+
 
117
+BEGIN {
 
118
+  import Amavis::Conf qw(:platform :confvars ca c);
 
119
+  import Amavis::Util qw(do_log am_id untaint debug_oneshot snmp_counters_init
 
120
+                        switch_to_my_time switch_to_client_time
 
121
+                         read_text xtext_encode xtext_decode);
 
122
+  import Amavis::Lookup qw(lookup);
 
123
+  import Amavis::Lookup::IP qw(lookup_ip_acl);
 
124
+  import Amavis::rfc2821_2822_Tools qw(quote_rfc2821_local qquote_rfc2821_local);
 
125
+  import Amavis::Timing qw(section_time);
 
126
+  import Amavis::TempDir;
 
127
+  import Amavis::In::Message;
 
128
+}
 
129
+
 
130
+# Amavis::In::Courier->new()
 
131
+# Creates a new Amavis::In::Courier object
 
132
+sub new() {
 
133
+  my($class) = @_;
 
134
+  my $tempdir = Amavis::TempDir->new;
 
135
+  bless { tempdir => $tempdir }, $class;
 
136
+}
 
137
+
 
138
+# courier_in_obj->process_courier_request(socket, conn, check_mail)
 
139
+# Processes a request from Courier to check a single message
 
140
+#   socket: the socket to communicate with courierfilter
 
141
+#   conn: Amavis::In::Connection object
 
142
+#   check_mail: reference to the MTA-independent function called to check the
 
143
+#               message
 
144
+sub process_courier_request($$$) {
 
145
+  my($self, $socket, $conn, $check_mail) = @_;
 
146
+  
 
147
+  # Save the policy bank so that it can be restored at the end
 
148
+  my %baseline_policy_bank = %current_policy_bank;
 
149
+  my $policy_bank_changed = 0;
 
150
+  
 
151
+  eval {
 
152
+    $self->init_request();
 
153
+    $self->read_courierfilter_socket($socket);
 
154
+    $self->open_mail_text();
 
155
+    $policy_bank_changed = $self->change_policy_bank();
 
156
+    $self->call_check_mail($conn, $check_mail);
 
157
+    $self->process_result();
 
158
+    
 
159
+  };  if ($@) {
 
160
+    # An exception occurred
 
161
+    chomp($@);
 
162
+    my $msg = "Error in processing: $@";
 
163
+    do_log(-2, "TROUBLE in process_courier_request: 451 4.5.0 %s", $msg);
 
164
+    # Close the mail text file
 
165
+    $self->{msginfo}->mail_text->close()  if ($self->{msginfo}->mail_text);
 
166
+    $self->{msginfo}->mail_text(undef);
 
167
+    # Send a temporary failure to Courier
 
168
+    $self->{smtp_resp} = "451 4.5.0 $msg";
 
169
+  }
 
170
+  
 
171
+  # Send the SMTP reponse back to Courier (done outside the eval to ensure that
 
172
+  # it always happens exactly once, whether or not there is an exception)
 
173
+  do_log(3, "Mail checking ended: %s", $self->{smtp_resp});
 
174
+  send($socket, "$self->{smtp_resp}\n", 0);
 
175
+  
 
176
+  # Record time
 
177
+  section_time('send response');
 
178
+  do_log(2, Amavis::Timing::report());
 
179
+  
 
180
+  # Restore the policy bank
 
181
+  %current_policy_bank = %baseline_policy_bank  if ($policy_bank_changed);
 
182
+  
 
183
+  # Clean up object
 
184
+  $self->{per_recip_data} = undef;
 
185
+  $self->{control_files} = undef;
 
186
+}
 
187
+
 
188
+# courier_in_obj->init_request( )
 
189
+# Begins processing for a single request: initialises global variables and
 
190
+# creates msginfo object
 
191
+sub init_request() {
 
192
+  my($self) = @_;
 
193
+  
 
194
+  # Set up globals
 
195
+  am_id("$$-$Amavis::child_invocation_count");
 
196
+  Amavis::Timing::init();
 
197
+  snmp_counters_init();
 
198
+  
 
199
+  # Create msginfo object
 
200
+  $self->{msginfo} = Amavis::In::Message->new;
 
201
+  $self->{msginfo}->rx_time(time);
 
202
+}
 
203
+
 
204
+# courier_in_obj->read_courierfilter_socket(socket)
 
205
+# Reads the courierfilter socket, which specifies the path to the mail text and
 
206
+# the control files, storing the path to the mail text in the msginfo object
 
207
+# Also reads the control files and stores their data in msginfo
 
208
+#  socket: The courierfilter socket
 
209
+sub read_courierfilter_socket($) {
 
210
+  my($self, $socket) = @_;
 
211
+  
 
212
+  # Just make sure
 
213
+  local $/ = "\n";
 
214
+  
 
215
+  # Read the path to the mail text
 
216
+  switch_to_client_time("start receiving message text path");
 
217
+  my $text_path = $socket->getline;
 
218
+  switch_to_my_time("received message text path");
 
219
+  $text_path || die "Can't read message text path: $!";
 
220
+  chomp($text_path);
 
221
+  $text_path = untaint($text_path)  if ($text_path =~ m{^[A-Za-z0-9/._=+-]+\z});
 
222
+  $self->{msginfo}->mail_text_fn($text_path);
 
223
+  
 
224
+  # Read control files
 
225
+  $self->{control_files} = [];
 
226
+  my $path;
 
227
+  switch_to_client_time("start receving control file paths");
 
228
+  for ($! = 0; defined($path = $socket->getline); $! = 0) {
 
229
+    chomp($path);
 
230
+    # courierfilter indicates end of control files by sending a blank line
 
231
+    $path || last;
 
232
+    
 
233
+    switch_to_my_time("received control file path");
 
234
+    $path = untaint($path)  if ($path =~ m{^[A-Za-z0-9/._=+-]+\z});
 
235
+    push(@{ $self->{control_files} }, $path);
 
236
+    $self->read_control_file($path);
 
237
+    switch_to_client_time("receiving control file paths");
 
238
+  }
 
239
+  switch_to_my_time("finished receiving control file paths");
 
240
+  
 
241
+  # Check we did actually get a control file
 
242
+  @{ $self->{control_files} } || die "No control files specified";
 
243
+  # Record the recipients in msginfo
 
244
+  $self->{msginfo}->per_recip_data($self->{per_recip_data});
 
245
+  
 
246
+  # Record time
 
247
+  section_time('read control');
 
248
+}
 
249
+
 
250
+# courier_in_obj->read_control_file(path)
 
251
+# Reads a single Courier control file, adding its recipients to
 
252
+# $self->{per_recip_data} and storing other information in msginfo.
 
253
+# $self->{per_recip_data} is an array of Amavis::In::Message::PerRecip objects.
 
254
+# (Note that this method will overwrite any previous settings for sender, etc,
 
255
+# but if there are multiple control files they should contain the same
 
256
+# information.)
 
257
+#   path: the path to the control file
 
258
+sub read_control_file($) {
 
259
+  my($self, $path) = @_;
 
260
+  
 
261
+  do_log(3, "Reading Courier control file %s", $path);
 
262
+  
 
263
+  # Read the file
 
264
+  my $control_data = read_text($path);
 
265
+  my ($rcpt_idx, $recip) = (0, undef);
 
266
+  foreach (split(/\n/, $control_data)) {
 
267
+    # Parse a line of the control file
 
268
+    # Sender
 
269
+    if (/^s ( .*? (?:  \[  (?: \\. | [^\]\\] )*  \]
 
270
+                       |  [^@"<>\[\]\\\s] )*
 
271
+            ) \z/xs) {
 
272
+      $self->{msginfo}->sender($1);
 
273
+    }
 
274
+    
 
275
+    # Recipient
 
276
+    if (/^r ( .*? (?:  \[  (?: \\. | [^\]\\] )*  \]
 
277
+                       |  [^@"<>\[\]\\\s] )*
 
278
+            ) \z/xs) {
 
279
+      $recip = Amavis::In::Message::PerRecip->new;
 
280
+      $recip->recip_addr($1);
 
281
+      $recip->courier_control_file($path);
 
282
+      $recip->courier_recip_index($rcpt_idx);
 
283
+      $recip->recip_destiny(D_PASS); # Default destiny
 
284
+      push(@{ $self->{per_recip_data} }, $recip);
 
285
+      $rcpt_idx++;
 
286
+    }
 
287
+    
 
288
+    # Original Recipient (RFC 3461)
 
289
+    if (/^R ( [!-~]+ ) \z/xs) { $recip->dsn_orcpt($1) }
 
290
+    # RFC 3461 NOTIFY value
 
291
+    if (/^N ( [FSDN]+ ) \z/xs) {
 
292
+      my %notify_values = ( F => 'FAILURE', S => 'SUCCESS', D => 'DELAY', N => 'NEVER' );
 
293
+      $recip->dsn_notify([ map { $notify_values{$_} } split(m//, $1) ]);
 
294
+    }
 
295
+    
 
296
+    # DSN RET parameter (RFC 3461)
 
297
+    if (/^t F \z/xs) { $self->{msginfo}->dsn_ret('FULL') }
 
298
+    if (/^t H \z/xs) { $self->{msginfo}->dsn_ret('HDRS') }
 
299
+    # Envid (RFC 3461)
 
300
+    if (/^e ( [!-~]+ ) \z/xs) { $self->{msginfo}->dsn_envid($1) }
 
301
+    
 
302
+    # Authenticated submitter (RFC 2554)
 
303
+    if (/^i ( [!-~]+ ) \z/xs) { $self->{msginfo}->auth_submitter(xtext_decode($1)) }
 
304
+    
 
305
+    # Received-From-MTA
 
306
+    if (/^f .*? ;\s*  (  [A-Za-z0-9\.-]+  |  \[ [0-9A-Fa-f\.:]+ \]  )  \s*
 
307
+            \(  ( [A-Za-z0-9\.-]* )  \s* \[  ( [0-9A-Fa-f\.:]+ )  \] \)
 
308
+            \z/xs) {
 
309
+      $self->{msginfo}->client_helo($1);
 
310
+      $self->{msginfo}->client_name($2);
 
311
+      $self->{msginfo}->client_addr($3);
 
312
+    }
 
313
+    
 
314
+    # Courier queue ID
 
315
+    if (/^M ( [0-9A-Fa-f]+ \. [0-9A-Fa-f]+ \. [0-9A-Fa-f]+ )
 
316
+            \z/xs) {
 
317
+      $self->{msginfo}->queue_id($1);
 
318
+    }
 
319
+  }
 
320
+}
 
321
+
 
322
+# courier_in_obj->open_mail_text( )
 
323
+# Opens the mail text file, whose path has been read into msginfo->mail_text_fn
 
324
+# The file handle is stored in msginfo->mail_text
 
325
+sub open_mail_text() {
 
326
+  my($self) = @_;
 
327
+  
 
328
+  # Open the file
 
329
+  my $fh = IO::File->new($self->{msginfo}->mail_text_fn, 'r');
 
330
+  $fh || die "Can't open ", $self->{msginfo}->mail_text_fn, ": $!";
 
331
+  
 
332
+  # Disable UTF-8 decoding of input data
 
333
+  if ($unicode_aware) {
 
334
+    binmode($fh, ':bytes') || die "Can't cancel :utf8 mode: $!";
 
335
+  }
 
336
+  
 
337
+  # Store file handle
 
338
+  $self->{msginfo}->mail_text($fh);
 
339
+  
 
340
+  # Record time
 
341
+  section_time('open text');
 
342
+}
 
343
+
 
344
+# courier_in_obj->change_policy_bank( )
 
345
+# Loads a new policy bank if necessary
 
346
+# Also enables debug_oneshot if necessary, and sets msginfo->client_addr_mynets
 
347
+# Returns 1 if the policy bank is changed, 0 otherwise
 
348
+sub change_policy_bank() {
 
349
+  my($self) = @_;
 
350
+  my $cl_ip = $self->{msginfo}->client_addr;
 
351
+  my $sender = $self->{msginfo}->sender;
 
352
+  my $policy_changed = 0;
 
353
+  
 
354
+  # Enable debug_oneshot if set for this sender
 
355
+  debug_oneshot(1)  if lookup(0, $sender, @{ ca('debug_sender_maps') });
 
356
+  
 
357
+  # Load MYNETS policy bank if client IP is local
 
358
+  my $cl_ip_mynets = ($cl_ip eq '' ? undef
 
359
+                     : lookup_ip_acl($cl_ip, @{ ca('mynetworks_maps') }));
 
360
+  $self->{msginfo}->client_addr_mynets($cl_ip_mynets);
 
361
+  if ($cl_ip_mynets && defined($policy_bank{'MYNETS'})) {
 
362
+    Amavis::load_policy_bank('MYNETS');
 
363
+    $policy_changed = 1;
 
364
+  }
 
365
+  
 
366
+  # Load MYUSERS policy bank if sender is local
 
367
+  if ($sender ne '' && defined($policy_bank{'MYUSERS'})
 
368
+      && lookup(0, $sender, @{ ca('local_domains_maps') }))
 
369
+  {
 
370
+    Amavis::load_policy_bank('MYUSERS');
 
371
+    $policy_changed = 1;
 
372
+  }
 
373
+  
 
374
+  $policy_changed;
 
375
+}
 
376
+
 
377
+# courier_in_obj->call_check_mail(conn, check_mail)
 
378
+# Calls the check_mail function to check a message - the properties of msginfo
 
379
+# must already be set
 
380
+# Also handles the tempdir and closes the mail_text file afterwards
 
381
+# Saves the STMP response returned by check_mail in $self->{smtp_resp}
 
382
+#   conn: Amavis::In::Connection object
 
383
+#   check_mail: reference to the function to call
 
384
+sub call_check_mail($$) {
 
385
+  my($self, $conn, $check_mail) = @_;
 
386
+  
 
387
+  # Initialise variables
 
388
+  Amavis::check_mail_begin_task();
 
389
+  
 
390
+  # Prepare temporary directory
 
391
+  $self->{tempdir}->prepare();
 
392
+  $self->{msginfo}->mail_tempdir($self->{tempdir}->path);
 
393
+  
 
394
+  # Courier is responsible for relaying the message, and so for success DSNs
 
395
+  $self->{msginfo}->dsn_passed_on(c('forward_method') eq '' ? 1 : 0);
 
396
+  
 
397
+  # Log the message
 
398
+  do_log(1, 'Courier %s %s: <%s> -> %s%s',
 
399
+            $self->{msginfo}->queue_id, $self->{tempdir}->path,
 
400
+            $self->{msginfo}->sender,
 
401
+            join(',', qquote_rfc2821_local(@{ $self->{msginfo}->recips })),
 
402
+            join('',
 
403
+                 !$self->{msginfo}->auth_submitter ||
 
404
+                      $self->{msginfo}->auth_submitter eq '<>' ? ():
 
405
+                          ' AUTH='.$self->{msginfo}->auth_submitter,
 
406
+                 !$self->{msginfo}->dsn_ret   ? () :
 
407
+                          ' RET='.$self->{msginfo}->dsn_ret,
 
408
+                 !$self->{msginfo}->dsn_envid ? () :
 
409
+                          ' ENVID='.xtext_decode($self->{msginfo}->dsn_envid),
 
410
+            ));
 
411
+  
 
412
+  # The temporary directory is about to become non-empty
 
413
+  $self->{tempdir}->empty(0);
 
414
+  # Do the work
 
415
+  my ($smtp_resp, $exit_code, $preserve_evidence)
 
416
+    = $check_mail->($conn, $self->{msginfo}, 0);
 
417
+  # Preserve evidence if necessary
 
418
+  $preserve_evidence && $self->{tempdir}->preserve(1);
 
419
+  
 
420
+  # Clean the temporary directory
 
421
+  $self->{tempdir}->clean();
 
422
+  
 
423
+  # Close the mail text file
 
424
+  $self->{msginfo}->mail_text->close() || die "Can't close temp file: $!";
 
425
+  $self->{msginfo}->mail_text(undef);
 
426
+  
 
427
+  # Save the SMTP response
 
428
+  $self->{smtp_resp} = $smtp_resp;
 
429
+}
 
430
+
 
431
+# courier_in_obj->process_result( )
 
432
+# Processes the result of mail scanning - recipient addition/deletion (for the
 
433
+# time being we do not support this and only put a warning in the log)
 
434
+# Before calling this, the SMTP response must be stored in $self->{smtp_resp}
 
435
+# and may be altered
 
436
+# This does not send the SMTP response back to Courier
 
437
+sub process_result() {
 
438
+  my($self) = @_;
 
439
+  
 
440
+  if ($self->{smtp_resp} =~ /^25/) {
 
441
+    foreach my $r (@{ $self->{msginfo}->per_recip_data }) {
 
442
+      my ($addr, $newaddr) = ($r->recip_addr, $r->recip_final_addr);
 
443
+      
 
444
+      if ($r->recip_done) {
 
445
+        $self->delete_recipient($r);
 
446
+        
 
447
+      } elsif ($newaddr ne $addr) {
 
448
+        $r->recip_smtp_response("251 2.1.5 Amavisd replaced recip with <$newaddr>");
 
449
+        $self->delete_recipient($r);
 
450
+        
 
451
+        my $orcpt = $r->dsn_orcpt || 'rfc822;'.xtext_encode(quote_rfc2821_local($_));
 
452
+        $self->add_recipient($newaddr, $orcpt, $r->dsn_notify);
 
453
+      }
 
454
+    }
 
455
+  }
 
456
+  
 
457
+  # Record time
 
458
+  section_time('process result');
 
459
+}
 
460
+
 
461
+# delete_recipient(recip)
 
462
+# Deletes a recipient by marking them as successfully delivered in the control
 
463
+# file.  If the same recipient appears more than once in the control files,
 
464
+# every instance will be marked as done.
 
465
+#   recip: Amavis::In::Message::PerRecip object
 
466
+sub delete_recipient($) {
 
467
+  my($self, $recip) = @_;
 
468
+  
 
469
+  do_log(1, "Amavis::In::Courier: Deleting recipient <%s>: %s",
 
470
+            $recip->recip_addr, $recip->recip_smtp_response);
 
471
+  
 
472
+  my $filename = $recip->courier_control_file;
 
473
+  my $control_file = IO::File->new($filename, 'a');
 
474
+  # Not sure why we do the seek when the file is already opened for append,
 
475
+  # but courier-pythonfilter does it so it's probably a good idea
 
476
+  seek($control_file, 0, 2);
 
477
+  # Courier may still append to the control file after calling the filter,
 
478
+  # so write a long blank line first to ensure that its additional records
 
479
+  # only overwrite the blank line
 
480
+  $control_file->print(" " x 254, "\n");
 
481
+  
 
482
+  # Tell Courier the message is delivered
 
483
+  $control_file->printf("I%d R %s\n", $recip->courier_recip_index,
 
484
+                        $recip->recip_smtp_response);
 
485
+  $control_file->printf("S%d %d\n", $recip->courier_recip_index, time);
 
486
+
 
487
+  $control_file->close or die "Error closing control file $filename: $!";
 
488
+}
 
489
+
 
490
+# add_recipient(recip)
 
491
+# Adds a recipient to the last control file for the message.
 
492
+#   address: recipient address to add
 
493
+#   orig_recip: RFC 3461 original recipient (if any)
 
494
+#   notify: reference to array containing values of the DSN NOTIFY value
 
495
+sub add_recipient($$$) {
 
496
+  my ($self, $address, $orig_recip, $notify) = @_;
 
497
+  
 
498
+  do_log(1, "Amavis::In::Courier: Adding recipient <%s>", $address);
 
499
+  
 
500
+  # Convert $notify array into character string
 
501
+  my $notify_str = join('', map { substr($_, 0, 1) } @$notify);
 
502
+  
 
503
+  # Open the last control file
 
504
+  my $filename = $self->{control_files}->[-1];
 
505
+  my $control_file = IO::File->new($filename, 'a');
 
506
+  # Take care with the control file: see comments in delete_recipient
 
507
+  seek($control_file, 0, 2);
 
508
+  $control_file->print(" " x 254, "\n");
 
509
+  
 
510
+  # Add recipient to control file
 
511
+  $control_file->print("r$address\n");
 
512
+  $control_file->print("R$orig_recip\n");
 
513
+  $control_file->print("N$notify_str\n");
 
514
+
 
515
+  $control_file->close or die "Error closing control file $filename: $!";
 
516
+}
 
517
 
 
518
 1;
 
519
--- amavisd.conf-sample.ori     Tue Jun 27 13:23:34 2006
 
520
+++ amavisd.conf-sample Tue Jun 27 13:25:58 2006
 
521
@@ -146,4 +146,11 @@
 
522
 #$notify_method = $forward_method;
 
523
 
 
524
+# COURIER using courierfilter
 
525
+#$forward_method = undef;  # no explicit forwarding, Courier does it itself
 
526
+#$notify_method = 'pipe:flags=q argv=perl -e $pid=fork();if($pid==-1){exit(75)}elsif($pid==0){exec(@ARGV)}else{exit(0)} /usr/sbin/sendmail -f ${sender} -- ${recipient}';
 
527
+# Only set $courierfilter_shutdown to 1 if you are using courierfilter to
 
528
+# control the startup and shutdown of amavis
 
529
+#$courierfilter_shutdown = 1; # (default 0)
 
530
+
 
531
 # prefer to collect mail for forwarding as BSMTP files?
 
532
 #$forward_method = "bsmtp:$MYHOME/out-%i-%n.bsmtp";
 
533
@@ -215,5 +222,5 @@
 
534
                                  # (default is true)
 
535
 
 
536
-# AMAVIS-CLIENT PROTOCOL INPUT SETTINGS (e.g. with amavisd-release, or
 
537
+# AMAVIS-CLIENT AND COURIER PROTOCOL INPUT SETTINGS (e.g. amavisd-release, or
 
538
 #   sendmail milter through helper clients like amavis-milter.c and amavis.c)
 
539
 #   option(s) -p overrides $inet_socket_port and $unix_socketname
 
540
@@ -221,4 +228,5 @@
 
541
 #$unix_socketname = undef;        # disable listening on a unix socket
 
542
                                   # (default is undef, i.e. disabled)
 
543
+#$unix_socketname = "/var/lib/courier/allfilters/amavisd"; # Courier socket
 
544
                                   # (usual setting is $MYHOME/amavisd.sock)
 
545
 
 
546
@@ -2303,4 +2311,7 @@
 
547
 #$interface_policy{'SOCK'} = 'AM.PDP-SOCK';
 
548
 
 
549
+# Needed for Courier: speak courier protocol on the socket
 
550
+#$interface_policy{'SOCK'} = 'AM-SOCK';
 
551
+#$policy_bank{'AM-SOCK'} = {protocol => 'COURIER'};
 
552
 
 
553
 # Want to execute additional configuration files from some directory?