scripts/mir-setup/README: update with link to new doc on wiki
[mir.git] / source / Mir.java
1 /*
2  * Copyright (C) 2001-2006 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  * and distribute linked combinations including the two.  You must obey the
23  * GNU General Public License in all respects for all of the code used other than
24  * the above mentioned libraries.  If you modify this file, you may extend this
25  * exception to your version of the file, but you are not obligated to do so.
26  * If you do not wish to do so, delete this exception statement from your version.
27  */
28
29 import mir.bundle.Bundle;
30 import mir.config.MirPropertiesConfiguration;
31 import mir.servlet.AbstractServlet;
32 import mir.servlet.AdminServletModule;
33 import mir.servlet.ServletModuleExc;
34 import mir.servlet.ServletModuleUserExc;
35 import mir.util.ExceptionRoutines;
36 import mir.util.StringRoutines;
37 import mircoders.entity.EntityUsers;
38 import mircoders.global.MirGlobal;
39 import mircoders.module.ModuleUsers;
40 import mircoders.servlet.ServletHelper;
41 import multex.Failure;
42
43 import javax.servlet.ServletConfig;
44 import javax.servlet.ServletException;
45 import javax.servlet.UnavailableException;
46 import javax.servlet.http.HttpServletRequest;
47 import javax.servlet.http.HttpServletResponse;
48 import javax.servlet.http.HttpSession;
49 import javax.servlet.http.HttpSessionBindingEvent;
50 import javax.servlet.http.HttpSessionBindingListener;
51 import java.io.IOException;
52 import java.io.PrintWriter;
53 import java.io.StringWriter;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collections;
57 import java.util.GregorianCalendar;
58 import java.util.HashMap;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Locale;
62 import java.util.Map;
63
64 public class Mir extends AbstractServlet {
65   private static ModuleUsers usersModule = null;
66   private static Locale fallbackLocale = null;
67
68   private static List loginLanguages = null;
69
70   private List getLoginLanguages() throws Failure {
71     synchronized (Mir.class) {
72       try {
73         if (loginLanguages == null) {
74           List languages =
75             StringRoutines.splitString(MirGlobal.config().getString("Mir.Login.Languages", "en"), ";");
76
77           loginLanguages = new ArrayList();
78
79           Iterator i = languages.iterator();
80
81           while (i.hasNext()) {
82             String code = (String) i.next();
83
84             Bundle bundle =
85                 MirGlobal.getBundleFactory().getBundle("etc/bundles/adminlocal", new String[] { code });
86             Bundle defaultBundle =
87                 MirGlobal.getBundleFactory().getBundle("bundles/admin", new String[] { code });
88
89             String name = bundle.getValue("languagename", Collections.EMPTY_LIST);
90
91             if (name == null) {
92               name = defaultBundle.getValue("languagename", Collections.EMPTY_LIST);
93             }
94
95             if (name == null) {
96               name = code;
97             }
98
99             Map record = new HashMap();
100             record.put("name", name);
101             record.put("code", code);
102             loginLanguages.add(record);
103           }
104         }
105
106         return loginLanguages;
107       }
108       catch (Throwable t) {
109         throw new Failure("Error while retrieving the available login languages", t);
110       }
111     }
112   }
113
114   public void init(ServletConfig config) throws ServletException {
115     super.init(config);
116
117     usersModule = new ModuleUsers();
118   }
119
120   protected String getDefaultLanguage(HttpServletRequest aRequest) {
121     String defaultlanguage =
122       MirGlobal.config().getString("Mir.Login.DefaultLanguage", "");
123
124     if (defaultlanguage.length() == 0) {
125       Locale locale = aRequest.getLocale();
126       defaultlanguage = locale.getLanguage();
127     }
128
129     return defaultlanguage;
130   }
131
132   protected synchronized Locale getFallbackLocale() throws ServletException {
133     try {
134       if (fallbackLocale == null) {
135         fallbackLocale = new Locale(MirPropertiesConfiguration.instance().getString("Mir.Admin.FallbackLanguage", "en"), "");
136       }
137     }
138     catch (Throwable t) {
139       throw new ServletException(t.getMessage());
140     }
141
142     return fallbackLocale;
143   }
144
145   public EntityUsers checkCredentials(HttpServletRequest aRequest) throws ServletException {
146     try {
147       EntityUsers user = ServletHelper.getUser(aRequest);
148
149       String username = aRequest.getParameter("login");
150       String password = aRequest.getParameter("password");
151
152       if (username != null && password != null) {
153         user = usersModule.getUserForLogin(username, password);
154
155         if (user!=null) {
156           ServletHelper.setUser(aRequest, user);
157           usersModule.recordLogin(user);
158           aRequest.getSession().setAttribute("sessiontracker", new SessionTracker(username, user.getId()));
159         }
160       }
161
162       return user;
163     }
164     catch (Throwable t) {
165       throw new ServletException(t.getMessage());
166     }
167   }
168
169   public void process(HttpServletRequest aRequest, HttpServletResponse aResponse) throws ServletException, IOException, UnavailableException {
170     try {
171       long startTime = System.currentTimeMillis();
172       long sessionConnectTime = 0;
173
174       HttpSession session = aRequest.getSession(true);
175       setNoCaching(aResponse);
176       aResponse.setContentType("text/html; charset=" +
177                                configuration.
178                                getString("Mir.DefaultHTMLCharset", "UTF-8"));
179
180       EntityUsers userEntity = checkCredentials(aRequest);
181
182       if (userEntity == null) {
183         String queryString = aRequest.getQueryString();
184
185         if ( (queryString != null) && (queryString.length() != 0) && session.getAttribute("login.target") == null &&
186              (aRequest.getParameter("module")==null ||
187               (!aRequest.getParameter("module").equals("login") && !aRequest.getParameter("module").equals("logout")))) {
188           session.setAttribute("login.target", queryString);
189         }
190
191         _sendLoginPage(aResponse, aRequest);
192       }
193       else {
194         String moduleName = aRequest.getParameter("module");
195         checkLanguage(session, aRequest);
196
197         if ( ( (moduleName == null) || moduleName.equals(""))) {
198           moduleName="Admin";
199         }
200
201         if (moduleName.equals("login")) {
202           String target = (String) session.getAttribute("login.target");
203
204           if (target != null) {
205             ServletHelper.redirect(aResponse, target);
206           }
207           else {
208             ServletHelper.redirect(aResponse, "");
209           }
210         }
211         else if (moduleName.equals("logout")) {
212           logger.info(userEntity.getFieldValue("login") + " has logged out");
213           session.invalidate();
214           _sendLoginPage(aResponse, aRequest);
215           return;
216         }
217         else {
218           try {
219             AdminServletModule servletModule = getServletModuleForName(moduleName);
220             servletModule.handleRequest(aRequest, aResponse);
221
222             sessionConnectTime = System.currentTimeMillis() - startTime;
223             logger.info("EXECTIME (" + moduleName + "): " + sessionConnectTime + " ms");
224           }
225           catch (Throwable e) {
226             Throwable cause = ExceptionRoutines.traceCauseException(e);
227
228             if (cause instanceof ServletModuleUserExc)
229               handleUserError(aRequest, aResponse, (ServletModuleUserExc) cause);
230             else
231               handleError(aRequest, aResponse, cause);
232           }
233
234           if (aRequest.getParameter("killsession")!=null)
235             aRequest.getSession().invalidate();
236         }
237       }
238     }
239     catch (Throwable t) {
240       throw new ServletException(t.toString());
241     }
242   }
243
244   /**
245    * caching routine to get a module for a module name
246    *
247    * @param aModuleName the module name
248    * @return the requested module
249    * @throws ServletModuleExc
250    */
251   private static AdminServletModule getServletModuleForName(String aModuleName) throws ServletModuleExc {
252     return ServletHelper.getServletModule(aModuleName);
253   }
254
255   private void handleUserError(HttpServletRequest aRequest, HttpServletResponse aResponse, ServletModuleUserExc anException) {
256     try {
257       logger.info("user error: " + anException.getMessage());
258
259       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()});
260
261       Bundle bundle =
262           MirGlobal.getBundleFactory().getBundle("etc/bundles/adminlocal", new
263               String[] { getLocale(aRequest).getLanguage() });
264       Bundle defaultBundle =
265           MirGlobal.getBundleFactory().getBundle("bundles/admin", new
266               String[] { getLocale(aRequest).getLanguage() });
267       String message =
268         bundle.getValue(anException.getMessage(), Arrays.asList(anException.getParameters()));
269
270       if (message==null) {
271         message =
272           defaultBundle.getValue(anException.getMessage(), Arrays.asList(anException.getParameters()));
273       }
274
275       responseData.put("errorstring", message);
276       responseData.put("date", new GregorianCalendar().getTime());
277
278       ServletHelper.generateResponse(aResponse.getWriter(), responseData, MirPropertiesConfiguration.instance().getString("Mir.UserErrorTemplate"));
279     }
280     catch (Throwable e) {
281       logger.error("Error handling user error" + e.toString());
282     }
283   }
284
285   private void handleError(HttpServletRequest aRequest, HttpServletResponse aResponse, Throwable anException) {
286     try {
287       logger.error("error: " + anException);
288
289       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()});
290
291       responseData.put("errorstring", anException.toString());
292       StringWriter writer = new StringWriter();
293       anException.printStackTrace(new PrintWriter(writer));
294       responseData.put("stacktrace", writer.toString());
295       responseData.put("date", new GregorianCalendar().getTime());
296
297       ServletHelper.generateResponse(aResponse.getWriter(), responseData, MirPropertiesConfiguration.instance().getString("Mir.ErrorTemplate"));
298     }
299     catch (Throwable e) {
300       logger.error("Error handling error: " + e.toString());
301
302       try {
303         Throwable rootException = ExceptionRoutines.traceCauseException(anException);
304
305         PrintWriter writer = aResponse.getWriter();
306         writer.println("<html><head><title>FATAL Error</title><body>");
307         writer.println("<h1>" + rootException.toString()+"</h1>");
308         writer.println("<code>");
309         rootException.printStackTrace(writer);
310         writer.println("</code>");
311         writer.println("</body></html>");
312         writer.close();
313       }
314       catch (Throwable t) {
315
316       }
317     }
318   }
319
320   // Redirect-methods
321   private void _sendLoginPage(HttpServletResponse aResponse, HttpServletRequest aRequest) {
322     String loginTemplate = configuration.getString("Mir.LoginTemplate");
323
324     try {
325       Map responseData = ServletHelper.makeGenerationData(aRequest, aResponse, new Locale[] {getLocale(aRequest), getFallbackLocale()});
326
327       responseData.put("defaultlanguage", getDefaultLanguage(aRequest));
328       responseData.put("languages", getLoginLanguages());
329
330       ServletHelper.generateResponse(aResponse.getWriter(), responseData, loginTemplate);
331     }
332     catch (Throwable e) {
333       handleError(aRequest, aResponse, e);
334     }
335   }
336
337   public String getServletInfo() {
338     return "Mir " + configuration.getString("Mir.Version");
339   }
340
341   private class SessionTracker implements HttpSessionBindingListener {
342     private String name;
343     private String id;
344
345     public SessionTracker(String aUserName, String anId) {
346       name = aUserName;
347       id = anId;
348     }
349
350     public void valueBound(HttpSessionBindingEvent anEvent) {
351       MirGlobal.registerLogin(name, id);
352     }
353
354     public void valueUnbound(HttpSessionBindingEvent anEvent) {
355       MirGlobal.registerLogout(name, id);
356     }
357   }
358 }