merge of '9f159df098940fb0feecf6eae0c990c62736bb9c'

and 'ba82e9e4c57bd8d9f567c9252fe7b5815972e370'
This commit is contained in:
zzz
2014-04-18 01:50:14 +00:00
10 changed files with 133 additions and 49 deletions

View File

@@ -113,10 +113,10 @@ public class ConfigClientsHelper extends HelperBase {
renderForm(buf, ""+cur, ca.clientName,
// urlify, enabled
false, !ca.disabled,
// read only
// read only, preventDisable
// dangerous, but allow editing the console args too
//"webConsole".equals(ca.clientName) || "Web console".equals(ca.clientName),
false,
false, RouterConsoleRunner.class.getName().equals(ca.className),
// description, edit
ca.className + ((ca.args != null) ? " " + ca.args : ""), (""+cur).equals(_edit),
// show edit button, show update button
@@ -129,7 +129,7 @@ public class ConfigClientsHelper extends HelperBase {
}
if ("new".equals(_edit))
renderForm(buf, "" + clients.size(), "", false, false, false, "", true, false, false, false, false, false);
renderForm(buf, "" + clients.size(), "", false, false, false, false, "", true, false, false, false, false, false);
buf.append("</table>\n");
return buf.toString();
}
@@ -150,7 +150,8 @@ public class ConfigClientsHelper extends HelperBase {
String val = props.getProperty(name);
boolean isRunning = WebAppStarter.isWebAppRunning(app);
renderForm(buf, app, app, !"addressbook".equals(app),
"true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war",
"true".equals(val), RouterConsoleRunner.ROUTERCONSOLE.equals(app),
RouterConsoleRunner.ROUTERCONSOLE.equals(app), app + ".war",
false, false, false, isRunning, false, !isRunning);
}
}
@@ -239,7 +240,7 @@ public class ConfigClientsHelper extends HelperBase {
enableStop &= PluginStarter.isPluginRunning(app, _context);
boolean enableStart = !PluginStarter.isPluginRunning(app, _context);
renderForm(buf, app, app, false,
"true".equals(val), false, desc.toString(), false, false,
"true".equals(val), false, false, desc.toString(), false, false,
updateURL != null, enableStop, true, enableStart);
}
}
@@ -253,7 +254,7 @@ public class ConfigClientsHelper extends HelperBase {
* ro trumps edit and showEditButton
*/
private void renderForm(StringBuilder buf, String index, String name, boolean urlify,
boolean enabled, boolean ro, String desc, boolean edit,
boolean enabled, boolean ro, boolean preventDisable, String desc, boolean edit,
boolean showEditButton, boolean showUpdateButton, boolean showStopButton,
boolean showDeleteButton, boolean showStartButton) {
String escapeddesc = DataHelper.escapeHTML(desc);
@@ -275,7 +276,7 @@ public class ConfigClientsHelper extends HelperBase {
buf.append("</td><td align=\"center\" width=\"10%\"><input type=\"checkbox\" class=\"optbox\" name=\"").append(index).append(".enabled\" value=\"true\" ");
if (enabled) {
buf.append("checked=\"checked\" ");
if (ro)
if (ro || preventDisable)
buf.append("disabled=\"disabled\" ");
}
buf.append("></td><td align=\"center\" width=\"15%\">");

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)