001: /**
002: * Copyright (C) 2001 Yasna.com. All rights reserved.
003: *
004: * ===================================================================
005: * The Apache Software License, Version 1.1
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by
022: * Yasna.com (http://www.yasna.com)."
023: * Alternately, this acknowledgment may appear in the software itself,
024: * if and wherever such third-party acknowledgments normally appear.
025: *
026: * 4. The names "Yazd" and "Yasna.com" must not be used to
027: * endorse or promote products derived from this software without
028: * prior written permission. For written permission, please
029: * contact yazd@yasna.com.
030: *
031: * 5. Products derived from this software may not be called "Yazd",
032: * nor may "Yazd" appear in their name, without prior written
033: * permission of Yasna.com.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL YASNA.COM OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of Yasna.com. For more information
051: * on Yasna.com, please see <http://www.yasna.com>.
052: */
053:
054: /**
055: * Copyright (C) 2000 CoolServlets.com. All rights reserved.
056: *
057: * ===================================================================
058: * The Apache Software License, Version 1.1
059: *
060: * Redistribution and use in source and binary forms, with or without
061: * modification, are permitted provided that the following conditions
062: * are met:
063: *
064: * 1. Redistributions of source code must retain the above copyright
065: * notice, this list of conditions and the following disclaimer.
066: *
067: * 2. Redistributions in binary form must reproduce the above copyright
068: * notice, this list of conditions and the following disclaimer in
069: * the documentation and/or other materials provided with the
070: * distribution.
071: *
072: * 3. The end-user documentation included with the redistribution,
073: * if any, must include the following acknowledgment:
074: * "This product includes software developed by
075: * CoolServlets.com (http://www.coolservlets.com)."
076: * Alternately, this acknowledgment may appear in the software itself,
077: * if and wherever such third-party acknowledgments normally appear.
078: *
079: * 4. The names "Jive" and "CoolServlets.com" must not be used to
080: * endorse or promote products derived from this software without
081: * prior written permission. For written permission, please
082: * contact webmaster@coolservlets.com.
083: *
084: * 5. Products derived from this software may not be called "Jive",
085: * nor may "Jive" appear in their name, without prior written
086: * permission of CoolServlets.com.
087: *
088: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
089: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
090: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
091: * DISCLAIMED. IN NO EVENT SHALL COOLSERVLETS.COM OR
092: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
093: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
094: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
095: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
096: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
097: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
098: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
099: * SUCH DAMAGE.
100: * ====================================================================
101: *
102: * This software consists of voluntary contributions made by many
103: * individuals on behalf of CoolServlets.com. For more information
104: * on CoolServlets.com, please see <http://www.coolservlets.com>.
105: */package com.Yasna.forum.util;
106:
107: import java.util.Date;
108: import java.util.Iterator;
109: import java.util.Locale;
110: import java.text.SimpleDateFormat;
111: import java.io.File;
112: import java.util.TimeZone;
113:
114: import javax.servlet.*;
115: import javax.servlet.http.*;
116:
117: import com.Yasna.forum.*;
118: import com.Yasna.forum.Tasks.SessionManager;
119: import com.Yasna.forum.locale.YazdLocale;
120: import com.Yasna.util.StringUtils;
121:
122: /**
123: * A collection of utility methods for use in Yazd WebSkins. Because these
124: * methods make skin development much easier, skin authors should study them
125: * carefully.<p>
126: *
127: * Three major areas of funtionality are provided:<p><ol>
128: * <li> Methods that simplify Authorization tasks:
129: * <ul>
130: * <li>{@link #getUserAuthorization(HttpServletRequest, HttpServletResponse)}
131: * <li>{@link #getUserAuthorization(HttpServletRequest, HttpServletResponse, boolean)}
132: * <li>{@link #setUserAuthorization(HttpServletRequest, HttpServletResponse, String, String, boolean)}
133: * <li>{@link #removeUserAuthorization(HttpServletRequest, HttpServletResponse)}
134: * <li>{@link #isSystemAdmin(Authorization)}
135: * <li>{@link #isForumAdmin(Authorization)}
136: * <li>{@link #isForumAdmin(Authorization, Forum)}
137: * <li>{@link #isGroupAdmin(Authorization)}
138: * <li>{@link #isGroupAdmin(Authorization, Group)}
139: * </ul>
140: * <p>
141: * <li> Methods that get and set Session and cookie values.
142: * <ul>
143: * <li>{@link #getCookie(HttpServletRequest, String)}
144: * <li>{@link #getCookieValue(HttpServletRequest, String)}
145: * <li>{@link #invalidateCookie(HttpServletRequest, HttpServletResponse, String)}
146: * <li>{@link #remove(HttpServletRequest, HttpServletResponse, String)}
147: * <li>{@link #retrieve(HttpServletRequest, HttpServletResponse, String)}
148: * <li>{@link #retrieve(HttpServletRequest, HttpServletResponse, String, boolean)}
149: * <li>{@link #store(HttpServletRequest, HttpServletResponse, String, String)}
150: * <li>{@link #store(HttpServletRequest, HttpServletResponse, String, String, int)}
151: * <li>{@link #store(HttpServletRequest, HttpServletResponse, String, String, int boolean)}
152: * </ul>
153: * <p>
154: * <li> Other methods.
155: * <ul>
156: * <li>{@link #dateToText(Date)}
157: * <li>(@link #getLastVisisted(HttpServletRequest, HttpServletResponse)}
158: * <li>(@link #getLastVisisted(HttpServletRequest, HttpServletResponse, boolean)}
159: * <li>{@link #isNewMessage(ForumMessage, long)}
160: * <li>(@link #quoteOriginal(ForumMessage, String, int)}
161: * </ul>
162: * </ol>
163: *
164: * All methods conform to the Servlet 1.1 and JSP 1.0 specs for maximum
165: * compatibility with application servers. This may yield deprecation warnings
166: * if you compile with a newer Servlet/JSP spec; these should be ignored. This
167: * class will periodically be updated to the newer specs as app servers mature.
168: */
169: public class SkinUtils {
170:
171: /** Name of the authentication token (is stored in the user's session) */
172: public static final String YAZD_AUTH_TOKEN = "yazdAuthorization";
173:
174: /** Name of the cookie used to store user info for auto-login purposes */
175: public static final String YAZD_AUTOLOGIN_COOKIE = "yazdAutoLogin";
176:
177: /** Name of the last visited token (is stored in the user's session) */
178: public static final String YAZD_LASTVISITED_TOKEN = "yazdLastVisited";
179:
180: /** Name of the cookie used to store last visited timestamp */
181: public static final String YAZD_LASTVISITED_COOKIE = "yazdLastVisited";
182:
183: // XXX keep this ?
184: /** Name of the "use last visited" property (is stored in yazd.properties) */
185: public static final String YAZD_LASTVISITED_PROP = "Site.useLastVisited";
186:
187: //Time constants (in milliseconds)
188: private static final long SECOND = 1000;
189: private static final long MINUTE = 60 * SECOND;
190: private static final long HOUR = 60 * MINUTE;
191: private static final long DAY = 24 * HOUR;
192: private static final long WEEK = 7 * DAY;
193:
194: //Default cookie time to live (in seconds).
195: private static final int MAX_COOKIE_AGE = (int) (WEEK / 1000) * 8;
196:
197: private static SessionManager sessionManager = new SessionManager();
198:
199: //Days of the week
200: private static final String[] DAYS_OF_WEEK = { "Sunday", "Monday",
201: "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
202:
203: // SimpleDateFormat objects for use in the dateToText method
204: private static final SimpleDateFormat yesterdayFormatter = new SimpleDateFormat(
205: "'Yesterday at' h:mm a");
206:
207: //"Tweakable" parameters for the cookie encoding. NOTE: changing these
208: //and recompiling this class will essentially invalidate old cookies.
209: private final static int ENCODE_XORMASK = 0x5A;
210: private final static char ENCODE_DELIMETER = '\002';
211: private final static char ENCODE_CHAR_OFFSET1 = 'A';
212: private final static char ENCODE_CHAR_OFFSET2 = 'h';
213:
214: /**
215: * Returns an Authorization token for the user. The following steps are
216: * performed to determine the token:<ol>
217: *
218: * <li>Check the session for the existence of a Yazd authorization token.
219: * If one is found, it is returned as we assume that the user has logged
220: * in and is authorized.
221: * <li>Check the Yazd authorization cookie for a username and password. If found,
222: * attempt to create a Yazd authorization token using that data. If
223: * successful, save the token to the session and return it.
224: * NOTE: This check can be skipped by setting
225: * <code>checkYazdCookie</code> to false.
226: * </ol><p>
227: *
228: * @param request the HttpServletRequest object, known as "request" in a
229: * JSP page.
230: * @param response the HttpServletResponse object, known as "response" in
231: * a JSP page.
232: * @param checkYazdCookie a boolean that indicates whether or not we want
233: * to use a cookie for authorization.
234: * @return the authorization token if authenticated, otherwise
235: * <code>null</code>.
236: * @see Authorization
237: */
238: public static Authorization getUserAuthorization(
239: HttpServletRequest request, HttpServletResponse response,
240: boolean checkYazdCookie) {
241: // we can get the session object from the request object:
242: HttpSession session = request.getSession();
243: //System.err.println("Aflatoon SkinUtils ID:"+session.getId());
244:
245: // Check 1: check for the yazd authentication token in the user's session.
246: Authorization authToken = (Authorization) session
247: .getAttribute(YAZD_AUTH_TOKEN);
248: if (authToken != null) {
249: sessionManager.addMessage(session.getId(), request
250: .getRemoteAddr(), authToken.getUserID());
251: return authToken;
252: }
253:
254: // Check 2: check the yazd cookie for username and password, if we're allowing that
255: if (checkYazdCookie) {
256: Cookie cookie = getCookie(request, YAZD_AUTOLOGIN_COOKIE);
257: try {
258: if (cookie != null) {
259: // at this point, we found a cookie so grab the username & password
260: // from it, create an authorization token and store that in the session
261: String[] values = decodePasswordCookie(cookie
262: .getValue());
263: String username = values[0];
264: String password = values[1];
265: // try to validate the user based on the info from the cookie
266: authToken = AuthorizationFactory.getAuthorization(
267: username, password);
268:
269: // put that token in the user's session:
270: session.setAttribute(YAZD_AUTH_TOKEN, authToken);
271:
272: // return the authorization token
273: sessionManager.addMessage(session.getId(), request
274: .getRemoteAddr(), authToken.getUserID());
275: return authToken;
276: }
277: } catch (Exception e) {
278: //We want any exceptions in this block to be caught so that an
279: //anonymous authorization token can be returned. The
280: //getAuthorzation(username,password) method above throws an
281: //UnauthorizedException. In the case of this exception or others,
282: //the cookie holds invalid login info, so we should remove it:
283: cookie = new Cookie(YAZD_AUTOLOGIN_COOKIE, null);
284: cookie.setMaxAge(0); // zero value causes cookie to be deleted
285: cookie.setPath("/");
286: response.addCookie(cookie);
287: }
288: }
289:
290: //Got this far, so return null.
291: sessionManager.addMessage(session.getId(), request
292: .getRemoteAddr(), -1);
293: return null;
294: }
295:
296: /**
297: * Returns an Authorization token for the user. This is a convenience method
298: * that that calls the other getUserAuthorization method with
299: * <code>checkYazdCookie</code set to true.
300: *
301: * @param request the HttpServletRequest object, known as "request" in a
302: * JSP page.
303: * @param response The HttpServletResponse object, known as "response" in
304: * a JSP page.
305: * @return The authorization token if authenticated, otherwise
306: * <code>null</code>.
307: * @see SkinUtils#getUserAuthorization(HttpServletRequest,HttpServletResponse,boolean)
308: */
309: public static Authorization getUserAuthorization(
310: HttpServletRequest request, HttpServletResponse response) {
311: return getUserAuthorization(request, response, true);
312: }
313:
314: /**
315: * Validates the user and optionally enables auto-login by creating an
316: * auto-login cookie.
317: *
318: * @param request the HttpServletRequest object, known as "request" in a JSP page.
319: * @param response the HttpServletResponse object, known as "response" in a JSP page.
320: * @param username the username.
321: * @param password the password.
322: * @param autoLogin if <code>true</code> create a cookie that enables auto-login.
323: * @throws UserNotFoundException
324: * @throws UnauthorizedException
325: * @return The authorization token if authenticated, otherwise
326: * <code>null</code>
327: */
328: public static Authorization setUserAuthorization(
329: HttpServletRequest request, HttpServletResponse response,
330: String username, String password, boolean autoLogin)
331: throws UserNotFoundException, UnauthorizedException {
332: HttpSession session = request.getSession();
333: Authorization authToken = AuthorizationFactory
334: .getAuthorization(username, password);
335: session.setAttribute(YAZD_AUTH_TOKEN, authToken);
336:
337: if (autoLogin) {
338: Cookie cookie = new Cookie(YAZD_AUTOLOGIN_COOKIE,
339: encodePasswordCookie(username, password));
340: cookie.setMaxAge(MAX_COOKIE_AGE);
341: cookie.setPath("/");
342: response.addCookie(cookie);
343: }
344:
345: return authToken;
346: }
347:
348: /**
349: * Invalidates the cookie that otherwise lets a user auto-login.
350: *
351: * @param request The HttpServletRequest object, known as "request" in a JSP page.
352: * @param response The HttpServletResponse object, known as "response" in a JSP page.
353: */
354: public static void removeUserAuthorization(
355: HttpServletRequest request, HttpServletResponse response) {
356: HttpSession session = request.getSession();
357: session.removeAttribute(YAZD_AUTH_TOKEN);
358: Cookie cookie = new Cookie(YAZD_AUTOLOGIN_COOKIE, null);
359: cookie.setMaxAge(0);
360: cookie.setPath("/");
361: response.addCookie(cookie);
362: }
363:
364: /**
365: * Invalidate the specified cookie and delete it from the response object.
366: *
367: * @param request The HttpServletRequest object, known as "request" in a JSP page.
368: * @param response The HttpServletResponse object, known as "response" in a JSP page.
369: * @param cookieName The name of the cookie you want to delete.
370: */
371: public static void invalidateCookie(HttpServletRequest request,
372: HttpServletResponse response, String cookieName) {
373: Cookie cookie = new Cookie(cookieName, null); // invalidate cookie
374: cookie.setMaxAge(0); // deletes cookie
375: cookie.setPath("/");
376: response.addCookie(cookie);
377: }
378:
379: /**
380: * Persists a value for the length of the user's session.
381: *
382: * @see SkinUtils#store(HttpServletRequest,HttpServletResponse,String,String,int) store
383: */
384: public static void store(HttpServletRequest request,
385: HttpServletResponse response, String id, String value) {
386: store(request, response, id, value, 0, false);
387: }
388:
389: /**
390: * Persists a value for the time (in seconds) specified
391: *
392: * @see SkinUtils#store(HttpServletRequest,HttpServletResponse,String,String,int) store
393: */
394: public static void store(HttpServletRequest request,
395: HttpServletResponse response, String id, String value,
396: int secsToLive) {
397: store(request, response, id, value, secsToLive, false);
398: }
399:
400: /**
401: * This method should be used in a jsp skin to store an arbritary value.
402: * For example, we could persist the name of a user so that on a form page
403: * where they enter their name, that field could be auto-filled in with
404: * the stored value.
405: * <p>
406: * To indicate that the data should only be persisted for a session, pass
407: * in 0 as the <code>timeToLive</code>.
408: *
409: * @param request The HttpServletRequest object, known as "request" on a JSP page.
410: * @param response The HttpServletRequest object, known as "response" on a JSP page.
411: * @param id The name or identifier of the data you want to persist.
412: * @param value The value you wish to store.
413: * @param secsToLive The length (in seconds) this value will persist. Any value of 0 or
414: * less indicates this data should only persist for a session.
415: */
416: public static void store(HttpServletRequest request,
417: HttpServletResponse response, String id, String value,
418: int secsToLive, boolean restoreInSession) {
419: // This method uses sessions and cookies to persist data. We always store
420: // it in the user's session. We'll only set it in a cookie if the
421: // 'timeToLive' parameter is > 0.
422:
423: // If the id is null, return
424: if (id == null) {
425: return;
426: }
427:
428: // Get the session object:
429: HttpSession session = request.getSession();
430:
431: // check to see if the value already exists in the session -- if it does,
432: // don't restore it unless specified
433: if (((String) session.getAttribute(id)) != null
434: && !restoreInSession) {
435: return;
436: }
437:
438: // At this point, restore (or store for the first time) the value in the session
439: // Servlet API 2.1 call. Used to preserve compatibility with older app
440: // servers. You might get deprecation warnings.
441: session.setAttribute(id, value);
442:
443: // if the timeToLive param is > 0, store to the cookie:
444: if (secsToLive > 0) {
445: Cookie cookie = new Cookie(id, value);
446: cookie.setMaxAge(secsToLive);
447: cookie.setPath("/");
448: response.addCookie(cookie);
449: }
450: }
451:
452: /**
453: * Retrieves a user stored value. Values are set using the <code>store(...)</code>
454: * methods.
455: *
456: * @param request The HttpServletRequest object, known as "request" on a JSP page.
457: * @param response The HttpServletRequest object, known as "response" on a JSP page.
458: * @param id The id or name of the stored value.
459: * @return The value of the specified id, otherwise <code>null</code>.
460: */
461: public static String retrieve(HttpServletRequest request,
462: HttpServletResponse response, String id) {
463: // just retrieve the value, don't remove it from persistence
464: return (retrieve(request, response, id, false));
465: }
466:
467: /**
468: * Retrieves a user stored value. Values are set using the <code>store(...)</code>
469: * methods. If <code>remove</code> is true, the value is also removed
470: * from persistence.
471: *
472: * @param request The HttpServletRequest object, known as "request" on a JSP page.
473: * @param response The HttpServletRequest object, known as "response" on a JSP page.
474: * @param id The id or name of the stored value.
475: * @return The value of the specified id, otherwise <code>null</code>.
476: */
477: public static String retrieve(HttpServletRequest request,
478: HttpServletResponse response, String id, boolean remove) {
479: // First, check the session.
480: HttpSession session = request.getSession();
481: String value = (String) session.getAttribute(id);
482:
483: // if it's not found, check the cookies
484: if (value == null) {
485: value = getCookieValue(request, id);
486: }
487:
488: // remove it from persistence if indicated
489: if (remove) {
490: remove(request, response, id);
491: }
492:
493: return value;
494: }
495:
496: /**
497: * Removes a user stored value. Values are set using the <code>store(...)</code>
498: * methods.
499: *
500: * @param request the HttpServletRequest object, known as "request" on a JSP page.
501: * @param response the HttpServletRequest object, known as "response" on a JSP page.
502: * @param id the id or name of the stored value you wish to remove from persistence.
503: */
504: public static void remove(HttpServletRequest request,
505: HttpServletResponse response, String id) {
506: // First, remove it from the session:
507: HttpSession session = request.getSession();
508: session.removeAttribute(id);
509:
510: // Invalidate the cookie by setting a null expired cookie in its place
511: Cookie cookie = new Cookie(id, null);
512: cookie.setMaxAge(0);
513: cookie.setPath("/");
514: response.addCookie(cookie);
515: }
516:
517: /**
518: * Returns the time in milliseconds that the user last visited Yazd.
519: *
520: * @param request the HttpServletRequest object, known as "request" on a JSP page.
521: * @param response the HttpServletRequest object, known as "response" on a JSP page.
522: * @see SkinUtils#getLastVisited(HttpServletRequest,HttpServletResponse,boolean) getLastVisited
523: * @return The time (in milliseconds) that the suer last visited Yazd.
524: */
525: public static long getLastVisited(HttpServletRequest request,
526: HttpServletResponse response) {
527: return getLastVisited(request, response, true);
528: }
529:
530: /**
531: * Returns the time in milliseconds that the user last visited the Yazd system.
532: *
533: * @param request the HttpServletRequest object, known as "request" on a JSP page.
534: * @param response the HttpServletRequest object, known as "response" on a JSP page.
535: * @param updateLastVisitedTime Set to <code>true</code> if you wish to update
536: * the user's last visited time to the current time; set to <code>false</code> otherwise.
537: * @return The time (in milliseconds) that the suer last visited Yazd.
538: */
539: public static long getLastVisited(HttpServletRequest request,
540: HttpServletResponse response, boolean updateLastVisitedTime) {
541: //Get session object
542: HttpSession session = request.getSession();
543:
544: //The current instant in time.
545: long now = System.currentTimeMillis();
546:
547: //First, try to retrieve the value from the session
548: String lastTime = (String) session
549: .getAttribute(YAZD_LASTVISITED_TOKEN);
550:
551: //Found a value in the session, so return it
552: if (lastTime != null) {
553: try {
554: long time = Long.parseLong(lastTime);
555: // update the last visited time to now, but don't update the
556: // last visited time in the session:
557: Cookie cookie = new Cookie(YAZD_LASTVISITED_TOKEN, Long
558: .toString(now));
559: cookie.setMaxAge(60 * 60 * 24 * 30);
560: cookie.setPath("/");
561: response.addCookie(cookie);
562: // return the time value
563: return time;
564: } catch (NumberFormatException e) {
565: e.printStackTrace();
566: }
567: }
568:
569: // getting to this point means no time value was found in the session,
570: // so look for it in the cookie:
571: long time = now;
572: lastTime = getCookieValue(request, YAZD_LASTVISITED_TOKEN);
573: if (lastTime != null) {
574: try {
575: time = Long.parseLong(lastTime);
576: } catch (NumberFormatException e) {
577: }
578: }
579:
580: // set the value in the cookie, return the time
581: session.setAttribute(YAZD_LASTVISITED_TOKEN, Long
582: .toString(time));
583: Cookie cookie = new Cookie(YAZD_LASTVISITED_TOKEN, Long
584: .toString(now));
585: cookie.setMaxAge(60 * 60 * 24 * 30);
586: cookie.setPath("/");
587: response.addCookie(cookie);
588:
589: return time;
590: }
591:
592: /**
593: * Returns true if the message has been created or updated since
594: * the last time the user visisted.
595: *
596: * @param message the message to check.
597: * @param lastVisted the time the user last visisted the forum.
598: * @return true if the message has been created or updated since the user's
599: * last visit.
600: */
601: public static boolean isNewMessage(ForumMessage message,
602: long lastVisited) {
603: if (message.getModifiedDate().getTime() > lastVisited) {
604: return true;
605: } else {
606: return false;
607: }
608: }
609:
610: /**
611: * Returns the specified Cookie object, or null if the cookie does not exist.
612: *
613: * @param request The HttpServletRequest object, known as "request" in a
614: * JSP page.
615: * @param name the name of the cookie.
616: * @return the Cookie object if it exists, otherwise null.
617: */
618: public static Cookie getCookie(HttpServletRequest request,
619: String name) {
620: Cookie cookies[] = request.getCookies();
621: if (cookies == null || name == null || name.length() == 0) {
622: return null;
623: }
624: //Otherwise, we have to do a linear scan for the cookie.
625: for (int i = 0; i < cookies.length; i++) {
626: if (cookies[i].getName().equals(name)) {
627: return cookies[i];
628: }
629: }
630: return null;
631: }
632:
633: /**
634: * Returns the value of the specified cookie as a String. If the cookie
635: * does not exist, the method returns null.
636: *
637: * @param request the HttpServletRequest object, known as "request" in a
638: * JSP page.
639: * @param name the name of the cookie
640: * @return the value of the cookie, or null if the cookie does not exist.
641: */
642: public static String getCookieValue(HttpServletRequest request,
643: String name) {
644: Cookie cookie = getCookie(request, name);
645: if (cookie != null) {
646: return cookie.getValue();
647: }
648: return null;
649: }
650:
651: /**
652: * Formats the unfiltered body of a message to make it appear in the "quote
653: * original" format. This is simply the body of the message with the
654: * delimiter appended to the beginning of each line. The delimiter
655: * is most often "> " by convention. A desired length for each line in the
656: * returned String can be specified to aid in formatting.<p>
657: *
658: * This method uses message.getUnfilteredBody() in order to get the body of
659: * the message. This usually yields better results for the formatting
660: * required by this method. However, it also has the potential of being
661: * a security risk if malicious HTML code is embedded in the body. Therefore,
662: * you should always filter HTML from the result of this method before
663: * showing it in an environment where HTML is interpreted. If you are
664: * showing the results of this method in an HTML <textarea>, there is
665: * no need to worry about malicious HTML.
666: *
667: * @param message the message to quote.
668: * @param delimiter a String that will start each line of the quoted
669: * message. For example, "> ";
670: * @param lineLength the desired length of each line in the quoted message.
671: * @return the unfiltered body of the message in the "quote original" format.
672: */
673: public static String quoteOriginal(String body, String delimiter,
674: int lineLength) {
675: if (body == null || body.length() == 0) {
676: return "";
677: }
678: int length = body.length();
679: //Create a StringBuffer to hold the quoted body; approximate size.
680: StringBuffer buf = new StringBuffer(body.length());
681: //i maintains the current position in the String.
682: for (int i = 0; i < length;) {
683: String partialString = StringUtils.chopAtWord(body
684: .substring(i), lineLength);
685: //System.out.println("--" + partialString);
686: i += partialString.length() + 1;
687: buf.append(delimiter).append(partialString.trim()).append(
688: "\\n");
689: }
690: return buf.toString();
691: }
692:
693: /**
694: * Returns a String describing the amount of time between now (current
695: * system time) and the passed in date time. Example output is "5 hours
696: * ago" or "Yesterday at 3:30 pm"
697: *
698: * @param date the Date to compare the current time with.
699: * @return a description of the difference in time, ie: "5 hours ago"
700: * or "Yesterday at 3:30pm"
701: */
702: public static String dateToText(Date date, Locale locale,
703: TimeZone timezone) {
704: if (date == null) {
705: return "";
706: }
707:
708: long delta = System.currentTimeMillis() - date.getTime();
709:
710: // within the last hour
711: if ((delta / HOUR) < 1) {
712: long minutes = (delta / MINUTE);
713: if (minutes == 0) {
714: return YazdLocale.getLocaleKey("Less_than_1_min_ago",
715: locale);
716: } else if (minutes == 1) {
717: return YazdLocale.getLocaleKey("1_minute_ago", locale);
718: } else {
719: return (minutes + " " + YazdLocale.getLocaleKey(
720: "minutes_ago", locale));
721: }
722: }
723:
724: // sometime today
725: if ((delta / DAY) < 1) {
726: long hours = (delta / HOUR);
727: if (hours <= 1) {
728: return YazdLocale.getLocaleKey("1_hour_ago", locale);
729: } else {
730: return (hours + " " + YazdLocale.getLocaleKey(
731: "hours_ago", locale));
732: }
733: }
734:
735: // within the last week
736: if ((delta / WEEK) < 1) {
737: double days = ((double) delta / (double) DAY);
738: if (days <= 1.0) {
739: SimpleDateFormat yestFormatter = new SimpleDateFormat(
740: "'"
741: + YazdLocale.getLocaleKey(
742: "Yesterday_at", locale)
743: + "' h:mm a", locale);
744: return yestFormatter.format(date);
745: } else {
746: SimpleDateFormat dateFormatter = new SimpleDateFormat(
747: "EEEE, MMM d '"
748: + YazdLocale.getLocaleKey("at", locale)
749: + "' h:mm a", locale);
750: dateFormatter.setTimeZone(timezone);
751: return dateFormatter.format(date);
752: }
753: }
754:
755: // before a week ago
756: else {
757: SimpleDateFormat dateFormatter = new SimpleDateFormat(
758: "EEEE, MMM d '"
759: + YazdLocale.getLocaleKey("at", locale)
760: + "' h:mm a", locale);
761: dateFormatter.setTimeZone(timezone);
762: return dateFormatter.format(date);
763: }
764: }
765:
766: /**
767: * Returns true if the user is a system administrator.
768: *
769: * @param authToken the authentication token of the user
770: * @return true if the user is a system administrator, false otherwise.
771: */
772: public static boolean isSystemAdmin(Authorization authToken) {
773: ForumFactory forumFactory = ForumFactory.getInstance(authToken);
774: ForumPermissions permissions = forumFactory
775: .getPermissions(authToken);
776: return permissions.get(ForumPermissions.SYSTEM_ADMIN);
777: }
778:
779: /**
780: * Returns true if the user is a forum adminstrator of any forum in the
781: * system. For example, if there are 3 forums in the system and the user
782: * is an adminstrator of any one or more of them, this method will return
783: * true.<p>
784: *
785: * Use the method <code>isForumAdmin( Authorization, Forum)</code> to
786: * check an individual forum for administrator status.)
787: *
788: * @param authToken the authentication token of the user
789: * @return true if the user is a forum administrator of any forum in the system.
790: * @see SkinUtils#isForumAdmin(Authorization, Forum)
791: */
792: public static boolean isForumAdmin(Authorization authToken) {
793: ForumFactory forumFactory = ForumFactory.getInstance(authToken);
794: Iterator forumIterator = forumFactory.forums();
795: if (!forumIterator.hasNext()) {
796: return false;
797: }
798: while (forumIterator.hasNext()) {
799: Forum forum = (Forum) forumIterator.next();
800: if (forum.hasPermission(ForumPermissions.FORUM_ADMIN)) {
801: return true;
802: }
803: }
804: return false;
805: }
806:
807: /**
808: * Returns true if the user is a forum moderator of any forum in the
809: * system. For example, if there are 3 forums in the system and the user
810: * is a moderator of any one or more of them, this method will return
811: * true.<p>
812: *
813: * Use the method <code>isForumModerator( Authorization, Forum)</code> to
814: * check an individual forum for moderator status.)
815: *
816: * @param authToken the authentication token of the user
817: * @return true if the user is a forum moderator of any forum in the system.
818: * @see SkinUtils#isForumModerator(Authorization, Forum)
819: */
820: public static boolean isForumModerator(Authorization authToken) {
821: ForumFactory forumFactory = ForumFactory.getInstance(authToken);
822: Iterator forumIterator = forumFactory.forums();
823: if (!forumIterator.hasNext()) {
824: return false;
825: }
826: while (forumIterator.hasNext()) {
827: Forum forum = (Forum) forumIterator.next();
828: if (forum.hasPermission(ForumPermissions.MODERATOR)) {
829: return true;
830: }
831: }
832: return false;
833: }
834:
835: /**
836: * Returns true if the user is a forum adminstrator of the given forum.
837: *
838: * @param authToken the authentication token of the user
839: * @param forum the forum to check administrator status on.
840: * @return true if the user is a forum administrator of the given forum.
841: */
842: public static boolean isForumAdmin(Authorization authToken,
843: Forum forum) {
844: return (forum.hasPermission(ForumPermissions.FORUM_ADMIN));
845: }
846:
847: /**
848: * Returns true if the user is a forum moderator of the given forum.
849: *
850: * @param authToken the authentication token of the user
851: * @param forum the forum to check moderator status on.
852: * @return true if the user is a forum moderator of the given forum.
853: */
854: public static boolean isForumModerator(Authorization authToken,
855: Forum forum) {
856: return (forum.hasPermission(ForumPermissions.MODERATOR));
857: }
858:
859: /**
860: * Returns true if the user is a group administrator of any group in the
861: * system. For example, if there are 3 groups in the system and the user
862: * is an adminstrator of any one or more of them, this method will return
863: * true.<p>
864: *
865: * Use the method <code>isGroupAdmin( Authorization, Group)</code> to check
866: * an individual group for administrator status.)
867: *
868: * @see SkinUtils#isGroupAdmin(Authorization, Group)
869: */
870: public static boolean isGroupAdmin(Authorization authToken) {
871: ForumFactory forumFactory = ForumFactory.getInstance(authToken);
872: ProfileManager manager = forumFactory.getProfileManager();
873: Iterator groupIterator = manager.groups();
874: if (!groupIterator.hasNext()) {
875: return false;
876: }
877: while (groupIterator.hasNext()) {
878: Group group = (Group) groupIterator.next();
879: if (group.hasPermission(ForumPermissions.GROUP_ADMIN)) {
880: return true;
881: }
882: }
883: return false;
884: }
885:
886: /**
887: * Returns true if the user is a group administrator of the given group.
888: *
889: * @param authToken the authentication token of the user
890: * @param group the group to check administrator status on.
891: * @return true if the user is a group administrator of the given group.
892: */
893: public static boolean isGroupAdmin(Authorization authToken,
894: Group group) {
895: return (group.hasPermission(ForumPermissions.GROUP_ADMIN));
896: }
897:
898: /**
899: * Builds a cookie string containing a username and password.<p>
900: *
901: * Note: with open source this is not really secure, but it prevents users
902: * from snooping the cookie file of others and by changing the XOR mask and
903: * character offsets, you can easily tweak results.
904: *
905: * @param username The username.
906: * @param password The password.
907: * @return String encoding the input parameters, an empty string if one of
908: * the arguments equals <code>null</code>.
909: */
910: private static String encodePasswordCookie(String username,
911: String password) {
912: StringBuffer buf = new StringBuffer();
913: if (username != null && password != null) {
914: byte[] bytes = (username + ENCODE_DELIMETER + password)
915: .getBytes();
916: int b;
917:
918: for (int n = 0; n < bytes.length; n++) {
919: b = bytes[n] ^ (ENCODE_XORMASK + n);
920: buf.append((char) (ENCODE_CHAR_OFFSET1 + (b & 0x0F)));
921: buf
922: .append((char) (ENCODE_CHAR_OFFSET2 + ((b >> 4) & 0x0F)));
923: }
924: }
925: return buf.toString();
926: }
927:
928: /**
929: * Unrafels a cookie string containing a username and password.
930: * @param value The cookie value.
931: * @return String[] containing the username at index 0 and the password at
932: * index 1, or <code>{ null, null }</code> if cookieVal equals
933: * <code>null</code> or the empty string.
934: */
935: private static String[] decodePasswordCookie(String cookieVal) {
936:
937: // check that the cookie value isn't null or zero-length
938: if (cookieVal == null || cookieVal.length() <= 0) {
939: return null;
940: }
941:
942: // unrafel the cookie value
943: char[] chars = cookieVal.toCharArray();
944: byte[] bytes = new byte[chars.length / 2];
945: int b;
946: for (int n = 0, m = 0; n < bytes.length; n++) {
947: b = chars[m++] - ENCODE_CHAR_OFFSET1;
948: b |= (chars[m++] - ENCODE_CHAR_OFFSET2) << 4;
949: bytes[n] = (byte) (b ^ (ENCODE_XORMASK + n));
950: }
951: cookieVal = new String(bytes);
952: int pos = cookieVal.indexOf(ENCODE_DELIMETER);
953: String username = (pos < 0) ? "" : cookieVal.substring(0, pos);
954: String password = (pos < 0) ? "" : cookieVal.substring(pos + 1);
955:
956: return new String[] { username, password };
957: }
958:
959: /**
960: * test method for this class
961: */
962: /*
963: public static void main( String args[] ) {
964: Calendar cal = Calendar.getInstance();
965: System.out.println( "now:\t" + dateToText(cal.getTime()) );
966: for( int i=0; i<122; i++ ) {
967: cal.setTime( new Date(cal.getTime().getTime() - (30*MINUTE) ) );
968: System.out.println( (i+1) + " min ago:\t" + dateToText(cal.getTime()) + "\t" + cal.getTime() );
969: }
970: }
971: */
972: private static void UpdateSession(String sID, String ip, int uid) {
973:
974: }
975:
976: }
|