1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.james.smtpserver;
23
24 import org.apache.avalon.framework.container.ContainerUtil;
25 import org.apache.james.Constants;
26 import org.apache.james.socket.CRLFTerminatedReader;
27 import org.apache.james.socket.ProtocolHandler;
28 import org.apache.james.socket.ProtocolHandlerHelper;
29 import org.apache.james.util.watchdog.Watchdog;
30 import org.apache.mailet.Mail;
31 import org.apache.mailet.base.RFC822DateFormat;
32
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Date;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Locale;
41 import java.util.Map;
42 import java.util.Random;
43
44
45
46
47
48
49
50 public class SMTPHandler implements ProtocolHandler, SMTPSession {
51
52 private ProtocolHandlerHelper helper;
53
54
55
56
57 private final static byte COMMAND_MODE = 1;
58 private final static byte RESPONSE_MODE = 2;
59 private final static byte MESSAGE_RECEIVED_MODE = 3;
60 private final static byte MESSAGE_ABORT_MODE = 4;
61
62
63
64
65 private final static String SOFTWARE_TYPE = "JAMES SMTP Server "
66 + Constants.SOFTWARE_VERSION;
67
68
69
70
71 private final static Random random = new Random();
72
73
74
75
76 private final static RFC822DateFormat rfc822DateFormat = new RFC822DateFormat();
77
78
79
80
81 String curCommandName = null;
82
83
84
85
86 String curCommandArgument = null;
87
88
89
90
91 SMTPHandlerChain handlerChain = null;
92
93
94
95
96
97 private byte mode;
98
99
100
101
102 private Mail mail = null;
103
104
105
106
107 private boolean sessionEnded = false;
108
109
110
111
112 private String authenticatedUser;
113
114
115
116
117 private boolean authRequired;
118
119
120
121
122 private boolean relayingAllowed;
123
124
125
126
127 private boolean heloEhloEnforcement;
128
129
130
131
132
133 private String smtpGreeting = null;
134
135
136
137
138 private String smtpID;
139
140
141
142
143 private SMTPHandlerConfigurationData theConfigData;
144
145
146
147
148
149
150
151
152 private HashMap state = new HashMap();
153
154
155
156
157 private HashMap connectionState = new HashMap();
158
159
160
161
162 private StringBuffer responseBuffer = new StringBuffer(256);
163
164 private boolean stopHandlerProcessing = false;
165
166
167
168
169
170
171 public void setConfigurationData(Object theData) {
172 if (theData instanceof SMTPHandlerConfigurationData) {
173 theConfigData = (SMTPHandlerConfigurationData) theData;
174 } else {
175 throw new IllegalArgumentException("Configuration object does not implement SMTPHandlerConfigurationData");
176 }
177 }
178
179
180
181
182 public void handleProtocol() throws IOException {
183 smtpID = random.nextInt(1024) + "";
184 relayingAllowed = theConfigData.isRelayingAllowed(helper.getRemoteIP());
185 authRequired = theConfigData.isAuthRequired(helper.getRemoteIP());
186 heloEhloEnforcement = theConfigData.useHeloEhloEnforcement();
187 sessionEnded = false;
188 smtpGreeting = theConfigData.getSMTPGreeting();
189 resetState();
190 resetConnectionState();
191
192
193 if (smtpGreeting == null) {
194
195
196
197 responseBuffer.append("220 ")
198 .append(theConfigData.getHelloName())
199 .append(" SMTP Server (")
200 .append(SOFTWARE_TYPE)
201 .append(") ready ")
202 .append(rfc822DateFormat.format(new Date()));
203 } else {
204 responseBuffer.append("220 ")
205 .append(smtpGreeting);
206 }
207 String responseString = clearResponseBuffer();
208 helper.writeLoggedFlushedResponse(responseString);
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236 List connectHandlers = handlerChain.getConnectHandlers();
237 if(connectHandlers != null) {
238 int count = connectHandlers.size();
239 for(int i = 0; i < count; i++) {
240 ((ConnectHandler)connectHandlers.get(i)).onConnect(this);
241 if(sessionEnded) {
242 break;
243 }
244 }
245 }
246
247 helper.getWatchdog().start();
248 while(!sessionEnded) {
249
250 curCommandName = null;
251 curCommandArgument = null;
252 mode = COMMAND_MODE;
253
254
255 String cmdString = readCommandLine();
256 if (cmdString == null) {
257 break;
258 }
259 int spaceIndex = cmdString.indexOf(" ");
260 if (spaceIndex > 0) {
261 curCommandName = cmdString.substring(0, spaceIndex);
262 curCommandArgument = cmdString.substring(spaceIndex + 1);
263 } else {
264 curCommandName = cmdString;
265 }
266 curCommandName = curCommandName.toUpperCase(Locale.US);
267
268
269 List commandHandlers = handlerChain.getCommandHandlers(curCommandName);
270 if(commandHandlers == null) {
271
272 break;
273 } else {
274 int count = commandHandlers.size();
275 for(int i = 0; i < count; i++) {
276 setStopHandlerProcessing(false);
277 ((CommandHandler)commandHandlers.get(i)).onCommand(this);
278
279 helper.getWatchdog().reset();
280
281
282 if(mode != COMMAND_MODE || getStopHandlerProcessing()) {
283 break;
284 }
285 }
286
287 }
288
289
290 if(mode == MESSAGE_RECEIVED_MODE) {
291 try {
292 helper.getAvalonLogger().debug("executing message handlers");
293 List messageHandlers = handlerChain.getMessageHandlers();
294 int count = messageHandlers.size();
295 for(int i =0; i < count; i++) {
296 ((MessageHandler)messageHandlers.get(i)).onMessage(this);
297
298 if(mode == MESSAGE_ABORT_MODE) {
299 break;
300 }
301 }
302 } finally {
303
304 if(mail != null) {
305 ContainerUtil.dispose(mail);
306
307
308 Object currentHeloMode = state.get(CURRENT_HELO_MODE);
309
310 mail = null;
311 resetState();
312
313
314 if (currentHeloMode != null) {
315 state.put(CURRENT_HELO_MODE,currentHeloMode);
316 }
317 }
318 }
319 }
320 }
321 helper.getWatchdog().stop();
322 helper.getAvalonLogger().debug("Closing socket.");
323 }
324
325
326
327
328 public void resetHandler() {
329 resetState();
330 resetConnectionState();
331
332 clearResponseBuffer();
333
334 authenticatedUser = null;
335 smtpID = null;
336 }
337
338
339
340
341
342
343 public void setHandlerChain(SMTPHandlerChain handlerChain) {
344 this.handlerChain = handlerChain;
345 }
346
347
348
349
350 public void writeResponse(String respString) {
351 helper.writeLoggedFlushedResponse(respString);
352
353 if(mode == COMMAND_MODE) {
354 mode = RESPONSE_MODE;
355 }
356 }
357
358
359
360
361 public String getCommandName() {
362 return curCommandName;
363 }
364
365
366
367
368 public String getCommandArgument() {
369 return curCommandArgument;
370 }
371
372
373
374
375 public Mail getMail() {
376 return mail;
377 }
378
379
380
381
382 public void setMail(Mail mail) {
383 this.mail = mail;
384 this.mode = MESSAGE_RECEIVED_MODE;
385 }
386
387
388
389
390 public String getRemoteHost() {
391 return helper.getRemoteHost();
392 }
393
394
395
396
397 public String getRemoteIPAddress() {
398 return helper.getRemoteIP();
399 }
400
401
402
403
404 public void endSession() {
405 sessionEnded = true;
406 }
407
408
409
410
411 public boolean isSessionEnded() {
412 return sessionEnded;
413 }
414
415
416
417
418 public void resetState() {
419 ArrayList recipients = (ArrayList)state.get(RCPT_LIST);
420 if (recipients != null) {
421 recipients.clear();
422 }
423 state.clear();
424 }
425
426
427
428
429 public Map getState() {
430 return state;
431 }
432
433
434
435
436 public SMTPHandlerConfigurationData getConfigurationData() {
437 return theConfigData;
438 }
439
440
441
442 public boolean isRelayingAllowed() {
443 return relayingAllowed;
444 }
445
446
447
448
449 public void setRelayingAllowed(boolean relayingAllowed) {
450 this.relayingAllowed = relayingAllowed;
451 }
452
453
454
455
456 public boolean isAuthRequired() {
457 return authRequired;
458 }
459
460
461
462
463 public boolean useHeloEhloEnforcement() {
464 return heloEhloEnforcement;
465 }
466
467
468
469 public String getUser() {
470 return authenticatedUser;
471 }
472
473
474
475
476 public void setUser(String userID) {
477 authenticatedUser = userID;
478 }
479
480
481
482
483 public StringBuffer getResponseBuffer() {
484 return responseBuffer;
485 }
486
487
488
489
490 public String clearResponseBuffer() {
491 String responseString = responseBuffer.toString();
492 responseBuffer.delete(0,responseBuffer.length());
493 return responseString;
494 }
495
496
497
498
499
500 public final String readCommandLine() throws IOException {
501 for (;;) try {
502 String commandLine = helper.getInputReader().readLine();
503 if (commandLine != null) {
504 commandLine = commandLine.trim();
505 }
506 return commandLine;
507 } catch (CRLFTerminatedReader.TerminationException te) {
508 helper.writeLoggedFlushedResponse("501 Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired. See RFC 2821 #2.7.1.");
509 } catch (CRLFTerminatedReader.LineLengthExceededException llee) {
510 helper.writeLoggedFlushedResponse("500 Line length exceeded. See RFC 2821 #4.5.3.1.");
511 }
512 }
513
514
515
516
517 public Watchdog getWatchdog() {
518 return helper.getWatchdog();
519 }
520
521
522
523
524 public InputStream getInputStream() {
525 return helper.getInputStream();
526 }
527
528
529
530
531 public String getSessionID() {
532 return smtpID;
533 }
534
535
536
537
538 public void abortMessage() {
539 mode = MESSAGE_ABORT_MODE;
540 }
541
542
543
544
545 public int getRcptCount() {
546 int count = 0;
547
548
549 if (state.get(SMTPSession.RCPT_LIST) != null) {
550 count = ((Collection) state.get(SMTPSession.RCPT_LIST)).size();
551 }
552
553 return count;
554 }
555
556
557
558
559 public void setStopHandlerProcessing(boolean stopHandlerProcessing) {
560 this.stopHandlerProcessing = stopHandlerProcessing;
561 }
562
563
564
565
566 public boolean getStopHandlerProcessing() {
567 return stopHandlerProcessing;
568 }
569
570 public void resetConnectionState() {
571 connectionState.clear();
572 }
573
574 public Map getConnectionState() {
575 return connectionState;
576 }
577
578 public void setProtocolHandlerHelper(ProtocolHandlerHelper phh) {
579 this.helper = phh;
580 }
581
582 public void errorHandler(RuntimeException e) {
583 helper.defaultErrorHandler(e);
584 }
585
586 }