Commits

xitiomet committed f622dfb

smtp support

  • Participants
  • Parent commits 7a77770

Comments (0)

Files changed (5)

 # Alternate build using the java vm
 jvm:
 	mkdir jvm-build
-	$(JAVAC) src/*.java src/org/json/*.java src/org/openstatic/http/*.java src/org/openstatic/util/*.java src/org/openstatic/placebo/*.java src/org/openstatic/placebo/plugins/*.java -d jvm-build
+	$(JAVAC) src/*.java src/org/json/*.java src/org/openstatic/http/*.java src/org/openstatic/util/*.java src/org/openstatic/placebo/*.java src/org/openstatic/smtp/*.java src/org/openstatic/placebo/plugins/*.java -d jvm-build
 	$(JAR) -cvmf res/manifest.mf placebohttp.jar -C jvm-build org -C jvm-build PlaceboBase.class www LICENSE
 
 # Executable Rule for GCJ
+#!/bin/sh
+##
+## placebo.sh
+## 
+## Made by Brian Dunigan
+## Login   <xitiomet@xi03>
+## 
+## Started on  Wed Jun  9 18:58:39 2010 Brian Dunigan
+## Last update Wed Jun  9 18:58:39 2010 Brian Dunigan
+##
+java -cp placebohttp.jar org.openstatic.smtp.PlaceboSmtpServer $*

src/org/openstatic/smtp/PlaceboSmtpServer.java

+/*
+    Copyright (C) 2010 Brian Dunigan
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package org.openstatic.smtp;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.Hashtable;
+import java.util.Date;
+import java.text.SimpleDateFormat;
+import java.io.PrintStream;
+import java.io.OutputStream;
+import org.json.*;
+
+public class PlaceboSmtpServer extends Thread   
+{
+    private int port;
+    private String myName;
+    private String domain;
+    private PrintStream debug_stream;
+    private boolean debug_mode;
+    private boolean show_data;
+    private boolean keep_running;
+    private ServerSocket ss;
+    private int routing_mode;
+    private LinkedBlockingQueue<SmtpMessage> message_queue;
+
+    public static void main(String[] args)
+    {
+        PlaceboSmtpServer smtp = new PlaceboSmtpServer(args[0]);
+        smtp.setDebug(true);
+        smtp.start();
+
+        try
+        {
+            while(true)
+            {
+                SmtpMessage nm = smtp.getNextMessage();
+                System.err.println(nm.toJSONObject().toString());
+            }
+         } catch (Exception e) {
+            System.err.println(e.toString() + "/" + e.getMessage());
+            e.printStackTrace(System.err);
+         }
+    }
+    
+    public PlaceboSmtpServer(String domain)
+    {
+        this(25, domain);
+    }
+    
+    public PlaceboSmtpServer(int port, String domain)
+    {
+        this.routing_mode = 0;
+        this.port = port;
+        this.myName = "Placebo:" + String.valueOf(this.port);
+        this.domain = domain;
+        this.debug_stream = System.err;
+        this.keep_running = true;
+        this.message_queue = new LinkedBlockingQueue<SmtpMessage>();
+    }
+    
+    public void setPort(int value)
+    {
+        this.port = value;
+        this.myName = "Placebo:" + String.valueOf(this.port);
+    }
+
+    public void setDomain(String dm)
+    {
+        this.domain = dm;
+    }
+
+    public String getDomain()
+    {
+        return this.domain;
+    }
+  
+    public void run()
+    {
+        ss = null;
+        try
+        {
+            ss = new ServerSocket(this.port);
+            logln(this.myName,"Init on port " + String.valueOf(this.port));
+        } catch (Exception n) {
+            logln(this.myName,"Init Failed on port " + String.valueOf(this.port) + " / " + n.getMessage());
+        }
+        logln(this.myName,"Entering Server Loop");
+        this.keep_running = true;
+        while(this.keep_running)
+        {
+            try
+            {
+                logln(this.myName,"Waiting for next Socket....");
+                Socket new_connection = ss.accept();
+                logln(this.myName,"Connection Accepted " + new_connection.getInetAddress().getCanonicalHostName());
+                SmtpRequestThread rt = new SmtpRequestThread(new_connection, this);
+                rt.start();
+            } catch (Exception x) {
+                logln(this.myName, x.toString() + " / " + x.getMessage());
+                x.printStackTrace(System.err);
+            }
+        }
+    }
+
+    public void queueMessage(SmtpMessage new_message)
+    {
+        try
+        {
+            this.message_queue.put(new_message);
+        } catch (Exception e) {
+
+        }
+    }
+
+    public SmtpMessage getNextMessage()
+    {
+        try
+        {
+            return message_queue.take();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    public void setDebug(boolean value)
+    {
+        this.debug_mode = value;
+    }
+
+    public void setShowData(boolean value)
+    {
+        this.show_data = value;
+    }
+    
+    public boolean isDebug()
+    {
+        return this.debug_mode;
+    }
+
+    public boolean isShowData()
+    {
+        return this.show_data;
+    }
+    
+    public void setDebugStream(OutputStream value)
+    {
+        this.debug_stream = new PrintStream(value);
+    }
+
+    public void setDebugStream(PrintStream value)
+    {
+        this.debug_stream = value;
+    }
+    
+    public PrintStream getDebugStream()
+    {
+        return this.debug_stream;
+    }
+    
+    public void logln(String msg)
+    {
+        if (this.debug_mode)
+        {
+            this.debug_stream.println(msg.replaceAll("\r","").replaceAll("\n",""));
+        }
+    }
+    
+    public void logln(String caption, String msg)
+    {
+        if (this.debug_mode)
+        {
+            this.debug_stream.println("[" + caption + "]" + msg.replaceAll("\r","").replaceAll("\n",""));
+        }
+    }
+    
+    private static String setSizedString(String value, int size)
+    {
+        if (value == null)
+        {
+            return getPaddingSpace(size);
+        } else if (value.length() == size) {
+            return value;
+        } else if (value.length() > size) {
+            return value.substring(0, size);
+        } else if (value.length() < size) {
+            return value + getPaddingSpace(size - value.length());
+        } else {
+            return null;
+        }
+    }
+    
+    public void shutdown()
+    {
+        try
+        {
+            ss.close();
+            this.keep_running = false;
+            this.interrupt();
+        } catch (Exception e) {}
+    }
+    
+    private static String getPaddingSpace(int value)
+    {
+        StringBuffer x = new StringBuffer("");
+        for (int n = 0; n < value; n++)
+        {
+            x.append(" ");
+        }
+        return x.toString();
+    }
+}

src/org/openstatic/smtp/SmtpMessage.java

+/*
+    Copyright (C) 2010 Brian Dunigan
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package org.openstatic.smtp;
+
+import java.util.StringTokenizer;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import org.json.*;
+
+public class SmtpMessage
+{
+    private String to;
+    private String from;
+    private String to_domain;
+    private String from_domain;
+    private String body;
+    private Hashtable<String,String> headers;
+
+    public SmtpMessage()
+    {
+        this.headers = new Hashtable<String,String>();
+    }
+
+    public void setToAddress(String address)
+    {
+        StringTokenizer ad = new StringTokenizer(address, "@");
+        this.to = ad.nextToken();
+        this.to_domain = ad.nextToken();
+    }
+
+    public void setFromAddress(String address)
+    {
+        StringTokenizer ad = new StringTokenizer(address, "@");
+        this.from = ad.nextToken();
+        this.from_domain = ad.nextToken();
+    }
+
+    public void setBody(String body)
+    {
+        this.body = body;
+    }
+
+    public void setHeader(String key, String value)
+    {
+        this.headers.put(key, value);
+    }
+
+    public JSONObject toJSONObject() throws JSONException
+    {
+        JSONObject jmsg = new JSONObject();
+        jmsg.put("to", this.to + "@" + this.to_domain);
+        jmsg.put("from", this.from + "@" + this.from_domain);
+        jmsg.put("body", this.body);
+        JSONObject jheaders = new JSONObject();
+        for(Enumeration<String> keys = this.headers.keys(); keys.hasMoreElements();)
+        {
+            String key = keys.nextElement();
+            String value = this.headers.get(key);
+            jheaders.put(key, value);
+        }
+        jmsg.put("headers", jheaders);
+        return jmsg;
+    }
+}

src/org/openstatic/smtp/SmtpRequestThread.java

+/*
+    Copyright (C) 2010 Brian Dunigan
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package org.openstatic.smtp;
+
+import java.net.Socket;
+import java.net.URLDecoder;
+import java.util.StringTokenizer;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Hashtable;
+
+public class SmtpRequestThread extends Thread
+{
+    private InputStream is;
+    private OutputStream os;
+    private Socket connection;
+    private PlaceboSmtpServer myServer;
+    private String clientHostname;
+    
+    public SmtpRequestThread(Socket connection, PlaceboSmtpServer myServer)
+    {
+        try
+        {
+            this.is = connection.getInputStream();
+            this.os = connection.getOutputStream();
+        } catch (Exception n) {}
+        this.connection = connection;
+        this.myServer = myServer;
+        this.clientHostname = this.connection.getInetAddress().getCanonicalHostName();
+    }
+    
+    public void run()
+    {
+        try
+        {            
+            // here we process input from the smtp server
+            BufferedReader br = new BufferedReader(new InputStreamReader(this.is));
+            socketWriteln("220 " + this.myServer.getDomain() + " ESMTP PlaceboSMTP");
+            String cmd_line = null;
+            boolean still_reading = true;
+            SmtpMessage nm = new SmtpMessage();
+            while (still_reading)
+            {
+                cmd_line = br.readLine();
+                this.myServer.logln(this.clientHostname, "-> " + cmd_line);
+
+                if (cmd_line.startsWith("HELO ") || cmd_line.startsWith("EHLO "))
+                {
+                    socketWriteln("250 Hello " + cmd_line.replaceAll("HELO ",""));
+                }
+
+                if (cmd_line.startsWith("MAIL FROM:"))
+                {
+                    nm.setFromAddress(dropCapsule(afterColon(cmd_line)));
+                    socketWriteln("250 Ok");
+                }
+
+                if (cmd_line.startsWith("RCPT TO:"))
+                {
+                    nm.setToAddress(dropCapsule(afterColon(cmd_line)));
+                    socketWriteln("250 Accepted");
+                }
+                
+                if (cmd_line.equals("DATA"))
+                {
+                    socketWriteln("354 Enter message, ending with \".\" on a line by itself");
+
+                    // read headers
+                    this.myServer.logln(this.clientHostname, "Reading headers");
+                    while(!cmd_line.equals(""))
+                    {
+                        cmd_line = br.readLine();
+                        this.myServer.logln(this.clientHostname, "-> " + cmd_line);
+                        if (cmd_line.contains(":") && !cmd_line.startsWith(" "))
+                        {
+                            StringTokenizer cs = new StringTokenizer(cmd_line, ": ");
+                            String key = cs.nextToken();
+                            if (!key.equals("Received"))
+                            {
+                                String value = cs.nextToken();
+                                nm.setHeader(key,value);
+                            }
+                        }
+                    }
+
+                    StringBuilder bd = new StringBuilder();
+                    this.myServer.logln(this.clientHostname, "Reading body");
+                    while(!cmd_line.equals("."))
+                    {
+                        cmd_line = br.readLine();
+                        this.myServer.logln(this.clientHostname, "-> " + cmd_line);
+                        if (!cmd_line.equals("."))
+                            bd.append(cmd_line + "\n");
+                    }
+                    nm.setBody(bd.toString());
+                    socketWriteln("250 OK");
+                    this.myServer.queueMessage(nm);
+                }
+
+                if (cmd_line.equals("QUIT"))
+                {
+                    socketWriteln("221 " + this.myServer.getDomain() + " closing connection");
+                    still_reading = false;
+                }
+            }
+            this.connection.close();
+        } catch (Exception x) {
+            System.err.println(x.toString() + "/" + x.getMessage());
+            x.printStackTrace(System.err);
+        }
+    }
+
+    private void socketWriteln(String out)
+    {
+        socketWrite(out + "\n");
+    }
+
+    private String afterColon(String s)
+    {
+        StringTokenizer cs = new StringTokenizer(s, ":");
+        cs.nextToken();
+        return cs.nextToken();
+    }
+
+    private String dropCapsule(String s)
+    {
+        if (s.startsWith("<") && s.endsWith(">"))
+        {
+            return s.substring(1, s.length()-1);
+        } else {
+            return s;
+        }
+    }
+
+    private void socketWrite(String out)
+    {
+        try
+        {
+            this.os.write(out.getBytes());
+            this.os.flush();
+            this.myServer.logln(this.clientHostname, "<- " + out);
+        } catch (Exception we) {}
+    }
+}