0074285c8ebfd40f6628782978f3b3e78c284335
[org.ibex.mail.git] / src / org / ibex / mail / protocol / SMTP.java
1 package org.ibex.mail.protocol;
2 public class SMTP extends MessageProtocol {
3
4     public SMTP() { setProtocolName("SMTP"); }
5     public ServerRequest createRequest(Connection conn) { return new Request((TcpConnection)conn); }
6
7     public static class Outgoing {
8         //  recommended retry interval is 30 minutes
9         //  give up after 4-5 days
10         //  should keep per-host success/failure so we don't retry on every message
11         //  exponential backoff on retry time?
12         //  check DNS resolvability as soon as domain is provided
13         //    only use implicit A-record if there are no MX-records
14         //  use null-sender for error messages (don't send errors to the null addr)
15         //  to prevent mail loops, drop messages with >100 Recieved headers
16         private final Queue queue = new Queue();
17         public static void send(Message m) { }
18         public static void enqueue(Message m) { }
19         public static void bounce(Message m, String reason) { }
20         private void runq() {
21             MessageStore store = MessageStore.root.slash("smtp").slash("outgoing");
22             int[] outgoing = store.list();
23             for(int i=0; i<outgoing.length; i++) queue.append(store.get(outgoing[i]));
24             while(true) {
25                 Message next = queue.dequeue(true);
26                 // FIXME
27             }
28         }
29     }
30
31     private class Incoming implements ServerRequest {
32         TcpConnection conn;
33         public Incoming(TcpConnection conn) { this.conn = conn; conn.getSocket().setSoTimeout(5 * 60 * 1000); }
34         public void init() { }
35
36         public boolean handleRequest() throws IOException {
37             ReadStream rs = conn.getReadStream();
38             WriteStream ws = conn.getWriteStream();
39             ws.setNewLineString("\r\n");
40
41             ws.println("220 " + conn.getVirtualHost() + " ESMTP " + this.getClass().getName());
42
43             Address from = null;
44             Vector to = new Vector();
45             // 551 = no, i won't forward that
46             // 452 = mailbox full
47             // see 4.4 for trace info
48             while(true) {
49                 String command = rs.readLine();
50                 // FIXME: validate the HELO domain argument
51                 //   (double check against other end of connection? must not reject though)
52                 if (hello.toUpperCase().startsWith("HELO")) {
53                     ws.println("250 HELO " + conn.getVirtualHost());
54                     from = null;
55                     to = new Vector();
56
57                 } else if (hello.toUpperCase().startsWith("EHLO")) {
58                     ws.pritnln("250-" + conn.getVirtualHost());
59                     ws.println("250-SIZE");
60                     ws.println("250 PIPELINING");
61                     from = null;
62                     to = new Vector();
63
64                 } else if (command.toUpperCase().startsWith("RSET")) {
65                     from = null;
66                     to = new Vector();
67                     ws.println("250 reset ok");
68
69                 } else if (command.toUpperCase().startsWith("MAIL FROM:")) {
70                     command = command.substring(10).trim();
71                     if(command.indexOf(' ') != -1) command = command.substring(0, command.indexOf(' '));
72                     from = RFC2822.Address.parse(command);
73
74                 } else if (command.toUpperCase().startsWith("RCPT TO:")) {
75                     if (from == null) {
76                         ws.println("503 MAIL FROM must precede RCPT TO");
77                         continue;
78                     }
79                     command = command.substring(10).trim();
80                     if(command.indexOf(' ') != -1) command = command.substring(0, command.indexOf(' '));
81                     to.addElement(RFC2822.Address.parse(command));
82
83                 } else if (command.toUpperCase().startsWith("DATA")) {
84                     if (from == null) { ws.println("503 MAIL FROM command must precede DATA"); continue; }
85                     if (to.size() == null) { ws.println("503 RCPT TO command must precede DATA"); continue; }
86                     ws.println("354 Enter message, ending with \".\" on a line by itself");
87                     StringBuffer data = new StringBuffer();
88                     // move this into the RFC2822 class
89                     while(true) {
90                         String line = rs.readLine();
91                         if (line.equals(".")) break;
92                         if (line.startsWith("..")) line = line.substring(1);
93                         data.append(line);
94                     }
95                     // FIXME: commit message to disk here
96                     ws.println("250 OK message accepted for delivery");
97                     //ws.println("251 user not local; will forward");
98                     
99                 } else if (command.toUpperCase().startsWith("HELP")) {
100                     ws.println("214 sorry, you are beyond help.  please see a trained professional.");
101
102                 } else if (command.toUpperCase().startsWith("VRFY")) { // FIXME, see code 252
103                 } else if (command.toUpperCase().startsWith("EXPN")) { ws.println("550 EXPN not available");
104                 } else if (command.toUpperCase().startsWith("NOOP")) { ws.println("250 OK");
105                 } else if (command.toUpperCase().startsWith("QUIT")) {
106                     ws.println("221 " + conn.getVirtualHost() + " closing connection");
107                     break;
108
109                 } else {
110                     ws.println("500 unrecognized command");
111                 }                    
112             
113             
114                 return false;  // FIXME: what does this mean?
115             }
116         }
117     }
118 }