You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@roller.apache.org by sn...@apache.org on 2005/06/09 05:19:20 UTC

svn commit: r189695 [42/67] - in /incubator/roller/trunk: ./ contrib/ contrib/lib/ contrib/plugins/ contrib/plugins/src/ contrib/plugins/src/org/ contrib/plugins/src/org/roller/ contrib/plugins/src/org/roller/presentation/ contrib/plugins/src/org/roller/presentation/velocity/ contrib/plugins/src/org/roller/presentation/velocity/plugins/ contrib/plugins/src/org/roller/presentation/velocity/plugins/acronyms/ contrib/plugins/src/org/roller/presentation/velocity/plugins/bookmarks/ contrib/plugins/src/org/roller/presentation/velocity/plugins/email/ contrib/plugins/src/org/roller/presentation/velocity/plugins/jspwiki/ contrib/plugins/src/org/roller/presentation/velocity/plugins/radeox/ contrib/plugins/src/org/roller/presentation/velocity/plugins/readmore/ contrib/plugins/src/org/roller/presentation/velocity/plugins/smileys/ contrib/plugins/src/org/roller/presentation/velocity/plugins/textile/ contrib/plugins/src/org/roller/presentation/velocity/plugins/topictag/ custom/ custom/src/ custom/web/ docs/ docs/images/ docs/installguide/ docs/installguide/old/ docs/userguide/ docs/userguide/images/ docs/userguide/old/ metadata/ metadata/database/ metadata/database/hibernate/ metadata/xdoclet/ nbproject/ personal/ personal/eclipse/ personal/testing/ sandbox/ sandbox/planetroller/ sandbox/planetroller/metadata/ sandbox/planetroller/metadata/database/ sandbox/planetroller/src/ sandbox/planetroller/src/org/ sandbox/planetroller/src/org/roller/ sandbox/planetroller/src/org/roller/tools/ sandbox/planetroller/src/org/roller/tools/planet/ sandbox/planetroller/templates/ sandbox/planetroller/test/ sandbox/planetroller/test/org/ sandbox/planetroller/test/org/roller/ sandbox/planetroller/test/org/roller/model/ sandbox/planetroller/test/org/roller/tools/ sandbox/planetroller/test/org/roller/tools/planet/ sandbox/planetroller/testdata/ sandbox/planetroller/testdata/cache/ sandbox/planetroller/testdata/output/ sandbox/standalone/ sandbox/standalone/jspwiki/ sandbox/standalone/jspwiki/default/ sandbox/standalone/jspwiki/default/images/ sandbox/standalone/lib/ sandbox/standalone/src/ sandbox/standalone/src/org/ sandbox/standalone/src/org/roller/ sandbox/standalone/src/org/roller/jspwiki/ sandbox/standalone/src/org/roller/tomcat/ sandbox/standalone/src/org/roller/util/ sandbox/standalone/tests/ sandbox/standalone/tests/org/ sandbox/standalone/tests/org/roller/ sandbox/standalone/tests/org/roller/util/ sandbox/standalone/tomcat/ src/ src/org/ src/org/roller/ src/org/roller/business/ src/org/roller/business/hibernate/ src/org/roller/business/search/ src/org/roller/business/search/operations/ src/org/roller/business/utils/ src/org/roller/config/ src/org/roller/config/runtime/ src/org/roller/model/ src/org/roller/pojos/ src/org/roller/presentation/ src/org/roller/presentation/atomapi/ src/org/roller/presentation/bookmarks/ src/org/roller/presentation/bookmarks/actions/ src/org/roller/presentation/bookmarks/formbeans/ src/org/roller/presentation/bookmarks/tags/ src/org/roller/presentation/filters/ src/org/roller/presentation/forms/ src/org/roller/presentation/newsfeeds/ src/org/roller/presentation/pagecache/ src/org/roller/presentation/pagecache/rollercache/ src/org/roller/presentation/pings/ src/org/roller/presentation/planet/ src/org/roller/presentation/tags/ src/org/roller/presentation/tags/calendar/ src/org/roller/presentation/tags/menu/ src/org/roller/presentation/util/ src/org/roller/presentation/velocity/ src/org/roller/presentation/weblog/ src/org/roller/presentation/weblog/actions/ src/org/roller/presentation/weblog/formbeans/ src/org/roller/presentation/weblog/tags/ src/org/roller/presentation/website/ src/org/roller/presentation/website/actions/ src/org/roller/presentation/website/formbeans/ src/org/roller/presentation/website/tags/ src/org/roller/presentation/xmlrpc/ src/org/roller/util/ src/org/roller/util/rome/ tests/ tests/org/ tests/org/roller/ tests/org/roller/ant/ tests/org/roller/business/ tests/org/roller/presentation/ tests/org/roller/presentation/atomapi/ tests/org/roller/presentation/bookmarks/ tests/org/roller/presentation/filters/ tests/org/roller/presentation/velocity/ tests/org/roller/presentation/velocity/plugins/ tests/org/roller/presentation/velocity/plugins/smileys/ tests/org/roller/presentation/velocity/plugins/textile/ tests/org/roller/presentation/weblog/ tests/org/roller/presentation/xmlrpc/ tests/org/roller/util/ tests/org/roller/util/rome/ tools/ tools/buildtime/ tools/buildtime/ant-1.6.2/ tools/buildtime/findbugs/ tools/buildtime/findbugs/lib/ tools/buildtime/findbugs/plugin/ tools/buildtime/mockrunner-0.3/ tools/buildtime/mockrunner-0.3/lib/ tools/buildtime/mockrunner-0.35/ tools/buildtime/mockrunner-0.35/lib/ tools/buildtime/tomcat-4.1.24/ tools/buildtime/xdoclet-1.2/ tools/buildtime/xdoclet-1.2/lib/ tools/hibernate-2.1/ tools/hibernate-2.1/lib/ tools/lib/ tools/standard-1.0.3/ tools/standard-1.0.3/lib/ tools/standard-1.0.3/tld/ tools/struts-1.2.4/ tools/struts-1.2.4/lib/ web/ web/WEB-INF/ web/WEB-INF/classes/ web/WEB-INF/classes/flavors/ web/WEB-INF/classes/themes/ web/bookmarks/ web/editor/ web/editor/images/ web/images/ web/images/editor/ web/images/midas/ web/images/preview/ web/images/smileys/ web/planet/ web/tags/ web/templates/ web/theme/ web/theme/images/ web/theme/lavender/ web/theme/scripts/ web/theme/scripts/classes/ web/themes/ web/themes/basic/ web/themes/berkley/ web/themes/berkley/images/ web/themes/brushedmetal/ web/themes/brushedmetal/images/ web/themes/cheb/ web/themes/cheb/images/ web/themes/cheb/scripts/ web/themes/clean/ web/themes/currency-i18n/ web/themes/currency-i18n/images/ web/themes/currency/ web/themes/currency/images/ web/themes/grey2/ web/themes/moonshine/ web/themes/movablemanila/ web/themes/movablemanila/images/ web/themes/pacifica/ web/themes/robot/ web/themes/rolling/ web/themes/rolling/images/ web/themes/sotto/ web/themes/sotto/images/ web/themes/sotto/styles/ web/themes/sunsets/ web/themes/sunsets/images/ web/themes/sunsets/scripts/ web/themes/sunsets/styles/ web/themes/werner/ web/themes/x2/ web/themes/x2/images/ web/themes/x2/scripts/ web/themes/x2/styles/ web/weblog/ web/website/

Added: incubator/roller/trunk/src/org/roller/util/CommentSpamChecker.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/CommentSpamChecker.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/CommentSpamChecker.java (added)
+++ incubator/roller/trunk/src/org/roller/util/CommentSpamChecker.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,90 @@
+package org.roller.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.roller.business.ThreadManagerImpl;
+import org.roller.model.RollerFactory;
+import org.roller.model.ThreadManager;
+import org.roller.pojos.CommentData;
+
+/**
+ * Created on Mar 9, 2004
+ * @author lance.lavandowska
+ */
+public class CommentSpamChecker
+{
+    private static Log mLogger = LogFactory.getLog(CommentSpamChecker.class);
+    private Blacklist blacklist = Blacklist.getBlacklist(null,null);
+
+    // -----------------------------------------------------------------------
+    /**
+     * Runs comment check on comment, sets spam flag on comment.
+     */
+    public void testComment(CommentData comment)
+    {
+        try
+        {
+            // by using the OR conditional it'll test each
+            // one in order and fall into the body without
+            // having to test each and every condition.
+            // Not sure which is the optimal order to check, though.
+            boolean isSpam = blacklist.isBlacklisted(comment.getUrl());
+            isSpam = blacklist.isBlacklisted(comment.getContent())?true:isSpam;
+            isSpam = blacklist.isBlacklisted(comment.getEmail())?true:isSpam;
+
+            if (isSpam)
+            {
+                comment.setSpam(Boolean.TRUE);
+                comment.save();
+                RollerFactory.getRoller().commit();
+            }
+        }
+        catch (Exception e)
+        {
+            mLogger.error("Processing Comment",e);
+        }
+        finally
+        {
+            RollerFactory.getRoller().release();
+        }
+    }
+    
+    // -----------------------------------------------------------------------
+    /**
+     * Spawns thread to run comment check.
+     */
+    public void testComment(CommentData comment, ThreadManager threadMgr)
+    {
+        try
+        {
+            if (threadMgr != null)
+            {
+                threadMgr.executeInBackground(new CommentCheckerRunnable(comment));
+            }
+            else
+            {
+                mLogger.warn("No thread manager found.");
+            }
+        } 
+        catch (InterruptedException e) {
+            mLogger.warn("Interrupted during Comment Spam check",e);
+        }
+    }
+
+    // -----------------------------------------------------------------------
+    /**
+     * Runnable to run spam check on it's own thread.
+     */
+    private class CommentCheckerRunnable implements Runnable
+    {
+        private CommentData mComment = null;
+        public CommentCheckerRunnable( CommentData comment)
+        {
+            mComment = comment;
+        }
+        public void run()
+        {
+            testComment(mComment);
+        }
+    }
+}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/roller/util/DateUtil.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/DateUtil.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/DateUtil.java (added)
+++ incubator/roller/trunk/src/org/roller/util/DateUtil.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1 @@
+package org.roller.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

/**
 * General purpose date utilities.
 * @author Mark Saarinen
 * @author Lance Lavandowska
 */
public abstract class DateUtil extends Object
{
    public static final long millisInDay = 86400000;

