private Opml DownloadOpmlFeed(Uri uri)
        {
            // look for disk cache first
            Opml opmlFeed = TryLoadFromDisk(uri);

            if (opmlFeed != null)
            {
                return(opmlFeed);
            }

            // May fail under partial trust
            try
            {
                byte[] feed = new WebClient().DownloadData(uri.AbsoluteUri);

                var opmlDoc = new XmlDocument();
                opmlDoc.Load(new MemoryStream(feed));
                opmlFeed = Opml.LoadFromXml(opmlDoc);

                opmlFeed.UtcExpiry = DateTime.UtcNow.AddMinutes(_defaultTtlMinutes);

                // save to disk
                TrySaveToDisk(opmlDoc, uri, opmlFeed.UtcExpiry);
            }
            catch
            {
                return(new Opml());
            }

            return(opmlFeed);
        }
        private Opml TryLoadFromDisk(Uri uri)
        {
            if (_directoryOnDisk == null)
            {
                return(null);  // no place to cache
            }

            // look for all files matching the prefix
            // looking for the one matching url that is not expired
            // removing expired (or invalid) ones
            string pattern = GetTempFileNamePrefixFromUrl(uri) + "_*.opml.resources";

            string[] files = Directory.GetFiles(_directoryOnDisk, pattern, SearchOption.TopDirectoryOnly);

            foreach (string opmlFilename in files)
            {
                XmlDocument opmlDoc               = null;
                bool        isOpmlFileValid       = false;
                DateTime    utcExpiryFromOpmlFile = DateTime.MinValue;
                string      urlFromOpmlFile       = null;

                try
                {
                    opmlDoc = new XmlDocument();
                    opmlDoc.Load(opmlFilename);

                    // look for special XML comment (before the root tag)'
                    // containing expiration and url
                    var comment = opmlDoc.DocumentElement.PreviousSibling as XmlComment;

                    if (comment != null)
                    {
                        string c = comment.Value;
                        int    i = c.IndexOf('@');
                        long   expiry;

                        if (long.TryParse(c.Substring(0, i), out expiry))
                        {
                            utcExpiryFromOpmlFile = DateTime.FromBinary(expiry);
                            urlFromOpmlFile       = c.Substring(i + 1);
                            isOpmlFileValid       = true;
                        }
                    }
                }
                catch
                {
                    // error processing one file shouldn't stop processing other files
                }

                // remove invalid or expired file
                if (!isOpmlFileValid || utcExpiryFromOpmlFile < DateTime.UtcNow)
                {
                    try
                    {
                        File.Delete(opmlFilename);
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex);
                    }

                    // try next file
                    continue;
                }

                // match url
                if (urlFromOpmlFile.ToLower() == uri.AbsoluteUri.ToLower())
                {
                    // found a good one - create DOM and set expiry (as found on disk)
                    Opml opmlFeed = Opml.LoadFromXml(opmlDoc);
                    opmlFeed.UtcExpiry = utcExpiryFromOpmlFile;
                    return(opmlFeed);
                }
            }

            // not found
            return(null);
        }