* SusiMail:

- Add MailPart constructor, make fields final
   - Add ReadBuffer constructor, make fields final
   - Move decoding to MailPart method
   - Setters/getters for Mail header, body, part
   - Classes package private
   - Finals, constructors
This commit is contained in:
zzz
2014-04-21 20:17:08 +00:00
parent b84682fdc9
commit 49f4f3398d
14 changed files with 187 additions and 159 deletions

View File

@@ -28,9 +28,15 @@ package i2p.susi.util;
*/
public class ReadBuffer {
public byte content[];
public int length, offset;
public final byte content[];
public final int length, offset;
public ReadBuffer(byte[] content, int offset, int length) {
this.content = content;
this.offset = offset;
this.length = length;
}
public String toString()
{
return content != null ? new String( content, offset, length ) : "";

View File

@@ -28,7 +28,7 @@ import i2p.susi.util.ReadBuffer;
/**
* @author user
*/
public class Attachment {
class Attachment {
private String fileName, contentType, transferEncoding;
private ReadBuffer buffer;
private String data;

View File

@@ -24,7 +24,9 @@
package i2p.susi.webmail;
import i2p.susi.util.Config;
import i2p.susi.debug.Debug;
import i2p.susi.util.ReadBuffer;
import i2p.susi.webmail.encoding.DecodingException;
import i2p.susi.webmail.encoding.Encoding;
import i2p.susi.webmail.encoding.EncodingFactory;
@@ -46,11 +48,11 @@ import net.i2p.I2PAppContext;
*
* @author susi
*/
public class Mail {
class Mail {
public static final String DATEFORMAT = "date.format";
private static final String DATEFORMAT = "date.format";
public static final String unknown = "unknown";
private static final String unknown = "unknown";
public int id, size;
public String sender, reply, subject, dateString,
@@ -58,11 +60,11 @@ public class Mail {
formattedDate, // US Locale, UTC
localFormattedDate, // Current Locale, local time zone
shortSender, shortSubject,
quotedDate, // Current Locale, local time zone, longer format
uidl;
quotedDate; // Current Locale, local time zone, longer format
public final String uidl;
public Date date;
public ReadBuffer header, body;
public MailPart part;
private ReadBuffer header, body;
private MailPart part;
String[] to, cc;
public String error;
@@ -71,7 +73,8 @@ public class Mail {
public boolean deleted;
public Mail() {
public Mail(String uidl) {
this.uidl = uidl;
formattedSender = unknown;
formattedSubject = unknown;
formattedDate = unknown;
@@ -81,6 +84,51 @@ public class Mail {
quotedDate = unknown;
error = "";
}
public ReadBuffer getHeader() {
return header;
}
public void setHeader(ReadBuffer rb) {
if (rb == null)
return;
header = rb;
parseHeaders();
}
public boolean hasHeader() {
return header != null;
}
public ReadBuffer getBody() {
return body;
}
public void setBody(ReadBuffer rb) {
if (rb == null)
return;
if (header == null)
setHeader(rb);
body = rb;
try {
part = new MailPart(rb);
} catch (DecodingException de) {
Debug.debug(Debug.ERROR, "Decode error: " + de);
}
}
public boolean hasBody() {
return body != null;
}
public MailPart getPart() {
return part;
}
public boolean hasPart() {
return part != null;
}
/**
*
* @param address E-mail address to be validated

View File

@@ -35,7 +35,7 @@ import java.util.List;
/**
* @author user
*/
public class MailCache {
class MailCache {
public static final boolean FETCH_HEADER = true;
public static final boolean FETCH_ALL = false;
@@ -73,35 +73,26 @@ public class MailCache {
synchronized(mails) {
mail = mails.get( uidl );
if( mail == null ) {
newMail = new Mail();
newMail = new Mail(uidl);
mails.put( uidl, newMail );
}
}
if( mail == null ) {
mail = newMail;
mail.uidl = uidl;
mail.size = mailbox.getSize( uidl );
}
if( mail.size <= FETCH_ALL_SIZE)
headerOnly = false;
boolean parseHeaders = mail.header == null;
if( headerOnly ) {
if( mail.header == null )
mail.header = mailbox.getHeader( uidl );
if(!mail.hasHeader())
mail.setHeader(mailbox.getHeader(uidl));
}
else {
if( mail.body == null ) {
mail.body = mailbox.getBody( uidl );
if( mail.body != null ) {
mail.header = mail.body;
MailPart.parse( mail );
}
if(!mail.hasBody()) {
mail.setBody(mailbox.getBody(uidl));
}
}
if( parseHeaders && mail.header != null )
mail.parseHeaders();
if( mail != null && mail.deleted )
mail = null;
return mail;
@@ -129,13 +120,12 @@ public class MailCache {
synchronized(mails) {
mail = mails.get( uidl );
if( mail == null ) {
newMail = new Mail();
newMail = new Mail(uidl);
mails.put( uidl, newMail );
}
}
if( mail == null ) {
mail = newMail;
mail.uidl = uidl;
mail.size = mailbox.getSize( uidl );
}
if(!mail.deleted) {
@@ -143,12 +133,12 @@ public class MailCache {
if( mail.size <= FETCH_ALL_SIZE)
headerOnly = false;
if( headerOnly ) {
if( mail.header == null ) {
if(!mail.hasHeader()) {
POP3Request pr = new POP3Request(mr, mail, true);
fetches.add(pr);
}
} else {
if( mail.body == null ) {
if(!mail.hasBody()) {
POP3Request pr = new POP3Request(mr, mail, false);
fetches.add(pr);
}
@@ -167,16 +157,11 @@ public class MailCache {
ReadBuffer rb = pr.buf;
if (rb != null) {
Mail mail = pr.mail;
boolean parseHeaders = mail.header == null;
if (pr.getHeaderOnly()) {
mail.header = rb;
mail.setHeader(rb);
} else {
mail.header = rb;
mail.body = rb;
MailPart.parse(mail);
mail.setBody(rb);
}
if (parseHeaders)
mail.parseHeaders();
}
}
}

View File

@@ -23,78 +23,87 @@
*/
package i2p.susi.webmail;
import i2p.susi.debug.Debug;
import i2p.susi.util.ReadBuffer;
import i2p.susi.webmail.encoding.DecodingException;
import i2p.susi.webmail.encoding.Encoding;
import i2p.susi.webmail.encoding.EncodingFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* @author susi23
*/
public class MailPart {
class MailPart {
public String headerLines[], type, boundary, encoding, name,
filename, description, disposition, charset, version;
public int beginBody, begin, end;
public ArrayList<MailPart> parts;
public boolean multipart, message;
public ReadBuffer buffer;
public final String[] headerLines;
public final String type, encoding, name,
description, disposition, charset, version;
private final int beginBody, begin, end;
/** fixme never set */
public final String filename = null;
public final List<MailPart> parts;
public final boolean multipart, message;
public final ReadBuffer buffer;
public void parse( ReadBuffer readBuffer )
public MailPart( ReadBuffer readBuffer ) throws DecodingException
{
parse( readBuffer, readBuffer.offset, readBuffer.length );
this(readBuffer, readBuffer.offset, readBuffer.length);
}
public void parse( ReadBuffer readBuffer, int offset, int length )
public MailPart( ReadBuffer readBuffer, int offset, int length ) throws DecodingException
{
begin = offset;
end = offset + length;
buffer = readBuffer;
if( parts == null )
parts = new ArrayList<MailPart>();
else
parts.clear();
parts = new ArrayList<MailPart>();
/*
* parse header lines
*/
beginBody = end;
for( int i = begin; i < end - 4; i++ )
int bb = end;
for( int i = begin; i < end - 4; i++ ) {
if( buffer.content[i] == '\r' &&
buffer.content[i+1] == '\n' &&
buffer.content[i+2] == '\r' &&
buffer.content[i+3] == '\n' ) {
beginBody = i + 2;
bb = i + 2;
break;
}
}
beginBody = bb;
ReadBuffer decodedHeaders = null;
try {
decodedHeaders = EncodingFactory.getEncoding( "HEADERLINE" ).decode( buffer.content, begin, beginBody - begin );
}
catch (Exception e) {
e.printStackTrace();
}
if (decodedHeaders == null)
return;
ReadBuffer decodedHeaders = EncodingFactory.getEncoding( "HEADERLINE" ).decode( buffer.content, begin, beginBody - begin );
headerLines = new String( decodedHeaders.content, decodedHeaders.offset, decodedHeaders.length ).split( "\r\n" );
String boundary = null;
String x_encoding = null;
String x_disposition = null;
String x_type = null;
boolean x_multipart = false;
boolean x_message = false;
String x_name = null;
String x_charset = null;
String x_description = null;
String x_version = null;
for( int i = 0; i < headerLines.length; i++ )
{
if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-transfer-encoding: " ) ) {
encoding = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
x_encoding = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-disposition: " ) ) {
disposition = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
x_disposition = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
String str;
str = getHeaderLineAttribute( headerLines[i], "filename" );
if( str != null )
name = str;
x_name = str;
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-type: " ) ) {
type = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
x_type = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
/*
* extract boundary, name and charset from content type
*/
@@ -102,24 +111,35 @@ public class MailPart {
str = getHeaderLineAttribute( headerLines[i], "boundary" );
if( str != null )
boundary = str;
if( type != null && type.startsWith( "multipart" ) && boundary != null )
multipart = true;
if( type != null && type.startsWith( "message" ) )
message = true;
if( x_type != null && x_type.startsWith( "multipart" ) && boundary != null )
x_multipart = true;
if( x_type != null && x_type.startsWith( "message" ) )
x_message = true;
str = getHeaderLineAttribute( headerLines[i], "name" );
if( str != null )
name = str;
x_name = str;
str = getHeaderLineAttribute( headerLines[i], "charset" );
if( str != null )
charset = str.toUpperCase(Locale.US);
x_charset = str.toUpperCase(Locale.US);
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-description: " ) ) {
description = getFirstAttribute( headerLines[i] );
x_description = getFirstAttribute( headerLines[i] );
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "mime-version: " ) ) {
version = getFirstAttribute( headerLines[i] );
x_version = getFirstAttribute( headerLines[i] );
}
}
encoding = x_encoding;
disposition = x_disposition;
type = x_type;
multipart = x_multipart;
message = x_message;
name = x_name;
charset = x_charset;
description = x_description;
version = x_version;
/*
* parse body
*/
@@ -153,8 +173,7 @@ public class MailPart {
int endLastPart = i + 2;
if( beginLastPart != -1 ) {
MailPart newPart = new MailPart();
newPart.parse( buffer, beginLastPart, endLastPart - beginLastPart );
MailPart newPart = new MailPart( buffer, beginLastPart, endLastPart - beginLastPart );
parts.add( newPart );
}
beginLastPart = k;
@@ -165,12 +184,29 @@ public class MailPart {
}
}
else if( message ) {
MailPart newPart = new MailPart();
newPart.parse( buffer, beginBody, end );
MailPart newPart = new MailPart(buffer, beginBody, end);
parts.add( newPart );
}
}
public static String getFirstAttribute( String line )
/**
* @param offset 2 for sendAttachment, 0 otherwise, don't know why
* @since 0.9.13
*/
public ReadBuffer decode(int offset) throws DecodingException {
String encg = encoding;
if (encg == null) {
//throw new DecodingException("No encoding specified");
Debug.debug(Debug.DEBUG, "Warning: no transfer encoding found, fallback to 7bit.");
encg = "7bit";
}
Encoding enc = EncodingFactory.getEncoding(encg);
if(enc == null)
throw new DecodingException(_("No encoder found for encoding \\''{0}\\''.", WebMail.quoteHTML(encg)));
return enc.decode(buffer.content, beginBody + offset, end - beginBody - offset);
}
private static String getFirstAttribute( String line )
{
String result = null;
int i = line.indexOf( ": " );
@@ -184,7 +220,8 @@ public class MailPart {
}
return result;
}
public static String getHeaderLineAttribute( String line, String attributeName )
private static String getHeaderLineAttribute( String line, String attributeName )
{
String result = null;
int h = 0;
@@ -253,11 +290,9 @@ public class MailPart {
}
return result;
}
/**
* @param mail
*/
public static void parse(Mail mail) {
// TODO Auto-generated method stub
/** translate */
private static String _(String s, Object o) {
return Messages.getString(s, o);
}
}

View File

@@ -51,7 +51,7 @@ import org.mortbay.servlet.MultiPartRequest;
*
* @author user
*/
public class RequestWrapper {
class RequestWrapper {
private final HttpServletRequest httpRequest;
private MultiPartRequest multiPartRequest;

View File

@@ -491,24 +491,13 @@ public class WebMail extends HttpServlet
prepareAttachment = true;
}
if( showBody ) {
String encoding = mailPart.encoding;
if( encoding == null ) {
encoding = "7bit";
reason += _("Warning: no transfer encoding found, fallback to 7bit.") + br;
}
Encoding e = EncodingFactory.getEncoding( encoding );
if( e == null ) {
showBody = false;
reason += _("No encoder found for encoding \\''{0}\\''.", quoteHTML( encoding ));
}
else {
String charset = mailPart.charset;
if( charset == null ) {
charset = "US-ASCII";
reason += _("Warning: no charset found, fallback to US-ASCII.") + br;
}
try {
ReadBuffer decoded = e.decode( mailPart.buffer.content, mailPart.beginBody, mailPart.end - mailPart.beginBody );
ReadBuffer decoded = mailPart.decode(0);
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), charset ) );
body = new StringBuilder();
String line;
@@ -525,7 +514,6 @@ public class WebMail extends HttpServlet
showBody = false;
reason += _("Part ({0}) not shown, because of {1}", ident, e1.getClass().getName()) + br;
}
}
}
if( html )
out.println( "<tr class=\"mailbody\"><td colspan=\"2\" align=\"center\">" );
@@ -567,7 +555,7 @@ public class WebMail extends HttpServlet
* @param line
* @return escaped string
*/
private static String quoteHTML( String line )
static String quoteHTML( String line )
{
if( line != null )
line = line.replaceAll( "<", "&lt;" ).replaceAll( ">", "&gt;" );
@@ -849,14 +837,11 @@ public class WebMail extends HttpServlet
if( uidl != null ) {
Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FETCH_ALL );
if( mail.part == null ) {
mail.part = new MailPart();
mail.part.parse( mail.body );
}
/*
* extract original sender from Reply-To: or From:
*/
if( mail.part != null ) {
MailPart part = mail.getPart();
if (part != null) {
if( reply || replyAll ) {
if( mail.reply != null && Mail.validateAddress( mail.reply ) )
sessionObject.replyTo = Mail.getAddress( mail.reply );
@@ -868,7 +853,7 @@ public class WebMail extends HttpServlet
pw.println( _("On {0} {1} wrote:", mail.formattedDate + " UTC", sessionObject.replyTo) );
StringWriter text2 = new StringWriter();
PrintWriter pw2 = new PrintWriter( text2 );
showPart( pw2, mail.part, 0, TEXT_ONLY );
showPart( pw2, part, 0, TEXT_ONLY );
pw2.flush();
String[] lines = text2.toString().split( "\r\n" );
for( int i = 0; i < lines.length; i++ )
@@ -933,7 +918,7 @@ public class WebMail extends HttpServlet
if( mail.dateString != null )
pw.print( "Date: " + mail.dateString );
pw.println();
showPart( pw, mail.part, 0, TEXT_ONLY );
showPart( pw, part, 0, TEXT_ONLY );
pw.println( "---- " + _("end forwarded mail") + " ----" );
pw.flush();
sessionObject.body = text.toString();
@@ -1140,7 +1125,7 @@ public class WebMail extends HttpServlet
try {
int hashCode = Integer.parseInt( str );
Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FETCH_ALL );
MailPart part = getMailPartFromHashCode( mail.part, hashCode );
MailPart part = getMailPartFromHashCode( mail.getPart(), hashCode );
if( part != null )
sessionObject.showAttachment = part;
}
@@ -1510,6 +1495,7 @@ public class WebMail extends HttpServlet
}
}
}
/**
* @param sessionObject
* @param response
@@ -1524,20 +1510,14 @@ public class WebMail extends HttpServlet
ReadBuffer content = part.buffer;
if( part.encoding != null ) {
Encoding encoding = EncodingFactory.getEncoding( part.encoding );
if( encoding != null ) {
try {
content = encoding.decode( part.buffer.content, part.beginBody + 2, part.end - part.beginBody - 2 );
// why +2 ??
content = part.decode(2);
}
catch (DecodingException e) {
sessionObject.error += _("Error decoding content: {0}", e.getMessage()) + "<br>";
content = null;
}
}
else {
sessionObject.error += _("Error decoding content: No encoder found.");
content = null;
}
}
if( content != null ) {
ZipOutputStream zip = null;
@@ -1922,14 +1902,11 @@ public class WebMail extends HttpServlet
out.println( "<p class=\"error\">" + _("Really delete this message?") + " " + button( REALLYDELETE, _("Yes, really delete it!") ) + "</p>" );
}
Mail mail = sessionObject.mailCache.getMail( sessionObject.showUIDL, MailCache.FETCH_ALL );
if( mail != null && mail.body != null && mail.part == null ) {
mail.part = new MailPart();
mail.part.parse( mail.body );
}
if(!RELEASE && mail != null && mail.body != null) {
if(!RELEASE && mail != null && mail.hasBody()) {
out.println( "<!--" );
// FIXME encoding, escaping --, etc... but disabled.
out.println( quoteHTML( new String( mail.body.content, mail.body.offset, mail.body.length ) ) );
ReadBuffer body = mail.getBody();
out.println( quoteHTML( new String(body.content, body.offset, body.length ) ) );
out.println( "-->" );
}
out.println( button( NEW, _("New") ) + spacer +
@@ -1954,8 +1931,8 @@ public class WebMail extends HttpServlet
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("Date:") +
"</td><td align=\"left\">" + mail.quotedDate + "</td></tr>\n" +
"<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>" );
if( mail.body != null ) {
showPart( out, mail.part, 0, SHOW_HTML );
if( mail.hasPart()) {
showPart( out, mail.getPart(), 0, SHOW_HTML );
}
else {
out.println( "<tr class=\"mailbody\"><td colspan=\"2\" align=\"center\"><p class=\"error\">" + _("Could not fetch mail body.") + "</p></td></tr>" );

View File

@@ -219,11 +219,7 @@ public class Base64 implements Encoding {
throw new DecodingException( "Decoding base64 failed (trailing garbage)." );
}
}
ReadBuffer readBuffer = new ReadBuffer();
readBuffer.content = out;
readBuffer.offset = 0;
readBuffer.length = written;
return readBuffer;
return new ReadBuffer(out, 0, written);
}
/*

View File

@@ -29,7 +29,7 @@ package i2p.susi.webmail.encoding;
public class DecodingException extends Exception {
private static final long serialVersionUID = 1L;
DecodingException( String msg ) {
public DecodingException( String msg ) {
super( msg );
}

View File

@@ -65,11 +65,7 @@ public class EightBit implements Encoding {
*/
public ReadBuffer decode(byte[] in, int offset, int length)
throws DecodingException {
ReadBuffer readBuffer = new ReadBuffer();
readBuffer.content = in;
readBuffer.offset = offset;
readBuffer.length = length;
return readBuffer;
return new ReadBuffer(in, offset, length);
}
/* (non-Javadoc)

View File

@@ -293,16 +293,13 @@ public class HeaderLine implements Encoding {
lastSkip = 0;
}
ReadBuffer readBuffer = new ReadBuffer();
readBuffer.content = out;
readBuffer.offset = 0;
readBuffer.length = written;
return readBuffer;
return new ReadBuffer(out, 0, written);
}
public ReadBuffer decode(String text) throws DecodingException {
return text != null ? decode( text.getBytes() ) : null;
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#decode(i2p.susi.webmail.util.ReadBuffer)
*/

View File

@@ -167,13 +167,9 @@ public class QuotedPrintable implements Encoding {
out[written++] = c;
}
ReadBuffer readBuffer = new ReadBuffer();
readBuffer.content = out;
readBuffer.offset = 0;
readBuffer.length = written;
return readBuffer;
return new ReadBuffer(out, 0, written);
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#decode(i2p.susi.webmail.util.ReadBuffer)
*/

View File

@@ -77,11 +77,7 @@ public class SevenBit implements Encoding {
continue;
throw new DecodingException( "No 8bit Data allowed (" + b + ")" );
}
ReadBuffer readBuffer = new ReadBuffer();
readBuffer.content = in;
readBuffer.offset = backupOffset;
readBuffer.length = backupLength;
return readBuffer;
return new ReadBuffer(in, backupOffset, backupLength);
}
/* (non-Javadoc)

View File

@@ -739,11 +739,7 @@ public class POP3MailBox {
baos.write((byte) '\n');
buf.setLength(0);
}
ReadBuffer readBuffer = new ReadBuffer();
readBuffer.content = baos.toByteArray();
readBuffer.offset = 0;
readBuffer.length = baos.size();
return readBuffer;
return new ReadBuffer(baos.toByteArray(), 0, baos.size());
}
/**