2 * Copyright (C) 2001, 2002 The Mir-coders group
4 * This file is part of Mir.
6 * Mir is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * Mir is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Mir; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * In addition, as a special exception, The Mir-coders gives permission to link
21 * the code of this program with any library licensed under the Apache Software License,
22 * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23 * (or with modified versions of the above that use the same license as the above),
24 * and distribute linked combinations including the two. You must obey the
25 * GNU General Public License in all respects for all of the code used other than
26 * the above mentioned libraries. If you modify this file, you may extend this
27 * exception to your version of the file, but you are not obligated to do so.
28 * If you do not wish to do so, delete this exception statement from your version.
30 package mircoders.localizer.basic;
32 import mir.config.MirPropertiesConfiguration;
33 import mir.log.LoggerWrapper;
35 import mir.storage.Database;
36 import mir.util.ExceptionRoutines;
37 import mir.util.FileRoutines;
38 import mir.util.IORoutines;
39 import mircoders.global.MirGlobal;
40 import mircoders.media.UnsupportedMediaTypeExc;
41 import mircoders.localizer.MirOpenPostingLocalizer;
47 * Extensible handler for open postings.
48 * Behaviour can be altered by overriding methods.
50 public abstract class MirBasicPostingSessionHandler implements SessionHandler {
51 protected static LoggerWrapper logger = new LoggerWrapper("Localizer.OpenPosting");
52 protected MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
54 /** Previously uploaded files */
55 protected final List attachments = new ArrayList();
56 /** counter to generate unique field names for uploaded files */
57 protected int uploadedFileIndex = 0;
59 private String normalResponseGenerator;
60 private String dupeResponseGenerator;
61 private String unsupportedMediaTypeResponseGenerator;
62 private String finalResponseGenerator;
64 private boolean persistentUploadedFiles;
66 public MirBasicPostingSessionHandler(boolean aPersistentUploadedFiles) {
67 persistentUploadedFiles = aPersistentUploadedFiles;
70 protected void setNormalResponseGenerator(String aGenerator) {
71 normalResponseGenerator = aGenerator;
74 protected void setResponseGenerators(String aNormalResponseGenerator,
75 String aDupeResponseGenerator, String anUnsupportedMediaTypeResponseGenerator,
76 String aFinalResponseGenerator) {
77 setNormalResponseGenerator(aNormalResponseGenerator);
78 dupeResponseGenerator = aDupeResponseGenerator;
79 unsupportedMediaTypeResponseGenerator = anUnsupportedMediaTypeResponseGenerator;
80 finalResponseGenerator = aFinalResponseGenerator;
83 public void processRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
84 if (MirGlobal.abuse().getOpenPostingDisabled()) {
85 makeOpenPostingDisabledResponse(aRequest, aSession, aResponse);
89 if (aSession.getAttribute("initialRequest") == null) {
90 initialRequest(aRequest, aSession, aResponse);
91 aSession.setAttribute("initialRequest", "no");
94 subsequentRequest(aRequest, aSession, aResponse);
99 protected void initialRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
100 initializeSession(aRequest, aSession);
101 initializeResponseData(aRequest, aSession, aResponse);
102 makeInitialResponse(aRequest, aSession, aResponse);
105 protected void processAttachments(Request aRequest, Session aSession, Response aResponse) {
106 Iterator i = attachments.iterator();
107 while (i.hasNext()) {
108 Attachment attachment = (Attachment) i.next();
110 processAttachment(aRequest, aSession, attachment);
112 catch (Throwable t) {
114 processAttachmentError(aRequest, aSession, attachment, t);
116 catch (Throwable u) {
117 logger.error("Error while processing attachment error", u);
119 logger.error("Error while processing attachment", t);
124 public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
127 List validationErrors = new ArrayList();
129 preprocessPreviousAttachments(aRequest, aSession);
130 preProcessNewAttachments(aRequest, aSession);
132 if (!shouldProcessRequest(aRequest, aSession, validationErrors)) {
133 initializeResponseData(aRequest, aSession, aResponse);
134 makeResponse(aRequest, aSession, aResponse, validationErrors);
137 preProcessRequest(aRequest, aSession);
139 processAttachments(aRequest, aSession, aResponse);
140 postProcessRequest(aRequest, aSession);
141 initializeResponseData(aRequest, aSession, aResponse);
142 makeFinalResponse(aRequest, aSession, aResponse);
143 aSession.terminate();
146 catch (Throwable t) {
147 initializeResponseData(aRequest, aSession, aResponse);
148 makeErrorResponse(aRequest, aSession, aResponse, t);
149 aSession.terminate();
152 catch (Throwable t) {
153 aSession.terminate();
155 throw new SessionFailure(t);
160 * Initializes a session.
161 * This may happen in the case of a new session being initiated, but also
162 * when an older session gets re-initiated after a session timeout.
164 protected void initializeSession(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
165 aSession.setAttribute("referer", aRequest.getHeader("Referer"));
169 * Called every time a response is being prepared.
170 * This may be at the initial response, or on a subsequent one.
172 protected void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
173 int nrMediaItems = configuration.getInt("ServletModule.OpenIndy.DefaultMediaUploadItems", 5);
175 if (aSession.getAttribute("nrmediaitems")!=null) {
176 nrMediaItems = ((Integer) aSession.getAttribute("nrmediaitems")).intValue();
180 nrMediaItems = Math.min(configuration.getInt("ServletModule.OpenIndy.MaxMediaUploadItems"), Integer.parseInt(aRequest.getParameter("nrmediaitems")));
182 catch (Throwable t) {
183 logger.warn("Error while retrieving configuration setting " +
184 "ServletModule.OpenIndy.MaxMediaUploadItems", t);
187 aSession.setAttribute("nrmediaitems", new Integer(nrMediaItems));
189 List mediaItems = new ArrayList();
190 for (int i=1; i<=nrMediaItems; i++) {
191 mediaItems.add(new Integer(i));
194 aResponse.setResponseValue("nrmediaitems", new Integer(nrMediaItems));
195 aResponse.setResponseValue("mediaitems", mediaItems);
197 if (MirGlobal.abuse().getRequireCaptcha()) {
198 aResponse.setResponseValue("password", Boolean.TRUE);
200 aResponse.setResponseValue("referer", aSession.getAttribute("referer"));
201 aResponse.setResponseValue("errors", null);
203 if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
204 aResponse.setResponseValue("ftpfiles",
205 FileRoutines.getDirectoryContentsAsList(configuration.getFile("Localizer.OpenSession.FTPDirectory"),
206 new FilenameFilter() {
207 public boolean accept(File aDir, String aName) {
208 return !(new File(aDir, aName).isDirectory());
213 aResponse.setResponseValue("ftpfiles", null);
216 initializeAttachmentResponseData(aRequest, aSession, aResponse);
220 * Process possible changes to previously uploaded files
222 protected void initializeAttachmentResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
223 List result = new ArrayList();
224 if (persistentUploadedFiles) {
225 Iterator i = attachments.iterator();
226 while (i.hasNext()) {
227 Attachment attachment = (Attachment) i.next();
228 Map attachmentData = new HashMap();
229 attachmentData.putAll(attachment.getAllAttributes());
230 attachmentData.put("fieldname", attachment.getFieldName());
231 attachmentData.put("filename", attachment.getFileName());
232 result.add(attachmentData);
236 aResponse.setResponseValue("attachments", result);
240 * Process possible changes to previously uploaded files
242 protected void preprocessPreviousAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
243 synchronized (attachments) {
244 List previouslyUploadedFiles = new ArrayList(attachments);
247 if (persistentUploadedFiles) {
248 Iterator i = previouslyUploadedFiles.iterator();
249 while (i.hasNext()) {
250 Attachment uploadedFile = (Attachment) i.next();
251 if (!(aRequest.getParameter(uploadedFile.getFieldName()+"_cancel")!=null)) {
252 addAttachment(aRequest, aSession, uploadedFile, uploadedFile.getFieldName());
259 protected void addAttachment(Request aRequest, Session aSession, Attachment anAttachment, String aFieldName) throws SessionExc, SessionFailure {
260 List parameters = aRequest.getPrefixedParameterNames(aFieldName+"_");
261 Iterator j = parameters.iterator();
262 while (j.hasNext()) {
263 String parameter = ((String) j.next());
264 anAttachment.setAttribute(
265 parameter.substring(aFieldName.length()+1),
266 aRequest.getParameter(parameter));
268 attachments.add(anAttachment);
271 public void preprocessNewAttachment(Request aRequest, Session aSession, UploadedFile aFile) throws SessionExc, SessionFailure {
272 Attachment uploadedFile =
273 new Attachment(aFile, aFile.getFileName(), "attachment"+ ++uploadedFileIndex, aFile.getContentType());
275 addAttachment(aRequest, aSession, uploadedFile, aFile.getFieldName());
280 * Process newly uploaded files
282 protected void preProcessNewAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
283 Iterator i = aRequest.getUploadedFiles().iterator();
284 while (i.hasNext()) {
285 preprocessNewAttachment(aRequest, aSession, (UploadedFile) i.next());
288 if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
289 File FTPDirectory = configuration.getFile("Localizer.OpenSession.FTPDirectory");
291 List ftpUploads = new ArrayList(aRequest.getPrefixedParameterNames("ftpupload"));
292 Collections.sort(ftpUploads, new Comparator() {
293 public int compare(Object o1, Object o2) {
294 if (o1 instanceof String && o2 instanceof String) {
295 return ((String) o1).compareTo((String) o2);
303 i = ftpUploads.iterator();
304 while (i.hasNext()) {
305 final String fieldName = (String) i.next();
307 if (fieldName.indexOf("_")<0) {
308 final String fileName = aRequest.getParameter(fieldName);
310 if (fileName!=null && fileName.trim().length()>0) {
311 final File sourceFile = new File(FTPDirectory, fileName);
313 if (sourceFile.getParentFile().equals(FTPDirectory)) {
314 preprocessNewAttachment(aRequest, aSession, new UploadedFile() {
315 public void writeToFile(File aFile) throws SessionFailure {
317 FileRoutines.move(sourceFile, aFile);
319 catch (IOException e) {
320 throw new SessionFailure(e);
324 public void writeToStream(OutputStream aStream) throws SessionFailure {
326 IORoutines.copyStream(getInputStream(), aStream);
328 catch (IOException e) {
329 throw new SessionFailure(e);
333 public InputStream getInputStream() throws SessionFailure {
335 return new FileInputStream(sourceFile);
337 catch (IOException e) {
338 throw new SessionFailure(e);
342 public String getFileName() {
346 public String getFieldName() {
350 public String getContentType() {
361 protected void makeInitialResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
362 aResponse.setResponseGenerator(normalResponseGenerator);
365 protected void makeResponse(Request aRequest, Session aSession, Response aResponse, List anErrors) throws SessionExc, SessionFailure {
366 aResponse.setResponseValue("errors", anErrors);
367 aResponse.setResponseGenerator(normalResponseGenerator);
370 protected void makeFinalResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
371 aResponse.setResponseGenerator(finalResponseGenerator);
374 protected void makeErrorResponse(Request aRequest, Session aSession, Response aResponse, Throwable anError) throws SessionExc, SessionFailure {
375 Throwable rootCause = ExceptionRoutines.traceCauseException(anError);
377 if (rootCause instanceof DuplicatePostingExc)
378 aResponse.setResponseGenerator(dupeResponseGenerator);
379 if (rootCause instanceof UnsupportedMediaTypeExc) {
380 aResponse.setResponseValue("mimetype", ((UnsupportedMediaTypeExc) rootCause).getMimeType());
381 aResponse.setResponseGenerator(unsupportedMediaTypeResponseGenerator);
384 List errors = new ArrayList();
385 errors.add(new ValidationError("", "general.unexpectederror",
386 new Object[] {anError.getMessage()}));
387 makeResponse(aRequest, aSession, aResponse, errors);
391 protected void makeOpenPostingDisabledResponse(Request aRequest, Session aSession, Response aResponse) {
392 aResponse.setResponseGenerator(configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate"));
398 protected void preProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
400 public void processAttachment(Request aRequest, Session aSession, Attachment aFile) throws SessionExc, SessionFailure {
402 public void processAttachmentError(Request aRequest, Session aSession, Attachment aFile, Throwable anError) {
404 protected void postProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
408 * Determine whether the request shoudl be processed: that is, the validate,
409 * preprocess, postprocess methods should be called to perform validations,
412 protected boolean shouldProcessRequest(Request aRequest, Session aSession, List aValidationErrors) throws SessionExc, SessionFailure {
413 if (aRequest.getParameter("post")==null) {
416 validate(aValidationErrors, aRequest, aSession);
418 return (aValidationErrors == null || aValidationErrors.size() == 0);
422 * Method used to validate user input.
423 * Multiple {@link ValidationError}s may be added to the
424 * <code>aResults</code> parameter.
425 * The request is considered validated if, after calling this method,
426 * <code>aResults</code> is empty.
428 protected void validate(List aResults, Request aRequest, Session aSession) throws SessionExc, SessionFailure {
429 if (MirGlobal.abuse().getRequireCaptcha()) {
430 String submittedPassword = aRequest.getParameter("password");
431 MirOpenPostingLocalizer.Captcha captcha = (MirOpenPostingLocalizer.Captcha) aSession.getAttribute("captcha");
433 if (captcha == null || !captcha.validateAnswer(submittedPassword)) {
434 aResults.add(new ValidationError("password", "validationerror.passwordmismatch"));
442 * Method to filter the attributes and their values of a request
443 * based on the fields of a database object.
445 protected static Map getIntersectingValues(Request aRequest, Database aStorage) throws SessionFailure {
446 Map result = new HashMap();
448 Iterator i = aStorage.getFieldNames().iterator();
450 while (i.hasNext()) {
451 String fieldName = (String) i.next();
452 Object value = aRequest.getParameter(fieldName);
454 result.put(fieldName, value);
461 * Exception to be thrown when an article or comment was already posted
463 protected static class DuplicatePostingExc extends SessionExc {
464 public DuplicatePostingExc(String aMessage) {
470 * A file that has been attached to a session
472 protected static class Attachment implements UploadedFile {
473 private UploadedFile uploadedFile;
474 private String filename;
475 private String fieldName;
476 private String contentType;
477 private Map attributes;
479 public Attachment(UploadedFile anUploadedFile, String aFilename, String aFieldName, String aContentType) {
480 attributes = new HashMap();
481 filename = aFilename;
482 fieldName = aFieldName;
483 contentType = aContentType;
484 uploadedFile = anUploadedFile;
487 public void writeToFile(File aFile) throws SessionExc, SessionFailure {
488 uploadedFile.writeToFile(aFile);
491 public void writeToStream(OutputStream aStream) throws SessionExc, SessionFailure {
493 IORoutines.copyStream(uploadedFile.getInputStream(), aStream);
495 catch (IOException e) {
496 throw new SessionFailure(e);
500 public InputStream getInputStream() throws SessionExc, SessionFailure {
501 return uploadedFile.getInputStream();
504 public String getFileName() {
508 public String getFieldName() {
512 public String getContentType() {
516 public String getAttribute(String anAttribute) {
517 return (String) attributes.get(anAttribute);
520 public void setAttribute(String anAttribute, String aValue) {
521 attributes.put(anAttribute, aValue);
524 public Map getAllAttributes() {
525 return Collections.unmodifiableMap(attributes);