/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.blogs.internal.util; import com.liferay.blogs.model.BlogsEntry; import com.liferay.blogs.service.BlogsEntryLocalService; import com.liferay.portal.kernel.comment.CommentManager; import com.liferay.portal.kernel.comment.DuplicateCommentException; import com.liferay.portal.kernel.language.LanguageUtil; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.model.Portlet; import com.liferay.portal.kernel.portlet.FriendlyURLMapper; import com.liferay.portal.kernel.portlet.FriendlyURLMapperThreadLocal; import com.liferay.portal.kernel.portlet.PortletProvider; import com.liferay.portal.kernel.portlet.PortletProviderUtil; import com.liferay.portal.kernel.service.IdentityServiceContextFunction; import com.liferay.portal.kernel.service.PortletLocalService; import com.liferay.portal.kernel.service.ServiceContext; import com.liferay.portal.kernel.service.UserLocalService; import com.liferay.portal.kernel.util.ArrayUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.Http; import com.liferay.portal.kernel.util.LocaleUtil; import com.liferay.portal.kernel.util.Portal; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.xmlrpc.Method; import com.liferay.portal.kernel.xmlrpc.Response; import com.liferay.portal.kernel.xmlrpc.XmlRpcConstants; import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil; import com.liferay.portal.util.PropsValues; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.List; import java.util.Map; import net.htmlparser.jericho.Element; import net.htmlparser.jericho.Source; import net.htmlparser.jericho.StartTag; import net.htmlparser.jericho.TextExtractor; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; /** * @author Alexander Chow */ @Component public class PingbackMethodImpl implements Method { public static final int ACCESS_DENIED = 49; public static final int GENERIC_FAULT = 0; public static final int PINGBACK_ALREADY_REGISTERED = 48; public static final int SERVER_ERROR = 50; public static final int SOURCE_URI_DOES_NOT_EXIST = 16; public static final int SOURCE_URI_INVALID = 17; public static final int TARGET_URI_DOES_NOT_EXIST = 32; public static final int TARGET_URI_INVALID = 33; @Override public Response execute(long companyId) { try { Response response = addPingback(companyId); if (response != null) { return response; } return XmlRpcUtil.createSuccess("Pingback accepted"); } catch (DuplicateCommentException dce) { return XmlRpcUtil.createFault( PINGBACK_ALREADY_REGISTERED, "Pingback is already registered: " + dce.getMessage()); } catch (Exception e) { if (_log.isDebugEnabled()) { _log.debug(e, e); } return XmlRpcUtil.createFault( TARGET_URI_INVALID, "Unable to parse target URI"); } } @Override public String getMethodName() { return "pingback.ping"; } @Override public String getToken() { return "pingback"; } @Override public boolean setArguments(Object[] arguments) { try { _sourceURI = (String)arguments[0]; _targetURI = (String)arguments[1]; return true; } catch (Exception e) { if (_log.isDebugEnabled()) { _log.debug(e, e); } return false; } } protected Response addPingback(long companyId) throws Exception { if (!PropsValues.BLOGS_PINGBACK_ENABLED) { return XmlRpcUtil.createFault( XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND, "Pingbacks are disabled"); } Response response = validateSource(); if (response != null) { return response; } BlogsEntry entry = getBlogsEntry(companyId); if (!entry.isAllowPingbacks() || Validator.isNull(entry.getUrlTitle())) { return XmlRpcUtil.createFault( XmlRpcConstants.REQUESTED_METHOD_NOT_FOUND, "Pingbacks are disabled"); } long userId = _userLocalService.getDefaultUserId(companyId); long groupId = entry.getGroupId(); String className = BlogsEntry.class.getName(); long classPK = entry.getEntryId(); String body = "[...] " + getExcerpt() + " [...] [url=" + _sourceURI + "]" + LanguageUtil.get(LocaleUtil.getSiteDefault(), "read-more") + "[/url]"; ServiceContext serviceContext = buildServiceContext( companyId, groupId, entry.getUrlTitle()); _commentManager.addComment( userId, groupId, className, classPK, body, new IdentityServiceContextFunction(serviceContext)); return null; } protected ServiceContext buildServiceContext( long companyId, long groupId, String urlTitle) throws Exception { ServiceContext serviceContext = new ServiceContext(); String pingbackUserName = LanguageUtil.get( LocaleUtil.getSiteDefault(), "pingback"); serviceContext.setAttribute("pingbackUserName", pingbackUserName); StringBundler sb = new StringBundler(5); String portletId = PortletProviderUtil.getPortletId( BlogsEntry.class.getName(), PortletProvider.Action.VIEW); if (Validator.isNull(portletId)) { return serviceContext; } String layoutFullURL = _portal.getLayoutFullURL(groupId, portletId); sb.append(layoutFullURL); sb.append(Portal.FRIENDLY_URL_SEPARATOR); Portlet portlet = _portletLocalService.getPortletById( companyId, portletId); sb.append(portlet.getFriendlyURLMapping()); sb.append(StringPool.SLASH); sb.append(urlTitle); serviceContext.setAttribute("redirect", sb.toString()); serviceContext.setLayoutFullURL(layoutFullURL); return serviceContext; } protected BlogsEntry getBlogsEntry(long companyId) throws Exception { BlogsEntry entry = null; URL url = new URL(_targetURI); String friendlyURL = url.getPath(); int end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR); if (end != -1) { friendlyURL = friendlyURL.substring(0, end); } long plid = _portal.getPlidFromFriendlyURL(companyId, friendlyURL); long groupId = _portal.getScopeGroupId(plid); Map<String, String[]> params = new HashMap<>(); FriendlyURLMapperThreadLocal.setPRPIdentifiers( new HashMap<String, String>()); String portletId = PortletProviderUtil.getPortletId( BlogsEntry.class.getName(), PortletProvider.Action.VIEW); Portlet portlet = _portletLocalService.getPortletById(portletId); FriendlyURLMapper friendlyURLMapper = portlet.getFriendlyURLMapperInstance(); friendlyURL = url.getPath(); end = friendlyURL.indexOf(Portal.FRIENDLY_URL_SEPARATOR); if (end != -1) { friendlyURL = friendlyURL.substring( end + Portal.FRIENDLY_URL_SEPARATOR.length() - 1); } Map<String, Object> requestContext = new HashMap<>(); friendlyURLMapper.populateParams(friendlyURL, params, requestContext); String param = getParam(params, "entryId"); if (Validator.isNotNull(param)) { long entryId = GetterUtil.getLong(param); entry = _blogsEntryLocalService.getEntry(entryId); } else { String urlTitle = getParam(params, "urlTitle"); entry = _blogsEntryLocalService.getEntry(groupId, urlTitle); } return entry; } protected String getExcerpt() throws IOException { String html = _http.URLtoString(_sourceURI); Source source = new Source(html); source.fullSequentialParse(); List<Element> elements = source.getAllElements("a"); for (Element element : elements) { String href = GetterUtil.getString( element.getAttributeValue("href")); if (href.equals(_targetURI)) { element = element.getParentElement(); TextExtractor textExtractor = new TextExtractor(element); String body = textExtractor.toString(); if (body.length() < PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH) { element = element.getParentElement(); if (element != null) { textExtractor = new TextExtractor(element); body = textExtractor.toString(); } } return StringUtil.shorten( body, PropsValues.BLOGS_LINKBACK_EXCERPT_LENGTH); } } return StringPool.BLANK; } protected String getParam(Map<String, String[]> params, String name) { String[] paramArray = params.get(name); if (paramArray == null) { String portletId = PortletProviderUtil.getPortletId( BlogsEntry.class.getName(), PortletProvider.Action.VIEW); String namespace = _portal.getPortletNamespace(portletId); paramArray = params.get(namespace + name); } if (ArrayUtil.isNotEmpty(paramArray)) { return paramArray[0]; } else { return null; } } @Reference(unbind = "-") protected void setBlogsEntryLocalService( BlogsEntryLocalService blogsEntryLocalService) { _blogsEntryLocalService = blogsEntryLocalService; } @Reference(unbind = "-") protected void setPortletLocalService( PortletLocalService portletLocalService) { _portletLocalService = portletLocalService; } @Reference(unbind = "-") protected void setUserLocalService(UserLocalService userLocalService) { _userLocalService = userLocalService; } protected Response validateSource() throws Exception { Source source = null; try { String html = _http.URLtoString(_sourceURI); source = new Source(html); } catch (Exception e) { if (_log.isDebugEnabled()) { _log.debug(e, e); } return XmlRpcUtil.createFault( SOURCE_URI_DOES_NOT_EXIST, "Error accessing source URI"); } List<StartTag> startTags = source.getAllStartTags("a"); for (StartTag startTag : startTags) { String href = GetterUtil.getString( startTag.getAttributeValue("href")); if (href.equals(_targetURI)) { return null; } } return XmlRpcUtil.createFault( SOURCE_URI_INVALID, "Unable to find target URI in source"); } private static final Log _log = LogFactoryUtil.getLog( PingbackMethodImpl.class); private BlogsEntryLocalService _blogsEntryLocalService; @Reference private CommentManager _commentManager; @Reference private Http _http; @Reference private Portal _portal; private PortletLocalService _portletLocalService; private String _sourceURI; private String _targetURI; private UserLocalService _userLocalService; }