source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java@ 35507

Last change on this file since 35507 was 35507, checked in by kjdon, 3 years ago

OOOPS. so many debugging messages were left in :-(

  • Property svn:keywords set to Author Date Id Revision
File size: 43.8 KB
Line 
1/*
2 * LibraryServlet.java
3 * Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20package org.greenstone.gsdl3;
21
22import java.io.File;
23import java.io.IOException;
24import java.io.PrintWriter;
25import java.io.Serializable;
26import java.lang.reflect.Type;
27import java.util.ArrayList;
28import java.util.Arrays;
29import java.util.Enumeration;
30import java.util.HashMap;
31import java.util.Hashtable;
32import java.util.Iterator;
33import java.util.List;
34import java.util.Map;
35
36import javax.servlet.ServletConfig;
37import javax.servlet.ServletContext;
38import javax.servlet.ServletException;
39import javax.servlet.http.Cookie;
40import javax.servlet.http.HttpServletRequest;
41import javax.servlet.http.HttpServletResponse;
42import javax.servlet.http.HttpSession;
43import javax.servlet.http.HttpSessionBindingEvent;
44import javax.servlet.http.HttpSessionBindingListener;
45
46import org.apache.commons.fileupload.FileItem;
47import org.apache.commons.fileupload.disk.DiskFileItemFactory;
48import org.apache.commons.fileupload.servlet.ServletFileUpload;
49import org.apache.commons.lang3.StringUtils;
50import org.apache.log4j.Logger;
51import org.greenstone.gsdl3.action.PageAction;
52import org.greenstone.gsdl3.comms.Communicator;
53import org.greenstone.gsdl3.comms.SOAPCommunicator;
54import org.greenstone.gsdl3.core.DefaultReceptionist;
55import org.greenstone.gsdl3.core.MessageRouter;
56import org.greenstone.gsdl3.core.Receptionist;
57import org.greenstone.gsdl3.service.Authentication;
58import org.greenstone.gsdl3.util.Dictionary;
59import org.greenstone.gsdl3.util.GSConstants;
60import org.greenstone.gsdl3.util.GSParams;
61import org.greenstone.gsdl3.util.GSXML;
62import org.greenstone.gsdl3.util.UserContext;
63import org.greenstone.gsdl3.util.XMLConverter;
64import org.greenstone.util.GlobalProperties;
65import org.json.JSONObject;
66import org.w3c.dom.Document;
67import org.w3c.dom.Element;
68import org.w3c.dom.Node;
69import org.w3c.dom.NodeList;
70
71import com.google.gson.Gson;
72import com.google.gson.reflect.TypeToken;
73
74/**
75 * a servlet to serve the greenstone library - we are using servlets instead of
76 * cgi
77 * the init method is called only once - the first time the servlet classes
78 * are loaded. Each time a request comes in to the servlet, the session() method
79 * is called in a new thread (calls doGet/doPut etc) takes the a=p&p=home type
80 * args and builds a simple request to send to its receptionist, which returns a
81 * result in html, cos output=html is set in the request
82 *
83 * 18/Jul/07 xiao modify to make the cached parameters collection-specific. Most
84 * of the work is done in doGet(), except adding an inner class
85 * UserSessionCache.
86 *
87 * @see Receptionist
88 */
89public class LibraryServlet extends BaseGreenstoneServlet
90{
91 protected static final String LOGIN_MESSAGE_PARAM = "loginMessage";
92 protected static final String REDIRECT_URL_PARAM = "redirectURL";
93 /** the receptionist to send messages to */
94 protected Receptionist recept = null;
95
96 /** We record the library name for later */
97 protected String library_name = null;
98 /** Whether or not client-side XSLT support should be exposed */
99 protected boolean supports_client_xslt = false;
100
101 /**
102 * the default language - is specified by setting a servlet param, otherwise
103 * DEFAULT_LANG is used
104 */
105 protected String default_lang = null;
106 /**
107 * The default default - used if a default lang is not specified in the
108 * servlet params
109 */
110 protected final String DEFAULT_LANG = "en";
111
112 /**
113 * the cgi stuff - the Receptionist can add new args to this
114 *
115 * its used by the servlet to determine what args to save,
116 * and to set default values
117 */
118 protected GSParams gs_params = null;
119
120 /**
121 * a hash that contains all the active session IDs mapped to the cached
122 * items It is updated whenever the whole site or a particular collection is
123 * reconfigured using the command a=s&sa=c or a=s&sa=c&c=xxx It is in the
124 * form: sid -> (UserSessionCache object)
125 */
126 protected Hashtable<String, UserSessionCache> session_ids_table = new Hashtable<String, UserSessionCache>();
127
128 /**
129 * the maximum interval that the cached info remains in session_ids_table
130 * (in seconds) This is set in web.xml
131 */
132 protected int session_expiration = 1800;
133
134 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.LibraryServlet.class.getName());
135
136 /** initialise the servlet */
137 public void init(ServletConfig config) throws ServletException
138 {
139 // always call super.init;
140 super.init(config);
141 // disable preferences - does this work anyway??
142 //System.setProperty("java.util.prefs.PreferencesFactory", "org.greenstone.gsdl3.util.DisabledPreferencesFactory");
143
144 // Look through all the init params
145 // these are the ones we are expecting
146 String interface_name = null;
147 String useXslt = null;
148 String sess_expire = null;
149 String site_name = null;
150 String recept_name = null;
151
152 // may have these instead of site_name
153 String remote_site_name = null;
154 String remote_site_type = null;
155 String remote_site_address = null;
156
157 // the stored config params
158 HashMap<String, Object> config_params = new HashMap<String, Object>();
159 Enumeration<String> params = config.getInitParameterNames();
160 while (params.hasMoreElements()) {
161 String name = params.nextElement();
162 String value = config.getInitParameter(name);
163 if (name.equals(GSConstants.LIBRARY_NAME)) {
164 library_name = value;
165 } else if (name.equals(GSConstants.INTERFACE_NAME)) {
166 interface_name = value;
167 } else if (name.equals(GSConstants.USE_CLIENT_SIDE_XSLT)) {
168 useXslt = value;
169 } else if (name.equals(GSConstants.DEFAULT_LANG)) {
170 this.default_lang = value;
171 } else if (name.equals(GSXML.SESSION_EXPIRATION)) {
172 sess_expire = value;
173 } else if (name.equals(GSConstants.SITE_NAME)) {
174 site_name = value;
175 } else if (name.equals(GSConstants.REMOTE_SITE_NAME)) {
176 remote_site_name = value;
177 } else if (name.equals(GSConstants.REMOTE_SITE_TYPE)) {
178 remote_site_type = value;
179 } else if (name.equals(GSConstants.REMOTE_SITE_ADDRESS)) {
180 remote_site_address = value;
181 } else if (name.equals(GSConstants.RECEPTIONIST_CLASS)) {
182 recept_name = value;
183 } else {
184 // If there is a param we are not expecting, just add to the list. That way users can add new servlet params which will go through to the XML source without modifying code.
185 config_params.put(name, value);
186 }
187 }
188 if (library_name == null || interface_name == null) {
189
190 // must have these
191 System.err.println("initialisation parameters not all set!");
192 System.err.println(" you must have libraryname and interfacename");
193 System.exit(1);
194 }
195
196 // If we don't have a site, then we must be using a Communicator, and all the remote params must be set
197 if (site_name == null && (remote_site_name == null || remote_site_type == null || remote_site_address == null)) {
198
199 System.err.println("initialisation paramters not all set!");
200 System.err.println("if site_name is not set, then you must have remote_site_name, remote_site_type and remote_site_address set");
201 System.exit(1);
202 }
203
204 supports_client_xslt = useXslt != null && useXslt.equals("true");
205 if (sess_expire != null && !sess_expire.equals(""))
206 {
207 this.session_expiration = Integer.parseInt(sess_expire);
208 }
209 if (this.default_lang == null)
210 {
211 // choose english
212 this.default_lang = DEFAULT_LANG;
213 }
214
215 config_params.put(GSConstants.LIBRARY_NAME, library_name);
216 config_params.put(GSConstants.INTERFACE_NAME, interface_name);
217 config_params.put(GSConstants.USE_CLIENT_SIDE_XSLT, supports_client_xslt);
218
219 if (site_name != null)
220 {
221 config_params.put(GSConstants.SITE_NAME, site_name);
222 }
223
224 // the receptionist -the servlet will talk to this
225 if (recept_name == null)
226 {
227 this.recept = new DefaultReceptionist();
228 }
229 else
230 {
231 try
232 {
233 this.recept = (Receptionist) Class.forName("org.greenstone.gsdl3.core." + recept_name).getDeclaredConstructor().newInstance();
234 }
235 catch (Exception e)
236 { // cant use this new one, so use normal one
237 System.err.println("LibraryServlet configure exception when trying to use a new Receptionist " + recept_name + ": " + e.getMessage());
238 e.printStackTrace();
239 this.recept = new DefaultReceptionist();
240 }
241 }
242 this.recept.setConfigParams(config_params);
243
244 // the params arg thingy
245
246 String params_name = (String) config.getInitParameter("params_class");
247 if (params_name == null)
248 {
249 this.gs_params = new GSParams();
250 }
251 else
252 {
253 try
254 {
255 this.gs_params = (GSParams) Class.forName("org.greenstone.gsdl3.util." + params_name).getDeclaredConstructor().newInstance();
256 }
257 catch (Exception e)
258 {
259 System.err.println("LibraryServlet configure exception when trying to use a new params thing " + params_name + ": " + e.getMessage());
260 e.printStackTrace();
261 this.gs_params = new GSParams();
262 }
263 }
264
265 // the receptionist uses a MessageRouter or Communicator to send its requests to. We either create a MessageRouter here for the designated site (if site_name set), or we create a Communicator for a remote site. The is given to the Receptionist, and the servlet never talks to it again directly.
266 if (site_name != null)
267 {
268 String mr_name = (String) config.getInitParameter("messagerouter_class");
269 MessageRouter message_router = null;
270 if (mr_name == null)
271 { // just use the normal MR
272 message_router = new MessageRouter();
273 }
274 else
275 { // try the specified one
276 try
277 {
278 message_router = (MessageRouter) Class.forName("org.greenstone.gsdl3.core." + mr_name).getDeclaredConstructor().newInstance();
279 }
280 catch (Exception e)
281 { // cant use this new one, so use normal one
282 System.err.println("LibraryServlet configure exception when trying to use a new MessageRouter " + mr_name + ": " + e.getMessage());
283 e.printStackTrace();
284 message_router = new MessageRouter();
285 }
286 }
287
288 message_router.setSiteName(site_name);
289 message_router.setLibraryName(library_name);
290 message_router.setParams(this.gs_params);
291 message_router.configure();
292 this.recept.setMessageRouter(message_router);
293 }
294 else
295 {
296 // TODO: what do we do about params here?
297 // talking to a remote site, create a communicator
298 Communicator communicator = null;
299 // we need to create the XML to configure the communicator
300 Document doc = XMLConverter.newDOM();
301 Element site_elem = doc.createElement(GSXML.SITE_ELEM);
302 site_elem.setAttribute(GSXML.TYPE_ATT, remote_site_type);
303 site_elem.setAttribute(GSXML.NAME_ATT, remote_site_name);
304 site_elem.setAttribute(GSXML.ADDRESS_ATT, remote_site_address);
305
306 if (remote_site_type.equals(GSXML.COMM_TYPE_SOAP_JAVA))
307 {
308 communicator = new SOAPCommunicator();
309 }
310 else
311 {
312 System.err.println("LibraryServlet.init Error: invalid Communicator type: " + remote_site_type);
313 System.exit(1);
314 }
315
316 if (!communicator.configure(site_elem))
317 {
318 System.err.println("LibraryServlet.init Error: Couldn't configure communicator");
319 System.exit(1);
320 }
321 this.recept.setMessageRouter(communicator);
322 }
323
324 // pass params to the receptionist
325 this.recept.setParams(this.gs_params);
326 this.recept.configure();
327
328 //Allow the message router and the document to be accessed from anywhere in this servlet context
329 this.getServletContext().setAttribute(library_name+"Router", this.recept.getMessageRouter());
330 }
331
332 private void logUsageInfo(HttpServletRequest request, Map<String, String[]> queryMap)
333 {
334 String usageInfo = "";
335
336 //session-info: get params stored in the session
337 HttpSession session = request.getSession(true);
338 Enumeration attributeNames = session.getAttributeNames();
339 while (attributeNames.hasMoreElements())
340 {
341 String name = (String) attributeNames.nextElement();
342 usageInfo += name + "=" + session.getAttribute(name) + " ";
343 }
344
345 String queryParamStr = "";
346 if (queryMap != null)
347 {
348 Iterator<String> queryIter = queryMap.keySet().iterator();
349 while (queryIter.hasNext()) {
350 String q = queryIter.next();
351 String[] vals = queryMap.get(q);
352 queryParamStr += q +"="+StringUtils.join(vals, ",")+" ";
353 }
354 }
355 //logged info = general-info + session-info
356 usageInfo = request.getServletPath() + " " + //serlvet
357 "[" + queryParamStr.trim()+"]"+" " + // query map params
358 "session:[" + usageInfo.trim() + "]" + " " + // params stored in a session
359 request.getRemoteAddr() + " " + //remote address
360 request.getRequestedSessionId() + " " + //session id
361 request.getHeader("user-agent") + " "; //the remote brower info
362
363 logger.info(usageInfo);
364
365 }
366
367 public class UserSessionCache implements HttpSessionBindingListener
368 {
369 String session_id = "";
370
371 /**
372 * a hash that maps the session ID to a hashtable that maps the
373 * coll_name to its parameters coll_name -> Hashtable (param_name ->
374 * param_value)
375 */
376 protected Hashtable<String, Hashtable<String, String>> coll_name_params_table = null;
377
378 public UserSessionCache(String id, Hashtable<String, Hashtable<String, String>> table)
379 {
380 session_id = id;
381 coll_name_params_table = (table == null) ? new Hashtable() : table;
382 }
383
384 protected void cleanupCache(String coll_name)
385 {
386 if (coll_name_params_table.containsKey(coll_name))
387 {
388 coll_name_params_table.remove(coll_name);
389 }
390 }
391
392 protected Hashtable<String, Hashtable<String, String>> getParamsTable()
393 {
394 return coll_name_params_table;
395 }
396
397 public void valueBound(HttpSessionBindingEvent event)
398 {
399 // Do nothing
400 }
401
402 // if session cache id removed from session, this triggers this valueUnbound method on the value=this object
403 public void valueUnbound(HttpSessionBindingEvent event)
404 {
405 if (session_ids_table.containsKey(session_id))
406 {
407 session_ids_table.remove(session_id);
408 }
409 }
410
411 public int tableSize()
412 {
413 return (coll_name_params_table == null) ? 0 : coll_name_params_table.size();
414 }
415 }
416
417 public void destroy()
418 {
419 recept.cleanUp();
420 }
421
422 public void doGetOrPost(HttpServletRequest request, HttpServletResponse response, Map<String, String[]> queryMap) throws ServletException, IOException
423 {
424 logUsageInfo(request, queryMap);
425
426 if (processRedirectRequest(request, response, queryMap)) {
427 // this method will do the redirect if needed and return true if it has
428 // done so.
429 // e.g. el=direct/framed&rl=0&href=http://newurl.com
430 return;
431 }
432
433 // Nested Diagnostic Configurator to identify the client for
434 HttpSession session = request.getSession(true);
435 session.setMaxInactiveInterval(session_expiration);
436 String uid = session.getId();
437 request.setCharacterEncoding("UTF-8");
438 response.setContentType("text/html;charset=UTF-8");
439 PrintWriter out = response.getWriter();
440
441 String lang = getFirstParam(GSParams.LANGUAGE, queryMap);
442 if (lang == null || lang.equals(""))
443 {
444 // try the session cached lang
445 lang = (String) session.getAttribute(GSParams.LANGUAGE);
446 if (lang == null || lang.equals(""))
447 {
448 // still not set, use the default
449 lang = this.default_lang;
450 }
451 }
452
453 // set the lang in the session
454 session.setAttribute(GSParams.LANGUAGE, lang);
455
456 String output = getFirstParam(GSParams.OUTPUT, queryMap);
457 if (output == null || output.equals(""))
458 {
459 output = "html"; // uses html by default
460 }
461
462 String requestedURL = request.getRequestURL().toString();
463 String baseURL = "";
464 //logger.error("requested URL = "+requestedURL);
465 if (requestedURL.indexOf(library_name) != -1)
466 {
467 // we need to work out the baseURL and set it
468 baseURL = requestedURL;
469 int protocol_boundary_pos = baseURL.indexOf("://");
470 if (protocol_boundary_pos>=1) {
471 baseURL = baseURL.substring(protocol_boundary_pos+1); // change things like http:// or https:// to //
472 }
473 // The baseURL is everything up to the library_name.
474 // eg https://community.greenstone.org/greenstone3/library/collection/twso/page/about
475 // baseURL is //community.greenstone.org/greenstone3
476 // Issues: the library_name may occur in more than one place - eg if its part of the domain name mylibrary.heritage.nz/greenstone3/library
477 // or if a collection name is the same as the library name eg localhost:8585/greenstone3/twso/collection/twso/page/about.
478 // So can't just use teh first or last occurrence. Look for /library-name, and check its not the first position (eg //library.nzdl.org/greenstone3/library)
479 int library_name_index = baseURL.indexOf("/"+library_name);
480 if (library_name_index == 1) {
481 // we have library name at the start of the url - need to use the second one
482 library_name_index = baseURL.indexOf("/"+library_name, 2);
483 }
484 baseURL = baseURL.substring(0, library_name_index+1);
485 //logger.error("new base url = "+baseURL);
486
487 }
488
489 String fullURL;
490 if (request.getQueryString() != null)
491 {
492 fullURL = requestedURL + "?" + request.getQueryString();
493 }
494 else
495 {
496 fullURL = requestedURL;
497 }
498
499 UserContext userContext = new UserContext();
500 userContext.setLanguage(lang);
501 userContext.setUserID(uid);
502
503 if (!processLoginChanges(request, userContext, out, baseURL, queryMap, lang, output)) {
504 // any invalid login attempt will redirect to a new login page and return false
505 return;
506 }
507
508
509 if (request.getAuthType() != null)
510 {
511 // sets username, groups etc into usercontext
512 updateUserContextWithAuthenticatedInfo(request, userContext);
513 }
514
515 // If server output, force a switch to traditional interface
516 //output = (output.equals("server")) ? "html" : output;
517
518 // Force change the output mode if client-side XSLT is supported - server vs. client
519 // BUT only if the library allows client-side transforms
520 if (supports_client_xslt)
521 {
522 // MUST be done before the xml_message is built
523 Cookie[] cookies = request.getCookies();
524 Cookie xsltCookie = null;
525
526 // The client has cookies enabled and a value set - use it!
527 if (cookies != null)
528 {
529 for (Cookie c : cookies)
530 {
531 if (c.getName().equals("supportsXSLT"))
532 {
533 xsltCookie = c;
534 break;
535 }
536 }
537 output = (xsltCookie != null && xsltCookie.getValue().equals("true") && output.equals("html")) ? "xsltclient" : output;
538 }
539 }
540
541
542 String action = getFirstParam(GSParams.ACTION, queryMap);
543 String subaction = getFirstParam(GSParams.SUBACTION, queryMap);
544 String collection = getFirstParam(GSParams.COLLECTION, queryMap);
545 String document = getFirstParam(GSParams.DOCUMENT, queryMap);
546 String service = getFirstParam(GSParams.SERVICE, queryMap);
547 String specified_cache_key = getFirstParam(GSParams.CACHE_KEY, queryMap);
548
549 if (collection != null && !collection.equals("")) {
550 //is this user allowed to access this collection/document?
551 if (!runCollectionSecurityCheck(request, userContext, out, baseURL, collection, document, lang, output)) {
552 return;
553 }
554 }
555 // We clean up the cache session_ids_table if system
556 // commands are issued, and also don't need to do caching for these request requests
557 boolean should_cache = true;
558 if (action != null && action.equals(GSParams.SYSTEM_ACTION)
559 && !subaction.equals(GSXML.SYSTEM_TYPE_PING)
560 && !subaction.equals(GSXML.SYSTEM_TYPE_AUTHENTICATED_PING)) // don't 'clean' anything on a mere ping
561 {
562 should_cache = false;
563
564 // we may want to remove all collection cache info, or just a specific collection
565 boolean clean_all = true;
566 String clean_collection = null;
567 // system commands are to activate/deactivate stuff
568 // collection param is in the sc parameter.
569 // don't like the fact that it is hard coded here
570 String coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
571 if (coll != null && !coll.equals(""))
572 {
573 clean_all = false;
574 clean_collection = coll;
575 }
576 else
577 {
578 // check other system types
579 if (subaction.equals("a") || subaction.equals("d"))
580 {
581 String module_name = getFirstParam("sn", queryMap);
582 if (module_name != null && !module_name.equals(""))
583 {
584 clean_all = false;
585 clean_collection = module_name;
586 }
587 }
588 }
589 if (clean_all)
590 {
591 // TODO
592 session_ids_table = new Hashtable<String, UserSessionCache>();
593 session.removeAttribute(GSParams.USER_SESSION_CACHE); // triggers valueUnbound(), which removes the session id from the session_ids_table
594 }
595 else
596 {
597 // just clean up info for clean_collection
598 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(session_ids_table.values());
599 for (int i = 0; i < cache_list.size(); i++)
600 {
601 UserSessionCache cache = cache_list.get(i);
602 cache.cleanupCache(clean_collection);
603 }
604
605 }
606 }
607
608 // cache_key is the collection name, or service name
609 String cache_key = specified_cache_key;
610 if (cache_key == null || cache_key.equals(""))
611 {
612 cache_key = collection;
613 }
614 if (cache_key == null || cache_key.equals(""))
615 {
616 cache_key = service;
617 }
618
619 //clear the collection-specific cache in the session, since we have no way to know whether this page is
620 //about the same collection as the last page or not.
621 Enumeration attributeNames = session.getAttributeNames();
622 while (attributeNames.hasMoreElements())
623 {
624 String name = (String) attributeNames.nextElement();
625 if (!name.equals(GSParams.USER_SESSION_CACHE) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT) && !this.gs_params.isGlobal(name))
626 {
627 session.removeAttribute(name);
628 }
629 }
630
631 UserSessionCache session_cache = null;
632 Hashtable<String, Hashtable<String, String>> param_table = null;
633 Hashtable<String, String> table = null;
634 boolean new_table = false;
635 String sid = session.getId();
636 boolean new_session = false;
637 if (!session_ids_table.containsKey(sid)) {
638 new_session = true;
639 }
640 if (should_cache == true && cache_key != null && !cache_key.equals(""))
641 {
642 if (!new_session)
643 {
644 session_cache = session_ids_table.get(sid);
645 param_table = session_cache.getParamsTable();
646 if (param_table.containsKey(cache_key))
647 {
648 table = param_table.get(cache_key);
649 }
650 else
651 {
652 table = new Hashtable<String, String>();
653 param_table.put(cache_key, table);
654 new_table = true;
655 }
656 }
657 else
658 {
659
660 param_table = new Hashtable<String, Hashtable<String, String>>();
661 table = new Hashtable<String, String>();
662 param_table.put(cache_key, table);
663 session_cache = new UserSessionCache(sid, param_table);
664 session_ids_table.put(sid, session_cache);
665 session.setAttribute(GSParams.USER_SESSION_CACHE, session_cache);
666 new_table = true;
667 }
668
669 }
670
671 // here we add in default params values if need be - if we have a new session, or if we have a new table
672 // in an existing session
673 // don't override any existing values
674 if (new_session || new_table) {
675
676 Iterator i = this.gs_params.getParamsWithDefaults().iterator();
677 while (i.hasNext()) {
678 String p = (String)i.next();
679 String v = this.gs_params.getParamDefault(p);
680 if (this.gs_params.isGlobal(p)) {
681 //need to add in to session unless its already there
682 if (session.getAttribute(p) == null) {
683 session.setAttribute(p, v);
684 }
685 } else {
686 // add to table unless its already there
687 if (new_table && !table.containsKey(p)) {
688 table.put(p,v);
689 }
690 }
691
692 }
693 }
694
695 // the request to the receptionist
696 Document msg_doc = XMLConverter.newDOM();
697 Element xml_message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
698 Element xml_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
699 xml_request.setAttribute(GSXML.OUTPUT_ATT, output);
700
701 xml_message.appendChild(xml_request);
702
703 if (request.getAuthType() != null) {
704 // lots of classes are using the <userInformation> element. But all its info is now in userContext, so maybe we can get rid of this one day?
705 appendUserInformation(xml_request, userContext);
706 }
707
708 if (action == null || action.equals(""))
709 {
710 // display the home page - the default page
711 xml_request.setAttribute(GSXML.ACTION_ATT, "p");
712 xml_request.setAttribute(GSXML.SUBACTION_ATT, PageAction.HOME_PAGE);
713 }
714 else
715 {
716 xml_request.setAttribute(GSXML.ACTION_ATT, action);
717 if (subaction != null)
718 {
719 xml_request.setAttribute(GSXML.SUBACTION_ATT, subaction);
720 }
721 }
722
723 // create the param list for the greenstone request - includes
724 // the params from the current request and any others from the saved session
725 Element xml_param_list = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
726 xml_request.appendChild(xml_param_list);
727
728 if (queryMap.containsKey("s1.collection") || queryMap.containsKey("s1.group")){
729 table.remove("s1.collection");
730 table.remove("s1.group");
731 }
732 for (String name : queryMap.keySet())
733 {
734 if (!name.equals(GSParams.ACTION) && !name.equals(GSParams.SUBACTION) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSParams.OUTPUT))
735 {// we have already dealt with these
736
737 String value = "";
738 String[] values = queryMap.get(name);
739 value = values[0];
740 if (values.length > 1)
741 {
742 for (int i = 1; i < values.length; i++)
743 {
744 value += "," + values[i];
745 }
746 }
747 // if we need to save the value, then add it to the session/cache table.
748 // otherwise add directly to the paramList
749 if (this.gs_params.shouldSave(name)) {
750 if (this.gs_params.isGlobal(name) || table == null) {
751 session.setAttribute(name, value);
752 } else {
753 table.put(name, value);
754 }
755 }
756 else
757 {
758 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
759 param.setAttribute(GSXML.NAME_ATT, name);
760 param.setAttribute(GSXML.VALUE_ATT, GSXML.xmlSafe(value));
761 if (this.gs_params.isSensitive(name)) {
762 param.setAttribute(GSXML.SENSITIVE_ATT, "true");
763 }
764 xml_param_list.appendChild(param);
765
766 }
767 }
768 }
769 //put everything in the table into the session
770 if (table != null)
771 {
772 Enumeration<String> keys = table.keys();
773 while (keys.hasMoreElements())
774 {
775 String name = keys.nextElement();
776 session.setAttribute(name, table.get(name));
777 }
778 }
779
780 // put in all the params from the session cache
781 Enumeration params = session.getAttributeNames();
782 while (params.hasMoreElements())
783 {
784 String name = (String) params.nextElement();
785 if (!name.equals(GSParams.USER_SESSION_CACHE) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
786 {
787
788 // lang and uid are stored but we dont want it in the param list cos its already in the request
789 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
790 param.setAttribute(GSXML.NAME_ATT, name);
791 String value = GSXML.xmlSafe((String) session.getAttribute(name));
792
793 // ugly hack to undo : escaping
794 value = StringUtils.replace(value, "%3A", "\\:");
795 param.setAttribute(GSXML.VALUE_ATT, value);
796 xml_param_list.appendChild(param);
797 }
798 }
799
800
801 if (output.equals("json"))
802 {
803 response.setContentType("application/json");
804 }
805 else if (!output.equals("html") && !output.equals("server") && !output.equals("xsltclient"))
806 {
807 response.setContentType("text/xml"); // for now use text
808 }
809
810 //Add custom HTTP headers if requested
811 String httpHeadersParam = getFirstParam(GSParams.HTTP_HEADER_FIELDS, queryMap);
812 if (httpHeadersParam != null && httpHeadersParam.length() > 0)
813 {
814 Gson gson = new Gson();
815 Type type = new TypeToken<List<Map<String, String>>>()
816 {
817 }.getType();
818 List<Map<String, String>> httpHeaders = gson.fromJson(httpHeadersParam, type);
819 if (httpHeaders != null && httpHeaders.size() > 0)
820 {
821
822 for (int j = 0; j < httpHeaders.size(); j++)
823 {
824 Map nameValueMap = httpHeaders.get(j);
825 String name = (String) nameValueMap.get("name");
826 String value = (String) nameValueMap.get("value");
827
828 if (name != null && value != null)
829 {
830 response.setHeader(name, value);
831 }
832 }
833 }
834 }
835
836
837 xml_request.setAttribute("remoteAddress", request.getRemoteAddr());
838 xml_request.setAttribute("fullURL", fullURL.replace("&", "&amp;"));
839 xml_request.setAttribute("baseURL", baseURL);
840
841// logger.error("about to process this message");
842 // logger.error(XMLConverter.getPrettyString(xml_message));
843 Node xml_result = this.recept.process(xml_message);
844 encodeURLs(xml_result, response);
845
846 String xml_string = XMLConverter.getPrettyString(xml_result);
847
848 if (output.equals("json"))
849 {
850 try
851 {
852 JSONObject json_obj = org.json.XML.toJSONObject(xml_string);
853
854 out.println(json_obj.toString());
855 }
856 catch (Exception e)
857 {
858 e.printStackTrace();
859 out.println("Error: failed to convert output XML to JSON format");
860 }
861 }
862 else
863 {
864 out.println(xml_string);
865 }
866
867 displaySize(session_ids_table);
868 }
869
870 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
871 {
872 doGetOrPost(request, response, request.getParameterMap());
873 } //end of doGet(HttpServletRequest, HttpServletResponse)
874
875 private boolean processRedirectRequest(HttpServletRequest request, HttpServletResponse response, Map<String, String[]> queryMap) throws IOException
876 {
877 if (queryMap != null)
878 {
879 Iterator<String> queryIter = queryMap.keySet().iterator();
880 boolean redirect = false;
881 String href = null;
882 String rl = null;
883 String el = null;
884 String collection = null;
885
886 while (queryIter.hasNext())
887 {
888 String q = queryIter.next();
889 if (q.equals(GSParams.COLLECTION))
890 {
891 collection = queryMap.get(q)[0];
892 }
893 else if (q.equals(GSParams.EXTERNAL_LINK_TYPE))
894 {
895 el = queryMap.get(q)[0];
896 }
897 else if (q.equals(GSParams.HREF))
898 {
899 href = queryMap.get(q)[0];
900 href = StringUtils.replace(href, "%2f", "/");
901 href = StringUtils.replace(href, "%7e", "~");
902 href = StringUtils.replace(href, "%3f", "?");
903 href = StringUtils.replace(href, "%3A", "\\:");
904 }
905 else if (q.equals(GSParams.RELATIVE_LINK))
906 {
907 rl = queryMap.get(q)[0];
908 }
909 }
910
911 //if query_string contains "el=direct", an href is specified, and its not a relative link, then the web page will be redirected to the external URl, otherwise a greenstone page with an external URL will be displayed
912 //"rl=0" this is an external link
913 //"rl=1" this is an internal link
914 if ((href != null) && (rl.equals("0")))
915 {// This is an external link,
916
917 if (el.equals("framed"))
918 {
919 // framed means we are linking to an external page inside our greenstone page
920 HttpSession session = request.getSession();
921 ServletContext context = session.getServletContext();
922 String new_url = context.getContextPath()+"/"+ library_name+"?a=p&sa=html&url="+href;
923 if (collection != null && !collection.equals("")) {
924 new_url += "&c="+collection;
925 }
926 response.sendRedirect(new_url);
927 }
928 else
929 {
930 // el = '' or direct
931 //the web page is re-directed to the external URL (&el=&rl=0&href="http://...")
932 response.sendRedirect(href);
933 }
934 return true;
935 }
936 }
937 return false;
938 }
939
940 private void generateLoginPage(String redirect_url_string, String error_message, UserContext userContext, PrintWriter out, String baseURL, String output) {
941
942 Document loginPageDoc = XMLConverter.newDOM();
943 Element loginPageMessage = loginPageDoc.createElement(GSXML.MESSAGE_ELEM);
944 Element loginPageRequest = GSXML.createBasicRequest(loginPageDoc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
945 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
946 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
947 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, output);
948 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
949
950 loginPageMessage.appendChild(loginPageRequest);
951
952 Element paramList = loginPageDoc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
953 loginPageRequest.appendChild(paramList);
954
955 Element messageParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
956 messageParam.setAttribute(GSXML.NAME_ATT, LOGIN_MESSAGE_PARAM);
957 messageParam.setAttribute(GSXML.VALUE_ATT, error_message);
958 paramList.appendChild(messageParam);
959
960 Element urlParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
961 urlParam.setAttribute(GSXML.NAME_ATT, REDIRECT_URL_PARAM);
962
963 if (redirect_url_string.matches("^[a-z]+://.*$")) {
964 int protocol_boundary_pos = redirect_url_string.indexOf("://");
965 redirect_url_string = redirect_url_string.substring(protocol_boundary_pos+1); // change things like http:// or https:// to //
966 }
967
968 urlParam.setAttribute(GSXML.VALUE_ATT, redirect_url_string);
969 paramList.appendChild(urlParam);
970
971 Node loginPageResponse = this.recept.process(loginPageMessage);
972 out.println(XMLConverter.getPrettyString(loginPageResponse));
973 }
974
975 private boolean processLoginChanges(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, Map<String, String[]> queryMap, String lang, String output) throws ServletException {
976
977 //Check if we need to login or logout
978 String username = getFirstParam(GSParams.USERNAME, queryMap);
979 String password = getFirstParam(GSParams.PASSWORD, queryMap);
980 String logout = getFirstParam(GSParams.LOGOUT, queryMap);
981
982 if (logout != null)
983 {
984 request.logout();
985 }
986
987 if (username != null && password != null)
988 {
989 //We are changing to another user, so log out first
990 if (request.getAuthType() != null)
991 {
992 request.logout();
993 }
994
995 //This try/catch block catches when the login request fails (e.g. The user enters an incorrect password).
996 try
997 {
998 //Try a global login first
999 password = Authentication.hashPassword(password);
1000 request.login(username, password);
1001 }
1002 catch (Exception ex)
1003 {
1004 try
1005 {
1006 //If the global login fails then try a site-level login
1007 String siteName = (String) this.recept.getConfigParams().get(GSConstants.SITE_NAME);
1008 request.login(siteName + "-" + username, password);
1009
1010 }
1011 catch (Exception exc)
1012 {
1013 //The user entered in either the wrong username or the wrong password
1014
1015 HttpSession session = request.getSession();
1016 ServletContext context = session.getServletContext();
1017 String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
1018
1019 if ((request.getQueryString() != null) && (request.getQueryString().length() > 0))
1020 {
1021 redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
1022 }
1023
1024 generateLoginPage(redirect_url_string, getTextString("auth.error.un_or_pw_err", lang), userContext, out, baseURL, output);
1025 return false;
1026 }
1027 }
1028 }
1029 return true;
1030
1031
1032 }
1033
1034 private void updateUserContextWithAuthenticatedInfo(HttpServletRequest request, UserContext userContext)
1035 {
1036
1037 //Get the username
1038 String user_name = request.getUserPrincipal().getName();
1039 userContext.setUsername(user_name);
1040
1041 //Get the groups for the user
1042 Document msg_doc = XMLConverter.newDOM();
1043 Element userInfoMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1044 Element userInfoRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PROCESS, "GetUserInformation", userContext);
1045 userInfoMessage.appendChild(userInfoRequest);
1046
1047 Element paramList = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1048 userInfoRequest.appendChild(paramList);
1049 paramList.appendChild(GSXML.createParameter(msg_doc, GSXML.USERNAME_ATT, user_name));
1050
1051 Element userInfoResponseMessage = (Element) this.recept.process(userInfoMessage);
1052 Element userInfoResponse = (Element) GSXML.getChildByTagName(userInfoResponseMessage, GSXML.RESPONSE_ELEM);
1053 Element respParamList = (Element) GSXML.getChildByTagName(userInfoResponse, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1054
1055 if (respParamList != null)
1056 {
1057 HashMap<String, Serializable> params = GSXML.extractParams(respParamList, false);
1058 String groups = (String) params.get("groups");
1059 String editEnabled = (String) params.get("editEnabled");
1060 userContext.setGroups(groups.split(","));
1061 userContext.setEditEnabled((editEnabled != null) ? editEnabled : "false");
1062 }
1063 }
1064
1065 private void appendUserInformation(Element xml_request, UserContext userContext)
1066 {
1067 Element userInformation = xml_request.getOwnerDocument().createElement(GSXML.USER_INFORMATION_ELEM);
1068 userInformation.setAttribute(GSXML.USERNAME_ATT, userContext.getUsername());
1069
1070
1071 userInformation.setAttribute(GSXML.GROUPS_ATT, userContext.getGroupsString());
1072 userInformation.setAttribute(GSXML.EDIT_ENABLED_ATT, userContext.getEditEnabled());
1073 xml_request.appendChild(userInformation);
1074 }
1075
1076
1077 private boolean runCollectionSecurityCheck(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document, String lang, String output) {
1078
1079 //Get the security info for this collection
1080 Document msg_doc = XMLConverter.newDOM();
1081 Element securityMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1082 Element securityRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_SECURITY, collection, userContext);
1083 securityMessage.appendChild(securityRequest);
1084 if (document != null && !document.equals(""))
1085 {
1086 securityRequest.setAttribute(GSXML.NODE_OID, document);
1087 }
1088
1089 Element securityResponse = (Element) GSXML.getChildByTagName(this.recept.process(securityMessage), GSXML.RESPONSE_ELEM);
1090 if (securityResponse == null)
1091 {
1092 return false;
1093 }
1094
1095 // Groups mentioned in collectionConfig.xml's Security element, se_groups
1096 ArrayList<String> se_groups = GSXML.getGroupsFromSecurityResponse(securityResponse);
1097
1098 //If guests are not allowed to access this page then check to see if the user is in a group that is allowed to access the page
1099 if (!se_groups.contains(""))
1100 {
1101 boolean found = false;
1102 for (String se_group : se_groups)
1103 {
1104 if (request.isUserInRole(se_group))
1105 {
1106 found = true;
1107 break;
1108 }
1109 }
1110
1111 //The current user is not allowed to access the page so produce a login page
1112 if (!found)
1113 {
1114
1115 String error_message = "";
1116 if (request.getAuthType() == null)
1117 {
1118 error_message = getTextString("auth.error.login", lang);
1119 }
1120 else
1121 {
1122 error_message = getTextString("auth.error.incorrect_login", lang);
1123 }
1124
1125 HttpSession session = request.getSession();
1126 ServletContext context = session.getServletContext();
1127 String redirect_url_string = request.getRequestURI().replaceFirst(context.getContextPath() + "/", "");
1128
1129 if (request.getQueryString() != null && request.getQueryString().length() > 0)
1130 {
1131 redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
1132 }
1133
1134 generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
1135 return false;
1136 }
1137 }
1138 return true;
1139 }
1140
1141 private String getTextString(String key, String lang) {
1142 return Dictionary.getTextString(key, lang, null, null, null, null, null);
1143
1144 }
1145 //a debugging method
1146 private void displaySize(Hashtable<String, UserSessionCache> table)
1147 {
1148 if (table == null)
1149 {
1150 //logger.info("cached table is null");
1151 return;
1152 }
1153 if (table.size() == 0)
1154 {
1155 //logger.info("cached table size is zero");
1156 return;
1157 }
1158 int num_cached_coll = 0;
1159 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(table.values());
1160 for (int i = 0; i < cache_list.size(); i++)
1161 {
1162 num_cached_coll += cache_list.get(i).tableSize();
1163 }
1164 //logger.info("Number of sessions : total number of cached collection info = " + table.size() + " : " + num_cached_coll);
1165 }
1166
1167 /** merely a debugging method! */
1168 private String tableToString(Hashtable<String, Hashtable<String, String>> table)
1169 {
1170 String str = "";
1171 Enumeration<String> keys = table.keys();
1172 while (keys.hasMoreElements())
1173 {
1174 String name = keys.nextElement();
1175 str += name + ", ";
1176 }
1177 return str;
1178 }
1179
1180 /**
1181 * this goes through each URL and adds in a session id if needed-- its
1182 * needed if the browser doesn't accept cookies also escapes things if
1183 * needed
1184 */
1185 protected void encodeURLs(Node dataNode, HttpServletResponse response)
1186 {
1187 if (dataNode == null)
1188 {
1189 return;
1190 }
1191
1192 Element data = null;
1193
1194 short nodeType = dataNode.getNodeType();
1195 if (nodeType == Node.DOCUMENT_NODE)
1196 {
1197 Document docNode = (Document) dataNode;
1198 data = docNode.getDocumentElement();
1199 }
1200 else
1201 {
1202 data = (Element) dataNode;
1203 }
1204
1205 if (data != null)
1206 {
1207
1208 // get all the <a> elements
1209 NodeList hrefs = data.getElementsByTagName("a");
1210 // Instead of calculating each iteration...
1211 int hrefscount = hrefs.getLength();
1212
1213 for (int i = 0; hrefs != null && i < hrefscount; i++)
1214 {
1215 Element a = (Element) hrefs.item(i);
1216 // ugly hack to get rid of : in the args - interferes with session handling
1217 String href = a.getAttribute("href");
1218 if (!href.equals(""))
1219 {
1220 if (href.indexOf("?") != -1)
1221 {
1222 String[] parts = StringUtils.split(href, "\\?", -1);
1223 if (parts.length == 1)
1224 {
1225 parts[0] = StringUtils.replace(parts[0], ":", "%3A");
1226 href = "?" + parts[0];
1227 }
1228 else
1229 {
1230 parts[1] = StringUtils.replace(parts[1], ":", "%3A");
1231 href = parts[0] + "?" + parts[1];
1232 }
1233
1234 }
1235 a.setAttribute("href", response.encodeURL(href));
1236 }
1237 }
1238
1239 // now find any submit bits - get all the <form> elements
1240 NodeList forms = data.getElementsByTagName("form");
1241 int formscount = forms.getLength();
1242 for (int i = 0; forms != null && i < formscount; i++)
1243 {
1244 Element form = (Element) forms.item(i);
1245 form.setAttribute("action", response.encodeURL(form.getAttribute("action")));
1246 }
1247 // are these the only cases where URLs occur??
1248 // we should only do this for greenstone urls?
1249 }
1250
1251 }
1252
1253 protected String getFirstParam(String name, Map<String, String[]> map)
1254 {
1255 String[] val = map.get(name);
1256 if (val == null || val.length == 0)
1257 {
1258 return null;
1259 }
1260
1261 return val[0];
1262 }
1263
1264 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
1265 {
1266 //Check if we need to process a file upload
1267 if (ServletFileUpload.isMultipartContent(request))
1268 {
1269 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
1270
1271 int sizeLimit = System.getProperties().containsKey("servlet.upload.filesize.limit") ? Integer.parseInt(System.getProperty("servlet.upload.filesize.limit")) : 100 * 1024 * 1024;
1272
1273 File tempDir = new File(GlobalProperties.getGSDL3Home() + File.separator + "tmp");
1274 if (!tempDir.exists())
1275 {
1276 tempDir.mkdirs();
1277 }
1278
1279 //We want all files to be stored on disk (hence the 0)
1280 fileItemFactory.setSizeThreshold(0);
1281 fileItemFactory.setRepository(tempDir);
1282
1283 ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);
1284 uploadHandler.setFileSizeMax(sizeLimit);
1285
1286 HashMap<String, String[]> queryMap = new HashMap<String, String[]>();
1287 try
1288 {
1289 List items = uploadHandler.parseRequest(request);
1290 Iterator iter = items.iterator();
1291 while (iter.hasNext())
1292 {
1293 FileItem current = (FileItem) iter.next();
1294 if (current.isFormField())
1295 {
1296 queryMap.put(current.getFieldName(), new String[] { current.getString() });
1297 }
1298 else if (current.getName() != null && !current.getName().equals(""))
1299 {
1300 File file = new File(tempDir, current.getName());
1301 current.write(file);
1302
1303 queryMap.put("md___ex.Filesize", new String[] { "" + file.length() });
1304 queryMap.put("md___ex.Filename", new String[] { "" + current.getName() });
1305 }
1306 }
1307 }
1308 catch (Exception e)
1309 {
1310 e.printStackTrace();
1311 }
1312
1313 doGetOrPost(request, response, queryMap);
1314 }
1315 else
1316 {
1317 doGetOrPost(request, response, request.getParameterMap());
1318 }
1319 }
1320}
Note: See TracBrowser for help on using the repository browser.