    // some static date formats
    private static SimpleDateFormat[] mDateFormats = loadDateFormats();
    
    private static final SimpleDateFormat mFormat8chars = 
        new SimpleDateFormat("yyyyMMdd");

    private static final SimpleDateFormat mFormatIso8601Day = 
        new SimpleDateFormat("yyyy-MM-dd");

    private static final SimpleDateFormat mFormatIso8601 = 
        new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
    
    // http://www.w3.org/Protocols/rfc822/Overview.html#z28
    // Using Locale.US to fix ROL-725 and ROL-628
    private static final SimpleDateFormat mFormatRfc822 = 
        new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US); 

    private static SimpleDateFormat[] loadDateFormats()
    {
        SimpleDateFormat[] temp = {
            //new SimpleDateFormat("MM/dd/yyyy hh:mm:ss.SSS a"),
            new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy"), // standard Date.toString() results
            new SimpleDateFormat("M/d/yy hh:mm:ss"),
            new SimpleDateFormat("M/d/yyyy hh:mm:ss"),
            new SimpleDateFormat("M/d/yy hh:mm a"),
            new SimpleDateFormat("M/d/yyyy hh:mm a"),
            new SimpleDateFormat("M/d/yy HH:mm"),
            new SimpleDateFormat("M/d/yyyy HH:mm"),
            new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"),
            new SimpleDateFormat("yy-MM-dd HH:mm:ss.SSS"),
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"), // standard Timestamp.toString() results
            new SimpleDateFormat("M-d-yy HH:mm"),
            new SimpleDateFormat("M-d-yyyy HH:mm"),
            new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS"),
            new SimpleDateFormat("M/d/yy"),
            new SimpleDateFormat("M/d/yyyy"),
            new SimpleDateFormat("M-d-yy"),
            new SimpleDateFormat("M-d-yyyy"),
            new SimpleDateFormat("MMMM d, yyyyy"),
            new SimpleDateFormat("MMM d, yyyyy")
        };
    
        return temp;
    }
    //-----------------------------------------------------------------------
    /**
     * Gets the array of SimpleDateFormats that DateUtil knows about.
    **/
    private static SimpleDateFormat[] getFormats()
    {
        return mDateFormats;
    }

	//-----------------------------------------------------------------------
	/**
	 * Returns a Date set to the last possible millisecond of the day, just
	 * before midnight. If a null day is passed in, a new Date is created.
	 * midnight (00m 00h 00s)
	 */
	public static Date getEndOfDay(Date day)
	{
		return getEndOfDay(day,Calendar.getInstance());
	}
	public static Date getEndOfDay(Date day,Calendar cal)
	{
		if (day == null) day = new Date();
		cal.setTime(day);
		cal.set(Calendar.HOUR_OF_DAY, cal.getMaximum(Calendar.HOUR_OF_DAY));
		cal.set(Calendar.MINUTE,      cal.getMaximum(Calendar.MINUTE));
		cal.set(Calendar.SECOND,      cal.getMaximum(Calendar.SECOND));
		cal.set(Calendar.MILLISECOND, cal.getMaximum(Calendar.MILLISECOND));
        return cal.getTime();
	}

    //-----------------------------------------------------------------------
	/**
	 * Returns a Date set to the first possible millisecond of the day, just
	 * after midnight. If a null day is passed in, a new Date is created.
	 * midnight (00m 00h 00s)
	 */
	public static Date getStartOfDay(Date day)
	{
		return getStartOfDay(day, Calendar.getInstance());
	}
	/**
	 * Returns a Date set to the first possible millisecond of the day, just
	 * after midnight. If a null day is passed in, a new Date is created.
	 * midnight (00m 00h 00s)
	 */
	public static Date getStartOfDay(Date day, Calendar cal)
	{
		if (day == null) day = new Date();
		cal.setTime(day);
		cal.set(Calendar.HOUR_OF_DAY, cal.getMinimum(Calendar.HOUR_OF_DAY));
		cal.set(Calendar.MINUTE,      cal.getMinimum(Calendar.MINUTE));
		cal.set(Calendar.SECOND,      cal.getMinimum(Calendar.SECOND));
		cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND));
        return cal.getTime();
	}

    /**
     * Returns a Date set just to Noon, to the closest possible millisecond
     * of the day. If a null day is passed in, a new Date is created.
     * nnoon (00m 12h 00s)
     */
    public static Date getNoonOfDay(Date day, Calendar cal)
    {
        if (day == null) day = new Date();
        cal.setTime(day);
        cal.set(Calendar.HOUR_OF_DAY, 12);
        cal.set(Calendar.MINUTE,      cal.getMinimum(Calendar.MINUTE));
        cal.set(Calendar.SECOND,      cal.getMinimum(Calendar.SECOND));
        cal.set(Calendar.MILLISECOND, cal.getMinimum(Calendar.MILLISECOND));
        return cal.getTime();
    }
    
    //-----------------------------------------------------------------------
    public static Date parseFromFormats(String aValue)
    {
        if (StringUtils.isEmpty(aValue)) return null;

        // get DateUtil's formats
        SimpleDateFormat formats[] = DateUtil.getFormats();
        if (formats == null) return null;

        // iterate over the array and parse
        Date myDate = null;
        for (int i = 0; i <formats.length; i++)
        {
            try
            {
                myDate = DateUtil.parse(aValue, formats[i]);
                //if (myDate instanceof Date) 
                return myDate;
            }
            catch (Exception e)
            {
                // do nothing because we want to try the next
                // format if current one fails
            }
       }
       // haven't returned so couldn't parse
       return null;
    }

    //-----------------------------------------------------------------------
    public static java.sql.Timestamp parseTimestampFromFormats(String aValue)
    {
        if (StringUtils.isEmpty(aValue)) return null;

        // call the regular Date formatter
        Date myDate = DateUtil.parseFromFormats(aValue);
        if (myDate != null) return new java.sql.Timestamp(myDate.getTime());
        return null;
    }
    //-----------------------------------------------------------------------
    /**
     * Returns a java.sql.Timestamp equal to the current time
    **/
    public static java.sql.Timestamp now()
    {
        return new java.sql.Timestamp(new java.util.Date().getTime());
    }

    //-----------------------------------------------------------------------
    /**
     * Returns a string the represents the passed-in date parsed
     * according to the passed-in format.  Returns an empty string
     * if the date or the format is null.
    **/
    public static String format(Date aDate, SimpleDateFormat aFormat)
    {
        if (aDate == null || aFormat == null ) { return ""; }
        synchronized (aFormat) 
        {
            return aFormat.format(aDate);
        }
    }

    //-----------------------------------------------------------------------
    /**
     * Tries to take the passed-in String and format it as a date string in the
     * the passed-in format.
    **/
    public static String formatDateString(String aString, SimpleDateFormat aFormat)
    {
        if (StringUtils.isEmpty(aString) || aFormat == null)  return "";
        try
        {
            java.sql.Timestamp aDate = parseTimestampFromFormats(aString);
            if (aDate != null)
            {
                return DateUtil.format(aDate, aFormat);
            }
        }
        catch (Exception e)
        {
            // Could not parse aString.
        }
        return "";
    }

    //-----------------------------------------------------------------------
    /**
     * Returns a Date using the passed-in string and format.  Returns null if the string
     * is null or empty or if the format is null.  The string must match the format.
    **/
    public static Date parse(String aValue, SimpleDateFormat aFormat) throws ParseException
    {
        if (StringUtils.isEmpty(aValue) || aFormat == null)
        {
            return null;
        }

        return aFormat.parse(aValue);
    }

    //-----------------------------------------------------------------------
    /**
     * Returns true if endDate is after startDate or if startDate equals endDate
     * or if they are the same date.  Returns false if either value is null.
    **/
    public static boolean isValidDateRange(Date startDate, Date endDate)
    {
        return isValidDateRange(startDate, endDate, true);
    }

    //-----------------------------------------------------------------------
    /**
     * Returns true if endDate is after startDate or if startDate equals endDate.
     * Returns false if either value is null.  If equalOK, returns true if the
     * dates are equal.
    **/
    public static boolean isValidDateRange(Date startDate, Date endDate, boolean equalOK)
    {
        // false if either value is null
        if (startDate == null || endDate == null) { return false; }

        if (equalOK)
        {
            // true if they are equal
            if (startDate.equals(endDate)) { return true; }
        }

        // true if endDate after startDate
        if (endDate.after(startDate)) { return true; }

        return false;
    }

    //-----------------------------------------------------------------------
    // returns full timestamp format
    public static java.text.SimpleDateFormat defaultTimestampFormat()
    {
        return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    }

    //-----------------------------------------------------------------------
    // convenience method returns minimal date format
    public static java.text.SimpleDateFormat get8charDateFormat()
    {
        return DateUtil.mFormat8chars;
    }

    //-----------------------------------------------------------------------
    // convenience method returns minimal date format
    public static java.text.SimpleDateFormat defaultDateFormat()
    {
        return DateUtil.friendlyDateFormat(true);
    }

    //-----------------------------------------------------------------------
    // convenience method
    public static String defaultTimestamp(Date date)
    {
        return DateUtil.format(date, DateUtil.defaultTimestampFormat());
    }
    
    //-----------------------------------------------------------------------
    // convenience method
    public static String defaultDate(Date date)
    {
        return DateUtil.format(date, DateUtil.defaultDateFormat());
    }

    //-----------------------------------------------------------------------
    // convenience method returns long friendly timestamp format
    public static java.text.SimpleDateFormat friendlyTimestampFormat()
    {
        return new java.text.SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
    }

    //-----------------------------------------------------------------------
    // convenience method returns long friendly formatted timestamp
    public static String friendlyTimestamp(Date date)
    {
        return DateUtil.format(date, DateUtil.friendlyTimestampFormat());
    }

    //-----------------------------------------------------------------------
    // convenience method returns long friendly formatted timestamp
    public static String format8chars(Date date)
    {
        return DateUtil.format(date, mFormat8chars);
    }

    //-----------------------------------------------------------------------
    // convenience method returns long friendly formatted timestamp
    public static String formatIso8601Day(Date date)
    {
        return DateUtil.format(date, mFormatIso8601Day);
    }

    //-----------------------------------------------------------------------
    public static String formatRfc822(Date date)
    {
        return DateUtil.format(date,mFormatRfc822);
    }

    //-----------------------------------------------------------------------
    // This is a hack, but it seems to work
    public static String formatIso8601(Date date)
    {
        if (date == null) return "";
        
        // Add a colon 2 chars before the end of the string
        // to make it a valid ISO-8601 date.
         
        String str = DateUtil.format(date,mFormatIso8601);
        StringBuffer sb = new StringBuffer();
        sb.append( str.substring(0,str.length()-2) );
        sb.append( ":" );
        sb.append( str.substring(str.length()-2) );
        return sb.toString();
    }

    //-----------------------------------------------------------------------
    // convenience method returns minimal date format
    public static java.text.SimpleDateFormat minimalDateFormat()
    {
        return DateUtil.friendlyDateFormat(true);
    }

    //-----------------------------------------------------------------------
    // convenience method using minimal date format
    public static String minimalDate(Date date)
    {
        return DateUtil.format(date, DateUtil.minimalDateFormat());
    }

    //-----------------------------------------------------------------------
    // convenience method that returns friendly data format
    // using full month, day, year digits.
    public static java.text.SimpleDateFormat fullDateFormat()
    {
        return DateUtil.friendlyDateFormat(false);
    }

    //-----------------------------------------------------------------------
    public static String fullDate(Date date)
    {
        return DateUtil.format(date, DateUtil.fullDateFormat());
    }

    //-----------------------------------------------------------------------
    /** Returns a "friendly" date format.
     *  @param mimimalFormat Should the date format allow single digits.
    **/
    public static java.text.SimpleDateFormat friendlyDateFormat(boolean minimalFormat)
    {
        if (minimalFormat)
        {
            return new java.text.SimpleDateFormat("d.M.yy");
        }

        return new java.text.SimpleDateFormat("dd.MM.yyyy");
    }

    //-----------------------------------------------------------------------
    /**
     * Format the date using the "friendly" date format.
     */
    public static String friendlyDate(Date date, boolean minimalFormat)
    {
        return DateUtil.format(date, DateUtil.friendlyDateFormat(minimalFormat));
    }

    //-----------------------------------------------------------------------
    // convenience method
    public static String friendlyDate(Date date)
    {
        return DateUtil.format(date, DateUtil.friendlyDateFormat(true));
    }
    
    public static Date parseIso8601(String value) throws Exception
    {
        return ISO8601DateParser.parse(value);
    }
}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/roller/util/ISO8601DateParser.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/ISO8601DateParser.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/ISO8601DateParser.java (added)
+++ incubator/roller/trunk/src/org/roller/util/ISO8601DateParser.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,122 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.roller.util;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * ISO 8601 date parsing utility.  Designed for parsing the ISO subset used in
+ * Dublin Core, RSS 1.0, and Atom.
+ * 
+ * @author <a href="mailto:burton@apache.org">Kevin A. Burton (burtonator)</a>
+ * @version $Id: ISO8601DateParser.java,v 1.2 2005/06/03 20:25:29 snoopdave Exp $
+ */
+public class ISO8601DateParser {
+
+    // 2004-06-14T19:GMT20:30Z
+    // 2004-06-20T06:GMT22:01Z
+
+    private static SimpleDateFormat df
+        = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssz" );
+
+    // http://www.cl.cam.ac.uk/~mgk25/iso-time.html
+    //    
+    // http://www.intertwingly.net/wiki/pie/DateTime
+    //
+    // http://www.w3.org/TR/NOTE-datetime
+    //
+    // Different standards may need different levels of granularity in the date and
+    // time, so this profile defines six levels. Standards that reference this
+    // profile should specify one or more of these granularities. If a given
+    // standard allows more than one granularity, it should specify the meaning of
+    // the dates and times with reduced precision, for example, the result of
+    // comparing two dates with different precisions.
+
+    // The formats are as follows. Exactly the components shown here must be
+    // present, with exactly this punctuation. Note that the "T" appears literally
+    // in the string, to indicate the beginning of the time element, as specified in
+    // ISO 8601.
+
+    //    Year:
+    //       YYYY (eg 1997)
+    //    Year and month:
+    //       YYYY-MM (eg 1997-07)
+    //    Complete date:
+    //       YYYY-MM-DD (eg 1997-07-16)
+    //    Complete date plus hours and minutes:
+    //       YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
+    //    Complete date plus hours, minutes and seconds:
+    //       YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
+    //    Complete date plus hours, minutes, seconds and a decimal fraction of a
+    // second
+    //       YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
+
+    // where:
+
+    //      YYYY = four-digit year
+    //      MM   = two-digit month (01=January, etc.)
+    //      DD   = two-digit day of month (01 through 31)
+    //      hh   = two digits of hour (00 through 23) (am/pm NOT allowed)
+    //      mm   = two digits of minute (00 through 59)
+    //      ss   = two digits of second (00 through 59)
+    //      s    = one or more digits representing a decimal fraction of a second
+    //      TZD  = time zone designator (Z or +hh:mm or -hh:mm)
+    public static Date parse( String input ) throws java.text.ParseException {
+
+        //NOTE: SimpleDateFormat uses GMT[-+]hh:mm for the TZ which breaks
+        //things a bit.  Before we go on we have to repair this.
+
+        //this is zero time so we need to add that TZ indicator for 
+        if ( input.endsWith( "Z" ) ) {
+            input = input.substring( 0, input.length() - 1) + "GMT-00:00";
+        } else {
+            int inset = 6;
+        
+            String s0 = input.substring( 0, input.length() - inset );
+            String s1 = input.substring( input.length() - inset, input.length() );
+
+            input = s0 + "GMT" + s1;
+        }
+        
+        return df.parse( input );
+        
+    }
+
+    public static String toString( Date date ) {
+
+        TimeZone tz = TimeZone.getTimeZone( "UTC" );
+        
+        df.setTimeZone( tz );
+
+        String output = df.format( date );
+
+        int inset0 = 9;
+        int inset1 = 6;
+        
+        String s0 = output.substring( 0, output.length() - inset0 );
+        String s1 = output.substring( output.length() - inset1, output.length() );
+
+        String result = s0 + s1;
+
+        result = result.replaceAll( "UTC", "+00:00" );
+        
+        return result;
+        
+    }
+
+}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/roller/util/LRUCache.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/LRUCache.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/LRUCache.java (added)
+++ incubator/roller/trunk/src/org/roller/util/LRUCache.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,21 @@
+/*
+ * Created on Jun 15, 2004
+ */
+package org.roller.util;
+
+import java.util.Map;
+
+// David Flanaghan: http://www.davidflanagan.com/blog/000014.html
+public class LRUCache extends java.util.LinkedHashMap 
+{
+    protected int maxsize;
+    public LRUCache(int maxsize) 
+    {
+        super(maxsize*4/3 + 1, 0.75f, true);
+        this.maxsize = maxsize;
+    }
+    protected boolean removeEldestEntry(Map.Entry eldest) { 
+        return size() > this.maxsize; 
+    }
+}
+    

