a small bugfix
[mir.git] / source / Mir.java
1 import freemarker.template.SimpleHash;\r
2 import freemarker.template.SimpleList;\r
3 import freemarker.template.SimpleScalar;\r
4 import freemarker.template.TemplateModel;\r
5 \r
6 import mir.config.MirPropertiesConfiguration;\r
7 \r
8 import mir.generator.FreemarkerGenerator;\r
9 \r
10 import mir.misc.HTMLTemplateProcessor;\r
11 import mir.misc.StringUtil;\r
12 \r
13 import mir.servlet.AbstractServlet;\r
14 import mir.servlet.ServletModule;\r
15 import mir.servlet.ServletModuleDispatch;\r
16 import mir.servlet.ServletModuleException;\r
17 import mir.servlet.ServletModuleUserException;\r
18 \r
19 import mir.util.StringRoutines;\r
20 \r
21 import mircoders.entity.EntityUsers;\r
22 \r
23 import mircoders.global.MirGlobal;\r
24 \r
25 import mircoders.module.ModuleMessage;\r
26 import mircoders.module.ModuleUsers;\r
27 \r
28 import mircoders.storage.DatabaseArticleType;\r
29 import mircoders.storage.DatabaseMessages;\r
30 import mircoders.storage.DatabaseUsers;\r
31 \r
32 import org.apache.struts.util.MessageResources;\r
33 \r
34 /*\r
35  * Copyright (C) 2001, 2002 The Mir-coders group\r
36  *\r
37  * This file is part of Mir.\r
38  *\r
39  * Mir is free software; you can redistribute it and/or modify\r
40  * it under the terms of the GNU General Public License as published by\r
41  * the Free Software Foundation; either version 2 of the License, or\r
42  * (at your option) any later version.\r
43  *\r
44  * Mir is distributed in the hope that it will be useful,\r
45  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
46  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
47  * GNU General Public License for more details.\r
48  *\r
49  * You should have received a copy of the GNU General Public License\r
50  * along with Mir; if not, write to the Free Software\r
51  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
52  *\r
53  * In addition, as a special exception, The Mir-coders gives permission to link\r
54  * the code of this program with the com.oreilly.servlet library, any library\r
55  * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
56  * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
57  * the above that use the same license as the above), and distribute linked\r
58  * combinations including the two.  You must obey the GNU General Public\r
59  * License in all respects for all of the code used other than the above\r
60  * mentioned libraries.  If you modify this file, you may extend this exception\r
61  * to your version of the file, but you are not obligated to do so.  If you do\r
62  * not wish to do so, delete this exception statement from your version.\r
63  */\r
64 import java.io.IOException;\r
65 import java.io.PrintWriter;\r
66 \r
67 import java.lang.reflect.Method;\r
68 \r
69 import java.util.GregorianCalendar;\r
70 import java.util.HashMap;\r
71 import java.util.Iterator;\r
72 import java.util.List;\r
73 import java.util.Locale;\r
74 import java.util.Map;\r
75 import java.util.Vector;\r
76 \r
77 import javax.servlet.ServletException;\r
78 import javax.servlet.UnavailableException;\r
79 import javax.servlet.http.HttpServletRequest;\r
80 import javax.servlet.http.HttpServletResponse;\r
81 import javax.servlet.http.HttpSession;\r
82 \r
83 \r
84 /**\r
85  * Mir.java - main servlet, that dispatches to servletmodules\r
86  *\r
87  * @author $Author: idfx $\r
88  * @version $Id: Mir.java,v 1.28 2003/01/28 23:37:08 idfx Exp $\r
89  *\r
90  */\r
91 public class Mir extends AbstractServlet {\r
92   private static ModuleUsers usersModule = null;\r
93   private static ModuleMessage messageModule = null;\r
94   private final static HashMap servletModuleInstanceHash = new HashMap();\r
95 \r
96   //I don't know about making this static cause it removes the \r
97   //possibility to change the config on the fly.. -mh\r
98   private static List loginLanguages = null;\r
99   public HttpSession session;\r
100 \r
101   public void doGet(HttpServletRequest req, HttpServletResponse res)\r
102     throws ServletException, IOException {\r
103     doPost(req, res);\r
104   }\r
105 \r
106   protected TemplateModel getLoginLanguages() throws ServletException {\r
107     synchronized (Mir.class) {\r
108       try {\r
109         if (loginLanguages == null) {\r
110           MessageResources messageResources2 =\r
111             MessageResources.getMessageResources("bundles.admin");\r
112           MessageResources messageResources =\r
113             MessageResources.getMessageResources("bundles.adminlocal");\r
114           List languages =\r
115             StringRoutines.splitString(MirGlobal.getConfigPropertyWithDefault(\r
116                 "Mir.Login.Languages", "en"), ";");\r
117 \r
118           loginLanguages = new Vector();\r
119 \r
120           Iterator i = languages.iterator();\r
121 \r
122           while (i.hasNext()) {\r
123             String code = (String) i.next();\r
124             Locale locale = new Locale(code, "");\r
125             String name = messageResources.getMessage(locale, "languagename");\r
126 \r
127             if (name == null) {\r
128               name = messageResources2.getMessage(locale, "languagename");\r
129             }\r
130 \r
131             if (name == null) {\r
132               name = code;\r
133             }\r
134 \r
135             Map record = new HashMap();\r
136             record.put("name", name);\r
137             record.put("code", code);\r
138             loginLanguages.add(record);\r
139           }\r
140         }\r
141 \r
142         return FreemarkerGenerator.makeAdapter(loginLanguages);\r
143       } catch (Throwable t) {\r
144         throw new ServletException(t.getMessage());\r
145       }\r
146     }\r
147   }\r
148 \r
149   // FIXME: this should probalby go into AbstractServlet so it can be used in \r
150   // OpenMir as well -mh\r
151   protected String getDefaultLanguage(HttpServletRequest req) {\r
152     String defaultlanguage =\r
153       MirGlobal.getConfigPropertyWithDefault("Mir.Login.DefaultLanguage", "");\r
154 \r
155     if (defaultlanguage.length() == 0) {\r
156       Locale locale = req.getLocale();\r
157       defaultlanguage = locale.getLanguage();\r
158     }\r
159 \r
160     return defaultlanguage;\r
161   }\r
162 \r
163   public void doPost(HttpServletRequest req, HttpServletResponse res)\r
164     throws ServletException, IOException, UnavailableException {\r
165     long startTime = System.currentTimeMillis();\r
166     long sessionConnectTime = 0;\r
167     EntityUsers userEntity;\r
168     String http = "";\r
169 \r
170     if ((configuration.getString("RootUri") == null) ||\r
171         configuration.getString("RootUri").equals("")) {\r
172       configuration.setProperty("RootUri", req.getContextPath());\r
173     }\r
174 \r
175     configuration.addProperty("ServletName", getServletName());\r
176 \r
177     //*** test\r
178     // Log.info(this, "blalalala");\r
179     session = req.getSession(true);\r
180     userEntity = (EntityUsers) session.getAttribute("login.uid");\r
181 \r
182     if (req.getServerPort() == 443) {\r
183       http = "https";\r
184     } else {\r
185       http = "http";\r
186     }\r
187 \r
188     //make sure client browsers don't cache anything\r
189     setNoCaching(res);\r
190 \r
191     //FIXME: this seems kind of hackish and only here because we can have \r
192     // default other than the one that the browser is set to.\r
193     Locale locale = new Locale(getDefaultLanguage(req), "");\r
194     MessageResources messageResources =\r
195       MessageResources.getMessageResources("bundles.admin");\r
196     String htmlcharset = messageResources.getMessage(locale, "htmlcharset");\r
197 \r
198     res.setContentType("text/html; charset=" + htmlcharset);\r
199 \r
200     String moduleName = req.getParameter("module");\r
201     checkLanguage(session, req);\r
202 \r
203     /** @todo for cleanup and readability this should be moved to\r
204      *  method loginIfNecessary() */\r
205     if ((moduleName != null) && moduleName.equals("direct")) {\r
206       //...\r
207     }\r
208 \r
209     // Authentication\r
210     if (((moduleName != null) && moduleName.equals("login")) ||\r
211         (userEntity == null)) {\r
212       String user = req.getParameter("login");\r
213       String passwd = req.getParameter("password");\r
214       logger.debug("--login: evaluating for user: " + user);\r
215       userEntity = allowedUser(user, passwd);\r
216 \r
217       if (userEntity == null) {\r
218         // login failed: redirecting to login\r
219         logger.warn("--login: failed!");\r
220         _sendLoginPage(res, req, res.getWriter());\r
221 \r
222         return;\r
223       } else if ((moduleName != null) && moduleName.equals("login")) {\r
224         // login successful\r
225         logger.info("--login: successful! setting uid: " + userEntity.getId());\r
226         session.setAttribute("login.uid", userEntity);\r
227         logger.debug("--login: trying to retrieve login.target");\r
228 \r
229         String target = (String) session.getAttribute("login.target");\r
230 \r
231         if (target != null) {\r
232           logger.debug("Redirect: " + target);\r
233 \r
234           int serverPort = req.getServerPort();\r
235           String redirect = "";\r
236           String redirectString = "";\r
237 \r
238           if (serverPort == 80) {\r
239             redirect =\r
240               res.encodeURL(http + "://" + req.getServerName() + target);\r
241             redirectString =\r
242               "<html><head><meta http-equiv=refresh content=\"1;URL=" +\r
243               redirect + "\"></head><body>going <a href=\"" + redirect +\r
244               "\">Mir</a></body></html>";\r
245           } else {\r
246             redirect =\r
247               res.encodeURL(http + "://" + req.getServerName() + ":" +\r
248                 req.getServerPort() + target);\r
249             redirectString =\r
250               "<html><head><meta http-equiv=refresh content=\"1;URL=" +\r
251               redirect + "\"></head><body>going <a href=\"" + redirect +\r
252               "\">Mir</a></body></html>";\r
253           }\r
254 \r
255           res.getWriter().println(redirectString);\r
256 \r
257           //res.sendRedirect(redirect);\r
258         } else {\r
259           // redirecting to default target\r
260           logger.debug("--login: no target - redirecting to default");\r
261           _sendStartPage(res, req, res.getWriter(), userEntity);\r
262         }\r
263 \r
264         return;\r
265       }\r
266        // if login succesful\r
267     }\r
268      // if login\r
269 \r
270     if ((moduleName != null) && moduleName.equals("logout")) {\r
271       logger.info("--logout");\r
272       session.invalidate();\r
273 \r
274       //session = req.getSession(true);\r
275       //checkLanguage(session, req);\r
276       _sendLoginPage(res, req, res.getWriter());\r
277 \r
278       return;\r
279     }\r
280 \r
281     // Check if authed!\r
282     if (userEntity == null) {\r
283       // redirect to loginpage\r
284       String redirectString = req.getRequestURI();\r
285       String queryString = req.getQueryString();\r
286 \r
287       if ((queryString != null) && !queryString.equals("")) {\r
288         redirectString += ("?" + req.getQueryString());\r
289         logger.debug("STORING: " + redirectString);\r
290         session.setAttribute("login.target", redirectString);\r
291       }\r
292 \r
293       _sendLoginPage(res, req, res.getWriter());\r
294 \r
295       return;\r
296     }\r
297 \r
298     // If no module is specified goto standard startpage\r
299     if ((moduleName == null) || moduleName.equals("")) {\r
300       logger.debug("no module: redirect to standardpage");\r
301       _sendStartPage(res, req, res.getWriter(), userEntity);\r
302 \r
303       return;\r
304     }\r
305 \r
306     // end of auth\r
307     // From now on regular dispatching...\r
308     try {\r
309       // get servletmodule by parameter and continue with dispacher\r
310       ServletModule smod = getServletModuleForName(moduleName);\r
311       ServletModuleDispatch.dispatch(smod, req, res);\r
312     } catch (ServletModuleException e) {\r
313       handleError(req, res, res.getWriter(),\r
314         "ServletException in Module " + moduleName + " -- " + e.getMessage());\r
315     } catch (ServletModuleUserException e) {\r
316       handleUserError(req, res, res.getWriter(), e.getMessage());\r
317     }\r
318 \r
319     // timing...\r
320     sessionConnectTime = System.currentTimeMillis() - startTime;\r
321     logger.info("EXECTIME (" + moduleName + "): " + sessionConnectTime + " ms");\r
322   }\r
323 \r
324   /**\r
325    *  Private method getServletModuleForName returns ServletModule\r
326    *  from Cache\r
327    *\r
328    * @param moduleName\r
329    * @return ServletModule\r
330    *\r
331    */\r
332   private static ServletModule getServletModuleForName(String moduleName)\r
333     throws ServletModuleException {\r
334     // Instance in Map ?\r
335     if (!servletModuleInstanceHash.containsKey(moduleName)) {\r
336       // was not found in hash...\r
337       try {\r
338         Class theServletModuleClass = null;\r
339 \r
340         try {\r
341           // first we try to get ServletModule from stern.che3.servlet\r
342           theServletModuleClass =\r
343             Class.forName("mircoders.servlet.ServletModule" + moduleName);\r
344         } catch (ClassNotFoundException e) {\r
345           // on failure, we try to get it from lib-layer\r
346           theServletModuleClass =\r
347             Class.forName("mir.servlet.ServletModule" + moduleName);\r
348         }\r
349 \r
350         Method m = theServletModuleClass.getMethod("getInstance", null);\r
351         ServletModule smod = (ServletModule) m.invoke(null, null);\r
352 \r
353         // we put it into map for further reference\r
354         servletModuleInstanceHash.put(moduleName, smod);\r
355 \r
356         return smod;\r
357       } catch (Exception e) {\r
358         throw new ServletModuleException("*** error resolving classname for " +\r
359           moduleName + " -- " + e.getMessage());\r
360       }\r
361     } else {\r
362       return (ServletModule) servletModuleInstanceHash.get(moduleName);\r
363     }\r
364   }\r
365 \r
366   private void handleError(HttpServletRequest req, HttpServletResponse res,\r
367     PrintWriter out, String errorString) {\r
368     try {\r
369       logger.error(errorString);\r
370 \r
371       SimpleHash modelRoot = new SimpleHash();\r
372       modelRoot.put("errorstring", new SimpleScalar(errorString));\r
373       modelRoot.put("date",\r
374         new SimpleScalar(StringUtil.date2readableDateTime(\r
375             new GregorianCalendar())));\r
376       HTMLTemplateProcessor.process(res,\r
377         MirPropertiesConfiguration.instance().getString("Mir.ErrorTemplate"),\r
378         modelRoot, out, getLocale(req));\r
379       out.close();\r
380     } catch (Exception e) {\r
381       e.printStackTrace(System.out);\r
382       System.err.println("Error in ErrorTemplate: " + e.getMessage());\r
383     }\r
384   }\r
385 \r
386   private void handleUserError(HttpServletRequest req, HttpServletResponse res,\r
387     PrintWriter out, String errorString) {\r
388     try {\r
389       logger.error(errorString);\r
390 \r
391       SimpleHash modelRoot = new SimpleHash();\r
392       modelRoot.put("errorstring", new SimpleScalar(errorString));\r
393       modelRoot.put("date",\r
394         new SimpleScalar(StringUtil.date2readableDateTime(\r
395             new GregorianCalendar())));\r
396       HTMLTemplateProcessor.process(res,\r
397         MirPropertiesConfiguration.instance().getString("Mir.UserErrorTemplate"),\r
398         modelRoot, out, getLocale(req));\r
399       out.close();\r
400     } catch (Exception e) {\r
401       System.err.println("Error in UserErrorTemplate");\r
402     }\r
403   }\r
404 \r
405   /**\r
406    *  evaluate login for user / password\r
407    */\r
408   protected EntityUsers allowedUser(String user, String password) {\r
409     try {\r
410       if (usersModule == null) {\r
411         usersModule = new ModuleUsers(DatabaseUsers.getInstance());\r
412       }\r
413 \r
414       return usersModule.getUserForLogin(user, password);\r
415     } catch (Exception e) {\r
416       logger.debug(e.getMessage());\r
417       e.printStackTrace();\r
418 \r
419       return null;\r
420     }\r
421   }\r
422 \r
423   // Redirect-methods\r
424   private void _sendLoginPage(HttpServletResponse res, HttpServletRequest req,\r
425     PrintWriter out) {\r
426     String loginTemplate = configuration.getString("Mir.LoginTemplate");\r
427     String sessionUrl = res.encodeURL("");\r
428 \r
429     try {\r
430       SimpleHash mergeData = new SimpleHash();\r
431       SimpleList languages = new SimpleList();\r
432 \r
433       mergeData.put("session", sessionUrl);\r
434 \r
435       mergeData.put("defaultlanguage", getDefaultLanguage(req));\r
436       mergeData.put("languages", getLoginLanguages());\r
437 \r
438       HTMLTemplateProcessor.process(res, loginTemplate, mergeData, out,\r
439         getLocale(req));\r
440     } catch (Throwable e) {\r
441       handleError(req, res, out, "Error sending login page: " + e.getMessage());\r
442     }\r
443   }\r
444 \r
445   private void _sendStartPage(HttpServletResponse res, HttpServletRequest req,\r
446     PrintWriter out, EntityUsers userEntity) {\r
447     String startTemplate = "templates/admin/start_admin.template";\r
448     String sessionUrl = res.encodeURL("");\r
449 \r
450     try {\r
451       // merge with logged in user and messages\r
452       SimpleHash mergeData = new SimpleHash();\r
453       mergeData.put("session", sessionUrl);\r
454       mergeData.put("login_user", userEntity);\r
455 \r
456       if (messageModule == null) {\r
457         messageModule = new ModuleMessage(DatabaseMessages.getInstance());\r
458       }\r
459 \r
460       mergeData.put("messages",\r
461         messageModule.getByWhereClause(null, "webdb_create desc", 0, 10));\r
462 \r
463       mergeData.put("articletypes",\r
464         DatabaseArticleType.getInstance().selectByWhereClause("", "id", 0, 20));\r
465 \r
466       HTMLTemplateProcessor.process(res, startTemplate, mergeData, out,\r
467         getLocale(req));\r
468     } catch (Exception e) {\r
469       e.printStackTrace(System.out);\r
470       handleError(req, res, out,\r
471         "error while trying to send startpage. " + e.getMessage());\r
472     }\r
473   }\r
474 \r
475   public String getServletInfo() {\r
476     return "Mir " + configuration.getString("Mir.Version");\r
477   }\r
478 \r
479   private void checkLanguage(HttpSession session, HttpServletRequest req) {\r
480     // a lang parameter always sets the language\r
481     String lang = req.getParameter("language");\r
482 \r
483     if (lang != null) {\r
484       logger.info("selected language " + lang + " overrides accept-language");\r
485       setLanguage(session, lang);\r
486       setLocale(session, new Locale(lang, ""));\r
487     }\r
488     // otherwise store language from accept header in session\r
489     else if (session.getAttribute("Language") == null) {\r
490       logger.info("accept-language is " + req.getLocale().getLanguage());\r
491       setLanguage(session, req.getLocale().getLanguage());\r
492       setLocale(session, req.getLocale());\r
493     }\r
494   }\r
495 }\r