| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. The ASF licenses this file to You |
| * 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. For additional information regarding |
| * copyright in this work, please see the NOTICE file in the top level |
| * directory of this distribution. |
| */ |
| |
| package org.apache.roller.weblogger.ui.rendering.servlets; |
| |
| import java.io.IOException; |
| import java.sql.Timestamp; |
| import java.util.Iterator; |
| |
| import javax.servlet.RequestDispatcher; |
| import javax.servlet.ServletConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.http.HttpServlet; |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.commons.validator.routines.UrlValidator; |
| import org.apache.roller.util.RollerConstants; |
| import org.apache.roller.weblogger.WebloggerException; |
| import org.apache.roller.weblogger.config.WebloggerConfig; |
| import org.apache.roller.weblogger.config.WebloggerRuntimeConfig; |
| import org.apache.roller.weblogger.business.search.IndexManager; |
| import org.apache.roller.weblogger.business.WebloggerFactory; |
| import org.apache.roller.weblogger.business.WeblogEntryManager; |
| import org.apache.roller.weblogger.pojos.WeblogEntryComment; |
| import org.apache.roller.weblogger.pojos.WeblogEntryComment.ApprovalStatus; |
| import org.apache.roller.weblogger.pojos.WeblogEntry; |
| import org.apache.roller.weblogger.pojos.Weblog; |
| import org.apache.roller.weblogger.ui.rendering.plugins.comments.CommentAuthenticator; |
| import org.apache.roller.weblogger.ui.rendering.plugins.comments.CommentValidationManager; |
| import org.apache.roller.weblogger.ui.rendering.plugins.comments.DefaultCommentAuthenticator; |
| import org.apache.roller.weblogger.ui.rendering.util.WeblogCommentRequest; |
| import org.apache.roller.weblogger.ui.rendering.util.WeblogEntryCommentForm; |
| import org.apache.roller.weblogger.util.GenericThrottle; |
| import org.apache.roller.weblogger.util.IPBanList; |
| import org.apache.roller.weblogger.util.MailUtil; |
| import org.apache.roller.weblogger.util.I18nMessages; |
| import org.apache.roller.weblogger.util.RollerMessages; |
| import org.apache.roller.weblogger.util.RollerMessages.RollerMessage; |
| import org.apache.roller.weblogger.util.URLUtilities; |
| import org.apache.roller.weblogger.util.Utilities; |
| import org.apache.roller.weblogger.util.cache.CacheManager; |
| |
| /** |
| * The CommentServlet handles all incoming weblog entry comment posts. |
| * |
| * We validate each incoming comment based on various comment settings and if |
| * all checks are passed then the comment is saved. |
| * |
| * Incoming comments are tested against the MT Bannedwordslist. If they are found to |
| * be spam, then they are marked as spam and hidden from view. |
| * |
| * If email notification is turned on, each new comment will result in an email |
| * sent to the blog owner and all who have commented on the same post. |
| */ |
| public class CommentServlet extends HttpServlet { |
| |
| private static Log log = LogFactory.getLog(CommentServlet.class); |
| |
| private CommentAuthenticator authenticator = null; |
| private CommentValidationManager commentValidationManager = null; |
| private GenericThrottle commentThrottle = null; |
| |
| /** |
| * Initialization. |
| */ |
| @Override |
| public void init(ServletConfig servletConfig) throws ServletException { |
| |
| super.init(servletConfig); |
| |
| log.info("Initializing CommentServlet"); |
| |
| // lookup the authenticator we are going to use and instantiate it |
| try { |
| String name = WebloggerConfig |
| .getProperty("comment.authenticator.classname"); |
| Class clazz = Class.forName(name); |
| this.authenticator = (CommentAuthenticator) clazz.newInstance(); |
| } catch (Exception e) { |
| log.error(e); |
| this.authenticator = new DefaultCommentAuthenticator(); |
| } |
| |
| // instantiate a comment validation manager for comment spam checking |
| commentValidationManager = new CommentValidationManager(); |
| |
| // are we doing throttling? |
| if (WebloggerConfig.getBooleanProperty("comment.throttle.enabled")) { |
| |
| int threshold = 25; |
| try { |
| threshold = Integer.parseInt(WebloggerConfig |
| .getProperty("comment.throttle.threshold")); |
| } catch (Exception e) { |
| log.warn( |
| "bad input for config property comment.throttle.threshold", |
| e); |
| } |
| |
| int interval = RollerConstants.MIN_IN_MS; |
| try { |
| interval = Integer.parseInt(WebloggerConfig |
| .getProperty("comment.throttle.interval")); |
| // convert from seconds to milliseconds |
| interval = interval * RollerConstants.SEC_IN_MS; |
| } catch (Exception e) { |
| log.warn( |
| "bad input for config property comment.throttle.interval", |
| e); |
| } |
| |
| int maxEntries = 250; |
| try { |
| maxEntries = Integer.parseInt(WebloggerConfig |
| .getProperty("comment.throttle.maxentries")); |
| } catch (Exception e) { |
| log.warn( |
| "bad input for config property comment.throttle.maxentries", |
| e); |
| } |
| |
| commentThrottle = new GenericThrottle(threshold, interval, |
| maxEntries); |
| |
| log.info("Comment Throttling ENABLED"); |
| } else { |
| log.info("Comment Throttling DISABLED"); |
| } |
| } |
| |
| /** |
| * Handle incoming http GET requests. |
| * |
| * The CommentServlet does not support GET requests, it's a 404. |
| */ |
| @Override |
| public void doGet(HttpServletRequest request, HttpServletResponse response) |
| throws IOException, ServletException { |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| } |
| |
| /** |
| * Service incoming POST requests. |
| * |
| * Here we handle incoming comment postings. |
| */ |
| @Override |
| public void doPost(HttpServletRequest request, HttpServletResponse response) |
| throws IOException, ServletException { |
| |
| String error = null; |
| String dispatch_url; |
| |
| Weblog weblog; |
| WeblogEntry entry; |
| |
| String message = null; |
| RollerMessages messages = new RollerMessages(); |
| |
| // are we doing a preview? or a post? |
| String method = request.getParameter("method"); |
| final boolean preview; |
| if (method != null && method.equals("preview")) { |
| preview = true; |
| messages.addMessage("commentServlet.previewCommentOnly"); |
| log.debug("Handling comment preview post"); |
| } else { |
| preview = false; |
| log.debug("Handling regular comment post"); |
| } |
| |
| // throttling protection against spammers |
| if (commentThrottle != null |
| && commentThrottle.processHit(request.getRemoteAddr())) { |
| |
| log.debug("ABUSIVE " + request.getRemoteAddr()); |
| IPBanList.getInstance().addBannedIp(request.getRemoteAddr()); |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| return; |
| } |
| |
| WeblogCommentRequest commentRequest; |
| try { |
| commentRequest = new WeblogCommentRequest(request); |
| |
| // lookup weblog specified by comment request |
| weblog = WebloggerFactory.getWeblogger().getWeblogManager() |
| .getWeblogByHandle(commentRequest.getWeblogHandle()); |
| |
| if (weblog == null) { |
| throw new WebloggerException("unable to lookup weblog: " |
| + commentRequest.getWeblogHandle()); |
| } |
| |
| // lookup entry specified by comment request |
| entry = commentRequest.getWeblogEntry(); |
| if (entry == null) { |
| throw new WebloggerException("unable to lookup entry: " |
| + commentRequest.getWeblogAnchor()); |
| } |
| |
| // we know what the weblog entry is, so setup our urls |
| dispatch_url = "/roller-ui/rendering/page/" + weblog.getHandle(); |
| if (commentRequest.getLocale() != null) { |
| dispatch_url += "/" + commentRequest.getLocale(); |
| } |
| dispatch_url += "/entry/" |
| + URLUtilities.encode(commentRequest.getWeblogAnchor()); |
| |
| } catch (Exception e) { |
| // some kind of error parsing the request or looking up weblog |
| log.debug("error creating page request", e); |
| response.sendError(HttpServletResponse.SC_NOT_FOUND); |
| return; |
| } |
| |
| log.debug("Doing comment posting for entry = " + entry.getPermalink()); |
| |
| // collect input from request params and construct new comment object |
| // fields: name, email, url, content, notify |
| // TODO: data validation on collected comment data |
| WeblogEntryComment comment = new WeblogEntryComment(); |
| comment.setName(commentRequest.getName()); |
| comment.setEmail(commentRequest.getEmail()); |
| |
| // Validate url |
| if (StringUtils.isNotEmpty(commentRequest.getUrl())) { |
| String theUrl = commentRequest.getUrl().trim().toLowerCase(); |
| StringBuilder url = new StringBuilder(); |
| if (theUrl.startsWith("http://")) { |
| url.append(theUrl); |
| } else if (theUrl.startsWith("https://")) { |
| url.append(theUrl); |
| } else { |
| url.append("http://").append(theUrl); |
| } |
| comment.setUrl(url.toString()); |
| } else { |
| comment.setUrl(""); |
| } |
| |
| comment.setContent(commentRequest.getContent()); |
| comment.setNotify(commentRequest.isNotify()); |
| comment.setWeblogEntry(entry); |
| comment.setRemoteHost(request.getRemoteHost()); |
| comment.setPostTime(new Timestamp(System.currentTimeMillis())); |
| |
| // set comment content-type depending on if html is allowed |
| if (WebloggerRuntimeConfig |
| .getBooleanProperty("users.comments.htmlenabled")) { |
| comment.setContentType("text/html"); |
| } else { |
| comment.setContentType("text/plain"); |
| } |
| |
| // set whatever comment plugins are configured |
| comment.setPlugins(WebloggerRuntimeConfig |
| .getProperty("users.comments.plugins")); |
| |
| WeblogEntryCommentForm cf = new WeblogEntryCommentForm(); |
| cf.setData(comment); |
| if (preview) { |
| cf.setPreview(comment); |
| } |
| |
| I18nMessages messageUtils = I18nMessages.getMessages(commentRequest |
| .getLocaleInstance()); |
| |
| // check if comments are allowed for this entry |
| // this checks site-wide settings, weblog settings, and entry settings |
| if (!entry.getCommentsStillAllowed() || !entry.isPublished()) { |
| error = messageUtils.getString("comments.disabled"); |
| |
| // Must have an email and also must be valid |
| } else if (StringUtils.isEmpty(commentRequest.getEmail()) |
| || StringUtils.isNotEmpty(commentRequest.getEmail()) |
| && !Utilities.isValidEmailAddress(commentRequest.getEmail())) { |
| error = messageUtils |
| .getString("error.commentPostFailedEmailAddress"); |
| log.debug("Email Adddress is invalid : " |
| + commentRequest.getEmail()); |
| // if there is an URL it must be valid |
| } else if (StringUtils.isNotEmpty(comment.getUrl()) |
| && !new UrlValidator(new String[] { "http", "https" }) |
| .isValid(comment.getUrl())) { |
| error = messageUtils.getString("error.commentPostFailedURL"); |
| log.debug("URL is invalid : " + comment.getUrl()); |
| // if this is a real comment post then authenticate request |
| } else if (!preview && !this.authenticator.authenticate(request)) { |
| String[] msg = { request.getParameter("answer") }; |
| error = messageUtils.getString("error.commentAuthFailed", msg); |
| log.debug("Comment failed authentication"); |
| } |
| |
| // bail now if we have already found an error |
| if (error != null) { |
| cf.setError(error); |
| request.setAttribute("commentForm", cf); |
| RequestDispatcher dispatcher = request |
| .getRequestDispatcher(dispatch_url); |
| dispatcher.forward(request, response); |
| return; |
| } |
| |
| int validationScore = commentValidationManager.validateComment(comment, |
| messages); |
| log.debug("Comment Validation score: " + validationScore); |
| |
| if (!preview) { |
| |
| if (validationScore == RollerConstants.PERCENT_100 |
| && weblog.getCommentModerationRequired()) { |
| // Valid comments go into moderation if required |
| comment.setStatus(ApprovalStatus.PENDING); |
| message = messageUtils |
| .getString("commentServlet.submittedToModerator"); |
| } else if (validationScore == RollerConstants.PERCENT_100) { |
| // else they're approved |
| comment.setStatus(ApprovalStatus.APPROVED); |
| message = messageUtils |
| .getString("commentServlet.commentAccepted"); |
| } else { |
| // Invalid comments are marked as spam |
| log.debug("Comment marked as spam"); |
| comment.setStatus(ApprovalStatus.SPAM); |
| error = messageUtils |
| .getString("commentServlet.commentMarkedAsSpam"); |
| |
| // add specific error messages if they exist |
| if (messages.getErrorCount() > 0) { |
| Iterator errors = messages.getErrors(); |
| RollerMessage errorKey; |
| |
| StringBuilder buf = new StringBuilder(); |
| buf.append("<ul>"); |
| while (errors.hasNext()) { |
| errorKey = (RollerMessage) errors.next(); |
| |
| buf.append("<li>"); |
| if (errorKey.getArgs() != null) { |
| buf.append(messageUtils.getString( |
| errorKey.getKey(), errorKey.getArgs())); |
| } else { |
| buf.append(messageUtils.getString(errorKey.getKey())); |
| } |
| buf.append("</li>"); |
| } |
| buf.append("</ul>"); |
| |
| error += buf.toString(); |
| } |
| |
| } |
| |
| try { |
| if (!ApprovalStatus.SPAM.equals(comment.getStatus()) |
| || !WebloggerRuntimeConfig |
| .getBooleanProperty("comments.ignoreSpam.enabled")) { |
| |
| WeblogEntryManager mgr = WebloggerFactory.getWeblogger() |
| .getWeblogEntryManager(); |
| mgr.saveComment(comment); |
| WebloggerFactory.getWeblogger().flush(); |
| |
| // Send email notifications only to subscribers if comment |
| // is 100% valid |
| boolean notifySubscribers = (validationScore == RollerConstants.PERCENT_100); |
| MailUtil.sendEmailNotification(comment, messages, |
| messageUtils, notifySubscribers); |
| |
| // only re-index/invalidate the cache if comment isn't |
| // moderated |
| if (!weblog.getCommentModerationRequired()) { |
| IndexManager manager = WebloggerFactory.getWeblogger() |
| .getIndexManager(); |
| |
| // remove entry before (re)adding it, or in case it |
| // isn't Published |
| manager.removeEntryIndexOperation(entry); |
| |
| // if published, index the entry |
| if (entry.isPublished()) { |
| manager.addEntryIndexOperation(entry); |
| } |
| |
| // Clear all caches associated with comment |
| CacheManager.invalidate(comment); |
| } |
| |
| // comment was successful, clear the comment form |
| cf = new WeblogEntryCommentForm(); |
| } |
| |
| } catch (WebloggerException re) { |
| log.error("Error saving comment", re); |
| error = re.getMessage(); |
| } |
| } |
| |
| // the work has been done, now send the user back to the entry page |
| if (error != null) { |
| cf.setError(error); |
| } |
| if (message != null) { |
| cf.setMessage(message); |
| } |
| request.setAttribute("commentForm", cf); |
| |
| log.debug("comment processed, forwarding to " + dispatch_url); |
| RequestDispatcher dispatcher = request |
| .getRequestDispatcher(dispatch_url); |
| dispatcher.forward(request, response); |
| } |
| |
| } |