1.1 restoration
[mir.git] / source / mircoders / localizer / basic / MirBasicPostingSessionHandler.java
1 /*
2  * Copyright (C) 2001, 2002 The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
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.
10  *
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.
15  *
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
19  *
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.
29  */
30 package mircoders.localizer.basic;
31
32 import mir.config.MirPropertiesConfiguration;
33 import mir.log.LoggerWrapper;
34 import mir.session.*;
35 import mir.storage.StorageObject;
36 import mir.util.ExceptionFunctions;
37 import mir.util.FileFunctions;
38 import mircoders.global.MirGlobal;
39 import mircoders.module.ModuleMediaType;
40 import mircoders.media.UnsupportedMediaTypeExc;
41
42 import java.io.*;
43 import java.util.*;
44
45 /**
46  * Extensible handler for open postings.
47  * Behaviour can be altered by overriding methods.
48  */
49 public abstract class MirBasicPostingSessionHandler implements SessionHandler {
50   protected static LoggerWrapper logger = new LoggerWrapper("Localizer.OpenPosting");
51   protected MirPropertiesConfiguration configuration = MirPropertiesConfiguration.instance();;
52
53   /** Previously uploaded files */
54   protected List attachments;
55   /** counter to generate unique field names for uploaded files */
56   protected int uploadedFileIndex = 0;
57
58   private String normalResponseGenerator;
59   private String dupeResponseGenerator;
60   private String unsupportedMediaTypeResponseGenerator;
61   private String finalResponseGenerator;
62
63   private boolean persistentUploadedFiles;
64
65   public MirBasicPostingSessionHandler(boolean aPersistentUploadedFiles) {
66     attachments = new ArrayList();
67     persistentUploadedFiles = aPersistentUploadedFiles;
68   }
69
70   protected void setNormalResponseGenerator(String aGenerator) {
71     normalResponseGenerator = aGenerator;
72   }
73
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;
81   }
82
83   public void processRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
84     if (MirGlobal.abuse().getOpenPostingDisabled()) {
85       makeOpenPostingDisabledResponse(aRequest, aSession, aResponse);
86       aSession.terminate();
87     }
88     else {
89       if (aSession.getAttribute("initialRequest") == null) {
90         initialRequest(aRequest, aSession, aResponse);
91         aSession.setAttribute("initialRequest", "no");
92       }
93       else {
94         subsequentRequest(aRequest, aSession, aResponse);
95       }
96     }
97   };
98
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);
103   }
104
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();
109       try{
110         processAttachment(aRequest, aSession, attachment);
111       }
112       catch (Throwable t) {
113         try {
114           processAttachmentError(aRequest, aSession, attachment, t);
115         }
116         catch (Throwable u) {
117         }
118       }
119     }
120   }
121
122   public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
123     try {
124       try {
125         List validationErrors = new ArrayList();
126
127         preprocessPreviousAttachments(aRequest, aSession);
128         preProcessNewAttachments(aRequest, aSession);
129
130         if (!shouldProcessRequest(aRequest, aSession, validationErrors)) {
131           initializeResponseData(aRequest, aSession, aResponse);
132           makeResponse(aRequest, aSession, aResponse, validationErrors);
133         }
134         else {
135           preProcessRequest(aRequest, aSession);
136
137           processAttachments(aRequest, aSession, aResponse);
138           postProcessRequest(aRequest, aSession);
139           initializeResponseData(aRequest, aSession, aResponse);
140           makeFinalResponse(aRequest, aSession, aResponse);
141           aSession.terminate();
142         }
143       }
144       catch (Throwable t) {
145         initializeResponseData(aRequest, aSession, aResponse);
146         makeErrorResponse(aRequest, aSession, aResponse, t);
147         aSession.terminate();
148       }
149     }
150     catch (Throwable t) {
151       aSession.terminate();
152
153       throw new SessionFailure(t);
154     }
155   }
156
157   /**
158    * Initializes a session.
159    * This may happen in the case of a new session being initiated, but also
160    * when an older session gets re-initiated after a session timeout.
161    */
162   protected void initializeSession(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
163     if (MirGlobal.abuse().getOpenPostingPassword()) {
164       String password = (String) aSession.getAttribute("password");
165       if (password==null) {
166         password = generateOnetimePassword();
167         aSession.setAttribute("password", password);
168       }
169     }
170     else {
171       aSession.deleteAttribute("password");
172     }
173
174     aSession.setAttribute("referer", aRequest.getHeader("Referer"));
175   }
176
177   /**
178    * Called every time a response is being prepared.
179    * This may be at the initial response, or on a subsequent one.
180    */
181   protected void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
182     int nrMediaItems = configuration.getInt("ServletModule.OpenIndy.DefaultMediaUploadItems", 5);
183
184     if (aSession.getAttribute("nrmediaitems")!=null) {
185       nrMediaItems = ((Integer) aSession.getAttribute("nrmediaitems")).intValue();
186     }
187
188     try {
189       nrMediaItems = Math.min(configuration.getInt("ServletModule.OpenIndy.MaxMediaUploadItems"), Integer.parseInt(aRequest.getParameter("nrmediaitems")));
190     }
191     catch (Throwable t) {
192     }
193
194     aSession.setAttribute("nrmediaitems", new Integer(nrMediaItems));
195
196     List mediaItems = new ArrayList();
197     for (int i=1; i<=nrMediaItems; i++) {
198       mediaItems.add(new Integer(i));
199     }
200
201     aResponse.setResponseValue("nrmediaitems", new Integer(nrMediaItems));
202     aResponse.setResponseValue("mediaitems", mediaItems);
203     aResponse.setResponseValue("password", aSession.getAttribute("password"));
204     aResponse.setResponseValue("referer", aSession.getAttribute("referer"));
205     aResponse.setResponseValue("errors", null);
206
207     if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
208       aResponse.setResponseValue("ftpfiles",
209           FileFunctions.getDirectoryContentsAsList(configuration.getFile("Localizer.OpenSession.FTPDirectory"),
210               new FilenameFilter() {
211                 public boolean accept(File aDir, String aName) {
212                   return !(new File(aDir, aName).isDirectory());
213                 }
214           }));
215     }
216     else {
217       aResponse.setResponseValue("ftpfiles", null);
218     }
219
220     initializeAttachmentResponseData(aRequest, aSession, aResponse);
221   }
222
223   /**
224    * Process possible changes to previously uploaded files
225    */
226   protected void initializeAttachmentResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
227     List result = new ArrayList();
228     if (persistentUploadedFiles) {
229       Iterator i = attachments.iterator();
230       while (i.hasNext()) {
231         Attachment attachment = (Attachment) i.next();
232         Map attachmentData = new HashMap();
233         attachmentData.putAll(attachment.getAllAttributes());
234         attachmentData.put("fieldname", attachment.getFieldName());
235         attachmentData.put("filename", attachment.getFileName());
236         result.add(attachmentData);
237       }
238     }
239
240     aResponse.setResponseValue("attachments", result);
241   }
242
243   /**
244    * Process possible changes to previously uploaded files
245    */
246   protected void preprocessPreviousAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
247     synchronized (attachments) {
248       List previouslyUploadedFiles = new ArrayList(attachments);
249       attachments.clear();
250
251       if (persistentUploadedFiles) {
252         Iterator i = previouslyUploadedFiles.iterator();
253         while (i.hasNext()) {
254           Attachment uploadedFile = (Attachment) i.next();
255           if (!(aRequest.getParameter(uploadedFile.getFieldName()+"_cancel")!=null)) {
256             addAttachment(aRequest, aSession, uploadedFile, uploadedFile.getFieldName());
257           }
258         }
259       }
260     }
261   }
262
263   protected void addAttachment(Request aRequest, Session aSession, Attachment anAttachment, String aFieldName) throws SessionExc, SessionFailure {
264     List parameters = aRequest.getPrefixedParameterNames(aFieldName+"_");
265     Iterator j = parameters.iterator();
266     while (j.hasNext()) {
267       String parameter = ((String) j.next());
268       anAttachment.setAttribute(
269           parameter.substring(aFieldName.length()+1),
270           aRequest.getParameter(parameter));
271     }
272     attachments.add(anAttachment);
273   }
274
275   public void preprocessNewAttachment(Request aRequest, Session aSession, UploadedFile aFile) throws SessionExc, SessionFailure {
276       Attachment uploadedFile =
277           new Attachment(aFile, aFile.getFileName(), "attachment"+ ++uploadedFileIndex, aFile.getContentType());
278
279       addAttachment(aRequest, aSession, uploadedFile, aFile.getFieldName());
280   }
281
282
283   /**
284    * Process newly uploaded files
285    */
286   protected void preProcessNewAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
287     Iterator i = aRequest.getUploadedFiles().iterator();
288     while (i.hasNext()) {
289       preprocessNewAttachment(aRequest, aSession, (UploadedFile) i.next());
290     }
291
292     if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
293       File FTPDirectory = configuration.getFile("Localizer.OpenSession.FTPDirectory");
294
295       List ftpUploads = aRequest.getPrefixedParameterNames("ftpupload");
296       i = ftpUploads.iterator();
297       while (i.hasNext()) {
298         final String fieldName = (String) i.next();
299
300         if (fieldName.indexOf("_")<0) {
301           final String fileName = aRequest.getParameter(fieldName);
302
303           if (fileName!=null && fileName.trim().length()>0) {
304             final File sourceFile = new File(FTPDirectory, fileName);
305
306             if (sourceFile.getParentFile().equals(FTPDirectory)) {
307               preprocessNewAttachment(aRequest, aSession, new UploadedFile() {
308                 public void writeToFile(File aFile) throws SessionFailure {
309                   try {
310                     FileFunctions.move(sourceFile, aFile);
311                   }
312                   catch (IOException e) {
313                     throw new SessionFailure(e);
314                   }
315                 }
316
317                 public InputStream getInputStream() throws SessionFailure {
318                   try {
319                     return new FileInputStream(sourceFile);
320                   }
321                   catch (IOException e) {
322                     throw new SessionFailure(e);
323                   }
324                 }
325
326                 public String getFileName() {
327                   return fileName;
328                 }
329
330                 public String getFieldName() {
331                   return fieldName;
332                 }
333
334                 public String getContentType() {
335                   return null;
336                 }
337               });
338             }
339           }
340         }
341       }
342     }
343   }
344
345   protected void makeInitialResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
346     aResponse.setResponseGenerator(normalResponseGenerator);
347   };
348
349   protected void makeResponse(Request aRequest, Session aSession, Response aResponse, List anErrors) throws SessionExc, SessionFailure {
350     aResponse.setResponseValue("errors", anErrors);
351     aResponse.setResponseGenerator(normalResponseGenerator);
352   };
353
354   protected void makeFinalResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
355     aResponse.setResponseGenerator(finalResponseGenerator);
356   };
357
358   protected void makeErrorResponse(Request aRequest, Session aSession, Response aResponse, Throwable anError) throws SessionExc, SessionFailure {
359     Throwable rootCause = ExceptionFunctions.traceCauseException(anError);
360
361     if (rootCause instanceof DuplicatePostingExc)
362       aResponse.setResponseGenerator(dupeResponseGenerator);
363     if (rootCause instanceof UnsupportedMediaTypeExc) {
364       aResponse.setResponseValue("mimetype", ((UnsupportedMediaTypeExc) rootCause).getMimeType());
365       aResponse.setResponseGenerator(unsupportedMediaTypeResponseGenerator);
366     }
367     else {
368       List errors = new ArrayList();
369       errors.add(new ValidationError("", "general.unexpectederror",
370           new Object[] {anError.getMessage()}));
371       makeResponse(aRequest, aSession, aResponse, errors);
372     }
373   };
374
375   protected void makeOpenPostingDisabledResponse(Request aRequest, Session aSession, Response aResponse) {
376     aResponse.setResponseGenerator(configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate"));
377   }
378
379   /**
380    *
381    */
382   protected void preProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
383   };
384   public void processAttachment(Request aRequest, Session aSession, Attachment aFile) throws SessionExc, SessionFailure {
385   };
386   public void processAttachmentError(Request aRequest, Session aSession, Attachment aFile, Throwable anError) {
387   };
388   protected void postProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
389   };
390
391   /**
392    * Determine whether the request shoudl be processed: that is, the validate,
393    * preprocess, postprocess methods should be called to perform validations,
394    * store data, etc
395    */
396   protected boolean shouldProcessRequest(Request aRequest, Session aSession, List aValidationErrors) throws SessionExc, SessionFailure {
397     if (aRequest.getParameter("post")==null)
398       return false;
399     else {
400       validate(aValidationErrors, aRequest, aSession);
401       return (aValidationErrors == null || aValidationErrors.size() == 0);
402     }
403   }
404
405   /**
406    * Method used to validate user input.
407    * Multiple {@link ValidationError}s may be added to the
408    * <code>aResults</code> parameter.
409    * The request is considered validated if, after calling this method,
410    * <code>aResults</code> is empty.
411    */
412   protected void validate(List aResults, Request aRequest, Session aSession) throws SessionExc, SessionFailure {
413     String password = (String) aSession.getAttribute("password");
414
415     if (password!=null) {
416       String submittedPassword= aRequest.getParameter("password").trim();
417
418       if (!password.equals(submittedPassword)) {
419         aResults.add(new ValidationError("password", "passwordmismatch"));
420       }
421     }
422   }
423
424
425   /**
426    * Method to generate a one-time password
427    *
428    * @return a password, to be used once
429    */
430   protected String generateOnetimePassword() {
431     Random r = new Random();
432     int random = r.nextInt();
433
434     long l = System.currentTimeMillis();
435
436     l = (l*l*l*l)/random;
437     if (l<0)
438       l = l * -1;
439
440     String returnString = ""+l;
441
442     return returnString.substring(5);
443   }
444
445   /**
446    * Method to filter the attributes and their values of a request
447    * based on the fields of a storage object.
448    */
449   protected static final Map getIntersectingValues(Request aRequest, StorageObject aStorage) throws SessionFailure {
450     Map result = new HashMap();
451
452     Iterator i = aStorage.getFieldNames().iterator();
453
454     while (i.hasNext()) {
455       String fieldName = (String) i.next();
456       Object value = aRequest.getParameter(fieldName);
457       if (value != null)
458         result.put(fieldName, value);
459     }
460
461     return result;
462   }
463
464   /**
465    * Exception to be thrown when an article or comment was already posted
466    */
467   protected static class DuplicatePostingExc extends SessionExc {
468     public DuplicatePostingExc(String aMessage) {
469       super(aMessage);
470     }
471   }
472
473   /**
474    * A file that has been attached to a session
475    */
476   protected static class Attachment implements UploadedFile {
477     private UploadedFile uploadedFile;
478     private String filename;
479     private String fieldName;
480     private String contentType;
481     private Map attributes;
482
483     public Attachment(UploadedFile anUploadedFile, String aFilename, String aFieldName, String aContentType) {
484       attributes = new HashMap();
485       filename = aFilename;
486       fieldName = aFieldName;
487       contentType = aContentType;
488       uploadedFile = anUploadedFile;
489     }
490
491     public void writeToFile(File aFile) throws SessionExc, SessionFailure {
492       uploadedFile.writeToFile(aFile);
493     }
494
495     public InputStream getInputStream() throws SessionExc, SessionFailure {
496       return uploadedFile.getInputStream();
497     }
498
499     public String getFileName() {
500       return filename;
501     }
502
503     public String getFieldName() {
504       return fieldName;
505     }
506
507     public String getContentType() {
508       return contentType;
509     }
510
511     public String getAttribute(String anAttribute) {
512       return (String) attributes.get(anAttribute);
513     }
514
515     public void setAttribute(String anAttribute, String aValue) {
516       attributes.put(anAttribute, aValue);
517     }
518
519     public Map getAllAttributes() {
520       return Collections.unmodifiableMap(attributes);
521     }
522   }
523 }
524