// Copyright 2005 by Omar Al Zabir. All rights are reserved.
//
// If you like this code then feel free to go ahead and use it.
// The only thing I ask is that you don't remove or alter my copyright notice.
//
// Your use of this software is entirely at your own risk. I make no claims or
// warrantees about the reliability or fitness of this code for any particular purpose.
// If you make changes or additions to this code please mark your code as being yours.
//
// website http://www.oazabir.com, email OmarAlZabir@gmail.com, msn oazabir@hotmail.com
using System;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Collections;
using System.Windows.Forms;
namespace RSSFeeder.Helpers{
using RSSCommon;
/// <summary>
/// Manages all RSS download and storage related activity
/// </summary>
public class RSSHelper
{
private static FeedDownloader _Form;
/// <summary>
/// Downloads specified channels and stored the downloaded posts in database
/// </summary>
/// <param name="parent"></param>
/// <param name="channels"></param>
public static void DownloadChannels( Form parent, IList channels, EventHandler downloadCompleteHandler )
{
DownloadChannels( parent, channels, downloadCompleteHandler, false );
}
public static void DownloadChannels( Form parent, IList channels, EventHandler downloadCompleteHandler, bool force )
{
// Check if already a previous download is going on
if( null != _Form )
{
if( !force ) return;
}
if( null != _Form )
{
_Form.Close();
_Form.Dispose();
_Form = null;
}
_Form = new FeedDownloader();
_Form.Channels = channels;
_Form.Position();
_Form.Show();
_Form.DownloadComplete += new EventHandler(downloader_DownloadComplete);
if( null != downloadCompleteHandler )
_Form.DownloadComplete += new EventHandler(downloadCompleteHandler);
_Form.Closed += new EventHandler(_Form_Closed);
_Form.Disposed += new EventHandler(_Form_Disposed);
if( Configuration.Instance.SilentMode )
{
_Form.Hide();
if( !Configuration.Instance.NoBaloonPopup )
RSSServer.TrayNotifyIcon.ShowBalloon("RSS Feeder", "Synchronization going on...", NotifyIcon.BalloonTip.NotifyInfoFlags.Info, 1000);
}
}
public static void ShowDownloadForm()
{
if( null != _Form ) _Form.Show();
}
public static void StopDownload()
{
if( null != _Form )
_Form.Close();
}
/// <summary>
/// Stores the rss items in database under the specified channel
/// </summary>
/// <param name="channel"></param>
/// <param name="feeds"></param>
/// <returns></returns>
public static int StoreRSSItems( Channel channel, IList feeds )
{
int itemsAdded = 0;
foreach( RssFeed feed in feeds )
{
feed.ChannelID = channel.Id;
feed.IsInOutlook = false;
feed.IsRead = false;
// If the feed is successfully added in database, it goes to outlook also
if( DatabaseHelper.AddNewFeed( feed, false ) )
{
// Feed was added properly in database
itemsAdded ++;
}
}
return itemsAdded;
}
/// <summary>
/// Feeds downloaded, store in database
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private static void downloader_DownloadComplete(object sender, EventArgs e)
{
DownloadEventArgs args = e as DownloadEventArgs;
Configuration.Instance.LastDownloadTime = DateTime.Now;
}
/// <summary>
/// Update channel with downloaded rss items from the RssChannel
/// </summary>
/// <param name="channel">Our channel</param>
/// <param name="rssChannel">Downloaded RSS Channel</param>
public static Channel UpdateChannel( Channel channel, RssChannel rssChannel )
{
// Update channel
if( null != rssChannel.Title )
channel.Title = rssChannel.Title;
channel.LastUpdated = DateTime.Now;
// Store downloaded feeds in database
int itemsAdded = StoreRSSItems( channel, rssChannel.Feeds );
// Update channel title and last update date
DatabaseHelper.UpdateChannel( channel );
// Recalculate number of items in channel
DatabaseHelper.RefreshChannelCounts( channel.Id );
return DatabaseHelper.GetChannel( channel.Id );
}
private static void _Form_Closed(object sender, EventArgs e)
{
_Form.Closed -= new EventHandler(_Form_Closed);
if( !_Form.Disposing )
_Form.Dispose();
}
private static void _Form_Disposed(object sender, EventArgs e)
{
_Form.Disposed -= new EventHandler(_Form_Disposed);
_Form = null;
MemoryHelper.ReduceMemory();
}
/// <summary>
/// Download the specified URL and see whether the URL is an HTML page or contains RSS XML.
/// If the URL is html page, then it looks for RSS sources. if RSS source found, then it
/// returns the potential RSS sources.
/// </summary>
/// <param name="uri"></param>
/// <param name="proxy"></param>
/// <param name="isHtml"></param>
/// <param name="channels"></param>
/// <param name="channelSources"></param>
public static void DownloadFeeds(
Uri uri, string userName, string userPassword,
string proxyName, int port, string proxyUserName, string proxyPassword,
out IList channels, out IList channelSources,
ProgressEventHandler progressHandler )
{
channelSources = new ArrayList();
channels = new ArrayList();
// Download the full content of store in a memory stream
MemoryStream memoryStream = new MemoryStream( 100 * 1024 );
// Download data from the specified uri
DownloadContent( uri, userName, userPassword,
proxyName, port, proxyUserName, proxyPassword,
progressHandler, memoryStream );
// Download complete, let's process the content
progressHandler( null, new ProgressEventArgs( "Parsing...", 0 ) );
// Discover the content whether it is HTML or some XML feed source
RssTypeEnum type = Discover( memoryStream, ref channelSources, progressHandler );
// If the content is an XML feed, then we use feed parser to parse the content
if( RssTypeEnum.Atom == type )
{
// Atom feed is converted to Rss Feed for generic parsing
// Output stream for holding the Rss Content
MemoryStream rssStream = new MemoryStream( (int)memoryStream.Length );
// Transform the Atom stream to Rss Stream
memoryStream.Position = 0;
AtomToRssConverter.Transform( memoryStream, rssStream );
// Set atom stream as Rss Stream
memoryStream = rssStream;
type = RssTypeEnum.RSS;
}
// If we have an Xml stream let's parse it
if( RssTypeEnum.RSS == type )
{
memoryStream.Position = 0;
XmlTextReader xmlReader = new XmlTextReader( new StreamReader( memoryStream, RSSCommon.Constants.DefaultEncoding ) );
xmlReader.Namespaces = false;
FeedProcessor processor = new FeedProcessor();
channels = processor.Parse( xmlReader );
}
((IDisposable)memoryStream).Dispose();
}
/// <summary>
/// Download content from the specified Uri and store in the memory stream
/// </summary>
/// <param name="uri"></param>
/// <param name="progressHandler"></param>
/// <param name="memoryStream"></param>
private static void DownloadContent( Uri uri, string userName, string userPassword,
string proxyName, int port, string proxyUserName, string proxyPassword,
ProgressEventHandler progressHandler, MemoryStream memoryStream )
{
WebRequest webRequest = HttpWebRequest.Create(uri);
// If the feed source requires authentication, let's do it
if( userName.Length > 0 )
webRequest.Credentials = new NetworkCredential( userName,
RSSCommon.PropertyEditor.PasswordEditor.PasswordProvider.Decrypt( userPassword ) );
// Set proxy settings to the web request
if( null != proxyName && proxyName.Length > 0 )
{
webRequest.Proxy = new System.Net.WebProxy( proxyName, port );
if( proxyUserName.Length > 0 )
{
// Decrypt the encrypted password
string password = RSSCommon.PropertyEditor.PasswordEditor.PasswordProvider.Decrypt( proxyPassword );
// Set proxy credentials
webRequest.Proxy.Credentials = new System.Net.NetworkCredential( proxyUserName, password );
}
}
else
webRequest.Proxy = System.Net.WebProxy.GetDefaultProxy();
DateTime startTime = DateTime.Now;
using(System.Net.WebResponse webResponse = webRequest.GetResponse())
{
using(System.IO.Stream stream = webResponse.GetResponseStream())
{
byte [] buffer = new byte[ 1024 * 2 ]; // 2 KB buffer
int size;
while( (size = stream.Read( buffer, 0, buffer.Length) ) > 0 )
{
memoryStream.Write( buffer, 0, size );
// Perform some speed calculation
TimeSpan duration = DateTime.Now - startTime; // Total time elapsed
double kb = (memoryStream.Length / 1024.0); // Downloaded kilobytes
double speed = kb / duration.TotalSeconds;
string message = string.Format( "{0} kb, {1} KBps", kb.ToString("n2"), speed.ToString("n2") );
// notify download progress
progressHandler( null, new ProgressEventArgs( message , 0 ) );
}
}
}
}
/// <summary>
/// Parses the specified meory stream to identify what type of content it contents.
/// The content can be either HTML or XML. If it is HTML, then it is a web page which may
/// contain some RSS feed links or sources. Try to discover if there is any.
///
/// If it is not HTML, then see whether we can find any clue if it is Atom or Rss feed.
/// </summary>
/// <param name="memoryStream"></param>
/// <param name="channelSources"></param>
/// <param name="isHtml"></param>
/// <param name="progressHandler"></param>
/// <returns></returns>
private static RssTypeEnum Discover( MemoryStream memoryStream,
ref IList channelSources, ProgressEventHandler progressHandler )
{
// Parse the content and find out whether the content is HTML or XML
Sgml.SgmlReader reader = new Sgml.SgmlReader();
bool isHtml = false;
RssTypeEnum feedType = RssTypeEnum.Unknown;
memoryStream.Position = 0;
/// Parse the response using a SGML parser in order to identify whether
/// the response is HTML or XML.
StreamReader streamReader = new StreamReader( memoryStream, RSSCommon.Constants.DefaultEncoding );
reader.InputStream = streamReader;
//reader.SetBaseUri( uri.ToString() );
try
{
while( reader.Read() )
{
if( null != reader.Name )
{
string name = reader.Name.Trim().ToLower();
/// If we get the html tag in the document, then it is definitely a HTML page
if( name == "html" || name == "body" )
{
progressHandler( null, new ProgressEventArgs("HTML", 0) );
isHtml = true;
feedType = RssTypeEnum.Unknown;
}
// RSS XML starts from <rss> node
else if( (name == "rss" || name == "rdf:rdf") && !isHtml )
{
progressHandler( null, new ProgressEventArgs("RSS Feed", 0) );
feedType = RssTypeEnum.RSS;
break; // We need to parse in a different way for RSS stream
}
// Atom XML starts from <feed> node
else if( name == "feed" && !isHtml )
{
progressHandler( null, new ProgressEventArgs("Atom Feed", 0) );
feedType = RssTypeEnum.Atom;
break; // We need to parse in a different way for RSS stream
}
else if( isHtml && name == "link" )
{
// Let's see if this link indicats a RSS feed
string rel = reader.GetAttribute("rel");
string type = reader.GetAttribute("type");
string title = reader.GetAttribute("title");
string href = reader.GetAttribute("href");
if( rel.ToLower() == "alternate" && type.ToLower() == "application/rss+xml" )
{
channelSources.Add( new string [] { title, href } );
}
if( rel.ToLower() == "alternate" && type.ToLower() == "application/atom+xml" )
{
channelSources.Add( new string [] { title, href } );
}
}
}
} // while( reader.Read() )
}
catch( Exception x )
{
EntLibHelper.Exception( "RSS Discovery", x );
// RSS discovery failed. SgmlReader can't parse it.
// May be this really is an RSS feed which .NET's framework
// can parse better.
feedType = RssTypeEnum.RSS;
//ErrorReportForm.QueueErrorReport( new ErrorReportItem( x.Message, x.ToString() ) );
}
return feedType;
}
}
}
|