media handling fixes, gotten rid of StorageObject, set the default method for blots...
[mir.git] / source / mircoders / localizer / basic / MirBasicPostingSessionHandler.java
index 245e6b3..75e7978 100755 (executable)
@@ -1,46 +1,48 @@
-/*\r
- * Copyright (C) 2001, 2002  The Mir-coders group\r
- *\r
- * This file is part of Mir.\r
- *\r
- * Mir is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * Mir is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with Mir; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- *\r
- * In addition, as a special exception, The Mir-coders gives permission to link\r
- * the code of this program with the com.oreilly.servlet library, any library\r
- * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
- * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
- * the above that use the same license as the above), and distribute linked\r
- * combinations including the two.  You must obey the GNU General Public\r
- * License in all respects for all of the code used other than the above\r
- * mentioned libraries.  If you modify this file, you may extend this exception\r
- * to your version of the file, but you are not obligated to do so.  If you do\r
- * not wish to do so, delete this exception statement from your version.\r
- */\r
-\r
-package mircoders.localizer.basic;\r
-\r
-import java.util.Arrays;
+/*
+ * Copyright (C) 2001, 2002 The Mir-coders group
+ *
+ * This file is part of Mir.
+ *
+ * Mir is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Mir is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mir; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * In addition, as a special exception, The Mir-coders gives permission to link
+ * the code of this program with  any library licensed under the Apache Software License,
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
+ * (or with modified versions of the above that use the same license as the above),
+ * and distribute linked combinations including the two.  You must obey the
+ * GNU General Public License in all respects for all of the code used other than
+ * the above mentioned libraries.  If you modify this file, you may extend this
+ * exception to your version of the file, but you are not obligated to do so.
+ * If you do not wish to do so, delete this exception statement from your version.
+ */
+package mircoders.localizer.basic;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
-import java.util.Vector;
 
 import mir.config.MirPropertiesConfiguration;
-import mir.entity.Entity;
 import mir.log.LoggerWrapper;
 import mir.session.Request;
 import mir.session.Response;
@@ -49,324 +51,490 @@ import mir.session.SessionExc;
 import mir.session.SessionFailure;
 import mir.session.SessionHandler;
 import mir.session.UploadedFile;
-import mir.storage.StorageObject;
+import mir.session.ValidationError;
+import mir.storage.Database;
 import mir.util.ExceptionFunctions;
-import mircoders.entity.EntityComment;
+import mir.util.FileFunctions;
 import mircoders.global.MirGlobal;
