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/10/21 23:46:28 UTC
svn commit: r327589 [43/72] - in /incubator/roller/branches/roller_1.x: ./
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...
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/MetaWeblogAPIHandler.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/MetaWeblogAPIHandler.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/MetaWeblogAPIHandler.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/MetaWeblogAPIHandler.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,479 @@
+
+package org.roller.presentation.xmlrpc;
+
+import java.io.ByteArrayInputStream;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts.util.RequestUtils;
+import org.apache.xmlrpc.XmlRpcException;
+import org.roller.RollerException;
+import org.roller.model.FileManager;
+import org.roller.model.Roller;
+import org.roller.model.WeblogManager;
+import org.roller.pojos.WeblogCategoryData;
+import org.roller.pojos.WeblogEntryData;
+import org.roller.pojos.WebsiteData;
+import org.roller.presentation.RollerContext;
+import org.roller.presentation.RollerRequest;
+import org.roller.util.RollerMessages;
+import org.roller.util.Utilities;
+
+
+/**
+ * Roller XML-RPC Handler for the MetaWeblog API.
+ *
+ * MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi
+ *
+ * @author David M Johnson
+ */
+public class MetaWeblogAPIHandler extends BloggerAPIHandler
+{
+ static final long serialVersionUID = -1364456614935668629L;
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(MetaWeblogAPIHandler.class);
+
+ public MetaWeblogAPIHandler()
+ {
+ super();
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Authenticates a user and returns the categories available in the website
+ *
+ * @param blogid Dummy Value for Roller
+ * @param userid Login for a MetaWeblog user who has permission to post to the blog
+ * @param password Password for said username
+ * @throws Exception
+ * @return
+ */
+ public Object getCategories(String blogid, String userid, String password)
+ throws Exception
+ {
+ mLogger.info("getCategories() Called =====[ SUPPORTED ]=====");
+ mLogger.info(" BlogId: " + blogid);
+ mLogger.info(" UserId: " + userid);
+
+ WebsiteData website = validate(userid,password);
+ RollerRequest rreq = RollerRequest.getRollerRequest();
+ Roller roller = rreq.getRoller();
+ try
+ {
+ Hashtable result = new Hashtable();
+ WeblogManager weblogMgr = roller.getWeblogManager();
+ List cats = weblogMgr.getWeblogCategories(website, false);
+ for (Iterator wbcItr = cats.iterator(); wbcItr.hasNext();) {
+ WeblogCategoryData category = (WeblogCategoryData) wbcItr.next();
+ result.put(category.getPath(), createCategoryStruct(category));
+ }
+ return result;
+ }
+ catch (Exception e)
+ {
+ String msg = "ERROR in MetaWeblogAPIHandler.getCategories";
+ mLogger.error(msg,e);
+ throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+ }
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Edits a given post. Optionally, will publish the blog after making the edit
+ *
+ * @param postid Unique identifier of the post to be changed
+ * @param userid Login for a MetaWeblog user who has permission to post to the blog
+ * @param password Password for said username
+ * @param struct Contents of the post
+ * @param publish If true, the blog will be published immediately after the post is made
+ * @throws org.apache.xmlrpc.XmlRpcException
+ * @return
+ */
+ public boolean editPost(String postid, String userid, String password,
+ Hashtable struct, boolean publish)
+ throws Exception
+ {
+ mLogger.info("editPost() Called ========[ SUPPORTED ]=====");
+ mLogger.info(" PostId: " + postid);
+ mLogger.info(" UserId: " + userid);
+ mLogger.info(" Publish: " + publish);
+
+ validate(userid,password);
+
+ Roller roller = RollerRequest.getRollerRequest().getRoller();
+
+ Hashtable postcontent = struct;
+ String description = (String)postcontent.get("description");
+ String title = (String)postcontent.get("title");
+ if (title == null) title = "";
+
+ Date dateCreated = (Date)postcontent.get("dateCreated");
+ if (dateCreated == null) dateCreated = (Date)postcontent.get("pubDate");
+
+ String cat = null;
+ if ( postcontent.get("categories") != null )
+ {
+ Vector cats = (Vector)postcontent.get("categories");
+ cat = (String)cats.elementAt(0);
+ }
+ mLogger.info(" Title: " + title);
+ mLogger.info(" Category: " + cat);
+
+ try
+ {
+ WeblogManager weblogMgr = roller.getWeblogManager();
+
+ Timestamp current =
+ new Timestamp(System.currentTimeMillis());
+
+ WeblogEntryData entry =
+ weblogMgr.retrieveWeblogEntry(postid);
+
+ if ( !title.equals("") ) entry.setTitle(title);
+ entry.setText(description);
+ entry.setUpdateTime(current);
+ if (dateCreated != null)
+ {
+ entry.setPubTime(new Timestamp(dateCreated.getTime()));
+ }
+ entry.setPublishEntry(Boolean.valueOf(publish));
+
+ if ( cat != null )
+ {
+ // Use first category specified by request
+ WeblogCategoryData cd =
+ weblogMgr.getWeblogCategoryByPath(entry.getWebsite(), cat);
+ entry.setCategory(cd);
+ }
+
+ entry.save();
+ roller.commit();
+ flushPageCache(userid);
+
+ // TODO: Roller timestamps need better than 1 second accuracy
+ // Until then, we can't allow more than one post per second
+ Thread.sleep(1000);
+
+ return true;
+ }
+ catch (Exception e)
+ {
+ String msg = "ERROR in MetaWeblogAPIHandler.editPost";
+ mLogger.error(msg,e);
+ throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+ }
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Makes a new post to a designated blog. Optionally, will publish the blog after making the post
+ *
+ * @param blogid Unique identifier of the blog the post will be added to
+ * @param userid Login for a MetaWeblog user who has permission to post to the blog
+ * @param password Password for said username
+ * @param struct Contents of the post
+ * @param publish If true, the blog will be published immediately after the post is made
+ * @throws org.apache.xmlrpc.XmlRpcException
+ * @return
+ */
+ public String newPost(
+ String blogid, String userid, String password, Hashtable struct, boolean publish)
+ throws Exception
+ {
+ mLogger.info("newPost() Called ===========[ SUPPORTED ]=====");
+ mLogger.info(" BlogId: " + blogid);
+ mLogger.info(" UserId: " + userid);
+ mLogger.info(" Publish: " + publish);
+
+ WebsiteData website = validate(userid,password);
+
+ Hashtable postcontent = struct;
+ String description = (String)postcontent.get("description");
+ String title = (String)postcontent.get("title");
+ if (title == null) title = "";
+
+ Date dateCreated = (Date)postcontent.get("dateCreated");
+ if (dateCreated == null) dateCreated = (Date)postcontent.get("pubDate");
+ if (dateCreated == null) dateCreated = new Date();
+
+ String cat = null;
+ if ( postcontent.get("categories") != null )
+ {
+ Vector cats = (Vector)postcontent.get("categories");
+ if (cats.size() > 0)
+ {
+ // only use the first category passed in
+ cat = (String)cats.elementAt(0);
+ }
+ }
+ mLogger.info(" Title: " + title);
+ mLogger.info(" Category: " + cat);
+
+ try
+ {
+ Roller roller = RollerRequest.getRollerRequest().getRoller();
+ WeblogManager weblogMgr = roller.getWeblogManager();
+
+ Timestamp current =
+ new Timestamp(System.currentTimeMillis());
+
+ WeblogEntryData entry = new WeblogEntryData();
+ entry.setTitle(title);
+ entry.setText(description);
+ entry.setPubTime(new Timestamp(dateCreated.getTime()));
+ entry.setUpdateTime(current);
+ entry.setWebsite(website);
+ entry.setPublishEntry(Boolean.valueOf(publish));
+
+ if ( cat != null )
+ {
+ // Use first category specified by request
+ WeblogCategoryData cd =
+ weblogMgr.getWeblogCategoryByPath(website, cat);
+ entry.setCategory(cd);
+ }
+ else
+ {
+ // Use Blogger API category from user's weblog config
+ entry.setCategory(website.getBloggerCategory());
+ }
+
+ entry.save();
+ roller.commit();
+ flushPageCache(userid);
+
+ // TODO: Roller timestamps need better than 1 second accuracy
+ // Until then, we can't allow more than one post per second
+ Thread.sleep(1000);
+
+ return entry.getId();
+ }
+ catch (Exception e)
+ {
+ String msg = "ERROR in MetaWeblogAPIHandler.newPost";
+ mLogger.error(msg,e);
+ throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+ }
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ *
+ * @param postid
+ * @param userid
+ * @param password
+ * @return
+ * @throws Exception
+ */
+ public Object getPost(String postid, String userid, String password)
+ throws Exception
+ {
+ mLogger.info("getPost() Called =========[ SUPPORTED ]=====");
+ mLogger.info(" PostId: " + postid);
+ mLogger.info(" UserId: " + userid);
+
+ validate(userid,password);
+
+ try
+ {
+ Roller roller = RollerRequest.getRollerRequest().getRoller();
+ WeblogManager weblogMgr = roller.getWeblogManager();
+ WeblogEntryData entry = weblogMgr.retrieveWeblogEntry(postid);
+ return createPostStruct(entry);
+ }
+ catch (Exception e)
+ {
+ String msg = "ERROR in MetaWeblogAPIHandler.getPost";
+ mLogger.error(msg,e);
+ throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+ }
+ }
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Allows user to post a binary object, a file, to Roller. If the file is
+ * allowed by the RollerConfig file-upload settings, then the file will be
+ * placed in the user's upload diretory.
+ */
+ public Object newMediaObject(
+ String blogid, String userid, String password, Hashtable struct)
+ throws Exception
+ {
+ mLogger.debug("newMediaObject() Called =[ SUPPORTED ]=====");
+ mLogger.debug(" BlogId: " + blogid);
+ mLogger.debug(" UserId: " + userid);
+ mLogger.debug(" Password: *********");
+
+ WebsiteData website = validate(userid, password);
+ try
+ {
+ String name = (String) struct.get("name");
+ name = name.replaceAll("/","_");
+ String type = (String) struct.get("type");
+ mLogger.debug("newMediaObject name: " + name);
+ mLogger.debug("newMediaObject type: " + type);
+
+ byte[] bits = (byte[]) struct.get("bits");
+
+ Roller roller = RollerRequest.getRollerRequest().getRoller();
+ FileManager fmgr = roller.getFileManager();
+ RollerMessages msgs = new RollerMessages();
+
+ // If save is allowed by Roller system-wide policies
+ if (fmgr.canSave(website, name, bits.length, msgs))
+ {
+ // Then save the file
+ fmgr.saveFile(
+ website, name, bits.length, new ByteArrayInputStream(bits));
+
+ RollerRequest rreq = RollerRequest.getRollerRequest();
+ HttpServletRequest request = rreq.getRequest();
+
+ // TODO: build URL to uploaded file should be done in FileManager
+ String uploadPath = RollerContext.getUploadPath(
+ request.getSession(true).getServletContext());
+ uploadPath += "/" + website.getUser().getUserName() + "/" + name;
+ String fileLink = RequestUtils.printableURL(
+ RequestUtils.absoluteURL(request, uploadPath));
+
+ Hashtable returnStruct = new Hashtable(1);
+ returnStruct.put("url", fileLink);
+ return returnStruct;
+ }
+ throw new XmlRpcException(UPLOAD_DENIED_EXCEPTION,
+ "File upload denied because:" + msgs.toString());
+ }
+ catch (RollerException e)
+ {
+ String msg = "ERROR in BlooggerAPIHander.newMediaObject";
+ mLogger.error(msg,e);
+ throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+ }
+ }
+
+ /**
+ * Get a list of recent posts for a category
+ *
+ * @param blogid Unique identifier of the blog the post will be added to
+ * @param userid Login for a Blogger user who has permission to post to the blog
+ * @param password Password for said username
+ * @param numposts Number of Posts to Retrieve
+ * @throws XmlRpcException
+ * @return
+ */
+ public Object getRecentPosts(
+ String blogid, String userid, String password, int numposts)
+ throws Exception
+ {
+ mLogger.info("getRecentPosts() Called ===========[ SUPPORTED ]=====");
+ mLogger.info(" BlogId: " + blogid);
+ mLogger.info(" UserId: " + userid);
+ mLogger.info(" Number: " + numposts);
+
+ WebsiteData website = validate(userid,password);
+
+ try
+ {
+ Vector results = new Vector();
+
+ Roller roller = RollerRequest.getRollerRequest().getRoller();
+ WeblogManager weblogMgr = roller.getWeblogManager();
+ if (website != null)
+ {
+ Map entries = weblogMgr.getWeblogEntryObjectMap(
+ website, // userName
+ null, // startDate
+ new Date(), // endDate
+ null, // catName
+ WeblogManager.ALL, // status
+ new Integer(numposts)); // maxEntries
+
+ Iterator iter = entries.values().iterator();
+ while (iter.hasNext())
+ {
+ ArrayList list = (ArrayList) iter.next();
+ Iterator entryIter = list.iterator();
+ while (entryIter.hasNext())
+ {
+ WeblogEntryData entry = (WeblogEntryData)entryIter.next();
+ results.addElement(createPostStruct(entry));
+ }
+ }
+ }
+ return results;
+ }
+ catch (Exception e)
+ {
+ String msg = "ERROR in BlooggerAPIHander.getRecentPosts";
+ mLogger.error(msg,e);
+ throw new XmlRpcException(UNKNOWN_EXCEPTION, msg);
+ }
+ }
+
+ private Hashtable createPostStruct(WeblogEntryData entry)
+ {
+ RollerRequest rreq = RollerRequest.getRollerRequest();
+ HttpServletRequest request = rreq.getRequest();
+ RollerContext rollerCtx = RollerContext.getRollerContext(request);
+ String permalink =
+ rollerCtx.getAbsoluteContextUrl(request) + entry.getPermaLink();
+
+ Hashtable struct = new Hashtable();
+ struct.put("title", entry.getTitle());
+ if (entry.getLink() != null)
+ {
+ struct.put("link", Utilities.escapeHTML(entry.getLink()));
+ }
+
+ struct.put("description", entry.getText());
+ struct.put("pubDate", entry.getPubTime());
+ struct.put("dateCreated", entry.getPubTime());
+ struct.put("guid", Utilities.escapeHTML(permalink));
+ struct.put("permaLink", Utilities.escapeHTML(permalink));
+ struct.put("postid", entry.getId());
+ struct.put("userid", entry.getWebsite().getUser().getId());
+
+ Vector catArray = new Vector();
+ catArray.addElement(entry.getCategory().getPath());
+ struct.put("categories", catArray);
+
+ return struct;
+ }
+
+ private Hashtable createCategoryStruct(WeblogCategoryData category)
+ {
+ RollerRequest rreq = RollerRequest.getRollerRequest();
+ HttpServletRequest req = rreq.getRequest();
+ String contextUrl = RollerContext.getRollerContext(req).getAbsoluteContextUrl(req);
+ String userid = category.getWebsite().getUser().getId();
+
+ Hashtable struct = new Hashtable();
+ struct.put("description", category.getPath());
+
+ String catUrl = contextUrl+"/page/"+userid+"?catname="+category.getPath();
+ catUrl = Utilities.stringReplace(catUrl," ","%20");
+ struct.put("htmlUrl", catUrl);
+
+ String rssUrl = contextUrl+"/rss/"+userid+"?catname="+category.getPath();
+ rssUrl = Utilities.stringReplace(catUrl," ","%20");
+ struct.put("rssUrl",rssUrl);
+
+ return struct;
+ }
+}
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/RollerXMLRPCServlet.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/RollerXMLRPCServlet.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/RollerXMLRPCServlet.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/RollerXMLRPCServlet.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,75 @@
+/*
+ * RollerXMLRPCServlet.java
+ */
+
+package org.roller.presentation.xmlrpc;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.xmlrpc.XmlRpcServer;
+
+import java.io.OutputStream;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Roller's XML RPC Servlet sets up XmlRpcHandler for Blogger/ API.
+ *
+ * @author David M Johnson
+ *
+ * @web.servlet name="RollerXMLRPCServlet"
+ * @web.servlet-mapping url-pattern="/xmlrpc"
+ */
+public class RollerXMLRPCServlet extends HttpServlet
+{
+ static final long serialVersionUID = -4424719615968330852L;
+
+ private static Log mLogger =
+ LogFactory.getFactory().getInstance(RollerXMLRPCServlet.class);
+
+ private transient XmlRpcServer mXmlRpcServer = new XmlRpcServer();
+ private BloggerAPIHandler mBloggerHandler = null;
+ private MetaWeblogAPIHandler mMetaWeblogHandler = null;
+
+ //------------------------------------------------------------------------
+
+ /**
+ * Initializes the servlet.
+ */
+ public void init(ServletConfig config) throws ServletException
+ {
+ super.init(config);
+ try
+ {
+ mBloggerHandler = new BloggerAPIHandler();
+ mXmlRpcServer.addHandler("blogger", mBloggerHandler);
+
+ mMetaWeblogHandler = new MetaWeblogAPIHandler();
+ mXmlRpcServer.addHandler("metaWeblog", mMetaWeblogHandler);
+ }
+ catch (Exception e)
+ {
+ mLogger.error("Initialization of XML-RPC servlet failed", e);
+ }
+ }
+
+ //------------------------------------------------------------------------
+
+ protected void service(HttpServletRequest request,
+ HttpServletResponse response)
+ throws ServletException, java.io.IOException
+ {
+ byte[] result = mXmlRpcServer.execute(request.getInputStream());
+
+ response.setContentType("text/xml");
+ response.setContentLength(result.length);
+
+ OutputStream output = response.getOutputStream();
+ output.write(result);
+ output.flush();
+ }
+}
Added: incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/package.html
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/package.html?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/package.html (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/presentation/xmlrpc/package.html Fri Oct 21 14:27:36 2005
@@ -0,0 +1,16 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <title></title>
+</head>
+<body>
+Roller implementation of Blogger and MetaWeblog APIs.
+
+<p>Blogger API spec can be found at http://plant.blogger.com/api/index.html</p>
+
+<p>See also http://xmlrpc.free-conversant.com/docs/bloggerAPI</p>
+
+<p>MetaWeblog API spec can be found at http://www.xmlrpc.com/metaWeblogApi</p>
+
+</body>
+</html>
Added: incubator/roller/branches/roller_1.x/src/org/roller/util/Blacklist.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/Blacklist.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/Blacklist.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/Blacklist.java Fri Oct 21 14:27:36 2005
@@ -0,0 +1,440 @@
+/*
+ * Created on Nov 11, 2003
+ */
+package org.roller.util;
+
+import org.roller.util.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Based on the list provided by Jay Allen for
+ * MT-Blacklist:
+ * http://www.jayallen.org/projects/mt-blacklist/
+ *
+ * Will provide response whether submitted string
+ * contains an item listed in the supplied blacklist.
+ * This implementation does not do everything
+ * MT-Blacklist does, such as the "Search & De-spam mode".
+ *
+ * @author lance
+ */
+public class Blacklist
+{
+ private static Log mLogger = LogFactory.getLog(Blacklist.class);
+
+ private static Blacklist blacklist;
+
+ public static final String blacklistFile = "blacklist.txt";
+ private static final String blacklistURL = "http://www.jayallen.org/comment_spam/blacklist.txt";
+ private static final String lastUpdateStr = "Last update:";
+
+ // Default location of blacklist file (relative to realPath) in case that uploadDir is null or empty
+ // and realPath is non-null.
+ private static final String DEFAULT_BLACKLIST_DIR = "resources";
+ private String realPath;
+ private String uploadDir;
+
+ private List blacklistStr = new LinkedList();
+ private List blacklistRegex = new LinkedList();
+
+ private Date ifModifiedSince = null;
+
+ /**
+ * Singleton factory method.
+ */
+ public static Blacklist getBlacklist(String realPath, String uploadDir)
+ {
+ if (blacklist == null)
+ {
+ Blacklist temp = new Blacklist(realPath, uploadDir);
+ temp.extractFromFile();
+ blacklist = temp;
+ }
+ return blacklist;
+ }
+
+ /**
+ * This will try to download a new set of Blacklist
+ * rules. If no change has occurred then return
+ * current Blacklist.
+ *
+ * @return New Blacklist if rules have changed,
+ * otherwise return current Blacklist.
+ */
+ public static void checkForUpdate()
+ {
+ blacklist = blacklist.extractFromURL();
+ }
+
+ /**
+ * Hide constructor
+ */
+ private Blacklist(String realPath, String uploadDir)
+ {
+ this.realPath = realPath;
+ this.uploadDir = uploadDir;
+ }
+
+ /**
+ * Read a local file for Blacklist rules.
+ */
+ private void extractFromFile()
+ {
+ InputStream txtStream = getFileInputStream();
+ if (txtStream != null)
+ {
+ readFromStream(txtStream, false);
+ }
+ else
+ {
+ throw new NullPointerException("Unable to load blacklist.txt. " +
+ "Make sure blacklist.txt is in classpath.");
+ }
+ }
+
+ /**
+ * Read in the InputStream for rules.
+ * @param txtStream
+ */
+ private String readFromStream(InputStream txtStream, boolean saveStream)
+ {
+ String line;
+ StringBuffer buf = new StringBuffer();
+ BufferedReader in = null;
+ try
+ {
+ in = new BufferedReader(
+ new InputStreamReader( txtStream, "UTF-8" ) );
+ while ((line = in.readLine()) != null)
+ {
+ if (line.startsWith("#"))
+ {
+ readComment(line);
+ }
+ else
+ {
+ readRule(line);
+ }
+
+ if (saveStream) buf.append(line).append("\n");
+ }
+ }
+ catch (Exception e)
+ {
+ mLogger.error(e);
+ }
+ finally
+ {
+ try
+ {
+ if (in != null) in.close();
+ }
+ catch (IOException e1)
+ {
+ mLogger.error(e1);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Connect to the web for blacklist. Check to
+ * see if a newer version exists before parsing.
+ */
+ private Blacklist extractFromURL()
+ {
+ // now see if we can update it from the web
+ Blacklist oldBlacklist = getBlacklist(realPath, uploadDir);
+ Blacklist newBlacklist = new Blacklist(realPath, uploadDir);
+ try
+ {
+ URL url = new URL(blacklistURL);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ if (oldBlacklist.ifModifiedSince != null)
+ {
+ connection.setRequestProperty("If-Modified-Since",
+ DateUtil.formatRfc822(oldBlacklist.ifModifiedSince));
+ }
+
+ // did the connection return NotModified? If so, no need to parse
+ if ( connection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED)
+ {
+ // we already have a current blacklist
+ return oldBlacklist;
+ }
+
+ // did the connection return a LastModified header?
+ long lastModifiedLong = connection.getHeaderFieldDate("Last-Modified", -1);
+
+ // if no ifModifiedSince, or lastModifiedLong is newer, then read stream
+ if (oldBlacklist.ifModifiedSince == null ||
+ oldBlacklist.ifModifiedSince.getTime() < lastModifiedLong)
+ {
+ String results = newBlacklist.readFromStream( connection.getInputStream(), true );
+
+ // save the new blacklist
+ newBlacklist.writeToFile(results);
+
+ if (newBlacklist.ifModifiedSince == null && lastModifiedLong != -1)
+ {
+ newBlacklist.ifModifiedSince = new Date(lastModifiedLong);
+ }
+
+ return newBlacklist;
+ }
+ }
+ catch (Exception e)
+ {
+ // Catch all exceptions and just log at INFO (should this be WARN?) without a full stacktrace.
+ mLogger.info("Roller Blacklist Update: Unable to update comment spam blacklist due to exception: " + e);
+ }
+ return oldBlacklist;
+ }
+
+ /**
+ * @param str
+ */
+ private void readRule(String str)
+ {
+ if (StringUtils.isEmpty(str)) return; // bad condition
+
+ String rule = str.trim();
+
+ if (str.indexOf("#") > 0) // line has a comment
+ {
+ int commentLoc = str.indexOf("#");
+ rule = str.substring(0, commentLoc-1).trim(); // strip comment
+ }
+
+ if (rule.indexOf( "(" ) > -1) // regex rule
+ {
+ // pre-compile patterns since they will be frequently used
+ blacklistRegex.add(Pattern.compile(rule));
+ }
+ else if (StringUtils.isNotEmpty(rule))
+ {
+ blacklistStr.add(rule);
+ }
+ }
+
+ /**
+ * Try to parse out "Last update" value: 2004/03/08 23:17:30.
+ * @param str
+ */
+ private void readComment(String str)
+ {
+ int lastUpdatePos = str.indexOf(lastUpdateStr);
+ if (lastUpdatePos > -1)
+ {
+ str = str.substring(lastUpdatePos + lastUpdateStr.length());
+ str = str.trim();
+ try
+ {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ ifModifiedSince = DateUtil.parse(str, sdf);
+ }
+ catch (ParseException e)
+ {
+ mLogger.debug("ParseException reading " + str);
+ }
+ }
+ }
+
+ /**
+ * Does the String argument match any of the rules in the blacklist?
+ *
+ * @param str
+ * @return
+ */
+ public boolean isBlacklisted(String str)
+ {
+ if (str == null || StringUtils.isEmpty(str)) return false;
+
+ // First iterate over blacklist, doing indexOf.
+ // Then iterate over blacklistRegex and test.
+ // As soon as there is a hit in either case return true
+
+ // test plain String.indexOf
+ if( testStringRules(str) ) return true;
+
+ // test regex blacklisted
+ return testRegExRules(str);
+ }
+
+ /**
+ * Test String against the RegularExpression rules.
+ *
+ * @param str
+ * @return
+ */
+ private boolean testRegExRules(String str)
+ {
+ boolean hit = false;
+ Pattern testPattern = null;
+ Iterator iter = blacklistRegex.iterator();
+ while (iter.hasNext())
+ {
+ testPattern = (Pattern)iter.next();
+
+ // want to see what it is matching on
+ // if we are in "debug mode"
+ if (mLogger.isDebugEnabled())
+ {
+ Matcher matcher = testPattern.matcher(str);
+ if (matcher.find())
+ {
+ mLogger.debug(matcher.group() + " matched by " + testPattern.pattern());
+ hit = true;
+ break;
+ }
+ }
+ else
+ {
+ if (testPattern.matcher(str).find())
+ {
+ hit = true;
+ break;
+ }
+ }
+ }
+ return hit;
+ }
+
+ /**
+ * Test the String against the String rules,
+ * using simple indexOf.
+ *
+ * @param str
+ * @return
+ */
+ private boolean testStringRules(String str)
+ {
+ String test;
+ Iterator iter = blacklistStr.iterator();
+ boolean hit = false;
+ while (iter.hasNext())
+ {
+ test = (String)iter.next();
+ //System.out.println("check against |" + test + "|");
+ if (str.indexOf(test) > -1)
+ {
+ // want to see what it is matching on
+ if (mLogger.isDebugEnabled())
+ {
+ mLogger.debug("matched:" + test + ":");
+ }
+ hit = true;
+ break;
+ }
+ }
+ return hit;
+ }
+
+ /**
+ * Try reading blacklist.txt from wherever RollerConfig.getUploadDir()
+ * is, otherwise try loading it from web resource (/WEB-INF/).
+ */
+ private InputStream getFileInputStream()
+ {
+ try
+ {
+ // TODO: clean up
+ // This was previously throwing an NPE to get to the exception case
+ // when being called in several places with indexDir==null.
+ // This is just about as bad; it needs to be cleaned up.
+ String path = getBlacklistFilePath();
+ if (path == null)
+ {
+ throw new FileNotFoundException(
+ "null path (indexDir and realPath both null)");
+ }
+ return new FileInputStream( path );
+ }
+ catch (Exception e)
+ {
+ return getClass().getResourceAsStream("/"+blacklistFile);
+ }
+ }
+
+ /**
+ * @param results
+ */
+ private void writeToFile(String results)
+ {
+ FileWriter out = null;
+ String path = getBlacklistFilePath();
+ if (path == null)
+ {
+ mLogger.debug("Not writing blacklist file since directory paths were null.");
+ return;
+ }
+ try
+ {
+ // attempt writing results
+ out = new FileWriter(path);
+ out.write( results.toCharArray() );
+ }
+ catch (Exception e)
+ {
+ mLogger.info("Unable to write new " + path);
+ }
+ finally
+ {
+ try
+ {
+ if (out != null) out.close();
+ }
+ catch (IOException e)
+ {
+ mLogger.error("Unable to close stream to " + path);
+ }
+ }
+ }
+
+ // Added for ROL-612 - TODO: Consider refactoring - nearly duplicate code in FileManagerImpl.
+ private String getBlacklistFilePath()
+ {
+ if (uploadDir == null && realPath==null)
+ {
+ // to preserve existing behavior forced to interpret this differently
+ return null;
+ }
+ if (uploadDir == null || uploadDir.trim().length() == 0)
+ {
+ uploadDir = realPath + File.separator + DEFAULT_BLACKLIST_DIR;
+ }
+ return uploadDir + File.separator + blacklistFile;
+ }
+
+ /**
+ * Return pretty list of String and RegEx rules.
+ */
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer("blacklist ");
+ buf.append(blacklistStr).append("\n");
+ buf.append("Regex blacklist ").append(blacklistRegex);
+ return buf.toString();
+ }
+}
Added: incubator/roller/branches/roller_1.x/src/org/roller/util/CommentSpamChecker.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/CommentSpamChecker.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/CommentSpamChecker.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/CommentSpamChecker.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/DateUtil.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/DateUtil.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/DateUtil.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/DateUtil.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/ISO8601DateParser.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/ISO8601DateParser.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/ISO8601DateParser.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/ISO8601DateParser.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/LRUCache.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/LRUCache.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/LRUCache.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/LRUCache.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/LRUCache2.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/LRUCache2.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/LRUCache2.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/LRUCache2.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/LinkbackExtractor.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/LinkbackExtractor.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/LinkbackExtractor.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/LinkbackExtractor.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/LocaleComparator.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/LocaleComparator.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/LocaleComparator.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/LocaleComparator.java Fri Oct 21 14:27:36 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/branches/roller_1.x/src/org/roller/util/MD5Encoder.java
URL: http://svn.apache.org/viewcvs/incubator/roller/branches/roller_1.x/src/org/roller/util/MD5Encoder.java?rev=327589&view=auto
==============================================================================
--- incubator/roller/branches/roller_1.x/src/org/roller/util/MD5Encoder.java (added)
+++ incubator/roller/branches/roller_1.x/src/org/roller/util/MD5Encoder.java Fri Oct 21 14:27:36 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);
+
+ }
+
+
+}
+