use of net.i2p.servlet.RequestWrapper in project i2p.i2p by i2p.
the class WebMail method processRequest.
// // Start request handling here ////
/**
* The entry point for all web page loads
*
* @param httpRequest
* @param response
* @param isPOST disallow button pushes if false
* @throws IOException
* @throws ServletException
*/
private void processRequest(HttpServletRequest httpRequest, HttpServletResponse response, boolean isPOST) throws IOException, ServletException {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
// Fetch routerconsole theme (or use our default if it doesn't exist)
String theme = ctx.getProperty(RC_PROP_THEME, DEFAULT_THEME);
// Apply any override
theme = Config.getProperty(CONFIG_THEME, theme);
boolean universalTheming = ctx.getBooleanProperty(RC_PROP_UNIVERSAL_THEMING);
if (universalTheming) {
// Fetch routerconsole theme (or use our default if it doesn't exist)
theme = ctx.getProperty(RC_PROP_THEME, DEFAULT_THEME);
// Ensure that theme exists
String[] themes = getThemes();
boolean themeExists = false;
for (int i = 0; i < themes.length; i++) {
if (themes[i].equals(theme))
themeExists = true;
}
if (!themeExists) {
theme = DEFAULT_THEME;
}
}
boolean forceMobileConsole = ctx.getBooleanProperty(RC_PROP_FORCE_MOBILE_CONSOLE);
boolean isMobile = (forceMobileConsole || isMobile(httpRequest.getHeader("User-Agent")));
httpRequest.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setHeader("X-Frame-Options", "SAMEORIGIN");
response.setHeader("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'");
response.setHeader("X-XSS-Protection", "1; mode=block");
response.setHeader("X-Content-Type-Options", "nosniff");
response.setHeader("Referrer-Policy", "no-referrer");
response.setHeader("Accept-Ranges", "none");
RequestWrapper request = new RequestWrapper(httpRequest);
SessionObject sessionObject = null;
String subtitle = "";
HttpSession httpSession = request.getSession(true);
sessionObject = getSessionObject(httpSession);
synchronized (sessionObject) {
sessionObject.error = "";
sessionObject.info = "";
sessionObject.pageChanged = false;
sessionObject.themePath = "/themes/susimail/" + theme + '/';
sessionObject.imgPath = sessionObject.themePath + "images/";
sessionObject.isMobile = isMobile;
if (isPOST) {
try {
String nonce = request.getParameter(SUSI_NONCE);
if (nonce == null || !sessionObject.isValidNonce(nonce)) {
// These two strings are already in the router console FormHandler,
// so translate with that bundle.
sessionObject.error += consoleGetString("Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.", ctx) + '\n' + consoleGetString("If the problem persists, verify that you have cookies enabled in your browser.", ctx) + '\n';
isPOST = false;
}
} catch (IllegalStateException ise) {
// too big, can't get any parameters
sessionObject.error += ise.getMessage() + '\n';
isPOST = false;
}
}
// // Start state determination and button processing
State state = null;
if (sessionObject.mailbox == null) {
state = State.AUTH;
} else if (isPOST) {
// This must be called to add the attachment before
// processStateChangeButtons() sends the message
state = processComposeButtons(sessionObject, request);
}
state = processStateChangeButtons(sessionObject, request, isPOST, state);
state = processConfigButtons(sessionObject, request, isPOST, state);
Debug.debug(Debug.DEBUG, "Prelim. state is " + state);
if (state == State.CONFIG) {
if (isPOST) {
// P-R-G
String q = '?' + CONFIGURE;
sendRedirect(httpRequest, response, q);
return;
}
}
if (state == State.LOADING) {
if (isPOST) {
sendRedirect(httpRequest, response, null);
return;
}
}
if (state != State.AUTH) {
if (isPOST)
state = processGenericButtons(sessionObject, request, state);
}
if (state == State.LIST) {
if (isPOST) {
int page = 1;
String sp = request.getParameter(CUR_PAGE);
if (sp != null) {
try {
page = Integer.parseInt(sp);
} catch (NumberFormatException nfe) {
}
}
int newPage = processFolderButtons(sessionObject, page, request);
// REFRESH on list page
if (newPage != page || buttonPressed(request, LIST) || buttonPressed(request, SEND) || buttonPressed(request, CANCEL) || buttonPressed(request, REFRESH) || buttonPressed(request, LOGIN) || buttonPressed(request, OFFLINE)) {
// P-R-G
String q = '?' + CUR_PAGE + '=' + newPage;
// CURRENT_SORT is only in page POSTs
String str = request.getParameter(CURRENT_SORT);
if (str != null && !str.equals(SORT_DEFAULT) && VALID_SORTS.contains(str))
q += "&sort=" + str;
sendRedirect(httpRequest, response, q);
return;
}
}
}
// ?show= links - this forces State.SHOW
String b64UIDL = request.getParameter(SHOW);
// attachment links, images, next/prev/delete on show form
if (b64UIDL == null)
b64UIDL = request.getParameter(B64UIDL);
String showUIDL = Base64.decodeToString(b64UIDL);
if (state == State.SHOW) {
if (isPOST) {
String newShowUIDL = processMessageButtons(sessionObject, showUIDL, request);
if (newShowUIDL == null) {
state = State.LIST;
showUIDL = null;
} else if (!newShowUIDL.equals(showUIDL) || buttonPressed(request, SEND) || buttonPressed(request, CANCEL)) {
// SEND and CANCEL are from NEW page
// P-R-G
String q;
if (newShowUIDL.equals(DELETE))
q = '?' + DELETE + "=1&" + SHOW + '=' + Base64.encode(showUIDL);
else
q = '?' + SHOW + '=' + Base64.encode(newShowUIDL);
sendRedirect(httpRequest, response, q);
return;
}
}
// ?download=nnn&b64uidl link (same for ?att) should be valid in any state
if (showUIDL != null && processDownloadLink(sessionObject, showUIDL, request, response)) {
// download or raw view sent, or 404
return;
}
if (isPOST && showUIDL != null && processSaveAsLink(sessionObject, showUIDL, request, response)) {
// download sent, or 404
return;
}
// sessionObject.showUIDL = null
if (showUIDL != null) {
Mail mail = sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.ALL);
if (mail != null && mail.error.length() > 0) {
sessionObject.error += mail.error;
mail.error = "";
}
} else {
// can't SHOW without a UIDL
state = State.LIST;
}
}
/*
* update folder content
* We need a valid and sorted folder for SHOW also, for the previous/next buttons
*/
Folder<String> folder = sessionObject.folder;
// folder could be null after an error, we can't proceed if it is
if (folder == null && (state == State.LIST || state == State.SHOW)) {
sessionObject.error += "Internal error, no folder\n";
state = State.AUTH;
}
// // End state determination, state will not change after here
Debug.debug(Debug.DEBUG, "Final state is " + state);
if (state == State.LIST || state == State.SHOW) {
// sort buttons are GETs
String oldSort = folder.getCurrentSortBy();
SortOrder oldOrder = folder.getCurrentSortingDirection();
processSortingButtons(sessionObject, request);
if (state == State.LIST) {
for (Iterator<String> it = sessionObject.folder.currentPageIterator(); it != null && it.hasNext(); ) {
String uidl = it.next();
Mail mail = sessionObject.mailCache.getMail(uidl, MailCache.FetchMode.HEADER);
if (mail != null && mail.error.length() > 0) {
sessionObject.error += mail.error;
mail.error = "";
}
}
}
// get through cache so we have the disk-only ones too
String[] uidls = sessionObject.mailCache.getUIDLs();
if (folder.addElements(Arrays.asList(uidls)) > 0) {
// we added elements, so it got sorted
} else {
// check for changed sort
String curSort = folder.getCurrentSortBy();
SortOrder curOrder = folder.getCurrentSortingDirection();
if (oldOrder != curOrder || !oldSort.equals(curSort))
folder.sort();
}
}
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
// // Begin output
PrintWriter out = response.getWriter();
/*
* build subtitle
*/
if (state == State.AUTH) {
subtitle = _t("Login");
} else if (state == State.LOADING) {
subtitle = _t("Loading emails, please wait...");
} else if (state == State.LIST) {
// mailbox.getNumMails() forces a connection, don't use it
// Not only does it slow things down, but a failure causes all our messages to "vanish"
// subtitle = ngettext("1 Message", "{0} Messages", sessionObject.mailbox.getNumMails());
subtitle = ngettext("1 Message", "{0} Messages", folder.getSize());
} else if (state == State.SHOW) {
Mail mail = showUIDL != null ? sessionObject.mailCache.getMail(showUIDL, MailCache.FetchMode.HEADER) : null;
if (mail != null && mail.hasHeader()) {
if (mail.shortSubject != null)
// already HTML encoded
subtitle = mail.shortSubject;
else
subtitle = _t("Show Message");
} else {
subtitle = _t("Message not found.");
}
} else if (state == State.NEW) {
subtitle = _t("New Message");
} else if (state == State.CONFIG) {
subtitle = _t("Configuration");
}
response.setContentType("text/html");
/*
* write header
*/
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n" + "<head>\n" + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n" + "<title>" + _t("SusiMail") + " - " + subtitle + "</title>\n" + "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + sessionObject.themePath + "susimail.css?" + CoreVersion.VERSION + "\">");
if (sessionObject.isMobile) {
out.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes\" />\n" + "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + sessionObject.themePath + "mobile.css?" + CoreVersion.VERSION + "\" />");
}
if (state != State.AUTH)
out.println("<link rel=\"stylesheet\" href=\"/susimail/css/print.css?" + CoreVersion.VERSION + "\" type=\"text/css\" media=\"print\" />");
if (state == State.NEW || state == State.CONFIG) {
// TODO cancel if to and body are empty
out.println("<script src=\"/susimail/js/compose.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>");
} else if (state == State.LIST) {
out.println("<script src=\"/susimail/js/folder.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>");
} else if (state == State.LOADING) {
// TODO JS?
out.println("<meta http-equiv=\"refresh\" content=\"5;url=" + myself + "\">");
// TODO we don't need the form below
}
out.print("</head>\n<body" + (state == State.LIST ? " onload=\"deleteboxclicked()\">" : ">"));
String nonce = state == State.AUTH ? LOGIN_NONCE : Long.toString(ctx.random().nextLong());
sessionObject.addNonce(nonce);
out.println("<div class=\"page\"><div class=\"header\"><img class=\"header\" src=\"" + sessionObject.imgPath + "susimail.png\" alt=\"Susimail\"></div>\n" + "<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.SHOW || state == State.NEW) {
// Store the reference UIDL on the compose form also
if (showUIDL != null) {
// reencode, as showUIDL may have changed, and also for XSS
b64UIDL = Base64.encode(showUIDL);
out.println("<input type=\"hidden\" name=\"" + B64UIDL + "\" value=\"" + b64UIDL + "\">");
} else if (state == State.NEW) {
// for NEW, try to get back to the current page if we weren't replying
int page = 1;
String sp = request.getParameter(CUR_PAGE);
if (sp != null) {
try {
page = Integer.parseInt(sp);
} catch (NumberFormatException nfe) {
}
}
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
String curSort = folder.getCurrentSortBy();
SortOrder curOrder = folder.getCurrentSortingDirection();
// UP is reverse sort. DOWN is normal sort.
String fullSort = curOrder == SortOrder.UP ? '-' + curSort : curSort;
out.println("<input type=\"hidden\" name=\"" + CURRENT_SORT + "\" value=\"" + fullSort + "\">");
out.println("<input type=\"hidden\" name=\"" + CURRENT_FOLDER + "\" value=\"" + PersistentMailCache.DIR_FOLDER + "\">");
}
boolean showRefresh = false;
if (sessionObject.isLoading) {
sessionObject.info += _t("Loading emails, please wait...") + '\n';
showRefresh = true;
}
if (sessionObject.isFetching) {
sessionObject.info += _t("Checking for new emails on server") + '\n';
showRefresh = true;
} else if (state != State.LOADING && state != State.AUTH && state != State.CONFIG) {
String error = sessionObject.connectError;
if (error != null && error.length() > 0) {
sessionObject.error += error + '\n';
sessionObject.connectError = null;
}
int added = sessionObject.newMails;
if (added > 0) {
sessionObject.info += ngettext("{0} new message", "{0} new messages", added) + '\n';
sessionObject.newMails = -1;
} else if (added == 0) {
sessionObject.info += _t("No new messages") + '\n';
sessionObject.newMails = -1;
}
}
if (showRefresh) {
sessionObject.info += _t("Refresh the page for updates") + '\n';
}
if (sessionObject.error.length() > 0 || sessionObject.info.length() > 0) {
out.println("<div class=\"notifications\" onclick=\"this.remove()\">");
if (sessionObject.error.length() > 0)
out.println("<p class=\"error\">" + quoteHTML(sessionObject.error).replace("\n", "<br>") + "</p>");
if (sessionObject.info.length() > 0)
out.println("<p class=\"info\"><b>" + quoteHTML(sessionObject.info).replace("\n", "<br>") + "</b></p>");
out.println("</div>");
}
/*
* now write body
*/
if (state == State.AUTH)
showLogin(out);
else if (state == State.LOADING)
showLoading(out, sessionObject, request);
else if (state == State.LIST)
showFolder(out, sessionObject, request);
else if (state == State.SHOW)
showMessage(out, sessionObject, showUIDL, buttonPressed(request, DELETE));
else if (state == State.NEW)
showCompose(out, sessionObject, request);
else if (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 class=\"footer\"><p class=\"footer\">susimail © 2004-2005 susi</p></div></div></body>\n</html>");
out.flush();
}
// synch sessionObject
}
Aggregations