# HG changeset patch # User xitiomet # Date 1321339294 18000 # Node ID f622dfb6866b81f99b035cb8499105ee455dc534 # Parent 7a777704df62b3e6dbfc5c3fdb00831b1d666126 smtp support diff -r 7a777704df62b3e6dbfc5c3fdb00831b1d666126 -r f622dfb6866b81f99b035cb8499105ee455dc534 Makefile --- a/Makefile Wed Nov 02 19:48:50 2011 -0400 +++ b/Makefile Tue Nov 15 01:41:34 2011 -0500 @@ -25,7 +25,7 @@ # 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 diff -r 7a777704df62b3e6dbfc5c3fdb00831b1d666126 -r f622dfb6866b81f99b035cb8499105ee455dc534 smtp.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/smtp.sh Tue Nov 15 01:41:34 2011 -0500 @@ -0,0 +1,11 @@ +#!/bin/sh +## +## placebo.sh +## +## Made by Brian Dunigan +## Login +## +## 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 $* diff -r 7a777704df62b3e6dbfc5c3fdb00831b1d666126 -r f622dfb6866b81f99b035cb8499105ee455dc534 src/org/openstatic/smtp/PlaceboSmtpServer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/openstatic/smtp/PlaceboSmtpServer.java Tue Nov 15 01:41:34 2011 -0500 @@ -0,0 +1,228 @@ +/* + 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 . +*/ + +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 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(); + } + + 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(); + } +} diff -r 7a777704df62b3e6dbfc5c3fdb00831b1d666126 -r f622dfb6866b81f99b035cb8499105ee455dc534 src/org/openstatic/smtp/SmtpMessage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/openstatic/smtp/SmtpMessage.java Tue Nov 15 01:41:34 2011 -0500 @@ -0,0 +1,79 @@ +/* + 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 . +*/ + +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 headers; + + public SmtpMessage() + { + this.headers = new Hashtable(); + } + + 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 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; + } +} diff -r 7a777704df62b3e6dbfc5c3fdb00831b1d666126 -r f622dfb6866b81f99b035cb8499105ee455dc534 src/org/openstatic/smtp/SmtpRequestThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/openstatic/smtp/SmtpRequestThread.java Tue Nov 15 01:41:34 2011 -0500 @@ -0,0 +1,161 @@ +/* + 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 . +*/ + +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) {} + } +}