1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.mercury.smtp.command;
14
15 import java.io.BufferedReader;
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InputStreamReader;
20 import java.io.OutputStream;
21 import java.net.InetSocketAddress;
22 import java.net.Socket;
23 import java.net.SocketTimeoutException;
24 import java.text.SimpleDateFormat;
25 import java.util.ArrayList;
26 import java.util.Date;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Properties;
30
31 import javax.mail.Folder;
32 import javax.mail.Message;
33 import javax.mail.MessagingException;
34 import javax.mail.Session;
35 import javax.mail.internet.MimeMessage;
36
37 import org.abstracthorizon.mercury.common.command.CommandException;
38 import org.abstracthorizon.mercury.common.io.TempStorage;
39 import org.abstracthorizon.mercury.smtp.SMTPResponses;
40 import org.abstracthorizon.mercury.smtp.SMTPScanner;
41 import org.abstracthorizon.mercury.smtp.SMTPSession;
42 import org.abstracthorizon.mercury.smtp.exception.ParserException;
43 import org.abstracthorizon.mercury.smtp.util.Path;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47
48
49
50
51
52 public class DataCommand extends SMTPCommand {
53
54 protected static final byte[] CRLF = new byte[] { '\r', '\n' };
55
56 public static final SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z");
57
58 protected static final Logger logger = LoggerFactory.getLogger(DataCommand.class);
59
60 public static Session mailSession = Session.getDefaultInstance(new Properties());
61
62
63
64
65
66 public DataCommand() {
67 }
68
69
70
71
72
73
74
75
76 protected void execute(SMTPSession connection) throws CommandException, IOException, ParserException {
77 if (connection.getState() != SMTPSession.STATE_MAIL) {
78 connection.sendResponse(SMTPResponses.BAD_SEQUENCE_OF_COMMANDS_RESPONSE);
79 return;
80 }
81 try {
82 SMTPScanner scanner = connection.getScanner();
83
84 readExtraParameters(connection, scanner);
85
86 if (precheck(connection)) {
87 TempStorage tempStorage = new TempStorage();
88 OutputStream out = tempStorage.getOutputStream();
89 out.write(composeReceivedHeader(connection).getBytes());
90 out.write(CRLF);
91 try {
92 connection.setStreamDebug(false);
93 readMail(connection.getInputStream(), out);
94 out.close();
95 } catch (IOException e) {
96
97
98
99
100
101
102 if (!(e instanceof SocketTimeoutException)) {
103 logger.error("Problem reading message", e);
104 }
105 connection.sendResponse(SMTPResponses.GENERIC_ERROR_RESPONSE);
106 connection.getMailSessionData().addToTotalBytes(tempStorage.getSize());
107 tempStorage.clear();
108 tempStorage = null;
109 } finally {
110 connection.setStreamDebug(true);
111 }
112
113
114
115 if (tempStorage != null) {
116 try {
117 MimeMessage message = new MimeMessage(mailSession, tempStorage.getInputStream());
118 File file = tempStorage.getFile();
119 if (file != null) {
120 message.setFileName(file.getAbsolutePath());
121 }
122 connection.getMailSessionData().setMessage(message);
123 processMail(connection, message);
124 } catch (MessagingException e) {
125 logger.error("Problem creating message", e);
126 } finally {
127 connection.getMailSessionData().setMessage(null);
128 connection.getMailSessionData().addToTotalBytes(tempStorage.getSize());
129 tempStorage.clear();
130 }
131 }
132 }
133 } finally {
134 connection.setState(SMTPSession.STATE_READY);
135 }
136 }
137
138
139
140
141
142
143 protected String composeReceivedHeader(SMTPSession connection) throws IOException {
144 Date now = new Date();
145 StringBuffer received = new StringBuffer("Received:");
146 Socket socket = (Socket)connection.adapt(Socket.class);
147 if (socket != null) {
148 append(received, " from " + connection.getMailSessionData().getSourceDomain() + getTCPInfo(socket));
149 } else {
150 append(received, " from " + connection.getMailSessionData().getSourceDomain());
151 }
152 append(received, " by " + connection.getConnectionHandler().getStorageManager().getMainDomain());
153 append(received, " for " + composeDestMailboxes(connection.getMailSessionData().getDestinationMailboxes()));
154 append(received, "; " + format.format(now));
155 return received.toString();
156 }
157
158
159
160
161
162
163 protected String getTCPInfo(Socket socket) {
164 return " (["+((InetSocketAddress)socket.getRemoteSocketAddress()).getAddress().getHostAddress()+"])";
165 }
166
167
168
169
170
171
172 protected String composeDestMailboxes(List<Path> dest) {
173 StringBuffer buf = new StringBuffer();
174 int i = 0;
175 while ((i < dest.size()) && (i < 3)) {
176 Path path = dest.get(i);
177 if (i > 0) {
178 buf.append(',');
179 }
180 buf.append(path.getMailbox());
181 i = i + 1;
182 }
183 if (i < dest.size()) {
184 buf.append(",...");
185 }
186 return buf.toString();
187 }
188
189
190
191
192
193
194 protected void append(StringBuffer buf, String part) {
195
196 int i = buf.lastIndexOf("\r\n");
197 if (i < 0) {
198 i = 0;
199 } else {
200 i = i+2;
201 }
202 if (buf.length()-i+part.length() > 999) {
203 buf.append("\r\n ");
204 }
205 buf.append(part);
206 }
207
208
209
210
211
212
213
214
215
216
217 protected void readMail(InputStream in, OutputStream mail) throws IOException {
218
219 boolean first = true;
220 BufferedReader reader = new BufferedReader(new InputStreamReader(in, "ISO-8859-1"));
221
222 String line = reader.readLine();
223 while (line != null) {
224 if (first) {
225 first = false;
226 } else {
227 if (line.equals(".")) { return; }
228 }
229 if (line.startsWith(".")) {
230 line = line.substring(1);
231 }
232 mail.write(line.getBytes());
233 mail.write(CRLF);
234 line = reader.readLine();
235 }
236 }
237
238
239
240
241
242
243
244
245
246
247
248 protected void readExtraParameters(SMTPSession connection, SMTPScanner scanner) throws IOException, ParserException, CommandException {
249 scanner.check_eol();
250 }
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265 protected boolean precheck(SMTPSession connection) throws CommandException, IOException {
266 connection.sendResponse(SMTPResponses.START_DATA_RESPONSE);
267 return true;
268 }
269
270
271
272
273
274
275
276 protected boolean postcheck(SMTPSession connection) throws IOException {
277
278 connection.resetLastAccessed();
279 return true;
280 }
281
282
283
284
285
286
287
288 protected void postProcessing(SMTPSession connection, boolean hasSuccessfuls) throws IOException {
289 if (hasSuccessfuls) {
290 connection.sendResponse(SMTPResponses.OK_RESPONSE);
291 } else {
292
293 connection.sendResponse(SMTPResponses.MAILBOX_UNAVAILABLE_RESPONSE);
294 }
295 }
296
297
298
299
300
301
302
303
304 protected void processMail(SMTPSession connection, MimeMessage message) throws IOException {
305 if (postcheck(connection)) {
306 boolean hasSuccesfuls = false;
307 List<Path> externals = new ArrayList<Path>();
308 Iterator<Path> it = connection.getMailSessionData().getDestinationMailboxes().iterator();
309 while (it.hasNext()) {
310 Path path = it.next();
311 if (path.isLocalMailbox()) {
312 if (processLocalMailbox(connection, path, message)) {
313 hasSuccesfuls = true;
314 }
315 } else if (!path.isLocalDomain()) {
316 externals.add(path);
317 }
318 }
319
320 if (externals.size() > 0) {
321 processExternalMail(connection, externals, message);
322 }
323
324 postProcessing(connection, hasSuccesfuls);
325 }
326 }
327
328
329
330
331
332
333
334
335
336 protected boolean processLocalMailbox(SMTPSession connection, Path path, MimeMessage message) {
337 try {
338 Folder folder = path.getFolder();
339 folder.open(Folder.READ_WRITE);
340
341 if (message.getSentDate() == null) {
342 Date rd = message.getReceivedDate();
343 if (rd == null) {
344 rd = new Date(System.currentTimeMillis());
345 }
346 message.setSentDate(rd);
347 message.setHeader("Date", format.format(rd));
348 }
349
350 try {
351 folder.appendMessages(new Message[] { message });
352 } finally {
353 folder.close(false);
354 }
355 return true;
356 } catch (MessagingException e) {
357 logger.error("Storing problem", e);
358 }
359 return false;
360 }
361
362
363
364
365
366
367
368
369
370
371 protected boolean processExternalMail(SMTPSession connection, List<Path> externals, MimeMessage message) {
372
373 return false;
374 }
375
376 }