cf010d0eb1661201eb395fb094e688253d4d87f3
[mir.git] / source / mircoders / servlet / ServletModuleOpenIndy.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.f
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
31 package mircoders.servlet;
32
33 import gnu.regexp.RE;
34 import gnu.regexp.REMatch;
35 import mir.bundle.Bundle;
36 import mir.entity.Entity;
37 import mir.generator.Generator;
38 import mir.log.LoggerWrapper;
39 import mir.misc.StringUtil;
40 import mir.servlet.ServletModule;
41 import mir.servlet.ServletModuleExc;
42 import mir.servlet.ServletModuleFailure;
43 import mir.servlet.ServletModuleUserExc;
44 import mir.session.*;
45 import mir.storage.StorageObjectFailure;
46 import mir.util.*;
47 import mircoders.entity.EntityComment;
48 import mircoders.entity.EntityContent;
49 import mircoders.global.CacheKey;
50 import mircoders.global.MirGlobal;
51 import mircoders.media.MediaUploadProcessor;
52 import mircoders.module.ModuleComment;
53 import mircoders.module.ModuleContent;
54 import mircoders.module.ModuleMediaType;
55 import mircoders.pdf.PDFGenerator;
56 import mircoders.search.*;
57 import mircoders.storage.*;
58 import org.apache.commons.fileupload.FileItem;
59 import org.apache.commons.net.smtp.SMTPClient;
60 import org.apache.commons.net.smtp.SMTPReply;
61 import org.apache.lucene.analysis.standard.StandardAnalyzer;
62 import org.apache.lucene.document.Document;
63 import org.apache.lucene.index.IndexReader;
64 import org.apache.lucene.queryParser.QueryParser;
65 import org.apache.lucene.search.Hits;
66 import org.apache.lucene.search.IndexSearcher;
67 import org.apache.lucene.search.Query;
68 import org.apache.lucene.search.Searcher;
69
70 import javax.servlet.http.HttpServletRequest;
71 import javax.servlet.http.HttpServletResponse;
72 import javax.servlet.http.HttpSession;
73 import java.io.*;
74 import java.util.*;
75
76 /*
77  *  ServletModuleOpenIndy -
78  *   is the open-access-servlet, which is responsible for
79  *    adding comments to articles &
80  *    open-postings to the newswire
81  *
82  * @author mir-coders group
83  *
84  */
85
86 public class ServletModuleOpenIndy extends ServletModule
87 {
88
89   private String        commentFormTemplate, commentFormDoneTemplate, commentFormDupeTemplate;
90   private String        postingFormTemplate, postingFormDoneTemplate, postingFormDupeTemplate;
91   private String        searchResultsTemplate;
92   private String        prepareMailTemplate,sentMailTemplate,emailAnArticleTemplate;
93   private ModuleContent contentModule;
94   private ModuleComment commentModule;
95   private String        directOp ="yes";
96   private static ServletModuleOpenIndy instance = new ServletModuleOpenIndy();
97
98   public  static ServletModule getInstance() {
99     return instance;
100   }
101
102   private ServletModuleOpenIndy() {
103     super();
104     try {
105       logger = new LoggerWrapper("ServletModule.OpenIndy");
106
107       commentFormTemplate = configuration.getString("ServletModule.OpenIndy.CommentTemplate");
108       commentFormDoneTemplate = configuration.getString("ServletModule.OpenIndy.CommentDoneTemplate");
109       commentFormDupeTemplate = configuration.getString("ServletModule.OpenIndy.CommentDupeTemplate");
110
111       postingFormTemplate = configuration.getString("ServletModule.OpenIndy.PostingTemplate");
112       postingFormDoneTemplate = configuration.getString("ServletModule.OpenIndy.PostingDoneTemplate");
113       postingFormDupeTemplate = configuration.getString("ServletModule.OpenIndy.PostingDupeTemplate");
114
115       searchResultsTemplate = configuration.getString("ServletModule.OpenIndy.SearchResultsTemplate");
116       prepareMailTemplate = configuration.getString("ServletModule.OpenIndy.PrepareMailTemplate");
117       emailAnArticleTemplate = configuration.getString("ServletModule.OpenIndy.MailableArticleTemplate");
118       sentMailTemplate = configuration.getString("ServletModule.OpenIndy.SentMailTemplate");
119       directOp = configuration.getString("DirectOpenposting").toLowerCase();
120       commentModule = new ModuleComment();
121       mainModule = commentModule;
122       contentModule = new ModuleContent();
123       defaultAction = "defaultAction";
124     }
125     catch (StorageObjectFailure e) {
126       logger.error("servletmoduleopenindy could not be initialized: " + e.getMessage());
127     }
128   }
129
130   /**
131    * Perform the default open posting action
132    */
133   public void defaultAction(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
134     opensession(aRequest, aResponse);
135   }
136
137   /**
138    * Method to return an out of service notice when open postings are disabled
139    *
140    * @param aRequest
141    * @param aResponse
142    * @throws ServletModuleExc
143    * @throws ServletModuleUserExc
144    * @throws ServletModuleFailure
145    */
146   public void openPostingDisabled(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
147     deliver(aRequest, aResponse, null, null, configuration.getString("ServletModule.OpenIndy.PostingDisabledTemplate"));
148   }
149
150   /**
151    *  Method for making a comment
152    */
153   public void addcomment(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
154     if (MirGlobal.abuse().getOpenPostingDisabled()) {
155       openPostingDisabled(req, res);
156
157       return;
158     }
159
160     String aid = req.getParameter("aid"); // the article id the comment will belong to
161
162     if (aid != null && !aid.equals("")) {
163       try {
164         Map mergeData = new HashMap();
165
166         // onetimepasswd
167         if (MirGlobal.abuse().getOpenPostingPassword()) {
168           String passwd = generateOnetimePassword();
169           HttpSession session = req.getSession(false);
170           session.setAttribute("passwd", passwd);
171           mergeData.put("passwd", passwd);
172         }
173         else {
174           mergeData.put("passwd", null);
175         }
176         mergeData.put("aid", aid);
177
178         Map extraInfo = new HashMap();
179         extraInfo.put("languagePopUpData", DatabaseLanguage.getInstance().getPopupData());
180
181         deliver(req, res, mergeData, extraInfo, commentFormTemplate);
182       }
183       catch (Throwable t) {
184         throw new ServletModuleFailure("ServletModuleOpenIndy.addcomment: " + t.getMessage(), t);
185       }
186     }
187     else
188       throw new ServletModuleExc("aid not set!");
189   }
190
191   /**
192    *  Method for inserting a comment into the Database and delivering
193    *  the commentDone Page
194    */
195
196   public void inscomment(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
197     if (MirGlobal.abuse().getOpenPostingDisabled()) {
198       openPostingDisabled(req, res);
199
200       return;
201     }
202
203     String aid = req.getParameter("to_media"); // the article id the comment will belong to
204     if (aid != null && !aid.equals("")) {
205       // ok, collecting data from form
206       try {
207         Map withValues = getIntersectingValues(req, DatabaseComment.getInstance());
208
209         //no html in comments(for now)
210         for (Iterator i = withValues.keySet().iterator(); i.hasNext(); ) {
211           String k = (String) i.next();
212           String v = (String) withValues.get(k);
213
214           withValues.put(k, StringUtil.removeHTMLTags(v));
215         }
216         withValues.put("is_published", "1");
217         withValues.put("to_comment_status", "1");
218         withValues.put("is_html", "0");
219
220         //checking the onetimepasswd
221         HttpSession session = req.getSession(false);
222         String sessionPasswd = (String) session.getAttribute("passwd");
223         if (sessionPasswd != null) {
224           String passwd = req.getParameter("passwd");
225           if (passwd == null || passwd.length() == 0) {
226             throw new ServletModuleUserExc("comment.error.missingpassword", new String[] {});
227           }
228           if (!sessionPasswd.equals(passwd)) {
229             throw new ServletModuleUserExc("comment.error.invalidpassword", new String[] {});
230           }
231           session.invalidate();
232         }
233
234         String id = mainModule.add(withValues);
235
236         SimpleResponse response = new SimpleResponse();
237         response.setResponseGenerator(commentFormDoneTemplate);
238
239         if (id == null) {
240           deliver(req, res, null, null, commentFormDupeTemplate);
241         }
242         else {
243           DatabaseContent.getInstance().setUnproduced("id=" + aid);
244
245           try {
246             EntityComment comment = (EntityComment) DatabaseComment.getInstance().selectById(id);
247             MirGlobal.localizer().openPostings().afterCommentPosting(comment);
248             MirGlobal.abuse().checkComment(
249                 comment, new HTTPAdapters.HTTPRequestAdapter(req), res);
250           }
251           catch (Throwable t) {
252             throw new ServletModuleExc(t.getMessage());
253           }
254         }
255
256         // redirecting to url
257         // should implement back to article
258         deliver(req, res, response.getResponseValues(), null, response.getResponseGenerator());
259       }
260       catch (Throwable e) {
261         throw new ServletModuleFailure(e);
262       }
263     }
264     else
265       throw new ServletModuleExc("aid not set!");
266
267   }
268
269   /**
270    *  Method for delivering the form-Page for open posting
271    */
272
273   public void addposting(HttpServletRequest req, HttpServletResponse res)
274       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
275     try {
276       if (MirGlobal.abuse().getOpenPostingDisabled()) {
277         openPostingDisabled(req, res);
278
279         return;
280       }
281
282       Map mergeData = new HashMap();
283
284       // onetimepasswd
285       if (MirGlobal.abuse().getOpenPostingPassword()) {
286         String passwd = generateOnetimePassword();
287         HttpSession session = req.getSession(false);
288         session.setAttribute("passwd", passwd);
289         mergeData.put("passwd", passwd);
290       }
291       else {
292         mergeData.put("passwd", null);
293       }
294
295       String maxMedia = configuration.getString("ServletModule.OpenIndy.MaxMediaUploadItems");
296       String defaultMedia = configuration.getString("ServletModule.OpenIndy.DefaultMediaUploadItems");
297       String numOfMedia = req.getParameter("medianum");
298
299       if (numOfMedia == null || numOfMedia.equals("")) {
300         numOfMedia = defaultMedia;
301       }
302       else if (Integer.parseInt(numOfMedia) > Integer.parseInt(maxMedia)) {
303         numOfMedia = maxMedia;
304       }
305
306       int mediaNum = Integer.parseInt(numOfMedia);
307       List mediaFields = new Vector();
308       for (int i = 0; i < mediaNum; i++) {
309         Integer mNum = new Integer(i + 1);
310         mediaFields.add(mNum.toString());
311       }
312       mergeData.put("medianum", numOfMedia);
313       mergeData.put("mediafields", mediaFields);
314       mergeData.put("to_topic", null);
315
316       Map extraInfo = new HashMap();
317       extraInfo.put("languagePopUpData", DatabaseLanguage.getInstance().getPopupData());
318       extraInfo.put("themenPopupData", DatabaseTopics.getInstance().getPopupData());
319
320       deliver(req, res, mergeData, extraInfo, postingFormTemplate);
321     }
322     catch (Throwable t) {
323       throw new ServletModuleFailure(t);
324     }
325   }
326
327   /**
328    *  Method for inserting an open posting into the Database and delivering
329    *  the postingDone Page
330    */
331
332   public void insposting(HttpServletRequest aRequest, HttpServletResponse aResponse) throws
333       ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
334     if (MirGlobal.abuse().getOpenPostingDisabled()) {
335       openPostingDisabled(aRequest, aResponse);
336
337       return;
338     }
339
340     try {
341       HTTPParsedRequest parsedRequest = new HTTPParsedRequest(
342           aRequest,
343           configuration.getString("Mir.DefaultEncoding"),
344           configuration.getInt("MaxMediaUploadSize")*1024,
345           configuration.getString("TempDir"));
346
347       Map mergeData = new HashMap();
348
349       HttpSession session = aRequest.getSession(false);
350       String sessionPasswd = (String) session.getAttribute("passwd");
351       if (sessionPasswd != null) {
352         String passwd = parsedRequest.getParameter("passwd");
353
354         if (passwd == null || passwd.length() == 0) {
355           throw new ServletModuleUserExc("posting.error.missingpassword", new String[] {});
356         }
357         if (!sessionPasswd.equals(passwd)) {
358           throw new ServletModuleUserExc("posting.error.invalidpassword", new String[] {});
359         }
360         session.invalidate();
361       }
362
363       if (((parsedRequest.getParameter("title")).length() == 0) ||
364           ((parsedRequest.getParameter("description")).length() == 0) ||
365           ((parsedRequest.getParameter("content_data")).length() == 0))
366         throw new ServletModuleUserExc("posting.error.missingfield", new String[] {});
367
368       List mediaList = new Vector();
369       Iterator i = parsedRequest.getFiles().iterator();
370
371       while (i.hasNext()) {
372         UploadedFile file = new mir.session.CommonsUploadedFileAdapter((FileItem) i.next());
373         Map mediaValues = new HashMap();
374
375         String suffix = file.getFieldName().substring(5); // media${m}
376         logger.debug("media_title" + suffix);
377         String title = parsedRequest.getParameter("media_title" + suffix);
378
379         mediaValues.put("title", StringUtil.removeHTMLTags(title));
380         mediaValues.put("creator", StringUtil.removeHTMLTags(parsedRequest.getParameter("creator")));
381         mediaValues.put("to_publisher", "0");
382         mediaValues.put("is_published", "1");
383         mediaValues.put("to_media_folder", "7");
384
385         mediaList.add(MediaUploadProcessor.processMediaUpload(file, mediaValues));
386       }
387
388       Map withValues = new HashMap();
389       i = DatabaseContent.getInstance().getFields().iterator();
390       while (i.hasNext()) {
391         String field = (String) i.next();
392         String value = parsedRequest.getParameter(field);
393         if (value!=null)
394           withValues.put(field, value);
395       }
396
397
398       for (i = withValues.keySet().iterator(); i.hasNext(); ) {
399         String k = (String) i.next();
400         String v = (String) withValues.get(k);
401
402         if (k.equals("content_data")) {
403           //this doesn't quite work yet, so for now, all html goes
404           //withValues.put(k,StringUtil.approveHTMLTags(v));
405           withValues.put(k, StringUtil.deleteForbiddenTags(v));
406         }
407         else if (k.equals("description")) {
408           String tmp = StringUtil.deleteForbiddenTags(v);
409           withValues.put(k, StringUtil.deleteHTMLTableTags(tmp));
410         }
411         else {
412           withValues.put(k, StringUtil.removeHTMLTags(v));
413         }
414       }
415
416       withValues.put("date", StringUtil.date2webdbDate(new GregorianCalendar()));
417       withValues.put("publish_path",
418                      StringUtil.webdbDate2path( (String) withValues.get("date")));
419       withValues.put("is_produced", "0");
420       withValues.put("is_published", "1");
421       if (directOp.equals("yes"))
422         withValues.put("to_article_type", "1");
423
424       withValues.put("to_publisher", "1");
425
426       // inserting  content into database
427       String cid = contentModule.add(withValues);
428       logger.debug("id: " + cid);
429       //insert was not successfull
430       if (cid == null) {
431         deliver(aRequest, aResponse, mergeData, null, postingFormDupeTemplate);
432         return;
433       }
434
435       List topics = parsedRequest.getParameterList("to_topic");
436       if (topics.size() > 0) {
437         try {
438           DatabaseContentToTopics.getInstance().setTopics(cid, topics);
439         }
440         catch (Throwable e) {
441           logger.error("setting content_x_topic failed");
442           contentModule.deleteById(cid);
443           throw new ServletModuleFailure(
444               "smod - openindy :: insposting: setting content_x_topic failed: " +
445               e.toString(), e);
446         }
447       }
448
449       i = mediaList.iterator();
450       while (i.hasNext()) {
451         Entity mediaEnt = (Entity) i.next();
452         DatabaseContentToMedia.getInstance().addMedia(cid, mediaEnt.getId());
453       }
454
455       EntityContent article = (EntityContent) contentModule.getById(cid);
456       try {
457         MirGlobal.abuse().checkArticle(
458             article, new HTTPAdapters.HTTPRequestAdapter(aRequest), aResponse);
459         MirGlobal.localizer().openPostings().afterContentPosting(article);
460       }
461       catch (Throwable t) {
462         logger.error("Error while post-processing article: " + t.getMessage());
463       }
464       deliver(aRequest, aResponse, mergeData, null, postingFormDoneTemplate);
465     }
466     catch (Throwable e) {
467       e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
468       Throwable cause = ExceptionFunctions.traceCauseException(e);
469
470       if (cause instanceof ModuleMediaType.UnsupportedMimeTypeExc) {
471         throw new ServletModuleUserExc("media.unsupportedformat", new String[] {});
472       }
473       throw new ServletModuleFailure(e);
474     }
475   }
476
477   /**
478    * Due to a serious shortcoming of Tomcat 3.3, an extra sessionid parameter is
479    *   generated into open session urls. Tomcat 3.3 makes it impossible to
480    *   distinguish between sessions that are identified using a url and those
481    *   that are identified using cookies: if both a sessionid cookie and a sessionid
482    *   url are available, tomcat 3.3 pretends the url wasn't there...
483    */
484   private static final String SESSION_REQUEST_KEY="sessionid";
485
486   /**
487    * Determines the Locale to be used for the current session
488    */
489   protected Locale getResponseLocale(HttpSession aSession, HttpServletRequest aRequest) {
490     String requestLanguage = aRequest.getParameter("language");
491     String sessionLanguage = (String) aSession.getAttribute("language");
492     String acceptLanguage = aRequest.getLocale().getLanguage();
493     String defaultLanguage = configuration.getString("Mir.Login.DefaultLanguage", "en");
494
495     String language = requestLanguage;
496
497     if (language==null)
498       language = sessionLanguage;
499
500     if (language==null)
501       language = acceptLanguage;
502
503     if (language==null)
504       language = defaultLanguage;
505
506     aSession.setAttribute("language", language);
507
508     return new Locale(language, "");
509   }
510
511   /**
512    * Dispatch method for open sessions: a flexible extensible and customizable way
513    *   for open access. Can be used for postings, but also for lots of other stuff.
514    *
515    * @param aRequest
516    * @param aResponse
517    * @throws ServletModuleExc
518    * @throws ServletModuleUserExc
519    * @throws ServletModuleFailure
520    */
521   public void opensession(HttpServletRequest aRequest, HttpServletResponse aResponse)
522       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
523
524     try {
525       Request request =
526           new HTTPAdapters.HTTPParsedRequestAdapter(new HTTPParsedRequest(aRequest,
527               configuration.getString("Mir.DefaultEncoding"),
528               configuration.getInt("MaxMediaUploadSize")*1024,
529               configuration.getString("TempDir")));
530
531       if (aRequest.isRequestedSessionIdValid() && !aRequest.isRequestedSessionIdFromURL() &&
532           !aRequest.getRequestedSessionId().equals(aRequest.getParameter(SESSION_REQUEST_KEY)))
533         aRequest.getSession().invalidate();
534
535       Session session = new HTTPAdapters.HTTPSessionAdapter(aRequest.getSession());
536
537       SimpleResponse response = new SimpleResponse(
538           ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] { getResponseLocale(aRequest.getSession(), aRequest), getFallbackLocale(aRequest)},
539              "etc/bundles/open"));
540
541       response.setResponseValue("actionURL", aResponse.encodeURL(MirGlobal.config().getString("RootUri") + "/servlet/OpenMir")+"?"+SESSION_REQUEST_KEY+"="+aRequest.getSession().getId());
542
543       SessionHandler handler = MirGlobal.localizer().openPostings().getOpenSessionHandler(request, session);
544
545       handler.processRequest(request, session, response);
546       ServletHelper.generateOpenPostingResponse(aResponse.getWriter(), response.getResponseValues(), response.getResponseGenerator());
547     }
548     catch (Throwable t) {
549       logger.error(t.toString());
550       t.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
551
552       throw new ServletModuleFailure(t);
553     }
554   }
555
556   /**
557    * Method for preparing and sending a content as an email message
558    */
559   public void mail(HttpServletRequest req, HttpServletResponse res)
560       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure
561   {
562     String aid = req.getParameter("mail_aid");
563     if (aid == null){
564       throw new ServletModuleExc("An article id must be specified in requests to email an article.  Something therefore went badly wrong....");
565     }
566
567     String to = req.getParameter("mail_to");
568     String from = req.getParameter("mail_from");
569     String from_name = req.getParameter("mail_from_name");
570     String from_ip = req.getRemoteAddr();
571     String comment = req.getParameter("mail_comment");
572     String mail_language = req.getParameter("mail_language");
573
574     Map mergeData = new HashMap();
575     mergeData.put("mail_to",to);
576     mergeData.put("mail_from",from);
577     mergeData.put("mail_from_name",from_name);
578     mergeData.put("mail_comment",comment);
579     mergeData.put("mail_aid",aid);
580     mergeData.put("mail_language",mail_language);
581
582
583     if (to == null || from == null || from_name == null|| to.equals("") || from.equals("") || from_name.equals("") || mail_language == null || mail_language.equals("")){
584       deliver(req, res, mergeData, null, prepareMailTemplate);
585     }
586     else {
587       //run checks on to and from and mail_language to make sure no monkey business occurring
588       if (mail_language.indexOf('.') != -1 || mail_language.indexOf('/') != -1 ) {
589         throw new ServletModuleExc("Invalid language");
590       }
591       if (to.indexOf('\n') != -1
592           || to.indexOf('\r') != -1
593           || to.indexOf(',') != -1) {
594         throw new ServletModuleUserExc("email.error.invalidtoaddress", new String[] {to});
595       }
596       if (from.indexOf('\n') != -1 || from.indexOf('\r') != -1 || from.indexOf(',') != -1 ) {
597         throw new ServletModuleUserExc("email.error.invalidfromaddress", new String[] {from});
598       }
599
600       CacheKey theCacheKey=new CacheKey("email",aid+mail_language);
601       String theEmailText;
602
603       if (MirGlobal.mruCache().hasObject(theCacheKey)){
604         logger.info("fetching email text for article "+aid+" from cache");
605         theEmailText = (String) MirGlobal.mruCache().getObject(theCacheKey);
606       }
607       else {
608         EntityContent contentEnt;
609         try {
610           contentEnt = (EntityContent) contentModule.getById(aid);
611           StringWriter theEMailTextWriter = new StringWriter();
612           PrintWriter dest = new PrintWriter(theEMailTextWriter);
613           Map articleData = new HashMap();
614           articleData.put("article", MirGlobal.localizer().dataModel().adapterModel().makeEntityAdapter("content", contentEnt));
615           articleData.put("languagecode", mail_language);
616           deliver(dest, req, res, articleData, null, emailAnArticleTemplate, mail_language);
617           theEmailText = theEMailTextWriter.toString();
618           MirGlobal.mruCache().storeObject(theCacheKey, theEmailText);
619         }
620         catch (Throwable e) {
621           throw new ServletModuleFailure("Couldn't get content for article " + aid + mail_language + ": " + e.getMessage(), e);
622         }
623       }
624
625       String content = theEmailText;
626
627
628       // add some headers
629       content = "To: " + to + "\nReply-To: "+ from + "\nX-Originating-IP: "+ from_ip + "\n" + content;
630       // put in the comment where it should go
631       if (comment != null) {
632         String commentTextToInsert = "\n\nAttached comment from " + from_name + ":\n" + comment;
633         try {
634           content=StringRoutines.performRegularExpressionReplacement(content,"!COMMENT!",commentTextToInsert);
635         }
636         catch (Throwable e){
637           throw new ServletModuleFailure("Problem doing regular expression replacement " + e.toString(), e);
638         }
639       }
640       else{
641         try {
642           content=StringRoutines.performRegularExpressionReplacement(content,"!COMMENT!","");
643         }
644         catch (Throwable e){
645           throw new ServletModuleFailure("Problem doing regular expression replacement " + e.toString(), e);
646         }
647       }
648
649       SMTPClient client=new SMTPClient();
650       try {
651         int reply;
652         client.connect(configuration.getString("ServletModule.OpenIndy.SMTPServer"));
653
654         reply = client.getReplyCode();
655
656         if (!SMTPReply.isPositiveCompletion(reply)) {
657           client.disconnect();
658           throw new ServletModuleExc("SMTP server refused connection.");
659         }
660
661         client.sendSimpleMessage(configuration.getString("ServletModule.OpenIndy.EmailIsFrom"), to, content);
662
663         client.disconnect();
664         //mission accomplished
665         deliver(req, res, mergeData, null, sentMailTemplate);
666       }
667       catch(IOException e) {
668         if(client.isConnected()) {
669           try {
670             client.disconnect();
671           } catch(IOException f) {
672             // do nothing
673           }
674         }
675         throw new ServletModuleFailure(e);
676       }
677     }
678   }
679
680
681
682   /**
683    * Method for querying a lucene index
684    *
685    * @param req
686    * @param res
687    * @throws ServletModuleExc
688    * @throws ServletModuleUserExc
689    * @throws ServletModuleFailure
690    */
691
692   public void search(HttpServletRequest req, HttpServletResponse res) throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
693     try {
694       final String[] search_variables = {
695           "search_content", "search_boolean", "search_creator",
696           "search_topic", "search_hasImages", "search_hasAudio", "search_hasVideo", "search_sort",
697           "search_submit", "search_back", "search_forward"};
698       HTTPRequestParser requestParser = new HTTPRequestParser(req);
699
700       int increment = 10;
701
702       HttpSession session = req.getSession(false);
703
704       String queryString = "";
705
706       Map mergeData = new HashMap();
707
708       KeywordSearchTerm dateTerm = new KeywordSearchTerm("date_formatted", "search_date", "webdb_create_formatted", "webdb_create_formatted", "webdb_create_formatted");
709       UnIndexedSearchTerm whereTerm = new UnIndexedSearchTerm("", "", "", "where", "where");
710       TextSearchTerm creatorTerm = new TextSearchTerm("creator", "search_creator", "creator", "creator", "creator");
711       TextSearchTerm titleTerm = new TextSearchTerm("title", "search_content", "title", "title", "title");
712       TextSearchTerm descriptionTerm = new TextSearchTerm("description", "search_content", "description", "description", "description");
713       ContentSearchTerm contentTerm = new ContentSearchTerm("content_data", "search_content", "content", "", "");
714       TopicSearchTerm topicTerm = new TopicSearchTerm();
715       TopicMatrixSearchTerm topicMatrixTerm = new TopicMatrixSearchTerm();
716       ImagesSearchTerm imagesTerm = new ImagesSearchTerm();
717       AudioSearchTerm audioTerm = new AudioSearchTerm();
718       VideoSearchTerm videoTerm = new VideoSearchTerm();
719
720       //make the query available to subsequent iterations
721
722       Iterator j = Arrays.asList(search_variables).iterator();
723       while (j.hasNext()) {
724         String variable = (String) j.next();
725
726         mergeData.put(variable, requestParser.getParameter(variable));
727       }
728
729       try {
730         mergeData.put("topics", DatabaseTopics.getInstance().getPopupData());
731       }
732       catch (Throwable e) {
733         logger.debug("Can't get topics: " + e.toString());
734       }
735
736       String searchBackValue = req.getParameter("search_back");
737       String searchForwardValue = req.getParameter("search_forward");
738
739       if (searchBackValue != null) {
740         int totalHits = ( (Integer) session.getAttribute("numberOfHits")).intValue();
741         int newPosition = ( (Integer) session.getAttribute("positionInResults")).intValue() - increment;
742         if (newPosition < 0)
743           newPosition = 0;
744         if (newPosition >= totalHits)
745           newPosition = totalHits - 1;
746         session.setAttribute("positionInResults", new Integer(newPosition));
747       }
748       else {
749         if (searchForwardValue != null) {
750           int totalHits = ( (Integer) session.getAttribute("numberOfHits")).intValue();
751           int newPosition = ( (Integer) session.getAttribute("positionInResults")).intValue() + increment;
752           if (newPosition < 0)
753             newPosition = 0;
754           if (newPosition >= totalHits)
755             newPosition = totalHits - 1;
756
757           session.setAttribute("positionInResults", new Integer(newPosition));
758         }
759         else {
760           File indexFile = FileFunctions.getAbsoluteOrRelativeFile(configuration.getHome(), configuration.getString("IndexPath"));
761
762           String creatorFragment = creatorTerm.makeTerm(req);
763           if (creatorFragment != null) {
764             queryString = queryString + " +" + creatorFragment;
765           }
766
767           // search title, description, and content for something
768           // the contentTerm uses param "search_boolean" to combine its terms
769           String contentFragment = contentTerm.makeTerm(req);
770           if (contentFragment != null) {
771             logger.debug("contentFragment: " + contentFragment);
772             queryString = queryString + " +" + contentFragment;
773           }
774
775           String topicFragment = topicTerm.makeTerm(req);
776           if (topicFragment != null) {
777             queryString = queryString + " +" + topicFragment;
778           }
779
780           String topicMatrixFragment = topicMatrixTerm.makeTerm(req);
781           if (topicMatrixFragment != null) {
782             queryString = queryString + " +" + topicMatrixFragment;
783           }
784
785           String imagesFragment = imagesTerm.makeTerm(req);
786           if (imagesFragment != null) {
787             queryString = queryString + " +" + imagesFragment;
788           }
789
790           String audioFragment = audioTerm.makeTerm(req);
791           if (audioFragment != null) {
792             queryString = queryString + " +" + audioFragment;
793           }
794
795           String videoFragment = videoTerm.makeTerm(req);
796           if (videoFragment != null) {
797             queryString = queryString + " +" + videoFragment;
798           }
799
800           if (queryString == null || queryString.length()==0) {
801             queryString = "";
802           }
803           else {
804             try {
805               Searcher searcher = null;
806               try {
807                 searcher = new IndexSearcher(IndexReader.open(indexFile));
808               }
809               catch (IOException e) {
810                 logger.debug("Can't open indexPath: " + indexFile.getAbsolutePath());
811                 throw new ServletModuleExc("Problem with Search Index! : " + e.toString());
812               }
813
814               Query query = null;
815               try {
816                 query = QueryParser.parse(queryString, "content", new StandardAnalyzer());
817               }
818               catch (Exception e) {
819                 searcher.close();
820                 logger.debug("Query don't parse: " + queryString);
821                 throw new ServletModuleExc("Problem with Query String! (was '" + queryString + "')");
822               }
823
824               Hits hits = null;
825               try {
826                 hits = searcher.search(query);
827               }
828               catch (IOException e) {
829                 searcher.close();
830                 logger.debug("Can't get hits: " + e.toString());
831                 throw new ServletModuleExc("Problem getting hits!");
832               }
833
834               int start = 0;
835               int end = hits.length();
836
837               String sortBy = req.getParameter("search_sort");
838               if (sortBy == null || sortBy.equals("")) {
839                 throw new ServletModuleExc("Please let me sort by something!(missing search_sort)");
840               }
841
842               // here is where the documents will go for storage across sessions
843               ArrayList theDocumentsSorted = new ArrayList();
844
845               if (sortBy.equals("score")) {
846                 for (int i = start; i < end; i++) {
847                   theDocumentsSorted.add(hits.doc(i));
848                 }
849               }
850               else {
851                 // then we'll sort by date!
852                 Map dateToPosition = new HashMap(end, 1.0F); //we know how big it will be
853                 for (int i = start; i < end; i++) {
854                   String creationDate = (hits.doc(i)).get("creationDate");
855                   // do a little dance in case two contents created at the same second!
856                   if (dateToPosition.containsKey(creationDate)) {
857                     ( (ArrayList) (dateToPosition.get(creationDate))).add(new Integer(i));
858                   }
859                   else {
860                     ArrayList thePositions = new ArrayList();
861                     thePositions.add(new Integer(i));
862                     dateToPosition.put(creationDate, thePositions);
863                   }
864                 }
865                 Set keys = dateToPosition.keySet();
866                 ArrayList keyList = new ArrayList(keys);
867                 Collections.sort(keyList);
868                 if (sortBy.equals("date_desc")) {
869                   Collections.reverse(keyList);
870                 }
871                 else {
872                   if (!sortBy.equals("date_asc")) {
873                     throw new ServletModuleExc("don't know how to sort by: " + sortBy);
874                   }
875                 }
876                 ListIterator keyTraverser = keyList.listIterator();
877                 while (keyTraverser.hasNext()) {
878                   ArrayList positions = (ArrayList) dateToPosition.get( (keyTraverser.next()));
879                   ListIterator positionsTraverser = positions.listIterator();
880                   while (positionsTraverser.hasNext()) {
881                     theDocumentsSorted.add(hits.doc( ( (Integer) (positionsTraverser.next())).intValue()));
882                   }
883                 }
884               }
885
886               try {
887                 searcher.close();
888               }
889               catch (IOException e) {
890                 logger.debug("Can't close searcher: " + e.toString());
891                 throw new ServletModuleFailure("Problem closing searcher(normal):" + e.getMessage(), e);
892               }
893
894               session.removeAttribute("numberOfHits");
895               session.removeAttribute("theDocumentsSorted");
896               session.removeAttribute("positionInResults");
897
898               session.setAttribute("numberOfHits", new Integer(end));
899               session.setAttribute("theDocumentsSorted", theDocumentsSorted);
900               session.setAttribute("positionInResults", new Integer(0));
901
902             }
903             catch (IOException e) {
904               logger.debug("Can't close searcher: " + e.toString());
905               throw new ServletModuleFailure("Problem closing searcher: " + e.getMessage(), e);
906             }
907           }
908         }
909       }
910
911       try {
912         ArrayList theDocs = (ArrayList) session.getAttribute("theDocumentsSorted");
913         if (theDocs != null) {
914
915           mergeData.put("numberOfHits", (session.getAttribute("numberOfHits")).toString());
916           List theHits = new Vector();
917           int pIR = ( (Integer) session.getAttribute("positionInResults")).intValue();
918           int terminus;
919           int numHits = ( (Integer) session.getAttribute("numberOfHits")).intValue();
920
921           if (! (pIR + increment >= numHits)) {
922             mergeData.put("hasNext", "y");
923           }
924           else {
925             mergeData.put("hasNext", null);
926           }
927           if (pIR > 0) {
928             mergeData.put("hasPrevious", "y");
929           }
930           else {
931             mergeData.put("hasPrevious", null);
932           }
933
934           if ( (pIR + increment) > numHits) {
935             terminus = numHits;
936           }
937           else {
938             terminus = pIR + increment;
939           }
940           for (int i = pIR; i < terminus; i++) {
941             Map h = new HashMap();
942             Document theHit = (Document) theDocs.get(i);
943             whereTerm.returnMeta(h, theHit);
944             creatorTerm.returnMeta(h, theHit);
945             titleTerm.returnMeta(h, theHit);
946             descriptionTerm.returnMeta(h, theHit);
947             dateTerm.returnMeta(h, theHit);
948             imagesTerm.returnMeta(h, theHit);
949             audioTerm.returnMeta(h, theHit);
950             videoTerm.returnMeta(h, theHit);
951             theHits.add(h);
952           }
953           mergeData.put("hits", theHits);
954         }
955       }
956       catch (Throwable e) {
957         logger.error("Can't iterate over hits: " + e.toString());
958
959         throw new ServletModuleFailure("Problem getting hits: " + e.getMessage(), e);
960       }
961
962       mergeData.put("queryString", queryString);
963
964       deliver(req, res, mergeData, null, searchResultsTemplate);
965     }
966     catch (NullPointerException n) {
967       throw new ServletModuleFailure("Null Pointer: " + n.toString(), n);
968     }
969   }
970
971   /*
972    * Method for dynamically generating a pdf using iText
973    */
974
975   public void getpdf(HttpServletRequest req, HttpServletResponse res)
976       throws ServletModuleExc, ServletModuleUserExc, ServletModuleFailure {
977     long starttime = System.currentTimeMillis();
978     String ID_REQUEST_PARAM = "id";
979     int maxArticlesInNewsleter = 15; // it is nice not to be dos'ed
980     try {
981       String idParam = req.getParameter(ID_REQUEST_PARAM);
982       if (idParam != null) {
983
984         RE re = new RE("[0-9]+");
985
986         REMatch[] idMatches = re.getAllMatches(idParam);
987
988         String cacheSelector = "";
989
990         for (int i = 0; i < idMatches.length; i++) {
991           cacheSelector = cacheSelector + "," + idMatches[i].toString();
992         }
993
994         String cacheType = "pdf";
995
996         CacheKey theCacheKey = new CacheKey(cacheType, cacheSelector);
997
998         byte[] thePDF;
999
1000         if (MirGlobal.mruCache().hasObject(theCacheKey)) {
1001           logger.info("fetching pdf from cache");
1002           thePDF = (byte[]) MirGlobal.mruCache().getObject(theCacheKey);
1003         }
1004         else {
1005           logger.info("generating pdf and caching it");
1006           ByteArrayOutputStream out = new ByteArrayOutputStream();
1007           PDFGenerator pdfMaker = new PDFGenerator(out);
1008
1009           if (idMatches.length > 1) {
1010             pdfMaker.addLine();
1011             for (int i = 0; i < idMatches.length && i < maxArticlesInNewsleter; i++) {
1012               REMatch aMatch = idMatches[i];
1013               String id = aMatch.toString();
1014               EntityContent contentEnt = (EntityContent) contentModule.getById(id);
1015               pdfMaker.addIndexItem(contentEnt);
1016             }
1017           }
1018
1019           for (int i = 0; i < idMatches.length; i++) {
1020             REMatch aMatch = idMatches[i];
1021             String id = aMatch.toString();
1022             EntityContent contentEnt = (EntityContent) contentModule.getById(id);
1023
1024             pdfMaker.add(contentEnt);
1025           }
1026
1027           pdfMaker.stop();
1028           thePDF = out.toByteArray();
1029
1030           //and save all our hard work!
1031           MirGlobal.mruCache().storeObject(theCacheKey, thePDF);
1032         }
1033
1034         res.setContentType("application/pdf");
1035         res.setContentLength(thePDF.length);
1036         res.getOutputStream().write(thePDF);
1037         res.getOutputStream().flush();
1038         String elapsedtime = (new Long(System.currentTimeMillis() - starttime)).toString();
1039         logger.info("pdf retireval took " + elapsedtime + " milliseconds");
1040
1041       }
1042       else {
1043         throw new ServletModuleExc("Missing id.");
1044       }
1045     }
1046     catch (Throwable t) {
1047       logger.error(t.toString());
1048       throw new ServletModuleFailure(t);
1049     }
1050   }
1051
1052
1053   public String generateOnetimePassword() {
1054     Random r = new Random();
1055     int random = r.nextInt();
1056
1057     long l = System.currentTimeMillis();
1058
1059     l = (l * l * l * l) / random;
1060     if (l < 0)
1061       l = l * -1;
1062
1063     String returnString = "" + l;
1064
1065     return returnString.substring(5);
1066   }
1067
1068   public void deliver(HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator) throws ServletModuleFailure {
1069     try {
1070       deliver(aResponse.getWriter(), aRequest, aResponse, aData, anExtra, aGenerator);
1071     }
1072     catch (Throwable t) {
1073       throw new ServletModuleFailure(t);
1074     }
1075   }
1076
1077   public void deliver(PrintWriter anOutputWriter, HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator)
1078       throws ServletModuleFailure {
1079     try {
1080       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] { getLocale(aRequest), getFallbackLocale(aRequest)}, "etc/bundles/open");
1081       responseData.put("data", aData);
1082       responseData.put("extra", anExtra);
1083
1084
1085       Generator generator = MirGlobal.localizer().generators().makeOpenPostingGeneratorLibrary().makeGenerator(aGenerator);
1086       generator.generate(anOutputWriter, responseData, logger);
1087
1088       anOutputWriter.close();
1089     }
1090     catch (Throwable e) {
1091       logger.error("Error while generating " + aGenerator + ": " + e.getMessage());
1092
1093       throw new ServletModuleFailure(e);
1094     }
1095   }
1096
1097   public void deliver(PrintWriter anOutputWriter, HttpServletRequest aRequest, HttpServletResponse aResponse, Map aData, Map anExtra, String aGenerator,String aLocaleString)
1098       throws ServletModuleFailure {
1099     try {
1100       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] { new Locale(aLocaleString,""), getFallbackLocale(aRequest)}, "etc/bundles/open");
1101       responseData.put("data", aData);
1102       responseData.put("extra", anExtra);
1103
1104
1105       Generator generator = MirGlobal.localizer().generators().makeOpenPostingGeneratorLibrary().makeGenerator(aGenerator);
1106       generator.generate(anOutputWriter, responseData, logger);
1107
1108       anOutputWriter.close();
1109     }
1110     catch (Throwable e) {
1111       logger.error("Error while generating " + aGenerator + ": " + e.getMessage());
1112
1113       throw new ServletModuleFailure(e);
1114     }
1115   }
1116
1117
1118   public void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse,PrintWriter out, Throwable anException) {
1119     try {
1120       logger.error("error: " + anException);
1121       Map data = new HashMap();
1122
1123       data.put("errorstring", anException.getMessage());
1124       data.put("date", StringUtil.date2readableDateTime(new GregorianCalendar()));
1125
1126       deliver(out, aRequest, aResponse, data, null, configuration.getString("ServletModule.OpenIndy.ErrorTemplate"));
1127     }
1128     catch (Throwable e) {
1129       throw new ServletModuleFailure(e);
1130     }
1131   }
1132
1133   public void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse,
1134                                PrintWriter out, ServletModuleUserExc anException) {
1135     try {
1136       logger.warn("user error: " + anException.getMessage());
1137       Map data = new HashMap();
1138
1139       Bundle bundle =
1140           MirGlobal.getBundleFactory().getBundle("etc/bundles/open", new
1141               String[] { getLocale(aRequest).getLanguage() });
1142       data.put("errorstring", bundle.getValue(anException.getMessage(), Arrays.asList(anException.getParameters())));
1143       data.put("date", StringUtil.date2readableDateTime(new GregorianCalendar()));
1144
1145       deliver(out, aRequest, aResponse, data, null, configuration.getString("ServletModule.OpenIndy.UserErrorTemplate"));
1146     }
1147     catch (Throwable e) {
1148       throw new ServletModuleFailure(e);
1149     }
1150   }
1151 }