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