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.pop3server;
23
24 import org.apache.avalon.framework.container.ContainerUtil;
25 import org.apache.james.Constants;
26 import org.apache.james.core.MailImpl;
27 import org.apache.james.services.MailRepository;
28 import org.apache.james.socket.CRLFTerminatedReader;
29 import org.apache.james.socket.ProtocolHandler;
30 import org.apache.james.socket.ProtocolHandlerHelper;
31 import org.apache.james.util.watchdog.Watchdog;
32 import org.apache.mailet.Mail;
33
34 import java.io.IOException;
35 import java.io.OutputStream;
36 import java.util.ArrayList;
37 import java.util.HashMap;
38 import java.util.Iterator;
39 import java.util.List;
40 import java.util.Locale;
41
42
43
44
45
46 public class POP3Handler implements POP3Session, ProtocolHandler {
47
48 private ProtocolHandlerHelper helper;
49
50 private final static byte COMMAND_MODE = 1;
51 private final static byte RESPONSE_MODE = 2;
52
53
54 private static final String softwaretype = "JAMES POP3 Server "
55 + Constants.SOFTWARE_VERSION;
56
57
58 final static String OK_RESPONSE = "+OK";
59
60
61 final static String ERR_RESPONSE = "-ERR";
62
63
64
65
66
67
68 final static int AUTHENTICATION_READY = 0;
69
70 final static int AUTHENTICATION_USERSET = 1;
71
72
73 final static int TRANSACTION = 2;
74
75
76
77
78 static final Mail DELETED = new MailImpl();
79
80
81
82
83
84
85
86
87
88 private POP3HandlerConfigurationData theConfigData;
89
90
91
92
93 private MailRepository userInbox;
94
95
96
97
98 private int handlerState;
99
100
101
102
103
104
105 private List userMailbox = new ArrayList();
106
107 private List backupUserMailbox;
108
109
110
111
112
113
114 private StringBuffer responseBuffer = new StringBuffer(256);
115
116
117
118
119
120 String curCommandName = null;
121
122
123
124
125 String curCommandArgument = null;
126
127
128
129
130 POP3HandlerChain handlerChain = null;
131
132
133
134
135 private boolean sessionEnded = false;
136
137
138
139
140
141
142
143
144
145 private HashMap state = new HashMap();
146
147
148
149
150 private String authenticatedUser;
151
152
153
154
155 private byte mode;
156
157
158
159
160
161
162
163 public void setConfigurationData(Object theData) {
164 if (theData instanceof POP3HandlerConfigurationData) {
165 theConfigData = (POP3HandlerConfigurationData) theData;
166 } else {
167 throw new IllegalArgumentException("Configuration object does not implement POP3HandlerConfigurationData");
168 }
169 }
170
171
172
173
174 public void handleProtocol() throws IOException {
175 handlerState = AUTHENTICATION_READY;
176 authenticatedUser = "unknown";
177
178 sessionEnded = false;
179 resetState();
180
181
182
183 responseBuffer.append(OK_RESPONSE)
184 .append(" ")
185 .append(theConfigData.getHelloName())
186 .append(" POP3 server (")
187 .append(POP3Handler.softwaretype)
188 .append(") ready ");
189 String responseString = clearResponseBuffer();
190 helper.writeLoggedFlushedResponse(responseString);
191
192
193 List connectHandlers = handlerChain.getConnectHandlers();
194 if(connectHandlers != null) {
195 int count = connectHandlers.size();
196 for(int i = 0; i < count; i++) {
197 ((ConnectHandler)connectHandlers.get(i)).onConnect(this);
198 if(sessionEnded) {
199 break;
200 }
201 }
202 }
203
204
205 helper.getWatchdog().start();
206 while(!sessionEnded) {
207
208 curCommandName = null;
209 curCommandArgument = null;
210 mode = COMMAND_MODE;
211
212
213 String cmdString = readCommandLine();
214 if (cmdString == null) {
215 break;
216 }
217 int spaceIndex = cmdString.indexOf(" ");
218 if (spaceIndex > 0) {
219 curCommandName = cmdString.substring(0, spaceIndex);
220 curCommandArgument = cmdString.substring(spaceIndex + 1);
221 } else {
222 curCommandName = cmdString;
223 }
224 curCommandName = curCommandName.toUpperCase(Locale.US);
225
226 if (helper.getAvalonLogger().isDebugEnabled()) {
227
228 if (!curCommandName.equals("PASS")) {
229 helper.getAvalonLogger().debug("Command received: " + cmdString);
230 } else {
231 helper.getAvalonLogger().debug("Command received: PASS <password omitted>");
232 }
233 }
234
235
236 List commandHandlers = handlerChain.getCommandHandlers(curCommandName);
237 if(commandHandlers == null) {
238
239 break;
240 } else {
241 int count = commandHandlers.size();
242 for(int i = 0; i < count; i++) {
243 ((CommandHandler)commandHandlers.get(i)).onCommand(this);
244 helper.getWatchdog().reset();
245
246 if(mode != COMMAND_MODE) {
247 break;
248 }
249 }
250
251 }
252 }
253 helper.getWatchdog().stop();
254 if (helper.getAvalonLogger().isInfoEnabled()) {
255 StringBuffer logBuffer =
256 new StringBuffer(128)
257 .append("Connection for ")
258 .append(getUser())
259 .append(" from ")
260 .append(helper.getRemoteHost())
261 .append(" (")
262 .append(helper.getRemoteIP())
263 .append(") closed.");
264 helper.getAvalonLogger().info(logBuffer.toString());
265 }
266 }
267
268
269
270
271 public void errorHandler(RuntimeException e) {
272 helper.defaultErrorHandler(e);
273 try {
274 helper.getOutputWriter().println(ERR_RESPONSE + " Error closing connection.");
275 helper.getOutputWriter().flush();
276 } catch (Throwable t) {
277
278 }
279 }
280
281
282
283
284 public void resetHandler() {
285
286 authenticatedUser = null;
287 userInbox = null;
288 if (userMailbox != null) {
289 Iterator i = userMailbox.iterator();
290 while (i.hasNext()) {
291 ContainerUtil.dispose(i.next());
292 }
293 userMailbox.clear();
294 userMailbox = null;
295 }
296
297 if (backupUserMailbox != null) {
298 Iterator i = backupUserMailbox.iterator();
299 while (i.hasNext()) {
300 ContainerUtil.dispose(i.next());
301 }
302 backupUserMailbox.clear();
303 backupUserMailbox = null;
304 }
305
306
307 theConfigData = null;
308 }
309
310
311
312
313
314
315
316 public final String readCommandLine() throws IOException {
317 for (;;) try {
318 String commandLine = helper.getInputReader().readLine();
319 if (commandLine != null) {
320 commandLine = commandLine.trim();
321 }
322 return commandLine;
323 } catch (CRLFTerminatedReader.TerminationException te) {
324 helper.writeLoggedFlushedResponse("-ERR Syntax error at character position " + te.position() + ". CR and LF must be CRLF paired. See RFC 1939 #3.");
325 }
326 }
327
328
329
330
331
332
333
334
335
336
337
338
339
340 public String getRemoteHost() {
341 return helper.getRemoteHost();
342 }
343
344
345
346
347 public String getRemoteIPAddress() {
348 return helper.getRemoteIP();
349 }
350
351
352
353
354 public void endSession() {
355 sessionEnded = true;
356 }
357
358
359
360
361 public boolean isSessionEnded() {
362 return sessionEnded;
363 }
364
365
366
367
368 public void resetState() {
369 state.clear();
370 }
371
372
373
374
375 public HashMap getState() {
376 return state;
377 }
378
379
380
381
382 public String getUser() {
383 return authenticatedUser;
384 }
385
386
387
388
389 public void setUser(String userID) {
390 authenticatedUser = userID;
391 }
392
393
394
395
396 public StringBuffer getResponseBuffer() {
397 return responseBuffer;
398 }
399
400
401
402
403 public String clearResponseBuffer() {
404 String responseString = responseBuffer.toString();
405 responseBuffer.delete(0,responseBuffer.length());
406 return responseString;
407 }
408
409
410
411
412 public Watchdog getWatchdog() {
413 return helper.getWatchdog();
414 }
415
416
417
418
419
420
421 public void setHandlerChain(POP3HandlerChain handlerChain) {
422 this.handlerChain = handlerChain;
423 }
424
425
426
427
428 public void writeResponse(String respString) {
429 helper.writeLoggedFlushedResponse(respString);
430
431 if(mode == COMMAND_MODE) {
432 mode = RESPONSE_MODE;
433 }
434 }
435
436
437
438
439 public String getCommandName() {
440 return curCommandName;
441 }
442
443
444
445
446 public String getCommandArgument() {
447 return curCommandArgument;
448 }
449
450
451
452
453 public POP3HandlerConfigurationData getConfigurationData() {
454 return theConfigData;
455 }
456
457
458
459
460 public int getHandlerState() {
461 return handlerState;
462 }
463
464
465
466
467 public void setHandlerState(int handlerState) {
468 this.handlerState = handlerState;
469 }
470
471
472
473
474 public MailRepository getUserInbox() {
475 return userInbox;
476 }
477
478
479
480
481 public void setUserInbox(MailRepository userInbox) {
482 this.userInbox = userInbox;
483 }
484
485
486
487
488 public List getUserMailbox() {
489 return userMailbox;
490 }
491
492
493
494
495 public void setUserMailbox(List userMailbox) {
496 this.userMailbox = userMailbox;
497 }
498
499
500
501
502 public List getBackupUserMailbox() {
503 return backupUserMailbox;
504 }
505
506
507
508
509
510 public void setBackupUserMailbox(List backupUserMailbox) {
511 this.backupUserMailbox = backupUserMailbox;
512 }
513
514
515
516
517 public OutputStream getOutputStream() {
518 return helper.getOutputStream();
519 }
520
521
522
523
524 public void setProtocolHandlerHelper(ProtocolHandlerHelper phh) {
525 this.helper = phh;
526 }
527
528 }