forked from I2P_Developers/i2p.i2p
* SusiMail:
- Add persistent cache - Remove ID sorter - Mail size getter/setter - Set mail size when setting body - Only send CAPA once - Tagged string tweaks
This commit is contained in:
@ -54,7 +54,7 @@ class Mail {
|
||||
|
||||
private static final String unknown = "unknown";
|
||||
|
||||
public int id, size;
|
||||
private int size;
|
||||
public String sender, reply, subject, dateString,
|
||||
formattedSender, formattedSubject,
|
||||
formattedDate, // US Locale, UTC
|
||||
@ -108,6 +108,7 @@ class Mail {
|
||||
if (header == null)
|
||||
setHeader(rb);
|
||||
body = rb;
|
||||
size = rb.length;
|
||||
try {
|
||||
part = new MailPart(rb);
|
||||
} catch (DecodingException de) {
|
||||
@ -127,6 +128,16 @@ class Mail {
|
||||
return part != null;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
if (body != null)
|
||||
return;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param address E-mail address to be validated
|
||||
|
@ -23,10 +23,12 @@
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import i2p.susi.debug.Debug;
|
||||
import i2p.susi.util.ReadBuffer;
|
||||
import i2p.susi.webmail.pop3.POP3MailBox;
|
||||
import i2p.susi.webmail.pop3.POP3MailBox.FetchRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@ -43,6 +45,7 @@ class MailCache {
|
||||
|
||||
private final POP3MailBox mailbox;
|
||||
private final Hashtable<String, Mail> mails;
|
||||
private final PersistentMailCache disk;
|
||||
|
||||
/** Includes header, headers are generally 1KB to 1.5 KB,
|
||||
* and bodies will compress well.
|
||||
@ -52,9 +55,18 @@ class MailCache {
|
||||
/**
|
||||
* @param mailbox non-null
|
||||
*/
|
||||
MailCache( POP3MailBox mailbox ) {
|
||||
MailCache(POP3MailBox mailbox,
|
||||
String host, int port, String user, String pass) {
|
||||
this.mailbox = mailbox;
|
||||
mails = new Hashtable<String, Mail>();
|
||||
PersistentMailCache pmc = null;
|
||||
try {
|
||||
pmc = new PersistentMailCache(host, port, user, pass);
|
||||
// TODO pmc.getMails()
|
||||
} catch (IOException ioe) {
|
||||
Debug.debug(Debug.ERROR, "Error creating disk cache: " + ioe);
|
||||
}
|
||||
disk = pmc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,11 +92,11 @@ class MailCache {
|
||||
}
|
||||
if( mail == null ) {
|
||||
mail = newMail;
|
||||
mail.size = mailbox.getSize( uidl );
|
||||
mail.setSize(mailbox.getSize(uidl));
|
||||
}
|
||||
if (mail.markForDeletion)
|
||||
return null;
|
||||
if( mail.size <= FETCH_ALL_SIZE)
|
||||
if(mail.getSize() <= FETCH_ALL_SIZE)
|
||||
headerOnly = false;
|
||||
|
||||
if( headerOnly ) {
|
||||
@ -95,6 +107,11 @@ class MailCache {
|
||||
mail.setBody(mailbox.getBody(uidl));
|
||||
}
|
||||
}
|
||||
if (disk != null) {
|
||||
if (disk.saveMail(mail) && mail.hasBody()) {
|
||||
// TODO delete on server
|
||||
}
|
||||
}
|
||||
return mail;
|
||||
}
|
||||
|
||||
@ -126,20 +143,32 @@ class MailCache {
|
||||
}
|
||||
if( mail == null ) {
|
||||
mail = newMail;
|
||||
mail.size = mailbox.getSize( uidl );
|
||||
mail.setSize(mailbox.getSize(uidl));
|
||||
}
|
||||
if (mail.markForDeletion)
|
||||
continue;
|
||||
mr.setMail(mail);
|
||||
if( mail.size <= FETCH_ALL_SIZE)
|
||||
if(mail.getSize() <= FETCH_ALL_SIZE)
|
||||
headerOnly = false;
|
||||
if( headerOnly ) {
|
||||
if(!mail.hasHeader()) {
|
||||
if (disk != null) {
|
||||
if (disk.getMail(mail, true)) {
|
||||
Debug.debug(Debug.DEBUG, "Loaded header from disk cache: " + uidl);
|
||||
continue; // found on disk, woo
|
||||
}
|
||||
}
|
||||
POP3Request pr = new POP3Request(mr, mail, true);
|
||||
fetches.add(pr);
|
||||
}
|
||||
} else {
|
||||
if(!mail.hasBody()) {
|
||||
if (disk != null) {
|
||||
if (disk.getMail(mail, false)) {
|
||||
Debug.debug(Debug.DEBUG, "Loaded body from disk cache: " + uidl);
|
||||
continue; // found on disk, woo
|
||||
}
|
||||
}
|
||||
POP3Request pr = new POP3Request(mr, mail, false);
|
||||
fetches.add(pr);
|
||||
}
|
||||
@ -162,6 +191,11 @@ class MailCache {
|
||||
} else {
|
||||
mail.setBody(rb);
|
||||
}
|
||||
if (disk != null) {
|
||||
if (disk.saveMail(mail) && mail.hasBody()) {
|
||||
// TODO delete on server
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,6 +222,8 @@ class MailCache {
|
||||
public void delete(Collection<String> uidls) {
|
||||
List<String> toDelete = new ArrayList<String>(uidls.size());
|
||||
for (String uidl : uidls) {
|
||||
if (disk != null)
|
||||
disk.deleteMail(uidl);
|
||||
Mail mail = mails.get(uidl);
|
||||
if (mail == null)
|
||||
continue;
|
||||
|
280
apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java
Normal file
280
apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java
Normal file
@ -0,0 +1,280 @@
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import i2p.susi.debug.Debug;
|
||||
import i2p.susi.webmail.Messages;
|
||||
import i2p.susi.util.ReadBuffer;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.PasswordManager;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
|
||||
/**
|
||||
* Manage the on-disk cache.
|
||||
*
|
||||
* This is a custom format with subdirectories, gzipped files,
|
||||
* and the encoded UIDL in the file name.
|
||||
* We store either the headers or the full message.
|
||||
* No, it is not Maildir format but we could add Maildir-style
|
||||
* status suffixes (e.g. ":2.SR") later.
|
||||
*
|
||||
* Exporting to a Maildir format would be just ungzipping
|
||||
* each file to a flat directory.
|
||||
*
|
||||
* TODO draft and sent folders, cached server caps and config.
|
||||
*
|
||||
* @since 0.9.14
|
||||
*/
|
||||
class PersistentMailCache {
|
||||
|
||||
private final File _cacheDir;
|
||||
|
||||
private static final String DIR_SUSI = "susimail";
|
||||
private static final String DIR_CACHE = "cache";
|
||||
private static final String CACHE_PREFIX = "cache-";
|
||||
private static final String DIR_FOLDER = "cur"; // MailDir-like
|
||||
private static final String DIR_PREFIX = "s";
|
||||
private static final String FILE_PREFIX = "mail-";
|
||||
private static final String HDR_SUFFIX = ".hdr.txt.gz";
|
||||
private static final String FULL_SUFFIX = ".full.txt.gz";
|
||||
private static final String B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-~";
|
||||
|
||||
/**
|
||||
* Use the params to generate a unique directory name.
|
||||
* @param pass ignored
|
||||
*/
|
||||
public PersistentMailCache(String host, int port, String user, String pass) throws IOException {
|
||||
_cacheDir = makeCacheDirs(host, port, user, pass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all mails from disk.
|
||||
*
|
||||
* @return An e-mail or null
|
||||
*/
|
||||
public Collection<Mail> getMails() {
|
||||
List<Mail> rv = new ArrayList<Mail>();
|
||||
for (int j = 0; j < B64.length(); j++) {
|
||||
File subdir = new File(_cacheDir, DIR_PREFIX + B64.charAt(j));
|
||||
File[] files = subdir.listFiles();
|
||||
if (files == null)
|
||||
continue;
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File f = files[i];
|
||||
if (!f.isFile())
|
||||
continue;
|
||||
Mail mail = load(f);
|
||||
if (mail != null)
|
||||
rv.add(mail);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch any needed data from disk.
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
public boolean getMail(Mail mail, boolean headerOnly) {
|
||||
File f = getFullFile(mail.uidl);
|
||||
if (f.exists()) {
|
||||
ReadBuffer rb = read(f);
|
||||
if (rb != null) {
|
||||
mail.setBody(rb);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
f = getHeaderFile(mail.uidl);
|
||||
if (f.exists()) {
|
||||
ReadBuffer rb = read(f);
|
||||
if (rb != null) {
|
||||
mail.setHeader(rb);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data to disk.
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
public boolean saveMail(Mail mail) {
|
||||
ReadBuffer rb = mail.getBody();
|
||||
if (rb != null) {
|
||||
File f = getFullFile(mail.uidl);
|
||||
if (f.exists())
|
||||
return true; // already there, all good
|
||||
boolean rv = write(rb, f);
|
||||
if (rv)
|
||||
getHeaderFile(mail.uidl).delete();
|
||||
return rv;
|
||||
}
|
||||
rb = mail.getHeader();
|
||||
if (rb != null) {
|
||||
File f = getHeaderFile(mail.uidl);
|
||||
if (f.exists())
|
||||
return true; // already there, all good
|
||||
boolean rv = write(rb, f);
|
||||
return rv;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Delete data from disk.
|
||||
*/
|
||||
public void deleteMail(Mail mail) {
|
||||
deleteMail(mail.uidl);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Delete data from disk.
|
||||
*/
|
||||
public void deleteMail(String uidl) {
|
||||
getFullFile(uidl).delete();
|
||||
getHeaderFile(uidl).delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* ~/.i2p/susimail/cache/cache-xxxxx/cur/s[a-z]/mail-xxxxx.full.txt.gz
|
||||
* folder1 is the base.
|
||||
*/
|
||||
private static File makeCacheDirs(String host, int port, String user, String pass) throws IOException {
|
||||
File f = new SecureDirectory(I2PAppContext.getGlobalContext().getConfigDir(), DIR_SUSI);
|
||||
if (!f.exists() && !f.mkdir())
|
||||
throw new IOException("Cannot create " + f);
|
||||
f = new SecureDirectory(f, DIR_CACHE);
|
||||
if (!f.exists() && !f.mkdir())
|
||||
throw new IOException("Cannot create " + f);
|
||||
f = new SecureDirectory(f, CACHE_PREFIX + Base64.encode(user + host + port));
|
||||
if (!f.exists() && !f.mkdir())
|
||||
throw new IOException("Cannot create " + f);
|
||||
File base = new SecureDirectory(f, DIR_FOLDER);
|
||||
if (!base.exists() && !base.mkdir())
|
||||
throw new IOException("Cannot create " + base);
|
||||
for (int i = 0; i < B64.length(); i++) {
|
||||
f = new SecureDirectory(base, DIR_PREFIX + B64.charAt(i));
|
||||
if (!f.exists() && !f.mkdir())
|
||||
throw new IOException("Cannot create " + f);
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
private File getHeaderFile(String uidl) {
|
||||
return getFile(uidl, HDR_SUFFIX);
|
||||
}
|
||||
|
||||
private File getFullFile(String uidl) {
|
||||
return getFile(uidl, FULL_SUFFIX);
|
||||
}
|
||||
|
||||
private File getFile(String uidl, String suffix) {
|
||||
byte[] raw = DataHelper.getASCII(uidl);
|
||||
byte[] md5 = PasswordManager.md5Sum(raw);
|
||||
String db64 = Base64.encode(md5);
|
||||
File dir = new File(_cacheDir, DIR_PREFIX + db64.charAt(0));
|
||||
String b64 = Base64.encode(uidl);
|
||||
return new SecureFile(dir, FILE_PREFIX + b64 + suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save data to disk.
|
||||
*
|
||||
* @return success
|
||||
*/
|
||||
private static boolean write(ReadBuffer rb, File f) {
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new BufferedOutputStream(new GZIPOutputStream(new SecureFileOutputStream(f)));
|
||||
out.write(rb.content, rb.offset, rb.length);
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
Debug.debug(Debug.ERROR, "Error writing: " + f + ": " + ioe);
|
||||
return false;
|
||||
} finally {
|
||||
if (out != null)
|
||||
try { out.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on failure
|
||||
*/
|
||||
private static ReadBuffer read(File f) {
|
||||
InputStream in = null;
|
||||
try {
|
||||
long len = f.length();
|
||||
if (len > 16 * 1024 * 1024) {
|
||||
throw new IOException("too big");
|
||||
}
|
||||
in = new GZIPInputStream(new BufferedInputStream(new FileInputStream(f)));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream((int) len);
|
||||
int read = 0;
|
||||
byte buf[] = new byte[4*1024];
|
||||
while ( (read = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, read);
|
||||
}
|
||||
ReadBuffer rb = new ReadBuffer(out.toByteArray(), 0, out.size());
|
||||
return rb;
|
||||
} catch (IOException ioe) {
|
||||
Debug.debug(Debug.ERROR, "Error reading: " + f + ": " + ioe);
|
||||
return null;
|
||||
} finally {
|
||||
if (in != null)
|
||||
try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null on failure
|
||||
*/
|
||||
private static Mail load(File f) {
|
||||
String name = f.getName();
|
||||
String uidl;
|
||||
boolean headerOnly;
|
||||
if (name.endsWith(FULL_SUFFIX)) {
|
||||
uidl= Base64.decodeToString(name.substring(FILE_PREFIX.length(), name.length() - FULL_SUFFIX.length()));
|
||||
headerOnly = false;
|
||||
} else if (name.endsWith(HDR_SUFFIX)) {
|
||||
uidl= Base64.decodeToString(name.substring(FILE_PREFIX.length(), name.length() - HDR_SUFFIX.length()));
|
||||
headerOnly = true;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
if (uidl == null)
|
||||
return null;
|
||||
ReadBuffer rb = read(f);
|
||||
if (rb == null)
|
||||
return null;
|
||||
Mail mail = new Mail(uidl);
|
||||
if (headerOnly)
|
||||
mail.setHeader(rb);
|
||||
else
|
||||
mail.setBody(rb);
|
||||
return mail;
|
||||
}
|
||||
}
|
@ -172,6 +172,7 @@ public class WebMail extends HttpServlet
|
||||
private static final String CONFIG_COMPOSER_ROWS = "composer.rows";
|
||||
|
||||
private static final String CONFIG_BCC_TO_SELF = "composer.bcc.to.self";
|
||||
private static final String CONFIG_LEAVE_ON_SERVER = "pop3.leave.on.server";
|
||||
private static final String CONFIG_DEBUG = "debug";
|
||||
|
||||
private static final String RC_PROP_THEME = "routerconsole.theme";
|
||||
@ -194,21 +195,15 @@ public class WebMail extends HttpServlet
|
||||
*
|
||||
* @author susi
|
||||
*/
|
||||
/****
|
||||
private static class IDSorter implements Comparator<String> {
|
||||
private final MailCache mailCache;
|
||||
|
||||
/**
|
||||
* Set MailCache object, where to get Mails from
|
||||
* @param mailCache
|
||||
*/
|
||||
public IDSorter( MailCache mailCache )
|
||||
{
|
||||
this.mailCache = mailCache;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
public int compare(String arg0, String arg1) {
|
||||
Mail a = mailCache.getMail( arg0, MailCache.FETCH_HEADER );
|
||||
Mail b = mailCache.getMail( arg1, MailCache.FETCH_HEADER );
|
||||
@ -219,6 +214,7 @@ public class WebMail extends HttpServlet
|
||||
return a.id - b.id;
|
||||
}
|
||||
}
|
||||
****/
|
||||
|
||||
/**
|
||||
* sorts Mail objects by sender field
|
||||
@ -338,7 +334,7 @@ public class WebMail extends HttpServlet
|
||||
return (b == null) ? 0 : 1;
|
||||
if (b == null)
|
||||
return -1;
|
||||
return a.size - b.size;
|
||||
return a.getSize() - b.getSize();
|
||||
}
|
||||
}
|
||||
|
||||
@ -425,7 +421,7 @@ public class WebMail extends HttpServlet
|
||||
*/
|
||||
private static String sortHeader( String name, String label, String imgPath )
|
||||
{
|
||||
return label + " <a href=\"" + myself + "?" + name + "=up\"><img src=\"" +
|
||||
return label + " <a href=\"" + myself + "?" + name + "=up\"><img src=\"" +
|
||||
imgPath + "3up.png\" border=\"0\" alt=\"^\"></a><a href=\"" + myself +
|
||||
"?" + name + "=down\"><img src=\"" + imgPath + "3down.png\" border=\"0\" alt=\"v\"></a>";
|
||||
}
|
||||
@ -556,6 +552,7 @@ public class WebMail extends HttpServlet
|
||||
}
|
||||
if( prepareAttachment ) {
|
||||
if( html ) {
|
||||
// TODO can we at least show images safely?
|
||||
out.println( "<hr><p class=\"mailbody\">" );
|
||||
out.println( "<a target=\"_blank\" href=\"" + myself + "?" + DOWNLOAD + "=" +
|
||||
mailPart.hashCode() + "\">" + _("Download attachment {0}", ident) + "</a>" +
|
||||
@ -671,9 +668,10 @@ public class WebMail extends HttpServlet
|
||||
sessionObject.host = host;
|
||||
sessionObject.smtpPort = smtpPortNo;
|
||||
sessionObject.state = STATE_LIST;
|
||||
MailCache mc = new MailCache(mailbox);
|
||||
MailCache mc = new MailCache(mailbox, host, pop3PortNo, user, pass);
|
||||
sessionObject.mailCache = mc;
|
||||
sessionObject.folder = new Folder<String>();
|
||||
// TODO get through cache so we have the disk-only ones too
|
||||
String[] uidls = mailbox.getUIDLs();
|
||||
sessionObject.folder.setElements(uidls);
|
||||
if (uidls.length > 0) {
|
||||
@ -686,7 +684,7 @@ public class WebMail extends HttpServlet
|
||||
mc.getMail(reqs);
|
||||
}
|
||||
|
||||
sessionObject.folder.addSorter( SORT_ID, new IDSorter( sessionObject.mailCache ) );
|
||||
//sessionObject.folder.addSorter( SORT_ID, new IDSorter( sessionObject.mailCache ) );
|
||||
sessionObject.folder.addSorter( SORT_SENDER, new SenderSorter( sessionObject.mailCache ) );
|
||||
sessionObject.folder.addSorter( SORT_SUBJECT, new SubjectSorter( sessionObject.mailCache ) );
|
||||
sessionObject.folder.addSorter( SORT_DATE, new DateSorter( sessionObject.mailCache ) );
|
||||
@ -1013,6 +1011,7 @@ public class WebMail extends HttpServlet
|
||||
}
|
||||
if( buttonPressed( request, REFRESH ) ) {
|
||||
sessionObject.mailbox.refresh();
|
||||
// TODO get through cache so we have the disk-only ones too
|
||||
String[] uidls = sessionObject.mailbox.getUIDLs();
|
||||
if (uidls != null)
|
||||
sessionObject.folder.setElements(uidls);
|
||||
@ -1436,6 +1435,7 @@ public class WebMail extends HttpServlet
|
||||
* update folder content
|
||||
*/
|
||||
if( sessionObject.state != STATE_AUTH ) {
|
||||
// TODO get through cache so we have the disk-only ones too
|
||||
String[] uidls = sessionObject.mailbox.getUIDLs();
|
||||
if (uidls != null)
|
||||
sessionObject.folder.setElements(uidls);
|
||||
@ -1485,7 +1485,7 @@ public class WebMail extends HttpServlet
|
||||
);
|
||||
}
|
||||
out.println( "</head>\n<body>\n" +
|
||||
"<div class=\"page\"><p><img src=\"" + sessionObject.imgPath + "susimail.png\" alt=\"Susimail\"><br> </p>\n" +
|
||||
"<div class=\"page\"><p><img src=\"" + sessionObject.imgPath + "susimail.png\" alt=\"Susimail\"></p>\n" +
|
||||
"<form method=\"POST\" enctype=\"multipart/form-data\" action=\"" + myself + "\" accept-charset=\"UTF-8\">" );
|
||||
|
||||
if( sessionObject.error != null && sessionObject.error.length() > 0 ) {
|
||||
@ -1756,15 +1756,15 @@ public class WebMail extends HttpServlet
|
||||
|
||||
out.println( "<table cellspacing=\"0\" cellpadding=\"5\">\n" +
|
||||
"<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("From:") + "</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_FROM + "\" value=\"" + from + "\" " + ( !fixed.equalsIgnoreCase("false") ? "disabled" : "" ) +"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("To:") + "</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_TO + "\" value=\"" + to + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Cc:") + "</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_CC + "\" value=\"" + cc + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Bcc:") + "</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_BCC + "\" value=\"" + bcc + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("From") + ":</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_FROM + "\" value=\"" + from + "\" " + ( !fixed.equalsIgnoreCase("false") ? "disabled" : "" ) +"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("To") + ":</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_TO + "\" value=\"" + to + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Cc") + ":</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_CC + "\" value=\"" + cc + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Bcc") + ":</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_BCC + "\" value=\"" + bcc + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Bcc to self") + ": </td><td align=\"left\"><input type=\"checkbox\" class=\"optbox\" name=\"" + NEW_BCC_TO_SELF + "\" value=\"1\" " + (sessionObject.bccToSelf ? "checked" : "" ) + "></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Subject:") + "</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_SUBJECT + "\" value=\"" + subject + "\"></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Subject") + ":</td><td align=\"left\"><input type=\"text\" size=\"80\" name=\"" + NEW_SUBJECT + "\" value=\"" + subject + "\"></td></tr>\n" +
|
||||
"<tr><td colspan=\"2\" align=\"center\"><textarea cols=\"" + Config.getProperty( CONFIG_COMPOSER_COLS, 80 )+ "\" rows=\"" + Config.getProperty( CONFIG_COMPOSER_ROWS, 10 )+ "\" name=\"" + NEW_TEXT + "\">" + text + "</textarea>" +
|
||||
"<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>\n" +
|
||||
"<tr><td align=\"right\">" + _("Add Attachment:") + "</td><td align=\"left\"><input type=\"file\" size=\"50%\" name=\"" + NEW_FILENAME + "\" value=\"\"></td></tr>" +
|
||||
"<tr><td align=\"right\">" + _("Add Attachment") + ":</td><td align=\"left\"><input type=\"file\" size=\"50%\" name=\"" + NEW_FILENAME + "\" value=\"\"></td></tr>" +
|
||||
// TODO disable/hide in JS if no file selected
|
||||
"<tr><td> </td><td align=\"left\">" + button(NEW_UPLOAD, _("Add another attachment")) + "</td></tr>");
|
||||
|
||||
@ -1772,7 +1772,7 @@ public class WebMail extends HttpServlet
|
||||
boolean wroteHeader = false;
|
||||
for( Attachment attachment : sessionObject.attachments ) {
|
||||
if( !wroteHeader ) {
|
||||
out.println("<tr><td align=\"right\">" + _("Attachments:") + "</td>");
|
||||
out.println("<tr><td align=\"right\">" + _("Attachments") + ":</td>");
|
||||
wroteHeader = true;
|
||||
} else {
|
||||
out.println("<tr><td align=\"right\"> </td>");
|
||||
@ -1858,7 +1858,7 @@ public class WebMail extends HttpServlet
|
||||
|
||||
out.println("<table id=\"mailbox\" cellspacing=\"0\" cellpadding=\"5\">\n" +
|
||||
"<tr><td colspan=\"8\"><hr></td></tr>\n<tr>" +
|
||||
thSpacer + "<th>" + sortHeader( SORT_SENDER, _("Sender"), sessionObject.imgPath ) + "</th>" +
|
||||
thSpacer + "<th>" + sortHeader( SORT_SENDER, _("From"), sessionObject.imgPath ) + "</th>" +
|
||||
thSpacer + "<th>" + sortHeader( SORT_SUBJECT, _("Subject"), sessionObject.imgPath ) + "</th>" +
|
||||
thSpacer + "<th>" + sortHeader( SORT_DATE, _("Date"), sessionObject.imgPath ) +
|
||||
//sortHeader( SORT_ID, "", sessionObject.imgPath ) +
|
||||
@ -1893,7 +1893,7 @@ public class WebMail extends HttpServlet
|
||||
link + mail.shortSender + "</a></td><td> </td><td>" + link + mail.shortSubject + "</a></td><td> </td><td>" +
|
||||
// don't let date get split across lines
|
||||
mail.localFormattedDate.replace(" ", " ") + "</td><td> </td><td align=\"right\">" +
|
||||
DataHelper.formatSize2(mail.size) + "B</td></tr>" );
|
||||
DataHelper.formatSize2(mail.getSize()) + "B</td></tr>" );
|
||||
bg = 1 - bg;
|
||||
i++;
|
||||
}
|
||||
@ -1907,7 +1907,7 @@ public class WebMail extends HttpServlet
|
||||
button( CLEAR, _("Clear") ) +
|
||||
"<br>");
|
||||
out.println(
|
||||
_("Page Size:") + " <input type=\"text\" style=\"text-align: right;\" name=\"" + PAGESIZE + "\" size=\"4\" value=\"" + sessionObject.folder.getPageSize() + "\">" +
|
||||
_("Page Size") + ": <input type=\"text\" style=\"text-align: right;\" name=\"" + PAGESIZE + "\" size=\"4\" value=\"" + sessionObject.folder.getPageSize() + "\">" +
|
||||
button( SETPAGESIZE, _("Set") ) );
|
||||
}
|
||||
}
|
||||
@ -1944,12 +1944,12 @@ public class WebMail extends HttpServlet
|
||||
if( mail != null ) {
|
||||
out.println( "<table cellspacing=\"0\" cellpadding=\"5\">\n" +
|
||||
"<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>\n" +
|
||||
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("From:") +
|
||||
"</td><td align=\"left\">" + quoteHTML( mail.sender ) + "</td></tr>\n" +
|
||||
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("Subject:") +
|
||||
"</td><td align=\"left\">" + quoteHTML( mail.formattedSubject ) + "</td></tr>\n" +
|
||||
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("Date:") +
|
||||
"</td><td align=\"left\">" + mail.quotedDate + "</td></tr>\n" +
|
||||
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("From") +
|
||||
":</td><td align=\"left\">" + quoteHTML( mail.sender ) + "</td></tr>\n" +
|
||||
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("Subject") +
|
||||
":</td><td align=\"left\">" + quoteHTML( mail.formattedSubject ) + "</td></tr>\n" +
|
||||
"<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.hasPart()) {
|
||||
showPart( out, mail.getPart(), 0, SHOW_HTML );
|
||||
|
@ -52,6 +52,7 @@ public class POP3MailBox {
|
||||
private int mails;
|
||||
|
||||
private boolean connected;
|
||||
private boolean gotCAPA;
|
||||
private boolean supportsPipelining;
|
||||
private boolean supportsTOP;
|
||||
private boolean supportsUIDL;
|
||||
@ -278,6 +279,7 @@ public class POP3MailBox {
|
||||
}
|
||||
if (srs.isEmpty())
|
||||
return;
|
||||
// TODO don't quit now, just set timer to quit later
|
||||
SendRecv sr = new SendRecv("QUIT", Mode.A1);
|
||||
srs.add(sr);
|
||||
try {
|
||||
@ -490,7 +492,7 @@ public class POP3MailBox {
|
||||
private void connect() {
|
||||
Debug.debug(Debug.DEBUG, "connect()");
|
||||
if (Debug.getLevel() == Debug.DEBUG)
|
||||
(new Exception()).printStackTrace();
|
||||
(new Exception("I did it")).printStackTrace();
|
||||
|
||||
clear();
|
||||
|
||||
@ -565,16 +567,17 @@ public class POP3MailBox {
|
||||
* @since 0.9.13
|
||||
*/
|
||||
private boolean doHandshake() throws IOException {
|
||||
// can we always pipeline this ?
|
||||
supportsPipelining = false;
|
||||
supportsUIDL = false;
|
||||
supportsTOP = false;
|
||||
List<SendRecv> cmds = new ArrayList<SendRecv>(2);
|
||||
cmds.add(new SendRecv(null, Mode.A1));
|
||||
SendRecv capa = new SendRecv("CAPA", Mode.LS);
|
||||
cmds.add(capa);
|
||||
SendRecv capa = null;
|
||||
if (gotCAPA) {
|
||||
Debug.debug(Debug.DEBUG, "Skipping CAPA");
|
||||
} else {
|
||||
capa = new SendRecv("CAPA", Mode.LS);
|
||||
cmds.add(capa);
|
||||
}
|
||||
boolean rv = sendCmds(cmds);
|
||||
if (rv) {
|
||||
if (rv && capa != null) {
|
||||
if (capa.ls != null) {
|
||||
for (String cap : capa.ls) {
|
||||
String t = cap.trim();
|
||||
@ -586,10 +589,11 @@ public class POP3MailBox {
|
||||
supportsTOP = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Debug.debug(Debug.DEBUG, "POP3 server caps: pipelining? " + supportsPipelining +
|
||||
gotCAPA = true;
|
||||
Debug.debug(Debug.DEBUG, "POP3 server caps: pipelining? " + supportsPipelining +
|
||||
" UIDL? " + supportsUIDL +
|
||||
" TOP? " + supportsTOP);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
14
history.txt
14
history.txt
@ -1,3 +1,17 @@
|
||||
2014-04-22 zzz
|
||||
* SusiMail:
|
||||
- Add persistent cache
|
||||
|
||||
2014-04-21 zzz
|
||||
* SusiMail:
|
||||
- Pipeline all deletes and quit
|
||||
- Don't reconnect after delete and quit
|
||||
- Verify connected before each POP3 operation
|
||||
- Don't clear messages if a reconnection fails
|
||||
- Use locale-based sorting for strings
|
||||
- Increase limit for full fetch again
|
||||
- Increase default page size back again
|
||||
|
||||
2014-04-21 dg
|
||||
* findbugs: mostly stream closure fixes in router, apps, core
|
||||
|
||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = CoreVersion.VERSION;
|
||||
public final static long BUILD = 7;
|
||||
public final static long BUILD = 8;
|
||||
|
||||
/** for example "-test" */
|
||||
public final static String EXTRA = "";
|
||||
|
Reference in New Issue
Block a user