/projects/roller-5.0.1/weblogger-web/src/main/java/org/apache/roller/weblogger/ui/rendering/servlets/CommentServlet.java
Java | 378 lines | 239 code | 66 blank | 73 comment | 44 complexity | 15dde050545ba5abe82cf8cd734f0f8b MD5 | raw file
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. The ASF licenses this file to You
4 * under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License. For additional information regarding
15 * copyright in this work, please see the NOTICE file in the top level
16 * directory of this distribution.
17 */
18
19package org.apache.roller.weblogger.ui.rendering.servlets;
20
21import java.io.IOException;
22import java.sql.Timestamp;
23import java.util.Iterator;
24import javax.servlet.RequestDispatcher;
25import javax.servlet.ServletConfig;
26import javax.servlet.ServletException;
27import javax.servlet.http.HttpServlet;
28import javax.servlet.http.HttpServletRequest;
29import javax.servlet.http.HttpServletResponse;
30import org.apache.commons.logging.Log;
31import org.apache.commons.logging.LogFactory;
32import org.apache.roller.weblogger.WebloggerException;
33import org.apache.roller.weblogger.config.WebloggerConfig;
34import org.apache.roller.weblogger.config.WebloggerRuntimeConfig;
35import org.apache.roller.weblogger.business.search.IndexManager;
36import org.apache.roller.weblogger.business.WebloggerFactory;
37import org.apache.roller.weblogger.business.WeblogEntryManager;
38import org.apache.roller.weblogger.pojos.WeblogEntryComment;
39import org.apache.roller.weblogger.pojos.WeblogEntry;
40import org.apache.roller.weblogger.pojos.Weblog;
41import org.apache.roller.weblogger.ui.rendering.plugins.comments.CommentAuthenticator;
42import org.apache.roller.weblogger.ui.rendering.plugins.comments.CommentValidationManager;
43import org.apache.roller.weblogger.ui.rendering.plugins.comments.DefaultCommentAuthenticator;
44import org.apache.roller.weblogger.ui.rendering.util.WeblogCommentRequest;
45import org.apache.roller.weblogger.ui.rendering.util.WeblogEntryCommentForm;
46import org.apache.roller.weblogger.util.GenericThrottle;
47import org.apache.roller.weblogger.util.IPBanList;
48import org.apache.roller.weblogger.util.MailUtil;
49import org.apache.roller.weblogger.util.I18nMessages;
50import org.apache.roller.weblogger.util.RollerMessages;
51import org.apache.roller.weblogger.util.RollerMessages.RollerMessage;
52import org.apache.roller.weblogger.util.URLUtilities;
53import org.apache.roller.weblogger.util.Utilities;
54import org.apache.roller.weblogger.util.cache.CacheManager;
55
56
57/**
58 * The CommentServlet handles all incoming weblog entry comment posts.
59 *
60 * We validate each incoming comment based on various comment settings and
61 * if all checks are passed then the comment is saved.
62 *
63 * Incoming comments are tested against the MT Blacklist. If they are found
64 * to be spam, then they are marked as spam and hidden from view.
65 *
66 * If email notification is turned on, each new comment will result in an
67 * email sent to the blog owner and all who have commented on the same post.
68 */
69public class CommentServlet extends HttpServlet {
70
71 private static Log log = LogFactory.getLog(CommentServlet.class);
72
73 private CommentAuthenticator authenticator = null;
74 private CommentValidationManager commentValidationManager = null;
75 private GenericThrottle commentThrottle = null;
76
77
78 /**
79 * Initialization.
80 */
81 @Override
82 public void init(ServletConfig servletConfig) throws ServletException {
83
84 super.init(servletConfig);
85
86 log.info("Initializing CommentServlet");
87
88 // lookup the authenticator we are going to use and instantiate it
89 try {
90 String name = WebloggerConfig.getProperty("comment.authenticator.classname");
91 Class clazz = Class.forName(name);
92 this.authenticator = (CommentAuthenticator) clazz.newInstance();
93 } catch(Exception e) {
94 log.error(e);
95 this.authenticator = new DefaultCommentAuthenticator();
96 }
97
98 // instantiate a comment validation manager for comment spam checking
99 commentValidationManager = new CommentValidationManager();
100
101 // instantiate a comment format manager for comment formatting
102 String fmtrs = WebloggerConfig.getProperty("comment.formatter.classnames");
103 String[] formatters = Utilities.stringToStringArray(fmtrs, ",");
104
105 // are we doing throttling?
106 if(WebloggerConfig.getBooleanProperty("comment.throttle.enabled")) {
107
108 int threshold = 25;
109 try {
110 threshold = Integer.parseInt(WebloggerConfig.getProperty("comment.throttle.threshold"));
111 } catch(Exception e) {
112 log.warn("bad input for config property comment.throttle.threshold", e);
113 }
114
115 int interval = 60000;
116 try {
117 interval = Integer.parseInt(WebloggerConfig.getProperty("comment.throttle.interval"));
118 // convert from seconds to milliseconds
119 interval = interval * 1000;
120 } catch(Exception e) {
121 log.warn("bad input for config property comment.throttle.interval", e);
122 }
123
124 int maxEntries = 250;
125 try {
126 maxEntries = Integer.parseInt(WebloggerConfig.getProperty("comment.throttle.maxentries"));
127 } catch(Exception e) {
128 log.warn("bad input for config property comment.throttle.maxentries", e);
129 }
130
131 commentThrottle = new GenericThrottle(threshold, interval, maxEntries);
132
133 log.info("Comment Throttling ENABLED");
134 } else {
135 log.info("Comment Throttling DISABLED");
136 }
137 }
138
139
140 /**
141 * Handle incoming http GET requests.
142 *
143 * The CommentServlet does not support GET requests, it's a 404.
144 */
145 @Override
146 public void doGet(HttpServletRequest request, HttpServletResponse response)
147 throws IOException, ServletException {
148 response.sendError(HttpServletResponse.SC_NOT_FOUND);
149 }
150
151
152 /**
153 * Service incoming POST requests.
154 *
155 * Here we handle incoming comment postings.
156 */
157 @Override
158 public void doPost(HttpServletRequest request, HttpServletResponse response)
159 throws IOException, ServletException {
160
161 String error = null;
162 String dispatch_url = null;
163
164 Weblog weblog = null;
165 WeblogEntry entry = null;
166
167 String message = null;
168 RollerMessages messages = new RollerMessages();
169
170 // are we doing a preview? or a post?
171 String method = request.getParameter("method");
172 final boolean preview;
173 if (method != null && method.equals("preview")) {
174 preview = true;
175 messages.addMessage("commentServlet.previewCommentOnly");
176 log.debug("Handling comment preview post");
177 } else {
178 preview = false;
179 log.debug("Handling regular comment post");
180 }
181
182 // throttling protection against spammers
183 if(commentThrottle != null &&
184 commentThrottle.processHit(request.getRemoteAddr())) {
185
186 log.debug("ABUSIVE "+request.getRemoteAddr());
187 IPBanList.getInstance().addBannedIp(request.getRemoteAddr());
188 response.sendError(HttpServletResponse.SC_NOT_FOUND);
189 return;
190 }
191
192 WeblogCommentRequest commentRequest = null;
193 try {
194 commentRequest = new WeblogCommentRequest(request);
195
196 // lookup weblog specified by comment request
197 weblog = WebloggerFactory.getWeblogger().getWeblogManager()
198 .getWeblogByHandle(commentRequest.getWeblogHandle());
199
200 if(weblog == null) {
201 throw new WebloggerException("unable to lookup weblog: "+
202 commentRequest.getWeblogHandle());
203 }
204
205 // lookup entry specified by comment request
206 entry = commentRequest.getWeblogEntry();
207 if(entry == null) {
208 throw new WebloggerException("unable to lookup entry: "+
209 commentRequest.getWeblogAnchor());
210 }
211
212 // we know what the weblog entry is, so setup our urls
213 dispatch_url = "/roller-ui/rendering/page/"+weblog.getHandle();
214 if(commentRequest.getLocale() != null) {
215 dispatch_url += "/"+commentRequest.getLocale();
216 }
217 dispatch_url += "/entry/"+URLUtilities.encode(commentRequest.getWeblogAnchor());
218
219 } catch (Exception e) {
220 // some kind of error parsing the request or looking up weblog
221 log.debug("error creating page request", e);
222 response.sendError(HttpServletResponse.SC_NOT_FOUND);
223 return;
224 }
225
226
227 log.debug("Doing comment posting for entry = "+entry.getPermalink());
228
229 // collect input from request params and construct new comment object
230 // fields: name, email, url, content, notify
231 // TODO: data validation on collected comment data
232 WeblogEntryComment comment = new WeblogEntryComment();
233 comment.setName(commentRequest.getName());
234 comment.setEmail(commentRequest.getEmail());
235 comment.setUrl(commentRequest.getUrl());
236 comment.setContent(commentRequest.getContent());
237 comment.setNotify(Boolean.valueOf(commentRequest.isNotify()));
238 comment.setWeblogEntry(entry);
239 comment.setRemoteHost(request.getRemoteHost());
240 comment.setPostTime(new Timestamp(System.currentTimeMillis()));
241
242 // set comment content-type depending on if html is allowed
243 if(WebloggerRuntimeConfig.getBooleanProperty("users.comments.htmlenabled")) {
244 comment.setContentType("text/html");
245 } else {
246 comment.setContentType("text/plain");
247 }
248
249 // set whatever comment plugins are configured
250 comment.setPlugins(WebloggerRuntimeConfig.getProperty("users.comments.plugins"));
251
252 WeblogEntryCommentForm cf = new WeblogEntryCommentForm();
253 cf.setData(comment);
254 if (preview) {
255 cf.setPreview(comment);
256 }
257
258 I18nMessages messageUtils = I18nMessages.getMessages(commentRequest.getLocaleInstance());
259
260 // check if comments are allowed for this entry
261 // this checks site-wide settings, weblog settings, and entry settings
262 if(!entry.getCommentsStillAllowed() || !entry.isPublished()) {
263 error = messageUtils.getString("comments.disabled");
264
265 // if this is a real comment post then authenticate request
266 } else if(!preview && !this.authenticator.authenticate(request)) {
267 error = messageUtils.getString("error.commentAuthFailed");
268 log.debug("Comment failed authentication");
269 }
270
271 // bail now if we have already found an error
272 if(error != null) {
273 cf.setError(error);
274 request.setAttribute("commentForm", cf);
275 RequestDispatcher dispatcher = request.getRequestDispatcher(dispatch_url);
276 dispatcher.forward(request, response);
277 return;
278 }
279
280 int validationScore = commentValidationManager.validateComment(comment, messages);
281 log.debug("Comment Validation score: " + validationScore);
282
283 if (!preview) {
284
285 if (validationScore == 100 && weblog.getCommentModerationRequired()) {
286 // Valid comments go into moderation if required
287 comment.setStatus(WeblogEntryComment.PENDING);
288 message = messageUtils.getString("commentServlet.submittedToModerator");
289 } else if (validationScore == 100) {
290 // else they're approved
291 comment.setStatus(WeblogEntryComment.APPROVED);
292 message = messageUtils.getString("commentServlet.commentAccepted");
293 } else {
294 // Invalid comments are marked as spam
295 log.debug("Comment marked as spam");
296 comment.setStatus(WeblogEntryComment.SPAM);
297 error = messageUtils.getString("commentServlet.commentMarkedAsSpam");
298
299 // add specific error messages if they exist
300 if(messages.getErrorCount() > 0) {
301 Iterator errors = messages.getErrors();
302 RollerMessage errorKey = null;
303
304 StringBuilder buf = new StringBuilder();
305 buf.append("<ul>");
306 while(errors.hasNext()) {
307 errorKey = (RollerMessage)errors.next();
308
309 buf.append("<li>");
310 if(errorKey.getArgs() != null) {
311 buf.append(messageUtils.getString(errorKey.getKey(), errorKey.getArgs()));
312 } else {
313 buf.append(messageUtils.getString(errorKey.getKey()));
314 }
315 buf.append("</li>");
316 }
317 buf.append("</ul>");
318
319 error += buf.toString();
320 }
321
322 }
323
324 try {
325 if(!WeblogEntryComment.SPAM.equals(comment.getStatus()) ||
326 !WebloggerRuntimeConfig.getBooleanProperty("comments.ignoreSpam.enabled")) {
327
328 WeblogEntryManager mgr = WebloggerFactory.getWeblogger().getWeblogEntryManager();
329 mgr.saveComment(comment);
330 WebloggerFactory.getWeblogger().flush();
331
332 // Send email notifications only to subscribers if comment is 100% valid
333 boolean notifySubscribers = (validationScore == 100);
334 MailUtil.sendEmailNotification(comment, messages, messageUtils, notifySubscribers);
335
336 // only re-index/invalidate the cache if comment isn't moderated
337 if(!weblog.getCommentModerationRequired()) {
338 IndexManager manager = WebloggerFactory.getWeblogger().getIndexManager();
339
340 // remove entry before (re)adding it, or in case it isn't Published
341 manager.removeEntryIndexOperation(entry);
342
343 // if published, index the entry
344 if (entry.isPublished()) {
345 manager.addEntryIndexOperation(entry);
346 }
347
348 // Clear all caches associated with comment
349 CacheManager.invalidate(comment);
350 }
351
352 // comment was successful, clear the comment form
353 cf = new WeblogEntryCommentForm();
354 }
355
356 } catch (WebloggerException re) {
357 log.error("Error saving comment", re);
358 error = re.getMessage();
359 }
360 }
361
362
363 // the work has been done, now send the user back to the entry page
364 if (error != null) {
365 cf.setError(error);
366 }
367 if (message != null) {
368 cf.setMessage(message);
369 }
370 request.setAttribute("commentForm", cf);
371
372 log.debug("comment processed, forwarding to "+dispatch_url);
373 RequestDispatcher dispatcher =
374 request.getRequestDispatcher(dispatch_url);
375 dispatcher.forward(request, response);
376 }
377
378}