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