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.WorkflowConstants;
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             long groupId = entry.getGroupId();
108             String className = BlogsEntry.class.getName();
109             long classPK = entry.getEntryId();
110 
111             MBMessageDisplay messageDisplay =
112                 MBMessageLocalServiceUtil.getDiscussionMessageDisplay(
113                     userId, groupId, className, classPK,
114                     WorkflowConstants.STATUS_APPROVED);
115 
116             MBThread thread = messageDisplay.getThread();
117 
118             long threadId = thread.getThreadId();
119             long parentMessageId = thread.getRootMessageId();
120             String body =
121                 "[...] " + getExcerpt() + " [...] [url=" + _sourceUri + "]" +
122                     LanguageUtil.get(LocaleUtil.getDefault(), "read-more") +
123                         "[/url]";
124 
125             List<MBMessage> messages =
126                 MBMessageLocalServiceUtil.getThreadMessages(
127                     threadId, WorkflowConstants.STATUS_APPROVED);
128 
129             for (MBMessage message : messages) {
130                 if (message.getBody().equals(body)) {
131                     return XmlRpcUtil.createFault(
132                         PINGBACK_ALREADY_REGISTERED,
133                         "Pingback previously registered");
134                 }
135             }
136 
137             ServiceContext serviceContext = new ServiceContext();
138 
139             MBMessageLocalServiceUtil.addDiscussionMessage(
140                 null, userId, StringPool.BLANK, groupId, className, classPK,
141                 threadId, parentMessageId, StringPool.BLANK, body,
142                 serviceContext);
143 
144             return XmlRpcUtil.createSuccess("Pingback accepted");
145         }
146         catch (Exception e) {
147             if (_log.isDebugEnabled()) {
148                 _log.debug(e, e);
149             }
150 
151             return XmlRpcUtil.createFault(
152                 TARGET_URI_INVALID, "Error parsing target URI");
153         }
154     }
155 
156     public String getMethodName() {
157         return "pingback.ping";
158     }
159 
160     public String getToken() {
161         return "pingback";
162     }
163 
164     public boolean setArguments(Object[] arguments) {
165         try {
166             _sourceUri = (String)arguments[0];
167             _targetUri = (String)arguments[1];
168 
169             return true;
170         }
171         catch (Exception e) {
172             return false;
173         }
174     }
175 
176     protected BlogsEntry getBlogsEntry(long companyId) throws Exception {
177         BlogsEntry entry = null;
178 
179         URL url = new URL(_targetUri);
180 
181         String friendlyURL = url.getPath();
182 
183         int end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
184 
185         if (end != -1) {
186             friendlyURL = friendlyURL.substring(0, end);
187         }
188 
189         long plid = PortalUtil.getPlidFromFriendlyURL(companyId, friendlyURL);
190         long groupId = PortalUtil.getScopeGroupId(plid);
191 
192         Map<String, String[]> params = new HashMap<String, String[]>();
193 
194         FriendlyURLMapperThreadLocal.setPRPIdentifiers(
195             new HashMap<String, String>());
196 
197         Portlet portlet =
198             PortletLocalServiceUtil.getPortletById(PortletKeys.BLOGS);
199 
200         FriendlyURLMapper friendlyURLMapper =
201             portlet.getFriendlyURLMapperInstance();
202 
203         friendlyURL = url.getPath();
204 
205         end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
206 
207         if (end != -1) {
208             friendlyURL = friendlyURL.substring(
209                 end + Portal.FRIENDLY_URL_SEPARATOR.length() - 1);
210         }
211 
212         Map<String, Object> requestContext = new HashMap<String, Object>();
213 
214         friendlyURLMapper.populateParams(friendlyURL, params, requestContext);
215 
216         String param = getParam(params, "entryId");
217 
218         if (Validator.isNotNull(param)) {
219             long entryId = GetterUtil.getLong(param);
220 
221             entry = BlogsEntryLocalServiceUtil.getEntry(entryId);
222         }
223         else {
224             String urlTitle = getParam(params, "urlTitle");
225 
226             entry = BlogsEntryLocalServiceUtil.getEntry(groupId, urlTitle);
227         }
228 
229         return entry;
230     }
231 
232     protected String getExcerpt() throws IOException {
233         String html = HttpUtil.URLtoString(_sourceUri);
234 
235         Source source = new Source(html);
236 
237         source.fullSequentialParse();
238 
239         List<Element> elements = source.getAllElements("a");
240 
241         for (Element element : elements) {
242             String href = GetterUtil.getString(
243                 element.getAttributeValue("href"));
244 
245             if (href.equals(_targetUri)) {
246                 element = element.getParentElement();
247 
248                 TextExtractor textExtractor = new TextExtractor(element);
249 
250                 String body = textExtractor.toString();
251 
252                 if (body.length() < PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH) {
253                     element = element.getParentElement();
254 
255                     if (element != null) {
256                         textExtractor = new TextExtractor(element);
257 
258                         body = textExtractor.toString();
259                     }
260                 }
261 
262                 return StringUtil.shorten(
263                     body, PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH);
264             }
265         }
266 
267         return StringPool.BLANK;
268     }
269 
270     protected String getParam(Map<String, String[]> params, String name) {
271         String[] paramArray = params.get(name);
272 
273         if (paramArray == null) {
274             String namespace = PortalUtil.getPortletNamespace(
275                 PortletKeys.BLOGS);
276 
277             paramArray = params.get(namespace + name);
278         }
279 
280         if ((paramArray != null) && (paramArray.length > 0)) {
281             return paramArray[0];
282         }
283         else {
284             return null;
285         }
286     }
287 
288     protected Response validateSource() {
289         Source source = null;
290 
291         try {
292             String html = HttpUtil.URLtoString(_sourceUri);
293 
294             source = new Source(html);
295         }
296         catch (Exception e) {
297             return XmlRpcUtil.createFault(
298                 SOURCE_URI_DOES_NOT_EXIST, "Error accessing source URI");
299         }
300 
301         List<StartTag> startTags = source.getAllStartTags("a");
302 
303         for (StartTag startTag : startTags) {
304             String href = GetterUtil.getString(
305                 startTag.getAttributeValue("href"));
306 
307             if (href.equals(_targetUri)) {
308                 return null;
309             }
310         }
311 
312         return XmlRpcUtil.createFault(
313             SOURCE_URI_INVALID, "Could not find target URI in source");
314     }
315 
316     private static Log _log = LogFactoryUtil.getLog(PingbackMethodImpl.class);
317 
318     private String _sourceUri;
319     private String _targetUri;
320 
321 }