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

Last change on this file since 34086 was 34086, checked in by davidb, 4 years ago

Refactoring code to have generateRedirect method needed some additional work to correctly handle setting RedirectURL in both cases where the method is called

  • Property svn:keywords set to Author Date Id Revision
File size: 42.7 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).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).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).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 if (requestedURL.indexOf(library_name) != -1)
465 {
466 baseURL = requestedURL.substring(0, requestedURL.indexOf(library_name));
467
468 int protocol_boundary_pos = baseURL.indexOf("://");
469 if (protocol_boundary_pos>=1) {
470 baseURL = baseURL.substring(protocol_boundary_pos+1); // change things like http:// or https:// to //
471 }
472
473 }
474 String fullURL;
475 if (request.getQueryString() != null)
476 {
477 fullURL = requestedURL + "?" + request.getQueryString();
478 }
479 else
480 {
481 fullURL = requestedURL;
482 }
483
484 UserContext userContext = new UserContext();
485 userContext.setLanguage(lang);
486 userContext.setUserID(uid);
487
488 if (!processLoginChanges(request, userContext, out, baseURL, queryMap, lang, output)) {
489 // any invalid login attempt will redirect to a new login page and return false
490 return;
491 }
492
493
494 if (request.getAuthType() != null)
495 {
496 // sets username, groups etc into usercontext
497 updateUserContextWithAuthenticatedInfo(request, userContext);
498 }
499
500 // If server output, force a switch to traditional interface
501 //output = (output.equals("server")) ? "html" : output;
502
503 // Force change the output mode if client-side XSLT is supported - server vs. client
504 // BUT only if the library allows client-side transforms
505 if (supports_client_xslt)
506 {
507 // MUST be done before the xml_message is built
508 Cookie[] cookies = request.getCookies();
509 Cookie xsltCookie = null;
510
511 // The client has cookies enabled and a value set - use it!
512 if (cookies != null)
513 {
514 for (Cookie c : cookies)
515 {
516 if (c.getName().equals("supportsXSLT"))
517 {
518 xsltCookie = c;
519 break;
520 }
521 }
522 output = (xsltCookie != null && xsltCookie.getValue().equals("true") && output.equals("html")) ? "xsltclient" : output;
523 }
524 }
525
526
527 String action = getFirstParam(GSParams.ACTION, queryMap);
528 String subaction = getFirstParam(GSParams.SUBACTION, queryMap);
529 String collection = getFirstParam(GSParams.COLLECTION, queryMap);
530 String document = getFirstParam(GSParams.DOCUMENT, queryMap);
531 String service = getFirstParam(GSParams.SERVICE, queryMap);
532 String specified_cache_key = getFirstParam(GSParams.CACHE_KEY, queryMap);
533
534 if (collection != null && !collection.equals("")) {
535 //is this user allowed to access this collection/document?
536 if (!runCollectionSecurityCheck(request, userContext, out, baseURL, collection, document, lang, output)) {
537 return;
538 }
539 }
540 // We clean up the cache session_ids_table if system
541 // commands are issued, and also don't need to do caching for these request requests
542 boolean should_cache = true;
543 if (action != null && action.equals(GSParams.SYSTEM_ACTION)
544 && !subaction.equals(GSXML.SYSTEM_TYPE_PING)
545 && !subaction.equals(GSXML.SYSTEM_TYPE_AUTHENTICATED_PING)) // don't 'clean' anything on a mere ping
546 {
547 should_cache = false;
548
549 // we may want to remove all collection cache info, or just a specific collection
550 boolean clean_all = true;
551 String clean_collection = null;
552 // system commands are to activate/deactivate stuff
553 // collection param is in the sc parameter.
554 // don't like the fact that it is hard coded here
555 String coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
556 if (coll != null && !coll.equals(""))
557 {
558 clean_all = false;
559 clean_collection = coll;
560 }
561 else
562 {
563 // check other system types
564 if (subaction.equals("a") || subaction.equals("d"))
565 {
566 String module_name = getFirstParam("sn", queryMap);
567 if (module_name != null && !module_name.equals(""))
568 {
569 clean_all = false;
570 clean_collection = module_name;
571 }
572 }
573 }
574 if (clean_all)
575 {
576 // TODO
577 session_ids_table = new Hashtable<String, UserSessionCache>();
578 session.removeAttribute(GSParams.USER_SESSION_CACHE); // triggers valueUnbound(), which removes the session id from the session_ids_table
579 }
580 else
581 {
582 // just clean up info for clean_collection
583 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(session_ids_table.values());
584 for (int i = 0; i < cache_list.size(); i++)
585 {
586 UserSessionCache cache = cache_list.get(i);
587 cache.cleanupCache(clean_collection);
588 }
589
590 }
591 }
592
593 // cache_key is the collection name, or service name
594 String cache_key = specified_cache_key;
595 if (cache_key == null || cache_key.equals(""))
596 {
597 cache_key = collection;
598 }
599 if (cache_key == null || cache_key.equals(""))
600 {
601 cache_key = service;
602 }
603
604 //clear the collection-specific cache in the session, since we have no way to know whether this page is
605 //about the same collection as the last page or not.
606 Enumeration attributeNames = session.getAttributeNames();
607 while (attributeNames.hasMoreElements())
608 {
609 String name = (String) attributeNames.nextElement();
610 if (!name.equals(GSParams.USER_SESSION_CACHE) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT) && !this.gs_params.isGlobal(name))
611 {
612 session.removeAttribute(name);
613 }
614 }
615
616 UserSessionCache session_cache = null;
617 Hashtable<String, Hashtable<String, String>> param_table = null;
618 Hashtable<String, String> table = null;
619 boolean new_table = false;
620 String sid = session.getId();
621 boolean new_session = false;
622 if (!session_ids_table.containsKey(sid)) {
623 new_session = true;
624 }
625 if (should_cache == true && cache_key != null && !cache_key.equals(""))
626 {
627 if (!new_session)
628 {
629 session_cache = session_ids_table.get(sid);
630 param_table = session_cache.getParamsTable();
631 if (param_table.containsKey(cache_key))
632 {
633 table = param_table.get(cache_key);
634 }
635 else
636 {
637 table = new Hashtable<String, String>();
638 param_table.put(cache_key, table);
639 new_table = true;
640 }
641 }
642 else
643 {
644
645 param_table = new Hashtable<String, Hashtable<String, String>>();
646 table = new Hashtable<String, String>();
647 param_table.put(cache_key, table);
648 session_cache = new UserSessionCache(sid, param_table);
649 session_ids_table.put(sid, session_cache);
650 session.setAttribute(GSParams.USER_SESSION_CACHE, session_cache);
651 new_table = true;
652 }
653
654 }
655
656 // here we add in default params values if need be - if we have a new session, or if we have a new table
657 // in an existing session
658 // don't override any existing values
659 if (new_session || new_table) {
660
661 Iterator i = this.gs_params.getParamsWithDefaults().iterator();
662 while (i.hasNext()) {
663 String p = (String)i.next();
664 String v = this.gs_params.getParamDefault(p);
665 if (this.gs_params.isGlobal(p)) {
666 //need to add in to session unless its already there
667 if (session.getAttribute(p) == null) {
668 session.setAttribute(p, v);
669 }
670 } else {
671 // add to table unless its already there
672 if (new_table && !table.containsKey(p)) {
673 table.put(p,v);
674 }
675 }
676
677 }
678 }
679
680 // the request to the receptionist
681 Document msg_doc = XMLConverter.newDOM();
682 Element xml_message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
683 Element xml_request = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
684 xml_request.setAttribute(GSXML.OUTPUT_ATT, output);
685
686 xml_message.appendChild(xml_request);
687
688 if (request.getAuthType() != null) {
689 // 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?
690 appendUserInformation(xml_request, userContext);
691 }
692
693 if (action == null || action.equals(""))
694 {
695 // display the home page - the default page
696 xml_request.setAttribute(GSXML.ACTION_ATT, "p");
697 xml_request.setAttribute(GSXML.SUBACTION_ATT, PageAction.HOME_PAGE);
698 }
699 else
700 {
701 xml_request.setAttribute(GSXML.ACTION_ATT, action);
702 if (subaction != null)
703 {
704 xml_request.setAttribute(GSXML.SUBACTION_ATT, subaction);
705 }
706 }
707
708 // create the param list for the greenstone request - includes
709 // the params from the current request and any others from the saved session
710 Element xml_param_list = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
711 xml_request.appendChild(xml_param_list);
712
713 if (queryMap.containsKey("s1.collection") || queryMap.containsKey("s1.group")){
714 table.remove("s1.collection");
715 table.remove("s1.group");
716 }
717 for (String name : queryMap.keySet())
718 {
719 if (!name.equals(GSParams.ACTION) && !name.equals(GSParams.SUBACTION) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSParams.OUTPUT))
720 {// we have already dealt with these
721
722 String value = "";
723 String[] values = queryMap.get(name);
724 value = values[0];
725 if (values.length > 1)
726 {
727 for (int i = 1; i < values.length; i++)
728 {
729 value += "," + values[i];
730 }
731 }
732 // if we need to save the value, then add it to the session/cache table.
733 // otherwise add directly to the paramList
734 if (this.gs_params.shouldSave(name)) {
735 if (this.gs_params.isGlobal(name) || table == null) {
736 session.setAttribute(name, value);
737 } else {
738 table.put(name, value);
739 }
740 }
741 else
742 {
743 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
744 param.setAttribute(GSXML.NAME_ATT, name);
745 param.setAttribute(GSXML.VALUE_ATT, GSXML.xmlSafe(value));
746 if (this.gs_params.isSensitive(name)) {
747 param.setAttribute(GSXML.SENSITIVE_ATT, "true");
748 }
749 xml_param_list.appendChild(param);
750
751 }
752 }
753 }
754 //put everything in the table into the session
755 if (table != null)
756 {
757 Enumeration<String> keys = table.keys();
758 while (keys.hasMoreElements())
759 {
760 String name = keys.nextElement();
761 session.setAttribute(name, table.get(name));
762 }
763 }
764
765 // put in all the params from the session cache
766 Enumeration params = session.getAttributeNames();
767 while (params.hasMoreElements())
768 {
769 String name = (String) params.nextElement();
770 if (!name.equals(GSParams.USER_SESSION_CACHE) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
771 {
772
773 // lang and uid are stored but we dont want it in the param list cos its already in the request
774 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
775 param.setAttribute(GSXML.NAME_ATT, name);
776 String value = GSXML.xmlSafe((String) session.getAttribute(name));
777
778 // ugly hack to undo : escaping
779 value = StringUtils.replace(value, "%3A", "\\:");
780 param.setAttribute(GSXML.VALUE_ATT, value);
781 xml_param_list.appendChild(param);
782 }
783 }
784
785
786 if (output.equals("json"))
787 {
788 response.setContentType("application/json");
789 }
790 else if (!output.equals("html") && !output.equals("server") && !output.equals("xsltclient"))
791 {
792 response.setContentType("text/xml"); // for now use text
793 }
794
795 //Add custom HTTP headers if requested
796 String httpHeadersParam = getFirstParam(GSParams.HTTP_HEADER_FIELDS, queryMap);
797 if (httpHeadersParam != null && httpHeadersParam.length() > 0)
798 {
799 Gson gson = new Gson();
800 Type type = new TypeToken<List<Map<String, String>>>()
801 {
802 }.getType();
803 List<Map<String, String>> httpHeaders = gson.fromJson(httpHeadersParam, type);
804 if (httpHeaders != null && httpHeaders.size() > 0)
805 {
806
807 for (int j = 0; j < httpHeaders.size(); j++)
808 {
809 Map nameValueMap = httpHeaders.get(j);
810 String name = (String) nameValueMap.get("name");
811 String value = (String) nameValueMap.get("value");
812
813 if (name != null && value != null)
814 {
815 response.setHeader(name, value);
816 }
817 }
818 }
819 }
820
821
822 xml_request.setAttribute("remoteAddress", request.getRemoteAddr());
823 xml_request.setAttribute("fullURL", fullURL.replace("&", "&amp;"));
824 xml_request.setAttribute("baseURL", baseURL);
825
826 logger.error("about to process this message");
827 logger.error(XMLConverter.getPrettyString(xml_message));
828 Node xml_result = this.recept.process(xml_message);
829 encodeURLs(xml_result, response);
830
831 String xml_string = XMLConverter.getPrettyString(xml_result);
832
833 if (output.equals("json"))
834 {
835 try
836 {
837 JSONObject json_obj = org.json.XML.toJSONObject(xml_string);
838
839 out.println(json_obj.toString());
840 }
841 catch (Exception e)
842 {
843 e.printStackTrace();
844 out.println("Error: failed to convert output XML to JSON format");
845 }
846 }
847 else
848 {
849 out.println(xml_string);
850 }
851
852 displaySize(session_ids_table);
853 }
854
855 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
856 {
857 doGetOrPost(request, response, request.getParameterMap());
858 } //end of doGet(HttpServletRequest, HttpServletResponse)
859
860 private boolean processRedirectRequest(HttpServletRequest request, HttpServletResponse response, Map<String, String[]> queryMap) throws IOException
861 {
862 if (queryMap != null)
863 {
864 Iterator<String> queryIter = queryMap.keySet().iterator();
865 boolean redirect = false;
866 String href = null;
867 String rl = null;
868 String el = null;
869 String collection = null;
870
871 while (queryIter.hasNext())
872 {
873 String q = queryIter.next();
874 if (q.equals(GSParams.COLLECTION))
875 {
876 collection = queryMap.get(q)[0];
877 }
878 else if (q.equals(GSParams.EXTERNAL_LINK_TYPE))
879 {
880 el = queryMap.get(q)[0];
881 }
882 else if (q.equals(GSParams.HREF))
883 {
884 href = queryMap.get(q)[0];
885 href = StringUtils.replace(href, "%2f", "/");
886 href = StringUtils.replace(href, "%7e", "~");
887 href = StringUtils.replace(href, "%3f", "?");
888 href = StringUtils.replace(href, "%3A", "\\:");
889 }
890 else if (q.equals(GSParams.RELATIVE_LINK))
891 {
892 rl = queryMap.get(q)[0];
893 }
894 }
895
896 //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
897 //"rl=0" this is an external link
898 //"rl=1" this is an internal link
899 if ((href != null) && (rl.equals("0")))
900 {// This is an external link,
901
902 if (el.equals("framed"))
903 {
904 // framed means we are linking to an external page inside our greenstone page
905 HttpSession session = request.getSession();
906 ServletContext context = session.getServletContext();
907 String new_url = context.getContextPath()+"/"+ library_name+"?a=p&sa=html&url="+href;
908 if (collection != null && !collection.equals("")) {
909 new_url += "&c="+collection;
910 }
911 response.sendRedirect(new_url);
912 }
913 else
914 {
915 // el = '' or direct
916 //the web page is re-directed to the external URL (&el=&rl=0&href="http://...")
917 response.sendRedirect(href);
918 }
919 return true;
920 }
921 }
922 return false;
923 }
924
925 private void generateLoginPage(String redirect_url_string, String error_message, UserContext userContext, PrintWriter out, String baseURL, String output) {
926
927 Document loginPageDoc = XMLConverter.newDOM();
928 Element loginPageMessage = loginPageDoc.createElement(GSXML.MESSAGE_ELEM);
929 Element loginPageRequest = GSXML.createBasicRequest(loginPageDoc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
930 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
931 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
932 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, output);
933 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
934
935 loginPageMessage.appendChild(loginPageRequest);
936
937 Element paramList = loginPageDoc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
938 loginPageRequest.appendChild(paramList);
939
940 Element messageParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
941 messageParam.setAttribute(GSXML.NAME_ATT, LOGIN_MESSAGE_PARAM);
942 messageParam.setAttribute(GSXML.VALUE_ATT, error_message);
943 paramList.appendChild(messageParam);
944
945 Element urlParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
946 urlParam.setAttribute(GSXML.NAME_ATT, REDIRECT_URL_PARAM);
947
948 if (redirect_url_string.matches("^[a-z]+://.*$")) {
949 int protocol_boundary_pos = redirect_url_string.indexOf("://");
950 redirect_url_string = redirect_url_string.substring(protocol_boundary_pos+1); // change things like http:// or https:// to //
951 }
952
953 urlParam.setAttribute(GSXML.VALUE_ATT, redirect_url_string);
954 paramList.appendChild(urlParam);
955
956 Node loginPageResponse = this.recept.process(loginPageMessage);
957 out.println(XMLConverter.getPrettyString(loginPageResponse));
958 }
959
960 private boolean processLoginChanges(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, Map<String, String[]> queryMap, String lang, String output) throws ServletException {
961
962 logger.error("kk in processLoginChanges");
963 //Check if we need to login or logout
964 String username = getFirstParam(GSParams.USERNAME, queryMap);
965 String password = getFirstParam(GSParams.PASSWORD, queryMap);
966 String logout = getFirstParam(GSParams.LOGOUT, queryMap);
967
968 if (logout != null)
969 {
970 logger.error("plc logging out");
971 request.logout();
972 }
973
974 if (username != null && password != null)
975 {
976 logger.error("kk plc u&p not null");
977 //We are changing to another user, so log out first
978 if (request.getAuthType() != null)
979 {
980 logger.error("plc logging out 2");
981 request.logout();
982 }
983
984 //This try/catch block catches when the login request fails (e.g. The user enters an incorrect password).
985 try
986 {
987 //Try a global login first
988 password = Authentication.hashPassword(password);
989 request.login(username, password);
990 logger.error("plc logged in");
991 }
992 catch (Exception ex)
993 {
994 try
995 {
996 //If the global login fails then try a site-level login
997 String siteName = (String) this.recept.getConfigParams().get(GSConstants.SITE_NAME);
998 request.login(siteName + "-" + username, password);
999 logger.error("plc logged in site wide");
1000 }
1001 catch (Exception exc)
1002 {
1003 //The user entered in either the wrong username or the wrong password
1004 String redirect_url_string = library_name;
1005 if (request.getQueryString() != null) // && request.getQueryString().length() > 0)
1006 {
1007 redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
1008 }
1009
1010 generateLoginPage(redirect_url_string, getTextString("auth.error.un_or_pw_err", lang), userContext, out, baseURL, output);
1011 return false;
1012 }
1013 }
1014 }
1015 return true;
1016
1017
1018 }
1019
1020 private void updateUserContextWithAuthenticatedInfo(HttpServletRequest request, UserContext userContext)
1021 {
1022 logger.error("in updateUserContext");
1023 //Get the username
1024 String user_name = request.getUserPrincipal().getName();
1025 userContext.setUsername(user_name);
1026 logger.error("setting username = "+request.getUserPrincipal().getName());
1027 //Get the groups for the user
1028 Document msg_doc = XMLConverter.newDOM();
1029 Element userInfoMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1030 Element userInfoRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_PROCESS, "GetUserInformation", userContext);
1031 userInfoMessage.appendChild(userInfoRequest);
1032
1033 Element paramList = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1034 userInfoRequest.appendChild(paramList);
1035 paramList.appendChild(GSXML.createParameter(msg_doc, GSXML.USERNAME_ATT, user_name));
1036
1037 Element userInfoResponseMessage = (Element) this.recept.process(userInfoMessage);
1038 Element userInfoResponse = (Element) GSXML.getChildByTagName(userInfoResponseMessage, GSXML.RESPONSE_ELEM);
1039 Element respParamList = (Element) GSXML.getChildByTagName(userInfoResponse, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1040
1041 if (respParamList != null)
1042 {
1043 HashMap<String, Serializable> params = GSXML.extractParams(respParamList, false);
1044 String groups = (String) params.get("groups");
1045 String editEnabled = (String) params.get("editEnabled");
1046 userContext.setGroups(groups.split(","));
1047 userContext.setEditEnabled((editEnabled != null) ? editEnabled : "false");
1048 }
1049 }
1050
1051 private void appendUserInformation(Element xml_request, UserContext userContext)
1052 {
1053 Element userInformation = xml_request.getOwnerDocument().createElement(GSXML.USER_INFORMATION_ELEM);
1054 userInformation.setAttribute(GSXML.USERNAME_ATT, userContext.getUsername());
1055
1056
1057 userInformation.setAttribute(GSXML.GROUPS_ATT, userContext.getGroupsString());
1058 userInformation.setAttribute(GSXML.EDIT_ENABLED_ATT, userContext.getEditEnabled());
1059 xml_request.appendChild(userInformation);
1060 }
1061
1062
1063 private boolean runCollectionSecurityCheck(HttpServletRequest request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document, String lang, String output) {
1064 logger.error("kk in a coll"+collection);
1065 //Get the security info for this collection
1066 Document msg_doc = XMLConverter.newDOM();
1067 Element securityMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
1068 Element securityRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_SECURITY, collection, userContext);
1069 securityMessage.appendChild(securityRequest);
1070 if (document != null && !document.equals(""))
1071 {
1072 securityRequest.setAttribute(GSXML.NODE_OID, document);
1073 }
1074
1075 Element securityResponse = (Element) GSXML.getChildByTagName(this.recept.process(securityMessage), GSXML.RESPONSE_ELEM);
1076 if (securityResponse == null)
1077 {
1078 return false;
1079 }
1080
1081 ArrayList<String> groups = GSXML.getGroupsFromSecurityResponse(securityResponse);
1082
1083 //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
1084 if (!groups.contains(""))
1085 {
1086 boolean found = false;
1087 for (String group : groups)
1088 {
1089 if (request.isUserInRole(group))
1090 {
1091 found = true;
1092 break;
1093 }
1094 }
1095
1096 //The current user is not allowed to access the page so produce a login page
1097 if (!found)
1098 {
1099 logger.error("kk current user not allowed to access");
1100 String error_message = "";
1101 if (request.getAuthType() == null)
1102 {
1103 error_message = getTextString("auth.error.login", lang);
1104 }
1105 else
1106 {
1107 error_message = getTextString("auth.error.incorrect_login", lang);
1108 }
1109 String redirect_url_string = request.getRequestURL().toString();
1110 if (request.getQueryString() != null && request.getQueryString().length() > 0)
1111 {
1112 redirect_url_string += "?" + request.getQueryString().replace("&", "&amp;");
1113 }
1114
1115 generateLoginPage(redirect_url_string, error_message, userContext, out, baseURL, output);
1116 return false;
1117 }
1118 }
1119 return true;
1120 }
1121
1122 private String getTextString(String key, String lang) {
1123 return Dictionary.getTextString(key, lang, null, null, null, null, null);
1124
1125 }
1126 //a debugging method
1127 private void displaySize(Hashtable<String, UserSessionCache> table)
1128 {
1129 if (table == null)
1130 {
1131 //logger.info("cached table is null");
1132 return;
1133 }
1134 if (table.size() == 0)
1135 {
1136 //logger.info("cached table size is zero");
1137 return;
1138 }
1139 int num_cached_coll = 0;
1140 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(table.values());
1141 for (int i = 0; i < cache_list.size(); i++)
1142 {
1143 num_cached_coll += cache_list.get(i).tableSize();
1144 }
1145 //logger.info("Number of sessions : total number of cached collection info = " + table.size() + " : " + num_cached_coll);
1146 }
1147
1148 /** merely a debugging method! */
1149 private String tableToString(Hashtable<String, Hashtable<String, String>> table)
1150 {
1151 String str = "";
1152 Enumeration<String> keys = table.keys();
1153 while (keys.hasMoreElements())
1154 {
1155 String name = keys.nextElement();
1156 str += name + ", ";
1157 }
1158 return str;
1159 }
1160
1161 /**
1162 * this goes through each URL and adds in a session id if needed-- its
1163 * needed if the browser doesn't accept cookies also escapes things if
1164 * needed
1165 */
1166 protected void encodeURLs(Node dataNode, HttpServletResponse response)
1167 {
1168 if (dataNode == null)
1169 {
1170 return;
1171 }
1172
1173 Element data = null;
1174
1175 short nodeType = dataNode.getNodeType();
1176 if (nodeType == Node.DOCUMENT_NODE)
1177 {
1178 Document docNode = (Document) dataNode;
1179 data = docNode.getDocumentElement();
1180 }
1181 else
1182 {
1183 data = (Element) dataNode;
1184 }
1185
1186 if (data != null)
1187 {
1188
1189 // get all the <a> elements
1190 NodeList hrefs = data.getElementsByTagName("a");
1191 // Instead of calculating each iteration...
1192 int hrefscount = hrefs.getLength();
1193
1194 for (int i = 0; hrefs != null && i < hrefscount; i++)
1195 {
1196 Element a = (Element) hrefs.item(i);
1197 // ugly hack to get rid of : in the args - interferes with session handling
1198 String href = a.getAttribute("href");
1199 if (!href.equals(""))
1200 {
1201 if (href.indexOf("?") != -1)
1202 {
1203 String[] parts = StringUtils.split(href, "\\?", -1);
1204 if (parts.length == 1)
1205 {
1206 parts[0] = StringUtils.replace(parts[0], ":", "%3A");
1207 href = "?" + parts[0];
1208 }
1209 else
1210 {
1211 parts[1] = StringUtils.replace(parts[1], ":", "%3A");
1212 href = parts[0] + "?" + parts[1];
1213 }
1214
1215 }
1216 a.setAttribute("href", response.encodeURL(href));
1217 }
1218 }
1219
1220 // now find any submit bits - get all the <form> elements
1221 NodeList forms = data.getElementsByTagName("form");
1222 int formscount = forms.getLength();
1223 for (int i = 0; forms != null && i < formscount; i++)
1224 {
1225 Element form = (Element) forms.item(i);
1226 form.setAttribute("action", response.encodeURL(form.getAttribute("action")));
1227 }
1228 // are these the only cases where URLs occur??
1229 // we should only do this for greenstone urls?
1230 }
1231
1232 }
1233
1234 protected String getFirstParam(String name, Map<String, String[]> map)
1235 {
1236 String[] val = map.get(name);
1237 if (val == null || val.length == 0)
1238 {
1239 return null;
1240 }
1241
1242 return val[0];
1243 }
1244
1245 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
1246 {
1247 //Check if we need to process a file upload
1248 if (ServletFileUpload.isMultipartContent(request))
1249 {
1250 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
1251
1252 int sizeLimit = System.getProperties().containsKey("servlet.upload.filesize.limit") ? Integer.parseInt(System.getProperty("servlet.upload.filesize.limit")) : 100 * 1024 * 1024;
1253
1254 File tempDir = new File(GlobalProperties.getGSDL3Home() + File.separator + "tmp");
1255 if (!tempDir.exists())
1256 {
1257 tempDir.mkdirs();
1258 }
1259
1260 //We want all files to be stored on disk (hence the 0)
1261 fileItemFactory.setSizeThreshold(0);
1262 fileItemFactory.setRepository(tempDir);
1263
1264 ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);
1265 uploadHandler.setFileSizeMax(sizeLimit);
1266
1267 HashMap<String, String[]> queryMap = new HashMap<String, String[]>();
1268 try
1269 {
1270 List items = uploadHandler.parseRequest(request);
1271 Iterator iter = items.iterator();
1272 while (iter.hasNext())
1273 {
1274 FileItem current = (FileItem) iter.next();
1275 if (current.isFormField())
1276 {
1277 queryMap.put(current.getFieldName(), new String[] { current.getString() });
1278 }
1279 else if (current.getName() != null && !current.getName().equals(""))
1280 {
1281 File file = new File(tempDir, current.getName());
1282 current.write(file);
1283
1284 queryMap.put("md___ex.Filesize", new String[] { "" + file.length() });
1285 queryMap.put("md___ex.Filename", new String[] { "" + current.getName() });
1286 }
1287 }
1288 }
1289 catch (Exception e)
1290 {
1291 e.printStackTrace();
1292 }
1293
1294 doGetOrPost(request, response, queryMap);
1295 }
1296 else
1297 {
1298 doGetOrPost(request, response, request.getParameterMap());
1299 }
1300 }
1301}
Note: See TracBrowser for help on using the repository browser.