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.GetterUtil;
023    import com.liferay.portal.kernel.util.HttpUtil;
024    import com.liferay.portal.kernel.util.LocaleUtil;
025    import com.liferay.portal.kernel.util.StringBundler;
026    import com.liferay.portal.kernel.util.StringPool;
027    import com.liferay.portal.kernel.util.StringUtil;
028    import com.liferay.portal.kernel.util.Validator;
029    import com.liferay.portal.kernel.workflow.WorkflowConstants;
030    import com.liferay.portal.kernel.xmlrpc.Method;
031    import com.liferay.portal.kernel.xmlrpc.Response;
032    import com.liferay.portal.kernel.xmlrpc.XmlRpcConstants;
033    import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil;
034    import com.liferay.portal.model.Portlet;
035    import com.liferay.portal.service.PortletLocalServiceUtil;
036    import com.liferay.portal.service.ServiceContext;
037    import com.liferay.portal.service.UserLocalServiceUtil;
038    import com.liferay.portal.util.Portal;
039    import com.liferay.portal.util.PortalUtil;
040    import com.liferay.portal.util.PortletKeys;
041    import com.liferay.portal.util.PropsValues;
042    import com.liferay.portlet.blogs.model.BlogsEntry;
043    import com.liferay.portlet.blogs.service.BlogsEntryLocalServiceUtil;
044    import com.liferay.portlet.messageboards.model.MBMessage;
045    import com.liferay.portlet.messageboards.model.MBMessageDisplay;
046    import com.liferay.portlet.messageboards.model.MBThread;
047    import com.liferay.portlet.messageboards.service.MBMessageLocalServiceUtil;
048    
049    import java.io.IOException;
050    
051    import java.net.URL;
052    
053    import java.util.HashMap;
054    import java.util.List;
055    import java.util.Map;
056    
057    import net.htmlparser.jericho.Element;
058    import net.htmlparser.jericho.Source;
059    import net.htmlparser.jericho.StartTag;
060    import net.htmlparser.jericho.TextExtractor;
061    
062    /**
063     * @author Alexander Chow
064     */
065    public class PingbackMethodImpl implements Method {
066    
067            public static final int ACCESS_DENIED = 49;
068    
069            public static final int GENERIC_FAULT = 0;
070    
071            public static final int PINGBACK_ALREADY_REGISTERED = 48;
072    
073            public static final int SERVER_ERROR = 50;
074    
075            public static final int SOURCE_URI_DOES_NOT_EXIST = 16;
076    
077            public static final int SOURCE_URI_INVALID = 17;
078    
079            public static final int TARGET_URI_DOES_NOT_EXIST = 32;
080    
081            public static final int TARGET_URI_INVALID = 33;
082    
083            @Override
084            public Response execute(long companyId) {
085                    if (!PropsValues.BLOGS_PINGBACK_ENABLED) {
086                            return XmlRpcUtil.createFault(
087                                    XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND,
088                                    "Pingbacks are disabled");
089                    }
090    
091                    Response response = validateSource();
092    
093                    if (response != null) {
094                            return response;
095                    }
096    
097                    try {
098                            BlogsEntry entry = getBlogsEntry(companyId);
099    
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                            String pingbackUserName = LanguageUtil.get(
140                                    LocaleUtil.getDefault(), "pingback");
141    
142                            serviceContext.setAttribute("pingbackUserName", pingbackUserName);
143    
144                            StringBundler sb = new StringBundler(5);
145    
146                            String layoutFullURL = PortalUtil.getLayoutFullURL(
147                                    groupId, PortletKeys.BLOGS);
148    
149                            sb.append(layoutFullURL);
150    
151                            sb.append(Portal.FRIENDLY_URL_SEPARATOR);
152    
153                            Portlet portlet = PortletLocalServiceUtil.getPortletById(
154                                    companyId, PortletKeys.BLOGS);
155    
156                            sb.append(portlet.getFriendlyURLMapping());
157                            sb.append(StringPool.SLASH);
158                            sb.append(entry.getUrlTitle());
159    
160                            serviceContext.setAttribute("redirect", sb.toString());
161    
162                            serviceContext.setLayoutFullURL(layoutFullURL);
163    
164                            MBMessageLocalServiceUtil.addDiscussionMessage(
165                                    userId, StringPool.BLANK, groupId, className, classPK, threadId,
166                                    parentMessageId, StringPool.BLANK, body, serviceContext);
167    
168                            return XmlRpcUtil.createSuccess("Pingback accepted");
169                    }
170                    catch (Exception e) {
171                            if (_log.isDebugEnabled()) {
172                                    _log.debug(e, e);
173                            }
174    
175                            return XmlRpcUtil.createFault(
176                                    TARGET_URI_INVALID, "Error parsing target URI");
177                    }
178            }
179    
180            @Override
181            public String getMethodName() {
182                    return "pingback.ping";
183            }
184    
185            @Override
186            public String getToken() {
187                    return "pingback";
188            }
189    
190            @Override
191            public boolean setArguments(Object[] arguments) {
192                    try {
193                            _sourceUri = (String)arguments[0];
194                            _targetUri = (String)arguments[1];
195    
196                            return true;
197                    }
198                    catch (Exception e) {
199                            return false;
200                    }
201            }
202    
203            protected BlogsEntry getBlogsEntry(long companyId) throws Exception {
204                    BlogsEntry entry = null;
205    
206                    URL url = new URL(_targetUri);
207    
208                    String friendlyURL = url.getPath();
209    
210                    int end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
211    
212                    if (end != -1) {
213                            friendlyURL = friendlyURL.substring(0, end);
214                    }
215    
216                    long plid = PortalUtil.getPlidFromFriendlyURL(companyId, friendlyURL);
217                    long groupId = PortalUtil.getScopeGroupId(plid);
218    
219                    Map<String, String[]> params = new HashMap<String, String[]>();
220    
221                    FriendlyURLMapperThreadLocal.setPRPIdentifiers(
222                            new HashMap<String, String>());
223    
224                    Portlet portlet = PortletLocalServiceUtil.getPortletById(
225                            PortletKeys.BLOGS);
226    
227                    FriendlyURLMapper friendlyURLMapper =
228                            portlet.getFriendlyURLMapperInstance();
229    
230                    friendlyURL = url.getPath();
231    
232                    end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR);
233    
234                    if (end != -1) {
235                            friendlyURL = friendlyURL.substring(
236                                    end + Portal.FRIENDLY_URL_SEPARATOR.length() - 1);
237                    }
238    
239                    Map<String, Object> requestContext = new HashMap<String, Object>();
240    
241                    friendlyURLMapper.populateParams(friendlyURL, params, requestContext);
242    
243                    String param = getParam(params, "entryId");
244    
245                    if (Validator.isNotNull(param)) {
246                            long entryId = GetterUtil.getLong(param);
247    
248                            entry = BlogsEntryLocalServiceUtil.getEntry(entryId);
249                    }
250                    else {
251                            String urlTitle = getParam(params, "urlTitle");
252    
253                            entry = BlogsEntryLocalServiceUtil.getEntry(groupId, urlTitle);
254                    }
255    
256                    return entry;
257            }
258    
259            protected String getExcerpt() throws IOException {
260                    String html = HttpUtil.URLtoString(_sourceUri);
261    
262                    Source source = new Source(html);
263    
264                    source.fullSequentialParse();
265    
266                    List<Element> elements = source.getAllElements("a");
267    
268                    for (Element element : elements) {
269                            String href = GetterUtil.getString(
270                                    element.getAttributeValue("href"));
271    
272                            if (href.equals(_targetUri)) {
273                                    element = element.getParentElement();
274    
275                                    TextExtractor textExtractor = new TextExtractor(element);
276    
277                                    String body = textExtractor.toString();
278    
279                                    if (body.length() < PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH) {
280                                            element = element.getParentElement();
281    
282                                            if (element != null) {
283                                                    textExtractor = new TextExtractor(element);
284    
285                                                    body = textExtractor.toString();
286                                            }
287                                    }
288    
289                                    return StringUtil.shorten(
290                                            body, PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH);
291                            }
292                    }
293    
294                    return StringPool.BLANK;
295            }
296    
297            protected String getParam(Map<String, String[]> params, String name) {
298                    String[] paramArray = params.get(name);
299    
300                    if (paramArray == null) {
301                            String namespace = PortalUtil.getPortletNamespace(
302                                    PortletKeys.BLOGS);
303    
304                            paramArray = params.get(namespace + name);
305                    }
306    
307                    if ((paramArray != null) && (paramArray.length > 0)) {
308                            return paramArray[0];
309                    }
310                    else {
311                            return null;
312                    }
313            }
314    
315            protected Response validateSource() {
316                    Source source = null;
317    
318                    try {
319                            String html = HttpUtil.URLtoString(_sourceUri);
320    
321                            source = new Source(html);
322                    }
323                    catch (Exception e) {
324                            return XmlRpcUtil.createFault(
325                                    SOURCE_URI_DOES_NOT_EXIST, "Error accessing source URI");
326                    }
327    
328                    List<StartTag> startTags = source.getAllStartTags("a");
329    
330                    for (StartTag startTag : startTags) {
331                            String href = GetterUtil.getString(
332                                    startTag.getAttributeValue("href"));
333    
334                            if (href.equals(_targetUri)) {
335                                    return null;
336                            }
337                    }
338    
339                    return XmlRpcUtil.createFault(
340                            SOURCE_URI_INVALID, "Could not find target URI in source");
341            }
342    
343            private static Log _log = LogFactoryUtil.getLog(PingbackMethodImpl.class);
344    
345            private String _sourceUri;
346            private String _targetUri;
347    
348    }