📄 trackbacknotificationproxy.cs
字号:
//------------------------------------------------------------------------------
// <copyright company="Telligent Systems">
// Copyright (c) Telligent Systems Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using CommunityServer.Blogs.Components;
using CommunityServer.Components;
using CommunityServer.Configuration;
using CookComputing.XmlRpc;
namespace CommunityServer.Blogs.Components
{
/// <summary>
/// Summary description for TrackBackNotificationProxy.
/// </summary>
public class TrackBackNotificationProxy
{
string body, title, link, blogname, description, hostPath;
int postID;
private int settingsID;
private static readonly Regex trackBackRDFRegex = new Regex(@"<rdf:\w+\s[^>]*?>(</rdf:rdf>)?", RegexOptions.IgnoreCase|RegexOptions.Compiled);
private static readonly Regex trackBackPingRegex = new Regex("trackback:ping=\"([^\"]+)\"", RegexOptions.IgnoreCase|RegexOptions.Compiled);
/// <summary>
/// Creates a new instance of TrackBackNotificationProxy setting the values needed to complete all the TrackBacks and PingBacks
/// </summary>
public TrackBackNotificationProxy(int settingsID, string body, string title, string link, string blogname, string description, int postID, string hostPath)
{
this.settingsID = settingsID;
this.body = body;
this.title = title;
this.link = link;
this.blogname = blogname;
this.description = description;
this.postID = postID;
this.hostPath = hostPath;
}
/// <summary>
/// Creates a new instance of TrackBackNotificationProxy and determines if the trackbacks should happen on a second thread
/// (Managed ThreadPool)
/// </summary>
public static void Track(WeblogPost post, bool backGroundThread)
{
Weblog wl = post.Section as Weblog;
if(wl != null && wl.ValidateTrackBacks(post))
{
string desc = post.HasExcerpt ? post.Excerpt : Formatter.RemoveHtml(post.FormattedBody,100);
CSContext cntx = CSContext.Current;
TrackBackNotificationProxy tbnp = new TrackBackNotificationProxy(cntx.SettingsID, post.FormattedBody, post.Subject, Globals.FullPath(BlogUrls.Instance().Post(post,wl)), wl.Name, desc, post.PostID, cntx.HostPath);
if(backGroundThread)
ManagedThreadPool.QueueUserWorkItem(new WaitCallback(tbnp.Track));
else
tbnp.Track();
}
}
/// <summary>
/// Simply calls this.Track();
/// </summary>
public void Track(object state)
{
// Create new Context
CSContext.Create(this.settingsID);
Track();
}
/// <summary>
/// Creates an array of links based on the post's body. Then walks through each link and attempts to "TrackBack"
/// </summary>
public void Track()
{
StringCollection links = GetLinks(body);
for(int i = 0; i < links.Count; i++)
{
string externalUrl = links[i];
try
{
HttpWebResponse response = CSRequest.GetResponse(externalUrl, link);
WebHeaderCollection headers = response.Headers;
string pageText = CSRequest.GetPageText(response);
if(pageText != null)
TryToPing(headers, pageText, externalUrl);
}
catch (System.Exception ex)
{
if (externalUrl == null)
externalUrl = "null";
string message = String.Format("Trackback/Pingback attempt to the url [{0}] failed for PostID {1} while retrieving the remote document. Error message returned was: {2}.", externalUrl, this.postID, ex.Message);
EventLogs.Warn(message, "Weblogs", 301, CSContext.Current.SettingsID);
}
}
}
/// <summary>
/// Attempts to make a TrackBack or PingBack request to the supplied Url. The page is requested and then search for TrackBack and PingBack links
/// </summary>
/// <param name="pageText">Full Text (HTML) of the page to search</param>
/// <param name="externalUrl">Url to ping</param>
/// <returns></returns>
public bool TryToPing(WebHeaderCollection headers, string pageText, string externalUrl)
{
CSContext cntx = CSContext.Current;
// First try to send a TrackBack if the required RDF info was embedded in the HTML
try
{
string trackBackItem = GetTrackBackText(pageText,externalUrl,link);
if (!Globals.IsNullorEmpty(trackBackItem))
{
if(!trackBackItem.ToLower().StartsWith("http://") && !trackBackItem.ToLower().StartsWith("https://"))
trackBackItem = "http://" + trackBackItem;
SendTrackBackPing(trackBackItem);
string message = String.Format("Trackback sent to {0} for PostID {1}.", externalUrl, this.postID);
EventLogs.Info(message, "Weblogs", 301, cntx.SettingsID);
return true;
}
}
catch (System.Exception ex)
{
string message = String.Format("Trackback attempt to the url [{0}] failed for PostID {1}. Error message returned was: {2}.", externalUrl, this.postID, ex.Message);
EventLogs.Warn(message, "Weblogs", 301, cntx.SettingsID);
}
// Now try to send a PingBack if the required PingBack XML-RPC service URL exists in the HTTP Header or HTML Head
try
{
string PingBackServerURI = GetPingBackServerURI(headers, pageText);
if (!Globals.IsNullorEmpty(PingBackServerURI))
{
SendPingBackPing(PingBackServerURI.Trim(), externalUrl);
string message = String.Format("Pingback sent to {0} for PostID {1}.", externalUrl, this.postID);
EventLogs.Info(message, "Weblogs", 303, cntx.SettingsID);
return true;
}
}
catch (CookComputing.XmlRpc.XmlRpcException ex)
{
string message = String.Format("Pingback attempt to the url [{0}] failed for PostID {1}. Error message returned was: {2}.", externalUrl, this.postID, ex.Message);
EventLogs.Warn(message, "Weblogs", 303, cntx.SettingsID);
}
catch (System.Exception ex)
{
string message = String.Format("Pingback attempt to the url [{0}] failed for PostID {1}. Error message returned was: {2}.", externalUrl, this.postID, ex.Message);
EventLogs.Warn(message, "Weblogs", 303, cntx.SettingsID);
}
return false;
}
/// <summary>
/// If the current site/link supports a TrackBack, we will ping the site here
/// </summary>
/// <param name="trackBackItem"></param>
protected virtual void SendTrackBackPing(string trackBackItem)
{
string parameters = "title=" + Globals.UrlEncode(Globals.HtmlDecode(title)) + "&url=" + Globals.UrlEncode(link) + "&blog_name=" + Globals.UrlEncode(Globals.HtmlDecode(blogname)) + "&excerpt=" + Globals.UrlEncode(description);
byte[] payload = Encoding.UTF8.GetBytes(parameters);
HttpWebRequest request = CSRequest.CreateRequest(trackBackItem,link);
request.Method = "POST";
request.ContentLength = payload.Length;
request.ContentType = "application/x-www-form-urlencoded";
request.KeepAlive = false;
using(Stream st = request.GetRequestStream())
{
st.Write(payload, 0, payload.Length);
st.Close();
using(WebResponse response = request.GetResponse())
{
response.Close();
}
}
}
/// <summary>
/// If the current site/link supports a PingBack, we will ping the site here
/// </summary>
protected virtual void SendPingBackPing(string PingBackServerURI, string externalUrl)
{
PingBackClientProxy pingBackClient = new PingBackClientProxy(PingBackServerURI);
pingBackClient.Ping(link, externalUrl);
}
#region Helpers
private static string GetTrackBackText(string pageText, string url, string PostUrl)
{
if(!Regex.IsMatch(pageText,PostUrl,RegexOptions.IgnoreCase|RegexOptions.Singleline))
{
Match m;
for (m = trackBackRDFRegex.Match(pageText); m.Success; m = m.NextMatch())
{
if(m.Groups.ToString().Length > 0)
{
string text = m.Groups[0].ToString();
if(text.IndexOf(url) > 0)
{
Match m2 = trackBackPingRegex.Match(text) ;
if ( m2.Success )
{
return m2.Result("$1") ;
}
return text;
}
}
}
}
return null;
}
private static string GetPingBackServerURI(WebHeaderCollection headers, string pageText)
{
string PingBackServerURI = null;
// First look for the X-Pingback HTTP Header
if (headers != null && headers.HasKeys())
{
foreach (string key in headers.Keys)
{
if (key.ToLower().Trim() == "x-pingback")
{
PingBackServerURI = headers[key];
continue;
}
}
}
// If the HTTP header was not found, look for the PingBack <Link> element in the body
if (Globals.IsNullorEmpty(PingBackServerURI) && !Globals.IsNullorEmpty(pageText))
{
Match m = trackBackPingRegex.Match(pageText) ;
if ( m.Success )
{
PingBackServerURI = m.Result("$1");
}
}
return PingBackServerURI;
}
/// <summary>
/// Gets a list of all of the valid html links from a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public StringCollection GetLinks(string text)
{
// Convert any relative links in the body to fully qualified URLS
text = Formatter.ConvertLocalUrls(text, hostPath);
StringCollection links = new StringCollection();
string sPattern = @"(?:[hH][rR][eE][fF]\s*=)" +
@"(?:[\s""']*)(?!#|[Mm]ailto|[lL]ocation.|[jJ]avascript|.*css|.*this\.)" +
@"(.*?)(?:[\s>""'])";
Regex r = new Regex(sPattern,RegexOptions.IgnoreCase);
Match m;
string link = null;
for (m = r.Match(text); m.Success; m = m.NextMatch())
{
if(m.Groups.ToString().Length > 0 )
{
link = m.Groups[1].ToString();
if(!links.Contains(link))
{
links.Add(link);
}
}
}
return links;
}
#endregion
}
/// <summary>
/// PingBack XML-RPC client proxy
/// </summary>
public class PingBackClientProxy : XmlRpcClientProtocol
{
public PingBackClientProxy(string remoteServerURI)
{
this.UserAgent = CSRequest.UserAgent;
this.Timeout = 60000;
this.Url = remoteServerURI;
// This improves compatibility with XML-RPC servers that do not fully comply with the XML-RPC specification.
this.NonStandard = XmlRpcNonStandard.All;
// Use a proxy server if one has been configured
CSConfiguration config = CSConfiguration.GetConfig();
if (config.ProxyHost != string.Empty)
{
WebProxy proxy = new WebProxy(config.ProxyHost, config.ProxyPort);
if (config.ProxyBypassList != string.Empty)
proxy.BypassList = config.ProxyBypassList.Split(',');
proxy.BypassProxyOnLocal = config.ProxyBypassOnLocal;
if (config.ProxyUsername != string.Empty)
proxy.Credentials = new NetworkCredential(config.ProxyUsername, config.ProxyPassword);
this.Proxy = proxy;
}
}
/// <summary>
/// Sends a PingBack request using XML-RPC
/// </summary>
/// <param name="sourceURI">The absolute URI of the post on the source page containing the link to the target site.</param>
/// <param name="targetURI">The absolute URI of the target of the link, as given on the source page.</param>
[XmlRpcMethod("pingback.ping",Description="Notifies the server that a link has been added to sourceURI, pointing to targetURI.")]
[return: XmlRpcReturnValue( Description="A Message String" )]
public string Ping(string sourceURI, string targetURI)
{
return (string) Invoke("Ping", new object[] {sourceURI, targetURI});
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -