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

« back to all changes in this revision

Viewing changes to helper-progs/amavis.pl

  • 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
#!/usr/bin/perl -T
 
2
 
 
3
#------------------------------------------------------------------------------
 
4
# This is amavis.pl, a simple demonstrational program functionally much like
 
5
# the amavis.c helper program, but talks the new AM.PDP protocol with the
 
6
# amavisd daemon. See README.protocol for the description of AM.PDP protocol.
 
7
# Usage:
 
8
#   amavis.pl sender recip1 recip2 ...  < message.txt
 
9
# To be placed in amavisd.conf:
 
10
#   $protocol='AM.PDP';  $unix_socketname='/var/amavis/amavisd.sock';
 
11
#
 
12
#
 
13
# Author: Mark Martinec <mark.martinec@ijs.si>
 
14
# Copyright (C) 2004  Mark Martinec,  All Rights Reserved.
 
15
#
 
16
# Redistribution and use in source and binary forms, with or without
 
17
# modification, are permitted provided that the following conditions are met:
 
18
#
 
19
# * Redistributions of source code must retain the above copyright notice,
 
20
#   this list of conditions and the following disclaimer.
 
21
# * Redistributions in binary form must reproduce the above copyright notice,
 
22
#   this list of conditions and the following disclaimer in the documentation
 
23
#   and/or other materials provided with the distribution.
 
24
# * Neither the name of the author, nor the name of the "Jozef Stefan"
 
25
#   Institute, nor the names of contributors may be used to endorse or
 
26
#   promote products derived from this software without specific prior
 
27
#   written permission.
 
28
#
 
29
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
30
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
31
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 
32
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 
33
# OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
34
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
35
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
36
# OR BUSINESS INTERRUPTION) HOWEVERREADME.protocol CAUSED AND ON ANY THEORY OF LIABILITY,
 
37
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 
38
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
39
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
40
#
 
41
#(the license above is the new BSD license, and pertains to this program only)
 
42
#
 
43
# Patches and problem reports are welcome.
 
44
# The latest version of this program is available at:
 
45
#   http://www.ijs.si/software/amavisd/
 
46
#------------------------------------------------------------------------------
 
47
 
 
48
use strict;
 
49
use re 'taint';
 
50
use IO::Socket;
 
51
use Digest::MD5;
 
52
use Time::HiRes ();
 
53
 
 
54
use vars qw($VERSION);  $VERSION = 1.000;
 
55
use vars qw($socketname);
 
56
 
 
57
# $socketname = '127.0.0.1:9998';
 
58
  $socketname = '/var/amavis/amavisd.sock';
 
59
 
 
60
sub sanitize_str {
 
61
  my($str, $keep_eol) = @_;
 
62
  my(%map) = ("\r" => '\\r', "\n" => '\\n', "\f" => '\\f', "\t" => '\\t',
 
63
              "\b" => '\\b', "\e" => '\\e', "\\" => '\\\\');
 
64
  if ($keep_eol) {
 
65
    $str =~ s/([^\012\040-\133\135-\176])/  # and \240-\376 ?
 
66
              exists($map{$1}) ? $map{$1} :
 
67
                     sprintf(ord($1)>255 ? '\\x{%04x}' : '\\%03o', ord($1))/eg;
 
68
  } else {
 
69
    $str =~ s/([^\040-\133\135-\176])/      # and \240-\376 ?
 
70
              exists($map{$1}) ? $map{$1} :
 
71
                     sprintf(ord($1)>255 ? '\\x{%04x}' : '\\%03o', ord($1))/eg;
 
72
  }
 
73
  $str;
 
74
}
 
75
 
 
76
sub do_log($$) {
 
77
  my($level, $errmsg) = @_;
 
78
  print STDERR sanitize_str($errmsg),"\n";
 
79
}
 
80
 
 
81
sub proto_decode($) {
 
82
  my($str) = @_;
 
83
  $str =~ s/%([0-9a-fA-F]{2})/pack("C",hex($1))/eg;
 
84
  $str;
 
85
}
 
86
 
 
87
sub proto_encode($@) {
 
88
  my($attribute_name,@strings) = @_; local($1);
 
89
  $attribute_name =~    # encode all but alfanumerics, '_' and '-'
 
90
    s/([^0-9a-zA-Z_-])/sprintf("%%%02x",ord($1))/eg;
 
91
  for (@strings) {      # encode % and nonprintables
 
92
    s/([^\041-\044\046-\176])/sprintf("%%%02x",ord($1))/eg;
 
93
  }
 
94
  $attribute_name . '=' . join(' ',@strings);
 
95
}
 
