case "mail.clamav.check": return METHOD;
case "mail.procmail": /* FEATURE */ return null;
case "mail.vacation": /* FEATURE */ return null;
+ case "mail.verp": return getSub("mail.verp");
+ case "mail.verp.check": return METHOD;
case "mail.dcc": return getSub("mail.dcc");
case "mail.dcc.check": return METHOD;
case "mail.bounce": return METHOD;
new Stream(p.getInputStream()).transcribe(ret);
return JSU.S(ret.toString());
}
+ if (name.equals("mail.verp.check")) {
+ String ret = VERP.verpVerify(Address.parse(JSU.toString(a)), "SECRET".getBytes(), 0);
+ return ret==null ? null : JSU.S(ret);
+ }
if (name.equals("mail.dcc.check")) {
Process p = Runtime.getRuntime().exec(new String[] { "dccproc", "-H" });
((Message)args[0]).getStream().transcribe(new Stream(p.getOutputStream()), true);
--- /dev/null
+// Copyright 2000-2008 the Contributors, as shown in the revision logs.
+// Licensed under the Apache Public Source License 2.0 ("the License").
+// You may not use this file except in compliance with the License.
+
+package org.ibex.mail;
+import org.ibex.crypto.*;
+import org.ibex.js.*;
+import org.ibex.util.*;
+import org.ibex.mail.protocol.*;
+import java.util.*;
+import java.net.*;
+import java.io.*;
+
+// http://cr.yp.to/proto/verp.txt
+public class VERP {
+
+ private static final Random random = new Random();
+
+ public static Address verpSign(Address a, byte[] secret) {
+ return verpSign(a, secret, System.currentTimeMillis());
+ }
+
+ // format:
+ // VERP--<when>-<salt>-<sig>-<user>@domain.com
+ // <when> = 64-bit hexadecimal integer
+ // <salt> = 16-bit hexadecimal integer
+ // <user> = original username (ie local-part)
+ // <sig> = base64(sha1(VERP--<when>-<salt>-<user><secret>))
+ /** converts an Address into a VERP-signed address with signature time "when" and extra payload "extra" */
+ public static Address verpSign(Address a, byte[] secret, long when) {
+ if (a.user==null || "".equals(a.user)) {
+ Log.warn(VERP.class, "note: not VERP-signing the null address: "+a);
+ return a;
+ }
+
+ int salt = Math.abs(random.nextInt()) & 0xffff;
+
+ StringBuffer ret = new StringBuffer();
+ ret.append("VERP--");
+ ret.append(Long.toString(when, 16));
+ ret.append('-');
+ ret.append(Integer.toString(salt, 16));
+ ret.append('-');
+
+ byte[] b = (ret.toString()+a.user).getBytes();
+ SHA1 sha1 = new SHA1();
+ sha1.update(b, 0, b.length);
+ sha1.update(secret, 0, secret.length);
+ b = new byte[sha1.getDigestSize()];
+ sha1.doFinal(b, 0);
+
+ ret.append(new String(Encode.toBase64(b)));
+ ret.append('-');
+ ret.append(a.user);
+ // FIXME: encode a.host in here
+ return new Address(ret.toString(), SMTP.localHostIsMXFor, a.description);
+ }
+
+ // FIXME: return unverpified address
+ /** returns the field passed as "extra" when signing, or null if signature invalid */
+ public static String verpVerify(Address a, byte[] secret, long noEarlierThan) {
+ String s = a.user;
+ int i = 0;
+ i = s.indexOf("--", i); if (i==-1) return null; i += 2;
+ int first = i;
+ i = s.indexOf('-', i); if (i==-1) return null; i++;
+ int second = i;
+ i = s.indexOf('-', i); if (i==-1) return null; i++;
+ int third = i;
+ i = s.indexOf('-', i); if (i==-1) return null; i++;
+ int fourth = i;
+ String verify = s.substring(0,third)+s.substring(fourth);
+ Log.error("VERP", "verify=\""+verify+"\"");
+
+ byte[] b = verify.getBytes();
+ SHA1 sha1 = new SHA1();
+ sha1.update(b, 0, b.length);
+ sha1.update(secret, 0, secret.length);
+ b = new byte[sha1.getDigestSize()];
+ sha1.doFinal(b, 0);
+ if (!new String(Encode.toBase64(b)).equals(s.substring(third,fourth-1))) {
+ Log.error("VERP", "decrypt failed\n"+new String(Encode.toBase64(b))+"\n"+s.substring(third,fourth-1));
+ return null;
+ }
+ // FIXME: check the host on the address
+ long when = Long.parseLong(s.substring(first, second-1), 16);
+ Log.error("VERP", "when="+when);
+ if (when < noEarlierThan) return null;
+ return s.substring(fourth);
+ }
+
+}