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