+ if (envelopeFrom==null || envelopeFrom.toString().equals("")) return null;
+
+ // FIXME: limit bounce body size
+ // FIXME: include headers from bounced message
+ Log.warn(Message.class, "bouncing message due to: " + reason);
+ Headers h = new Headers(headers, new String[] {
+ "Envelope-To", envelopeFrom.toString(),
+ "Return-Path", "<>",
+ "From", "MAILER-DAEMON <>",
+ "To", envelopeFrom.toString(),
+ "Subject", "failure notice"
+ });
+
+ String error =
+ "\r\n"+
+ "Hi. This is the Ibex Mail Server. I'm afraid I wasn't able to deliver\r\n"+
+ "your message to the following addresses. This is a permanent error;\r\n"+
+ "I've given up. Sorry it didn't work out\r\n."+
+ "\r\n"+
+ "<"+envelopeTo.toString()+">:\r\n"+
+ reason+"\r\n"+
+ "\r\n"+
+ "--- Below this line is a copy of the message.\r\n"+
+ "\r\n";
+
+ try {
+ return newMessage(Fountain.Util.concat(h, Fountain.Util.create(error), getBody()));
+ } catch (Message.Malformed e) {
+ Log.error(this, "caught Message.Malformed in Message.bounce(); this should never happen");
+ Log.error(this, e);
+ return null;
+ }
+ }
+
+ public String toString() { throw new RuntimeException("Message.toString() called"); }
+ public final String summary() { return "[" + envelopeFrom + " -> " + envelopeTo + "] " + subject; }
+
+ public static class Malformed extends MailException { public Malformed(String s) { super(s); } }
+
+ /** reads an SMTP-style dot-escaped message */
+ static Message readDotEncodedMessage(Stream conn) {
+ StringBuffer buf = new StringBuffer();
+ while(true) {
+ String s = conn.readln();
+ if (s == null) throw new RuntimeException("connection closed");
+ if (s.equals(".")) break;
+ if (s.startsWith(".")) s = s.substring(1);
+ buf.append(s);
+ buf.append("\r\n");
+ }
+ return Message.newMessage(new Fountain.StringFountain(buf.toString()));