96
 
 
97
sub ask_amavisd($$) {
 
98
  my($sock,$query_ref) = @_;
 
99
  my(@encoded_query) =
 
100
    map { /^([^=]+)=(.*)\z/s; proto_encode($1,$2) } @$query_ref;
 
101
  do_log(0,'> '.$_)  for @encoded_query;
 
102
  $sock->print( map { $_."\015\012" } (@encoded_query,'') )
 
103
    or die "Can't write response to socket: $!";
 
104
  $sock->flush or die "Can't flush on socket: $!";
 
105
  my(%attr);
 
106
  local($/) = "\015\012";    # set line terminator to CRLF
 
107
  # must not use \r and \n, which may not be \015 and \012 on certain platforms
 
108
  do_log(0,"waiting for response");
 
109
  while(<$sock>) {
 
110
    last  if /^\015\012\z/;  # end of response
 
111
    if (/^ ([^=\000\012]*?) (=|:[ \t]*) ([^\012]*?) \015\012 \z/xsi) {
 
112
      my($attr_name) = proto_decode($1);
 
113
      my($attr_val)  = proto_decode($3);
 
114
      if (!exists $attr{$attr_name}) { $attr{$attr_name} = [] }
 
115
      push(@{$attr{$attr_name}}, $attr_val);
 
116
    }
 
117
  }
 
118
  if (!defined($_) && $! != 0) { die "read from socket failed: $!" }
 
119
  \%attr;
 
120
}
 
121
 
 
122
# Main program starts here
 
123
 
 
124
  die "Usage:  amavis.pl sender recip1 recip2 ...  < message.txt\n" if !@ARGV;
 
125
  my($sock);
 
126
  my($is_inet) = $socketname=~m{^/} ? 0 : 1; # simpleminded: unix vs. inet sock
 
127
  if ($is_inet) {   # inet socket
 
128
    $sock = IO::Socket::INET->new($socketname)
 
129
      or die "Can't connect to INET socket $socketname: $!";
 
130
  } else {          # unix socket
 
131
    $sock = IO::Socket::UNIX->new(Type => SOCK_STREAM)
 
132
      or die "Can't create UNIX socket: $!";
 
133
    $sock->connect( pack_sockaddr_un($socketname) )
 
134
      or die "Can't connect to UNIX socket $socketname: $!";
 
135
  }
 
136
 
 
137
  # generate some semi-unique directory name; not good enough for production
 
138
  my($ctx) = Digest::MD5->new; # 128 bits (32 hex digits)
 
139
  $ctx->add(sprintf("%s %.9f %s", $$, Time::HiRes::time, join(',',@ARGV)));
 
140
  my($id) = substr($ctx->b64digest,0,16);  $id =~ tr{+/}{-.};
 
141
 
 
142
  my($tempdir) = "/var/amavis/amavis-milter-$id";
 
143
  my($fname) = "$tempdir/email.txt";
 
144
  mkdir($tempdir,0750) or die "Can't create directory $tempdir: $!";
 
145
 
 
146
  # copy message from stdin to a file email.txt in the temporary directory
 
147
  open(F,">$fname") or die "Can't create file $fname: $!";
 
148
  while (<STDIN>) { print F $_  or die "Can't write to $fname: $!" }
 
149
  close(F) or die "Can't close $fname: $!";
 
150
 
 
151
  my(@query) = (
 
152
    'request=AM.PDP',
 
153
    "mail_file=$fname",
 
154
    "tempdir=$tempdir",
 
155
    'tempdir_removed_by=server',
 
156
    'sender='.shift @ARGV,
 
157
    map {"recipient=$_"} @ARGV,
 
158
#   'protocol_name=ESMTP',
 
159
#   'helo_name=b.example.com',
 
160
#   'client_address=10.2.3.4',
 
161
  );
 
162
  my($attr_ref) = ask_amavisd($sock,\@query);
 
163
  for my $attr_name (keys %$attr_ref) {
 
164
    for my $attr_val (@{$attr_ref->{$attr_name}})
 
165
      { do_log(0,"< $attr_name=$attr_val") }
 
166
  }
 
167
  # may do another query here if needed ...
 
168
  $sock->close or die "Can't close socket: $!";
 
169
  close(STDIN) or die "Can't close STDIN: $!";
 
170
  my($exit_code) = shift @{$attr_ref->{'exit_code'}};
 
171
  $exit_code = 0  if $exit_code==99;  # same thing in this case, both is ok
 
172
  exit 0+$exit_code;