diff --git a/apps/susimail/src/icons/wrench.png b/apps/susimail/src/icons/wrench.png new file mode 100644 index 0000000000000000000000000000000000000000..5c8213fef5ab969f03189d4367e32e597e38bd7f Binary files /dev/null and b/apps/susimail/src/icons/wrench.png differ diff --git a/apps/susimail/src/src/i2p/susi/util/Config.java b/apps/susimail/src/src/i2p/susi/util/Config.java index 4e068669df0bd3a3dfba05f069fc99ae80313e71..a84ccede473ab1bde9d2ffcd6d38cbbc44224f46 100644 --- a/apps/susimail/src/src/i2p/susi/util/Config.java +++ b/apps/susimail/src/src/i2p/susi/util/Config.java @@ -29,10 +29,12 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; +import java.util.Map; import java.util.Properties; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; +import net.i2p.util.OrderedProperties; /** * Warning - static - not for use by multiple applications or prefixes @@ -48,7 +50,7 @@ public class Config { * * @param name */ - public static String getProperty( String name ) + public synchronized static String getProperty( String name ) { if( configPrefix != null ) name = configPrefix + name; @@ -79,7 +81,7 @@ public class Config { * Don't bother showing a reload config button if this returns false. * @since 0.9.13 */ - public static boolean hasConfigFile() { + public synchronized static boolean hasConfigFile() { File cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), "susimail.config"); return cfg.exists(); } @@ -88,7 +90,7 @@ public class Config { * * */ - public static void reloadConfiguration() + public synchronized static void reloadConfiguration() { // DEBUG level logging won't work here since we haven't loaded the config yet... properties = new Properties(); @@ -104,7 +106,7 @@ public class Config { try { File cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), "susimail.config"); if (cfg.exists()) { - config = new Properties(); + config = new OrderedProperties(); DataHelper.loadProps(config, cfg); } } catch (Exception e) { @@ -112,22 +114,75 @@ public class Config { } } + /** + * Returns the properties, sorted, WITHOUT the prefix + * + * @since 0.9.13 + */ + public synchronized static Properties getProperties() { + Properties rv = new OrderedProperties(); + if (properties != null) { + if (configPrefix == null) { + rv.putAll(properties); + } else { + for (Map.Entry<Object, Object> e : properties.entrySet()) { + String k = (String) e.getKey(); + if (k.startsWith(configPrefix)) + rv.put(k.substring(configPrefix.length()), e.getValue()); + } + } + } + if (config != null) { + if (configPrefix == null) { + rv.putAll(config); + } else { + for (Map.Entry<Object, Object> e : config.entrySet()) { + String k = (String) e.getKey(); + if (k.startsWith(configPrefix)) + rv.put(k.substring(configPrefix.length()), e.getValue()); + } + } + } + return rv; + } + + /** + * Saves the properties. A property not in newProps will be removed but + * will not override the default in the resource. + * + * @param newProps non-null WITHOUT the prefix + * @since 0.9.13 + */ + public synchronized static void saveConfiguration(Properties newProps) throws IOException { + Properties toSave = new OrderedProperties(); + for (Map.Entry<Object, Object> e : newProps.entrySet()) { + Object k = e.getKey(); + if (configPrefix != null) + k = configPrefix + k; + toSave.put(k, e.getValue()); + } + config = toSave; + File cfg = new File(I2PAppContext.getGlobalContext().getConfigDir(), "susimail.config"); + DataHelper.storeProps(toSave, cfg); + } + /** * * @param name * @param defaultValue */ - public static String getProperty( String name, String defaultValue ) + public synchronized static String getProperty( String name, String defaultValue ) { String result = getProperty( name ); return result != null ? result : defaultValue; } + /** * * @param name * @param defaultValue */ - public static int getProperty( String name, int defaultValue ) + public synchronized static int getProperty( String name, int defaultValue ) { int result = defaultValue; @@ -148,7 +203,7 @@ public class Config { * Static! Not for use by multiple applications! * @param prefix */ - public static void setPrefix( String prefix ) + public synchronized static void setPrefix( String prefix ) { configPrefix = prefix.endsWith( "." ) ? prefix : prefix + "."; } diff --git a/apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java b/apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java index 24056aaa582084cbfeb219017d0e743a69f692b1..fe858fb1009b2a487f77384f5c24aa724cd4d2da 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java +++ b/apps/susimail/src/src/i2p/susi/webmail/PersistentMailCache.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -46,6 +47,15 @@ import net.i2p.util.SecureFileOutputStream; */ class PersistentMailCache { + /** + * One lock for each user in the whole JVM, to protect against multiple sessions. + * One big lock for the whole cache dir, not one for each file or subdir. + * Never expired. + * Sure, if we did a maildir format we wouldn't need this. + */ + private static final ConcurrentHashMap<String, Object> _locks = new ConcurrentHashMap<String, Object>(); + + private final Object _lock; private final File _cacheDir; private static final String DIR_SUSI = "susimail"; @@ -63,8 +73,10 @@ class PersistentMailCache { * @param pass ignored */ public PersistentMailCache(String host, int port, String user, String pass) throws IOException { - _cacheDir = makeCacheDirs(host, port, user, pass); - // TODO static locking + _lock = getLock(host, port, user, pass); + synchronized(_lock) { + _cacheDir = makeCacheDirs(host, port, user, pass); + } } /** @@ -73,6 +85,12 @@ class PersistentMailCache { * @return a new collection */ public Collection<Mail> getMails() { + synchronized(_lock) { + return locked_getMails(); + } + } + + private Collection<Mail> locked_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)); @@ -97,6 +115,12 @@ class PersistentMailCache { * @return success */ public boolean getMail(Mail mail, boolean headerOnly) { + synchronized(_lock) { + return locked_getMail(mail, headerOnly); + } + } + + private boolean locked_getMail(Mail mail, boolean headerOnly) { File f = getFullFile(mail.uidl); if (f.exists()) { ReadBuffer rb = read(f); @@ -122,6 +146,12 @@ class PersistentMailCache { * @return success */ public boolean saveMail(Mail mail) { + synchronized(_lock) { + return locked_saveMail(mail); + } + } + + private boolean locked_saveMail(Mail mail) { ReadBuffer rb = mail.getBody(); if (rb != null) { File f = getFullFile(mail.uidl); @@ -156,8 +186,16 @@ class PersistentMailCache { * Delete data from disk. */ public void deleteMail(String uidl) { - getFullFile(uidl).delete(); - getHeaderFile(uidl).delete(); + synchronized(_lock) { + getFullFile(uidl).delete(); + getHeaderFile(uidl).delete(); + } + } + + private static Object getLock(String host, int port, String user, String pass) { + Object lock = new Object(); + Object old = _locks.putIfAbsent(user + host + port, lock); + return (old != null) ? old : lock; } /** diff --git a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java index 207487508bda9c328eb3637a667e44f8aecff718..1539a5cde822831bde6be4acfe328200f309a5ea 100644 --- a/apps/susimail/src/src/i2p/susi/webmail/WebMail.java +++ b/apps/susimail/src/src/i2p/susi/webmail/WebMail.java @@ -53,6 +53,8 @@ import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Properties; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -91,7 +93,6 @@ public class WebMail extends HttpServlet private static final int STATE_LIST = 2; private static final int STATE_SHOW = 3; private static final int STATE_NEW = 4; - // TODO private static final int STATE_CONFIG = 5; // TODO generate from servlet name to allow for renaming or multiple instances @@ -113,7 +114,9 @@ public class WebMail extends HttpServlet */ private static final String LOGOUT = "logout"; private static final String RELOAD = "reload"; + private static final String SAVE = "save"; private static final String REFRESH = "refresh"; + private static final String CONFIGURE = "configure"; private static final String NEW = "new"; private static final String REPLY = "reply"; private static final String REPLYALL = "replyall"; @@ -158,6 +161,8 @@ public class WebMail extends HttpServlet private static final String SORT_DATE = "sort_date"; private static final String SORT_SIZE = "sort_size"; + private static final String CONFIG_TEXT = "config_text"; + private static final boolean SHOW_HTML = true; private static final boolean TEXT_ONLY = false; @@ -827,9 +832,9 @@ public class WebMail extends HttpServlet * @param sessionObject * @param request */ - private static void processLogout( SessionObject sessionObject, RequestWrapper request ) + private static void processLogout( SessionObject sessionObject, RequestWrapper request, boolean isPOST ) { - if( buttonPressed( request, LOGOUT ) ) { + if( buttonPressed( request, LOGOUT ) && isPOST) { Debug.debug(Debug.DEBUG, "LOGOUT, REMOVING SESSION"); HttpSession session = request.getSession(); session.removeAttribute( "sessionObject" ); @@ -842,12 +847,12 @@ public class WebMail extends HttpServlet } sessionObject.info += _("User logged out.") + "<br>"; sessionObject.state = STATE_AUTH; - } - else if( sessionObject.mailbox == null ) { + } else if( sessionObject.mailbox == null ) { sessionObject.error += _("Internal error, lost connection.") + "<br>"; sessionObject.state = STATE_AUTH; } } + /** * Process all buttons, which possibly change internal state. * Also processes ?show=x for a GET @@ -864,8 +869,8 @@ public class WebMail extends HttpServlet if( sessionObject.state == STATE_AUTH && isPOST ) processLogin( sessionObject, request ); - if( sessionObject.state != STATE_AUTH && isPOST ) - processLogout( sessionObject, request ); + if( sessionObject.state != STATE_AUTH && sessionObject.state != STATE_CONFIG ) + processLogout( sessionObject, request, isPOST ); /* * compose dialog @@ -907,9 +912,9 @@ public class WebMail extends HttpServlet } } /* - * message dialog + * message dialog or config */ - if( sessionObject.state == STATE_SHOW && isPOST ) { + if((sessionObject.state == STATE_SHOW || sessionObject.state == STATE_CONFIG) && isPOST ) { if( buttonPressed( request, LIST ) ) { sessionObject.state = STATE_LIST; } else if (buttonPressed( request, CANCEL ) || @@ -930,6 +935,18 @@ public class WebMail extends HttpServlet sessionObject.state = STATE_LIST; } } + + /* + * config + */ + if (sessionObject.state == STATE_CONFIG && isPOST) { + if (buttonPressed(request, OFFLINE)) { // lost + sessionObject.state = STATE_AUTH; + } else if (buttonPressed(request, LOGIN)) { // lost + sessionObject.state = STATE_AUTH; + } + } + /* * buttons on both folder and message dialog */ @@ -1329,7 +1346,7 @@ public class WebMail extends HttpServlet /* * process paging buttons */ - if (buttonPressed(request, PAGESIZE) && !buttonPressed(request, RELOAD)) { + if (buttonPressed(request, SETPAGESIZE)) { try { int pageSize = Math.max(5, Integer.parseInt(request.getParameter(PAGESIZE))); int oldPageSize = sessionObject.folder.getPageSize(); @@ -1419,6 +1436,59 @@ public class WebMail extends HttpServlet } } } + + /* + * process config buttons, both entering and exiting + */ + private static void processConfigButtons(SessionObject sessionObject, RequestWrapper request) { + if (buttonPressed(request, SAVE)) { + try { + String raw = request.getParameter(CONFIG_TEXT); + if (raw == null) + return; + Properties props = new Properties(); + DataHelper.loadProps(props, new ByteArrayInputStream(DataHelper.getUTF8(raw))); + Config.saveConfiguration(props); + String ps = props.getProperty(Folder.PAGESIZE); + if (sessionObject.folder != null && ps != null) { + try { + int pageSize = Math.max(5, Integer.parseInt(request.getParameter(PAGESIZE))); + int oldPageSize = sessionObject.folder.getPageSize(); + if( pageSize != oldPageSize ) + sessionObject.folder.setPageSize( pageSize ); + } catch( NumberFormatException nfe ) {} + } + sessionObject.state = sessionObject.folder != null ? STATE_LIST : STATE_AUTH; + sessionObject.info = _("Configuration saved"); + } catch (IOException ioe) { + sessionObject.error = ioe.toString(); + } + } else if (buttonPressed(request, SETPAGESIZE)) { + try { + int pageSize = Math.max(5, Integer.parseInt(request.getParameter(PAGESIZE))); + Properties props = Config.getProperties(); + props.setProperty(Folder.PAGESIZE, String.valueOf(pageSize)); + Config.saveConfiguration(props); + if (sessionObject.folder != null) { + int oldPageSize = sessionObject.folder.getPageSize(); + if( pageSize != oldPageSize ) + sessionObject.folder.setPageSize( pageSize ); + sessionObject.state = STATE_LIST; + } else { + sessionObject.state = STATE_AUTH; + } + } catch (IOException ioe) { + sessionObject.error = ioe.toString(); + } catch( NumberFormatException nfe ) { + sessionObject.error += _("Invalid pagesize number, resetting to default value.") + "<br>"; + } + } else if (buttonPressed(request, CANCEL)) { + sessionObject.state = (sessionObject.folder != null) ? STATE_LIST : STATE_AUTH; + } else if (buttonPressed(request, CONFIGURE)) { + sessionObject.state = STATE_CONFIG; + } + } + /** * @param httpSession * @return non-null @@ -1437,6 +1507,7 @@ public class WebMail extends HttpServlet } return sessionObject; } + /** * Copied from net.i2p.router.web.CSSHelper * @since 0.9.7 @@ -1528,6 +1599,8 @@ public class WebMail extends HttpServlet int oldState = sessionObject.state; processStateChangeButtons( sessionObject, request, isPOST ); + if (isPOST) + processConfigButtons( sessionObject, request ); int newState = sessionObject.state; if (oldState != newState) Debug.debug(Debug.DEBUG, "STATE CHANGE from " + oldState + " to " + newState); @@ -1582,7 +1655,7 @@ public class WebMail extends HttpServlet /* * update folder content */ - if( sessionObject.state != STATE_AUTH ) { + if( sessionObject.state == STATE_LIST ) { // get through cache so we have the disk-only ones too String[] uidls = sessionObject.mailCache.getUIDLs(); if (uidls != null) { @@ -1611,6 +1684,8 @@ public class WebMail extends HttpServlet subtitle = _("Show Message"); } else if( sessionObject.state == STATE_NEW ) { subtitle = _("New Message"); + } else if( sessionObject.state == STATE_CONFIG ) { + subtitle = _("Configuration"); } response.setContentType( "text/html" ); @@ -1648,7 +1723,7 @@ public class WebMail extends HttpServlet out.println( "<p class=\"error\">" + sessionObject.error + "</p>" ); } if( sessionObject.info != null && sessionObject.info.length() > 0 ) { - out.println( "<p class=\"info\">" + sessionObject.info + "</p>" ); + out.println( "<p class=\"info\"><b>" + sessionObject.info + "</b></p>" ); } /* * now write body @@ -1665,6 +1740,9 @@ public class WebMail extends HttpServlet else if( sessionObject.state == STATE_NEW ) showCompose( out, sessionObject, request ); + else if( sessionObject.state == STATE_CONFIG ) + showConfig(out, sessionObject); + //out.println( "</form><div id=\"footer\"><hr><p class=\"footer\">susimail v0." + version +" " + ( RELEASE ? "release" : "development" ) + " © 2004-2005 <a href=\"mailto:susi23@mail.i2p\">susi</a></div></div></body>\n</html>"); out.println( "</form><div id=\"footer\"><hr><p class=\"footer\">susimail © 2004-2005 susi</div></div></body>\n</html>"); out.flush(); @@ -1972,6 +2050,7 @@ public class WebMail extends HttpServlet } out.println( "</table>" ); } + /** * * @param out @@ -1999,13 +2078,18 @@ public class WebMail extends HttpServlet out.println( "<tr><td colspan=\"2\"> </td></tr>\n" + "<tr><td></td><td align=\"left\">" + button( LOGIN, _("Login") ) + spacer + - button(OFFLINE, _("Read Mail Offline") ) + spacer + - " <input class=\"cancel\" type=\"reset\" value=\"" + _("Reset") + "\"></td></tr>\n" + + button(OFFLINE, _("Read Mail Offline") ) + + //spacer + + //" <input class=\"cancel\" type=\"reset\" value=\"" + _("Reset") + "\">" + + spacer + + button(CONFIGURE, _("Settings")) + + "</td></tr>\n" + "<tr><td colspan=\"2\"> </td></tr>\n" + "<tr><td></td><td align=\"left\"><a href=\"http://hq.postman.i2p/?page_id=14\">" + _("Learn about I2P mail") + "</a></td></tr>\n" + "<tr><td></td><td align=\"left\"><a href=\"http://hq.postman.i2p/?page_id=16\">" + _("Create Account") + "</a></td></tr>\n" + "</table>"); } + /** * * @param out @@ -2014,16 +2098,16 @@ public class WebMail extends HttpServlet */ private static void showFolder( PrintWriter out, SessionObject sessionObject, RequestWrapper request ) { - out.println( button( NEW, _("New") ) + spacer + + out.println( button( NEW, _("New") ) + spacer); // In theory, these are valid and will apply to the first checked message, // but that's not obvious and did it work? //button( REPLY, _("Reply") ) + //button( REPLYALL, _("Reply All") ) + //button( FORWARD, _("Forward") ) + spacer + //button( DELETE, _("Delete") ) + spacer + - button( REFRESH, _("Check Mail") ) + spacer); - if (Config.hasConfigFile()) - out.println(button( RELOAD, _("Reload Config") ) + spacer); + out.println(button( REFRESH, _("Check Mail") ) + spacer); + //if (Config.hasConfigFile()) + // out.println(button( RELOAD, _("Reload Config") ) + spacer); out.println(button( LOGOUT, _("Logout") )); if (sessionObject.folder.getPages() > 1) @@ -2057,6 +2141,7 @@ public class WebMail extends HttpServlet else type = "linkold"; String link = "<a href=\"" + myself + "?" + SHOW + "=" + i + "\" class=\"" + type + "\">"; + String jslink = " onclick=\"document.location='" + myself + '?' + SHOW + '=' + i + "';\" "; boolean idChecked = false; String checkId = sessionObject.pageChanged ? null : request.getParameter( "check" + i ); @@ -2075,15 +2160,16 @@ public class WebMail extends HttpServlet // ", markAll=" + sessionObject.markAll + // ", 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" : "" ) + ">" + "</td><td>" + - (mail.isNew() ? "<img src=\"/susimail/icons/flag_green.png\" alt=\"\" title=\"" + _("Message is new") + "\">" : " ") + "</td><td>" + - link + mail.shortSender + "</a></td><td>" + - (mail.hasAttachment() ? "<img src=\"/susimail/icons/attach.png\" alt=\"\" title=\"" + _("Message has an attachment") + "\">" : " ") + "</td><td>" + - link + mail.shortSubject + "</a></td><td>" + - (mail.isSpam() ? "<img src=\"/susimail/icons/flag_red.png\" alt=\"\" title=\"" + _("Message is spam") + "\">" : " ") + "</td><td>" + + out.println( "<tr class=\"list" + bg + "\">" + + "<td><input type=\"checkbox\" class=\"optbox\" name=\"check" + i + "\" value=\"1\"" + + ( idChecked ? "checked" : "" ) + ">" + "</td><td " + jslink + ">" + + (mail.isNew() ? "<img src=\"/susimail/icons/flag_green.png\" alt=\"\" title=\"" + _("Message is new") + "\">" : " ") + "</td><td " + jslink + ">" + + link + mail.shortSender + "</a></td><td " + jslink + ">" + + (mail.hasAttachment() ? "<img src=\"/susimail/icons/attach.png\" alt=\"\" title=\"" + _("Message has an attachment") + "\">" : " ") + "</td><td " + jslink + ">" + + link + mail.shortSubject + "</a></td><td " + jslink + ">" + + (mail.isSpam() ? "<img src=\"/susimail/icons/flag_red.png\" alt=\"\" title=\"" + _("Message is spam") + "\">" : " ") + "</td><td " + jslink + ">" + // don't let date get split across lines - mail.localFormattedDate.replace(" ", " ") + "</td><td> </td><td align=\"right\">" + + mail.localFormattedDate.replace(" ", " ") + "</td><td " + jslink + "> </td><td align=\"right\" " + jslink + ">" + ((mail.getSize() > 0) ? (DataHelper.formatSize2(mail.getSize()) + 'B') : "???") + "</td></tr>" ); bg = 1 - bg; i++; @@ -2108,18 +2194,21 @@ public class WebMail extends HttpServlet // TODO js out.println(button( DELETE, _("Delete Selected") ) + "<br>"); out.print( - button( CLEAR, _("Clear All") ) + + button( MARKALL, _("Mark All") ) + " " + - button( MARKALL, _("Mark All") )); + button( CLEAR, _("Clear All") )); //"<br>" + //button( INVERT, _("Invert Selection") ) + //"<br>"); } out.print("</td>\n<td colspan=\"4\" align=\"right\">"); - out.print( - _("Page Size") + ": <input type=\"text\" style=\"text-align: right;\" name=\"" + PAGESIZE + "\" size=\"4\" value=\"" + sessionObject.folder.getPageSize() + "\">" + - " " + - button( SETPAGESIZE, _("Set") ) ); + // moved to config page + //out.print( + // _("Page Size") + ": <input type=\"text\" style=\"text-align: right;\" name=\"" + PAGESIZE + "\" size=\"4\" value=\"" + sessionObject.folder.getPageSize() + "\">" + + // " " + + // button( SETPAGESIZE, _("Set") ) ); + out.print("<br>"); + out.print(button(CONFIGURE, _("Settings"))); out.println("</td>"); } out.println( "</table>"); @@ -2198,6 +2287,37 @@ public class WebMail extends HttpServlet out.println( "<tr><td colspan=\"2\" align=\"center\"><hr></td></tr>\n</table>" ); } + /** + * Simple configure page + * + * @since 0.9.13 + */ + private static void showConfig(PrintWriter out, SessionObject sessionObject) { + int sz; + if (sessionObject.folder != null) + sz = sessionObject.folder.getPageSize(); + else + sz = Config.getProperty(Folder.PAGESIZE, Folder.DEFAULT_PAGESIZE); + out.println( + _("Folder Page Size") + ": <input type=\"text\" style=\"text-align: right;\" name=\"" + PAGESIZE + + "\" size=\"4\" value=\"" + sz + "\">" + + " " + + button( SETPAGESIZE, _("Set") ) ); + out.println("<p>"); + out.print(_("Advanced Configuration")); + out.print(":</p><textarea cols=\"80\" rows=\"20\" spellcheck=\"false\" name=\"" + CONFIG_TEXT + "\">"); + Properties config = Config.getProperties(); + for (Map.Entry<Object, Object> e : config.entrySet()) { + out.print(e.getKey()); + out.print('='); + out.println(e.getValue()); + } + out.println("</textarea>"); + out.println("</br>"); + out.println(button(SAVE, _("Save Configuration"))); + out.println(button(CANCEL, _("Cancel"))); + } + /** translate */ private static String _(String s) { return Messages.getString(s); diff --git a/apps/susimail/src/susimail.properties b/apps/susimail/src/susimail.properties index 45b5f0fe9054936ca9244daa670889f4c76d6485..0a79031256fab4ad67bd4bec45aae45a769b7474 100644 --- a/apps/susimail/src/susimail.properties +++ b/apps/susimail/src/susimail.properties @@ -4,8 +4,6 @@ susimail.ports.fixed=true susimail.ports.pop3=7660 susimail.ports.smtp=7659 -susimail.fast.start=true - susimail.sender.fixed=true susimail.sender.domain=mail.i2p diff --git a/installer/resources/themes/susimail/dark/susimail.css b/installer/resources/themes/susimail/dark/susimail.css index 8e79ee9379054406281d730749c903437f3b3ba5..1f7686b8357c4e8e0a1e61056551e8ff6aed0fcd 100644 --- a/installer/resources/themes/susimail/dark/susimail.css +++ b/installer/resources/themes/susimail/dark/susimail.css @@ -233,11 +233,16 @@ input.prevpage, input.prev { min-height: 22px; } -input.send, input.setpagesize { +input.send, input.setpagesize, input.save { background: #000 url('/themes/console/images/accept.png') no-repeat 2px center; min-height: 22px; } +input.configure { + background: #000 url('/susimail/icons/wrench.png') no-repeat 2px center; + min-height: 22px; +} + input[type=file], input.new_upload { background: #000 url('/themes/console/images/add.png') no-repeat 2px center; min-height: 22px; diff --git a/installer/resources/themes/susimail/light/susimail.css b/installer/resources/themes/susimail/light/susimail.css index 94634134f6a8672140f6e55e7f952ca16fba933b..48f5ccd9175b307f3e40aac8d492e5feab568d8d 100644 --- a/installer/resources/themes/susimail/light/susimail.css +++ b/installer/resources/themes/susimail/light/susimail.css @@ -114,11 +114,27 @@ td { } tr.list0 { - background-color:#e0e0e0; + background-color:#ececec; } tr.list1 { - background-color:#ffffff; + background-color:#f4f4f4; +} + +tr.list0:hover { + background-color:#e0e0f4; +} + +tr.list1:hover { + background-color:#e0e0f4; +} + +tr.list0 a:link { + text-decoration: none; +} + +tr.list1 a:link { + text-decoration: none; } .iframed tr.list1 { @@ -242,12 +258,18 @@ input.prevpage, input.prev { min-height: 22px; } -input.send, input.setpagesize { +input.send, input.setpagesize, input.save { background: #ddf url('/themes/console/images/accept.png') no-repeat 4px center; padding: 2px 3px 2px 24px; min-height: 22px; } +input.configure { + background: #ddf url('/susimail/icons/wrench.png') no-repeat 4px center; + padding: 2px 3px 2px 24px; + min-height: 22px; +} + input[type=file], input.new_upload { background: #ddf url('/themes/console/images/add.png') no-repeat 4px center; padding: 2px 3px 2px 24px;