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