Added: incubator/roller/trunk/src/org/roller/util/LRUCache2.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/LRUCache2.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/LRUCache2.java (added)
+++ incubator/roller/trunk/src/org/roller/util/LRUCache2.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,160 @@
+package org.roller.util;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+/**
+ * LRU cache with per-entry timeout logic.
+ * 
+ * @author Dave Johnson
+ */
+public class LRUCache2
+{
+    private long timeout;
+    private Map cache = null;
+    private Environment environment = null;
+
+    /**
+     * Create cache.
+     * 
+     * @param maxsize
+     *            Maximum number of entries in cache.
+     * @param timeout
+     *            Entry timeout in milli-seconds.
+     */
+    public LRUCache2(int maxsize, long timeout)
+    {
+        this.environment = new DefaultEnvironment();
+        this.timeout = timeout;
+        this.cache = new LRULinkedHashMap(maxsize);
+    }
+
+    /**
+     * Create cache that uses custom environment.
+     * 
+     * @param maxsize
+     *            Maximum number of entries in cache.
+     * @param timeout
+     *            Entry timeout in milli-seconds.
+     */
+    public LRUCache2(Environment environment, int maxsize, long timeout)
+    {
+        this.environment = environment;
+        this.timeout = timeout;
+        this.cache = new LRULinkedHashMap(maxsize);
+    }
+
+    public synchronized void put(Object key, Object value)
+    {
+        CacheEntry entry = new CacheEntry(value, environment
+                        .getCurrentTimeInMillis());
+        cache.put(key, entry);
+    }
+
+    public Object get(Object key)
+    {
+        Object value = null;
+        CacheEntry entry = null;
+        synchronized(this)
+        {
+            entry = (CacheEntry) cache.get(key);
+        }
+        if (entry != null)
+        {
+            if (environment.getCurrentTimeInMillis() - entry.getTimeCached() < timeout)
+            {
+                value = entry.getValue();
+            }
+            else
+            {
+                cache.remove(entry);
+            }
+        }
+        return value;
+    }
+
+    public synchronized void purge()
+    {
+        cache.clear();
+    }
+
+    public synchronized void purge(String[] patterns)
+    {
+        List purgeList = new ArrayList();
+        Iterator keys = cache.keySet().iterator();
+        while (keys.hasNext())
+        {
+            String key = (String) keys.next();
+            for (int i = 0; i < patterns.length; i++)
+            {
+                if (key.indexOf(patterns[i]) != -1)
+                {
+                    purgeList.add(key);
+                    break;
+                }
+            }
+        }
+        Iterator purgeIter = purgeList.iterator();
+        while (purgeIter.hasNext())
+        {
+            String key = (String) purgeIter.next();
+            cache.remove(key);
+        }
+    }
+
+    public int size()
+    {
+        return cache.size();
+    }
+    public interface Environment
+    {
+        public long getCurrentTimeInMillis();
+    }
+    public static class DefaultEnvironment implements Environment
+    {
+        public long getCurrentTimeInMillis()
+        {
+            return System.currentTimeMillis();
+        }
+    }
+    private static class CacheEntry
+    {
+        private Object value;
+        private long timeCached = -1;
+
+        public CacheEntry(Object value, long timeCached)
+        {
+            this.timeCached = timeCached;
+            this.value = value;
+        }
+
+        public long getTimeCached()
+        {
+            return timeCached;
+        }
+
+        public Object getValue()
+        {
+            return value;
+        }
+    }
+    
+    // David Flanaghan: http://www.davidflanagan.com/blog/000014.html
+    private static class LRULinkedHashMap extends LinkedHashMap
+    {
+        protected int maxsize;
+
+        public LRULinkedHashMap(int maxsize)
+        {
+            super(maxsize * 4 / 3 + 1, 0.75f, true);
+            this.maxsize = maxsize;
+        }
+
+        protected boolean removeEldestEntry(Map.Entry eldest)
+        {
+            return this.size() > this.maxsize;
+        }
+    }
+}

