/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.app.webui.util;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.mail.internet.MimeUtility;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.jstl.core.Config;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.dspace.app.util.Util;
import org.dspace.authenticate.AuthenticationManager;
import org.dspace.content.Collection;
import org.dspace.content.Community;
import org.dspace.content.DCDate;
import org.dspace.core.ConfigurationManager;
import org.dspace.core.Constants;
import org.dspace.core.Context;
import org.dspace.core.Email;
import org.dspace.core.I18nUtil;
import org.dspace.eperson.EPerson;
/**
* Miscellaneous UI utility methods
*
* @author Robert Tansley
* @version $Revision$
*/
public class UIUtil extends Util
{
/** Whether to look for x-forwarded headers for logging IP addresses */
private static Boolean useProxies;
/** log4j category */
public static final Logger log = Logger.getLogger(UIUtil.class);
/**
* Pattern used to get file.ext from filename (which can be a path)
*/
private static Pattern p = Pattern.compile("[^/]*$");
/**
* Obtain a new context object. If a context object has already been created
* for this HTTP request, it is re-used, otherwise it is created. If a user
* has authenticated with the system, the current user of the context is set
* appropriately.
*
* @param request
* the HTTP request
*
* @return a context object
*/
public static Context obtainContext(HttpServletRequest request)
throws SQLException
{
//Set encoding to UTF-8, if not set yet
//This avoids problems of using the HttpServletRequest
//in the getSpecialGroups() for an AuthenticationMethod,
//which causes the HttpServletRequest to default to
//non-UTF-8 encoding.
try
{
if(request.getCharacterEncoding()==null)
{
request.setCharacterEncoding(Constants.DEFAULT_ENCODING);
}
}
catch(Exception e)
{
log.error("Unable to set encoding to UTF-8.", e);
}
Context c = (Context) request.getAttribute("dspace.context");
if (c == null)
{
// No context for this request yet
c = new Context();
HttpSession session = request.getSession();
// See if a user has authentication
Integer userID = (Integer) session.getAttribute(
"dspace.current.user.id");
if (userID != null)
{
String remAddr = (String)session.getAttribute("dspace.current.remote.addr");
if (remAddr != null && remAddr.equals(request.getRemoteAddr()))
{
EPerson e = EPerson.find(c, userID.intValue());
Authenticate.loggedIn(c, request, e);
}
else
{
log.warn("POSSIBLE HIJACKED SESSION: request from "+
request.getRemoteAddr()+" does not match original "+
"session address: "+remAddr+". Authentication rejected.");
}
}
// Set any special groups - invoke the authentication mgr.
int[] groupIDs = AuthenticationManager.getSpecialGroups(c, request);
for (int i = 0; i < groupIDs.length; i++)
{
c.setSpecialGroup(groupIDs[i]);
log.debug("Adding Special Group id="+String.valueOf(groupIDs[i]));
}
// Set the session ID and IP address
String ip = request.getRemoteAddr();
if (useProxies == null) {
useProxies = ConfigurationManager.getBooleanProperty("useProxies", false);
}
if(useProxies && request.getHeader("X-Forwarded-For") != null)
{
/* This header is a comma delimited list */
for(String xfip : request.getHeader("X-Forwarded-For").split(","))
{
if(!request.getHeader("X-Forwarded-For").contains(ip))
{
ip = xfip.trim();
}
}
}
c.setExtraLogInfo("session_id=" + request.getSession().getId() + ":ip_addr=" + ip);
// Store the context in the request
request.setAttribute("dspace.context", c);
}
// Set the locale to be used
Locale sessionLocale = getSessionLocale(request);
Config.set(request.getSession(), Config.FMT_LOCALE, sessionLocale);
c.setCurrentLocale(sessionLocale);
return c;
}
/**
* Get the current community location, that is, where the user "is". This
* returns <code>null</code> if there is no location, i.e. "all of DSpace"
* is the location.
*
* @param request
* current HTTP request
*
* @return the current community location, or null
*/
public static Community getCommunityLocation(HttpServletRequest request)
{
return ((Community) request.getAttribute("dspace.community"));
}
/**
* Get the current collection location, that is, where the user "is". This
* returns null if there is no collection location, i.e. the location is
* "all of DSpace" or a community.
*
* @param request
* current HTTP request
*
* @return the current collection location, or null
*/
public static Collection getCollectionLocation(HttpServletRequest request)
{
return ((Collection) request.getAttribute("dspace.collection"));
}
/**
* Put the original request URL into the request object as an attribute for
* later use. This is necessary because forwarding a request removes this
* information. The attribute is only written if it hasn't been before; thus
* it can be called after a forward safely.
*
* @param request
* the HTTP request
*/
public static void storeOriginalURL(HttpServletRequest request)
{
String orig = (String) request.getAttribute("dspace.original.url");
if (orig == null)
{
String fullURL = request.getRequestURL().toString();
if (request.getQueryString() != null)
{
fullURL = fullURL + "?" + request.getQueryString();
}
request.setAttribute("dspace.original.url", fullURL);
}
}
/**
* Get the original request URL.
*
* @param request
* the HTTP request
*
* @return the original request URL
*/
public static String getOriginalURL(HttpServletRequest request)
{
// Make sure there's a URL in the attribute
storeOriginalURL(request);
return ((String) request.getAttribute("dspace.original.url"));
}
/**
* Write a human-readable version of a DCDate.
*
* @param d
* the date
* @param time
* if true, display the time with the date
* @param localTime
* if true, adjust for local timezone, otherwise GMT
* @param request
* the servlet request
*
* @return the date in a human-readable form.
*/
public static String displayDate(DCDate d, boolean time, boolean localTime, HttpServletRequest request)
{
return d.displayDate(time, localTime, getSessionLocale(request));
}
/**
* Return a string for logging, containing useful information about the
* current request - the URL, the method and parameters.
*
* @param request
* the request object.
* @return a multi-line string containing information about the request.
*/
public static String getRequestLogInfo(HttpServletRequest request)
{
StringBuilder report = new StringBuilder();
report.append("-- URL Was: ").append(getOriginalURL(request)).append("\n").toString();
report.append("-- Method: ").append(request.getMethod()).append("\n").toString();
// First write the parameters we had
report.append("-- Parameters were:\n");
Enumeration e = request.getParameterNames();
while (e.hasMoreElements())
{
String name = (String) e.nextElement();
if (name.equals("login_password"))
{
// We don't want to write a clear text password
// to the log, even if it's wrong!
report.append("-- ").append(name).append(": *not logged*\n").toString();
}
else
{
report.append("-- ").append(name).append(": \"")
.append(request.getParameter(name)).append("\"\n");
}
}
return report.toString();
}
/**
* Get the Locale for a session according to the user's language selection or language preferences.
* Order of selection
* - language selected via UI
* - language as set by application
* - language browser default
*
* @param request
* the request Object
* @return supportedLocale
* Locale supported by this DSpace Instance for this request
*/
public static Locale getSessionLocale(HttpServletRequest request)
{
String paramLocale = request.getParameter("locale");
Locale sessionLocale = null;
Locale supportedLocale = null;
if (!StringUtils.isEmpty(paramLocale))
{
/* get session locale according to user selection */
sessionLocale = new Locale(paramLocale);
}
if (sessionLocale == null)
{
/* get session locale set by application */
HttpSession session = request.getSession();
sessionLocale = (Locale) Config.get(session, Config.FMT_LOCALE);
}
/*
* if session not set by selection or application then default browser
* locale
*/
if (sessionLocale == null)
{
sessionLocale = request.getLocale();
}
if (sessionLocale == null)
{
sessionLocale = I18nUtil.DEFAULTLOCALE;
}
supportedLocale = I18nUtil.getSupportedLocale(sessionLocale);
return supportedLocale;
}
/**
* Send an alert to the designated "alert recipient" - that is, when a
* database error or internal error occurs, this person is sent an e-mail
* with details.
* <P>
* The recipient is configured via the "alert.recipient" property in
* <code>dspace.cfg</code>. If this property is omitted, no alerts are
* sent.
* <P>
* This method "swallows" any exception that might occur - it will just be
* logged. This is because this method will usually be invoked as part of an
* error handling routine anyway.
*
* @param request
* the HTTP request leading to the error
* @param exception
* the exception causing the error, or null
*/
public static void sendAlert(HttpServletRequest request, Exception exception)
{
String logInfo = UIUtil.getRequestLogInfo(request);
Context c = (Context) request.getAttribute("dspace.context");
Locale locale = getSessionLocale(request);
EPerson user = null;
try
{
String recipient = ConfigurationManager
.getProperty("alert.recipient");
if (recipient != null)
{
Email email = ConfigurationManager.getEmail(I18nUtil.getEmailFilename(locale, "internal_error"));
email.addRecipient(recipient);
email.addArgument(ConfigurationManager
.getProperty("dspace.url"));
email.addArgument(new Date());
email.addArgument(request.getSession().getId());
email.addArgument(logInfo);
String stackTrace;
if (exception != null)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
exception.printStackTrace(pw);
pw.flush();
stackTrace = sw.toString();
}
else
{
stackTrace = "No exception";
}
email.addArgument(stackTrace);
try
{
user = c.getCurrentUser();
}
catch (Exception e)
{
log.warn("No context, the database might be down or the connection pool exhausted.");
}
if (user != null)
{
email.addArgument(user.getFullName() + " (" + user.getEmail() + ")");
}
else
{
email.addArgument("Anonymous");
}
email.addArgument(request.getRemoteAddr());
email.send();
}
}
catch (Exception e)
{
// Not much we can do here!
log.warn("Unable to send email alert", e);
}
}
/**
* Evaluate filename and client and encode appropriate disposition
*
* @param filename
* @param request
* @param response
* @throws UnsupportedEncodingException
*/
public static void setBitstreamDisposition(String filename, HttpServletRequest request,
HttpServletResponse response)
{
String name = filename;
Matcher m = p.matcher(name);
if (m.find() && !m.group().equals(""))
{
name = m.group();
}
try
{
String agent = request.getHeader("USER-AGENT");
if (null != agent && -1 != agent.indexOf("MSIE"))
{
name = URLEncoder.encode(name, "UTF8");
}
else if (null != agent && -1 != agent.indexOf("Mozilla"))
{
name = MimeUtility.encodeText(name, "UTF8", "B");
}
}
catch (UnsupportedEncodingException e)
{
log.error(e.getMessage(),e);
}
finally
{
response.setHeader("Content-Disposition", "attachment;filename=" + name);
}
}
}