SusiMail: Recheck max size if server reports less than default

- More javadocs and cleanups
This commit is contained in:
zzz
2017-12-07 15:45:55 +00:00
parent 71dbc0abe6
commit 3291b761e8
9 changed files with 80 additions and 69 deletions

View File

@@ -94,17 +94,18 @@ class MailPart {
for( int i = 0; i < headerLines.length; i++ )
{
if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-transfer-encoding: " ) ) {
String hlc = headerLines[i].toLowerCase(Locale.US);
if( hlc.startsWith( "content-transfer-encoding: " ) ) {
x_encoding = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-disposition: " ) ) {
else if( hlc.startsWith( "content-disposition: " ) ) {
x_disposition = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
String str;
str = getHeaderLineAttribute( headerLines[i], "filename" );
if( str != null )
x_name = str;
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-type: " ) ) {
else if( hlc.startsWith( "content-type: " ) ) {
x_type = getFirstAttribute( headerLines[i] ).toLowerCase(Locale.US);
/*
* extract boundary, name and charset from content type
@@ -124,10 +125,10 @@ class MailPart {
if( str != null )
x_charset = str.toUpperCase(Locale.US);
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "content-description: " ) ) {
else if( hlc.startsWith( "content-description: " ) ) {
x_description = getFirstAttribute( headerLines[i] );
}
else if( headerLines[i].toLowerCase(Locale.US).startsWith( "mime-version: " ) ) {
else if( hlc.startsWith( "mime-version: " ) ) {
x_version = getFirstAttribute( headerLines[i] );
}
}

View File

@@ -31,16 +31,11 @@ import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import net.i2p.data.DataHelper;
/**
* @author susi
*/
public class Base64 extends Encoding {
/* (non-Javadoc)
* @see i2p.susi23.util.Encoding#getName()
*/
public String getName() {
return "base64";
}
@@ -166,9 +161,6 @@ public class Base64 extends Encoding {
return b;
}
/**
* @see Base64#decode(String)
*/
public ReadBuffer decode(byte[] in, int offset, int length) throws DecodingException {
byte out[] = new byte[length * 3 / 4 + 1 ];
int written = 0;
@@ -197,7 +189,7 @@ public class Base64 extends Encoding {
length -= 4;
}
else {
System.err.println( "" );
//System.err.println( "" );
throw new DecodingException( "Decoding base64 failed (trailing garbage)." );
}
}

View File

@@ -25,30 +25,32 @@ package i2p.susi.webmail.encoding;
import i2p.susi.util.ReadBuffer;
import net.i2p.data.DataHelper;
/**
* Decode only. See encode().
* @author susi
*/
public class EightBit extends Encoding {
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#getName()
*/
public String getName() {
return "8bit";
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#encode(byte[])
/**
* TODO would be nice to implement this, as it is supported on the project server,
* but content must be CRLF terminated with a max of 998 chars per line.
* And you can't have leading dots either, we'd have to prevent or double-dot it.
* That would be expensive to check, using either a double read or
* pulling it all into memory.
* So it's prohibitive for attachments. We could do it for the message body,
* since it's in memory already, but that's not much of a win.
* ref: https://stackoverflow.com/questions/29510178/how-to-handle-1000-character-lines-in-8bit-mime
*
* @throws EncodingException always
*/
public String encode(byte[] in) throws EncodingException {
throw new EncodingException("unsupported");
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#decode(byte[], int, int)
*/
public ReadBuffer decode(byte[] in, int offset, int length)
throws DecodingException {
return new ReadBuffer(in, offset, length);

View File

@@ -42,7 +42,12 @@ public abstract class Encoding {
public abstract String getName();
/**
* Encode a byte array to a ASCII or ISO-8859-1 String
* Encode a byte array to a ASCII or ISO-8859-1 String.
* Output must be SMTP-safe: Line length of 998 or less,
* using SMTP-safe characters,
* followed by \r\n, and must not start with a '.'
* unless escaped by a 2nd dot.
* For some encodings, max line length is 76.
*
* @param in
* @return Encoded string.
@@ -51,7 +56,12 @@ public abstract class Encoding {
public abstract String encode( byte in[] ) throws EncodingException;
/**
* Encode a (UTF-8) String to a ASCII or ISO-8859-1 String
* Encode a (UTF-8) String to a ASCII or ISO-8859-1 String.
* Output must be SMTP-safe: Line length of 998 or less,
* using SMTP-safe characters,
* followed by \r\n, and must not start with a '.'
* unless escaped by a 2nd dot.
* For some encodings, max line length is 76.
*
* This implementation just converts the string to a byte array
* and then calls encode(byte[]).
@@ -67,6 +77,13 @@ public abstract class Encoding {
}
/**
* Encode an input stream of bytes to a ASCII or ISO-8859-1 String.
* Output must be SMTP-safe: Line length of 998 or less,
* using SMTP-safe characters,
* followed by \r\n, and must not start with a '.'
* unless escaped by a 2nd dot.
* For some encodings, max line length is 76.
*
* This implementation just reads the whole stream into memory
* and then calls encode(byte[]).
* Subclasses should implement a more memory-efficient method

View File

@@ -30,23 +30,14 @@ import i2p.susi.util.ReadBuffer;
*/
public class HTML extends Encoding {
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#getName()
*/
public String getName() {
return "HTML";
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#encode(byte[])
*/
public String encode(byte[] in) throws EncodingException {
throw new EncodingException("unsupported");
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#encode(java.lang.String)
*/
@Override
public String encode(String str) throws EncodingException
{
@@ -56,9 +47,6 @@ public class HTML extends Encoding {
.replaceAll( "\r{0,1}\n", "<br>\r\n" );
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#decode(byte[], int, int)
*/
public ReadBuffer decode(byte[] in, int offset, int length)
throws DecodingException {
throw new DecodingException("unsupported");

View File

@@ -44,17 +44,13 @@ import net.i2p.data.DataHelper;
*/
public class HeaderLine extends Encoding {
public static final String NAME = "HEADERLINE";
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#getName()
*/
public String getName() {
return NAME;
}
private static final int BUFSIZE = 2;
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#encode(byte[])
*/
public String encode( byte in[] ) throws EncodingException {
StringBuilder out = new StringBuilder();
int l = 0, buffered = 0, tmp[] = new int[BUFSIZE];
@@ -148,9 +144,6 @@ public class HeaderLine extends Encoding {
return out.toString();
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#decode(java.lang.String)
*/
public ReadBuffer decode( byte in[], int offset, int length ) throws DecodingException {
ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
int written = 0;

View File

@@ -32,26 +32,18 @@ import java.io.InputStream;
import java.io.StringWriter;
import java.io.Writer;
import net.i2p.data.DataHelper;
/**
* ref: https://en.wikipedia.org/wiki/Quoted-printable
* @author susi
*/
public class QuotedPrintable extends Encoding {
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#getName()
*/
public String getName() {
return "quoted-printable";
}
private static int BUFSIZE = 2;
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#encode(byte[])
*/
public String encode( byte in[] ) throws EncodingException {
try {
StringWriter strBuf = new StringWriter();
@@ -142,9 +134,6 @@ public class QuotedPrintable extends Encoding {
}
}
/* (non-Javadoc)
* @see i2p.susi.webmail.encoding.Encoding#decode(byte[], int, int)
*/
public ReadBuffer decode(byte[] in, int offset, int length) {
byte[] out = new byte[length];
int written = 0;

View File

@@ -25,29 +25,25 @@ package i2p.susi.webmail.encoding;
import i2p.susi.util.ReadBuffer;
import net.i2p.data.DataHelper;
/**
* Decode only.
* @author susi
*/
public class SevenBit extends Encoding {
/* (non-Javadoc)
* @see i2p.susi23.mail.encoding.Encoding#getName()
*/
public String getName() {
return "7bit";
}
/* (non-Javadoc)
* @see i2p.susi23.mail.encoding.Encoding#encode(byte[])
/**
* @throws EncodingException always
*/
public String encode(byte[] in) throws EncodingException {
throw new EncodingException("unsupported");
}
/* (non-Javadoc)
* @see i2p.susi23.mail.encoding.Encoding#decode(byte[], int, int)
/**
* @throws DecodingException on illegal characters
*/
public ReadBuffer decode(byte[] in, int offset, int length)
throws DecodingException {

View File

@@ -64,7 +64,8 @@ public class SMTPClient {
private Socket socket;
public String error;
private String lastResponse;
private boolean supportsPipelining;
private boolean supportsPipelining, eightBitMime;
private long maxSize = DEFAULT_MAX_SIZE;
private static final Encoding base64;
@@ -258,12 +259,44 @@ public class SMTPClient {
socket.setSoTimeout(60*1000);
Result r = getFullResult();
if (r.result == 250) {
supportsPipelining = r.recv.contains("PIPELINING");
String[] caps = DataHelper.split(r.recv, "\r");
for (String c : caps) {
if (c.equals("PIPELINING")) {
supportsPipelining = true;
Debug.debug(Debug.DEBUG, "Server supports pipelining");
} else if (c.startsWith("SIZE ")) {
try {
maxSize = Long.parseLong(c.substring(5));
Debug.debug(Debug.DEBUG, "Server max size: " + maxSize);
} catch (NumberFormatException nfe) {}
} else if (c.equals("8BITMIME")) {
// unused, see encoding/EightBit.java
eightBitMime = true;
Debug.debug(Debug.DEBUG, "Server supports 8bitmime");
}
}
} else {
error += _t("Server refused connection") + " (" + r + ")\n";
ok = false;
}
}
if (ok && maxSize < DEFAULT_MAX_SIZE) {
Debug.debug(Debug.DEBUG, "Rechecking with new max size");
// recalculate whether we'll fit
// copied from WebMail
long total = body.length();
if (attachments != null && !attachments.isEmpty()) {
for(Attachment a : attachments) {
total += a.getSize();
}
}
long binaryMax = (long) ((maxSize * 57.0d / 78) - 32*1024);
if (total > binaryMax) {
ok = false;
error += _t("Email is too large, max is {0}",
DataHelper.formatSize2(binaryMax, false) + 'B') + '\n';
}
}
if (ok) {
// RFC 4954 says AUTH must be the last but let's assume
// that includes the user/pass on following lines