* SusiMail:

- Extend session expiration (ticket #1253)
   - Handle non-UTF8 encoding on header lines (ticket #508)
   - Display dates in current locale and time zone
   - Display sender name on message view
   - Remove sort-by-ID buttons
   - Hide "reload config" button unless config file is present
   - Increase default page size
   - Add dependency tracking to build
This commit is contained in:
zzz
2014-04-18 01:48:03 +00:00
parent eb96a74e32
commit 028776de88
9 changed files with 125 additions and 42 deletions

View File

@@ -13,8 +13,22 @@
<condition property="no.bundle">
<isfalse value="${require.gettext}" />
</condition>
<condition property="depend.available">
<typefound name="depend" />
</condition>
<target name="depend" if="depend.available">
<depend
cache="../../build"
srcdir="./src/src"
destdir="./src/WEB-INF/classes" >
<!-- Depend on classes instead of jars where available -->
<classpath>
<pathelement location="../../core/java/build/obj" />
</classpath>
</depend>
</target>
<target name="compile">
<target name="compile" depends="depend" >
<mkdir dir="./src/WEB-INF/classes" />
<javac
srcdir="./src/src"

View File

@@ -13,7 +13,7 @@
<url-pattern>/susimail</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>15</session-timeout>
<session-timeout>1440</session-timeout>
</session-config>
<!--

View File

@@ -68,6 +68,16 @@ public class Config {
return result;
}
/**
* Don't bother showing a reload config button if this returns false.
* @since 0.9.13
*/
public static boolean hasConfigFile() {
File cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), "susimail.config");
return cfg.exists();
}
/**
*
*

View File

@@ -37,6 +37,9 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import net.i2p.I2PAppContext;
/**
* data structure to hold a single message, mostly used with folder view and sorting
@@ -51,8 +54,12 @@ public class Mail {
public int id, size;
public String sender, reply, subject, dateString,
formattedSender, formattedSubject, formattedDate,
shortSender, shortSubject, quotedDate, uidl;
formattedSender, formattedSubject,
formattedDate, // US Locale, UTC
localFormattedDate, // Current Locale, local time zone
shortSender, shortSubject,
quotedDate, // Current Locale, local time zone, longer format
uidl;
public Date date;
public ReadBuffer header, body;
public MailPart part;
@@ -68,6 +75,7 @@ public class Mail {
formattedSender = unknown;
formattedSubject = unknown;
formattedDate = unknown;
localFormattedDate = unknown;
shortSender = unknown;
shortSubject = unknown;
quotedDate = unknown;
@@ -110,7 +118,7 @@ public class Mail {
for( int i = 0; i < tokens.length; i++ ) {
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
return "<" + tokens[i] + ">";
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
return tokens[i];
}
@@ -149,7 +157,16 @@ public class Mail {
}
public void parseHeaders()
{
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
DateFormat dateFormatter = new SimpleDateFormat("yyyy-mm-dd HH:mm");
DateFormat localDateFormatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
DateFormat longLocalDateFormatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
// the router sets the JVM time zone to UTC but saves the original here so we can get it
String systemTimeZone = I2PAppContext.getGlobalContext().getProperty("i2p.systemTimeZone");
if (systemTimeZone != null) {
TimeZone tz = TimeZone.getTimeZone(systemTimeZone);
localDateFormatter.setTimeZone(tz);
longLocalDateFormatter.setTimeZone(tz);
}
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
error = "";
@@ -195,7 +212,9 @@ public class Mail {
try {
date = mailDateFormatter.parse( dateString );
formattedDate = dateFormatter.format( date );
quotedDate = html.encode( dateString );
localFormattedDate = localDateFormatter.format( date );
//quotedDate = html.encode( dateString );
quotedDate = longLocalDateFormatter.format(date);
}
catch (ParseException e) {
date = null;
@@ -211,16 +230,16 @@ public class Mail {
shortSubject = html.encode( shortSubject );
}
else if( line.toLowerCase(Locale.US).startsWith( "reply-to:" ) ) {
reply = Mail.getAddress( line.substring( 9 ).trim() );
reply = getAddress( line.substring( 9 ).trim() );
}
else if( line.startsWith( "To:" ) ) {
ArrayList<String> list = new ArrayList<String>();
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
getRecipientsFromList( list, line.substring( 3 ).trim(), true );
to = list.toArray();
}
else if( line.startsWith( "Cc:" ) ) {
ArrayList<String> list = new ArrayList<String>();
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
getRecipientsFromList( list, line.substring( 3 ).trim(), true );
cc = list.toArray();
}
}

View File

@@ -370,7 +370,9 @@ public class WebMail extends HttpServlet
*/
private static String sortHeader( String name, String label, String imgPath )
{
return "" + label + "&nbsp;<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>";
return label + "&nbsp;<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>";
}
/**
* check, if a given button "was pressed" in the received http request
@@ -758,7 +760,7 @@ public class WebMail extends HttpServlet
sessionObject.subject = "Re: " + mail.formattedSubject;
StringWriter text = new StringWriter();
PrintWriter pw = new PrintWriter( text );
pw.println( _("On {0} {1} wrote:", mail.formattedDate, sessionObject.replyTo) );
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 );
@@ -1273,7 +1275,7 @@ public class WebMail extends HttpServlet
if( sessionObject.state == STATE_LIST ) {
processFolderButtons( sessionObject, request );
for( Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) {
String uidl = (String)it.next();
String uidl = it.next();
Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FETCH_HEADER );
if( mail != null && mail.error.length() > 0 ) {
sessionObject.error += mail.error;
@@ -1579,9 +1581,10 @@ public class WebMail extends HttpServlet
{
out.println( button( SEND, _("Send") ) +
button( CANCEL, _("Cancel") ) + spacer +
(sessionObject.attachments != null && (!sessionObject.attachments.isEmpty()) ? button( DELETE_ATTACHMENT, _("Delete Attachment") ) : button2( DELETE_ATTACHMENT, _("Delete Attachment") ) ) + spacer +
button( RELOAD, _("Reload Config") ) + spacer +
button( LOGOUT, _("Logout") ) );
(sessionObject.attachments != null && (!sessionObject.attachments.isEmpty()) ? button( DELETE_ATTACHMENT, _("Delete Attachment") ) : button2( DELETE_ATTACHMENT, _("Delete Attachment") ) ) + spacer);
if (Config.hasConfigFile())
out.println(button( RELOAD, _("Reload Config") ) + spacer);
out.println(button( LOGOUT, _("Logout") ) );
String from = request.getParameter( NEW_FROM );
String fixed = Config.getProperty( CONFIG_SENDER_FIXED, "true" );
@@ -1672,18 +1675,21 @@ public class WebMail extends HttpServlet
button( REPLYALL, _("Reply All") ) +
button( FORWARD, _("Forward") ) + spacer +
button( DELETE, _("Delete") ) + spacer +
button( REFRESH, _("Check Mail") ) + spacer +
button( RELOAD, _("Reload Config") ) + spacer +
button( LOGOUT, _("Logout") ) + "<table id=\"mailbox\" cellspacing=\"0\" cellpadding=\"5\">\n" +
button( REFRESH, _("Check Mail") ) + spacer);
if (Config.hasConfigFile())
out.println(button( RELOAD, _("Reload Config") ) + spacer);
out.println(button( LOGOUT, _("Logout") ) + "<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_SUBJECT, _("Subject"), sessionObject.imgPath ) + "</th>" +
thSpacer + "<th>" + sortHeader( SORT_DATE, _("Date"), sessionObject.imgPath ) + sortHeader( SORT_ID, "", sessionObject.imgPath ) + "</th>" +
thSpacer + "<th>" + sortHeader( SORT_DATE, _("Date"), sessionObject.imgPath ) +
//sortHeader( SORT_ID, "", sessionObject.imgPath ) +
"</th>" +
thSpacer + "<th>" + sortHeader( SORT_SIZE, _("Size"), sessionObject.imgPath ) + "</th></tr>" );
int bg = 0;
int i = 0;
for( Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) {
String uidl = (String)it.next();
String uidl = it.next();
Mail mail = sessionObject.mailCache.getMail( uidl, MailCache.FETCH_HEADER );
String link = "<a href=\"" + myself + "?" + SHOW + "=" + i + "\">";
@@ -1705,7 +1711,9 @@ public class WebMail extends HttpServlet
", invert=" + sessionObject.invert +
", clear=" + sessionObject.clear );
out.println( "<tr class=\"list" + bg + "\"><td><input type=\"checkbox\" class=\"optbox\" name=\"check" + i + "\" value=\"1\"" +
( idChecked ? "checked" : "" ) + ">" + ( RELEASE ? "" : "" + i ) + "</td><td>" + link + mail.shortSender + "</a></td><td>&nbsp;</td><td>" + link + mail.shortSubject + "</a></td><td>&nbsp;</td><td>" + mail.formattedDate + "</td><td>&nbsp;</td><td>" + ngettext("1 Byte", "{0} Bytes", mail.size) + "</td></tr>" );
( idChecked ? "checked" : "" ) + ">" + ( RELEASE ? "" : "" + i ) + "</td><td>" +
link + mail.shortSender + "</a></td><td>&nbsp;</td><td>" + link + mail.shortSubject + "</a></td><td>&nbsp;</td><td>" +
mail.localFormattedDate + "</td><td>&nbsp;</td><td>" + ngettext("1 Byte", "{0} Bytes", mail.size) + "</td></tr>" );
bg = 1 - bg;
i++;
}
@@ -1754,15 +1762,19 @@ public class WebMail extends HttpServlet
button( DELETE, _("Delete") ) + spacer +
( sessionObject.folder.isFirstElement( sessionObject.showUIDL ) ? button2( PREV, _("Previous") ) : button( PREV, _("Previous") ) ) +
( sessionObject.folder.isLastElement( sessionObject.showUIDL ) ? button2( NEXT, _("Next") ) : button( NEXT, _("Next") ) ) + spacer +
button( LIST, _("Back to Folder") ) + spacer +
button( RELOAD, _("Reload Config") ) + spacer +
button( LOGOUT, _("Logout") ) );
button( LIST, _("Back to Folder") ) + spacer);
if (Config.hasConfigFile())
out.println(button( RELOAD, _("Reload Config") ) + spacer);
out.println(button( LOGOUT, _("Logout") ) );
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\">" + _("From:") + "</td><td align=\"left\">" + quoteHTML( mail.formattedSender ) + "</td></tr>\n" +
"<tr class=\"mailhead\"><td align=\"right\">" + _("Date:") + "</td><td align=\"left\">" + mail.quotedDate + "</td></tr>\n" +
"<tr class=\"mailhead\"><td align=\"right\">" + _("Subject:") + "</td><td align=\"left\">" + quoteHTML( mail.formattedSubject ) + "</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\">" + _("Date:") +
"</td><td align=\"left\">" + mail.quotedDate + "</td></tr>\n" +
"<tr class=\"mailhead\"><td align=\"right\" valign=\"top\">" + _("Subject:") +
"</td><td align=\"left\">" + quoteHTML( mail.formattedSubject ) + "</td></tr>\n" +
"<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>" );
if( mail.body != null ) {
showPart( out, mail.part, 0, SHOW_HTML );

View File

@@ -23,12 +23,14 @@
*/
package i2p.susi.webmail.encoding;
import i2p.susi.debug.Debug;
import i2p.susi.util.HexTable;
import i2p.susi.util.ReadBuffer;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
/**
* Ref:
@@ -170,7 +172,8 @@ public class HeaderLine implements Encoding {
if( length > 0 ) {
if( in[offset] == '?' ) {
// System.err.println( "=? found at " + ( offset -1 ) );
int f2 = offset + 1;
int f1 = offset;
int f2 = f1 + 1;
// FIXME save charset here ticket #508
for( ; f2 < end && in[f2] != '?'; f2++ );
if( f2 < end ) {
@@ -201,18 +204,31 @@ public class HeaderLine implements Encoding {
try {
// System.err.println( "decode(" + (f3 + 1) + "," + ( f4 - f3 - 1 ) + ")" );
tmp = e.decode( in, f3 + 1, f4 - f3 - 1 );
// FIXME use saved charset here ticket #508
for( int j = 0; j < tmp.length; j++ ) {
byte d = tmp.content[ tmp.offset + j ];
out[written++] = ( d == '_' ? 32 : d );
// get charset
String charset = new String(in, f1 + 1, f2 - f1 - 1, "ISO-8859-1");
String clc = charset.toLowerCase(Locale.US);
if (clc.equals("utf-8") || clc.equals("utf8")) {
for( int j = 0; j < tmp.length; j++ ) {
byte d = tmp.content[ tmp.offset + j ];
out[written++] = ( d == '_' ? 32 : d );
}
} else {
// decode string
String decoded = new String(tmp.content, tmp.offset, tmp.length, charset);
// encode string
byte[] utf8 = decoded.getBytes("UTF-8");
for( int j = 0; j < utf8.length; j++ ) {
byte d = utf8[j];
out[written++] = ( d == '_' ? 32 : d );
}
}
int distance = f4 + 2 - offset;
offset += distance;
length -= distance;
lastCharWasQuoted = true;
continue;
}
catch (Exception e1) {
} catch (Exception e1) {
Debug.debug(Debug.ERROR, e1.toString());
}
}
}

View File

@@ -38,19 +38,19 @@ import java.net.Socket;
*/
public class SMTPClient {
Socket socket;
byte buffer[];
private Socket socket;
private final byte buffer[];
public String error;
String lastResponse;
private String lastResponse;
private static Encoding base64 = null;
private static final Encoding base64;
static {
base64 = EncodingFactory.getEncoding( "base64" );
}
public SMTPClient()
{
socket = null;
buffer = new byte[10240];
error = "";
lastResponse = "";

View File

@@ -11,10 +11,10 @@ susimail.fast.start=true
susimail.sender.fixed=true
susimail.sender.domain=mail.i2p
susimail.pager.pagesize=10
susimail.pager.pagesize=40
susimail.composer.cols=80
susimail.composer.rows=15
susimail.composer.bcc.to.self=true
susimail.date.format=MM/dd/yyyy HH:mm:ss
susimail.date.format=MM/dd/yyyy HH:mm:ss

View File

@@ -1,3 +1,15 @@
2014-04-18 zzz
* configclients: Don't allow console disable
* SusiMail:
- Extend session expiration (ticket #1253)
- Handle non-UTF8 encoding on header lines (ticket #508)
- Display dates in current locale and time zone
- Display sender name on message view
- Remove sort-by-ID buttons
- Hide "reload config" button unless config file is present
- Increase default page size
- Add dependency tracking to build
2014-04-17 zzz
* i2psnark: Randomize announce list order and limit size
* SSU: SessionRequest replay prevention (ticket #1212)