update manpage and homepage
[undersmtpd.git] / _smtpd
1 #!/usr/bin/perl -w
2
3 use strict;
4 use Getopt::Std;
5 use vars qw(%aliases $opt_d $opt_a);
6 use subs qw(validate_recipient process_message read_aliases);
7 use Net::Server::Mail::ESMTP;
8
9 getopt('a:');
10
11 my $smtp = new Net::Server::Mail::ESMTP;
12 $smtp->set_callback(RCPT => \&validate_recipient);
13 $smtp->set_callback(DATA => \&process_message);
14
15 read_aliases;
16 $smtp->process;
17
18 #--
19
20 sub validate_recipient {
21   my($session, $recipient) = @_;
22   $recipient =~ s/^<//;
23   $recipient =~ s/\@.*$//;
24   return(0, 550, "Unknown user $recipient") unless exists $aliases{$recipient};
25   return(1);
26 }
27
28 sub process_message {
29   my($session, $data) = @_;
30
31   #warn "DATA: ". $$data;
32
33   my @recipients = $session->get_recipients();
34   return(0, 554, 'Error: no valid recipients')
35     unless(@recipients);
36
37   foreach my $recipient ( @recipients ) {
38     $recipient =~ s/^<//;
39     $recipient =~ s/\@.*$//;
40
41     open(PIPE, '|'.$aliases{$recipient})
42       or return(0, 451, "Can't fork: $!" );
43     print PIPE $$data
44       or return(0, 451, "Can't write to pipe: $!" );
45     close PIPE
46       or return(0, 451, "Can't close: status=$?" );
47
48   }
49
50   return(1, 250, 'message piped');
51
52 }
53
54 #--
55
56 sub read_aliases {
57   open(ALIASES, $opt_a || '/etc/aliases' ) or die $!;
58
59   while(<ALIASES>) {
60     chomp;
61     s/^\s+//; s/\s+$//;
62     next if /^$/ or /^#/;
63     /^([\w\-\+\.]+):\s*("?)\|(.*)\2\s*$/ or next;
64     #$aliases{$1} = [ split(/\s+/, $3) ];
65     $aliases{$1} = $3;
66   }
67
68 }
69
70 =head1 NAME
71
72 _smtpd - UnderSMTPD, the underscore SMTP daemon
73
74 =head1 SYNOPSIS
75
76   #make some aliases
77   echo 'username: "|someprogram and args"' > /etc/aliases
78
79   #inetd setup
80   echo "smtp stream tcp nowait mail /usr/local/bin/_smtpd" >>/etc/inetd.conf
81   echo "_smtpd: my.mail.server.ip"                         >>/etc/hosts.allow
82   echo "_smtpd: ALL"                                       >>/etc/hosts.deny
83
84   #or add an smtp file to /etc/xinetd.d/
85   service smtp
86   {
87         socket_type = stream
88         protocol    = tcp
89         wait        = no
90         user        = mail
91         server      = /usr/local/bin/_smtpd
92   }
93
94 =head1 DESCRIPTION
95
96 This is a minimal SMTP server which only forwards mail to pipe destinations
97 in /etc/aliases.  It does nothing else.  Its intended function is on an
98 internal mail server that forwards mail to other programs on a per address
99 basis.
100
101 UnderSMTPD reads /etc/aliases for usernames; if a match is identified
102 the message is piped to the given program.  Any problems executing the program
103 will cause a temporary SMTP error to be returned to the connecting client.
104
105 Other kinds of aliases are not recognized and cause a perminant SMTP error
106 to be returned to the connecting client, as do usernames not found in
107 /etc/aliases
108
109 UnderSMTP was originally written to be used with the Request Tracker ticketing
110 system.
111
112 UnderSMTP uses Net::SMTP::Mail to do all the hard work.
113
114 =head1 OPTIONS
115
116 =over 4
117
118 =item -a filename: Alternate aliases file
119
120 =back
121
122 =head1 ALIASES FORMAT
123
124   username: |program and args
125   username: "|program and args"
126
127 Quotes are not necessary around the pipe symbol, program and arguments but are
128 stripped if present.  Line continuations are not supported.
129
130 =head1 RT ALIASES EXAMPLE
131
132   support: |/opt/rt3/bin/rt-mailgate --queue support --action correspond --url http://rt.example.com/
133   billing: |/opt/rt3/bin/rt-mailgate --queue billing --action correspond --url http://rt.example.com/
134
135 =head1 BUGS
136
137 Yes.
138
139 =head1 AUTHOR
140
141 Ivan Kohler <ivan-undersmtpd@420.am>
142
143 =head1 SEE ALSO
144
145 L<Net::SMTP::Mail>
146
147 =cut
148
149 1;
150