1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portlet.blogs.util;
16  
17  import com.liferay.portal.kernel.language.LanguageUtil;
18  import com.liferay.portal.kernel.log.Log;
19  import com.liferay.portal.kernel.log.LogFactoryUtil;
20  import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
21  import com.liferay.portal.kernel.portlet.FriendlyURLMapperThreadLocal;
22  import com.liferay.portal.kernel.util.GetterUtil;
23  import com.liferay.portal.kernel.util.HttpUtil;
24  import com.liferay.portal.kernel.util.LocaleUtil;
25  import com.liferay.portal.kernel.util.StringPool;
26  import com.liferay.portal.kernel.util.StringUtil;
27  import com.liferay.portal.kernel.util.Validator;
28  import com.liferay.portal.kernel.workflow.StatusConstants;
29  import com.liferay.portal.kernel.xmlrpc.Method;
30  import com.liferay.portal.kernel.xmlrpc.Response;
31  import com.liferay.portal.kernel.xmlrpc.XmlRpcConstants;
32  import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil;
33  import com.liferay.portal.model.Portlet;
34  import com.liferay.portal.service.PortletLocalServiceUtil;
35  import com.liferay.portal.service.ServiceContext;
36  import com.liferay.portal.service.UserLocalServiceUtil;
37  import com.liferay.portal.util.Portal;
38  import com.liferay.portal.util.PortalUtil;
39  import com.liferay.portal.util.PortletKeys;
40  import com.liferay.portal.util.PropsValues;
41  import com.liferay.portlet.blogs.model.BlogsEntry;
42  import com.liferay.portlet.blogs.service.BlogsEntryLocalServiceUtil;
43  import com.liferay.portlet.messageboards.model.MBMessage;
44  import com.liferay.portlet.messageboards.model.MBMessageDisplay;
45  import com.liferay.portlet.messageboards.model.MBThread;
46  import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
47  
48  import java.io.IOException;
49  
50  import java.net.URL;
51  
52  import java.util.HashMap;
53  import java.util.List;
54  import java.util.Map;
55  
56  import net.htmlparser.jericho.Element;
57  import net.htmlparser.jericho.Source;
58  import net.htmlparser.jericho.StartTag;
59  import net.htmlparser.jericho.TextExtractor;
60  
61  /**
62   * <a href="PingbackMethodImpl.java.html"><b><i>View Source</i></b></a>
63   *
64   * @author Alexander Chow
65   */
66  public class PingbackMethodImpl implements Method {
67  
68      public static int ACCESS_DENIED = 49;
69  
70      public static int GENERIC_FAULT = 0;
71  
72      public static int PINGBACK_ALREADY_REGISTERED = 48;
73  
74      public static int SERVER_ERROR = 50;
75  
76      public static int SOURCE_URI_DOES_NOT_EXIST = 16;
77  
78      public static int SOURCE_URI_INVALID = 17;
79  
80      public static int TARGET_URI_DOES_NOT_EXIST = 32;
81  
82      public static int TARGET_URI_INVALID = 33;
83  
84      public Response execute(long companyId) {
85          if (!PropsValues.BLOGS_PINGBACK_ENABLED) {
86              return XmlRpcUtil.createFault(
87                  XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND,
88                  "Pingbacks are disabled");
89          }
90  
91          Response response = validateSource();
92  
93          if (response != null) {
94              return response;
95          }
96  
97          try {
98              BlogsEntry entry = getBlogsEntry(companyId);
99  
100             if (!entry.isAllowPingbacks()) {
101                 return XmlRpcUtil.createFault(
102                     XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND,
103                     "Pingbacks are disabled");
104             }
105 
106             long userId = UserLocalServiceUtil.getDefaultUserId(companyId);
107             String className = BlogsEntry.class.getName();
108             long classPK = entry.getEntryId();
109 
110             MBMessageDisplay messageDisplay =
111                 MBMessageLocalServiceUtil.getDiscussionMessageDisplay(
112                     userId, className, classPK, StatusConstants.APPROVED);
113 
114             MBThread thread = messageDisplay.getThread();
115 
116             long threadId = thread.getThreadId();
117             long parentMessageId = thread.getRootMessageId();
118             String body =
119                 "[...] " + getExcerpt() + " [...] [url=" + _sourceUri + "]" +
120                     LanguageUtil.get(LocaleUtil.getDefault(), "read-more") +
121                         "[/url]";
122 
123             List<MBMessage> messages =
124                 MBMessageLocalServiceUtil.getThreadMessages(
125                     threadId, StatusConstants.APPROVED);
126 
127             for (MBMessage message : messages) {
128                 if (message.getBody().equals(body)) {
129                     return XmlRpcUtil.createFault(
130                         PINGBACK_ALREADY_REGISTERED,
131                         "Pingback previously registered");
132                 }
133             }
134 
135             MBMessageLocalServiceUtil.addDiscussionMessage(
136                 userId, StringPool.BLANK, className, classPK, threadId,
137                 parentMessageId, StringPool.BLANK, body, new ServiceContext());
138 
139             return XmlRpcUtil.createSuccess("Pingback accepted");
140         }
141         catch (Exception e) {
142             if (_log.isDebugEnabled()) {
143                 _log.debug(e, e);
144             }
145 
146             return XmlRpcUtil.createFault(
147                 TARGET_URI_INVALID, "Error parsing target URI");
148         }
149     }
150 
151     public String getMethodName() {
152         return "pingback.ping";
153     }
154 
155     public String getToken() {
156         return "pingback";
157     }
158 
159     public boolean setArguments(Object[] arguments) {
160         try {
161             _sourceUri = (String)arguments[0];
162             _targetUri = (String)arguments[1];
163 
164             return true;
165         }
166         catch (Exception e) {
167             return false;
168         }
169     }
170 
171     protected BlogsEntry getBlogsEntry(long companyId) throws Exception {
172         BlogsEntry entry = null;
173 
174         URL url = new URL(_targetUri);
175 
176         String friendlyURL = url.getPath();
177 
178         int end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
179 
180         if (end != -1) {
181             friendlyURL = friendlyURL.substring(0, end);
182         }
183 
184         long plid = PortalUtil.getPlidFromFriendlyURL(companyId, friendlyURL);
185         long groupId = PortalUtil.getScopeGroupId(plid);
186 
187         Map<String, String[]> params = new HashMap<String, String[]>();
188 
189         FriendlyURLMapperThreadLocal.setPRPIdentifiers(
190             new HashMap<String, String>());
191 
192         Portlet portlet =
193             PortletLocalServiceUtil.getPortletById(PortletKeys.BLOGS);
194 
195         FriendlyURLMapper friendlyURLMapper =
196             portlet.getFriendlyURLMapperInstance();
197 
198         friendlyURL = url.getPath();
199 
200         end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
201 
202         if (end != -1) {
203             friendlyURL = friendlyURL.substring(
204                 end + Portal.FRIENDLY_URL_SEPARATOR.length() - 1);
205         }
206 
207         friendlyURLMapper.populateParams(friendlyURL, params);
208 
209         String param = getParam(params, "entryId");
210 
211         if (Validator.isNotNull(param)) {
212             long entryId = GetterUtil.getLong(param);
213 
214             entry = BlogsEntryLocalServiceUtil.getEntry(entryId);
215         }
216         else {
217             String urlTitle = getParam(params, "urlTitle");
218 
219             entry = BlogsEntryLocalServiceUtil.getEntry(groupId, urlTitle);
220         }
221 
222         return entry;
223     }
224 
225     protected String getExcerpt() throws IOException {
226         String html = HttpUtil.URLtoString(_sourceUri);
227 
228         Source source = new Source(html);
229 
230         source.fullSequentialParse();
231 
232         List<Element> elements = source.getAllElements("a");
233 
234         for (Element element : elements) {
235             String href = GetterUtil.getString(
236                 element.getAttributeValue("href"));
237 
238             if (href.equals(_targetUri)) {
239                 element = element.getParentElement();
240 
241                 TextExtractor textExtractor = new TextExtractor(element);
242 
243                 String body = textExtractor.toString();
244 
245                 if (body.length() < PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH) {
246                     element = element.getParentElement();
247 
248                     if (element != null) {
249                         textExtractor = new TextExtractor(element);
250 
251                         body = textExtractor.toString();
252                     }
253                 }
254 
255                 return StringUtil.shorten(
256                     body, PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH);
257             }
258         }
259 
260         return StringPool.BLANK;
261     }
262 
263     protected String getParam(Map<String, String[]> params, String name) {
264         String[] paramArray = params.get(name);
265 
266         if (paramArray == null) {
267             String namespace = PortalUtil.getPortletNamespace(
268                 PortletKeys.BLOGS);
269 
270             paramArray = params.get(namespace + name);
271         }
272 
273         if ((paramArray != null) && (paramArray.length > 0)) {
274             return paramArray[0];
275         }
276         else {
277             return null;
278         }
279     }
280 
281     protected Response validateSource() {
282         Source source = null;
283 
284         try {
285             String html = HttpUtil.URLtoString(_sourceUri);
286 
287             source = new Source(html);
288         }
289         catch (Exception e) {
290             return XmlRpcUtil.createFault(
291                 SOURCE_URI_DOES_NOT_EXIST, "Error accessing source URI");
292         }
293 
294         List<StartTag> startTags = source.getAllStartTags("a");
295 
296         for (StartTag startTag : startTags) {
297             String href = GetterUtil.getString(
298                 startTag.getAttributeValue("href"));
299 
300             if (href.equals(_targetUri)) {
301                 return null;
302             }
303         }
304 
305         return XmlRpcUtil.createFault(
306             SOURCE_URI_INVALID, "Could not find target URI in source");
307     }
308 
309     private static Log _log = LogFactoryUtil.getLog(PingbackMethodImpl.class);
310 
311     private String _sourceUri;
312     private String _targetUri;
313 
314 }