forked from I2P_Developers/i2p.i2p
Compare commits
1 Commits
console-lo
...
susimail-s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
497e5a7873 |
@@ -40,7 +40,7 @@ function addClickHandler1(elem)
|
|||||||
function addClickHandler2(elem)
|
function addClickHandler2(elem)
|
||||||
{
|
{
|
||||||
elem.addEventListener("click", function() {
|
elem.addEventListener("click", function() {
|
||||||
var form = document.forms[0];
|
var form = document.forms[3];
|
||||||
form.delete.disabled = false;
|
form.delete.disabled = false;
|
||||||
form.markall.disabled = true;
|
form.markall.disabled = true;
|
||||||
form.clearselection.disabled = false;
|
form.clearselection.disabled = false;
|
||||||
@@ -57,7 +57,7 @@ function addClickHandler2(elem)
|
|||||||
function addClickHandler3(elem)
|
function addClickHandler3(elem)
|
||||||
{
|
{
|
||||||
elem.addEventListener("click", function() {
|
elem.addEventListener("click", function() {
|
||||||
var form = document.forms[0];
|
var form = document.forms[3];
|
||||||
form.delete.disabled = true;
|
form.delete.disabled = true;
|
||||||
form.markall.disabled = false;
|
form.markall.disabled = false;
|
||||||
form.clearselection.disabled = true;
|
form.clearselection.disabled = true;
|
||||||
@@ -82,7 +82,7 @@ function deleteboxclicked() {
|
|||||||
var hasOne = false;
|
var hasOne = false;
|
||||||
var hasAll = true;
|
var hasAll = true;
|
||||||
var hasNone = true;
|
var hasNone = true;
|
||||||
var form = document.forms[0];
|
var form = document.forms[3];
|
||||||
for(i = 0; i < form.elements.length; i++) {
|
for(i = 0; i < form.elements.length; i++) {
|
||||||
var elem = form.elements[i];
|
var elem = form.elements[i];
|
||||||
if (elem.type == 'checkbox') {
|
if (elem.type == 'checkbox') {
|
||||||
|
|||||||
@@ -61,12 +61,28 @@ public class Folder<O extends Object> {
|
|||||||
UP;
|
UP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 0.9.63
|
||||||
|
*/
|
||||||
|
public interface Selector<O> {
|
||||||
|
/**
|
||||||
|
* @return non-null
|
||||||
|
*/
|
||||||
|
public String getSelectionKey();
|
||||||
|
/**
|
||||||
|
* @return true if selected
|
||||||
|
*/
|
||||||
|
public boolean select(O element);
|
||||||
|
}
|
||||||
|
|
||||||
private int pages, pageSize, currentPage;
|
private int pages, pageSize, currentPage;
|
||||||
private O[] elements;
|
private O[] elements;
|
||||||
private final Map<String, Comparator<O>> sorter;
|
private final Map<String, Comparator<O>> sorter;
|
||||||
private SortOrder sortingDirection;
|
private SortOrder sortingDirection;
|
||||||
private Comparator<O> currentSorter;
|
private Comparator<O> currentSorter;
|
||||||
private String currentSortID;
|
private String currentSortID;
|
||||||
|
private Selector<O> currentSelector;
|
||||||
|
private List<O> selected;
|
||||||
|
|
||||||
public Folder()
|
public Folder()
|
||||||
{
|
{
|
||||||
@@ -109,10 +125,18 @@ public class Folder<O extends Object> {
|
|||||||
/**
|
/**
|
||||||
* Returns the number of pages in the folder.
|
* Returns the number of pages in the folder.
|
||||||
* Minimum of 1 even if empty.
|
* Minimum of 1 even if empty.
|
||||||
|
* If the selector is non-null, this will be the number of pages in the selected results.
|
||||||
|
*
|
||||||
* @return Returns the number of pages.
|
* @return Returns the number of pages.
|
||||||
*/
|
*/
|
||||||
public synchronized int getPages() {
|
public synchronized int getPages() {
|
||||||
return pages;
|
if (currentSelector == null || elements == null)
|
||||||
|
return pages;
|
||||||
|
int ps = getPageSize();
|
||||||
|
int rv = selected.size() / ps;
|
||||||
|
if (rv * ps < elements.length)
|
||||||
|
rv++;
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -177,6 +201,7 @@ public class Folder<O extends Object> {
|
|||||||
if (elements.length > 0) {
|
if (elements.length > 0) {
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
sort();
|
sort();
|
||||||
|
select();
|
||||||
} else {
|
} else {
|
||||||
this.elements = null;
|
this.elements = null;
|
||||||
}
|
}
|
||||||
@@ -285,6 +310,27 @@ public class Folder<O extends Object> {
|
|||||||
return list.iterator();
|
return list.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an iterator containing the elements on the current page.
|
||||||
|
* This iterator is over a copy of the current page, and so
|
||||||
|
* is thread safe w.r.t. other operations on this folder,
|
||||||
|
* but will not reflect subsequent changes, and iter.remove()
|
||||||
|
* will not change the folder.
|
||||||
|
*
|
||||||
|
* @return Iterator containing the elements on the current page.
|
||||||
|
* @since 0.9.63
|
||||||
|
*/
|
||||||
|
public synchronized Iterator<O> currentPageSelectorIterator()
|
||||||
|
{
|
||||||
|
if (currentSelector == null)
|
||||||
|
return currentPageIterator();
|
||||||
|
int pageSize = getPageSize();
|
||||||
|
int offset = ( currentPage - 1 ) * pageSize;
|
||||||
|
if (selected == null || offset > selected.size())
|
||||||
|
return Collections.<O>emptyList().iterator();
|
||||||
|
return selected.subList(offset, Math.min(selected.size(), offset + pageSize)).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns folder to next page.
|
* Turns folder to next page.
|
||||||
*/
|
*/
|
||||||
@@ -372,25 +418,52 @@ public class Folder<O extends Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the element on the current page on the given position.
|
* Warning, this does not do the actual selection, this is done in the iterator.
|
||||||
|
* Resets page to 1 if selector changed.
|
||||||
*
|
*
|
||||||
* @param x Position of the element on the current page.
|
* @param selector may be null
|
||||||
* @return Element on the current page on the given position.
|
* @since 0.9.63
|
||||||
*/
|
*/
|
||||||
/**** unused, we now fetch by UIDL, not position
|
public synchronized void setSelector(Selector<O> selector) {
|
||||||
public synchronized O getElementAtPosXonCurrentPage( int x )
|
if ((currentSelector != null && selector == null) ||
|
||||||
{
|
(currentSelector == null && selector != null) ||
|
||||||
O result = null;
|
(currentSelector != null && selector != null &&
|
||||||
if( elements != null ) {
|
!currentSelector.getSelectionKey().equals(selector.getSelectionKey()))) {
|
||||||
int pageSize = getPageSize();
|
currentPage = 1;
|
||||||
int offset = ( currentPage - 1 ) * pageSize;
|
}
|
||||||
offset += x;
|
currentSelector = selector;
|
||||||
if( offset >= 0 && offset < elements.length )
|
if (selector != null)
|
||||||
result = elements[offset];
|
select();
|
||||||
|
else
|
||||||
|
selected = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return current selector or null
|
||||||
|
* @since 0.9.63
|
||||||
|
*/
|
||||||
|
public synchronized Selector<O> getCurrentSelector() {
|
||||||
|
return currentSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select and cache results
|
||||||
|
* @since 0.9.63
|
||||||
|
*/
|
||||||
|
private synchronized void select() {
|
||||||
|
if (selected == null)
|
||||||
|
selected = new ArrayList<O>();
|
||||||
|
else
|
||||||
|
selected.clear();
|
||||||
|
if (elements == null || currentSelector == null)
|
||||||
|
return;
|
||||||
|
int sz = getSize();
|
||||||
|
for (int i = 0; i < sz; i++) {
|
||||||
|
if (currentSelector.select(elements[i])) {
|
||||||
|
selected.add(elements[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
****/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the first element of the sorted folder.
|
* Returns the first element of the sorted folder.
|
||||||
@@ -467,6 +540,47 @@ public class Folder<O extends Object> {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the next element in the sorted array.
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* @return The next element
|
||||||
|
*/
|
||||||
|
public synchronized O getNextSelectedElement(O element)
|
||||||
|
{
|
||||||
|
if (currentSelector == null)
|
||||||
|
return getNextElement(element);
|
||||||
|
O result = null;
|
||||||
|
int i = selected.indexOf(element);
|
||||||
|
if (i != -1 && selected != null) {
|
||||||
|
i++;
|
||||||
|
if (i < selected.size())
|
||||||
|
result = selected.get(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the previous element in the sorted array.
|
||||||
|
*
|
||||||
|
* @param element
|
||||||
|
* @return The previous element
|
||||||
|
*/
|
||||||
|
public synchronized O getPreviousSelectedElement(O element)
|
||||||
|
{
|
||||||
|
if (currentSelector == null)
|
||||||
|
return getPreviousElement(element);
|
||||||
|
O result = null;
|
||||||
|
int i = selected.indexOf(element);
|
||||||
|
if (i != -1 && selected != null) {
|
||||||
|
i--;
|
||||||
|
if (i >= 0 && i < selected.size())
|
||||||
|
result = selected.get(i);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves element at index i.
|
* Retrieves element at index i.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ public class WebMail extends HttpServlet
|
|||||||
private static final String NEXT_PAGE_NUM = "nextpagenum";
|
private static final String NEXT_PAGE_NUM = "nextpagenum";
|
||||||
private static final String CURRENT_SORT = "currentsort";
|
private static final String CURRENT_SORT = "currentsort";
|
||||||
private static final String CURRENT_FOLDER = "folder";
|
private static final String CURRENT_FOLDER = "folder";
|
||||||
|
private static final String CURRENT_SEARCH = "currentsearch";
|
||||||
private static final String NEW_FOLDER = "newfolder";
|
private static final String NEW_FOLDER = "newfolder";
|
||||||
private static final String DRAFT_EXISTS = "draftexists";
|
private static final String DRAFT_EXISTS = "draftexists";
|
||||||
private static final String DEBUG_STATE = "currentstate";
|
private static final String DEBUG_STATE = "currentstate";
|
||||||
@@ -167,6 +168,7 @@ public class WebMail extends HttpServlet
|
|||||||
private static final String REALLYDELETE = "really_delete";
|
private static final String REALLYDELETE = "really_delete";
|
||||||
private static final String MOVE_TO = "moveto";
|
private static final String MOVE_TO = "moveto";
|
||||||
private static final String SWITCH_TO = "switchto";
|
private static final String SWITCH_TO = "switchto";
|
||||||
|
private static final String SEARCH = "s";
|
||||||
// also a GET param
|
// also a GET param
|
||||||
private static final String SHOW = "show";
|
private static final String SHOW = "show";
|
||||||
private static final String DOWNLOAD = "download";
|
private static final String DOWNLOAD = "download";
|
||||||
@@ -412,6 +414,8 @@ public class WebMail extends HttpServlet
|
|||||||
buf.append(" beforePopup\"");
|
buf.append(" beforePopup\"");
|
||||||
else
|
else
|
||||||
buf.append('"');
|
buf.append('"');
|
||||||
|
if (name.equals(NEW_UPLOAD))
|
||||||
|
buf.append(" id=\"" + NEW_UPLOAD + '"');
|
||||||
// These are icons only now, via the CSS, so add a tooltip
|
// These are icons only now, via the CSS, so add a tooltip
|
||||||
if (name.equals(FIRSTPAGE) || name.equals(PREVPAGE) || name.equals(NEXTPAGE) || name.equals(LASTPAGE) ||
|
if (name.equals(FIRSTPAGE) || name.equals(PREVPAGE) || name.equals(NEXTPAGE) || name.equals(LASTPAGE) ||
|
||||||
name.equals(PREV) || name.equals(LIST) || name.equals(NEXT))
|
name.equals(PREV) || name.equals(LIST) || name.equals(NEXT))
|
||||||
@@ -1177,7 +1181,8 @@ public class WebMail extends HttpServlet
|
|||||||
buttonPressed( request, MARKALL ) ||
|
buttonPressed( request, MARKALL ) ||
|
||||||
buttonPressed( request, CLEAR ) ||
|
buttonPressed( request, CLEAR ) ||
|
||||||
buttonPressed( request, INVERT ) ||
|
buttonPressed( request, INVERT ) ||
|
||||||
buttonPressed( request, REFRESH )) {
|
buttonPressed( request, SORT ) ||
|
||||||
|
buttonPressed( request, REFRESH ) ||
|
||||||
buttonPressed( request, SEARCH )) {
|
buttonPressed( request, SEARCH )) {
|
||||||
state = State.LIST;
|
state = State.LIST;
|
||||||
} else if (buttonPressed(request, PREV) ||
|
} else if (buttonPressed(request, PREV) ||
|
||||||
@@ -1205,6 +1210,9 @@ public class WebMail extends HttpServlet
|
|||||||
state = State.SHOW;
|
state = State.SHOW;
|
||||||
} else if (buttonPressed(request, DRAFT_ATTACHMENT)) {
|
} else if (buttonPressed(request, DRAFT_ATTACHMENT)) {
|
||||||
// GET params
|
// GET params
|
||||||
|
state = State.NEW;
|
||||||
|
} else if (buttonPressed(request, SEARCH)) {
|
||||||
|
// GET params for XHR
|
||||||
return State.LIST;
|
return State.LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1809,7 +1817,7 @@ public class WebMail extends HttpServlet
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param id a content-id, without the surrounding <> or trailing @ part
|
* Recursive.
|
||||||
* @param id a content-id, without the surrounding <>
|
* @param id a content-id, without the surrounding <>
|
||||||
* @return the part or null
|
* @return the part or null
|
||||||
* @since 0.9.62
|
* @since 0.9.62
|
||||||
@@ -1818,14 +1826,6 @@ public class WebMail extends HttpServlet
|
|||||||
if( part == null )
|
if( part == null )
|
||||||
return null;
|
return null;
|
||||||
if (id.equals(part.cid))
|
if (id.equals(part.cid))
|
||||||
if (part.cid != null) {
|
|
||||||
// strip @ and try again,
|
|
||||||
int idx = part.cid.indexOf('@');
|
|
||||||
if (idx > 0) {
|
|
||||||
if (id.equals(part.cid.substring(0, idx)))
|
|
||||||
return part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return part;
|
return part;
|
||||||
if( part.multipart || part.message ) {
|
if( part.multipart || part.message ) {
|
||||||
for( MailPart p : part.parts ) {
|
for( MailPart p : part.parts ) {
|
||||||
@@ -2191,8 +2191,9 @@ public class WebMail extends HttpServlet
|
|||||||
boolean forceMobileConsole = ctx.getBooleanProperty(RC_PROP_FORCE_MOBILE_CONSOLE);
|
boolean forceMobileConsole = ctx.getBooleanProperty(RC_PROP_FORCE_MOBILE_CONSOLE);
|
||||||
boolean isMobile = (forceMobileConsole || isMobile(httpRequest.getHeader("User-Agent")));
|
boolean isMobile = (forceMobileConsole || isMobile(httpRequest.getHeader("User-Agent")));
|
||||||
|
|
||||||
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
httpRequest.setCharacterEncoding("UTF-8");
|
||||||
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
response.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||||
|
// very strict CSP for HTML emails in iframes
|
||||||
// TODO self paths, review
|
// TODO self paths, review
|
||||||
if (httpRequest.getParameter(RAW_ATTACHMENT) != null ||
|
if (httpRequest.getParameter(RAW_ATTACHMENT) != null ||
|
||||||
httpRequest.getParameter(CID_ATTACHMENT) != null ||
|
httpRequest.getParameter(CID_ATTACHMENT) != null ||
|
||||||
@@ -2211,7 +2212,7 @@ public class WebMail extends HttpServlet
|
|||||||
response.setHeader("Content-Security-Policy", "default-src 'self'; style-src 'self'; script-src 'self'; form-action 'self'; frame-ancestors 'self'; object-src 'none'; media-src 'none'; img-src 'self'");
|
response.setHeader("Content-Security-Policy", "default-src 'self'; style-src 'self'; script-src 'self'; form-action 'self'; frame-ancestors 'self'; object-src 'none'; media-src 'none'; img-src 'self'");
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
}
|
}
|
||||||
response.setHeader("X-XSS-Protection", "1; mode=block");
|
response.setHeader("Permissions-Policy", "accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(), camera=(), display-capture(), fullscreen=(self), geolocation=(), gyroscope=(), interest-cohort=(), magnetometer=(), microphone=(), midi=(), payment=(), usb=(), vibrate=(), vr=()");
|
||||||
response.setHeader("X-XSS-Protection", "1; mode=block");
|
response.setHeader("X-XSS-Protection", "1; mode=block");
|
||||||
response.setHeader("X-Content-Type-Options", "nosniff");
|
response.setHeader("X-Content-Type-Options", "nosniff");
|
||||||
response.setHeader("Referrer-Policy", "no-referrer");
|
response.setHeader("Referrer-Policy", "no-referrer");
|
||||||
@@ -2568,15 +2569,31 @@ public class WebMail extends HttpServlet
|
|||||||
out.println("<script src=\"/js/iframeResizer.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>");
|
out.println("<script src=\"/js/iframeResizer.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>");
|
||||||
}
|
}
|
||||||
out.println("<script src=\"/susimail/js/notifications.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>");
|
out.println("<script src=\"/susimail/js/notifications.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>");
|
||||||
String nonce = state == State.AUTH ? LOGIN_NONCE :
|
out.print("</head>\n<body>");
|
||||||
Long.toString(ctx.random().nextLong());
|
out.println("<div class=\"page\" id=\"page\">");
|
||||||
sessionObject.addNonce(nonce);
|
|
||||||
out.println(
|
if (state != State.LIST) {
|
||||||
"<div class=\"page\">" +
|
// For all states except LIST, we have one big form for the whole page.
|
||||||
"<form method=\"POST\" enctype=\"multipart/form-data\" action=\"" + myself + "\" accept-charset=\"UTF-8\">\n" +
|
// LIST has several forms, we will output them in showFolder().
|
||||||
"<input type=\"hidden\" name=\"" + SUSI_NONCE + "\" value=\"" + nonce + "\">\n" +
|
String nonce = state == State.AUTH ? LOGIN_NONCE :
|
||||||
// we use this to know if the user thought he was logged in at the time
|
Long.toString(ctx.random().nextLong());
|
||||||
"<input type=\"hidden\" name=\"" + DEBUG_STATE + "\" value=\"" + state + "\">");
|
sessionObject.addNonce(nonce);
|
||||||
|
out.println("<form method=\"POST\" enctype=\"multipart/form-data\" action=\"" + myself + "\" accept-charset=\"UTF-8\">\n" +
|
||||||
|
"<input type=\"hidden\" name=\"" + SUSI_NONCE + "\" value=\"" + nonce + "\">\n" +
|
||||||
|
// we use this to know if the user thought he was logged in at the time
|
||||||
|
"<input type=\"hidden\" name=\"" + DEBUG_STATE + "\" value=\"" + state + "\">");
|
||||||
|
if (state != State.AUTH && state != State.CONFIG && state != State.LOADING) {
|
||||||
|
// maintain the search param when changing pages or folders or
|
||||||
|
// going to message view and back
|
||||||
|
String search = request.getParameter(CURRENT_SEARCH);
|
||||||
|
if (search == null || search.length() == 0) {
|
||||||
|
Folder.Selector selector = mc.getFolder().getCurrentSelector();
|
||||||
|
if (selector != null)
|
||||||
|
search = selector.getSelectionKey();
|
||||||
|
}
|
||||||
|
if (search != null && search.length() > 0)
|
||||||
|
out.println("<input type=\"hidden\" name=\"" + CURRENT_SEARCH + "\" value=\"" + DataHelper.escapeHTML(search) + "\">\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (state == State.NEW) {
|
if (state == State.NEW) {
|
||||||
String newUIDL = request.getParameter(NEW_UIDL);
|
String newUIDL = request.getParameter(NEW_UIDL);
|
||||||
@@ -2600,8 +2617,6 @@ public class WebMail extends HttpServlet
|
|||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
out.println("<input type=\"hidden\" name=\"" + CUR_PAGE + "\" value=\"" + page + "\">");
|
out.println("<input type=\"hidden\" name=\"" + CUR_PAGE + "\" value=\"" + page + "\">");
|
||||||
}
|
|
||||||
if (state == State.SHOW || state == State.NEW || state == State.LIST) {
|
|
||||||
}
|
}
|
||||||
// Save sort order in case it changes later
|
// Save sort order in case it changes later
|
||||||
String curSort = folder.getCurrentSortBy();
|
String curSort = folder.getCurrentSortBy();
|
||||||
@@ -2652,6 +2667,7 @@ public class WebMail extends HttpServlet
|
|||||||
out.println("</b></p>");
|
out.println("</b></p>");
|
||||||
}
|
}
|
||||||
out.println("</div>" );
|
out.println("</div>" );
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* now write body
|
* now write body
|
||||||
@@ -2689,7 +2705,8 @@ public class WebMail extends HttpServlet
|
|||||||
|
|
||||||
else if( state == State.CONFIG )
|
else if( state == State.CONFIG )
|
||||||
showConfig(out, folder);
|
showConfig(out, folder);
|
||||||
out.println("</form>\n");
|
|
||||||
|
if (state != State.LIST)
|
||||||
out.println("</form>\n");
|
out.println("</form>\n");
|
||||||
|
|
||||||
out.println("<div class=\"footer\"><p class=\"footer\">\n" +
|
out.println("<div class=\"footer\"><p class=\"footer\">\n" +
|
||||||
@@ -3426,18 +3443,9 @@ public class WebMail extends HttpServlet
|
|||||||
* @param request
|
* @param request
|
||||||
*/
|
*/
|
||||||
private static void showFolder( PrintWriter out, SessionObject sessionObject, MailCache mc, RequestWrapper request )
|
private static void showFolder( PrintWriter out, SessionObject sessionObject, MailCache mc, RequestWrapper request )
|
||||||
out.println("<div class=\"topbuttons\">");
|
|
||||||
out.println( button( NEW, _t("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, _t("Reply") ) +
|
|
||||||
//button( REPLYALL, _t("Reply All") ) +
|
|
||||||
//button( FORWARD, _t("Forward") ) + spacer +
|
|
||||||
//button( DELETE, _t("Delete") ) + spacer +
|
|
||||||
{
|
{
|
||||||
String folderName = mc.getFolderName();
|
String folderName = mc.getFolderName();
|
||||||
String floc;
|
String floc;
|
||||||
out.println((sessionObject.isFetching ? button2(REFRESH, _t("Check Mail")) : button(REFRESH, _t("Check Mail"))) + spacer);
|
|
||||||
if (folderName.equals(DIR_FOLDER)) {
|
if (folderName.equals(DIR_FOLDER)) {
|
||||||
floc = "";
|
floc = "";
|
||||||
} else if (folderName.equals(DIR_DRAFTS)) {
|
} else if (folderName.equals(DIR_DRAFTS)) {
|
||||||
@@ -3446,11 +3454,75 @@ public class WebMail extends HttpServlet
|
|||||||
floc = '&' + CURRENT_FOLDER + '=' + folderName;
|
floc = '&' + CURRENT_FOLDER + '=' + folderName;
|
||||||
}
|
}
|
||||||
boolean isSpamFolder = folderName.equals(DIR_SPAM);
|
boolean isSpamFolder = folderName.equals(DIR_SPAM);
|
||||||
//if (Config.hasConfigFile())
|
boolean showToColumn = folderName.equals(DIR_DRAFTS) || folderName.equals(DIR_SENT);
|
||||||
// out.println(button( RELOAD, _t("Reload Config") ) + spacer);
|
// For all states except LIST, we have one big form for the whole page.
|
||||||
out.println(button( LOGOUT, _t("Logout") ));
|
// Here, for LIST, we set up 4-5 forms
|
||||||
int page = 1;
|
// to deal with html rules and have a search box that works right.
|
||||||
|
// 1: new/checkmail/logout, inside topbuttons div
|
||||||
|
// 2: search, inside topbuttons div
|
||||||
|
// 3: change folder and page buttons, inside topbuttons div, surrounds pagenav table
|
||||||
|
// 4: mail delete boxes and bottombuttons div, surrounds mailbox table
|
||||||
|
// 5: change folder and page buttons, inside 2nd topbuttons div, surrounds pagenav table,
|
||||||
|
// only shown if more than 30 mails on a page
|
||||||
|
//
|
||||||
|
// Create the common form header
|
||||||
|
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||||
|
String nonce = Long.toString(ctx.random().nextLong());
|
||||||
|
sessionObject.addNonce(nonce);
|
||||||
|
// for all but search
|
||||||
|
String form = "<form method=\"POST\" enctype=\"multipart/form-data\" action=\"" + myself + "\" accept-charset=\"UTF-8\">\n";
|
||||||
|
StringBuilder fbf = new StringBuilder(256);
|
||||||
|
fbf.append("<input type=\"hidden\" name=\"").append(SUSI_NONCE).append("\" value=\"").append(nonce).append("\">\n")
|
||||||
.append("<input type=\"hidden\" name=\"").append(DEBUG_STATE).append("\" value=\"").append(State.LIST).append("\">\n");
|
.append("<input type=\"hidden\" name=\"").append(DEBUG_STATE).append("\" value=\"").append(State.LIST).append("\">\n");
|
||||||
|
Folder<String> folder = mc.getFolder();
|
||||||
|
String curSort = folder.getCurrentSortBy();
|
||||||
|
SortOrder curOrder = folder.getCurrentSortingDirection();
|
||||||
|
// UP is reverse sort. DOWN is normal sort.
|
||||||
|
String fullSort = curOrder == SortOrder.UP ? '-' + curSort : curSort;
|
||||||
|
fbf.append("<input type=\"hidden\" name=\"").append(CURRENT_SORT).append("\" value=\"").append(fullSort).append("\">\n")
|
||||||
|
.append("<input type=\"hidden\" name=\"").append(CURRENT_FOLDER).append("\" value=\"").append(folderName).append("\">\n");
|
||||||
|
String cursearch = request.getParameter(CURRENT_SEARCH);
|
||||||
|
String search = request.getParameter(SEARCH);
|
||||||
|
if (cursearch != null && cursearch.length() > 0) {
|
||||||
|
if (search == null) {
|
||||||
|
// we came from somewhere else, set search to cursearch, will set selector below
|
||||||
|
search = cursearch;
|
||||||
|
} else {
|
||||||
|
// we came from here, search wins, will set selector below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (search != null && search.length() > 0) {
|
||||||
|
fbf.append("<input type=\"hidden\" name=\"").append(CURRENT_SEARCH).append("\" value=\"").append(DataHelper.escapeHTML(search)).append("\">\n");
|
||||||
|
Folder.Selector olds = folder.getCurrentSelector();
|
||||||
|
if (olds == null || !olds.getSelectionKey().equals(search)) {
|
||||||
|
folder.setSelector(new SearchSelector(mc, search));
|
||||||
|
}
|
||||||
|
} else if ((search == null || search.length() == 0) && folder.getCurrentSelector() != null) {
|
||||||
|
folder.setSelector(null);
|
||||||
|
}
|
||||||
|
String hidden = fbf.toString();
|
||||||
|
|
||||||
|
out.println("<div class=\"topbuttons\">");
|
||||||
|
// form 1
|
||||||
|
out.print(form);
|
||||||
|
out.print(hidden);
|
||||||
|
out.println( button( NEW, _t("New") ) + spacer);
|
||||||
|
if (folderName.equals(DIR_FOLDER))
|
||||||
|
out.println((sessionObject.isFetching ? button2(REFRESH, _t("Check Mail")) : button(REFRESH, _t("Check Mail"))) + spacer);
|
||||||
|
out.println(button( LOGOUT, _t("Logout") ));
|
||||||
|
out.println("</form>");
|
||||||
|
|
||||||
|
if (folder.getSize() > 1 || (search != null && search.length() > 0)) {
|
||||||
|
// form 2
|
||||||
|
out.println("<form class=\"search\" id = \"search\" action=\"" + myself + "\" method=\"POST\">");
|
||||||
|
out.print(hidden);
|
||||||
|
out.write("<input type=\"text\" name=\"" + SEARCH + "\" size=\"20\" class=\"search\" id=\"searchbox\"");
|
||||||
|
if (search != null)
|
||||||
|
out.write(" value=\"" + DataHelper.escapeHTML(search) + '"');
|
||||||
|
out.println("><a class=\"cancel\" id=\"searchcancel\" href=\"" + myself + "\"></a>");
|
||||||
|
out.println("</form>");
|
||||||
|
}
|
||||||
|
|
||||||
int page = 1;
|
int page = 1;
|
||||||
if (folder.getPages() > 1) {
|
if (folder.getPages() > 1) {
|
||||||
String sp = request.getParameter(CUR_PAGE);
|
String sp = request.getParameter(CUR_PAGE);
|
||||||
@@ -3460,13 +3532,18 @@ public class WebMail extends HttpServlet
|
|||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
folder.setCurrentPage(page);
|
folder.setCurrentPage(page);
|
||||||
|
}
|
||||||
|
// form 3
|
||||||
|
out.print(form);
|
||||||
out.print(hidden);
|
out.print(hidden);
|
||||||
|
showPageButtons(out, sessionObject.user, folderName, page, folder.getPages(), true);
|
||||||
out.println("</form>");
|
out.println("</form>");
|
||||||
out.println("</div>");
|
out.println("</div>");
|
||||||
String curSort = folder.getCurrentSortBy();
|
|
||||||
SortOrder curOrder = folder.getCurrentSortingDirection();
|
// form 4
|
||||||
out.println("<table id=\"mailbox\" cellspacing=\"0\" cellpadding=\"5\">\n" +
|
out.print(form);
|
||||||
"<tr><td colspan=\"9\"><hr></td></tr>\n<tr><th title=\"" + _t("Mark for deletion") + "\"> </th>" +
|
out.print(hidden);
|
||||||
|
out.println("<table id=\"mailbox\" cellspacing=\"0\" cellpadding=\"5\">\n");
|
||||||
out.println("<tr><td colspan=\"9\"><hr></td></tr>\n<tr><th title=\"" + _t("Mark for deletion") + "\"> </th>" +
|
out.println("<tr><td colspan=\"9\"><hr></td></tr>\n<tr><th title=\"" + _t("Mark for deletion") + "\"> </th>" +
|
||||||
thSpacer + "<th>" + sortHeader(SORT_SENDER, showToColumn ? _t("To") : _t("From"), sessionObject.imgPath, curSort, curOrder, page, folderName) + "</th>" +
|
thSpacer + "<th>" + sortHeader(SORT_SENDER, showToColumn ? _t("To") : _t("From"), sessionObject.imgPath, curSort, curOrder, page, folderName) + "</th>" +
|
||||||
thSpacer + "<th>" + sortHeader(SORT_SUBJECT, _t("Subject"), sessionObject.imgPath, curSort, curOrder, page, folderName) + "</th>" +
|
thSpacer + "<th>" + sortHeader(SORT_SUBJECT, _t("Subject"), sessionObject.imgPath, curSort, curOrder, page, folderName) + "</th>" +
|
||||||
@@ -3474,7 +3551,7 @@ public class WebMail extends HttpServlet
|
|||||||
"</th>" +
|
"</th>" +
|
||||||
thSpacer + "<th>" + sortHeader(SORT_SIZE, _t("Size"), sessionObject.imgPath, curSort, curOrder, page, folderName) + "</th></tr>" );
|
thSpacer + "<th>" + sortHeader(SORT_SIZE, _t("Size"), sessionObject.imgPath, curSort, curOrder, page, folderName) + "</th></tr>" );
|
||||||
int bg = 0;
|
int bg = 0;
|
||||||
for (Iterator<String> it = folder.currentPageIterator(); it != null && it.hasNext(); ) {
|
int i = 0;
|
||||||
for (Iterator<String> it = folder.currentPageSelectorIterator(); it != null && it.hasNext(); ) {
|
for (Iterator<String> it = folder.currentPageSelectorIterator(); it != null && it.hasNext(); ) {
|
||||||
String uidl = it.next();
|
String uidl = it.next();
|
||||||
Mail mail = mc.getMail(uidl, MailCache.FetchMode.HEADER_CACHE_ONLY);
|
Mail mail = mc.getMail(uidl, MailCache.FetchMode.HEADER_CACHE_ONLY);
|
||||||
@@ -3586,14 +3663,82 @@ public class WebMail extends HttpServlet
|
|||||||
out.print("<br>");
|
out.print("<br>");
|
||||||
out.print(button(CONFIGURE, _t("Settings")));
|
out.print(button(CONFIGURE, _t("Settings")));
|
||||||
out.println("</td></tr>");
|
out.println("</td></tr>");
|
||||||
if (folder.getPages() > 1 && i > 30) {
|
out.println( "</table>");
|
||||||
|
out.println("</form>");
|
||||||
|
int ps = folder.getPageSize();
|
||||||
if (ps > 30 && (folder.getCurrentPage() - 1) * ps < folder.getSize() - 30) {
|
if (ps > 30 && (folder.getCurrentPage() - 1) * ps < folder.getSize() - 30) {
|
||||||
// show the buttons again if page is big
|
// show the buttons again if page is big
|
||||||
|
out.println("<div class=\"topbuttons\">");
|
||||||
|
// form 5
|
||||||
|
out.print(form);
|
||||||
out.print(hidden);
|
out.print(hidden);
|
||||||
|
showPageButtons(out, sessionObject.user, folderName, page, folder.getPages(), false);
|
||||||
out.println("</form>");
|
out.println("</form>");
|
||||||
out.println("</div>");
|
out.println("</div>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Folder callback to search mails for matching terms.
|
||||||
|
* Only subject and sender (or recipients for drafts).
|
||||||
|
* Mail bodies are not in-memory and would be very slow.
|
||||||
|
*
|
||||||
|
* @since 0.9.63
|
||||||
|
*/
|
||||||
|
private static class SearchSelector implements Folder.Selector<String> {
|
||||||
|
private final String key;
|
||||||
|
private final MailCache mc;
|
||||||
|
private final String[] terms;
|
||||||
|
private final boolean isDrafts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param search non-null, non-empty, and %-encoded, will be decoded here
|
||||||
|
*/
|
||||||
|
public SearchSelector(MailCache cache, String search) {
|
||||||
|
mc = cache;
|
||||||
|
isDrafts = mc.getFolderName().equals(DIR_DRAFTS);
|
||||||
|
key = search;
|
||||||
|
terms = DataHelper.split(search, " ");
|
||||||
|
// decode
|
||||||
|
for (int i = 0; i < terms.length; i++) {
|
||||||
|
terms[i] = terms[i].toLowerCase(Locale.US);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSelectionKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean select(String uidl) {
|
||||||
|
Mail mail = mc.getMail(uidl, MailCache.FetchMode.HEADER_CACHE_ONLY);
|
||||||
|
if (mail == null)
|
||||||
|
return false;
|
||||||
|
String subj = mail.subject.toLowerCase(Locale.US);
|
||||||
|
String sender = isDrafts ? null : mail.sender;
|
||||||
|
if (sender != null)
|
||||||
|
sender = sender.toLowerCase(Locale.US);
|
||||||
|
String[] to = isDrafts ? mail.to : null;
|
||||||
|
for (String term : terms) {
|
||||||
|
if (subj.contains(term))
|
||||||
|
return true;
|
||||||
|
if (sender != null && sender.contains(term))
|
||||||
|
return true;
|
||||||
|
if (to != null) {
|
||||||
|
for (int i = 0; i < to.length; i++) {
|
||||||
|
if (to[i].toLowerCase(Locale.US).contains(term))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Search selector for '" + key + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Folder selector, then, if pages greater than 1:
|
* Folder selector, then, if pages greater than 1:
|
||||||
@@ -3726,7 +3871,11 @@ public class WebMail extends HttpServlet
|
|||||||
// processRequest() will P-R-G the PREV and NEXT so we have a consistent URL
|
// processRequest() will P-R-G the PREV and NEXT so we have a consistent URL
|
||||||
out.println("<div id=\"messagenav\">");
|
out.println("<div id=\"messagenav\">");
|
||||||
Folder<String> folder = mc.getFolder();
|
Folder<String> folder = mc.getFolder();
|
||||||
String uidl = folder.getPreviousElement(showUIDL);
|
if (hasHeader) {
|
||||||
|
String uidl;
|
||||||
|
if (folder.getCurrentSelector() != null)
|
||||||
|
uidl = folder.getPreviousSelectedElement(showUIDL);
|
||||||
|
else
|
||||||
uidl = folder.getPreviousElement(showUIDL);
|
uidl = folder.getPreviousElement(showUIDL);
|
||||||
String text = _t("Previous");
|
String text = _t("Previous");
|
||||||
if (uidl == null || folder.isFirstElement(showUIDL)) {
|
if (uidl == null || folder.isFirstElement(showUIDL)) {
|
||||||
@@ -3741,7 +3890,11 @@ public class WebMail extends HttpServlet
|
|||||||
int page = folder.getPageOf(showUIDL);
|
int page = folder.getPageOf(showUIDL);
|
||||||
out.println("<input type=\"hidden\" name=\"" + CUR_PAGE + "\" value=\"" + page + "\">");
|
out.println("<input type=\"hidden\" name=\"" + CUR_PAGE + "\" value=\"" + page + "\">");
|
||||||
out.println(button( LIST, _t("Back to Folder") ) + spacer);
|
out.println(button( LIST, _t("Back to Folder") ) + spacer);
|
||||||
String uidl = folder.getNextElement(showUIDL);
|
if (hasHeader) {
|
||||||
|
String uidl;
|
||||||
|
if (folder.getCurrentSelector() != null)
|
||||||
|
uidl = folder.getNextSelectedElement(showUIDL);
|
||||||
|
else
|
||||||
uidl = folder.getNextElement(showUIDL);
|
uidl = folder.getNextElement(showUIDL);
|
||||||
String text = _t("Next");
|
String text = _t("Next");
|
||||||
if (uidl == null || folder.isLastElement(showUIDL)) {
|
if (uidl == null || folder.isLastElement(showUIDL)) {
|
||||||
|
|||||||
@@ -244,6 +244,30 @@ div.topbuttons br {
|
|||||||
border-radius: 15px;
|
border-radius: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#searchbox {
|
||||||
|
background: #f8f8ff url(/themes/console/images/buttons/search.png) 7px center no-repeat !important;
|
||||||
|
margin: 2px 4px 2px 24px !important;
|
||||||
|
padding: 4px 32px 4px 32px !important;
|
||||||
|
color: #47475f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchbox:focus, #searchbox:active {
|
||||||
|
color: #19191f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchcancel {
|
||||||
|
background: url(../images/delete.png) 0px center no-repeat;
|
||||||
|
margin: 9px 4px 2px 2px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
color: transparent;
|
||||||
|
border: none;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchcancel.disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
table#pagenav {
|
table#pagenav {
|
||||||
float: right;
|
float: right;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
|||||||
@@ -959,7 +959,7 @@ hr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#composemail table {
|
#composemail table {
|
||||||
width: auto;
|
width: 90%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1187,6 +1187,30 @@ h3#config {
|
|||||||
float: none !important;
|
float: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#searchbox {
|
||||||
|
background: #f8f8ff url(/themes/console/images/buttons/search.png) 7px center no-repeat !important;
|
||||||
|
margin: 2px 4px 2px 24px !important;
|
||||||
|
padding: 4px 32px 4px 32px !important;
|
||||||
|
color: #47475f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchbox:focus, #searchbox:active {
|
||||||
|
color: #19191f;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchcancel {
|
||||||
|
background: url(../images/delete.png) 0px center no-repeat;
|
||||||
|
margin: 9px 4px 2px 2px;
|
||||||
|
padding: 6px 12px;
|
||||||
|
color: transparent;
|
||||||
|
border: none;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#searchcancel.disabled {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
input.moveto {
|
input.moveto {
|
||||||
float: left;
|
float: left;
|
||||||
margin-left: 14px;
|
margin-left: 14px;
|
||||||
|
|||||||
Reference in New Issue
Block a user