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;
46 * Extensible handler for open postings.
47 * Behaviour can be altered by overriding methods.
49 public abstract class MirBasicPostingSessionHandler implements SessionHandler {
50 protected static LoggerWrapper logger = new LoggerWrapper("Localizer.OpenPosting");
51 protected MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();
53 /** Previously uploaded files */
54 protected List attachments;
55 /** counter to generate unique field names for uploaded files */
56 protected int uploadedFileIndex = 0;
58 private String normalResponseGenerator;
59 private String dupeResponseGenerator;
60 private String unsupportedMediaTypeResponseGenerator;
61 private String finalResponseGenerator;
63 private boolean persistentUploadedFiles;
65 public MirBasicPostingSessionHandler(boolean aPersistentUploadedFiles) {
66 attachments = new ArrayList();
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) {
118 logger.error("Error while processing attachment", t);
123 public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
126 List validationErrors = new ArrayList();
128 preprocessPreviousAttachments(aRequest, aSession);
129 preProcessNewAttachments(aRequest, aSession);
131 if (!shouldProcessRequest(aRequest, aSession, validationErrors)) {
132 initializeResponseData(aRequest, aSession, aResponse);
133 makeResponse(aRequest, aSession, aResponse, validationErrors);
136 preProcessRequest(aRequest, aSession);
138 processAttachments(aRequest, aSession, aResponse);
139 postProcessRequest(aRequest, aSession);
140 initializeResponseData(aRequest, aSession, aResponse);
141 makeFinalResponse(aRequest, aSession, aResponse);
142 aSession.terminate();
145 catch (Throwable t) {
146 initializeResponseData(aRequest, aSession, aResponse);
147 makeErrorResponse(aRequest, aSession, aResponse, t);
148 aSession.terminate();
151 catch (Throwable t) {
152 aSession.terminate();
154 throw new SessionFailure(t);
159 * Initializes a session.
160 * This may happen in the case of a new session being initiated, but also
161 * when an older session gets re-initiated after a session timeout.
163 protected void initializeSession(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
164 if (MirGlobal.abuse().getOpenPostingPassword()) {
165 String password = (String) aSession.getAttribute("password");
166 if (password==null) {
167 password = generateOnetimePassword();
168 aSession.setAttribute("password", password);
172 aSession.deleteAttribute("password");
175 aSession.setAttribute("referer", aRequest.getHeader("Referer"));
179 * Called every time a response is being prepared.
180 * This may be at the initial response, or on a subsequent one.
182 protected void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
183 int nrMediaItems = configuration.getInt("ServletModule.OpenIndy.DefaultMediaUploadItems", 5);
185 if (aSession.getAttribute("nrmediaitems")!=null) {
186 nrMediaItems = ((Integer) aSession.getAttribute("nrmediaitems")).intValue();
190 nrMediaItems = Math.min(configuration.getInt("ServletModule.OpenIndy.MaxMediaUploadItems"), Integer.parseInt(aRequest.getParameter("nrmediaitems")));
192 catch (Throwable t) {
195 aSession.setAttribute("nrmediaitems", new Integer(nrMediaItems));
197 List mediaItems = new ArrayList();
198 for (int i=1; i<=nrMediaItems; i++) {
199 mediaItems.add(new Integer(i));
202 aResponse.setResponseValue("nrmediaitems", new Integer(nrMediaItems));
203 aResponse.setResponseValue("mediaitems", mediaItems);
204 aResponse.setResponseValue("password", aSession.getAttribute("password"));
205 aResponse.setResponseValue("referer", aSession.getAttribute("referer"));
206 aResponse.setResponseValue("errors", null);
208 if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
209 aResponse.setResponseValue("ftpfiles",
210 FileRoutines.getDirectoryContentsAsList(configuration.getFile("Localizer.OpenSession.FTPDirectory"),
211 new FilenameFilter() {
212 public boolean accept(File aDir, String aName) {
213 return !(new File(aDir, aName).isDirectory());
218 aResponse.setResponseValue("ftpfiles", null);
221 initializeAttachmentResponseData(aRequest, aSession, aResponse);
225 * Process possible changes to previously uploaded files
227 protected void initializeAttachmentResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
228 List result = new ArrayList();
229 if (persistentUploadedFiles) {
230 Iterator i = attachments.iterator();
231 while (i.hasNext()) {
232 Attachment attachment = (Attachment) i.next();
233 Map attachmentData = new HashMap();
234 attachmentData.putAll(attachment.getAllAttributes());
235 attachmentData.put("fieldname", attachment.getFieldName());
236 attachmentData.put("filename", attachment.getFileName());
237 result.add(attachmentData);
241 aResponse.setResponseValue("attachments", result);
245 * Process possible changes to previously uploaded files
247 protected void preprocessPreviousAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
248 synchronized (attachments) {
249 List previouslyUploadedFiles = new ArrayList(attachments);
252 if (persistentUploadedFiles) {
253 Iterator i = previouslyUploadedFiles.iterator();
254 while (i.hasNext()) {
255 Attachment uploadedFile = (Attachment) i.next();
256 if (!(aRequest.getParameter(uploadedFile.getFieldName()+"_cancel")!=null)) {
257 addAttachment(aRequest, aSession, uploadedFile, uploadedFile.getFieldName());
264 protected void addAttachment(Request aRequest, Session aSession, Attachment anAttachment, String aFieldName) throws SessionExc, SessionFailure {
265 List parameters = aRequest.getPrefixedParameterNames(aFieldName+"_");
266 Iterator j = parameters.iterator();
267 while (j.hasNext()) {
268 String parameter = ((String) j.next());
269 anAttachment.setAttribute(
270 parameter.substring(aFieldName.length()+1),
271 aRequest.getParameter(parameter));
273 attachments.add(anAttachment);
276 public void preprocessNewAttachment(Request aRequest, Session aSession, UploadedFile aFile) throws SessionExc, SessionFailure {
277 Attachment uploadedFile =
278 new Attachment(aFile, aFile.getFileName(), "attachment"+ ++uploadedFileIndex, aFile.getContentType());
280 addAttachment(aRequest, aSession, uploadedFile, aFile.getFieldName());
285 * Process newly uploaded files
287 protected void preProcessNewAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
288 Iterator i = aRequest.getUploadedFiles().iterator();
289 while (i.hasNext()) {
290 preprocessNewAttachment(aRequest, aSession, (UploadedFile) i.next());
293 if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
294 File FTPDirectory = configuration.getFile("Localizer.OpenSession.FTPDirectory");
296 List ftpUploads = new ArrayList(aRequest.getPrefixedParameterNames("ftpupload"));
297 Collections.sort(ftpUploads, new Comparator() {
298 public int compare(Object o1, Object o2) {
299 if (o1 instanceof String && o2 instanceof String) {
300 return ((String) o1).compareTo((String) o2);
308 i = ftpUploads.iterator();
309 while (i.hasNext()) {
310 final String fieldName = (String) i.next();
312 if (fieldName.indexOf("_")<0) {
313 final String fileName = aRequest.getParameter(fieldName);
315 if (fileName!=null && fileName.trim().length()>0) {
316 final File sourceFile = new File(FTPDirectory, fileName);
318 if (sourceFile.getParentFile().equals(FTPDirectory)) {
319 preprocessNewAttachment(aRequest, aSession, new UploadedFile() {
320 public void writeToFile(File aFile) throws SessionFailure {
322 FileRoutines.move(sourceFile, aFile);
324 catch (IOException e) {
325 throw new SessionFailure(e);
329 public void writeToStream(OutputStream aStream) throws SessionFailure {
331 IORoutines.copyStream(getInputStream(), aStream);
333 catch (IOException e) {
334 throw new SessionFailure(e);
338 public InputStream getInputStream() throws SessionFailure {
340 return new FileInputStream(sourceFile);
342 catch (IOException e) {
343 throw new SessionFailure(e);
347 public String getFileName() {
351 public String getFieldName() {
355 public String getContentType() {
366 protected void makeInitialResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
367 aResponse.setResponseGenerator(normalResponseGenerator);
370 protected void makeResponse(Request aRequest, Session aSession, Response aResponse, List anErrors) throws SessionExc, SessionFailure {
371 aResponse.setResponseValue("errors", anErrors);
372 aResponse.setResponseGenerator(normalResponseGenerator);
375 protected void makeFinalResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
376 aResponse.setResponseGenerator(finalResponseGenerator);
379 protected void makeErrorResponse(Request aRequest, Session aSession, Response aResponse, Throwable anError) throws SessionExc, SessionFailure {
380 Throwable rootCause = ExceptionRoutines.traceCauseException(anError);
382 if (rootCause instanceof DuplicatePostingExc)
383 aResponse.setResponseGenerator(dupeResponseGenerator);
384 if (rootCause instanceof UnsupportedMediaTypeExc) {
385 aResponse.setResponseValue("mimetype", ((UnsupportedMediaTypeExc) rootCause).getMimeType());
386 aResponse.setResponseGenerator(unsupportedMediaTypeResponseGenerator);
389 List errors = new ArrayList();
390 errors.add(new ValidationError("", "general.unexpectederror",
391 new Object[] {anError.getMessage()}));
392 makeResponse(aRequest, aSession, aResponse, errors);
396 protected void makeOpenPostingDisabledResponse(Request aRequest, Session aSession, Response aResponse) {
397 aResponse.setResponseGenerator(configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate"));
403 protected void preProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
405 public void processAttachment(Request aRequest, Session aSession, Attachment aFile) throws SessionExc, SessionFailure {
407 public void processAttachmentError(Request aRequest, Session aSession, Attachment aFile, Throwable anError) {
409 protected void postProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
413 * Determine whether the request shoudl be processed: that is, the validate,
414 * preprocess, postprocess methods should be called to perform validations,
417 protected boolean shouldProcessRequest(Request aRequest, Session aSession, List aValidationErrors) throws SessionExc, SessionFailure {
418 if (aRequest.getParameter("post")==null)
420 validate(aValidationErrors, aRequest, aSession);
421 return (aValidationErrors == null || aValidationErrors.size() == 0);
425 * Method used to validate user input.
426 * Multiple {@link ValidationError}s may be added to the
427 * <code>aResults</code> parameter.
428 * The request is considered validated if, after calling this method,
429 * <code>aResults</code> is empty.
431 protected void validate(List aResults, Request aRequest, Session aSession) throws SessionExc, SessionFailure {
432 String password = (String) aSession.getAttribute("password");
434 if (password!=null) {
435 String submittedPassword= aRequest.getParameter("password").trim();
437 if (!password.equals(submittedPassword)) {
438 aResults.add(new ValidationError("password", "passwordmismatch"));
445 * Method to generate a one-time password
447 * @return a password, to be used once
449 protected String generateOnetimePassword() {
450 Random r = new Random();
451 int random = r.nextInt();
453 long l = System.currentTimeMillis();
455 l = (l*l*l*l)/random;
459 String returnString = ""+l;
461 return returnString.substring(5);
465 * Method to filter the attributes and their values of a request
466 * based on the fields of a database object.
468 protected static final Map getIntersectingValues(Request aRequest, Database aStorage) throws SessionFailure {
469 Map result = new HashMap();
471 Iterator i = aStorage.getFieldNames().iterator();
473 while (i.hasNext()) {
474 String fieldName = (String) i.next();
475 Object value = aRequest.getParameter(fieldName);
477 result.put(fieldName, value);
484 * Exception to be thrown when an article or comment was already posted
486 protected static class DuplicatePostingExc extends SessionExc {
487 public DuplicatePostingExc(String aMessage) {
493 * A file that has been attached to a session
495 protected static class Attachment implements UploadedFile {
496 private UploadedFile uploadedFile;
497 private String filename;
498 private String fieldName;
499 private String contentType;
500 private Map attributes;
502 public Attachment(UploadedFile anUploadedFile, String aFilename, String aFieldName, String aContentType) {
503 attributes = new HashMap();
504 filename = aFilename;
505 fieldName = aFieldName;
506 contentType = aContentType;
507 uploadedFile = anUploadedFile;
510 public void writeToFile(File aFile) throws SessionExc, SessionFailure {
511 uploadedFile.writeToFile(aFile);
514 public void writeToStream(OutputStream aStream) throws SessionExc, SessionFailure {
516 IORoutines.copyStream(uploadedFile.getInputStream(), aStream);
518 catch (IOException e) {
519 throw new SessionFailure(e);
523 public InputStream getInputStream() throws SessionExc, SessionFailure {
524 return uploadedFile.getInputStream();
527 public String getFileName() {
531 public String getFieldName() {
535 public String getContentType() {
539 public String getAttribute(String anAttribute) {
540 return (String) attributes.get(anAttribute);
543 public void setAttribute(String anAttribute, String aValue) {
544 attributes.put(anAttribute, aValue);
547 public Map getAllAttributes() {
548 return Collections.unmodifiableMap(attributes);