package org.madore.damlengine; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.Properties; import java.util.Enumeration; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Paths; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* Thinkos about the mess that is tomcat: * - Make sure that /usr/share/java/postgresql-jdbc4.jar (or whatever * contains the class org.postgresql.Driver) is symlinked from * /var/lib/tomcat7/common (or some path listed in the common.loader * property of the catalina.properties file). Similarly, * xercesImpl.jar and probably xml-resolver.jar need to be linked. * - The following parameters need to be defined in tags * inside the block: base_path, template_path, pghost, * pgport, dbname, pguser and pgpass or pgpassfile. */ public class WeblogServlet extends HttpServlet { private static final long serialVersionUID = 2014101204L; @Override public void init(ServletConfig cfg) throws ServletException { super.init(cfg); // Important! synchronized ( DamlEngine.class ) { if ( DamlEngine.appProps == null ) { DamlEngine.runAsServlet = true; DamlEngine.appProps = new Properties(); for (Enumeration e = cfg.getInitParameterNames() ; e.hasMoreElements() ; ) { String k = e.nextElement(); DamlEngine.appProps.setProperty(k, cfg.getInitParameter(k)); } if ( DamlEngine.appProps.getProperty("base_path") != null ) DamlEngine.basePath = Paths.get(DamlEngine.appProps.getProperty("base_path")); if ( DamlEngine.basePath == null ) DamlEngine.basePath = Paths.get(getServletContext().getRealPath("/")); if ( DamlEngine.appProps.getProperty("template_path") != null ) DamlEngine.templatePath = Paths.get(DamlEngine.appProps.getProperty("template_path")); if ( DamlEngine.templatePath == null ) DamlEngine.templatePath = DamlEngine.basePath.resolve("templates"); } } } static abstract class RequestPath { public static RequestPath parsePath(String pathInfo, boolean inGet) throws ServletException { Matcher matcher; if ( pathInfo == null ) return new NoSuchPath(); if ( (matcher=Pattern.compile("/d\\.(\\d{4}-\\d{2}-\\d{2})\\.(\\d{4})(?:\\.([\\p{Alnum}\\-]*))?\\.html").matcher(pathInfo)).matches() ) { final String date; final int number; final String extra; try { date = matcher.group(1); number = Integer.parseInt(matcher.group(2)); extra = matcher.group(3); } catch (NumberFormatException e) { return new NoSuchPath(); } try { final Connection conn = WeblogDatabaseConnection.getConnection(); final PreparedStatement selSt = conn.prepareStatement("SELECT id , extract(epoch from mdate) , special_name FROM entries WHERE edate=? AND id=?"); selSt.setString(1,date); selSt.setInt(2,number); final ResultSet selRes = selSt.executeQuery(); if ( selRes.next() ) { int id = selRes.getInt(1); double mdate = selRes.getDouble(2); String specialName = selRes.getString(3); if ( id != number ) throw new ServletException("this cannot happen"); if ( ! ( ( extra == null && specialName == null ) || ( extra != null && specialName != null && extra.equals(specialName) ) ) ) return new SingleBlogEntryRedirectPath(date, id, specialName); long lastModified = (long)(mdate*1000); long thisLastModified; try { // Attempts to detect when *this* class was last modified. thisLastModified = WeblogServlet.class.getResource("WeblogServlet.class").openConnection().getLastModified(); } catch (Exception e) { thisLastModified = 0; } if ( thisLastModified > lastModified ) lastModified = thisLastModified; return new SingleBlogEntryPath(number, lastModified); } else return new NoSuchPath(); } catch (SQLException e) { throw new ServletException(e); } } // Legacy support for an old permalink format: if ( inGet && (matcher=Pattern.compile("/d\\.(\\d{4}-\\d{2}-\\d{2})\\.([\\p{Alpha}][\\p{Alnum}\\-]*)\\.html").matcher(pathInfo)).matches() ) { final String date; final String extra; try { date = matcher.group(1); extra = matcher.group(2); } catch (NumberFormatException e) { return new NoSuchPath(); } try { final Connection conn = WeblogDatabaseConnection.getConnection(); final PreparedStatement selSt = conn.prepareStatement("SELECT id FROM entries WHERE edate=? AND special_name=?"); selSt.setString(1,date); selSt.setString(2,extra); final ResultSet selRes = selSt.executeQuery(); if ( selRes.next() ) { int id = selRes.getInt(1); return new SingleBlogEntryRedirectPath(date, id, extra); } else return new NoSuchPath(); } catch (SQLException e) { throw new ServletException(e); } } return new NoSuchPath(); } } static final class NoSuchPath extends RequestPath { } static final class SingleBlogEntryPath extends RequestPath { public int number; public long lastModified; public SingleBlogEntryPath(int number, long lastModified) { this.number = number; this.lastModified = lastModified; } } static final class SingleBlogEntryRedirectPath extends RequestPath { public String date; public int number; public String extra; // May be null (passed to WeblogLink class) public SingleBlogEntryRedirectPath(String date, int number, String extra) { this.date = date; this.number = number; this.extra = extra; } } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { final RequestPath rp = RequestPath.parsePath(request.getPathInfo(), true); if ( rp instanceof NoSuchPath ) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } if ( rp instanceof SingleBlogEntryRedirectPath ) { SingleBlogEntryRedirectPath rrp = (SingleBlogEntryRedirectPath)rp; Matcher matcher = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})").matcher(rrp.date); if ( ! matcher.matches() ) throw new AssertionError("bad date format"); WeblogLink lk = new WeblogLink(matcher.group(1), matcher.group(2), matcher.group(3), String.format("%04d", rrp.number), "", rrp.extra); lk.setTypeStandard(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; // response.sendRedirect(lk.getFile(basePath)); response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); response.setHeader("Location", lk.getFile(basePath)); return; } if ( ! (rp instanceof SingleBlogEntryPath) ) throw new ServletException("this cannot happen"); final int number = ((SingleBlogEntryPath)rp).number; response.setContentType("text/html; charset=utf-8"); response.setCharacterEncoding("UTF-8"); OutputStream out = response.getOutputStream(); try { Context.DynamicContext dc = new Context.DynamicContext(); dc.modTime = ((SingleBlogEntryPath)rp).lastModified; WeblogSelect.fullProcess(new Context.WeblogSingleSelectionContext(number), dc, out); } catch (Exception e) { throw new ServletException("exception during WeblogSelect.fullProcess()", e); } } @Override public long getLastModified(HttpServletRequest request) { final RequestPath rp; try { rp = RequestPath.parsePath(request.getPathInfo(), false); } catch (Exception e) { return -1; } if ( rp instanceof SingleBlogEntryPath ) { final long lastModified = ((SingleBlogEntryPath)rp).lastModified; return (lastModified/1000)*1000; } return -1; } }