]> git.proxmox.com Git - pmg-api.git/blame - PMG/SMTP.pm
SMTPWhitelist: use better API paths
[pmg-api.git] / PMG / SMTP.pm
CommitLineData
14726412 1package PMG::SMTP;
73238bb3
DM
2
3use strict;
14726412 4use warnings;
73238bb3
DM
5use Carp;
6use IO::Socket;
73238bb3 7
14726412
DM
8use PVE::SafeSyslog;
9
10use PMG::MailQueue;
11
73238bb3
DM
12sub new {
13 my($this, $sock) = @_;
14726412 14
73238bb3
DM
15 my $class = ref($this) || $this;
16
17 croak("undefined socket: ERROR") if !defined($sock);
18
19 my $self = {};
20 $self->{sock} = $sock;
21 $self->{lmtp} = undef;
22 bless($self, $class);
23
24 $self->reset();
25
26 $self->reply ("220 Proxmox SMTP Ready.");
27 return $self;
28}
29
30sub reset {
31 my $self = shift;
32
33 $self->{from} = undef;
34 $self->{to} = [];
35 $self->{queue} = undef;
36 delete $self->{xforward};
37 delete $self->{status};
38}
39
40sub abort {
41 shift->{sock}->close();
42}
43
44sub reply {
45 print {shift->{sock}} @_, "\r\n";;
46
47}
48
49sub loop {
50 my ($self, $func, $data, $maxcount) = @_;
51
52 my($cmd, $args);
73238bb3 53
14726412 54 my $sock = $self->{sock};
73238bb3
DM
55
56 my $count = 0;
57
58 while(<$sock>) {
59 chomp;
60 s/^\s+//;
61 s/\s+$//;
62
63 if (!length ($_)) {
64 $self->reply ("500 5.5.1 Error: bad syntax");
65 next;
66 }
67 ($cmd, $args) = split(/\s+/, $_, 2);
68 $cmd = lc ($cmd);
69
70 if ($cmd eq 'helo' || $cmd eq 'ehlo' || $cmd eq 'lhlo') {
71 $self->reset();
72
73238bb3
DM
73 $self->reply ("250-PIPELINING");
74 $self->reply ("250-ENHANCEDSTATUSCODES");
75 $self->reply ("250-8BITMIME");
76 $self->reply ("250-XFORWARD NAME ADDR PROTO HELO");
77 $self->reply ("250 OK.");
78 $self->{lmtp} = 1 if ($cmd eq 'lhlo');
79 next;
80 } elsif ($cmd eq 'xforward') {
81 my @tmp = split (/\s+/, $args);
82 foreach my $attr (@tmp) {
83 my ($n, $v) = ($attr =~ /^(.*?)=(.*)$/);
84 $self->{xforward}->{lc($n)} = $v;
85 }
86 $self->reply ("250 2.5.0 OK");
87 next;
88 } elsif ($cmd eq 'noop') {
89 $self->reply ("250 2.5.0 OK");
90 next;
91 } elsif ($cmd eq 'quit') {
92 $self->reply ("221 2.2.0 OK");
93 last;
94 } elsif ($cmd eq 'rset') {
95 $self->reset();
96 $self->reply ("250 2.5.0 OK");
97 next;
98 } elsif ($cmd eq 'mail') {
99 if ($args =~ m/^from:\s*<(\S*)>/i) {
100 delete $self->{to};
101 $self->{from} = $1;
102 $self->reply ('250 2.5.0 OK');
103 next;
104 } else {
105 $self->reply ("501 5.5.2 Syntax: MAIL FROM: <address>");
106 next;
107 }
108 } elsif ($cmd eq 'rcpt') {
109 if ($args =~ m/^to:\s*<(\S+)>/i) {
110 push @{$self->{to}} , $1;
111 $self->reply ('250 2.5.0 OK');
112 next;
113 } else {
114 $self->reply ("501 5.5.2 Syntax: MAIL FROM: <address>");
115 next;
116 }
117 } elsif ($cmd eq 'data') {
118 if ($self->save_data ()) {
119 eval { &$func ($data, $self); };
9d04575a 120 if (my $err = $@) {
73238bb3 121 $data->{errors} = 1;
9d04575a 122 syslog ('err', $err);
73238bb3
DM
123 }
124
125 if ($self->{lmtp}) {
126 foreach $a (@{$self->{to}}) {
127 if ($self->{queue}->{status}->{$a} eq 'delivered') {
128 $self->reply ("250 2.5.0 OK ($self->{queue}->{logid})");
129 } elsif ($self->{queue}->{status}->{$a} eq 'blocked') {
130 $self->reply ("250 2.7.0 BLOCKED ($self->{queue}->{logid})");
131 } elsif ($self->{queue}->{status}->{$a} eq 'error') {
132 my $code = $self->{queue}->{status_code}->{$a};
133 my $resp = substr($code, 0, 1);
134 my $mess = $self->{queue}->{status_message}->{$a};
135 $self->reply ("$code $resp.0.0 $mess");
136 } else {
137 $self->reply ("451 4.4.0 detected undelivered mail to <$a>");
138 }
139 }
140 } else {
141 my $all_done = 1;
142
143 foreach $a (@{$self->{to}}) {
144 if (!($self->{queue}->{status}->{$a} eq 'delivered' ||
145 $self->{queue}->{status}->{$a} eq 'blocked')) {
146 $all_done = 0;
147 }
148 }
149 if ($all_done) {
150 $self->reply ("250 2.5.0 OK ($self->{queue}->{logid})");
151 } else {
152 $self->reply ("451 4.4.0 detected undelivered mail");
153 }
154 }
155 }
156
157 $self->reset();
158
159 $count++;
160 last if $count >= $maxcount;
161 last if $data->{errors}; # abort if we find errors
162 next;
163 }
14726412 164
73238bb3
DM
165 $self->reply ("500 5.5.1 Error: unknown command");
166 }
167
168 $self->{sock}->close;
169 return $count;
170}
171
172sub save_data {
173 my $self = shift;
174 my $done = undef;
14726412 175
73238bb3
DM
176 if(!defined($self->{from})) {
177 $self->reply ("503 5.5.1 Tell me who you are.");
178 return 0;
179 }
14726412 180
73238bb3
DM
181 if(!defined($self->{to})) {
182 $self->reply ("503 5.5.1 Tell me who to send it.");
183 return 0;
184 }
185
186 $self->reply ("354 End data with <CR><LF>.<CR><LF>");
187
188 my $sock = $self->{sock};
189
190 my $queue;
191
192 eval {
14726412
DM
193 $queue = PMG::MailQueue->new ($self->{from}, $self->{to});
194
73238bb3
DM
195 while(<$sock>) {
196
197 if(/^\.\015\012$/) {
198 $done = 1;
199 last;
200 }
14726412 201
73238bb3
DM
202 # RFC 2821 compliance.
203 s/^\.\./\./;
204
14726412 205 s/\015\012/\n/;
73238bb3
DM
206
207 print {$queue->{fh}} $_;
208 $queue->{bytes} += length ($_);
209 }
210
211 $queue->{fh}->flush ();
212
14726412 213 $self->{queue} = $queue;
73238bb3 214 };
9d04575a
DM
215 if (my $err = $@) {
216 syslog ('err', $err);
217 $self->reply ("451 4.5.0 Local delivery failed: $err");
73238bb3
DM
218 return 0;
219 }
220 if(!defined($done)) {
221 $self->reply ("451 4.5.0 Local delivery failed: unfinished data");
222 return 0;
223 }
224
225 return 1;
226}
227
2281;