Organizing import, refresh the license (zapata deleted cos.jar)
[mir.git] / source / Mir.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
31 import java.io.IOException;
32 import java.io.PrintWriter;
33 import java.lang.reflect.Method;
34 import java.util.GregorianCalendar;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Locale;
39 import java.util.Map;
40 import java.util.Vector;
41
42 import javax.servlet.ServletException;
43 import javax.servlet.UnavailableException;
44 import javax.servlet.http.HttpServletRequest;
45 import javax.servlet.http.HttpServletResponse;
46 import javax.servlet.http.HttpSession;
47
48 import mir.config.MirPropertiesConfiguration;
49 import mir.entity.adapter.EntityIteratorAdapter;
50 import mir.generator.FreemarkerGenerator;
51 import mir.log.LoggerWrapper;
52 import mir.misc.HTMLTemplateProcessor;
53 import mir.misc.StringUtil;
54 import mir.servlet.AbstractServlet;
55 import mir.servlet.ServletModule;
56 import mir.servlet.ServletModuleDispatch;
57 import mir.servlet.ServletModuleExc;
58 import mir.servlet.ServletModuleUserExc;
59 import mir.util.CachingRewindableIterator;
60 import mir.util.ExceptionFunctions;
61 import mir.util.StringRoutines;
62 import mircoders.entity.EntityUsers;
63 import mircoders.global.MirGlobal;
64 import mircoders.module.ModuleMessage;
65 import mircoders.module.ModuleUsers;
66 import mircoders.servlet.ServletHelper;
67 import mircoders.storage.DatabaseUsers;
68
69 import org.apache.struts.util.MessageResources;
70
71 import freemarker.template.SimpleHash;
72 import freemarker.template.SimpleList;
73 import freemarker.template.SimpleScalar;
74 import freemarker.template.TemplateModel;
75
76
77
78
79 /**
80  * Mir.java - main servlet, that dispatches to servletmodules
81  *
82  * @author $Author: idfx $
83  * @version $Id: Mir.java,v 1.45 2003/04/21 12:42:46 idfx Exp $
84  *
85  */
86 public class Mir extends AbstractServlet {
87   private static ModuleUsers usersModule = null;
88   private static ModuleMessage messageModule = null;
89   private final static Map servletModuleInstanceHash = new HashMap();
90   private static Locale fallbackLocale = null;
91
92   //I don't know about making this static cause it removes the
93   //possibility to change the config on the fly.. -mh
94   private static List loginLanguages = null;
95
96   protected TemplateModel getLoginLanguages() throws ServletException {
97     synchronized (Mir.class) {
98       try {
99         if (loginLanguages == null) {
100           MessageResources messageResources =
101             MessageResources.getMessageResources("bundles.adminlocal");
102           MessageResources messageResources2 =
103             MessageResources.getMessageResources("bundles.admin");
104
105           List languages =
106             StringRoutines.splitString(MirGlobal.config().getString("Mir.Login.Languages", "en"), ";");
107
108           loginLanguages = new Vector();
109
110           Iterator i = languages.iterator();
111
112           while (i.hasNext()) {
113             String code = (String) i.next();
114             Locale locale = new Locale(code, "");
115             String name = messageResources.getMessage(locale, "languagename");
116
117             if (name == null) {
118               name = messageResources2.getMessage(locale, "languagename");
119             }
120
121             if (name == null) {
122               name = code;
123             }
124
125             Map record = new HashMap();
126             record.put("name", name);
127             record.put("code", code);
128             loginLanguages.add(record);
129           }
130         }
131
132         return FreemarkerGenerator.makeAdapter(loginLanguages);
133       }
134       catch (Throwable t) {
135         throw new ServletException(t.getMessage());
136       }
137     }
138   }
139
140   // FIXME: this should probalby go into AbstractServlet so it can be used in
141   // OpenMir as well -mh
142   protected String getDefaultLanguage(HttpServletRequest aRequest) {
143     String defaultlanguage =
144       MirGlobal.config().getString("Mir.Login.DefaultLanguage", "");
145
146     if (defaultlanguage.length() == 0) {
147       Locale locale = aRequest.getLocale();
148       defaultlanguage = locale.getLanguage();
149     }
150
151     return defaultlanguage;
152   }
153
154   protected synchronized Locale getFallbackLocale() throws ServletException {
155     try {
156       if (fallbackLocale == null) {
157         fallbackLocale = new Locale(MirPropertiesConfiguration.instance().getString("Mir.Admin.FallbackLanguage", "en"), "");
158       }
159     }
160     catch (Throwable t) {
161       throw new ServletException(t.getMessage());
162     }
163
164     return fallbackLocale;
165   }
166
167   public void process(HttpServletRequest aRequest, HttpServletResponse aResponse)
168     throws ServletException, IOException, UnavailableException {
169     long startTime = System.currentTimeMillis();
170     long sessionConnectTime = 0;
171     EntityUsers userEntity;
172     HttpSession session;
173     String http = "";
174
175     configuration.addProperty("ServletName", getServletName());
176
177     session = aRequest.getSession(true);
178     userEntity = (EntityUsers) session.getAttribute("login.uid");
179
180     if (aRequest.getServerPort() == 443) {
181       http = "https";
182     } else {
183       http = "http";
184     }
185
186     //make sure client browsers don't cache anything
187     setNoCaching(aResponse);
188
189     //FIXME: this seems kind of hackish and only here because we can have
190     // default other than the one that the browser is set to.
191     Locale locale = new Locale(getDefaultLanguage(aRequest), "");
192     String htmlcharset = "UTF-8";
193     try {
194       htmlcharset = MirPropertiesConfiguration.instance().getString("Mir.DefaultHTMLCharset");
195     }
196     catch (Throwable t) {
197     }
198
199     aResponse.setContentType("text/html; charset=" + htmlcharset);
200
201     String moduleName = aRequest.getParameter("module");
202     checkLanguage(session, aRequest);
203
204
205
206     // Authentication
207     if (((moduleName != null) && moduleName.equals("login")) ||
208         (userEntity == null)) {
209       String user = aRequest.getParameter("login");
210       String passwd = aRequest.getParameter("password");
211       logger.debug("--login: evaluating for user: " + user);
212       userEntity = allowedUser(user, passwd);
213
214       if (userEntity == null) {
215         // login failed: redirecting to login
216         logger.warn("--login: failed!");
217         _sendLoginPage(aResponse, aRequest, aResponse.getWriter());
218
219         return;
220       }
221       else if ((moduleName != null) && moduleName.equals("login")) {
222         // login successful
223         logger.info("--login: successful! setting uid: " + userEntity.getId());
224         session.setAttribute("login.uid", userEntity);
225         logger.debug("--login: trying to retrieve login.target");
226
227         String target = (String) session.getAttribute("login.target");
228
229         if (target != null) {
230           logger.debug("Redirect: " + target);
231
232           int serverPort = aRequest.getServerPort();
233           String redirect = "";
234           String redirectString = "";
235
236           if (serverPort == 80) {
237             redirect =
238               aResponse.encodeURL(http + "://" + aRequest.getServerName() + target);
239             redirectString =
240               "<html><head><meta http-equiv=refresh content=\"1;URL=" +
241               redirect + "\"></head><body>going <a href=\"" + redirect +
242               "\">Mir</a></body></html>";
243           } else {
244             redirect =
245               aResponse.encodeURL(http + "://" + aRequest.getServerName() + ":" +
246                 aRequest.getServerPort() + target);
247             redirectString =
248               "<html><head><meta http-equiv=refresh content=\"1;URL=" +
249               redirect + "\"></head><body>going <a href=\"" + redirect +
250               "\">Mir</a></body></html>";
251           }
252
253           aResponse.getWriter().println(redirectString);
254
255           //aResponse.sendRedirect(redirect);
256         } else {
257           // redirecting to default target
258           logger.debug("--login: no target - redirecting to default");
259           _sendStartPage(aResponse, aRequest, aResponse.getWriter(), userEntity);
260         }
261
262         return;
263       }
264        // if login succesful
265     }
266      // if login
267
268     if ((moduleName != null) && moduleName.equals("logout")) {
269       logger.info("--logout");
270       session.invalidate();
271
272       //session = aRequest.getSession(true);
273       //checkLanguage(session, aRequest);
274       _sendLoginPage(aResponse, aRequest, aResponse.getWriter());
275
276       return;
277     }
278
279     // Check if authed!
280     if (userEntity == null) {
281       // redirect to loginpage
282       String redirectString = aRequest.getRequestURI();
283       String queryString = aRequest.getQueryString();
284
285       if ((queryString != null) && !queryString.equals("")) {
286         redirectString += ("?" + aRequest.getQueryString());
287         logger.debug("STORING: " + redirectString);
288         session.setAttribute("login.target", redirectString);
289       }
290
291       _sendLoginPage(aResponse, aRequest, aResponse.getWriter());
292
293       return;
294     }
295
296     // If no module is specified goto standard startpage
297     if ((moduleName == null) || moduleName.equals("")) {
298       logger.debug("no module: redirect to standardpage");
299       _sendStartPage(aResponse, aRequest, aResponse.getWriter(), userEntity);
300
301       return;
302     }
303
304     // end of auth
305     // From now on regular dispatching...
306     try {
307       // get servletmodule by parameter and continue with dispacher
308       ServletModule smod = getServletModuleForName(moduleName);
309       ServletModuleDispatch.dispatch(smod, aRequest, aResponse);
310     }
311     catch (Throwable e) {
312       Throwable cause = ExceptionFunctions.traceCauseException(e);
313
314       if (cause instanceof ServletModuleUserExc)
315         handleUserError(aRequest, aResponse, aResponse.getWriter(), (ServletModuleUserExc) cause);
316       else
317         handleError(aRequest, aResponse, aResponse.getWriter(), cause);
318
319     }
320
321     // timing...
322     sessionConnectTime = System.currentTimeMillis() - startTime;
323     logger.info("EXECTIME (" + moduleName + "): " + sessionConnectTime + " ms");
324   }
325
326   /**
327    *  Private method getServletModuleForName returns ServletModule
328    *  from Cache
329    *
330    * @param moduleName
331    * @return ServletModule
332    *
333    */
334   private static ServletModule getServletModuleForName(String moduleName) throws ServletModuleExc {
335     // Instance in Map ?
336     if (!servletModuleInstanceHash.containsKey(moduleName)) {
337       // was not found in hash...
338       try {
339         Class theServletModuleClass = null;
340
341         try {
342           // first we try to get ServletModule from stern.che3.servlet
343           theServletModuleClass =
344             Class.forName("mircoders.servlet.ServletModule" + moduleName);
345         } catch (ClassNotFoundException e) {
346           // on failure, we try to get it from lib-layer
347           theServletModuleClass =
348             Class.forName("mir.servlet.ServletModule" + moduleName);
349         }
350
351         Method m = theServletModuleClass.getMethod("getInstance", null);
352         ServletModule smod = (ServletModule) m.invoke(null, null);
353
354         // we put it into map for further reference
355         servletModuleInstanceHash.put(moduleName, smod);
356
357         return smod;
358       }
359       catch (Exception e) {
360         throw new ServletModuleExc("*** error resolving classname for " + moduleName + " -- " + e.getMessage());
361       }
362     }
363     else {
364       return (ServletModule) servletModuleInstanceHash.get(moduleName);
365     }
366   }
367
368   private void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse,
369                                PrintWriter out, ServletModuleUserExc anException) {
370     try {
371       logger.info("user error: " + anException.getMessage());
372       SimpleHash modelRoot = new SimpleHash();
373       MessageResources messages = MessageResources.getMessageResources("bundles.admin");
374       modelRoot.put("errorstring",
375           new SimpleScalar(
376               messages.getMessage(getLocale(aRequest), anException.getMessage(), anException.getParameters())
377           ));
378       modelRoot.put("date", new SimpleScalar(StringUtil.date2readableDateTime(new GregorianCalendar())));
379       HTMLTemplateProcessor.process(
380           aResponse,
381           MirPropertiesConfiguration.instance().getString("Mir.UserErrorTemplate"),
382           modelRoot,
383           null,
384           out,
385           getLocale(aRequest),
386           fallbackLocale);
387       out.close();
388     }
389     catch (Exception e) {
390       logger.error("Error in UserErrorTemplate");
391     }
392
393   }
394
395   private void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse,PrintWriter out, Throwable anException) {
396
397     try {
398       logger.error("error: " + anException);
399       SimpleHash modelRoot = new SimpleHash();
400       modelRoot.put("errorstring", new SimpleScalar(anException.getMessage()));
401       modelRoot.put("date", new SimpleScalar(StringUtil.date2readableDateTime(
402                                                new GregorianCalendar())));
403       HTMLTemplateProcessor.process(
404           aResponse,MirPropertiesConfiguration.instance().getString("Mir.ErrorTemplate"),
405           modelRoot,null,out, getLocale(aRequest), getFallbackLocale());
406       out.close();
407     }
408     catch (Exception e) {
409       logger.error("Error in ErrorTemplate");
410     }
411   }
412
413   /**
414    *  evaluate login for user / password
415    */
416   protected EntityUsers allowedUser(String user, String password) {
417     try {
418       if (usersModule == null) {
419         usersModule = new ModuleUsers(DatabaseUsers.getInstance());
420       }
421
422       return usersModule.getUserForLogin(user, password);
423     }
424     catch (Exception e) {
425       logger.debug(e.getMessage());
426       e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
427
428       return null;
429     }
430   }
431
432   // Redirect-methods
433   private void _sendLoginPage(HttpServletResponse aResponse, HttpServletRequest aRequest,
434     PrintWriter out) {
435     String loginTemplate = configuration.getString("Mir.LoginTemplate");
436     String sessionUrl = aResponse.encodeURL("");
437
438     try {
439       SimpleHash mergeData = new SimpleHash();
440       SimpleList languages = new SimpleList();
441
442       mergeData.put("session", sessionUrl);
443
444       mergeData.put("defaultlanguage", getDefaultLanguage(aRequest));
445       mergeData.put("languages", getLoginLanguages());
446
447       HTMLTemplateProcessor.process(aResponse, loginTemplate, mergeData, null, out, getLocale(aRequest), getFallbackLocale());
448     }
449     catch (Throwable e) {
450       handleError(aRequest, aResponse, out, e);
451     }
452   }
453
454   private void _sendStartPage(HttpServletResponse aResponse, HttpServletRequest aRequest,
455     PrintWriter out, EntityUsers userEntity) {
456     String startTemplate = configuration.getString("Mir.StartTemplate");
457     String sessionUrl = aResponse.encodeURL("");
458
459     try {
460       Map mergeData = ServletHelper.makeGenerationData(new Locale[] {getLocale(aRequest), getFallbackLocale()}, "bundles.admin", "bundles.adminlocal");
461       mergeData.put("messages",
462              new CachingRewindableIterator(
463                new EntityIteratorAdapter( "", "webdb_create desc", 10,
464                  MirGlobal.localizer().dataModel().adapterModel(), "internalMessage", 10, 0)));
465       mergeData.put("searchvalue", null);
466       mergeData.put("searchfield", null);
467       mergeData.put("searchispublished", null);
468       mergeData.put("searcharticletype", null);
469       mergeData.put("searchorder", null);
470       mergeData.put("selectarticleurl", null);
471
472       ServletHelper.generateResponse(aResponse.getWriter(), mergeData, startTemplate);
473     }
474     catch (Exception e) {
475       e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
476       handleError(aRequest, aResponse, out, e);
477     }
478   }
479
480   public String getServletInfo() {
481     return "Mir " + configuration.getString("Mir.Version");
482   }
483
484   private void checkLanguage(HttpSession session, HttpServletRequest aRequest) {
485     // a lang parameter always sets the language
486     String lang = aRequest.getParameter("language");
487
488     if (lang != null) {
489       logger.info("selected language " + lang + " overrides accept-language");
490       setLanguage(session, lang);
491     }
492     // otherwise store language from accept header in session
493     else if (session.getAttribute("language") == null) {
494       logger.info("accept-language is " + aRequest.getLocale().getLanguage());
495       setLanguage(session, aRequest.getLocale().getLanguage());
496     }
497   }
498 }