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

Last change on this file since 32939 was 32939, checked in by kjdon, 5 years ago

add LibraryName attribute to the servlet context so that URLFilter can access it

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