diff --git a/jspwiki-main/src/main/java/org/apache/wiki/attachment/AttachmentServlet.java b/jspwiki-main/src/main/java/org/apache/wiki/attachment/AttachmentServlet.java index 9485aafef1..961c93d460 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/attachment/AttachmentServlet.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/attachment/AttachmentServlet.java @@ -61,10 +61,13 @@ Licensed to the Apache Software Foundation (ASF) under one import jakarta.servlet.ServletConfig; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; +import jakarta.servlet.ServletInputStream; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.fileupload2.core.FileUploadByteCountLimitException; import java.util.ResourceBundle; +import org.apache.wiki.tags.MaxUploadTag; /** @@ -103,6 +106,8 @@ public class AttachmentServlet extends HttpServlet { /** * Initializes the servlet from Engine properties. + * @param config + * @throws jakarta.servlet.ServletException */ @Override public void init( final ServletConfig config ) throws ServletException { @@ -352,7 +357,17 @@ public void doPost( final HttpServletRequest req, final HttpServletResponse res } catch( final RedirectException e ) { final Session session = Wiki.session().find( m_engine, req ); session.addMessage( e.getMessage() ); - + //drain the request body + try (ServletInputStream inputStream = req.getInputStream();) { + int data; + while ((data = inputStream.read()) != -1) { + //we are just reading the stream to the end + } + } catch (Exception err) { + //ignore it + } + + req.getSession().setAttribute("msg", e.getMessage()); res.sendRedirect( e.getRedirect() ); } @@ -376,6 +391,8 @@ private String validateNextPage( String nextPage, final String errorPage ) { /** * Uploads a specific mime multipart input set, intercepts exceptions. + * + * If the total request size is too big, the user will be redirected to the Error.jsp page * * @param req The servlet request * @return The page to which we should go next. @@ -391,7 +408,7 @@ protected String upload( final HttpServletRequest req ) throws RedirectException // Check that we have a file upload request if( !JakartaServletFileUpload.isMultipartContent(req) ) { - throw new RedirectException( "Not a file upload", errorPage ); + throw new RedirectException( "Not a file upload", nextPage ); } try { @@ -402,15 +419,24 @@ protected String upload( final HttpServletRequest req ) throws RedirectException final UploadListener pl = new UploadListener(); m_engine.getManager( ProgressManager.class ).startProgress( pl, progressId ); - + + if (req.getContentLengthLong() > m_maxSize) { + //we don't want total upload size to be larger than the max + //this is to prevent resource exhaustion + //TODO i18n this error message + throw new RedirectException("Request too big " + + MaxUploadTag.humanReadableByteCountBin(req.getContentLengthLong()) + " vs " + + MaxUploadTag.humanReadableByteCountBin(m_maxSize), errorPage +"?Error=true"); + } final JakartaServletFileUpload upload = new JakartaServletFileUpload( factory ); upload.setHeaderCharset(StandardCharsets.UTF_8); - if( !context.hasAdminPermissions() ) { - upload.setFileSizeMax( m_maxSize ); - } upload.setProgressListener( pl ); - final List items = upload.parseRequest( req ); - + final List items; + try { + items = upload.parseRequest(req); + } catch (FileUploadByteCountLimitException ex) { + throw new RedirectException( "Request too big " + ex.getMessage(), nextPage ); + } String wikipage = null; String changeNote = null; //FileItem actualFile = null; @@ -443,10 +469,19 @@ protected String upload( final HttpServletRequest req ) throws RedirectException } if(fileItems.isEmpty()) { - throw new RedirectException( "Broken file upload", errorPage ); + throw new RedirectException( "Broken file upload", nextPage ); } else { for( final FileItem actualFile : fileItems ) { + if( !context.hasAdminPermissions() ) { + if (actualFile.getSize()> m_maxSize) { + //TODO i18n this error message + throw new RedirectException("Attachment too big " + actualFile.getName() + " " + + MaxUploadTag.humanReadableByteCountBin(actualFile.getSize()) + " vs " + + MaxUploadTag.humanReadableByteCountBin(m_maxSize), nextPage); + } + } + final String filename = actualFile.getName(); final long fileSize = actualFile.getSize(); try( final InputStream in = actualFile.getInputStream() ) { diff --git a/jspwiki-main/src/main/java/org/apache/wiki/tags/MaxUploadTag.java b/jspwiki-main/src/main/java/org/apache/wiki/tags/MaxUploadTag.java new file mode 100644 index 0000000000..7d787cfc34 --- /dev/null +++ b/jspwiki-main/src/main/java/org/apache/wiki/tags/MaxUploadTag.java @@ -0,0 +1,66 @@ +/* + * Copyright 2025 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.wiki.tags; + +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.wiki.util.TextUtil; + +/** + * Outputs the server's configured maximum file upload size. + * + * @since 3.0.0 + */ +public class MaxUploadTag extends WikiTagBase { + + private static final Logger LOG = LogManager.getLogger(MaxUploadTag.class); + + @Override + public int doWikiStartTag() throws Exception { + String maxUploadSize = m_wikiContext.getEngine().getWikiProperties().getProperty("jspwiki.attachment.maxsize"); + if (maxUploadSize != null) { + try { + long bytes = Long.parseLong(maxUploadSize); + String humanFormat = humanReadableByteCountBin(bytes); + pageContext.getOut().print(TextUtil.replaceEntities(humanFormat)); + } catch (NumberFormatException ex) { + LOG.warn("Parse error from configuration setting jspwiki.attachment.maxsize " + ex.getMessage()); + } + } + return SKIP_BODY; + } + + /* + Binary (1 Ki = 1,024) + from https://stackoverflow.com/a/3758880/1203182 + */ + public static String humanReadableByteCountBin(long bytes) { + long absB = bytes == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(bytes); + if (absB < 1024) { + return bytes + " B"; + } + long value = absB; + CharacterIterator ci = new StringCharacterIterator("KMGTPE"); + for (int i = 40; i >= 0 && absB > 0xfffccccccccccccL >> i; i -= 10) { + value >>= 10; + ci.next(); + } + value *= Long.signum(bytes); + return String.format("%.1f %ciB", value / 1024.0, ci.current()); + } +} diff --git a/jspwiki-main/src/main/resources/META-INF/jspwiki.tld b/jspwiki-main/src/main/resources/META-INF/jspwiki.tld index 7507d0d26b..bf867ff917 100644 --- a/jspwiki-main/src/main/resources/META-INF/jspwiki.tld +++ b/jspwiki-main/src/main/resources/META-INF/jspwiki.tld @@ -872,4 +872,10 @@ true + + + MaxUpload + org.apache.wiki.tags.MaxUploadTag + JSP + diff --git a/jspwiki-main/src/main/resources/templates/default.properties b/jspwiki-main/src/main/resources/templates/default.properties index 33537fc726..1f81ab30c2 100644 --- a/jspwiki-main/src/main/resources/templates/default.properties +++ b/jspwiki-main/src/main/resources/templates/default.properties @@ -36,6 +36,7 @@ attach.add.info=In order to upload a new attachment to this page, please use the attach.add.permission=Only authorized users are allowed to upload new attachments. attach.bytes=bytes attach.add.selectfile=Select file: +attach.add.maxUpload=Max upload size: attach.add.changenote=Change Note: attach.add.submit=Upload attach.add.select=Select files or drop them here diff --git a/jspwiki-main/src/main/resources/templates/default_de.properties b/jspwiki-main/src/main/resources/templates/default_de.properties index aeb3da49bd..f115eab38c 100644 --- a/jspwiki-main/src/main/resources/templates/default_de.properties +++ b/jspwiki-main/src/main/resources/templates/default_de.properties @@ -742,3 +742,4 @@ javascript.tip.default.title=Weitere... javascript.prefs.areyousure=Wenn du nicht auf 'Benutzereinstellungen speichern' klickst, \ werden deine Einstellungen verworfen. Bist du sicher, dass diese Seite verlassen werden soll? +attach.add.maxUpload=Maximale Uploadgr\u00f6\u00dfe: diff --git a/jspwiki-main/src/main/resources/templates/default_es.properties b/jspwiki-main/src/main/resources/templates/default_es.properties index 89a7f01011..27beb72f4f 100644 --- a/jspwiki-main/src/main/resources/templates/default_es.properties +++ b/jspwiki-main/src/main/resources/templates/default_es.properties @@ -698,4 +698,5 @@ javascript.dialog.plugin = Plugin javascript.dialog.principal = Roles, Grupos o Usuarios javascript.dialog.styles = Estilos adicionales javascript.dialog.toc.options = opciones de la Tabla de Contenidos -javascript.preview.zone = Zona de previsualizaci\u00f3n \ No newline at end of file +javascript.preview.zone = Zona de previsualizaci\u00f3n +attach.add.maxUpload=Tama\u00f1o m\u00e1ximo de carga: diff --git a/jspwiki-main/src/main/resources/templates/default_fi.properties b/jspwiki-main/src/main/resources/templates/default_fi.properties index 18790a7a3d..253d92d142 100644 --- a/jspwiki-main/src/main/resources/templates/default_fi.properties +++ b/jspwiki-main/src/main/resources/templates/default_fi.properties @@ -707,3 +707,4 @@ notification.createUserProfile.accept.subject=Tervetuloa {0}:aan info.keywords=Avainsanat: {0} notification.createUserProfile.accept.content=Onnittelut! Uusi profiilisi {0}:ssa on luotu. Profiilisi tiedot ovat seuraavat: \n\nKirjautumisnimi: \ {1} \nNimesi: {2} \nS\u00e4hk\u00f6posti: {3} \n\nJos unohdat salasanasi, voit palauttaa sen osoitteessa {4} +attach.add.maxUpload=Suurin l\u00e4hetyskoko: diff --git a/jspwiki-main/src/main/resources/templates/default_fr.properties b/jspwiki-main/src/main/resources/templates/default_fr.properties index d0259660e4..5e34306a6e 100644 --- a/jspwiki-main/src/main/resources/templates/default_fr.properties +++ b/jspwiki-main/src/main/resources/templates/default_fr.properties @@ -748,3 +748,4 @@ javascript.tip.default.title=Plus... javascript.prefs.areyousure=Si vous ne cliquez pas sur le bouton 'Enregistrer les pr\u00e9f\u00e9rences utilisateur', \ vos changements seront perdus. Etes-vous s\u00fbr de vouloir quitter cette page\u00a0? +attach.add.maxUpload=Taille maximale de t\u00e9l\u00e9chargement\u00a0: diff --git a/jspwiki-main/src/main/resources/templates/default_it.properties b/jspwiki-main/src/main/resources/templates/default_it.properties index 174580b375..ae836ff0b1 100644 --- a/jspwiki-main/src/main/resources/templates/default_it.properties +++ b/jspwiki-main/src/main/resources/templates/default_it.properties @@ -736,3 +736,4 @@ prefs.user.pagecookies.actions = Azioni javascript.dialog.character.entities = Entit\u00e0 dei caratteri prefs.password0 = Password attuale * userbox.button = Menu utente +attach.add.maxUpload=Dimensione massima di caricamento: diff --git a/jspwiki-main/src/main/resources/templates/default_nl.properties b/jspwiki-main/src/main/resources/templates/default_nl.properties index 212309bef1..5f020c41c4 100644 --- a/jspwiki-main/src/main/resources/templates/default_nl.properties +++ b/jspwiki-main/src/main/resources/templates/default_nl.properties @@ -737,3 +737,4 @@ javascript.dialog.styles = Extra Stijlen javascript.dialog.toc.options = TOC-opties prefs.password0 = Huidig Wachtwoord * javascript.dialog.plugin = Plug-in +attach.add.maxUpload=Maximale uploadgrootte: diff --git a/jspwiki-main/src/main/resources/templates/default_pt_BR.properties b/jspwiki-main/src/main/resources/templates/default_pt_BR.properties index 77116a5eda..d5cad82f9c 100644 --- a/jspwiki-main/src/main/resources/templates/default_pt_BR.properties +++ b/jspwiki-main/src/main/resources/templates/default_pt_BR.properties @@ -682,3 +682,4 @@ javascript.slimbox.previous=«Anterior javascript.slimbox.btn=Clique para visualizar {0} javascript.slimbox.size=Tamanho: {0}px x {1}px javascript.tip.default.title=Mais... +attach.add.maxUpload=Tamanho m\u00e1ximo de upload: diff --git a/jspwiki-main/src/main/resources/templates/default_ru.properties b/jspwiki-main/src/main/resources/templates/default_ru.properties index 8388cd24f4..4c2db69489 100644 --- a/jspwiki-main/src/main/resources/templates/default_ru.properties +++ b/jspwiki-main/src/main/resources/templates/default_ru.properties @@ -722,3 +722,4 @@ javascript.filter.hint=\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0448\u0430\u javascript.tablefilter=\u0424\u0438\u043b\u044c\u0442\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 \u0442\u0430\u0431\u043b\u0438\u0446\u044b javascript.slimbox.caption=\u041f\u0440\u044f\u043c\u0430\u044f \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 {0} javascript.slimbox.btn=\u041d\u0430\u0436\u043c\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c {0} +attach.add.maxUpload=\u041c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u0439 \u0440\u0430\u0437\u043c\u0435\u0440 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438: diff --git a/jspwiki-main/src/main/resources/templates/default_zh_CN.properties b/jspwiki-main/src/main/resources/templates/default_zh_CN.properties index 03bcc41ec0..81e8c491f1 100644 --- a/jspwiki-main/src/main/resources/templates/default_zh_CN.properties +++ b/jspwiki-main/src/main/resources/templates/default_zh_CN.properties @@ -723,3 +723,4 @@ attach.add.drop = \u6216\u62d6\u653e\u5230\u6b64\u5904! sbox.search.button = \u5feb\u9012\u641c\u7d22\u83dc\u5355 javascript.dialog.principal = \u89d2\u8272\u3001\u7fa4\u7ec4\u6216\u7528\u6237 prefs.user.pagecookies.actions = \u64cd\u4f5c +attach.add.maxUpload=\u6700\u5927\u4e0a\u4f20\u5927\u5c0f: diff --git a/jspwiki-war/src/main/webapp/Error.jsp b/jspwiki-war/src/main/webapp/Error.jsp index d1da112d60..1d00b45298 100644 --- a/jspwiki-war/src/main/webapp/Error.jsp +++ b/jspwiki-war/src/main/webapp/Error.jsp @@ -32,15 +32,20 @@ <% Engine wiki = Wiki.engine().find( getServletConfig() ); Context wikiContext = Wiki.context().create( wiki, request, ContextEnum.WIKI_ERROR.getRequestContext() ); + //the following line is critical to get the jspwiki jsp tags to work + pageContext.setAttribute( Context.ATTR_CONTEXT, wikiContext, PageContext.REQUEST_SCOPE); String pagereq = wikiContext.getName(); response.setContentType("text/html; charset="+wiki.getContentEncoding() ); String msg = "An unknown error was caught by Error.jsp"; - + if ("true".equalsIgnoreCase(request.getParameter("Error"))) { + //this is coming from the attaching upload servlet + msg = (String) request.getSession().getAttribute("msg"); + } Throwable realcause = null; - - msg = exception.getMessage(); + if (exception!=null) + msg = exception.getMessage(); if( msg == null || msg.isEmpty()) { msg = "An unknown exception "+exception.getClass().getName()+" was caught by Error.jsp."; @@ -52,7 +57,7 @@ // imported in JSP pages. // - if( exception instanceof jakarta.servlet.jsp.JspException ) + if( exception!=null && exception instanceof jakarta.servlet.jsp.JspException ) { log.debug("IS JSPEXCEPTION"); realcause = ((jakarta.servlet.jsp.JspException)exception).getCause(); @@ -83,9 +88,9 @@ <% if (wikiContext.hasAdminPermissions()) { %>
Exception
-
<%=realcause.getClass().getName()%>
+
<%=realcause!=null ? realcause.getClass().getName() : ""%>
Place where detected
-
<%=FileUtil.getThrowingMethod(realcause)%>
+
<%=realcause!=null ? FileUtil.getThrowingMethod(realcause) : ""%>
<% } %>

diff --git a/jspwiki-war/src/main/webapp/templates/default/AttachmentTab.jsp b/jspwiki-war/src/main/webapp/templates/default/AttachmentTab.jsp index cb56e01c06..5c734a2b9f 100644 --- a/jspwiki-war/src/main/webapp/templates/default/AttachmentTab.jsp +++ b/jspwiki-war/src/main/webapp/templates/default/AttachmentTab.jsp @@ -53,7 +53,11 @@ <%--

--%>
- +