/** * $RCSfile: SkinUtils.java,v $ * $Revision: 1.8.2.4 $ * $Date: 2001/04/06 05:31:24 $ * * Copyright (C) 2000 CoolServlets.com. All rights reserved. * * =================================================================== * The Apache Software License, Version 1.1 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by * CoolServlets.com (http://www.coolservlets.com)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Jive" and "CoolServlets.com" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact webmaster@coolservlets.com. * * 5. Products derived from this software may not be called "Jive", * nor may "Jive" appear in their name, without prior written * permission of CoolServlets.com. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of CoolServlets.com. For more information * on CoolServlets.com, please see . */ package com.coolservlets.forum.util; import java.util.Date; import java.util.Iterator; import java.text.SimpleDateFormat; import java.io.File; import javax.servlet.*; import javax.servlet.http.*; import com.coolservlets.forum.*; import com.coolservlets.util.StringUtils; /** * A collection of utility methods for use in Jive WebSkins. Because these * methods make skin development much easier, skin authors should study them * carefully.

* * Three major areas of funtionality are provided:

    *
  1. Methods that simplify Authorization tasks: *
      *
    • {@link #getUserAuthorization(HttpServletRequest, HttpServletResponse)} *
    • {@link #getUserAuthorization(HttpServletRequest, HttpServletResponse, boolean)} *
    • {@link #setUserAuthorization(HttpServletRequest, HttpServletResponse, String, String, boolean)} *
    • {@link #removeUserAuthorization(HttpServletRequest, HttpServletResponse)} *
    • {@link #isSystemAdmin(Authorization)} *
    • {@link #isForumAdmin(Authorization)} *
    • {@link #isForumAdmin(Authorization, Forum)} *
    • {@link #isGroupAdmin(Authorization)} *
    • {@link #isGroupAdmin(Authorization, Group)} *
    *

    *

  2. Methods that get and set Session and cookie values. *
      *
    • {@link #getCookie(HttpServletRequest, String)} *
    • {@link #getCookieValue(HttpServletRequest, String)} *
    • {@link #invalidateCookie(HttpServletRequest, HttpServletResponse, String)} *
    • {@link #remove(HttpServletRequest, HttpServletResponse, String)} *
    • {@link #retrieve(HttpServletRequest, HttpServletResponse, String)} *
    • {@link #retrieve(HttpServletRequest, HttpServletResponse, String, boolean)} *
    • {@link #store(HttpServletRequest, HttpServletResponse, String, String)} *
    • {@link #store(HttpServletRequest, HttpServletResponse, String, String, int)} *
    • {@link #store(HttpServletRequest, HttpServletResponse, String, String, int boolean)} *
    *

    *

  3. Other methods. *
      *
    • {@link #dateToText(Date)} *
    • (@link #getLastVisisted(HttpServletRequest, HttpServletResponse)} *
    • (@link #getLastVisisted(HttpServletRequest, HttpServletResponse, boolean)} *
    • {@link #isNewMessage(ForumMessage, long)} *
    • (@link #quoteOriginal(ForumMessage, String, int)} *
    *
* * All methods conform to the Servlet 1.1 and JSP 1.0 specs for maximum * compatibility with application servers. This may yield deprecation warnings * if you compile with a newer Servlet/JSP spec; these should be ignored. This * class will periodically be updated to the newer specs as app servers mature. */ public class SkinUtils { /** Name of the authentication token (is stored in the user's session) */ public static final String JIVE_AUTH_TOKEN = "jiveAuthorization"; /** Name of the cookie used to store user info for auto-login purposes */ public static final String JIVE_AUTOLOGIN_COOKIE = "jiveAutoLogin"; /** Name of the last visited token (is stored in the user's session) */ public static final String JIVE_LASTVISITED_TOKEN = "jiveLastVisited"; /** Name of the cookie used to store last visited timestamp */ public static final String JIVE_LASTVISITED_COOKIE = "jiveLastVisited"; // XXX keep this ? /** Name of the "use last visited" property (is stored in jive.properties) */ public static final String JIVE_LASTVISITED_PROP = "Site.useLastVisited"; //Time constants (in milliseconds) private static final long SECOND = 1000; private static final long MINUTE = 60 * SECOND; private static final long HOUR = 60 * MINUTE; private static final long DAY = 24 * HOUR; private static final long WEEK = 7 * DAY; //Default cookie time to live (in seconds). private static final int MAX_COOKIE_AGE = (int)(WEEK / 1000) * 8; //Days of the week private static final String[] DAYS_OF_WEEK = { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" }; // SimpleDateFormat objects for use in the dateToText method private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("EEEE, MMM d 'at' h:mm a"); private static final SimpleDateFormat yesterdayFormatter = new SimpleDateFormat("'Yesterday at' h:mm a"); //"Tweakable" parameters for the cookie encoding. NOTE: changing these //and recompiling this class will essentially invalidate old cookies. private final static int ENCODE_XORMASK = 0x5A; private final static char ENCODE_DELIMETER = '\002'; private final static char ENCODE_CHAR_OFFSET1 = 'A'; private final static char ENCODE_CHAR_OFFSET2 = 'h'; /** * Returns an Authorization token for the user. The following steps are * performed to determine the token:
    * *
  1. Check the session for the existence of a Jive authorization token. * If one is found, it is returned as we assume that the user has logged * in and is authorized. *
  2. Check the Jive authorization cookie for a username and password. If found, * attempt to create a Jive authorization token using that data. If * successful, save the token to the session and return it. * NOTE: This check can be skipped by setting * checkJiveCookie to false. *

* * @param request the HttpServletRequest object, known as "request" in a * JSP page. * @param response the HttpServletResponse object, known as "response" in * a JSP page. * @param checkJiveCookie a boolean that indicates whether or not we want * to use a cookie for authorization. * @return the authorization token if authenticated, otherwise * null. * @see Authorization */ public static Authorization getUserAuthorization(HttpServletRequest request, HttpServletResponse response, boolean checkJiveCookie) { // we can get the session object from the request object: HttpSession session = request.getSession(); // Check 1: check for the jive authentication token in the user's session. Authorization authToken = (Authorization)session.getValue(JIVE_AUTH_TOKEN); if (authToken != null) { return authToken; } // Check 2: check the jive cookie for username and password, if we're allowing that if( checkJiveCookie ) { Cookie cookie = getCookie(request, JIVE_AUTOLOGIN_COOKIE); try { if( cookie != null ) { // at this point, we found a cookie so grab the username & password // from it, create an authorization token and store that in the session String[] values = decodePasswordCookie(cookie.getValue()); String username = values[0]; String password = values[1]; // try to validate the user based on the info from the cookie authToken = AuthorizationFactory.getAuthorization(username,password); // put that token in the user's session: session.putValue( JIVE_AUTH_TOKEN, authToken ); // return the authorization token return authToken; } } catch( Exception e ) { //We want any exceptions in this block to be caught so that an //anonymous authorization token can be returned. The //getAuthorzation(username,password) method above throws an //UnauthorizedException. In the case of this exception or others, //the cookie holds invalid login info, so we should remove it: cookie = new Cookie(JIVE_AUTOLOGIN_COOKIE,null); cookie.setMaxAge(0); // zero value causes cookie to be deleted cookie.setPath("/"); response.addCookie(cookie); } } //Got this far, so return null. return null; } /** * Returns an Authorization token for the user. This is a convenience method * that that calls the other getUserAuthorization method with * checkJiveCookienull. * @see SkinUtils#getUserAuthorization(HttpServletRequest,HttpServletResponse,boolean) */ public static Authorization getUserAuthorization ( HttpServletRequest request, HttpServletResponse response ) { return getUserAuthorization(request, response, true); } /** * Validates the user and optionally enables auto-login by creating an * auto-login cookie. * * @param request the HttpServletRequest object, known as "request" in a JSP page. * @param response the HttpServletResponse object, known as "response" in a JSP page. * @param username the username. * @param password the password. * @param autoLogin if true create a cookie that enables auto-login. * @throws UserNotFoundException * @throws UnauthorizedException * @return The authorization token if authenticated, otherwise * null */ public static Authorization setUserAuthorization(HttpServletRequest request, HttpServletResponse response, String username, String password, boolean autoLogin) throws UserNotFoundException, UnauthorizedException { HttpSession session = request.getSession(); Authorization authToken = AuthorizationFactory.getAuthorization(username, password); session.putValue(JIVE_AUTH_TOKEN, authToken); if (autoLogin) { Cookie cookie = new Cookie(JIVE_AUTOLOGIN_COOKIE, encodePasswordCookie(username, password)); cookie.setMaxAge(MAX_COOKIE_AGE); cookie.setPath("/"); response.addCookie(cookie); } return authToken; } /** * Invalidates the cookie that otherwise lets a user auto-login. * * @param request The HttpServletRequest object, known as "request" in a JSP page. * @param response The HttpServletResponse object, known as "response" in a JSP page. */ public static void removeUserAuthorization( HttpServletRequest request, HttpServletResponse response ) { HttpSession session = request.getSession(); session.removeValue(JIVE_AUTH_TOKEN); Cookie cookie = new Cookie(JIVE_AUTOLOGIN_COOKIE, null); cookie.setMaxAge(0); cookie.setPath("/"); response.addCookie(cookie); } /** * Invalidate the specified cookie and delete it from the response object. * * @param request The HttpServletRequest object, known as "request" in a JSP page. * @param response The HttpServletResponse object, known as "response" in a JSP page. * @param cookieName The name of the cookie you want to delete. */ public static void invalidateCookie( HttpServletRequest request, HttpServletResponse response, String cookieName ) { Cookie cookie = new Cookie( cookieName, null ); // invalidate cookie cookie.setMaxAge(0); // deletes cookie cookie.setPath("/"); response.addCookie(cookie); } /** * Persists a value for the length of the user's session. * * @see SkinUtils#store(HttpServletRequest,HttpServletResponse,String,String,int) store */ public static void store( HttpServletRequest request, HttpServletResponse response, String id, String value ) { store( request,response,id,value,0,false ); } /** * Persists a value for the time (in seconds) specified * * @see SkinUtils#store(HttpServletRequest,HttpServletResponse,String,String,int) store */ public static void store( HttpServletRequest request, HttpServletResponse response, String id, String value, int secsToLive ) { store( request,response,id,value,secsToLive,false ); } /** * This method should be used in a jsp skin to store an arbritary value. * For example, we could persist the name of a user so that on a form page * where they enter their name, that field could be auto-filled in with * the stored value. *

* To indicate that the data should only be persisted for a session, pass * in 0 as the timeToLive. * * @param request The HttpServletRequest object, known as "request" on a JSP page. * @param response The HttpServletRequest object, known as "response" on a JSP page. * @param id The name or identifier of the data you want to persist. * @param value The value you wish to store. * @param secsToLive The length (in seconds) this value will persist. Any value of 0 or * less indicates this data should only persist for a session. */ public static void store( HttpServletRequest request, HttpServletResponse response, String id, String value, int secsToLive, boolean restoreInSession ) { // This method uses sessions and cookies to persist data. We always store // it in the user's session. We'll only set it in a cookie if the // 'timeToLive' parameter is > 0. // If the id is null, return if( id == null ) { return; } // Get the session object: HttpSession session = request.getSession(); // check to see if the value already exists in the session -- if it does, // don't restore it unless specified if( ((String)session.getValue(id)) != null && !restoreInSession ) { return; } // At this point, restore (or store for the first time) the value in the session // Servlet API 2.1 call. Used to preserve compatibility with older app // servers. You might get deprecation warnings. session.putValue(id,value); // if the timeToLive param is > 0, store to the cookie: if( secsToLive > 0 ) { Cookie cookie = new Cookie(id,value); cookie.setMaxAge(secsToLive); cookie.setPath("/"); response.addCookie(cookie); } } /** * Retrieves a user stored value. Values are set using the store(...) * methods. * * @param request The HttpServletRequest object, known as "request" on a JSP page. * @param response The HttpServletRequest object, known as "response" on a JSP page. * @param id The id or name of the stored value. * @return The value of the specified id, otherwise null. */ public static String retrieve( HttpServletRequest request, HttpServletResponse response, String id ) { // just retrieve the value, don't remove it from persistence return( retrieve( request,response,id,false ) ); } /** * Retrieves a user stored value. Values are set using the store(...) * methods. If remove is true, the value is also removed * from persistence. * * @param request The HttpServletRequest object, known as "request" on a JSP page. * @param response The HttpServletRequest object, known as "response" on a JSP page. * @param id The id or name of the stored value. * @return The value of the specified id, otherwise null. */ public static String retrieve( HttpServletRequest request, HttpServletResponse response, String id, boolean remove ) { // First, check the session. HttpSession session = request.getSession(); String value = (String)session.getValue(id); // if it's not found, check the cookies if( value == null ) { value = getCookieValue(request,id); } // remove it from persistence if indicated if( remove ) { remove( request,response,id ); } return value; } /** * Removes a user stored value. Values are set using the store(...) * methods. * * @param request the HttpServletRequest object, known as "request" on a JSP page. * @param response the HttpServletRequest object, known as "response" on a JSP page. * @param id the id or name of the stored value you wish to remove from persistence. */ public static void remove( HttpServletRequest request, HttpServletResponse response, String id ) { // First, remove it from the session: HttpSession session = request.getSession(); session.removeValue(id); // Invalidate the cookie by setting a null expired cookie in its place Cookie cookie = new Cookie( id, null ); cookie.setMaxAge(0); cookie.setPath("/"); response.addCookie(cookie); } /** * Returns the time in milliseconds that the user last visited Jive. * * @param request the HttpServletRequest object, known as "request" on a JSP page. * @param response the HttpServletRequest object, known as "response" on a JSP page. * @see SkinUtils#getLastVisited(HttpServletRequest,HttpServletResponse,boolean) getLastVisited * @return The time (in milliseconds) that the suer last visited Jive. */ public static long getLastVisited(HttpServletRequest request, HttpServletResponse response) { return getLastVisited(request,response,true); } /** * Returns the time in milliseconds that the user last visited the Jive system. * * @param request the HttpServletRequest object, known as "request" on a JSP page. * @param response the HttpServletRequest object, known as "response" on a JSP page. * @param updateLastVisitedTime Set to true if you wish to update * the user's last visited time to the current time; set to false otherwise. * @return The time (in milliseconds) that the suer last visited Jive. */ public static long getLastVisited(HttpServletRequest request, HttpServletResponse response, boolean updateLastVisitedTime) { //Get session object HttpSession session = request.getSession(); //The current instant in time. long now = System.currentTimeMillis(); //First, try to retrieve the value from the session String lastTime = (String)session.getValue(JIVE_LASTVISITED_TOKEN); //Found a value in the session, so return it if(lastTime != null) { try { long time = Long.parseLong(lastTime); // update the last visited time to now, but don't update the // last visited time in the session: Cookie cookie = new Cookie(JIVE_LASTVISITED_TOKEN, Long.toString(now)); cookie.setMaxAge(60*60*24*30); cookie.setPath("/"); response.addCookie(cookie); // return the time value return time; } catch(NumberFormatException e) { e.printStackTrace(); } } // getting to this point means no time value was found in the session, // so look for it in the cookie: long time = now; lastTime = getCookieValue(request,JIVE_LASTVISITED_TOKEN); if( lastTime != null ) { try { time = Long.parseLong(lastTime); } catch( NumberFormatException e ) {} } // set the value in the cookie, return the time session.putValue(JIVE_LASTVISITED_TOKEN, Long.toString(time)); Cookie cookie = new Cookie(JIVE_LASTVISITED_TOKEN, Long.toString(now)); cookie.setMaxAge(60*60*24*30); cookie.setPath("/"); response.addCookie(cookie); return time; } /** * Returns true if the message has been created or updated since * the last time the user visisted. * * @param message the message to check. * @param lastVisted the time the user last visisted the forum. * @return true if the message has been created or updated since the user's * last visit. */ public static boolean isNewMessage(ForumMessage message, long lastVisited) { if (message.getModifiedDate().getTime() > lastVisited) { return true; } else { return false; } } /** * Returns the specified Cookie object, or null if the cookie does not exist. * * @param request The HttpServletRequest object, known as "request" in a * JSP page. * @param name the name of the cookie. * @return the Cookie object if it exists, otherwise null. */ public static Cookie getCookie( HttpServletRequest request, String name ) { Cookie cookies[] = request.getCookies(); if(cookies == null || name == null || name.length() == 0) { return null; } //Otherwise, we have to do a linear scan for the cookie. for( int i = 0; i < cookies.length; i++ ) { if(cookies[i].getName().equals(name) ) { return cookies[i]; } } return null; } /** * Returns the value of the specified cookie as a String. If the cookie * does not exist, the method returns null. * * @param request the HttpServletRequest object, known as "request" in a * JSP page. * @param name the name of the cookie * @return the value of the cookie, or null if the cookie does not exist. */ public static String getCookieValue(HttpServletRequest request, String name) { Cookie cookie = getCookie(request,name); if(cookie != null) { return cookie.getValue(); } return null; } /** * Formats the unfiltered body of a message to make it appear in the "quote * original" format. This is simply the body of the message with the * delimiter appended to the beginning of each line. The delimiter * is most often "> " by convention. A desired length for each line in the * returned String can be specified to aid in formatting.

* * This method uses message.getUnfilteredBody() in order to get the body of * the message. This usually yields better results for the formatting * required by this method. However, it also has the potential of being * a security risk if malicious HTML code is embedded in the body. Therefore, * you should always filter HTML from the result of this method before * showing it in an environment where HTML is interpreted. If you are * showing the results of this method in an HTML <textarea>, there is * no need to worry about malicious HTML. * * @param message the message to quote. * @param delimiter a String that will start each line of the quoted * message. For example, "> "; * @param lineLength the desired length of each line in the quoted message. * @return the unfiltered body of the message in the "quote original" format. */ public static String quoteOriginal(String body, String delimiter, int lineLength) { if (body == null || body.length() == 0) { return ""; } int length = body.length(); //Create a StringBuffer to hold the quoted body; approximate size. StringBuffer buf = new StringBuffer(body.length()); //i maintains the current position in the String. for (int i=0; i * * Use the method isForumAdmin( Authorization, Forum) to * check an individual forum for administrator status.) * * @param authToken the authentication token of the user * @return true if the user is a forum administrator of any forum in the system. * @see SkinUtils#isForumAdmin(Authorization, Forum) */ public static boolean isForumAdmin( Authorization authToken ) { ForumFactory forumFactory = ForumFactory.getInstance(authToken); Iterator forumIterator = forumFactory.forums(); if( !forumIterator.hasNext() ) { return false; } while( forumIterator.hasNext() ) { Forum forum = (Forum)forumIterator.next(); if( forum.hasPermission(ForumPermissions.FORUM_ADMIN) ) { return true; } } return false; } /** * Returns true if the user is a forum adminstrator of the given forum. * * @param authToken the authentication token of the user * @param forum the forum to check administrator status on. * @return true if the user is a forum administrator of the given forum. */ public static boolean isForumAdmin( Authorization authToken, Forum forum ) { return( forum.hasPermission(ForumPermissions.FORUM_ADMIN) ); } /** * Returns true if the user is a group administrator of any group in the * system. For example, if there are 3 groups in the system and the user * is an adminstrator of any one or more of them, this method will return * true.

* * Use the method isGroupAdmin( Authorization, Group) to check * an individual group for administrator status.) * * @see SkinUtils#isGroupAdmin(Authorization, Group) */ public static boolean isGroupAdmin( Authorization authToken ) { ForumFactory forumFactory = ForumFactory.getInstance(authToken); ProfileManager manager = forumFactory.getProfileManager(); Iterator groupIterator = manager.groups(); if( !groupIterator.hasNext() ) { return false; } while( groupIterator.hasNext() ) { Group group = (Group)groupIterator.next(); if( group.hasPermission(ForumPermissions.GROUP_ADMIN) ) { return true; } } return false; } /** * Returns true if the user is a group administrator of the given group. * * @param authToken the authentication token of the user * @param group the group to check administrator status on. * @return true if the user is a group administrator of the given group. */ public static boolean isGroupAdmin( Authorization authToken, Group group ) { return( group.hasPermission(ForumPermissions.GROUP_ADMIN) ); } /** * Builds a cookie string containing a username and password.

* * Note: with open source this is not really secure, but it prevents users * from snooping the cookie file of others and by changing the XOR mask and * character offsets, you can easily tweak results. * * @param username The username. * @param password The password. * @return String encoding the input parameters, an empty string if one of * the arguments equals null. */ private static String encodePasswordCookie (String username, String password) { StringBuffer buf = new StringBuffer(); if (username != null && password != null) { byte[] bytes = (username + ENCODE_DELIMETER + password).getBytes(); int b; for (int n = 0; n < bytes.length; n++) { b = bytes[n] ^ (ENCODE_XORMASK + n); buf.append((char)(ENCODE_CHAR_OFFSET1 + (b & 0x0F))); buf.append((char)(ENCODE_CHAR_OFFSET2 + ((b >> 4) & 0x0F))); } } return buf.toString(); } /** * Unrafels a cookie string containing a username and password. * @param value The cookie value. * @return String[] containing the username at index 0 and the password at * index 1, or { null, null } if cookieVal equals * null or the empty string. */ private static String[] decodePasswordCookie( String cookieVal ) { // check that the cookie value isn't null or zero-length if( cookieVal == null || cookieVal.length() <= 0 ) { return null; } // unrafel the cookie value char[] chars = cookieVal.toCharArray(); byte[] bytes = new byte[chars.length / 2]; int b; for (int n = 0, m = 0; n < bytes.length; n++) { b = chars[m++] - ENCODE_CHAR_OFFSET1; b |= (chars[m++] - ENCODE_CHAR_OFFSET2) << 4; bytes[n] = (byte)(b ^ (ENCODE_XORMASK + n)); } cookieVal = new String(bytes); int pos = cookieVal.indexOf(ENCODE_DELIMETER); String username = (pos < 0) ? "" : cookieVal.substring(0, pos); String password = (pos < 0) ? "" : cookieVal.substring(pos + 1); return new String[] { username, password }; } /** * test method for this class */ /* public static void main( String args[] ) { Calendar cal = Calendar.getInstance(); System.out.println( "now:\t" + dateToText(cal.getTime()) ); for( int i=0; i<122; i++ ) { cal.setTime( new Date(cal.getTime().getTime() - (30*MINUTE) ) ); System.out.println( (i+1) + " min ago:\t" + dateToText(cal.getTime()) + "\t" + cal.getTime() ); } } */ }