📄 attachmentservlet.java
字号:
/* JSPWiki - a JSP-based WikiWiki clone. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you 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 com.ecyrd.jspwiki.attachment;import java.io.File;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.SocketException;import java.security.Permission;import java.security.Principal;import java.util.List;import java.util.Properties;import javax.servlet.ServletConfig;import javax.servlet.ServletContext;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.apache.commons.fileupload.FileItem;import org.apache.commons.fileupload.FileItemFactory;import org.apache.commons.fileupload.FileUploadException;import org.apache.commons.fileupload.ProgressListener;import org.apache.commons.fileupload.disk.DiskFileItemFactory;import org.apache.commons.fileupload.servlet.ServletFileUpload;import org.apache.log4j.Logger;import com.ecyrd.jspwiki.*;import com.ecyrd.jspwiki.auth.AuthorizationManager;import com.ecyrd.jspwiki.auth.permissions.PermissionFactory;import com.ecyrd.jspwiki.dav.AttachmentDavProvider;import com.ecyrd.jspwiki.dav.DavPath;import com.ecyrd.jspwiki.dav.DavProvider;import com.ecyrd.jspwiki.dav.WebdavServlet;import com.ecyrd.jspwiki.dav.methods.DavMethod;import com.ecyrd.jspwiki.dav.methods.PropFindMethod;import com.ecyrd.jspwiki.filters.RedirectException;import com.ecyrd.jspwiki.i18n.InternationalizationManager;import com.ecyrd.jspwiki.providers.ProviderException;import com.ecyrd.jspwiki.ui.progress.ProgressItem;import com.ecyrd.jspwiki.util.HttpUtil;/** * This is the chief JSPWiki attachment management servlet. It is used for * both uploading new content and downloading old content. It can handle * most common cases, e.g. check for modifications and return 304's as necessary. * <p> * Authentication is done using JSPWiki's normal AAA framework. * <p> * This servlet is also capable of managing dynamically created attachments. * * @author Erik Bunn * * @since 1.9.45. */public class AttachmentServlet extends WebdavServlet{ private static final int BUFFER_SIZE = 8192; private static final long serialVersionUID = 3257282552187531320L; private WikiEngine m_engine; static Logger log = Logger.getLogger(AttachmentServlet.class.getName()); private static final String HDR_VERSION = "version"; // private static final String HDR_NAME = "page"; /** Default expiry period is 1 day */ protected static final long DEFAULT_EXPIRY = 1 * 24 * 60 * 60 * 1000; private String m_tmpDir; private DavProvider m_attachmentProvider; /** * The maximum size that an attachment can be. */ private int m_maxSize = Integer.MAX_VALUE; /** * List of attachment types which are allowed */ private String[] m_allowedPatterns; private String[] m_forbiddenPatterns; // // Not static as DateFormat objects are not thread safe. // Used to handle the RFC date format = Sat, 13 Apr 2002 13:23:01 GMT // //private final DateFormat rfcDateFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z"); /** * Initializes the servlet from WikiEngine properties. * * {@inheritDoc} */ public void init( ServletConfig config ) throws ServletException { super.init( config ); m_engine = WikiEngine.getInstance( config ); Properties props = m_engine.getWikiProperties(); m_attachmentProvider = new AttachmentDavProvider( m_engine ); m_tmpDir = m_engine.getWorkDir()+File.separator+"attach-tmp"; m_maxSize = TextUtil.getIntegerProperty( props, AttachmentManager.PROP_MAXSIZE, Integer.MAX_VALUE ); String allowed = TextUtil.getStringProperty( props, AttachmentManager.PROP_ALLOWEDEXTENSIONS, null ); if( allowed != null && allowed.length() > 0 ) m_allowedPatterns = allowed.toLowerCase().split("\\s"); else m_allowedPatterns = new String[0]; String forbidden = TextUtil.getStringProperty( props, AttachmentManager.PROP_FORDBIDDENEXTENSIONS, null ); if( forbidden != null && forbidden.length() > 0 ) m_forbiddenPatterns = forbidden.toLowerCase().split("\\s"); else m_forbiddenPatterns = new String[0]; File f = new File( m_tmpDir ); if( !f.exists() ) { f.mkdirs(); } else if( !f.isDirectory() ) { log.fatal("A file already exists where the temporary dir is supposed to be: "+m_tmpDir+". Please remove it."); } log.debug( "UploadServlet initialized. Using " + m_tmpDir + " for temporary storage." ); } private boolean isTypeAllowed( String name ) { if( name == null || name.length() == 0 ) return false; name = name.toLowerCase(); for( int i = 0; i < m_forbiddenPatterns.length; i++ ) { if( name.endsWith(m_forbiddenPatterns[i]) && m_forbiddenPatterns[i].length() > 0 ) return false; } for( int i = 0; i < m_allowedPatterns.length; i++ ) { if( name.endsWith(m_allowedPatterns[i]) && m_allowedPatterns[i].length() > 0 ) return true; } return m_allowedPatterns.length == 0; } /** * Implements the PROPFIND method. * * @param req The servlet request * @param res The servlet response * @throws IOException If input/output fails * @throws ServletException If the servlet has issues */ public void doPropFind( HttpServletRequest req, HttpServletResponse res ) throws IOException, ServletException { DavMethod dm = new PropFindMethod( m_attachmentProvider ); String p = new String(req.getPathInfo().getBytes("ISO-8859-1"), "UTF-8"); DavPath path = new DavPath( p ); dm.execute( req, res, path ); } /** * Implements the OPTIONS method. * * @param req The servlet request * @param res The servlet response */ protected void doOptions( HttpServletRequest req, HttpServletResponse res ) { res.setHeader( "DAV", "1" ); // We support only Class 1 res.setHeader( "Allow", "GET, PUT, POST, OPTIONS, PROPFIND, PROPPATCH, MOVE, COPY, DELETE"); res.setStatus( HttpServletResponse.SC_OK ); } /** * Serves a GET with two parameters: 'wikiname' specifying the wikiname * of the attachment, 'version' specifying the version indicator. * * {@inheritDoc} */ // FIXME: Messages would need to be localized somehow. public void doGet( HttpServletRequest req, HttpServletResponse res ) throws IOException, ServletException { WikiContext context = m_engine.createContext( req, WikiContext.ATTACH ); String version = req.getParameter( HDR_VERSION ); String nextPage = req.getParameter( "nextpage" ); String msg = "An error occurred. Ouch."; int ver = WikiProvider.LATEST_VERSION; AttachmentManager mgr = m_engine.getAttachmentManager(); AuthorizationManager authmgr = m_engine.getAuthorizationManager(); String page = context.getPage().getName(); if( page == null ) { log.info("Invalid attachment name."); res.sendError( HttpServletResponse.SC_BAD_REQUEST ); return; } OutputStream out = null; InputStream in = null; try { log.debug("Attempting to download att "+page+", version "+version); if( version != null ) { ver = Integer.parseInt( version ); } Attachment att = mgr.getAttachmentInfo( page, ver ); if( att != null ) { // // Check if the user has permission for this attachment // Permission permission = PermissionFactory.getPagePermission( att, "view" ); if( !authmgr.checkPermission( context.getWikiSession(), permission ) ) { log.debug("User does not have permission for this"); res.sendError( HttpServletResponse.SC_FORBIDDEN ); return; } // // Check if the client already has a version of this attachment. // if( HttpUtil.checkFor304( req, att ) ) { log.debug("Client has latest version already, sending 304..."); res.sendError( HttpServletResponse.SC_NOT_MODIFIED ); return; } String mimetype = getMimeType( context, att.getFileName() ); res.setContentType( mimetype ); // // We use 'inline' instead of 'attachment' so that user agents // can try to automatically open the file. // res.addHeader( "Content-Disposition", "inline; filename=\"" + att.getFileName() + "\";" ); res.addDateHeader("Last-Modified",att.getLastModified().getTime()); if( !att.isCacheable() ) { res.addHeader( "Pragma", "no-cache" ); res.addHeader( "Cache-control", "no-cache" ); } // If a size is provided by the provider, report it. if( att.getSize() >= 0 ) { // log.info("size:"+att.getSize()); res.setContentLength( (int)att.getSize() ); } out = res.getOutputStream(); in = mgr.getAttachmentStream( context, att ); int read = 0; byte[] buffer = new byte[BUFFER_SIZE]; while( (read = in.read( buffer )) > -1 ) { out.write( buffer, 0, read ); } if(log.isDebugEnabled()) { msg = "Attachment "+att.getFileName()+" sent to "+req.getRemoteUser()+" on "+req.getRemoteAddr(); log.debug( msg ); } if( nextPage != null ) res.sendRedirect( nextPage ); return; } msg = "Attachment '" + page + "', version " + ver + " does not exist."; log.info( msg ); res.sendError( HttpServletResponse.SC_NOT_FOUND, msg ); return; } catch( ProviderException pe ) { msg = "Provider error: "+pe.getMessage(); log.debug("Provider failed while reading", pe); // // This might fail, if the response is already committed. So in that // case we just log it. // try { res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg ); } catch( IllegalStateException e ) {} return; } catch( NumberFormatException nfe ) { msg = "Invalid version number (" + version + ")"; res.sendError( HttpServletResponse.SC_BAD_REQUEST, msg ); return; } catch( SocketException se ) { // // These are very common in download situations due to aggressive // clients. No need to try and send an error. // log.debug("I/O exception during download",se); return; } catch( IOException ioe ) { // // Client dropped the connection or something else happened. // We don't know where the error came from, so we'll at least // try to send an error and catch it quietly if it doesn't quite work. // msg = "Error: " + ioe.getMessage(); log.debug("I/O exception during download",ioe); try { res.sendError( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg ); } catch( IllegalStateException e ) {} return; } finally { if( in != null ) { try { in.close(); } catch( IOException e ) {} } // // Quite often, aggressive clients close the connection when they have // received the last bits. Therefore, we close the output, but ignore // any exception that might come out of it. // if( out != null ) { try { out.close(); } catch( IOException e ) {} } } } /** * Returns the mime type for this particular file. Case does not matter.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -