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