2 import java.lang.reflect.*;
3 //import org.prevayler.*;
4 import org.ibex.crypto.*;
5 import org.ibex.util.*;
6 import org.ibex.mail.protocol.*;
9 import java.util.zip.*;
14 * A convenient way to verify that the agent requesting an action
15 * owns a particular email address. Extend this class; the
16 * Transaction.executeOn() method will be invoked when the user
19 public abstract class Confirmation implements Externalizable {
21 public static final long serialVersionUID = 0x981879f18a11ffeeL;
22 public static final Address FROM = Address.parse("adam@megacz.com"); // FIXME
24 public transient Address who = null;
25 public long expiration;
26 public abstract String getDescription();
28 protected Confirmation(Address who, long expiration) { this.who = who; this.expiration = expiration; }
30 public void readExternal(ObjectInput s) throws IOException {
32 int numfields = s.readInt();
33 Class c = this.getClass();
34 for(int i=0; i<numfields; i++) {
35 String name = s.readUTF();
36 Field f = c.getField(name);
37 Class c2 = f.getType();
38 if (c2 == Boolean.TYPE) f.setBoolean(this, s.readBoolean());
39 else if (c2 == Byte.TYPE) f.setByte(this, s.readByte());
40 else if (c2 == Long.TYPE) f.setLong(this, s.readLong());
41 else if (c2 == Integer.TYPE) f.setInt(this, s.readInt());
42 else if (c2 == Character.TYPE) f.setChar(this, s.readChar());
43 else if (c2 == Short.TYPE) f.setShort(this, s.readShort());
44 else if (c2 == Float.TYPE) f.setFloat(this, s.readFloat());
45 else if (c2 == Double.TYPE) f.setDouble(this, s.readDouble());
46 else if (c2 == String.class) f.set(this, s.readObject());
47 else f.set(this, s.readObject());
49 } catch (Exception e) {
54 public void writeExternal(ObjectOutput s) throws IOException {
56 Class c = this.getClass();
57 Field[] fields = c.getFields();
59 for(int i=0; i<fields.length; i++) {
61 if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) continue;
64 s.writeInt(numfields);
65 for(int i=0; i<fields.length; i++) {
67 if ((f.getModifiers() & (Modifier.STATIC | Modifier.TRANSIENT)) != 0) continue;
68 s.writeUTF(fields[i].getName());
69 Class c2 = f.getType();
70 if (c2 == Boolean.TYPE) s.writeBoolean(f.getBoolean(this));
71 else if (c2 == Byte.TYPE) s.writeByte(f.getByte(this));
72 else if (c2 == Long.TYPE) s.writeLong(f.getLong(this));
73 else if (c2 == Integer.TYPE) s.writeInt(f.getInt(this));
74 else if (c2 == Character.TYPE) s.writeChar(f.getChar(this));
75 else if (c2 == Short.TYPE) s.writeShort(f.getShort(this));
76 else if (c2 == Float.TYPE) s.writeFloat(f.getFloat(this));
77 else if (c2 == Double.TYPE) s.writeDouble(f.getDouble(this));
78 else if (c2 == String.class) s.writeUTF((String)f.get(this));
79 else s.writeObject(f.get(this));
81 } catch (Exception e) {
86 public void signAndSend(long secret, Date now) throws IOException, Message.Malformed {
87 SMTP.Outgoing.accept(Message.newMessage(new Stream("From: " + FROM + "\r\n" +
88 "To: " + who.toString(true) + "\r\n" +
89 "Subject: confirm " + getDescription() + "\r\n" +
91 "Please click the link below to " + getDescription() + "\r\n" +
97 public String sign(long secret) throws IOException {
98 ByteArrayOutputStream os = new ByteArrayOutputStream();
99 ObjectOutputStream oos = new ObjectOutputStream(new DeflaterOutputStream(os));
100 oos.writeObject(this);
103 byte[] b = os.toByteArray();
104 StringBuffer sb = new StringBuffer(new String(Base64.encode(b)));
106 SHA1 sha1 = new SHA1();
107 sha1.update(b, 0, b.length);
108 b = new byte[sha1.getDigestSize()];
110 sb.append(new String(Base64.encode(b)));
111 return sb.toString();
114 public static Confirmation decode(String encoded, long secret, Date now) {
116 String payload = encoded.substring(0, encoded.indexOf('.'));
117 ObjectInputStream ois = new ObjectInputStream(new InflaterInputStream(new Base64.InputStream(payload)));
118 Confirmation cve = (Confirmation)ois.readObject();
119 if (!cve.sign(secret).equals(encoded)) throw new InvalidSignature();
120 if (now.getTime() > cve.expiration) throw new Expired();
122 } catch (ClassNotFoundException e) {
123 Log.error(Confirmation.class, e);
124 throw new InvalidSignature();
125 } catch (IOException e) {
126 Log.error(Confirmation.class, e);
127 throw new InvalidSignature();
131 public static class Exn extends RuntimeException { }
132 public static class Expired extends Exn { }
133 public static class InvalidSignature extends Exn { }