Added: incubator/roller/trunk/src/org/roller/util/LinkbackExtractor.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/LinkbackExtractor.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/LinkbackExtractor.java (added)
+++ incubator/roller/trunk/src/org/roller/util/LinkbackExtractor.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,408 @@
+package org.roller.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLEditorKit;
+import javax.swing.text.html.HTML.Tag;
+import javax.swing.text.html.HTMLEditorKit.Parser;
+import javax.swing.text.html.HTMLEditorKit.ParserCallback;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.sun.syndication.feed.synd.SyndEntry;
+import com.sun.syndication.feed.synd.SyndFeed;
+import com.sun.syndication.io.FeedException;
+import com.sun.syndication.io.SyndFeedInput;
+
+/**
+ * Parses HTML file for referring linkback title and excerpt.
+ * 
+ * @author David M Johnson
+ */
+public class LinkbackExtractor
+{
+    private static Log mLogger        = LogFactory.getFactory().getInstance(
+                                              LinkbackExtractor.class);
+    private boolean    mFound         = false;
+    private String     mTitle         = "";
+    private String     mRssLink       = null;
+    private String     mExcerpt       = null;
+    private String     mPermalink     = null;
+    private int        mStart         = 0;
+    private int        mEnd           = 0;
+    private int        mMaxExcerpt    = 500;                           // characters
+    private String     mRequestURL    = null;
+    private String     mRequestURLWWW = null;
+    private String     mRefererURL;
+
+    //------------------------------------------------------------------------
+    /**
+     * Extract referring page title, excerpt, and permalink.
+     * 
+     * @param refererUrl
+     * @param requestUrl
+     */
+    public LinkbackExtractor(String refererURL, String requestURL)
+            throws MalformedURLException, IOException
+    {
+        try
+        {
+            extractByParsingHtml(refererURL, requestURL);
+            if (mRssLink != null)
+            {
+                extractByParsingRss(mRssLink, requestURL);
+            }
+        }
+        catch (Exception e)
+        {
+            if (mLogger.isDebugEnabled())
+            {
+                mLogger.debug("Extracting linkback", e);
+            }
+        }
+    }
+
+    //------------------------------------------------------------------------
+    private void extractByParsingHtml(String refererURL, String requestURL)
+            throws MalformedURLException, IOException
+    {
+        URL url = new URL(refererURL);
+        InputStream is = url.openStream();
+
+        mRefererURL = refererURL;
+
+        if (requestURL.startsWith("http://www."))
+        {
+            mRequestURLWWW = requestURL;
+            mRequestURL = "http://" + mRequestURLWWW.substring(11);
+        }
+        else
+        {
+            mRequestURL = requestURL;
+            mRequestURLWWW = "http://www." + mRequestURL.substring(7);
+        }
+
+        // Trick gets Swing's HTML parser
+        Parser parser = (new HTMLEditorKit() {
+            public Parser getParser()
+            {
+                return super.getParser();
+            }
+        }).getParser();
+
+        // Read HTML file into string
+        StringBuffer sb = new StringBuffer();
+        InputStreamReader isr = new InputStreamReader(is);
+        BufferedReader br = new BufferedReader(isr);
+        try
+        {
+            String line = null;
+            while ((line = br.readLine()) != null)
+            {
+                sb.append(line);
+            }
+        }
+        finally
+        {
+            br.close();
+        }
+
+        // Parse HTML string to find title and start and end position
+        // of the referring excerpt.
+        StringReader sr = new StringReader(sb.toString());
+        parser.parse(sr, new LinkbackCallback(), true);
+
+        if (mStart != 0 && mEnd != 0 && mEnd > mStart)
+        {
+            mExcerpt = sb.toString().substring(mStart, mEnd);
+            mExcerpt = Utilities.removeHTML(mExcerpt);
+
+            if (mExcerpt.length() > mMaxExcerpt)
+            {
+                mExcerpt = mExcerpt.substring(0, mMaxExcerpt) + "...";
+            }
+        }
+
+        if (mTitle.startsWith(">") && mTitle.length() > 1)
+        {
+            mTitle = mTitle.substring(1);
+        }
+    }
+
+    //------------------------------------------------------------------------
+    private void extractByParsingRss(String rssLink, String requestURL)
+            throws IllegalArgumentException, MalformedURLException, FeedException, IOException
+    {
+        SyndFeedInput feedInput = new SyndFeedInput();       
+        SyndFeed feed = feedInput.build(
+            new InputStreamReader(new URL(rssLink).openStream()));
+        Iterator itemIter = feed.getEntries().iterator();
+        String feedTitle = feed.getTitle();
+
+        int count = 0;
+
+        if (mLogger.isDebugEnabled())
+        {
+            mLogger.debug("Feed parsed, title: " + feedTitle);
+        }
+
+        while (itemIter.hasNext())
+        {
+            count++;
+            SyndEntry item = (SyndEntry) itemIter.next();
+            if (item.getDescription().getValue().indexOf(requestURL) != -1)
+            {
+                mFound = true;
+                mPermalink = item.getLink().toString();
+                if (feedTitle != null && feedTitle.trim().length() > 0)
+                {
+                    mTitle = feedTitle + ": " + item.getTitle();
+                }
+                else
+                {
+                    mTitle = item.getTitle();
+                }
+                mExcerpt = item.getDescription().getValue();
+                mExcerpt = Utilities.removeHTML(mExcerpt);
+                if (mExcerpt.length() > mMaxExcerpt)
+                {
+                    mExcerpt = mExcerpt.substring(0, mMaxExcerpt) + "...";
+                }
+                break;
+            }
+        }
+
+        if (mLogger.isDebugEnabled())
+        {
+            mLogger.debug("Parsed " + count + " articles, found linkback="
+                    + mFound);
+        }
+    }
+
+    //------------------------------------------------------------------------
+    /**
+     * Returns the excerpt.
+     * 
+     * @return String
+     */
+    public String getExcerpt()
+    {
+        return mExcerpt;
+    }
+
+    //------------------------------------------------------------------------
+    /**
+     * Returns the title.
+     * 
+     * @return String
+     */
+    public String getTitle()
+    {
+        return mTitle;
+    }
+
+    //------------------------------------------------------------------------
+    /**
+     * Returns the permalink.
+     * 
+     * @return String
+     */
+    public String getPermalink()
+    {
+        return mPermalink;
+    }
+
+    //------------------------------------------------------------------------
+    /**
+     * Sets the permalink.
+     * 
+     * @param permalink
+     *            The permalink to set
+     */
+    public void setPermalink(String permalink)
+    {
+        mPermalink = permalink;
+    }
+
+    /////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Parser callback that finds title and excerpt. As we walk through the HTML
+     * tags, we keep track of the most recently encountered divider tag in the
+     * mStart field. Once we find the referring permalink, we set the mFound
+     * flag. After that, we look for the next divider tag and save it's position
+     * in the mEnd field.
+     */
+    private final class LinkbackCallback extends ParserCallback
+    {
+        // Dividers
+        private Tag[] mDivTags    = { Tag.TD, Tag.DIV, Tag.SPAN,
+                                          Tag.BLOCKQUOTE, Tag.P, Tag.LI,
+                                          Tag.BR, Tag.HR, Tag.PRE, Tag.H1,
+                                          Tag.H2, Tag.H3, Tag.H4, Tag.H5,
+                                          Tag.H6 };
+
+        private List  mList       = Arrays.asList(mDivTags);
+
+        private Tag   mCurrentTag = null;
+
+        /**
+         * Look for divider tags and for the permalink.
+         * 
+         * @param tag
+         *            HTML tag
+         * @param atts
+         *            Attributes of that tag
+         * @param pos
+         *            Tag's position in file
+         */
+        public void handleStartTag(Tag tag, MutableAttributeSet atts, int pos)
+        {
+            if (mList.contains(tag) && !mFound)
+            {
+                mStart = pos;
+            }
+            else if (mList.contains(tag) && mFound && mEnd == 0)
+            {
+                mEnd = pos;
+            }
+            else if (tag.equals(Tag.A))
+            {
+                String href = (String) atts.getAttribute(HTML.Attribute.HREF);
+                if (href == null)
+                    return;
+                int hashPos = href.lastIndexOf('#');
+                if (hashPos != -1)
+                {
+                    href = href.substring(0, hashPos);
+                }
+                if (href != null
+                        && (href.equals(mRequestURL) || href
+                                .equals(mRequestURLWWW)))
+                {
+                    mFound = true;
+                }
+                else
+                {
+                    /*
+                     * if (mLogger.isDebugEnabled()) { mLogger.debug("No match:
+                     * "+href); }
+                     */
+                }
+            }
+            mCurrentTag = tag;
+        }
+
+        /**
+         * Needed to handle SPAN tag.
+         */
+        public void handleSimpleTag(Tag tag, MutableAttributeSet atts, int pos)
+        {
+            if (mList.contains(tag) && mFound && mEnd == 0)
+            {
+                mEnd = pos;
+            }
+            else if (tag.equals(Tag.LINK))
+            {
+                // Look out for RSS autodiscovery link
+                String title = (String) atts.getAttribute(HTML.Attribute.TITLE);
+                String type = (String) atts.getAttribute(HTML.Attribute.TYPE);
+                if (title != null && type != null
+                        && type.equals("application/rss+xml")
+                        && title.equals("RSS"))
+                {
+                    mRssLink = (String) atts.getAttribute(HTML.Attribute.HREF);
+
+                    if (mLogger.isDebugEnabled())
+                    {
+                        mLogger.debug("Found RSS link " + mRssLink);
+                    }
+
+                    if (mRssLink.startsWith("/") && mRssLink.length() > 1)
+                    {
+                        try
+                        {
+                            URL url = new URL(mRefererURL);
+                            mRssLink = url.getProtocol() + "://"
+                                    + url.getHost() + ":" + url.getPort()
+                                    + mRssLink;
+                        }
+                        catch (MalformedURLException e)
+                        {
+                            mRssLink = null;
+                            if (mLogger.isDebugEnabled())
+                            {
+                                mLogger.debug("Determining RSS URL", e);
+                            }
+                        }
+                    }
+                    else if (!mRssLink.startsWith("http"))
+                    {
+                        int slash = mRefererURL.lastIndexOf("/");
+                        if (slash != -1)
+                        {
+                            mRssLink = mRefererURL.substring(0, slash) + "/"
+                                    + mRssLink;
+                        }
+                    }
+                    if (mLogger.isDebugEnabled())
+                    {
+                        mLogger.debug("Qualified RSS link is " + mRssLink);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Stop at the very first divider tag after the permalink.
+         * 
+         * @param tag
+         *            End tag
+         * @param pos
+         *            Position in HTML file
+         */
+        public void handleEndTag(Tag tag, int pos)
+        {
+            if (mList.contains(tag) && mFound && mEnd == 0)
+            {
+                mEnd = pos;
+            }
+            else if (mList.contains(tag) && !mFound)
+            {
+                mStart = pos;
+            }
+            else
+            {
+                mCurrentTag = null;
+            }
+        }
+
+        /**
+         * Get the page title
+         */
+        public void handleText(char[] data, int pos)
+        {
+            if (mCurrentTag != null && mCurrentTag.equals(Tag.TITLE))
+            {
+                String newText = new String(data);
+                if (mTitle.length() < 50)
+                {
+                    mTitle += newText;
+                }
+            }
+        }
+    }
+}
+

Added: incubator/roller/trunk/src/org/roller/util/LocaleComparator.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/LocaleComparator.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/LocaleComparator.java (added)
+++ incubator/roller/trunk/src/org/roller/util/LocaleComparator.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,3 @@
+package org.roller.util;

import java.util.Locale;
import java.util.Comparator;
import java.io.Serializable;

public class LocaleComparator implements Comparator, Serializable
{
    public int compare(Object obj1, Object obj2)
    {
        if (obj1 instanceof Locale && obj2 instanceof Locale)
        {
            Locale locale1 = (Locale)obj1;
            Locale locale2 = (Locale)obj2;
            int compName = locale1.getDisplayName().compareTo(locale2.getDisplayName());
            if (compName == 0)
            {
                return locale1.toString().compareTo(locale2.toString());
            }
            return compName;
        }
+        return 0;
    }
/* Do Comparators need to implement equals()? -Lance
    public boolean equals(Object obj)
    {
        if (obj instanceof LocaleComparator)
        {
            if (obj.equals(this)) return true;
        }
        return false;
    }
*/
+}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/roller/util/MD5Encoder.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/MD5Encoder.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/MD5Encoder.java (added)
+++ incubator/roller/trunk/src/org/roller/util/MD5Encoder.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,72 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.roller.util;
+
+
+/**
+ * Encode an MD5 digest into a String.
+ * <p>
+ * The 128 bit MD5 hash is converted into a 32 character long String.
+ * Each character of the String is the hexadecimal representation of 4 bits
+ * of the digest.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 1.1 $ $Date: 2005/06/01 19:51:22 $
+ */
+
+public final class MD5Encoder {
+
+
+    // ----------------------------------------------------- Instance Variables
+
+
+    private static final char[] hexadecimal =
+    {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+     'a', 'b', 'c', 'd', 'e', 'f'};
+
+
+    // --------------------------------------------------------- Public Methods
+
+
+    /**
+     * Encodes the 128 bit (16 bytes) MD5 into a 32 character String.
+     *
+     * @param binaryData Array containing the digest
+     * @return Encoded MD5, or null if encoding failed
+     */
+    public String encode( byte[] binaryData ) {
+
+        if (binaryData.length != 16)
+            return null;
+
+        char[] buffer = new char[32];
+
+        for (int i=0; i<16; i++) {
+            int low = (int) (binaryData[i] & 0x0f);
+            int high = (int) ((binaryData[i] & 0xf0) >> 4);
+            buffer[i*2] = hexadecimal[high];
+            buffer[i*2 + 1] = hexadecimal[low];
+        }
+
+        return new String(buffer);
+
+    }
+
+
+}
+

Added: incubator/roller/trunk/src/org/roller/util/MailUtil.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/MailUtil.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/MailUtil.java (added)
+++ incubator/roller/trunk/src/org/roller/util/MailUtil.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,298 @@
+package org.roller.util;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.SendFailedException;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.Address;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeMessage;
+
+
+public class MailUtil extends Object {
+   
+	private static Log mLogger = 
+		LogFactory.getFactory().getInstance(MailUtil.class);
+
+    // agangolli: Incorporated suggested changes from Ken Blackler.
+
+    /**
+     * This method is used to send a Message with a pre-defined
+     * mime-type.
+     * 
+     * @param from e-mail address of sender
+     * @param to e-mail address(es) of recipients
+     * @param subject subject of e-mail
+     * @param content the body of the e-mail
+     * @param mimeType type of message, i.e. text/plain or text/html
+     * @throws MessagingException the exception to indicate failure
+     */
+    public static void sendMessage
+    (
+    	Session session,
+        String from,
+        String[] to,
+        String[] cc,
+        String[] bcc,
+        String subject,
+        String content,
+        String mimeType
+    ) 
+    throws MessagingException
+    {
+        Message message = new MimeMessage(session);
+
+        // n.b. any default from address is expected to be determined by caller.
+        if (! StringUtils.isEmpty(from)) {
+			InternetAddress sentFrom = new InternetAddress(from);
+			message.setFrom(sentFrom);
+			if (mLogger.isDebugEnabled()) mLogger.debug("e-mail from: " + sentFrom);
+        }
+
+		if (to!=null)
+		{
+			InternetAddress[] sendTo = new InternetAddress[to.length];
+	
+			for (int i = 0; i < to.length; i++) 
+			{
+				sendTo[i] = new InternetAddress(to[i]);
+				if (mLogger.isDebugEnabled()) mLogger.debug("sending e-mail to: " + to[i]);
+			}
+			message.setRecipients(Message.RecipientType.TO, sendTo);
+		}
+
+		if (cc != null) 
+		{
+			InternetAddress[] copyTo = new InternetAddress[cc.length];
+
+			for (int i = 0; i < cc.length; i++) 
+			{
+				copyTo[i] = new InternetAddress(cc[i]);
+				if (mLogger.isDebugEnabled()) mLogger.debug("copying e-mail to: " + cc[i]);
+			}
+			message.setRecipients(Message.RecipientType.CC, copyTo);
+		}	        
+
+		if (bcc != null) 
+		{
+			InternetAddress[] copyTo = new InternetAddress[bcc.length];
+
+			for (int i = 0; i < bcc.length; i++) 
+			{
+				copyTo[i] = new InternetAddress(bcc[i]);
+				if (mLogger.isDebugEnabled()) mLogger.debug("blind copying e-mail to: " + bcc[i]);
+			}
+			message.setRecipients(Message.RecipientType.BCC, copyTo);
+		}	        
+        message.setSubject((subject == null) ? "(no subject)" : subject);
+        message.setContent(content, mimeType);
+
+		// First collect all the addresses together.
+        Address[] remainingAddresses = message.getAllRecipients();
+        int nAddresses = remainingAddresses.length;
+        boolean bFailedToSome = false;
+        
+        SendFailedException sendex = new SendFailedException("Unable to send message to some recipients");
+        
+		// Try to send while there remain some potentially good addresses
+		do
+        {
+			// Avoid a loop if we are stuck
+			nAddresses = remainingAddresses.length;
+
+			try
+			{
+				// Send to the list of remaining addresses, ignoring the addresses attached to the message
+		        Transport.send(message,remainingAddresses);
+			}
+			catch(SendFailedException ex)
+			{
+				bFailedToSome=true;
+				sendex.setNextException(ex);
+				
+				// Extract the remaining potentially good addresses
+				remainingAddresses=ex.getValidUnsentAddresses();
+			}
+        } while (remainingAddresses!=null && remainingAddresses.length>0 && remainingAddresses.length!=nAddresses);
+        
+        if (bFailedToSome) throw sendex;
+    }
+
+    /**
+     * This method is used to send a Text Message.
+     * 
+     * @param from e-mail address of sender
+     * @param to e-mail addresses of recipients
+     * @param subject subject of e-mail
+     * @param content the body of the e-mail
+     * @throws MessagingException the exception to indicate failure
+     */
+    public static void sendTextMessage
+    (
+    	Session session,
+        String from,
+        String[] to,
+        String[] cc,
+        String[] bcc,
+        String subject,
+        String content
+    ) 
+    throws MessagingException
+    {
+        sendMessage(session, from, to, cc, bcc, subject, content, "text/plain; charset=utf-8");
+    }
+    
+	/**
+	 * This method overrides the sendTextMessage to specify
+	 * one receiver and mulitple cc recipients.
+	 * 
+	 * @param from e-mail address of sender
+	 * @param to e-mail addresses of recipients
+	 * @param subject subject of e-mail
+	 * @param content the body of the e-mail
+	 * @throws MessagingException the exception to indicate failure
+	 */
+	public static void sendTextMessage
+	(
+		Session session,
+		String from,
+		String to,
+		String[] cc,
+        String[] bcc,
+		String subject,
+		String content
+	) 
+	throws MessagingException
+	{
+        String[] recipient = null;
+		if (to!=null) recipient = new String[] {to};
+
+		sendMessage(session, from, recipient, cc, bcc, subject, content, "text/plain; charset=utf-8");
+	}
+	
+    /**
+	 * This method overrides the sendTextMessage to specify
+	 * only one receiver and cc recipients, rather than 
+	 * an array of recipients.
+     * 
+     * @param from e-mail address of sender
+     * @param to e-mail address of recipient
+     * @param cc e-mail address of cc recipient
+     * @param subject subject of e-mail
+     * @param content the body of the e-mail
+     * @throws MessagingException the exception to indicate failure
+     */
+    public static void sendTextMessage
+    (
+    	Session session,
+        String from,
+        String to,
+        String cc,
+        String bcc,
+        String subject,
+        String content
+    ) 
+    throws MessagingException
+    {
+        String[] recipient = null;
+        String[] copy = null;
+        String[] bcopy = null;
+
+		if (to!=null) recipient = new String[] {to};
+		if (cc!=null) copy = new String[] {cc};
+		if (bcc!=null) bcopy = new String[] {bcc};
+
+        sendMessage(session, from, recipient, copy, bcopy, subject, content, "text/plain; charset=utf-8");
+    }
+    
+    /**
+     * This method is used to send a HTML Message
+     * 
+     * @param from e-mail address of sender
+     * @param to e-mail address(es) of recipients
+     * @param subject subject of e-mail
+     * @param content the body of the e-mail
+     * @throws MessagingException the exception to indicate failure
+     */
+    public static void sendHTMLMessage
+    (
+    	Session session,
+        String from,
+        String[] to,
+        String[] cc,
+        String[] bcc,
+        String subject,
+        String content
+    ) 
+    throws MessagingException
+    {
+        sendMessage(session, from, to, cc, bcc, subject, content, "text/html; charset=utf-8");
+    }
+    
+    /**
+     * This method overrides the sendHTMLMessage to specify
+     * only one sender, rather than an array of senders.
+     * 
+     * @param from e-mail address of sender
+     * @param to e-mail address of recipients
+     * @param subject subject of e-mail
+     * @param content the body of the e-mail
+     * @throws MessagingException the exception to indicate failure
+     */
+	public static void sendHTMLMessage
+    (
+    	Session session,
+        String from,
+        String to,
+        String cc,
+        String bcc,
+        String subject,
+        String content
+    ) 
+    throws MessagingException
+    {
+        String[] recipient = null;
+        String[] copy = null;
+        String[] bcopy = null;
+
+		if (to!=null) recipient = new String[] {to};
+		if (cc!=null) copy = new String[] {cc};
+		if (bcc!=null) bcopy = new String[] {bcc};
+
+        sendMessage(session, from, recipient, copy, bcopy, subject, content, "text/html; charset=utf-8");
+    }
+    
+	/**
+	 * This method overrides the sendHTMLMessage to specify
+	 * one receiver and mulitple cc recipients.
+	 * 
+	 * @param from e-mail address of sender
+	 * @param to e-mail address of recipient
+	 * @param cc e-mail addresses of recipients
+	 * @param subject subject of e-mail
+	 * @param content the body of the e-mail
+	 * @throws MessagingException the exception to indicate failure
+	 */
+	public static void sendHTMLMessage
+	(
+		Session session,
+		String from,
+		String to,
+		String[] cc,
+		String[] bcc,
+		String subject,
+		String content
+	) 
+	throws MessagingException
+	{
+        String[] recipient = null;
+		if (to!=null) recipient = new String[] {to};
+
+		sendMessage(session, from, recipient, cc, bcc, subject, content, "text/html; charset=utf-8");
+	}
+}
+

Added: incubator/roller/trunk/src/org/roller/util/OldRollerConfig.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/OldRollerConfig.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/OldRollerConfig.java (added)
+++ incubator/roller/trunk/src/org/roller/util/OldRollerConfig.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1 @@
+package org.roller.util;

import java.beans.IntrospectionException;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.betwixt.io.BeanReader;
import org.apache.commons.betwixt.io.BeanWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.roller.RollerException;
import org.roller.pojos.RollerConfigData;

import org.xml.sax.SAXException;


/**
 * Configuration object for Roller.  Reads and writes roller-config.xml.
 * This file is a relic of the old days, back when we used to store the Roller
 * configuration in an XML file. In Roller 0.9.9 and later, this file only 
 * exists to allow use to read only roller-config.xml files on startup and
 * copy them into the database. 
 */
public class OldRollerConfig implements java.io.Serializable
{
    static final long serialVersionUID = -6625873343838437510L;
    
    private static Log mLogger =
        LogFactory.getFactory().getInstance( OldRollerConfig.class );

    /**
     * Absolute URL for site, for cases where infered absolute URL doesn't
     * work.
     */
    protected String mAbsoluteURL = null;

    /** Should Roller cache return RSS pages. */
    protected boolean mRssUseCache = false;

    /** Duration to cache RSS pages (in seconds). */
    protected int mRssCacheTime = 3000;

    /** Does Roller allow the creation of new users. */
    protected boolean mNewUserAllowed = false;

    /** List of usernames with Admin priviledges. */
    protected List mAdminUsers = new ArrayList();

    /** Where to get data for creating new users (new-user.xml). */
    protected String mNewUserData = "/templates";

    /** Where to get Themes presented to new users. */
    protected String mNewUserThemes = "/themes";

    /** List of "editor pages" for the Weblog entry editor. */
    protected List mEditorPages = new ArrayList();

    /** Dis/enble RSS aggregation capabilities. */
    protected boolean mEnableAggregator = false;

    /** Are file uploads enabled. */
    protected boolean mUploadEnabled = false;

    /** The maximum size of each user's upload directory. */
    protected Float mUploadMaxDirMB = new Float( "2" );

    /** The maximum size allowed per uploaded file. */
    protected Float mUploadMaxFileMB = new Float( ".5" );

    /**
     * List of permitted file extensions (not including the "dot"). This
     * attribute is mutually exclusive with uploadForbid.
     */
    protected List mUploadAllow = new ArrayList();

    /**
     * List of forbidden file extensions (not including the "dot"). This
     * attribute is mutually exclusive with uploadAllow.
     */
    protected List mUploadForbid = new ArrayList();

    /**
     * Directory where uploaded files will be stored. May end with a slash.
     * Optional, this value will default to RollerContext.USER_RESOURCES.  If
     * specified, should be a full path on the system harddrive or relative to
     * the WebApp.
     */
    protected String mUploadDir = "";

    /**
     * The path from which the webserver will serve upload files. This values
     * must not end in a slash.
     */
    protected String uploadPath = "/resources";
    protected boolean mMemDebug = false;

    /**
     * Determines if the Comment page will "autoformat" comments.  That is,
     * replace carriage-returns with <br />.
     */
    protected boolean mAutoformatComments = false;

    /** Determines if the Comment page will escape html in comments. */
    protected boolean mEscapeCommentHtml = false;

    /** Determines if e-mailing comments is enabled. */
    protected boolean mEmailComments = false;

    /** Enable linkback extraction. */
    protected boolean mEnableLinkback = false;

    /** Name of this site */
    protected String mSiteName = "Roller-based Site";

    /** Description of this site */
    protected String mSiteDescription = "Roller-based Site";

    /** Site administrator's email address */
    protected String mEmailAddress = "";

    /** Lucene index directory */
    protected String mIndexDir =
        "${user.home}" + File.separator + "roller-index";

    /**
     * Flag for encrypting passwords
     */
    protected boolean mEncryptPasswords = false;
    
    /** Algorithm for encrypting passwords */
    protected String mAlgorithm = "SHA";
    
    public OldRollerConfig()
    {
    }

    public OldRollerConfig( RollerConfigData rConfig )
    {
        this.setAbsoluteURL( rConfig.getAbsoluteURL() );
        this.setRssUseCache( rConfig.getRssUseCache().booleanValue() );
        this.setRssCacheTime( rConfig.getRssCacheTime().intValue() );
        this.setNewUserAllowed( rConfig.getNewUserAllowed().booleanValue() );
        this.setNewUserThemes( rConfig.getUserThemes() );
        this.setEditorPages( rConfig.getEditorPagesList() );
        this.setEnableAggregator( rConfig.getEnableAggregator().booleanValue() );
        this.setUploadEnabled( rConfig.getUploadEnabled().booleanValue() );
        this.setUploadMaxDirMB( new Float( rConfig.getUploadMaxDirMB()
                                                  .doubleValue() ) );
        this.setUploadMaxFileMB( new Float( rConfig.getUploadMaxFileMB()
                                                   .doubleValue() ) );
        this.setUploadAllow( Arrays.asList( rConfig.uploadAllowArray() ) );
        this.setUploadForbid( Arrays.asList( rConfig.uploadForbidArray() ) );
        this.setUploadDir( rConfig.getUploadDir() );
        this.setUploadPath( rConfig.getUploadPath() );
        this.setMemDebug( rConfig.getMemDebug().booleanValue() );
        this.setAutoformatComments( rConfig.getAutoformatComments()
                                           .booleanValue() );
        this.setEscapeCommentHtml( rConfig.getEscapeCommentHtml()
                                          .booleanValue() );
        this.setEmailComments( rConfig.getEmailComments().booleanValue() );
        this.setEnableLinkback( rConfig.getEnableLinkback().booleanValue() );
        this.setSiteName( rConfig.getSiteName() );
        this.setSiteDescription( rConfig.getSiteDescription() );
        this.setEmailAddress( rConfig.getEmailAddress() );
        this.setIndexDir( rConfig.getIndexDir() );
        this.setEncryptPasswords( rConfig.getEncryptPasswords().booleanValue() );
        this.setAlgorithm( rConfig.getAlgorithm() );
    }

    //-------------------------------------- begin requisite getters & setters
    public String getAbsoluteURL()
    {
        return mAbsoluteURL;
    }

    public void setAbsoluteURL( String string )
    {
        mAbsoluteURL = string;
    }

    public boolean getRssUseCache()
    {
        return mRssUseCache;
    }

    public void setRssUseCache( boolean use )
    {
        mRssUseCache = use;
    }

    public int getRssCacheTime()
    {
        return mRssCacheTime;
    }

    public void setRssCacheTime( int cacheTime )
    {
        mRssCacheTime = cacheTime;
    }

    public boolean getNewUserAllowed()
    {
        return mNewUserAllowed;
    }

    public void setNewUserAllowed( boolean use )
    {
        mNewUserAllowed = use;
    }

    public List getAdminUsers()
    {
        return mAdminUsers;
    }

    /**
     * @param _adminUsers
     */
    public void setAdminUsers( List _adminUsers )
    {
        mAdminUsers = _adminUsers;
    }

    /**
     * @param ignore
     */
    public void addAdminUsers( String ignore )
    {
        mAdminUsers.add( ignore );
    }

    public String getNewUserData()
    {
        return mNewUserData;
    }

    /**
     * @param str
     */
    public void setNewUserData( String str )
    {
        mNewUserData = str;
    }

    public String getNewUserThemes()
    {
        return mNewUserThemes;
    }

    /**
     * @param str
     */
    public void setNewUserThemes( String str )
    {
        mNewUserThemes = str;
    }

    public List getEditorPages()
    {
        return mEditorPages;
    }

    /**
     * @param _editorPages
     */
    public void setEditorPages( List _editorPages )
    {
        mEditorPages = _editorPages;
    }

    /**
     * @param ignore
     */
    public void addEditorPages( String ignore )
    {
        mEditorPages.add( ignore );
    }

    public boolean getEnableAggregator()
    {
        return mEnableAggregator;
    }

    public void setEnableAggregator( boolean use )
    {
        mEnableAggregator = use;
    }

    public boolean getUploadEnabled()
    {
        return mUploadEnabled;
    }

    public void setUploadEnabled( boolean use )
    {
        mUploadEnabled = use;
    }

    public Float getUploadMaxDirMB()
    {
        return mUploadMaxDirMB;
    }

    public void setUploadMaxDirMB( Float use )
    {
        mUploadMaxDirMB = use;
    }

    public Float getUploadMaxFileMB()
    {
        return mUploadMaxFileMB;
    }

    public void setUploadMaxFileMB( Float use )
    {
        mUploadMaxFileMB = use;
    }

    public List getUploadAllow()
    {
        return mUploadAllow;
    }

    /**
     * @param _uploadAllow
     */
    public void setUploadAllow( List _uploadAllow )
    {
        mUploadAllow = _uploadAllow;
    }

    /**
     * @param ignore
     */
    public void addUploadAllow( String ignore )
    {
        mUploadAllow.add( ignore );
    }

    public List getUploadForbid()
    {
        return mUploadForbid;
    }

    /**
     * @param _uploadForbid
     */
    public void setUploadForbid( List _uploadForbid )
    {
        mUploadForbid = _uploadForbid;
    }

    /**
     * @param ignore
     */
    public void addUploadForbid( String ignore )
    {
        mUploadForbid.add( ignore );
    }

    public String getUploadDir()
    {
        return mUploadDir;
    }

    /**
     * @param str
     */
    public void setUploadDir( String str )
    {
        mUploadDir = str;
    }

    public String getUploadPath()
    {
        return uploadPath;
    }

    /**
     * @param str
     */
    public void setUploadPath( String str )
    {
        uploadPath = str;
    }

    public boolean getMemDebug()
    {
        return mMemDebug;
    }

    /**
     * Set memory debugging on or off.
     *
     * @param memDebug The mMemDebug to set
     */
    public void setMemDebug( boolean memDebug )
    {
        mMemDebug = memDebug;
    }

    public boolean getAutoformatComments()
    {
        return mAutoformatComments;
    }

    /**
     * @param value
     */
    public void setAutoformatComments( boolean value )
    {
        mAutoformatComments = value;
    }

    public boolean getEscapeCommentHtml()
    {
        return mEscapeCommentHtml;
    }

    /**
     * @param value
     */
    public void setEscapeCommentHtml( boolean value )
    {
        mEscapeCommentHtml = value;
    }

    /**
     * @return boolean
     */
    public boolean getEmailComments()
    {
        return mEmailComments;
    }

    /**
     * Sets the emailComments.
     *
     * @param emailComments The emailComments to set
     */
    public void setEmailComments( boolean emailComments )
    {
        this.mEmailComments = emailComments;
    }

    /**
     * Enable linkback.
     *
     * @return
     */
    public boolean isEnableLinkback()
    {
        return mEnableLinkback;
    }

    /**
     * Enable linkback.
     *
     * @param b
     */
    public void setEnableLinkback( boolean b )
    {
        mEnableLinkback = b;
    }

    /**
     * @return
     */
    public String getSiteDescription()
    {
        return mSiteDescription;
    }

    /**
     * @return
     */
    public String getSiteName()
    {
        return mSiteName;
    }

    /**
     * @param string
     */
    public void setSiteDescription( String string )
    {
        mSiteDescription = string;
    }

    /**
     * @param string
     */
    public void setSiteName( String string )
    {
        mSiteName = string;
    }

    /**
     * @return
     */
    public String getEmailAddress()
    {
        return mEmailAddress;
    }

    /**
     * @param emailAddress
     */
    public void setEmailAddress( String emailAddress )
    {
        mEmailAddress = emailAddress;
    }

    /**
     * @return the index directory
     */
    public String getIndexDir()
    {
        return mIndexDir;
    }

    /**
     * @param indexDir new index directory
     */
    public void setIndexDir( String indexDir )
    {
        mIndexDir = indexDir;
    }

    public boolean getEncryptPasswords()
    {
        return mEncryptPasswords;
    }

    public void setEncryptPasswords( boolean use )
    {
        mEncryptPasswords = use;
    }
    
    /**
     * @return the algorithm for encrypting passwords
     */
    public String getAlgorithm()
    {
        return mAlgorithm;
    }

    /**
     * @param algorithm, the new algorithm
     */
    public void setAlgorithm( String algorithm )
    {
        mAlgorithm = algorithm;
    }
    
    //---------------------------------------- end requisite getters & setters

    /**
     * Convenience method for getAdminUsers.
     *
     * @return
     */
    public String[] adminUsersArray()
    {
        if ( mAdminUsers == null )
        {
            mAdminUsers = new ArrayList();
        }

        return (String[]) mAdminUsers.toArray( new String[mAdminUsers.size()] );
    }

    /**
     * Convenience method for getEditorPages.
     *
     * @return
     */
    public String[] editorPagesArray()
    {
        if ( mEditorPages == null )
        {
            mEditorPages = new ArrayList();
        }

        return (String[]) mEditorPages.toArray( new String[mEditorPages.size()] );
    }

    /**
     * Convenience method for getUploadAllow.
     *
     * @return
     */
    public String[] uploadAllowArray()
    {
        if ( mUploadAllow == null )
        {
            mUploadAllow = new ArrayList();
        }

        return (String[]) mUploadAllow.toArray( new String[mUploadAllow.size()] );
    }

    /**
     * Convenience method for getUploadForbid.
     *
     * @return
     */
    public String[] uploadForbidArray()
    {
        if ( mUploadForbid == null )
        {
            mUploadForbid = new ArrayList();
        }

        return (String[]) mUploadForbid.toArray( new String[mUploadForbid.size()] );
    }

    public void updateValues( OldRollerConfig child )
    {
        this.mAbsoluteURL = child.getAbsoluteURL();
        this.mRssUseCache = child.getRssUseCache();
        this.mRssCacheTime = child.getRssCacheTime();
        this.mNewUserAllowed = child.getNewUserAllowed();
        this.mAdminUsers = child.getAdminUsers();
        this.mNewUserData = child.getNewUserData();
        this.mNewUserThemes = child.getNewUserThemes();
        this.mEditorPages = child.getEditorPages();
        this.mEnableAggregator = child.getEnableAggregator();
        this.mUploadEnabled = child.getUploadEnabled();
        this.mUploadMaxDirMB = child.getUploadMaxDirMB();
        this.mUploadMaxFileMB = child.getUploadMaxFileMB();
        this.mUploadAllow = child.getUploadAllow();
        this.mUploadForbid = child.getUploadForbid();
        this.mUploadDir = child.getUploadDir();
        this.uploadPath = child.getUploadPath();
        this.mMemDebug = child.getMemDebug();
        this.mAutoformatComments = child.getAutoformatComments();
        this.mEscapeCommentHtml = child.getEscapeCommentHtml();
        this.mEmailComments = child.getEmailComments();
        this.mEnableLinkback = child.isEnableLinkback();
        this.mSiteName = child.getSiteName();
        this.mSiteDescription = child.getSiteDescription();
        this.mEmailAddress = child.getEmailAddress();
        this.mIndexDir = child.getIndexDir();
        this.mEncryptPasswords = child.getEncryptPasswords();
        this.mAlgorithm = child.getAlgorithm();
    }

    /**
     * nice output for debugging
     *
     * @return
     */
    public String toString()
    {
        StringBuffer buf = new StringBuffer();

        buf.append( "RollerConfig \n" );

        Class clazz = getClass();

        Field[] fields = clazz.getDeclaredFields();

        try
        {
            AccessibleObject.setAccessible( fields, true );

            for ( int i = 0; i < fields.length; i++ )
            {
                buf.append( "\t[" + fields[i].getName() + "=" +
                            fields[i].get( this ) + "], \n" );
            }
        }
        catch ( Exception e )
        {
            // ignored!
        }

        return buf.toString();
    }

    /**
     * Read the RollerConfig from a file, as specified by a String path.
     *
     * @param path
     *
     * @return
     */
    public static OldRollerConfig readConfig( String path )
    {
        InputStream in = null;

        try
        {
            in = new FileInputStream( path );
            return OldRollerConfig.readConfig( in );
        }
        catch ( Exception e )
        {
            System.out.println( "Exception reading RollerConfig: " +
                                e.getMessage() );
        }
        finally
        {
            try
            {
                if ( in != null )
                {
                    in.close();
                }
            }

            catch ( java.io.IOException ioe )
            {
                System.err.println( "RollerConfig.writeConfig() unable to close InputStream" );
            }
        }

        return new OldRollerConfig();
    }

    /**
     * Read the RollerConfig from a file, as specified by an InputStream.
     *
     * @param in
     *
     * @return
     *
     * @throws RuntimeException
     */
    public static OldRollerConfig readConfig( InputStream in )
    {
        try
        {
            BeanReader reader = new BeanReader();
            reader.setDebug(99);
            reader.registerBeanClass( OldRollerConfig.class );
            return (OldRollerConfig) reader.parse( in );
        }

        catch ( IOException e )
        {
            throw new RuntimeException( "FATAL ERROR reading RollerConfig inputstream.",
                                        e );
        }

        catch ( SAXException e )
        {
            throw new RuntimeException( "FATAL ERROR parsing RollerConfig, file is corrupted?",
                                        e );
        }

        catch ( IntrospectionException e )
        {
            throw new RuntimeException( "FATAL ERROR introspecting RollerConfig bean.",
                                        e );
        }
    }

    /**
     * Write RollerConfig to file, as specified by a String path.
     *
     * @param path
     *
     * @throws RollerException
     */
    public void writeConfig( String path ) throws RollerException
    {
        FileOutputStream out = null;

        try
        {
            out = new FileOutputStream( path );
            writeConfig( out );
        }
        catch ( FileNotFoundException e )
        {
            throw new RollerException( "ERROR file not found: " + path, e );
        }
        finally
        {
            try
            {
                if ( out != null )
                {
                    out.close();
                }
            }
            catch ( java.io.IOException ioe )
            {
                System.err.println( "RollerConfig.writeConfig() unable to close OutputStream" );
            }
        }
    }

    /**
     * Write RollerConfig to file, as specified by an OutputStream.
     *
     * @param out
     *
     * @throws RollerException
     */
    public void writeConfig( OutputStream out ) throws RollerException
    {
        BeanWriter writer = new BeanWriter( out );
        writer.enablePrettyPrint();
        writer.setIndent( "    " );
        writer.setWriteIDs( false );

        try
        {
            writer.write( this );
        }
        catch ( IOException e )
        {
            throw new RollerException( "ERROR writing to roller-config.xml stream.",
                                       e );
        }
        catch ( SAXException e )
        {
            throw new RollerException( "ERROR writing to roller-config.xml stream.",
                                       e );
        }
        catch ( IntrospectionException e )
        {
            throw new RollerException( "ERROR introspecting RollerConfig bean.",
                                       e );
        }
    }

    /**
     * test stuff
     *
     * @param args
     */
    public static void main( String[] args )
    {
        String basedir = System.getProperty( "basedir" );
        String path = "build/roller/WEB-INF/roller-config.xml";
        path = new java.io.File( basedir, path ).getAbsolutePath();
        if ( ( args.length > 0 ) && args[0].equals( "read" ) )
        {
            OldRollerConfig.readConfig( path );
        }
        else if ( ( args.length > 0 ) && args[0].equals( "write" ) ) // write
        {
            path = "build/roller/WEB-INF/roller-config-test.xml";
            path = new java.io.File( basedir, path ).getAbsolutePath();
            OldRollerConfig bean = new OldRollerConfig();

            try
            {
                bean.writeConfig( path );
            }
            catch ( Exception e )
            {
                mLogger.error( "Unexpected exception", e );
            }
        }
        else // both
        {
            OldRollerConfig bean = OldRollerConfig.readConfig( path );
            path = "build/roller/WEB-INF/roller-config-test.xml";
            path = new java.io.File( basedir, path ).getAbsolutePath();

            try
            {
                bean.writeConfig( path );
            }
            catch ( Exception e )
            {
                mLogger.error( "Unexpected exception", e );
            }
        }

        System.out.println( "RollerConfig.main completed" );
    }
}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/roller/util/PojoUtil.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/PojoUtil.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/PojoUtil.java (added)
+++ incubator/roller/trunk/src/org/roller/util/PojoUtil.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,26 @@
+package org.roller.util;
+
+/**
+ * This class contains helper methods
+ * for Roller pojos (only?)
+**/
+public abstract class PojoUtil
+{
+    public static boolean equals(boolean lEquals, Object a, Object b)
+    {
+        if (a == null)
+        {
+            lEquals = lEquals && (b == null);
+        }
+        else
+        {
+            lEquals = lEquals && a.equals(b);
+        }
+        return lEquals;
+    }
+
+    public static int addHashCode(int result, Object a)
+    {
+		return (37 * result) + ((a != null) ? a.hashCode() : 0);
+	}
+}
\ No newline at end of file

Added: incubator/roller/trunk/src/org/roller/util/RandomGUID.java
URL: http://svn.apache.org/viewcvs/incubator/roller/trunk/src/org/roller/util/RandomGUID.java?rev=189695&view=auto
==============================================================================
--- incubator/roller/trunk/src/org/roller/util/RandomGUID.java (added)
+++ incubator/roller/trunk/src/org/roller/util/RandomGUID.java Wed Jun  8 20:18:46 2005
@@ -0,0 +1,230 @@
+package org.roller.util;
+/*
+ * RandomGUID from http://www.javaexchange.com/aboutRandomGUID.html
+ * @version 1.2.1 11/05/02
+ * @author Marc A. Mnich
+ *
+ * From www.JavaExchange.com, Open Software licensing
+ *
+ * 11/05/02 -- Performance enhancement from Mike Dubman.  
+ *             Moved InetAddr.getLocal to static block.  Mike has measured
+ *             a 10 fold improvement in run time.
+ * 01/29/02 -- Bug fix: Improper seeding of nonsecure Random object
+ *             caused duplicate GUIDs to be produced.  Random object
+ *             is now only created once per JVM.
+ * 01/19/02 -- Modified random seeding and added new constructor
+ *             to allow secure random feature.
+ * 01/14/02 -- Added random function seeding with JVM run time
+ *
+ */
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/*
+ * In the multitude of java GUID generators, I found none that
+ * guaranteed randomness.  GUIDs are guaranteed to be globally unique
+ * by using ethernet MACs, IP addresses, time elements, and sequential
+ * numbers.  GUIDs are not expected to be random and most often are
+ * easy/possible to guess given a sample from a given generator.
+ * SQL Server, for example generates GUID that are unique but
+ * sequencial within a given instance.
+ *
+ * GUIDs can be used as security devices to hide things such as
+ * files within a filesystem where listings are unavailable (e.g. files
+ * that are served up from a Web server with indexing turned off).
+ * This may be desireable in cases where standard authentication is not
+ * appropriate. In this scenario, the RandomGUIDs are used as directories.
+ * Another example is the use of GUIDs for primary keys in a database
+ * where you want to ensure that the keys are secret.  Random GUIDs can
+ * then be used in a URL to prevent hackers (or users) from accessing
+ * records by guessing or simply by incrementing sequential numbers.
+ *
+ * There are many other possiblities of using GUIDs in the realm of
+ * security and encryption where the element of randomness is important.
+ * This class was written for these purposes but can also be used as a
+ * general purpose GUID generator as well.
+ *
+ * RandomGUID generates truly random GUIDs by using the system's
+ * IP address (name/IP), system time in milliseconds (as an integer),
+ * and a very large random number joined together in a single String
+ * that is passed through an MD5 hash.  The IP address and system time
+ * make the MD5 seed globally unique and the random number guarantees
+ * that the generated GUIDs will have no discernable pattern and
+ * cannot be guessed given any number of previously generated GUIDs.
+ * It is generally not possible to access the seed information (IP, time,
+ * random number) from the resulting GUIDs as the MD5 hash algorithm
+ * provides one way encryption.
+ *
+ * ----> Security of RandomGUID: <-----
+ * RandomGUID can be called one of two ways -- with the basic java Random
+ * number generator or a cryptographically strong random generator
+ * (SecureRandom).  The choice is offered because the secure random
+ * generator takes about 3.5 times longer to generate its random numbers
+ * and this performance hit may not be worth the added security
+ * especially considering the basic generator is seeded with a
+ * cryptographically strong random seed.
+ *
+ * Seeding the basic generator in this way effectively decouples
+ * the random numbers from the time component making it virtually impossible
+ * to predict the random number component even if one had absolute knowledge
+ * of the System time.  Thanks to Ashutosh Narhari for the suggestion
+ * of using the static method to prime the basic random generator.
+ *
+ * Using the secure random option, this class compies with the statistical
+ * random number generator tests specified in FIPS 140-2, Security
+ * Requirements for Cryptographic Modules, secition 4.9.1.
+ *
+ * I converted all the pieces of the seed to a String before handing
+ * it over to the MD5 hash so that you could print it out to make
+ * sure it contains the data you expect to see and to give a nice
+ * warm fuzzy.  If you need better performance, you may want to stick
+ * to byte[] arrays.
+ *
+ * I believe that it is important that the algorithm for
+ * generating random GUIDs be open for inspection and modification.
+ * This class is free for all uses.
+ *
+ *
+ * - Marc
+ */
+
+public class RandomGUID extends Object {
+
+    public String valueBeforeMD5 = "";
+    public String valueAfterMD5 = "";
+    private static Random myRand;
+    private static SecureRandom mySecureRand;
+
+    private static String s_id;
+
+    /*
+     * Static block to take care of one time secureRandom seed.
+     * It takes a few seconds to initialize SecureRandom.  You might
+     * want to consider removing this static block or replacing
+     * it with a "time since first loaded" seed to reduce this time.
+     * This block will run only once per JVM instance.
+     */
+
+    static {
+        mySecureRand = new SecureRandom();
+        long secureInitializer = mySecureRand.nextLong();
+        myRand = new Random(secureInitializer);
+        try {
+            s_id = InetAddress.getLocalHost().toString();
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+
+    /*
+     * Default constructor.  With no specification of security option,
+     * this constructor defaults to lower security, high performance.
+     */
+    public RandomGUID() {
+        getRandomGUID(false);
+    }
+
+    /*
+     * Constructor with security option.  Setting secure true
+     * enables each random number generated to be cryptographically
+     * strong.  Secure false defaults to the standard Random function seeded
+     * with a single cryptographically strong random number.
+     */
+    public RandomGUID(boolean secure) {
+        getRandomGUID(secure);
+    }
+
+    /*
+     * Method to generate the random GUID
+     */
+    private void getRandomGUID(boolean secure) {
+        MessageDigest md5 = null;
+        StringBuffer sbValueBeforeMD5 = new StringBuffer();
+
+        try {
+            md5 = MessageDigest.getInstance("MD5");
+        } catch (NoSuchAlgorithmException e) {
+            System.out.println("Error: " + e);
+        }
+
+        try {
+            long time = System.currentTimeMillis();
+            long rand = 0;
+
+            if (secure) {
+                rand = mySecureRand.nextLong();
+            } else {
+                rand = myRand.nextLong();
+            }
+
+            // This StringBuffer can be a long as you need; the MD5
+            // hash will always return 128 bits.  You can change
+            // the seed to include anything you want here.
+            // You could even stream a file through the MD5 making
+            // the odds of guessing it at least as great as that
+            // of guessing the contents of the file!
+            sbValueBeforeMD5.append(s_id);
+            sbValueBeforeMD5.append(":");
+            sbValueBeforeMD5.append(Long.toString(time));
+            sbValueBeforeMD5.append(":");
+            sbValueBeforeMD5.append(Long.toString(rand));
+
+            valueBeforeMD5 = sbValueBeforeMD5.toString();
+            md5.update(valueBeforeMD5.getBytes());
+
+            byte[] array = md5.digest();
+            StringBuffer sb = new StringBuffer();
+            for (int j = 0; j < array.length; ++j) {
+                int b = array[j] & 0xFF;
+                if (b < 0x10) sb.append('0');
+                sb.append(Integer.toHexString(b));
+            }
+
+            valueAfterMD5 = sb.toString();
+
+        } catch (Exception e) {
+            System.out.println("Error:" + e);
+        }
+    }
+
+
+    /*
+     * Convert to the standard format for GUID
+     * (Useful for SQL Server UniqueIdentifiers, etc.)
+     * Example: C2FEEEAC-CFCD-11D1-8B05-00600806D9B6
+     */
+    public String toString() {
+        String raw = valueAfterMD5.toUpperCase();
+        StringBuffer sb = new StringBuffer();
+        sb.append(raw.substring(0, 8));
+        sb.append("-");
+        sb.append(raw.substring(8, 12));
+        sb.append("-");
+        sb.append(raw.substring(12, 16));
+        sb.append("-");
+        sb.append(raw.substring(16, 20));
+        sb.append("-");
+        sb.append(raw.substring(20));
+
+        return sb.toString();
+    }
+
+    /*
+     * Demonstraton and self test of class
+     */
+    public static void main(String args[]) {
+        for (int i=0; i< 100; i++) {
+            RandomGUID myGUID = new RandomGUID();
+            System.out.println("Seeding String=" + myGUID.valueBeforeMD5);
+            System.out.println("rawGUID=" + myGUID.valueAfterMD5);
+            System.out.println("RandomGUID=" + myGUID.toString());
+        }
+    }
+}