package net.i2p.data.i2np; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import net.i2p.I2PAppContext; import net.i2p.data.Certificate; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.DataStructureImpl; import net.i2p.util.Log; /** * Contains one deliverable message encrypted to a router along with instructions * and a certificate 'paying for' the delivery. * * Note that certificates are always the null certificate at this time, others are unimplemented. * * @author jrandom */ public class GarlicClove extends DataStructureImpl { //private final Log _log; private static final long serialVersionUID = 1L; private transient final I2PAppContext _context; private DeliveryInstructions _instructions; private I2NPMessage _msg; private long _cloveId; private Date _expiration; private Certificate _certificate; public GarlicClove(I2PAppContext context) { _context = context; //_log = context.logManager().getLog(GarlicClove.class); _cloveId = -1; } public DeliveryInstructions getInstructions() { return _instructions; } public void setInstructions(DeliveryInstructions instr) { _instructions = instr; } public I2NPMessage getData() { return _msg; } public void setData(I2NPMessage msg) { _msg = msg; } public long getCloveId() { return _cloveId; } public void setCloveId(long id) { _cloveId = id; } public Date getExpiration() { return _expiration; } public void setExpiration(Date exp) { _expiration = exp; } public Certificate getCertificate() { return _certificate; } public void setCertificate(Certificate cert) { _certificate = cert; } /** * @deprecated unused, use byte array method to avoid copying * @throws UnsupportedOperationException always */ @Deprecated public void readBytes(InputStream in) throws DataFormatException, IOException { throw new UnsupportedOperationException(); /**** _instructions = new DeliveryInstructions(); _instructions.readBytes(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("Read instructions: " + _instructions); try { _msg = _handler.readMessage(in); } catch (I2NPMessageException ime) { throw new DataFormatException("Unable to read the message from a garlic clove", ime); } _cloveId = DataHelper.readLong(in, 4); _expiration = DataHelper.readDate(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration); //_certificate = new Certificate(); //_certificate.readBytes(in); _certificate = Certificate.create(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("Read cert: " + _certificate); ****/ } /** * */ public int readBytes(byte source[], int offset) throws DataFormatException { int cur = offset; _instructions = DeliveryInstructions.create(source, offset); cur += _instructions.getSize(); //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Read instructions: " + _instructions); try { I2NPMessageHandler handler = new I2NPMessageHandler(_context); cur += handler.readMessage(source, cur); _msg = handler.lastRead(); } catch (I2NPMessageException ime) { throw new DataFormatException("Unable to read the message from a garlic clove", ime); } _cloveId = DataHelper.fromLong(source, cur, 4); cur += 4; _expiration = DataHelper.fromDate(source, cur); cur += DataHelper.DATE_LENGTH; //if (_log.shouldLog(Log.DEBUG)) // _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration); //_certificate = new Certificate(); //cur += _certificate.readBytes(source, cur); _certificate = Certificate.create(source, cur); cur += _certificate.size(); //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Read cert: " + _certificate); return cur - offset; } /** * @deprecated unused, use byte array method to avoid copying * @throws UnsupportedOperationException always */ @Deprecated public void writeBytes(OutputStream out) throws DataFormatException, IOException { throw new UnsupportedOperationException(); /**** StringBuilder error = null; if (_instructions == null) { if (error == null) error = new StringBuilder(); error.append("No instructions "); } if (_msg == null) { if (error == null) error = new StringBuilder(); error.append("No message "); } if (_cloveId < 0) { if (error == null) error = new StringBuilder(); error.append("CloveID < 0 [").append(_cloveId).append("] "); } if (_expiration == null) { if (error == null) error = new StringBuilder(); error.append("Expiration is null "); } if (_certificate == null) { if (error == null) error = new StringBuilder(); error.append("Certificate is null "); } if ( (error != null) && (error.length() > 0) ) throw new DataFormatException(error.toString()); _instructions.writeBytes(out); if (_log.shouldLog(Log.DEBUG)) _log.debug("Wrote instructions: " + _instructions); try { byte m[] = _msg.toByteArray(); if (m == null) throw new RuntimeException("foo, returned null"); if (m.length <= 0) throw new RuntimeException("foo, returned 0 length"); out.write(m); } catch (RuntimeException e) { throw new DataFormatException("Unable to write the clove: " + _msg + " to " + out, e); } DataHelper.writeLong(out, 4, _cloveId); DataHelper.writeDate(out, _expiration); if (_log.shouldLog(Log.DEBUG)) _log.debug("CloveID written: " + _cloveId + " expiration written: " + _expiration); _certificate.writeBytes(out); if (_log.shouldLog(Log.DEBUG)) _log.debug("Written cert: " + _certificate); ****/ } /** * */ @Override public byte[] toByteArray() { byte rv[] = new byte[estimateSize()]; int offset = 0; offset += _instructions.writeBytes(rv, offset); //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Wrote instructions: " + _instructions); //offset += _msg.toByteArray(rv); try { byte m[] = _msg.toByteArray(); System.arraycopy(m, 0, rv, offset, m.length); offset += m.length; } catch (RuntimeException e) { throw new RuntimeException("Unable to write: " + _msg + ": " + e.getMessage()); } DataHelper.toLong(rv, offset, 4, _cloveId); offset += 4; DataHelper.toDate(rv, offset, _expiration.getTime()); offset += DataHelper.DATE_LENGTH; offset += _certificate.writeBytes(rv, offset); if (offset != rv.length) { Log log = I2PAppContext.getGlobalContext().logManager().getLog(GarlicClove.class); log.error("Clove offset: " + offset + " but estimated length: " + rv.length); } return rv; } public int estimateSize() { return _instructions.getSize() + _msg.getMessageSize() + 4 // cloveId + DataHelper.DATE_LENGTH + _certificate.size(); // certificate } @Override public boolean equals(Object obj) { if ( (obj == null) || !(obj instanceof GarlicClove)) return false; GarlicClove clove = (GarlicClove)obj; return DataHelper.eq(_certificate, clove._certificate) && _cloveId == clove._cloveId && DataHelper.eq(_msg, clove._msg) && DataHelper.eq(_expiration, clove._expiration) && DataHelper.eq(_instructions, clove._instructions); } @Override public int hashCode() { return DataHelper.hashCode(_certificate) ^ (int) _cloveId ^ DataHelper.hashCode(_msg) ^ DataHelper.hashCode(_expiration) ^ DataHelper.hashCode(_instructions); } @Override public String toString() { StringBuilder buf = new StringBuilder(128); buf.append("[GarlicClove: "); buf.append("\n\tInstructions: ").append(_instructions); buf.append("\n\tCertificate: ").append(_certificate); buf.append("\n\tClove ID: ").append(_cloveId); buf.append("\n\tExpiration: ").append(_expiration); buf.append("\n\tData: ").append(_msg); buf.append("]"); return buf.toString(); } }