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.ibm.icu.util.Calendar;
18  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
19  import com.liferay.portal.kernel.log.Log;
20  import com.liferay.portal.kernel.log.LogFactoryUtil;
21  import com.liferay.portal.kernel.servlet.HttpHeaders;
22  import com.liferay.portal.kernel.util.GetterUtil;
23  import com.liferay.portal.kernel.util.HtmlUtil;
24  import com.liferay.portal.kernel.util.Http;
25  import com.liferay.portal.kernel.util.HttpUtil;
26  import com.liferay.portal.kernel.util.ReleaseInfo;
27  import com.liferay.portal.kernel.util.Tuple;
28  import com.liferay.portal.kernel.util.Validator;
29  import com.liferay.portal.kernel.xmlrpc.Response;
30  import com.liferay.portal.kernel.xmlrpc.XmlRpcException;
31  import com.liferay.portal.kernel.xmlrpc.XmlRpcUtil;
32  import com.liferay.portal.xml.StAXReaderUtil;
33  
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.Date;
37  import java.util.List;
38  import java.util.Map;
39  
40  import javax.xml.stream.XMLInputFactory;
41  import javax.xml.stream.XMLStreamReader;
42  
43  import net.htmlparser.jericho.Source;
44  import net.htmlparser.jericho.StartTag;
45  
46  /**
47   * <a href="LinkbackProducerUtil.java.html"><b><i>View Source</i></b></a>
48   *
49   * @author Alexander Chow
50   */
51  public class LinkbackProducerUtil {
52  
53      public static void sendPingback(String sourceUri, String targetUri)
54          throws Exception {
55  
56          String serverUri = _discoverPingbackServer(targetUri);
57  
58          if (Validator.isNull(serverUri)) {
59              return;
60          }
61  
62          _pingbackQueue.add(
63              new Tuple(new Date(), serverUri, sourceUri, targetUri));
64      }
65  
66      public static synchronized void sendQueuedPingbacks()
67          throws XmlRpcException {
68  
69          Calendar cal = Calendar.getInstance();
70  
71          cal.add(Calendar.MINUTE, -1);
72  
73          Date expiration = cal.getTime();
74  
75          while (!_pingbackQueue.isEmpty()) {
76              Tuple tuple = _pingbackQueue.get(0);
77  
78              Date time = (Date)tuple.getObject(0);
79  
80              if (time.before(expiration)) {
81                  _pingbackQueue.remove(0);
82  
83                  String serverUri = (String)tuple.getObject(1);
84                  String sourceUri = (String)tuple.getObject(2);
85                  String targetUri = (String)tuple.getObject(3);
86  
87                  if (_log.isInfoEnabled()) {
88                      _log.info(
89                          "XML-RPC pingback " + serverUri + ", source " +
90                              sourceUri + ", target " + targetUri);
91                  }
92  
93                  Response response = XmlRpcUtil.executeMethod(
94                      serverUri, "pingback.ping",
95                      new Object[] {sourceUri, targetUri});
96  
97                  if (_log.isInfoEnabled()) {
98                      _log.info(response.toString());
99                  }
100             }
101             else {
102                 break;
103             }
104         }
105     }
106 
107     public static boolean sendTrackback(
108             String trackback, Map<String, String> parts)
109         throws Exception {
110 
111         if (_log.isInfoEnabled()) {
112             _log.info("Pinging trackback " + trackback);
113         }
114 
115         Http.Options options = new Http.Options();
116 
117         options.addHeader(HttpHeaders.USER_AGENT, ReleaseInfo.getServerInfo());
118         options.setLocation(trackback);
119         options.setParts(parts);
120         options.setPost(true);
121 
122         String xml = HttpUtil.URLtoString(options);
123 
124         if (_log.isDebugEnabled()) {
125             _log.debug(xml);
126         }
127 
128         String error = xml;
129 
130         XMLStreamReader xmlStreamReader = null;
131 
132         try {
133             XMLInputFactory xmlInputFactory =
134                 StAXReaderUtil.getXMLInputFactory();
135 
136             xmlStreamReader = xmlInputFactory.createXMLStreamReader(
137                 new UnsyncStringReader(xml));
138 
139             xmlStreamReader.nextTag();
140             xmlStreamReader.nextTag();
141 
142             String name = xmlStreamReader.getLocalName();
143 
144             if (name.equals("error")) {
145                 int status = GetterUtil.getInteger(
146                     xmlStreamReader.getElementText(), 1);
147 
148                 if (status == 0) {
149                     if (_log.isInfoEnabled()) {
150                         _log.info("Trackback accepted");
151                     }
152 
153                     return true;
154                 }
155 
156                 xmlStreamReader.nextTag();
157 
158                 name = xmlStreamReader.getLocalName();
159 
160                 if (name.equals("message")) {
161                     error = xmlStreamReader.getElementText();
162                 }
163             }
164         }
165         finally {
166             if (xmlStreamReader != null) {
167                 try {
168                     xmlStreamReader.close();
169                 }
170                 catch (Exception e) {
171                 }
172             }
173         }
174 
175         _log.error(
176             "Error while pinging trackback at " + trackback + ": " + error);
177 
178         return false;
179     }
180 
181     private static String _discoverPingbackServer(String targetUri) {
182         String serverUri = null;
183 
184         try {
185             Http.Options options = new Http.Options();
186 
187             options.addHeader(
188                 HttpHeaders.USER_AGENT, ReleaseInfo.getServerInfo());
189             options.setLocation(targetUri);
190             options.setHead(true);
191 
192             HttpUtil.URLtoByteArray(options);
193 
194             Http.Response response = options.getResponse();
195 
196             serverUri = response.getHeader("X-Pingback");
197         }
198         catch (Exception e) {
199             _log.error("Unable to call HEAD of " + targetUri, e);
200         }
201 
202         if (Validator.isNull(serverUri)) {
203             try {
204                 Source clientSource = new Source(
205                     HttpUtil.URLtoString(targetUri));
206 
207                 List<StartTag> startTags = clientSource.getAllStartTags("link");
208 
209                 for (StartTag startTag : startTags) {
210                     String rel = startTag.getAttributeValue("rel");
211 
212                     if (rel.equalsIgnoreCase("pingback")) {
213                         String href = startTag.getAttributeValue("href");
214 
215                         serverUri = HtmlUtil.escape(href);
216 
217                         break;
218                     }
219                 }
220             }
221             catch (Exception e) {
222                 _log.error("Unable to call GET of " + targetUri, e);
223             }
224         }
225 
226         return serverUri;
227     }
228 
229     private static Log _log = LogFactoryUtil.getLog(LinkbackProducerUtil.class);
230 
231     private static List<Tuple> _pingbackQueue = Collections.synchronizedList(
232         new ArrayList<Tuple>());
233 
234 }