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