31822f50faf87d22c1ff4280eca4a1f1759d3e4e
[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.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
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         logger.error("Error while processing attachment", t);
119       }
120     }
121   }
122
123   public void subsequentRequest(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
124     try {
125       try {
126         List validationErrors = new ArrayList();
127
128         preprocessPreviousAttachments(aRequest, aSession);
129         preProcessNewAttachments(aRequest, aSession);
130
131         if (!shouldProcessRequest(aRequest, aSession, validationErrors)) {
132           initializeResponseData(aRequest, aSession, aResponse);
133           makeResponse(aRequest, aSession, aResponse, validationErrors);
134         }
135         else {
136           preProcessRequest(aRequest, aSession);
137
138           processAttachments(aRequest, aSession, aResponse);
139           postProcessRequest(aRequest, aSession);
140           initializeResponseData(aRequest, aSession, aResponse);
141           makeFinalResponse(aRequest, aSession, aResponse);
142           aSession.terminate();
143         }
144       }
145       catch (Throwable t) {
146         initializeResponseData(aRequest, aSession, aResponse);
147         makeErrorResponse(aRequest, aSession, aResponse, t);
148         aSession.terminate();
149       }
150     }
151     catch (Throwable t) {
152       aSession.terminate();
153
154       throw new SessionFailure(t);
155     }
156   }
157
158   /**
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.
162    */
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);
169       }
170     }
171     else {
172       aSession.deleteAttribute("password");
173     }
174
175     aSession.setAttribute("referer", aRequest.getHeader("Referer"));
176   }
177
178   /**
179    * Called every time a response is being prepared.
180    * This may be at the initial response, or on a subsequent one.
181    */
182   protected void initializeResponseData(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
183     int nrMediaItems = configuration.getInt("ServletModule.OpenIndy.DefaultMediaUploadItems", 5);
184
185     if (aSession.getAttribute("nrmediaitems")!=null) {
186       nrMediaItems = ((Integer) aSession.getAttribute("nrmediaitems")).intValue();
187     }
188
189     try {
190       nrMediaItems = Math.min(configuration.getInt("ServletModule.OpenIndy.MaxMediaUploadItems"), Integer.parseInt(aRequest.getParameter("nrmediaitems")));
191     }
192     catch (Throwable t) {
193     }
194
195     aSession.setAttribute("nrmediaitems", new Integer(nrMediaItems));
196
197     List mediaItems = new ArrayList();
198     for (int i=1; i<=nrMediaItems; i++) {
199       mediaItems.add(new Integer(i));
200     }
201
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);
207
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());
214                 }
215           }));
216     }
217     else {
218       aResponse.setResponseValue("ftpfiles", null);
219     }
220
221     initializeAttachmentResponseData(aRequest, aSession, aResponse);
222   }
223
224   /**
225    * Process possible changes to previously uploaded files
226    */
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);
238       }
239     }
240
241     aResponse.setResponseValue("attachments", result);
242   }
243
244   /**
245    * Process possible changes to previously uploaded files
246    */
247   protected void preprocessPreviousAttachments(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
248     synchronized (attachments) {
249       List previouslyUploadedFiles = new ArrayList(attachments);
250       attachments.clear();
251
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());
258           }
259         }
260       }
261     }
262   }
263
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));
272     }
273     attachments.add(anAttachment);
274   }
275
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());
279
280       addAttachment(aRequest, aSession, uploadedFile, aFile.getFieldName());
281   }
282
283
284   /**
285    * Process newly uploaded files
286    */
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());
291     }
292
293     if (configuration.getBoolean("Localizer.OpenSession.AllowFTPUploads", false)) {
294       File FTPDirectory = configuration.getFile("Localizer.OpenSession.FTPDirectory");
295
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);
301           }
302           else {
303             return 0;
304           }
305         }
306       });
307
308       i = ftpUploads.iterator();
309       while (i.hasNext()) {
310         final String fieldName = (String) i.next();
311
312         if (fieldName.indexOf("_")<0) {
313           final String fileName = aRequest.getParameter(fieldName);
314
315           if (fileName!=null && fileName.trim().length()>0) {
316             final File sourceFile = new File(FTPDirectory, fileName);
317
318             if (sourceFile.getParentFile().equals(FTPDirectory)) {
319               preprocessNewAttachment(aRequest, aSession, new UploadedFile() {
320                 public void writeToFile(File aFile) throws SessionFailure {
321                   try {
322                     FileRoutines.move(sourceFile, aFile);
323                   }
324                   catch (IOException e) {
325                     throw new SessionFailure(e);
326                   }
327                 }
328
329                 public void writeToStream(OutputStream aStream) throws SessionFailure {
330                   try {
331                     IORoutines.copyStream(getInputStream(), aStream);
332                   }
333                   catch (IOException e) {
334                     throw new SessionFailure(e);
335                   }
336                 }
337
338                 public InputStream getInputStream() throws SessionFailure {
339                   try {
340                     return new FileInputStream(sourceFile);
341                   }
342                   catch (IOException e) {
343                     throw new SessionFailure(e);
344                   }
345                 }
346
347                 public String getFileName() {
348                   return fileName;
349                 }
350
351                 public String getFieldName() {
352                   return fieldName;
353                 }
354
355                 public String getContentType() {
356                   return null;
357                 }
358               });
359             }
360           }
361         }
362       }
363     }
364   }
365
366   protected void makeInitialResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
367     aResponse.setResponseGenerator(normalResponseGenerator);
368   }
369
370   protected void makeResponse(Request aRequest, Session aSession, Response aResponse, List anErrors) throws SessionExc, SessionFailure {
371     aResponse.setResponseValue("errors", anErrors);
372     aResponse.setResponseGenerator(normalResponseGenerator);
373   }
374
375   protected void makeFinalResponse(Request aRequest, Session aSession, Response aResponse) throws SessionExc, SessionFailure {
376     aResponse.setResponseGenerator(finalResponseGenerator);
377   }
378
379   protected void makeErrorResponse(Request aRequest, Session aSession, Response aResponse, Throwable anError) throws SessionExc, SessionFailure {
380     Throwable rootCause = ExceptionRoutines.traceCauseException(anError);
381
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);
387     }
388     else {
389       List errors = new ArrayList();
390       errors.add(new ValidationError("", "general.unexpectederror",
391           new Object[] {anError.getMessage()}));
392       makeResponse(aRequest, aSession, aResponse, errors);
393     }
394   }
395
396   protected void makeOpenPostingDisabledResponse(Request aRequest, Session aSession, Response aResponse) {
397     aResponse.setResponseGenerator(configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate"));
398   }
399
400   /**
401    *
402    */
403   protected void preProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
404   }
405   public void processAttachment(Request aRequest, Session aSession, Attachment aFile) throws SessionExc, SessionFailure {
406   }
407   public void processAttachmentError(Request aRequest, Session aSession, Attachment aFile, Throwable anError) {
408   }
409   protected void postProcessRequest(Request aRequest, Session aSession) throws SessionExc, SessionFailure {
410   }
411
412   /**
413    * Determine whether the request shoudl be processed: that is, the validate,
414    * preprocess, postprocess methods should be called to perform validations,
415    * store data, etc
416    */
417   protected boolean shouldProcessRequest(Request aRequest, Session aSession, List aValidationErrors) throws SessionExc, SessionFailure {
418     if (aRequest.getParameter("post")==null)
419       return false;
420                 validate(aValidationErrors, aRequest, aSession);
421                 return (aValidationErrors == null || aValidationErrors.size() == 0);
422   }
423
424   /**
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.
430    */
431   protected void validate(List aResults, Request aRequest, Session aSession) throws SessionExc, SessionFailure {
432     String password = (String) aSession.getAttribute("password");
433
434     if (password!=null) {
435       String submittedPassword= aRequest.getParameter("password").trim();
436
437       if (!password.equals(submittedPassword)) {
438         aResults.add(new ValidationError("password", "passwordmismatch"));
439       }
440     }
441   }
442
443
444   /**
445    * Method to generate a one-time password
446    *
447    * @return a password, to be used once
448    */
449   protected String generateOnetimePassword() {
450     Random r = new Random();
451     int random = r.nextInt();
452
453     long l = System.currentTimeMillis();
454
455     l = (l*l*l*l)/random;
456     if (l<0)
457       l = l * -1;
458
459     String returnString = ""+l;
460
461     return returnString.substring(5);
462   }
463
464   /**
465    * Method to filter the attributes and their values of a request
466    * based on the fields of a database object.
467    */
468   protected static final Map getIntersectingValues(Request aRequest, Database aStorage) throws SessionFailure {
469     Map result = new HashMap();
470
471     Iterator i = aStorage.getFieldNames().iterator();
472
473     while (i.hasNext()) {
474       String fieldName = (String) i.next();
475       Object value = aRequest.getParameter(fieldName);
476       if (value != null)
477         result.put(fieldName, value);
478     }
479
480     return result;
481   }
482
483   /**
484    * Exception to be thrown when an article or comment was already posted
485    */
486   protected static class DuplicatePostingExc extends SessionExc {
487     public DuplicatePostingExc(String aMessage) {
488       super(aMessage);
489     }
490   }
491
492   /**
493    * A file that has been attached to a session
494    */
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;
501
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;
508     }
509
510     public void writeToFile(File aFile) throws SessionExc, SessionFailure {
511       uploadedFile.writeToFile(aFile);
512     }
513
514     public void writeToStream(OutputStream aStream) throws SessionExc, SessionFailure {
515       try {
516         IORoutines.copyStream(uploadedFile.getInputStream(), aStream);
517       }
518       catch (IOException e) {
519         throw new SessionFailure(e);
520       }
521     }
522
523     public InputStream getInputStream() throws SessionExc, SessionFailure {
524       return uploadedFile.getInputStream();
525     }
526
527     public String getFileName() {
528       return filename;
529     }
530
531     public String getFieldName() {
532       return fieldName;
533     }
534
535     public String getContentType() {
536       return contentType;
537     }
538
539     public String getAttribute(String anAttribute) {
540       return (String) attributes.get(anAttribute);
541     }
542
543     public void setAttribute(String anAttribute, String aValue) {
544       attributes.put(anAttribute, aValue);
545     }
546
547     public Map getAllAttributes() {
548       return Collections.unmodifiableMap(attributes);
549     }
550   }
551 }
552