-import mircoders.media.MediaUploadProcessor;
-import mircoders.module.ModuleComment;
-import mircoders.storage.DatabaseComment;
-import mircoders.storage.DatabaseCommentToMedia;
-import mircoders.storage.DatabaseContent;\r
-\r
-/**\r
- *\r
- * <p>Title: Experimental session handler for comment postings </p>\r
- * <p>Description: </p>\r
- * <p>Copyright: Copyright (c) 2003</p>\r
- * <p>Company: </p>\r
- * @author not attributable\r
- * @version 1.0\r
- */\r
-\r
-public class MirBasicPostingSessionHandler implements SessionHandler {\r
-  protected LoggerWrapper logger;\r
-  protected MirPropertiesConfiguration configuration;\r
-  protected ModuleComment commentModule;\r
-  protected DatabaseCommentToMedia commentToMedia = DatabaseCommentToMedia.getInstance();\r
-\r
-  public MirBasicPostingSessionHandler() {\r
-    logger = new LoggerWrapper("Localizer.OpenPosting");\r
-    try {\r
-      configuration = MirPropertiesConfiguration.instance();\r
-    }\r
-    catch (Throwable t) {\r
-      logger.fatal("Cannot load configuration: " + t.toString());\r
-\r
-      throw new RuntimeException("Cannot load configuration: " + t.toString());\r
-    }\r
-    commentModule= new ModuleComment(DatabaseComment.getInstance());\r
-  }\r
-\r
-  public void processRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {\r
-    if (aSession.getAttribute("initialRequest")==null) {\r
-      initialRequest(aRequest, aSession, aResponse);\r
-      aSession.setAttribute("initialRequest", "no");\r
-    }\r
-    else {\r
-      subsequentRequest(aRequest, aSession, aResponse);\r
-    }\r
-  };\r
-\r
-  protected Map getIntersectingValues(Request aRequest, StorageObject aStorage) throws SessionExc, SessionFailure {\r
-    Map result = new HashMap();\r
-\r
-    Iterator i = aStorage.getFields().iterator();\r
-\r
-    while (i.hasNext()) {\r
-      String fieldName = (String) i.next();\r
-      Object value = aRequest.getParameter(fieldName);\r
-      if (value != null)\r
-        result.put(fieldName, value);\r
-    }\r
-\r
-    return result;\r
-  }\r
-\r
-\r
-  protected String generateOnetimePassword() {\r
-    Random r = new Random();\r
-    int random = r.nextInt();\r
-\r
-    long l = System.currentTimeMillis();\r
-\r
-    l = (l*l*l*l)/random;\r
-    if (l<0)\r
-      l = l * -1;\r
-\r
-    String returnString = ""+l;\r
-\r
-    return returnString.substring(5);\r
-  }\r
-\r
-  protected void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {\r
-    if (MirGlobal.abuse().getOpenPostingPassword()) {\r
-      String password = (String) aSession.getAttribute("password");\r
-      if (password==null) {\r
-        password = generateOnetimePassword();\r
-        aSession.setAttribute("password", password);\r
-      }\r
-      aResponse.setResponseValue("password", password);\r
-    }\r
-    else {\r
-      aResponse.setResponseValue("password", null);\r
-      aSession.deleteAttribute("password");\r
-    }\r
-\r
-    aResponse.setResponseValue("errors", null);\r
-  };\r
-\r
-  protected void initialRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure{\r
-    Iterator i = DatabaseComment.getInstance().getFields().iterator();\r
-    while (i.hasNext()) {\r
-      aResponse.setResponseValue( (String) i.next(), null);\r
-    }\r
-\r
-    String articleId = aRequest.getParameter("to_media");\r
-\r
-    if (articleId == null)\r
-      throw new SessionExc("MirBasicPostingSessionHandler.initialRequest: article id not set!");\r
-\r
-    aSession.setAttribute("to_media", articleId);\r
-\r
-    initializeResponseData(aRequest, aSession, aResponse);\r
-\r
-    try {\r
-      aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.EditTemplate"));\r
-    }\r
-    catch (Throwable e) {\r
-      throw new SessionFailure("Can't get configuration: " + e.getMessage(), e);\r
-    }\r
-\r
-  }\r
-\r
-  protected boolean testFieldEntered(Request aRequest, String aFieldName, String anErrorMessageResource, List aValidationResults) {\r
-    Object value = aRequest.getParameter(aFieldName);\r
-    if (value==null || !(value instanceof String) || ((String) value).trim().length()==0) {\r
-      logger.debug("  missing field " + aFieldName + " value = " + value);\r
-      aValidationResults.add(new ValidationError(aFieldName, anErrorMessageResource));\r
-      return false;\r
-    }\r
-    else\r
-      return true;\r
-  }\r
-\r
-  protected boolean testFieldIsNumeric(Request aRequest, String aFieldName, String anErrorMessageResource, List aValidationResults) {\r
-    Object value = aRequest.getParameter(aFieldName);\r
-    if (value!=null) {\r
-      try {\r
-        Integer.parseInt((String) value);\r
-        return true;\r
-      }\r
-      catch (Throwable t) {\r
-        logger.debug("  field not numeric: " + aFieldName + " value = " + value);\r
-        aValidationResults.add(new ValidationError(aFieldName, anErrorMessageResource));\r
-        return false;\r
-      }\r
-    }\r
-    return true;\r
-  }\r
-\r
-  public void validate(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {\r
-\r
-  }\r
-\r
-  public List validate(Request aRequest, Session aSession) throws SessionExc, SessionFailure {\r
-    List result = new Vector();\r
-\r
-    testFieldEntered(aRequest, "title", "validationerror.missing", result);\r
-    testFieldEntered(aRequest, "description", "validationerror.missing", result);\r
-    testFieldEntered(aRequest, "creator", "validationerror.missing", result);\r
-\r
-    return result;\r
-  }\r
-\r
-  public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {\r
-    try {\r
-      Map commentFields = new HashMap();\r
-\r
-      Iterator i = DatabaseContent.getInstance().getFields().iterator();\r
-      while (i.hasNext()) {\r
-        String field = (String) i.next();\r
-        aResponse.setResponseValue(field, aRequest.getParameter(field));\r
-        if (aRequest.getParameter(field)!=null) {\r
-          commentFields.put(field, aRequest.getParameter(field));\r
-        }\r
-      }\r
-\r
-      initializeResponseData(aRequest, aSession, aResponse);\r
-\r
-      List validationErrors = validate(aRequest, aSession);\r
-\r
-      if (validationErrors != null && validationErrors.size()>0) {\r
-        returnValidationErrors(aRequest, aSession, aResponse, validationErrors);\r
-      }\r
-      else {\r
-//        finish(aRequest, aSession, aResponse);\r
-\r
-        EntityComment comment = (EntityComment) commentModule.createNew ();\r
-//        comment.setValues(getIntersectingValues(aRequest, ));\r
-\r
-        finishComment(aRequest, aSession, comment);\r
-\r
-        String id = comment.insert();\r
-        if(id==null){\r
-          afterDuplicateCommentPosting(aRequest, aSession, aResponse, comment);\r
-          logger.info("Dupe comment rejected");\r
-          aSession.terminate();\r
-        }\r
-        else {\r
-          i = aRequest.getUploadedFiles().iterator();\r
-          while (i.hasNext()) {\r
-            UploadedFile file = (UploadedFile) i.next();\r
-            processMediaFile(aRequest, aSession, comment, file);\r
-          }\r
-\r
-          afterCommentPosting(aRequest, aSession, aResponse, comment);\r
-          MirGlobal.abuse().checkComment(comment, aRequest, null);\r
-          MirGlobal.localizer().openPostings().afterCommentPosting(comment);\r
-          logger.info("Comment posted");\r
-          aSession.terminate();\r
-        }\r
-      }\r
-    }\r
-    catch (Throwable t) {\r
-      ExceptionFunctions.traceCauseException(t).printStackTrace();\r
-\r
-      throw new SessionFailure("MirBasicPostingSessionHandler.subsequentRequest: " + t.getMessage(), t);\r
-    }\r
-  }\r
-\r
-  public void initializeCommentPosting(Request aRequest, Session aSession, Response aResponse) throws SessionFailure, SessionExc {\r
-    String articleId = aRequest.getParameter("to_media");\r
-    if (articleId==null)\r
-      articleId = aRequest.getParameter("aid");\r
-\r
-    if (articleId==null)\r
-      throw new SessionExc("initializeCommentPosting: article id not set!");\r
-\r
-    aSession.setAttribute("to_media", articleId);\r
-    processCommentPosting(aRequest, aSession, aResponse);\r
-  };\r
-\r
-  public void returnValidationErrors(Request aRequest, Session aSession, Response aResponse, List aValidationErrors) throws SessionFailure, SessionExc {\r
-    aResponse.setResponseValue("errors", aValidationErrors);\r
-    aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.EditTemplate"));\r
-  };\r
-\r
-  public void processCommentPosting(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {\r
-    if (MirGlobal.abuse().getOpenPostingPassword()) {\r
-      String password = generateOnetimePassword();\r
-      aSession.setAttribute("password", password);\r
-      aResponse.setResponseValue("password", password);\r
-      aResponse.setResponseValue("passwd", password);\r
-    }\r
-    else {\r
-      aResponse.setResponseValue("password", null);\r
-    }\r
-\r
-    aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.EditTemplate"));\r
-  };\r
-\r
-  public void processMediaFile(Request aRequest, Session aSession, EntityComment aComment, UploadedFile aFile) throws SessionExc, SessionFailure {\r
-    try {\r
-      Entity mediaItem = MediaUploadProcessor.processMediaUpload(aFile, new HashMap());\r
-      finishMedia(aRequest, aSession, aFile, mediaItem);\r
-      mediaItem.update();\r
-      commentToMedia.addMedia(aComment.getId(), mediaItem.getId());\r
-    }\r
-    catch (Throwable t) {\r
-      throw new SessionFailure(t);\r
-    }\r
-  }\r
-\r
-  public void finishMedia(Request aRequest, Session aSession, UploadedFile aFile, Entity aMedia) throws SessionExc, SessionFailure {\r
-  }\r
-\r
-  public void finishComment(Request aRequest, Session aSession, EntityComment aComment) throws SessionExc, SessionFailure {\r
-    if (aSession.getAttribute("to_media") == null)\r
-      throw new SessionExc("missing to_media");\r
-\r
-    aComment.setValueForProperty("is_published", "1");\r
-    aComment.setValueForProperty("to_comment_status", "1");\r
-    aComment.setValueForProperty("is_html","0");\r
-    aComment.setValueForProperty("to_media", (String) aSession.getAttribute("to_media"));\r
-  };\r
-\r
-  public void addMedia(Request aRequest, Session aSession, EntityComment aComment)  throws SessionExc, SessionFailure {\r
-  }\r
-\r
-  public void afterCommentPosting(Request aRequest, Session aSession, Response aResponse, EntityComment aComment) {\r
-    DatabaseContent.getInstance().setUnproduced("id=" + aComment.getValue("to_media"));\r
-    aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.DoneTemplate"));\r
-  };\r
-\r
-  public void afterDuplicateCommentPosting(Request aRequest, Session aSession, Response aResponse, EntityComment aComment) {\r
-    aResponse.setResponseGenerator(configuration.getString("Localizer.OpenSession.comment.DupeTemplate"));\r
-  };\r
-\r
-  public class ValidationError {\r
-    private String field;\r
-    private String message;\r
-    private List parameters;\r
-\r
-    public ValidationError(String aField, String aMessage) {\r
-      this (aField, aMessage, new String[] {});\r
-    }\r
-\r
-    public ValidationError(String aField, String aMessage, Object aParameter) {\r
-      this (aField, aMessage, new Object[] {aParameter});\r
-    }\r
-\r
-    public ValidationError(String aField, String aMessage, Object[] aParameters) {\r
-      field = aField;\r
-      message = aMessage;\r
-      parameters = Arrays.asList(aParameters);\r
-    }\r
-\r
-    public String getMessage() {\r
-      return message;\r
-    }\r
-\r
-    public String getField() {\r
-      return field;\r
-    }\r
-\r
-    public List getParameters() {\r
-      return parameters;\r
-    }\r
-  }\r
-\r
-\r
-\r
-}\r
+import mircoders.media.UnsupportedMediaTypeExc;
+
+/**
+ * Extensible handler for open postings.
+ * Behaviour can be altered by overriding methods.
+ */
+public abstract class MirBasicPostingSessionHandler implements SessionHandler {
+  protected static LoggerWrapper logger = new LoggerWrapper("Localizer.OpenPosting");
+  protected MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();;
+
+  /** Previously uploaded files */
+  protected List attachments;
+  /** counter to generate unique field names for uploaded files */
+  protected int uploadedFileIndex = 0;
+
+  private String normalResponseGenerator;
+  private String dupeResponseGenerator;
+  private String unsupportedMediaTypeResponseGenerator;
+  private String finalResponseGenerator;
+
+  private boolean persistentUploadedFiles;
+
+  public MirBasicPostingSessionHandler(boolean aPersistentUploadedFiles) {
+    attachments = new ArrayList();
+    persistentUploadedFiles = aPersistentUploadedFiles;
+  }
+
+  protected void setNormalResponseGenerator(String aGenerator) {
+    normalResponseGenerator = aGenerator;
+  }
+
+  protected void setResponseGenerators(String aNormalResponseGenerator,
+        String aDupeResponseGenerator, String anUnsupportedMediaTypeResponseGenerator,
+        String aFinalResponseGenerator) {
+    setNormalResponseGenerator(aNormalResponseGenerator);
+    dupeResponseGenerator = aDupeResponseGenerator;
+    unsupportedMediaTypeResponseGenerator = anUnsupportedMediaTypeResponseGenerator;
+    finalResponseGenerator = aFinalResponseGenerator;
+  }
+
+  public void processRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    if (MirGlobal.abuse().getOpenPostingDisabled()) {
+      makeOpenPostingDisabledResponse(aRequest, aSession, aResponse);
+      aSession.terminate();
+    }
+    else {
+      if (aSession.getAttribute("initialRequest") == null) {
+        initialRequest(aRequest, aSession, aResponse);
+        aSession.setAttribute("initialRequest", "no");
+      }
+      else {
+        subsequentRequest(aRequest, aSession, aResponse);
+      }
+    }
+  };
+
+  protected void initialRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    initializeSession(aRequest, aSession);
+    initializeResponseData(aRequest, aSession, aResponse);
+    makeInitialResponse(aRequest, aSession, aResponse);
+  }
+
+  protected void processAttachments(Request aRequest, Session aSession, Response aResponse) {
+    Iterator i = attachments.iterator();
+    while (i.hasNext()) {
+      Attachment attachment = (Attachment) i.next();
+      try{
+        processAttachment(aRequest, aSession, attachment);
+      }
+      catch (Throwable t) {
+        try {
+          processAttachmentError(aRequest, aSession, attachment, t);
+        }
+        catch (Throwable u) {
+        }
+      }
+    }
+  }
+
+  public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    try {
+      try {
+        List validationErrors = new ArrayList();
+
+        preprocessPreviousAttachments(aRequest, aSession);
+        preProcessNewAttachments(aRequest, aSession);
+
+        if (!shouldProcessRequest(aRequest, aSession, validationErrors)) {
+          initializeResponseData(aRequest, aSession, aResponse);
+          makeResponse(aRequest, aSession, aResponse, validationErrors);
+        }
+        else {
+          preProcessRequest(aRequest, aSession);
+
+          processAttachments(aRequest, aSession, aResponse);
+          postProcessRequest(aRequest, aSession);
+          initializeResponseData(aRequest, aSession, aResponse);
+          makeFinalResponse(aRequest, aSession, aResponse);
+          aSession.terminate();
+        }
+      }
+      catch (Throwable t) {
+        initializeResponseData(aRequest, aSession, aResponse);
+        makeErrorResponse(aRequest, aSession, aResponse, t);
+        aSession.terminate();
+      }
+    }
+    catch (Throwable t) {
+      aSession.terminate();
+
+      throw new SessionFailure(t);
+    }
+  }
+
+  /**
+   * Initializes a session.
+   * This may happen in the case of a new session being initiated, but also
+   * when an older session gets re-initiated after a session timeout.
+   */
+  protected void initializeSession(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
+    if (MirGlobal.abuse().getOpenPostingPassword()) {
+      String password = (String) aSession.getAttribute("password");
+      if (password==null) {
+        password = generateOnetimePassword();
+        aSession.setAttribute("password", password);
+      }
+    }
+    else {
+      aSession.deleteAttribute("password");
+    }
+
+    aSession.setAttribute("referer", aRequest.getHeader("Referer"));
+  }
+
+  /**
+   * Called every time a response is being prepared.
+   * This may be at the initial response, or on a subsequent one.
+   */
+  protected void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    int nrMediaItems = configuration.getInt("ServletModule.OpenIndy.DefaultMediaUploadItems", 5);
+
+    if (aSession.getAttribute("nrmediaitems")!=null) {
+      nrMediaItems = ((Integer) aSession.getAttribute("nrmediaitems")).intValue();
+    }
+
+    try {
+      nrMediaItems = Math.min(configuration.getInt("ServletModule.OpenIndy.MaxMediaUploadItems"), Integer.parseInt(aRequest.getParameter("nrmediaitems")));
+    }
+    catch (Throwable t) {
+    }
+
+    aSession.setAttribute("nrmediaitems", new Integer(nrMediaItems));
+
+    List mediaItems = new ArrayList();
+    for (int i=1; i<=nrMediaItems; i++) {
+      mediaItems.add(new Integer(i));
+    }
+
+    aResponse.setResponseValue("nrmediaitems", new Integer(nrMediaItems));
+    aResponse.setResponseValue("mediaitems", mediaItems);
+    aResponse.setResponseValue("password", aSession.getAttribute("password"));
+    aResponse.setResponseValue("referer", aSession.getAttribute("referer"));
+    aResponse.setResponseValue("errors", null);
+
+    if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
+      aResponse.setResponseValue("ftpfiles",
+          FileFunctions.getDirectoryContentsAsList(configuration.getFile("Localizer.OpenSession.FTPDirectory"),
+              new FilenameFilter() {
+                public boolean accept(File aDir, String aName) {
+                  return !(new File(aDir, aName).isDirectory());
+                }
+          }));
+    }
+    else {
+      aResponse.setResponseValue("ftpfiles", null);
+    }
+
+    initializeAttachmentResponseData(aRequest, aSession, aResponse);
+  }
+
+  /**
+   * Process possible changes to previously uploaded files
+   */
+  protected void initializeAttachmentResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    List result = new ArrayList();
+    if (persistentUploadedFiles) {
+      Iterator i = attachments.iterator();
+      while (i.hasNext()) {
+        Attachment attachment = (Attachment) i.next();
+        Map attachmentData = new HashMap();
+        attachmentData.putAll(attachment.getAllAttributes());
+        attachmentData.put("fieldname", attachment.getFieldName());
+        attachmentData.put("filename", attachment.getFileName());
+        result.add(attachmentData);
+      }
+    }
+
+    aResponse.setResponseValue("attachments", result);
+  }
+
+  /**
+   * Process possible changes to previously uploaded files
+   */
+  protected void preprocessPreviousAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
+    synchronized (attachments) {
+      List previouslyUploadedFiles = new ArrayList(attachments);
+      attachments.clear();
+
+      if (persistentUploadedFiles) {
+        Iterator i = previouslyUploadedFiles.iterator();
+        while (i.hasNext()) {
+          Attachment uploadedFile = (Attachment) i.next();
+          if (!(aRequest.getParameter(uploadedFile.getFieldName()+"_cancel")!=null)) {
+            addAttachment(aRequest, aSession, uploadedFile, uploadedFile.getFieldName());
+          }
+        }
+      }
+    }
+  }
+
+  protected void addAttachment(Request aRequest, Session aSession, Attachment anAttachment, String aFieldName) throws SessionExc, SessionFailure {
+    List parameters = aRequest.getPrefixedParameterNames(aFieldName+"_");
+    Iterator j = parameters.iterator();
+    while (j.hasNext()) {
+      String parameter = ((String) j.next());
+      anAttachment.setAttribute(
+          parameter.substring(aFieldName.length()+1),
+          aRequest.getParameter(parameter));
+    }
+    attachments.add(anAttachment);
+  }
+
+  public void preprocessNewAttachment(Request aRequest, Session aSession, UploadedFile aFile) throws SessionExc, SessionFailure {
+      Attachment uploadedFile =
+          new Attachment(aFile, aFile.getFileName(), "attachment"+ ++uploadedFileIndex, aFile.getContentType());
+
+      addAttachment(aRequest, aSession, uploadedFile, aFile.getFieldName());
+  }
+
+
+  /**
+   * Process newly uploaded files
+   */
+  protected void preProcessNewAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
+    Iterator i = aRequest.getUploadedFiles().iterator();
+    while (i.hasNext()) {
+      preprocessNewAttachment(aRequest, aSession, (UploadedFile) i.next());
+    }
+
+    if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
+      File FTPDirectory = configuration.getFile("Localizer.OpenSession.FTPDirectory");
+
+      List ftpUploads = aRequest.getPrefixedParameterNames("ftpupload");
+      i = ftpUploads.iterator();
+      while (i.hasNext()) {
+        final String fieldName = (String) i.next();
+
+        if (fieldName.indexOf("_")<0) {
+          final String fileName = aRequest.getParameter(fieldName);
+
+          if (fileName!=null && fileName.trim().length()>0) {
+            final File sourceFile = new File(FTPDirectory, fileName);
+
+            if (sourceFile.getParentFile().equals(FTPDirectory)) {
+              preprocessNewAttachment(aRequest, aSession, new UploadedFile() {
+                public void writeToFile(File aFile) throws SessionFailure {
+                  try {
+                    FileFunctions.move(sourceFile, aFile);
+                  }
+                  catch (IOException e) {
+                    throw new SessionFailure(e);
+                  }
+                }
+
+                public InputStream getInputStream() throws SessionFailure {
+                  try {
+                    return new FileInputStream(sourceFile);
+                  }
+                  catch (IOException e) {
+                    throw new SessionFailure(e);
+                  }
+                }
+
+                public String getFileName() {
+                  return fileName;
+                }
+
+                public String getFieldName() {
+                  return fieldName;
+                }
+
+                public String getContentType() {
+                  return null;
+                }
+              });
+            }
+          }
+        }
+      }
+    }
+  }
+
+  protected void makeInitialResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    aResponse.setResponseGenerator(normalResponseGenerator);
+  };
+
+  protected void makeResponse(Request aRequest, Session aSession, Response aResponse, List anErrors) throws SessionExc, SessionFailure {
+    aResponse.setResponseValue("errors", anErrors);
+    aResponse.setResponseGenerator(normalResponseGenerator);
+  };
+
+  protected void makeFinalResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
+    aResponse.setResponseGenerator(finalResponseGenerator);
+  };
+
+  protected void makeErrorResponse(Request aRequest, Session aSession, Response aResponse, Throwable anError) throws SessionExc, SessionFailure {
+    Throwable rootCause = ExceptionFunctions.traceCauseException(anError);
+
+    if (rootCause instanceof DuplicatePostingExc)
+      aResponse.setResponseGenerator(dupeResponseGenerator);
+    if (rootCause instanceof UnsupportedMediaTypeExc) {
+      aResponse.setResponseValue("mimetype", ((UnsupportedMediaTypeExc) rootCause).getMimeType());
+      aResponse.setResponseGenerator(unsupportedMediaTypeResponseGenerator);
+    }
+    else {
+      List errors = new ArrayList();
+      errors.add(new ValidationError("", "general.unexpectederror",
+          new Object[] {anError.getMessage()}));
+      makeResponse(aRequest, aSession, aResponse, errors);
+    }
+  };
+
+  protected void makeOpenPostingDisabledResponse(Request aRequest, Session aSession, Response aResponse) {
+    aResponse.setResponseGenerator(configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate"));
+  }
+
+  /**
+   *
+   */
+  protected void preProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
+  };
+  public void processAttachment(Request aRequest, Session aSession, Attachment aFile) throws SessionExc, SessionFailure {
+  };
+  public void processAttachmentError(Request aRequest, Session aSession, Attachment aFile, Throwable anError) {
+  };
+  protected void postProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
+  };
+
+  /**
+   * Determine whether the request shoudl be processed: that is, the validate,
+   * preprocess, postprocess methods should be called to perform validations,
+   * store data, etc
+   */
+  protected boolean shouldProcessRequest(Request aRequest, Session aSession, List aValidationErrors) throws SessionExc, SessionFailure {
+    if (aRequest.getParameter("post")==null)
+      return false;
+    else {
+      validate(aValidationErrors, aRequest, aSession);
+      return (aValidationErrors == null || aValidationErrors.size() == 0);
+    }
+  }
+
+  /**
+   * Method used to validate user input.
+   * Multiple {@link ValidationError}s may be added to the
+   * <code>aResults</code> parameter.
+   * The request is considered validated if, after calling this method,
+   * <code>aResults</code> is empty.
+   */
+  protected void validate(List aResults, Request aRequest, Session aSession) throws SessionExc, SessionFailure {
+    String password = (String) aSession.getAttribute("password");
+
+    if (password!=null) {
+      String submittedPassword= aRequest.getParameter("password").trim();
+
+      if (!password.equals(submittedPassword)) {
+        aResults.add(new ValidationError("password", "passwordmismatch"));
+      }
+    }
+  }
+
+
+  /**
+   * Method to generate a one-time password
+   *
+   * @return a password, to be used once
+   */
+  protected String generateOnetimePassword() {
+    Random r = new Random();
+    int random = r.nextInt();
+
+    long l = System.currentTimeMillis();
+
+    l = (l*l*l*l)/random;
+    if (l<0)
+      l = l * -1;
+
+    String returnString = ""+l;
+
+    return returnString.substring(5);
+  }
+
+  /**
+   * Method to filter the attributes and their values of a request
+   * based on the fields of a database object.
+   */
+  protected static final Map getIntersectingValues(Request aRequest, Database aStorage) throws SessionFailure {
+    Map result = new HashMap();
+
+    Iterator i = aStorage.getFieldNames().iterator();
+
+    while (i.hasNext()) {
+      String fieldName = (String) i.next();
+      Object value = aRequest.getParameter(fieldName);
+      if (value != null)
+        result.put(fieldName, value);
+    }
+
+    return result;
+  }
+
+  /**
+   * Exception to be thrown when an article or comment was already posted
+   */
+  protected static class DuplicatePostingExc extends SessionExc {
+    public DuplicatePostingExc(String aMessage) {
+      super(aMessage);
+    }
+  }
+
+  /**
+   * A file that has been attached to a session
+   */
+  protected static class Attachment implements UploadedFile {
+    private UploadedFile uploadedFile;
+    private String filename;
+    private String fieldName;
+    private String contentType;
+    private Map attributes;
+
+    public Attachment(UploadedFile anUploadedFile, String aFilename, String aFieldName, String aContentType) {
+      attributes = new HashMap();
+      filename = aFilename;
+      fieldName = aFieldName;
+      contentType = aContentType;
+      uploadedFile = anUploadedFile;
+    }
+
+    public void writeToFile(File aFile) throws SessionExc, SessionFailure {
+      uploadedFile.writeToFile(aFile);
+    }
+
+    public InputStream getInputStream() throws SessionExc, SessionFailure {
+      return uploadedFile.getInputStream();
+    }
+
+    public String getFileName() {
+      return filename;
+    }
+
+    public String getFieldName() {
+      return fieldName;
+    }
+
+    public String getContentType() {
+      return contentType;
+    }
+
+    public String getAttribute(String anAttribute) {
+      return (String) attributes.get(anAttribute);
+    }
+
+    public void setAttribute(String anAttribute, String aValue) {
+      attributes.put(anAttribute, aValue);
+    }
+
+    public Map getAllAttributes() {
+      return Collections.unmodifiableMap(attributes);
+    }
+  }
+}
+