1
2
3
4
5
6
7
8
9
10
11
12
13 package org.abstracthorizon.mercury.imap;
14
15
16 import java.io.EOFException;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.io.InterruptedIOException;
20 import java.net.Socket;
21 import java.util.concurrent.Executor;
22
23 import javax.mail.Session;
24 import javax.mail.Store;
25
26 import org.abstracthorizon.danube.connection.Connection;
27 import org.abstracthorizon.danube.connection.ConnectionException;
28 import org.abstracthorizon.danube.connection.ConnectionHandler;
29 import org.abstracthorizon.mercury.common.StorageManager;
30 import org.abstracthorizon.mercury.common.command.CommandException;
31 import org.abstracthorizon.mercury.imap.cmd.IMAPCommandFactory;
32 import org.abstracthorizon.mercury.imap.cmd.UIDCommand;
33 import org.abstracthorizon.mercury.imap.response.BADResponse;
34 import org.abstracthorizon.mercury.imap.response.ByeResponse;
35 import org.abstracthorizon.mercury.imap.response.NOResponse;
36 import org.abstracthorizon.mercury.imap.response.OKResponse;
37 import org.abstracthorizon.mercury.imap.response.Response;
38 import org.abstracthorizon.mercury.imap.util.IMAPScanner;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43
44
45
46
47 public class IMAPConnectionHandler implements ConnectionHandler {
48
49
50 protected final Logger logger = LoggerFactory.getLogger(getClass());
51
52
53 protected StorageManager storageManager;
54
55
56 protected Store store;
57
58
59 protected IMAPCommandFactory factory = new IMAPCommandFactory();
60
61
62 protected Session session;
63
64
65 protected Executor threadPool;
66
67
68 protected char[] passPhrase;
69
70
71 protected boolean allowInsecure = true;
72
73
74
75
76 public IMAPConnectionHandler() {
77 }
78
79
80
81
82
83 public void setStorageManager(StorageManager storageManager) {
84 this.storageManager = storageManager;
85 }
86
87
88
89
90
91 public StorageManager getStorageManager() {
92 return storageManager;
93 }
94
95
96
97
98
99 public IMAPCommandFactory getFactory() {
100 return factory;
101 }
102
103
104
105
106
107 public Session getJavaMailSession() {
108 return session;
109 }
110
111
112
113
114
115 public void setJavaMailSession(Session session) {
116 this.session = session;
117 }
118
119
120
121
122
123 public void setThreadPool(Executor executor) {
124 this.threadPool = executor;
125 }
126
127
128
129
130
131 public Executor getThreadPool() {
132 return threadPool;
133 }
134
135
136
137
138
139 public InputStream getKeyStoreInputStream() {
140 return Thread.currentThread().getContextClassLoader().getResourceAsStream("META-INF/keystore");
141 }
142
143
144
145
146
147 public void setPassPhrase(char[] passPhrase) {
148 this.passPhrase = passPhrase;
149 }
150
151
152
153
154
155 public char[] getPassPhrase() {
156 return passPhrase;
157 }
158
159
160
161
162
163 public boolean isInsecureAllowed() {
164 return allowInsecure;
165 }
166
167
168
169
170
171 public void setInsecureAllowed(boolean allowInsecure) {
172 this.allowInsecure = allowInsecure;
173 }
174
175
176
177
178
179 public void handleConnection(Connection connection) {
180 IMAPSession imapConnection = new IMAPSession(connection, this);
181
182 try {
183 try {
184 try {
185 new OKResponse(imapConnection, Response.UNTAGGED_RESPONSE, "Service Ready").submit();
186 boolean persistConnection = true;
187 while (persistConnection) {
188 IMAPScanner scanner = imapConnection.getScanner();
189
190 processInput(imapConnection);
191
192 Socket socket = (Socket)connection.adapt(Socket.class);
193 persistConnection = (socket != null) && !socket.isInputShutdown() && !socket.isOutputShutdown();
194
195 if (((socket == null) || socket.isConnected()) && !imapConnection.isCleanInput()) {
196 scanner.skip_line();
197 imapConnection.setCleanInput(true);
198 }
199 }
200 } catch (ConnectionException e) {
201 Throwable w = e.getCause();
202 if (w != null) {
203 throw w;
204 }
205 }
206 } catch (InterruptedIOException e) {
207 imapConnection.setKeepLog(true);
208 imapConnection.writeLogMessage("Closing because of inactivity");
209 new ByeResponse(imapConnection);
210 } catch (EOFException e) {
211
212 } catch (IOException e) {
213 Socket socket = (Socket)connection.adapt(Socket.class);
214 if ((socket != null) && socket.isConnected()) {
215 logger.error("End of session exception: ", e);
216 }
217 } catch (Throwable t) {
218 logger.error("Got problem: ", t);
219 }
220 } finally {
221 imapConnection.close();
222 }
223 }
224
225
226
227
228
229
230 public void processInput(IMAPSession imapConnection) throws IOException {
231 imapConnection.setCleanInput(false);
232
233 IMAPScanner scanner = imapConnection.getScanner();
234
235 StringBuffer tagBuffer = new StringBuffer();
236 try {
237 if (!scanner.tag(tagBuffer)) {
238 new BADResponse(imapConnection, Response.UNTAGGED_RESPONSE, "tag is missing").submit();
239 }
240 } catch (IOException e) {
241 e.printStackTrace();
242
243 imapConnection.close();
244 return;
245 }
246 String tag = tagBuffer.toString();
247 imapConnection.setTag(tag);
248 if (!scanner.is_char(' ')) {
249 new BADResponse(imapConnection, "space after tag is missing").submit();
250 }
251 if (!imapConnection.isAuthorised()) {
252
253 if (commandNonAuth(imapConnection)) {
254 } else if (commandAny(imapConnection)) {
255 } else {
256 new BADResponse(imapConnection, "Command not recognised or not allowed in non-authorised mode.").submit();
257 }
258 } else if (imapConnection.getSelectedFolder() == null) {
259
260 if (commandAuth(imapConnection)){
261 } else if (commandAny(imapConnection)) {
262 } else {
263 new BADResponse(imapConnection, "Command not recognised or not allowed in not selected mode.").submit();
264 }
265 } else {
266
267 if (commandSelected(imapConnection)) {
268 } else if (commandAuth(imapConnection)) {
269 } else if (commandAny(imapConnection)) {
270 } else {
271 new BADResponse(imapConnection, "Command not recognised or not allowed in non-authorised mode.").submit();
272 }
273 }
274 }
275
276
277
278
279
280
281
282 public void invokeCommand(IMAPSession imapConnection, String name) throws IOException {
283 invokeCommand(imapConnection, name, false);
284 }
285
286
287
288
289
290
291
292
293 public void invokeCommand(IMAPSession imapConnection, String name, boolean uid) throws IOException {
294 IMAPScanner scanner = imapConnection.getScanner();
295 if (scanner.is_char(' ') || scanner.peek_char('\r')) {
296 try {
297 ConnectionHandler command = factory.getCommand(name);
298
299 if (uid && (command instanceof UIDCommand)) {
300 ((UIDCommand)command).setAsUID();
301 }
302 command.handleConnection(imapConnection);
303 } catch (NOCommandException e) {
304 logger.debug("NO: ", e);
305 new NOResponse(imapConnection, name+" "+e.getMessage()).submit();
306 imapConnection.setKeepLog(true);
307 } catch (BADCommandException e) {
308 logger.error("BAD: ", e);
309 new BADResponse(imapConnection, name+" "+e.getMessage()).submit();
310 imapConnection.setKeepLog(true);
311 } catch (CommandException e) {
312 logger.error("UNEXPECTED: ", e);
313 new BADResponse(imapConnection, e.getMessage()).submit();
314 imapConnection.setKeepLog(true);
315 }
316 } else {
317 new BADResponse(imapConnection, "Missing space after the command").submit();
318 }
319 }
320
321
322
323
324
325
326
327 public boolean commandAny(IMAPSession imapConnection) throws IOException {
328 IMAPScanner scanner = imapConnection.getScanner();
329 if (scanner.keyword("CAPABILITY")) {
330 invokeCommand(imapConnection, "CAPABILITY");
331 return true;
332 } else if (scanner.keyword("LOGOUT")) {
333 invokeCommand(imapConnection, "LOGOUT");
334 return true;
335 } else if (scanner.keyword("NOOP")) {
336 invokeCommand(imapConnection, "NOOP");
337 return true;
338 } else {
339 return false;
340 }
341 }
342
343
344
345
346
347
348
349 public boolean commandNonAuth(IMAPSession imapConnection) throws IOException {
350 IMAPScanner scanner = imapConnection.getScanner();
351 if (scanner.keyword("LOGIN")) {
352 invokeCommand(imapConnection, "LOGIN");
353 } else if (scanner.keyword("AUTHENTICATE")) {
354 invokeCommand(imapConnection, "AUTHENTICATE");
355 } else if (scanner.keyword("STARTTLS")) {
356 invokeCommand(imapConnection, "STARTTLS");
357 } else {
358 return false;
359 }
360 return true;
361 }
362
363
364
365
366
367
368
369 public boolean commandAuth(IMAPSession imapConnection) throws IOException {
370 IMAPScanner scanner = imapConnection.getScanner();
371 if (scanner.keyword("APPEND")) {
372 invokeCommand(imapConnection, "APPEND");
373 } else if (scanner.keyword("CREATE")) {
374 invokeCommand(imapConnection, "CREATE");
375 } else if (scanner.keyword("DELETE")) {
376 invokeCommand(imapConnection, "DELETE");
377 } else if (scanner.keyword("EXAMINE")) {
378 invokeCommand(imapConnection, "EXAMINE");
379 } else if (scanner.keyword("LIST")) {
380 invokeCommand(imapConnection, "LIST");
381 } else if (scanner.keyword("LSUB")) {
382 invokeCommand(imapConnection, "LSUB");
383 } else if (scanner.keyword("RENAME")) {
384 invokeCommand(imapConnection, "RENAME");
385 } else if (scanner.keyword("SELECT")) {
386 invokeCommand(imapConnection, "SELECT");
387 } else if (scanner.keyword("STATUS")) {
388 invokeCommand(imapConnection, "STATUS");
389 } else if (scanner.keyword("SUBSCRIBE")) {
390 invokeCommand(imapConnection, "SUBSCRIBE");
391 } else if (scanner.keyword("UNSUBSCRIBE")) {
392 invokeCommand(imapConnection, "UNSUBSCRIBE");
393 } else {
394 return false;
395 }
396 return true;
397 }
398
399
400
401
402
403
404
405 public boolean commandSelected(IMAPSession imapConnection) throws IOException {
406 IMAPScanner scanner = imapConnection.getScanner();
407 if (scanner.keyword("CHECK")) {
408 invokeCommand(imapConnection, "CHECK");
409 } else if (scanner.keyword("CLOSE")) {
410 invokeCommand(imapConnection, "CLOSE");
411 } else if (scanner.keyword("EXPUNGE")) {
412 invokeCommand(imapConnection, "EXPUNGE");
413 } else if (scanner.keyword("COPY")) {
414 invokeCommand(imapConnection, "COPY");
415 } else if (scanner.keyword("FETCH")) {
416 invokeCommand(imapConnection, "FETCH");
417 } else if (scanner.keyword("STORE")) {
418 invokeCommand(imapConnection, "STORE");
419 } else if (scanner.keyword("UID")) {
420 invokeCommand(imapConnection, "UID");
421 } else if (scanner.keyword("SEARCH")) {
422 invokeCommand(imapConnection, "SEARCH");
423 } else if (scanner.keyword("IDLE")) {
424 invokeCommand(imapConnection, "IDLE");
425 } else {
426 return false;
427 }
428 return true;
429 }
430 }