/* * $Id$ * $URL$ */ package org.subethamail.web.servlet; import java.io.IOException; import java.net.URL; import java.util.logging.Level; import javax.inject.Inject; import javax.servlet.FilterChain; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import lombok.extern.java.Log; import org.subethamail.common.NotFoundException; import org.subethamail.core.lists.i.ListMgr; import org.subethamail.web.util.AbstractFilter; /** * This is a little trick that lets mailing lists "claim" any URL that successfully * resolves to this server. Mounted on /*, this filter will try to resolve any * request normally - but if the result is 404, it will try to lookup the URL as * a mailing list URL. If it finds one, it forwards to the list page. Finally * as a last resort if it can do nothing else it will return 404. */ @Log public class ListFilter extends AbstractFilter { private static final long serialVersionUID = 1L; /** */ public static final String LIST_PAGE = "/list.jsp"; public static final String ID_PARAM_NAME = "listId"; public static final String WELCOME_PAGE = "/welcome.jsp"; /** Good for shunting output to the bitbucket */ static final ServletOutputStream NOOP_OUTPUTSTREAM = new ServletOutputStream() { @Override public void write(int b) throws IOException {} @Override public void write(byte[] b, int off, int len) throws IOException {} }; /** * <p>An {@link HttpServletResponseWrapper} that traps HTTP errors by * overriding {@code sendError(int, ..)}. If the error is 404, * anything written is abandoned. If the response is anything else, * it is passed through as-is.</p> * * <p>Note that this wrapper translates getWriter() calls into * getOutputStream() calls to work around broken containers (and * broken specs). */ public class TrappingResponseWrapper extends HttpServletResponseWrapper { /** True when we get a 404 */ boolean trapped; /** Wrap the given {@code response}. */ public TrappingResponseWrapper(HttpServletResponse response) { super(response); } /** */ @Override public void sendError(int errorCode, String errorMessage) throws IOException { if (errorCode == HttpServletResponse.SC_NOT_FOUND) { log.log(Level.FINE,"Got 404, trapping error"); this.trapped = true; } else super.sendError(errorCode, errorMessage); } /** */ @Override public void sendError(int errorCode) throws IOException { if (errorCode == HttpServletResponse.SC_NOT_FOUND) { log.log(Level.FINE,"Got 404, trapping error"); this.trapped = true; } else super.sendError(errorCode); } /** */ @Override public ServletOutputStream getOutputStream() throws IOException { if (log.isLoggable(Level.FINE)) log.log(Level.FINE,"Getting a " + ((this.trapped)?"NOOP":"real") + " OutputStream"); if (this.trapped) return NOOP_OUTPUTSTREAM; else return super.getOutputStream(); } /** */ public boolean isTrapped() { return this.trapped; } } /** */ @Inject ListMgr listMgr; /* (non-Javadoc) * @see org.subethamail.web.util.AbstractFilter#doFilter(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain) */ @Override public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // We only work for GET requests if (!"GET".equals(request.getMethod())) { chain.doFilter(request, response); return; } // First try to process the request normally. If we get 404, we need // to check for a special list address. TrappingResponseWrapper responseWrapper = new TrappingResponseWrapper(response); chain.doFilter(request, responseWrapper); // Maybe we're already done if (!responseWrapper.isTrapped()) return; // Apparently not, let's lookup the URL and see if we have a hit String urlString = request.getRequestURL().toString(); URL url = new URL(urlString); try { Long listId = listMgr.lookup(url); RequestDispatcher dispatcher = request.getRequestDispatcher(LIST_PAGE + "?" + ID_PARAM_NAME + "=" + listId); dispatcher.forward(request, response); } catch (NotFoundException ex) { RequestDispatcher dispatcher = request.getRequestDispatcher(WELCOME_PAGE); dispatcher.forward(request, response); } } }