001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.blogs.util;
016    
017    import com.liferay.portal.kernel.language.LanguageUtil;
018    import com.liferay.portal.kernel.log.Log;
019    import com.liferay.portal.kernel.log.LogFactoryUtil;
020    import com.liferay.portal.kernel.portlet.FriendlyURLMapper;
021    import com.liferay.portal.kernel.portlet.FriendlyURLMapperThreadLocal;
022    import com.liferay.portal.kernel.util.ArrayUtil;
023    import com.liferay.portal.kernel.util.GetterUtil;
024    import com.liferay.portal.kernel.util.HttpUtil;
025    import com.liferay.portal.kernel.util.LocaleUtil;
026    import com.liferay.portal.kernel.util.StringBundler;
027    import com.liferay.portal.kernel.util.StringPool;
028    import com.liferay.portal.kernel.util.StringUtil;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.kernel.workflow.WorkflowConstants;
031    import com.liferay.portal.kernel.xmlrpc.Method;
032    import com.liferay.portal.kernel.xmlrpc.Response;
033    import com.liferay.portal.kernel.xmlrpc.XmlRpcConstants;
034    import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil;
035    import com.liferay.portal.model.Portlet;
036    import com.liferay.portal.service.PortletLocalServiceUtil;
037    import com.liferay.portal.service.ServiceContext;
038    import com.liferay.portal.service.UserLocalServiceUtil;
039    import com.liferay.portal.util.Portal;
040    import com.liferay.portal.util.PortalUtil;
041    import com.liferay.portal.util.PortletKeys;
042    import com.liferay.portal.util.PropsValues;
043    import com.liferay.portlet.blogs.model.BlogsEntry;
044    import com.liferay.portlet.blogs.service.BlogsEntryLocalServiceUtil;
045    import com.liferay.portlet.messageboards.model.MBMessage;
046    import com.liferay.portlet.messageboards.model.MBMessageDisplay;
047    import com.liferay.portlet.messageboards.model.MBThread;
048    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
049    
050    import java.io.IOException;
051    
052    import java.net.URL;
053    
054    import java.util.HashMap;
055    import java.util.List;
056    import java.util.Map;
057    
058    import net.htmlparser.jericho.Element;
059    import net.htmlparser.jericho.Source;
060    import net.htmlparser.jericho.StartTag;
061    import net.htmlparser.jericho.TextExtractor;
062    
063    /**
064     * @author Alexander Chow
065     */
066    public class PingbackMethodImpl implements Method {
067    
068            public static final int ACCESS_DENIED = 49;
069    
070            public static final int GENERIC_FAULT = 0;
071    
072            public static final int PINGBACK_ALREADY_REGISTERED = 48;
073    
074            public static final int SERVER_ERROR = 50;
075    
076            public static final int SOURCE_URI_DOES_NOT_EXIST = 16;
077    
078            public static final int SOURCE_URI_INVALID = 17;
079    
080            public static final int TARGET_URI_DOES_NOT_EXIST = 32;
081    
082            public static final int TARGET_URI_INVALID = 33;
083    
084            @Override
085            public Response execute(long companyId) {
086                    if (!PropsValues.BLOGS_PINGBACK_ENABLED) {
087                            return XmlRpcUtil.createFault(
088                                    XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND,
089                                    "Pingbacks are disabled");
090                    }
091    
092                    Response response = validateSource();
093    
094                    if (response != null) {
095                            return response;
096                    }
097    
098                    try {
099                            BlogsEntry entry = getBlogsEntry(companyId);
100    
101                            if (!entry.isAllowPingbacks()) {
102                                    return XmlRpcUtil.createFault(
103                                            XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND,
104                                            "Pingbacks are disabled");
105                            }
106    
107                            long userId = UserLocalServiceUtil.getDefaultUserId(companyId);
108                            long groupId = entry.getGroupId();
109                            String className = BlogsEntry.class.getName();
110                            long classPK = entry.getEntryId();
111    
112                            MBMessageDisplay messageDisplay =
113                                    MBMessageLocalServiceUtil.getDiscussionMessageDisplay(
114                                            userId, groupId, className, classPK,
115                                            WorkflowConstants.STATUS_APPROVED);
116    
117                            MBThread thread = messageDisplay.getThread();
118    
119                            long threadId = thread.getThreadId();
120                            long parentMessageId = thread.getRootMessageId();
121                            String body =
122                                    "[...] " + getExcerpt() + " [...] [url=" + _sourceUri + "]" +
123                                            LanguageUtil.get(LocaleUtil.getSiteDefault(), "read-more") +
124                                                    "[/url]";
125    
126                            List<MBMessage> messages =
127                                    MBMessageLocalServiceUtil.getThreadMessages(
128                                            threadId, WorkflowConstants.STATUS_APPROVED);
129    
130                            for (MBMessage message : messages) {
131                                    if (message.getBody().equals(body)) {
132                                            return XmlRpcUtil.createFault(
133                                                    PINGBACK_ALREADY_REGISTERED,
134                                                    "Pingback previously registered");
135                                    }
136                            }
137    
138                            ServiceContext serviceContext = new ServiceContext();
139    
140                            String pingbackUserName = LanguageUtil.get(
141                                    LocaleUtil.getSiteDefault(), "pingback");
142    
143                            serviceContext.setAttribute("pingbackUserName", pingbackUserName);
144    
145                            StringBundler sb = new StringBundler(5);
146    
147                            String layoutFullURL = PortalUtil.getLayoutFullURL(
148                                    groupId, PortletKeys.BLOGS);
149    
150                            sb.append(layoutFullURL);
151    
152                            sb.append(Portal.FRIENDLY_URL_SEPARATOR);
153    
154                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
155                                    companyId, PortletKeys.BLOGS);
156    
157                            sb.append(portlet.getFriendlyURLMapping());
158                            sb.append(StringPool.SLASH);
159                            sb.append(entry.getUrlTitle());
160    
161                            serviceContext.setAttribute("redirect", sb.toString());
162    
163                            serviceContext.setLayoutFullURL(layoutFullURL);
164    
165                            MBMessageLocalServiceUtil.addDiscussionMessage(
166                                    userId, StringPool.BLANK, groupId, className, classPK, threadId,
167                                    parentMessageId, StringPool.BLANK, body, serviceContext);
168    
169                            return XmlRpcUtil.createSuccess("Pingback accepted");
170                    }
171                    catch (Exception e) {
172                            if (_log.isDebugEnabled()) {
173                                    _log.debug(e, e);
174                            }
175    
176                            return XmlRpcUtil.createFault(
177                                    TARGET_URI_INVALID, "Error parsing target URI");
178                    }
179            }
180    
181            @Override
182            public String getMethodName() {
183                    return "pingback.ping";
184            }
185    
186            @Override
187            public String getToken() {
188                    return "pingback";
189            }
190    
191            @Override
192            public boolean setArguments(Object[] arguments) {
193                    try {
194                            _sourceUri = (String)arguments[0];
195                            _targetUri = (String)arguments[1];
196    
197                            return true;
198                    }
199                    catch (Exception e) {
200                            return false;
201                    }
202            }
203    
204            protected BlogsEntry getBlogsEntry(long companyId) throws Exception {
205                    BlogsEntry entry = null;
206    
207                    URL url = new URL(_targetUri);
208    
209                    String friendlyURL = url.getPath();
210    
211                    int end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
212    
213                    if (end != -1) {
214                            friendlyURL = friendlyURL.substring(0, end);
215                    }
216    
217                    long plid = PortalUtil.getPlidFromFriendlyURL(companyId, friendlyURL);
218                    long groupId = PortalUtil.getScopeGroupId(plid);
219    
220                    Map<String, String[]> params = new HashMap<String, String[]>();
221    
222                    FriendlyURLMapperThreadLocal.setPRPIdentifiers(
223                            new HashMap<String, String>());
224    
225                    Portlet portlet = PortletLocalServiceUtil.getPortletById(
226                            PortletKeys.BLOGS);
227    
228                    FriendlyURLMapper friendlyURLMapper =
229                            portlet.getFriendlyURLMapperInstance();
230    
231                    friendlyURL = url.getPath();
232    
233                    end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
234    
235                    if (end != -1) {
236                            friendlyURL = friendlyURL.substring(
237                                    end + Portal.FRIENDLY_URL_SEPARATOR.length() - 1);
238                    }
239    
240                    Map<String, Object> requestContext = new HashMap<String, Object>();
241    
242                    friendlyURLMapper.populateParams(friendlyURL, params, requestContext);
243    
244                    String param = getParam(params, "entryId");
245    
246                    if (Validator.isNotNull(param)) {
247                            long entryId = GetterUtil.getLong(param);
248    
249                            entry = BlogsEntryLocalServiceUtil.getEntry(entryId);
250                    }
251                    else {
252                            String urlTitle = getParam(params, "urlTitle");
253    
254                            entry = BlogsEntryLocalServiceUtil.getEntry(groupId, urlTitle);
255                    }
256    
257                    return entry;
258            }
259    
260            protected String getExcerpt() throws IOException {
261                    String html = HttpUtil.URLtoString(_sourceUri);
262    
263                    Source source = new Source(html);
264    
265                    source.fullSequentialParse();
266    
267                    List<Element> elements = source.getAllElements("a");
268    
269                    for (Element element : elements) {
270                            String href = GetterUtil.getString(
271                                    element.getAttributeValue("href"));
272    
273                            if (href.equals(_targetUri)) {
274                                    element = element.getParentElement();
275    
276                                    TextExtractor textExtractor = new TextExtractor(element);
277    
278                                    String body = textExtractor.toString();
279    
280                                    if (body.length() < PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH) {
281                                            element = element.getParentElement();
282    
283                                            if (element != null) {
284                                                    textExtractor = new TextExtractor(element);
285    
286                                                    body = textExtractor.toString();
287                                            }
288                                    }
289    
290                                    return StringUtil.shorten(
291                                            body, PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH);
292                            }
293                    }
294    
295                    return StringPool.BLANK;
296            }
297    
298            protected String getParam(Map<String, String[]> params, String name) {
299                    String[] paramArray = params.get(name);
300    
301                    if (paramArray == null) {
302                            String namespace = PortalUtil.getPortletNamespace(
303                                    PortletKeys.BLOGS);
304    
305                            paramArray = params.get(namespace + name);
306                    }
307    
308                    if (ArrayUtil.isNotEmpty(paramArray)) {
309                            return paramArray[0];
310                    }
311                    else {
312                            return null;
313                    }
314            }
315    
316            protected Response validateSource() {
317                    Source source = null;
318    
319                    try {
320                            String html = HttpUtil.URLtoString(_sourceUri);
321    
322                            source = new Source(html);
323                    }
324                    catch (Exception e) {
325                            return XmlRpcUtil.createFault(
326                                    SOURCE_URI_DOES_NOT_EXIST, "Error accessing source URI");
327                    }
328    
329                    List<StartTag> startTags = source.getAllStartTags("a");
330    
331                    for (StartTag startTag : startTags) {
332                            String href = GetterUtil.getString(
333                                    startTag.getAttributeValue("href"));
334    
335                            if (href.equals(_targetUri)) {
336                                    return null;
337                            }
338                    }
339    
340                    return XmlRpcUtil.createFault(
341                            SOURCE_URI_INVALID, "Could not find target URI in source");
342            }
343    
344            private static Log _log = LogFactoryUtil.getLog(PingbackMethodImpl.class);
345    
346            private String _sourceUri;
347            private String _targetUri;
348    
349    }