restriction of user activity: concept of super-users: the only ones that can add...
[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 import javax.servlet.ServletException;
42 import javax.servlet.UnavailableException;
43 import javax.servlet.http.HttpServletRequest;
44 import javax.servlet.http.HttpServletResponse;
45 import javax.servlet.http.HttpSession;
46
47 import org.apache.struts.util.MessageResources;
48 import freemarker.template.SimpleHash;
49 import freemarker.template.SimpleList;
50 import freemarker.template.SimpleScalar;
51 import freemarker.template.TemplateModel;
52
53 import mir.config.MirPropertiesConfiguration;
54 import mir.entity.adapter.EntityIteratorAdapter;
55 import mir.generator.FreemarkerGenerator;
56 import mir.log.LoggerWrapper;
57 import mir.misc.HTMLTemplateProcessor;
58 import mir.misc.StringUtil;
59 import mir.servlet.AbstractServlet;
60 import mir.servlet.ServletModule;
61 import mir.servlet.ServletModuleDispatch;
62 import mir.servlet.ServletModuleExc;
63 import mir.servlet.ServletModuleUserExc;
64 import mir.util.CachingRewindableIterator;
65 import mir.util.ExceptionFunctions;
66 import mir.util.StringRoutines;
67 import mircoders.entity.EntityUsers;
68 import mircoders.global.MirGlobal;
69 import mircoders.module.ModuleMessage;
70 import mircoders.module.ModuleUsers;
71 import mircoders.servlet.ServletHelper;
72 import mircoders.servlet.ServletModuleFileEdit;
73 import mircoders.servlet.ServletModuleLocalizer;
74 import mircoders.storage.DatabaseUsers;
75
76
77
78
79 /**
80  * Mir.java - main servlet, that dispatches to servletmodules
81  *
82  * @author $Author: zapata $
83  * @version $Id: Mir.java,v 1.49.2.2 2003/06/19 02:24:12 zapata 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   protected String getDefaultLanguage(HttpServletRequest aRequest) {
141     String defaultlanguage =
142       MirGlobal.config().getString("Mir.Login.DefaultLanguage", "");
143
144     if (defaultlanguage.length() == 0) {
145       Locale locale = aRequest.getLocale();
146       defaultlanguage = locale.getLanguage();
147     }
148
149     return defaultlanguage;
150   }
151
152   protected synchronized Locale getFallbackLocale() throws ServletException {
153     try {
154       if (fallbackLocale == null) {
155         fallbackLocale = new Locale(MirPropertiesConfiguration.instance().getString("Mir.Admin.FallbackLanguage", "en"), "");
156       }
157     }
158     catch (Throwable t) {
159       throw new ServletException(t.getMessage());
160     }
161
162     return fallbackLocale;
163   }
164
165   public void process(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletException, IOException, UnavailableException {
166     try {
167       long startTime = System.currentTimeMillis();
168       long sessionConnectTime = 0;
169       EntityUsers userEntity;
170       HttpSession session;
171
172       configuration.addProperty("ServletName", getServletName());
173
174       session = aRequest.getSession(true);
175       userEntity = ServletHelper.getUser(aRequest);
176
177       setNoCaching(aResponse);
178
179       Locale locale = new Locale(getDefaultLanguage(aRequest), "");
180
181       aResponse.setContentType("text/html; charset=" +
182                                configuration.getString("Mir.DefaultHTMLCharset", "UTF-8"));
183
184       String moduleName = aRequest.getParameter("module");
185       checkLanguage(session, aRequest);
186
187       // Authentication
188       if ( ( (moduleName != null) && moduleName.equals("login")) || (userEntity == null)) {
189         String user = aRequest.getParameter("login");
190         String passwd = aRequest.getParameter("password");
191         logger.debug("--login: evaluating for user: " + user);
192         userEntity = allowedUser(user, passwd);
193
194         if (userEntity == null) {
195           // login failed: redirecting to login
196           logger.warn("--login: failed!");
197           _sendLoginPage(aResponse, aRequest, aResponse.getWriter());
198
199           return;
200         }
201         else if ( (moduleName != null) && moduleName.equals("login")) {
202           // login successful
203           logger.info("--login: successful! setting uid: " + userEntity.getId());
204           ServletHelper.setUser(aRequest, userEntity);
205
206           logger.debug("--login: trying to retrieve login.target");
207           String target = (String) session.getAttribute("login.target");
208
209           if (target != null) {
210             logger.debug("Redirect: " + target);
211
212             aResponse.sendRedirect(
213                 MirPropertiesConfiguration.instance().getString("RootUri") + "/Mir?" + target);
214
215           }
216           else {
217             logger.debug("--login: no target - redirecting to default");
218             _sendStartPage(aResponse, aRequest, aResponse.getWriter(), userEntity);
219           }
220
221           return;
222         }
223         // if login succesful
224       }
225       // if login
226
227       if ( (moduleName != null) && moduleName.equals("logout")) {
228         logger.info("--logout");
229         session.invalidate();
230
231         _sendLoginPage(aResponse, aRequest, aResponse.getWriter());
232
233         return;
234       }
235
236       // Check if authed!
237       if (userEntity == null) {
238         // redirect to loginpage
239         String redirectString = aRequest.getRequestURI();
240         String queryString = aRequest.getQueryString();
241
242         if ( (queryString != null) && queryString.length() != 0) {
243           redirectString += ("?" + aRequest.getQueryString());
244           session.setAttribute("login.target", redirectString);
245         }
246
247         _sendLoginPage(aResponse, aRequest, aResponse.getWriter());
248
249         return;
250       }
251
252       // If no module is specified goto standard startpage
253       if ( (moduleName == null) || moduleName.equals("")) {
254         _sendStartPage(aResponse, aRequest, aResponse.getWriter(), userEntity);
255
256         return;
257       }
258
259       try {
260         // get servletmodule by parameter and continue with dispacher
261         ServletModule smod = getServletModuleForName(moduleName);
262         ServletModuleDispatch.dispatch(smod, aRequest, aResponse);
263       }
264       catch (Throwable e) {
265         Throwable cause = ExceptionFunctions.traceCauseException(e);
266
267         if (cause instanceof ServletModuleUserExc)
268           handleUserError(aRequest, aResponse, aResponse.getWriter(), (ServletModuleUserExc) cause);
269         else
270           handleError(aRequest, aResponse, aResponse.getWriter(), cause);
271
272       }
273
274       // timing...
275       sessionConnectTime = System.currentTimeMillis() - startTime;
276       logger.info("EXECTIME (" + moduleName + "): " + sessionConnectTime + " ms");
277     }
278     catch (Throwable t) {
279       throw new ServletException(t.toString());
280     }
281   }
282
283   /**
284    *  Private method getServletModuleForName returns ServletModule
285    *  from Cache
286    *
287    * @param moduleName
288    * @return ServletModule
289    *
290    */
291   private static ServletModule getServletModuleForName(String moduleName) throws ServletModuleExc {
292     // Instance in Map ?
293     if (!servletModuleInstanceHash.containsKey(moduleName)) {
294       // was not found in hash...
295       try {
296         Class theServletModuleClass = null;
297
298         try {
299           // first we try to get ServletModule from stern.che3.servlet
300           theServletModuleClass =
301             Class.forName("mircoders.servlet.ServletModule" + moduleName);
302         } catch (ClassNotFoundException e) {
303           // on failure, we try to get it from lib-layer
304           theServletModuleClass =
305             Class.forName("mir.servlet.ServletModule" + moduleName);
306         }
307
308         Method m = theServletModuleClass.getMethod("getInstance", null);
309         ServletModule smod = (ServletModule) m.invoke(null, null);
310
311         // we put it into map for further reference
312         servletModuleInstanceHash.put(moduleName, smod);
313
314         return smod;
315       }
316       catch (Exception e) {
317         throw new ServletModuleExc("*** error resolving classname for " + moduleName + " -- " + e.getMessage());
318       }
319     }
320     else {
321       return (ServletModule) servletModuleInstanceHash.get(moduleName);
322     }
323   }
324
325   private void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse,
326                                PrintWriter out, ServletModuleUserExc anException) {
327     try {
328       logger.info("user error: " + anException.getMessage());
329       SimpleHash modelRoot = new SimpleHash();
330       MessageResources messages = MessageResources.getMessageResources("bundles.admin");
331       modelRoot.put("errorstring",
332           new SimpleScalar(
333               messages.getMessage(getLocale(aRequest), anException.getMessage(), anException.getParameters())
334           ));
335       modelRoot.put("date", new SimpleScalar(StringUtil.date2readableDateTime(new GregorianCalendar())));
336       HTMLTemplateProcessor.process(
337           aResponse,
338           MirPropertiesConfiguration.instance().getString("Mir.UserErrorTemplate"),
339           modelRoot,
340           null,
341           out,
342           getLocale(aRequest),
343           fallbackLocale);
344       out.close();
345     }
346     catch (Throwable e) {
347       logger.error("Error handling user error" + e.toString());
348     }
349
350   }
351
352   private void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse,PrintWriter out, Throwable anException) {
353
354     try {
355       logger.error("error: " + anException);
356       SimpleHash modelRoot = new SimpleHash();
357       modelRoot.put("errorstring", new SimpleScalar(anException.getMessage()));
358       modelRoot.put("date", new SimpleScalar(StringUtil.date2readableDateTime(
359                                                new GregorianCalendar())));
360       HTMLTemplateProcessor.process(
361           aResponse,MirPropertiesConfiguration.instance().getString("Mir.ErrorTemplate"),
362           modelRoot,null,out, getLocale(aRequest), getFallbackLocale());
363       out.close();
364     }
365     catch (Throwable e) {
366       logger.error("Error handling error: " + e.toString());
367     }
368   }
369
370   /**
371    *  evaluate login for user / password
372    */
373   protected EntityUsers allowedUser(String user, String password) {
374     try {
375       if (usersModule == null) {
376         usersModule = new ModuleUsers(DatabaseUsers.getInstance());
377       }
378
379       return usersModule.getUserForLogin(user, password);
380     }
381     catch (Exception e) {
382       logger.debug(e.getMessage());
383       e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
384
385       return null;
386     }
387   }
388
389   // Redirect-methods
390   private void _sendLoginPage(HttpServletResponse aResponse, HttpServletRequest aRequest,
391     PrintWriter out) {
392     String loginTemplate = configuration.getString("Mir.LoginTemplate");
393     String sessionUrl = aResponse.encodeURL("");
394
395     try {
396       SimpleHash mergeData = new SimpleHash();
397       SimpleList languages = new SimpleList();
398
399       mergeData.put("session", sessionUrl);
400
401       mergeData.put("defaultlanguage", getDefaultLanguage(aRequest));
402       mergeData.put("languages", getLoginLanguages());
403
404       HTMLTemplateProcessor.process(aResponse, loginTemplate, mergeData, null, out, getLocale(aRequest), getFallbackLocale());
405     }
406     catch (Throwable e) {
407       handleError(aRequest, aResponse, out, e);
408     }
409   }
410
411   private void _sendStartPage(HttpServletResponse aResponse, HttpServletRequest aRequest,
412     PrintWriter out, EntityUsers userEntity) {
413     String startTemplate = configuration.getString("Mir.StartTemplate");
414     String sessionUrl = aResponse.encodeURL("");
415
416     try {
417       Map mergeData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()}, "bundles.admin", "bundles.adminlocal");
418       mergeData.put("messages",
419              new CachingRewindableIterator(
420                new EntityIteratorAdapter( "", "webdb_create desc", 10,
421                  MirGlobal.localizer().dataModel().adapterModel(), "internalMessage", 10, 0)));
422
423       mergeData.put("fileeditentries", ((ServletModuleFileEdit) ServletModuleFileEdit.getInstance()).getEntries());
424       mergeData.put("administeroperations", ((ServletModuleLocalizer) ServletModuleLocalizer.getInstance()).getAdministerOperations());
425
426       mergeData.put("searchvalue", null);
427       mergeData.put("searchfield", null);
428       mergeData.put("searchispublished", null);
429       mergeData.put("searcharticletype", null);
430       mergeData.put("searchorder", null);
431       mergeData.put("selectarticleurl", null);
432
433       ServletHelper.generateResponse(out, mergeData, startTemplate);
434     }
435     catch (Exception e) {
436       e.printStackTrace(logger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
437       handleError(aRequest, aResponse, out, e);
438     }
439   }
440
441   public String getServletInfo() {
442     return "Mir " + configuration.getString("Mir.Version");
443   }
444 }