public void WhenCalling_GetImageAsync_WithNullParameter_Should_ThrowArgumentNullException() { var sut = new ImagesCache(A.Dummy <IDistributedCache>()); sut.Awaiting(ic => ic.GetImageAsync(null)) .Should().ThrowExactly <ArgumentNullException>(); }
public static Image GetSteamProfileBigImage(ulong steamId) { try { var image = ImagesCache.GetImage($"{steamId}-big"); if (image != null) { return(image); } var client = new RestClient("https://steamcommunity.com"); var request = new RestRequest($"/profiles/{steamId}/?xml=1", Method.GET); var response = client.Execute(request); var content = response.Content; var result = Regex.Match(content, @"<avatarFull><!\[CDATA\[(.*)\]\]></avatarFull>"); var imageUrl = result.Groups[1].ToString(); image = DownloadImage(imageUrl); ImagesCache.CacheImage($"{steamId}-big", image); return(image); } catch (Exception ex) { Logger.Error("Error on getting profile image", ex); return(null); } }
public void WhenCalling_CacheImageAsync_WithNullImageParameter_Should_ThrowArgumentNullException() { var distributedCache = A.Dummy <IDistributedCache>(); var sut = new ImagesCache(distributedCache); sut.Awaiting(ic => ic.CacheImageAsync(new ImageCacheKey("01_04_2019_001103"), null)) .Should().ThrowExactly <ArgumentNullException>(); }
public void WhenCalling_CacheImageAsync_WithNullKeyParameter_Should_ThrowArgumentNullException() { var distributedCache = A.Dummy <IDistributedCache>(); var sut = new ImagesCache(distributedCache); sut.Awaiting(ic => ic.CacheImageAsync(null, new Image())) .Should().ThrowExactly <ArgumentNullException>(); }
public async Task WhenCalling_GetImageAsync_Should_ReturnNullIfNotFound() { var distributedCache = A.Fake <IDistributedCache>(); A.CallTo(() => distributedCache.GetAsync(A <string> ._, A <CancellationToken> ._)) .Returns(Task.FromResult((byte[])null)); var sut = new ImagesCache(distributedCache); var result = await sut.GetImageAsync(new ImageCacheKey("01_04_2019_001103")); result.Should().BeNull(); }
public async Task WhenCalling_GetImageAsync_Should_CallDistributedCacheToRetrieveImage() { var distributedCache = A.Fake <IDistributedCache>(); var sut = new ImagesCache(distributedCache); await sut.GetImageAsync(new ImageCacheKey("01_04_2019_001103")); A.CallTo(() => distributedCache.GetAsync( A <string> .That.Matches(key => key == "01_04_2019_001103"), A <CancellationToken> ._)) .MustHaveHappenedOnceExactly(); }
public async Task WhenCalling_GetImageAsync_Should_CallDistributedCacheWithCorrectImageTypeInKey(string imageType, string expectedCacheKey) { var distributedCache = A.Fake <IDistributedCache>(); var sut = new ImagesCache(distributedCache); await sut.GetImageAsync(new ImageCacheKey("01_04_2019_001103", null, null, null, imageType)); A.CallTo(() => distributedCache.GetAsync( A <string> .That.Matches(key => key == expectedCacheKey), A <CancellationToken> ._)) .MustHaveHappenedOnceExactly(); }
public async Task WhenCalling_GetImageAsync_Should_CallDistributedCacheWithWatermarkHashInKey() { var distributedCache = A.Fake <IDistributedCache>(); var sut = new ImagesCache(distributedCache); await sut.GetImageAsync(new ImageCacheKey("01_04_2019_001103", null, null, "Some watermark text")); A.CallTo(() => distributedCache.GetAsync( A <string> .That.Matches(key => key == "01_04_2019_001103|wm=NZyQwos0iWRAmdBFA9T6q+MZn9B0fgauzH3MJpNplsc="), A <CancellationToken> ._)) .MustHaveHappenedOnceExactly(); }
public async Task WhenCalling_CacheImageAsync_Should_CallDistributedCacheToSetImage() { var distributedCache = A.Fake <IDistributedCache>(); var sut = new ImagesCache(distributedCache); var testGdiImage = TestHelpers.GetTestImage(); await sut.CacheImageAsync(new ImageCacheKey("01_04_2019_001103"), new Image("01_04_2019_001103", testGdiImage)); A.CallTo(() => distributedCache.SetAsync( A <string> .That.Matches(key => key == "01_04_2019_001103"), A <byte[]> .That.Matches(value => IsMatchingImageBytes(value, "01_04_2019_001103", testGdiImage)), A <DistributedCacheEntryOptions> ._, A <CancellationToken> ._)) .MustHaveHappenedOnceExactly(); }
public async Task WhenCalling_CacheImageAsync_Should_CallDistributedCacheWithWatermarkHashInKey() { var distributedCache = A.Fake <IDistributedCache>(); var sut = new ImagesCache(distributedCache); var testGdiImage = TestHelpers.GetTestImage(); await sut.CacheImageAsync( new ImageCacheKey("01_04_2019_001103", null, null, "Some watermark text"), new Image("01_04_2019_001103", testGdiImage)); A.CallTo(() => distributedCache.SetAsync( A <string> .That.Matches(key => key == "01_04_2019_001103|wm=NZyQwos0iWRAmdBFA9T6q+MZn9B0fgauzH3MJpNplsc="), A <byte[]> .That.Matches(value => IsMatchingImageBytes(value, "01_04_2019_001103", testGdiImage)), A <DistributedCacheEntryOptions> ._, A <CancellationToken> ._)) .MustHaveHappenedOnceExactly(); }
public async Task WhenCalling_GetImageAsync_Should_ReturnCachedImageIfFound() { var distributedCache = A.Fake <IDistributedCache>(); var testGdiImage = TestHelpers.GetTestImage(); A.CallTo(() => distributedCache.GetAsync(A <string> ._, A <CancellationToken> ._)) .ReturnsLazily(callinfo => { var testImage = new Image("01_04_2019_001103", testGdiImage); var bytes = testImage.ToBytes(); return(Task.FromResult(bytes)); }); var sut = new ImagesCache(distributedCache); var result = await sut.GetImageAsync(new ImageCacheKey("01_04_2019_001103")); result.Name.Should().BeEquivalentTo("01_04_2019_001103"); TestHelpers.CompareImages(testGdiImage, result.ToGdiImage()).Should().BeEquivalentTo(CompareResult.Same); }
public static void UpdateImageOnItemPanelAsync(string hash, string iconUrl, Panel imageBox) { Task.Run( () => { lastRequestedImageHashName = hash; var image = ImagesCache.GetImage(hash); if (image != null) { if (lastRequestedImageHashName == hash) { imageBox.BackgroundImage = ResizeImage(image, 100, 100); } } else { if (lastRequestedImageHashName == hash) { imageBox.BackgroundImage = Resources.DefaultItem; } image = DownloadImage(iconUrl); if (image != null) { ImagesCache.CacheImage(hash, image); if (lastRequestedImageHashName == hash) { imageBox.BackgroundImage = ResizeImage(image, 100, 100); } } else { if (lastRequestedImageHashName == hash) { imageBox.BackgroundImage = Resources.DefaultItem; } } } }); }
/// <summary> /// POST requests handler /// </summary> /// <param name="p"></param> public override void HandleGETRequest(HttpProcessor processor) { Log.WriteLine("HTTP GET request from {0}: {1}", ((System.Net.IPEndPoint)processor.Socket.Client.RemoteEndPoint).Address, processor.HttpUrl); try { // Parse request string xml = string.Empty; string request = processor.HttpUrl; // Remove prefix if any if (!string.IsNullOrEmpty(Properties.Settings.Default.RootPrefix)) { request = request.Replace(Properties.Settings.Default.RootPrefix, "/"); } while (request.IndexOf("//") >= 0) { request = request.Replace("//", "/"); } string ext = Path.GetExtension(request); string[] http_params = request.Split(new Char[] { '?', '=', '&' }); // User-agent check: some e-book readers can handle fb2 files (no conversion is needed) string userAgent = processor.HttpHeaders["User-Agent"] as string; bool acceptFB2 = Utils.DetectFB2Reader(userAgent); // Is it OPDS request? if (string.IsNullOrEmpty(ext) || !new string[] { ".ico", ".jpeg", ".epub", ".zip", ".xml" }.Contains(ext)) { try { // Is it root node requested? if (request.Equals("/")) { xml = new RootCatalog().Catalog.ToString(); } else if (request.StartsWith("/authorsindex")) { int numChars = request.StartsWith("/authorsindex/") ? 14 : 13; xml = new AuthorsCatalog().GetCatalog(request.Substring(numChars)).ToString(); } else if (request.StartsWith("/author/")) { xml = new BooksCatalog().GetCatalogByAuthor(request.Substring(8), acceptFB2).ToString(); } else if (request.StartsWith("/sequencesindex")) { int numChars = request.StartsWith("/sequencesindex/") ? 16 : 15; xml = new SequencesCatalog().GetCatalog(request.Substring(numChars)).ToString(); } else if (request.Contains("/sequence/")) { xml = new BooksCatalog().GetCatalogBySequence(request.Substring(10), acceptFB2).ToString(); } else if (request.StartsWith("/genres")) { int numChars = request.Contains("/genres/") ? 8 : 7; xml = new GenresCatalog().GetCatalog(request.Substring(numChars)).ToString(); } else if (request.StartsWith("/genre/")) { xml = new BooksCatalog().GetCatalogByGenre(request.Substring(7), acceptFB2).ToString(); } else if (request.StartsWith("/search")) { if (http_params[1].Equals("searchTerm")) { xml = new OpenSearch().Search(http_params[2], "", acceptFB2).ToString(); } else if (http_params[1].Equals("searchType")) { int pageNumber = 0; if (http_params.Length > 6 && http_params[5].Equals("pageNumber")) { int.TryParse(http_params[6], out pageNumber); } xml = new OpenSearch().Search(http_params[4], http_params[2], acceptFB2, pageNumber).ToString(); } } if (string.IsNullOrEmpty(xml)) { processor.WriteFailure(); return; } // Modify and send xml back to the client app xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + xml.Insert(5, " xmlns=\"http://www.w3.org/2005/Atom\""); if (Properties.Settings.Default.UseAbsoluteUri) { try { string host = processor.HttpHeaders["Host"].ToString(); xml = xml.Replace("href=\"", "href=\"http://" + host.UrlCombine(Properties.Settings.Default.RootPrefix)); } catch { } } #if USE_GZIP_ENCODING /// Unfortunately, current OPDS-enabled apps don't support this feature, even those that pretend to (like FBReader for Android) // Compress xml if compression supported if (!processor.HttpHeaders.ContainsValue("gzip")) { byte[] temp = Encoding.UTF8.GetBytes(xml); using (MemoryStream inStream = new MemoryStream(temp)) using (MemoryStream outStream = new MemoryStream()) using (GZipStream gzipStream = new GZipStream(outStream, CompressionMode.Compress)) { inStream.CopyTo(gzipStream); outStream.Position = 0; processor.WriteSuccess("application/atom+xml;charset=utf=8", true); outStream.CopyTo(processor.OutputStream.BaseStream); } } else #endif { processor.WriteSuccess("application/atom+xml;charset=utf-8"); processor.OutputStream.Write(xml); } } catch (Exception e) { Log.WriteLine(LogLevel.Error, "OPDS catalog exception {0}", e.Message); } return; } else if (request.Contains("opds-opensearch.xml")) { xml = new OpenSearch().OpenSearchDescription().ToString(); xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + xml.Insert(22, " xmlns=\"http://a9.com/-/spec/opensearch/1.1/\""); if (Properties.Settings.Default.UseAbsoluteUri) { try { string host = processor.HttpHeaders["Host"].ToString(); xml = xml.Replace("href=\"", "href=\"http://" + host.UrlCombine(Properties.Settings.Default.RootPrefix)); } catch { } } processor.WriteSuccess("application/atom+xml;charset=utf-8"); processor.OutputStream.Write(xml); return; } // fb2.zip book request else if (request.Contains(".fb2.zip")) { MemoryStream memStream = null; try { memStream = new MemoryStream(); Book book = LibraryFactory.GetLibrary().GetBook(request.Substring(1, request.IndexOf('/', 1) - 1)); if (book.FilePath.ToLower().Contains(".zip@")) { string[] pathParts = book.FilePath.Split('@'); using (ZipFile zipFile = new ZipFile(pathParts[0])) { ZipEntry entry = zipFile.Entries.First(e => e.FileName.Contains(pathParts[1])); if (entry != null) { entry.Extract(memStream); } } } else { using (FileStream stream = new FileStream(book.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) stream.CopyTo(memStream); } memStream.Position = 0; // Compress fb2 document to zip using (ZipFile zip = new ZipFile()) { zip.AddEntry(Transliteration.Front(string.Format("{0}_{1}.fb2", book.Authors.First(), book.Title)), memStream); using (MemoryStream outputStream = new MemoryStream()) { zip.Save(outputStream); outputStream.Position = 0; processor.WriteSuccess("application/fb2+zip"); outputStream.CopyTo(processor.OutputStream.BaseStream); } } HttpServer.ServerStatistics.BooksSent++; } catch (Exception e) { Log.WriteLine(LogLevel.Error, "FB2 file exception {0}", e.Message); } finally { processor.OutputStream.BaseStream.Flush(); if (memStream != null) { memStream.Dispose(); } } return; } // epub book request else if (ext.Contains(".epub")) { MemoryStream memStream = null; try { memStream = new MemoryStream(); Book book = LibraryFactory.GetLibrary().GetBook(request.Substring(1, request.IndexOf('/', 1) - 1)); if (book.FilePath.ToLower().Contains(".zip@")) { string[] pathParts = book.FilePath.Split('@'); using (ZipFile zipFile = new ZipFile(pathParts[0])) { ZipEntry entry = zipFile.Entries.First(e => e.FileName.Contains(pathParts[1])); if (entry != null) { entry.Extract(memStream); } entry = null; } } else { using (FileStream stream = new FileStream(book.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) stream.CopyTo(memStream); } memStream.Position = 0; // At this moment, memStream has a copy of requested book // For fb2, we need convert book to epub if (book.BookType == BookType.FB2) { // No convertor found, return an error if (string.IsNullOrEmpty(Properties.Settings.Default.ConvertorPath)) { Log.WriteLine(LogLevel.Error, "No FB2 to EPUB convertor found, file request can not be completed!"); processor.WriteFailure(); return; } // Save fb2 book to the temp folder string inFileName = Path.Combine(Path.GetTempPath(), book.ID + ".fb2"); using (FileStream stream = new FileStream(inFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) memStream.CopyTo(stream); // Run converter string outFileName = Path.Combine(Path.GetTempPath(), book.ID + ".epub"); string command = Path.Combine(Properties.Settings.Default.ConvertorPath, Utils.IsLinux ? "fb2toepub" : "Fb2ePub.exe"); string arguments = string.Format(Utils.IsLinux ? "{0} {1}" : "\"{0}\" \"{1}\"", inFileName, outFileName); using (ProcessHelper converter = new ProcessHelper(command, arguments)) { converter.Run(); if (File.Exists(outFileName)) { memStream = new MemoryStream(); using (FileStream fileStream = new FileStream(outFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) fileStream.CopyTo(memStream); // Cleanup temp folder try { File.Delete(inFileName); } catch { } try { File.Delete(outFileName); } catch { } } else { string converterError = string.Empty; foreach (string s in converter.ProcessOutput) { converterError += s + " "; } Log.WriteLine(LogLevel.Error, "EPUB conversion error on file {0}. Error description: {1}", inFileName, converterError); processor.WriteFailure(); return; } } } // At this moment, memStream has a copy of epub processor.WriteSuccess("application/epub+zip"); memStream.Position = 0; memStream.CopyTo(processor.OutputStream.BaseStream); HttpServer.ServerStatistics.BooksSent++; } catch (Exception e) { Log.WriteLine(LogLevel.Error, "EPUB file exception {0}", e.Message); } finally { processor.OutputStream.BaseStream.Flush(); if (memStream != null) { memStream.Dispose(); } } return; } // Cover image or thumbnail request else if (ext.Contains(".jpeg")) { bool getCover = true; string bookID = string.Empty; if (request.Contains("/cover/")) { bookID = Path.GetFileNameWithoutExtension(request.Substring(request.IndexOf("/cover/") + 7)); } else if (request.Contains("/thumbnail/")) { bookID = Path.GetFileNameWithoutExtension(request.Substring(request.IndexOf("/thumbnail/") + 11)); getCover = false; } if (!string.IsNullOrEmpty(bookID)) { CoverImage image = null; Book book = LibraryFactory.GetLibrary().GetBook(bookID); if (book != null) { if (ImagesCache.HasImage(bookID)) { image = ImagesCache.GetImage(bookID); } else { image = new CoverImage(book); if (image != null && image.HasImages) { ImagesCache.Add(image); } } if (image != null && image.HasImages) { processor.WriteSuccess("image/jpeg"); (getCover ? image.CoverImageStream : image.ThumbnailImageStream).CopyTo(processor.OutputStream.BaseStream); processor.OutputStream.BaseStream.Flush(); HttpServer.ServerStatistics.ImagesSent++; return; } } } } // favicon.ico request else if (ext.Contains(".ico")) { string icon = Path.GetFileName(request); Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("TinyOPDS.Icons." + icon); if (stream != null && stream.Length > 0) { processor.WriteSuccess("image/x-icon"); stream.CopyTo(processor.OutputStream.BaseStream); processor.OutputStream.BaseStream.Flush(); return; } } processor.WriteFailure(); } catch (Exception e) { Log.WriteLine(LogLevel.Error, ".HandleGETRequest() exception {0}", e.Message); processor.WriteFailure(); } }
/// <summary> /// POST requests handler /// </summary> /// <param name="p"></param> public override void HandleGETRequest(HttpProcessor processor) { Log.WriteLine("HTTP GET request from {0}: {1}", ((System.Net.IPEndPoint)processor.Socket.Client.RemoteEndPoint).Address, processor.HttpUrl); try { // Parse request string xml = string.Empty; string request = processor.HttpUrl; // Check for www request bool isWWWRequest = request.StartsWith("/" + TinyOPDS.Properties.Settings.Default.HttpPrefix) && !request.StartsWith("/" + TinyOPDS.Properties.Settings.Default.RootPrefix) ? true : false; // Remove prefix if any if (!request.Contains("opds-opensearch.xml") && !string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.RootPrefix)) { request = request.Replace(TinyOPDS.Properties.Settings.Default.RootPrefix, "/"); } if (!string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.HttpPrefix)) { request = request.Replace(TinyOPDS.Properties.Settings.Default.HttpPrefix, "/"); } while (request.IndexOf("//") >= 0) { request = request.Replace("//", "/"); } // Remove any parameters from request except TinyOPDS params int paramPos = request.IndexOf('?'); if (paramPos >= 0) { int ourParamPos = request.IndexOf("pageNumber") + request.IndexOf("searchTerm"); if (ourParamPos >= 0) { ourParamPos = request.IndexOf('&', ourParamPos + 10); if (ourParamPos >= 0) { request = request.Substring(0, ourParamPos); } } else { request = request.Substring(0, paramPos); } } string ext = Path.GetExtension(request).ToLower(); if (!_extensions.Contains(ext)) { ext = string.Empty; } string[] http_params = request.Split(new Char[] { '?', '=', '&' }); // User-agent check: some e-book readers can handle fb2 files (no conversion is needed) string userAgent = processor.HttpHeaders["User-Agent"] as string; bool acceptFB2 = Utils.DetectFB2Reader(userAgent) || isWWWRequest; int threshold = (int)(isWWWRequest ? TinyOPDS.Properties.Settings.Default.ItemsPerWebPage : TinyOPDS.Properties.Settings.Default.ItemsPerOPDSPage); // Is it OPDS request? if (string.IsNullOrEmpty(ext)) { try { // Is it root node requested? if (request.Equals("/")) { xml = new RootCatalog().GetCatalog().ToStringWithDeclaration(); } else if (request.StartsWith("/newdate")) { xml = new NewBooksCatalog().GetCatalog(request.Substring(8), true, acceptFB2, threshold).ToStringWithDeclaration(); } else if (request.StartsWith("/newtitle")) { xml = new NewBooksCatalog().GetCatalog(request.Substring(9), false, acceptFB2, threshold).ToStringWithDeclaration(); } else if (request.StartsWith("/authorsindex")) { int numChars = request.StartsWith("/authorsindex/") ? 14 : 13; xml = new AuthorsCatalog().GetCatalog(request.Substring(numChars), false, threshold).ToStringWithDeclaration(); } else if (request.StartsWith("/author/")) { xml = new BooksCatalog().GetCatalogByAuthor(request.Substring(8), acceptFB2, threshold).ToStringWithDeclaration(); } else if (request.StartsWith("/sequencesindex")) { int numChars = request.StartsWith("/sequencesindex/") ? 16 : 15; xml = new SequencesCatalog().GetCatalog(request.Substring(numChars), threshold).ToStringWithDeclaration(); } else if (request.Contains("/sequence/")) { xml = new BooksCatalog().GetCatalogBySequence(request.Substring(10), acceptFB2, threshold).ToStringWithDeclaration(); } else if (request.StartsWith("/genres")) { int numChars = request.Contains("/genres/") ? 8 : 7; xml = new GenresCatalog().GetCatalog(request.Substring(numChars)).ToStringWithDeclaration(); } else if (request.StartsWith("/genre/")) { xml = new BooksCatalog().GetCatalogByGenre(request.Substring(7), acceptFB2, threshold).ToStringWithDeclaration(); } else if (request.StartsWith("/search")) { if (http_params.Length > 1 && http_params[1].Equals("searchTerm")) { xml = new OpenSearch().Search(http_params[2], "", acceptFB2).ToStringWithDeclaration(); } else if (http_params[1].Equals("searchType")) { int pageNumber = 0; if (http_params.Length > 6 && http_params[5].Equals("pageNumber")) { int.TryParse(http_params[6], out pageNumber); } xml = new OpenSearch().Search(http_params[4], http_params[2], acceptFB2, pageNumber).ToStringWithDeclaration(); } } if (string.IsNullOrEmpty(xml)) { processor.WriteFailure(); return; } // Fix for the root namespace // TODO: fix with standard way (how?) xml = xml.Insert(xml.IndexOf("<feed ") + 5, " xmlns=\"http://www.w3.org/2005/Atom\""); if (TinyOPDS.Properties.Settings.Default.UseAbsoluteUri) { try { string host = processor.HttpHeaders["Host"].ToString(); xml = xml.Replace("href=\"", "href=\"http://" + (isWWWRequest ? host.UrlCombine(TinyOPDS.Properties.Settings.Default.HttpPrefix) : host.UrlCombine(TinyOPDS.Properties.Settings.Default.RootPrefix))); } catch { } } else { string prefix = isWWWRequest ? TinyOPDS.Properties.Settings.Default.HttpPrefix : TinyOPDS.Properties.Settings.Default.RootPrefix; if (!string.IsNullOrEmpty(prefix)) { prefix = "/" + prefix; } xml = xml.Replace("href=\"", "href=\"" + prefix); // Fix open search link xml = xml.Replace(prefix + "/opds-opensearch.xml", "/opds-opensearch.xml"); } // Apply xsl transform if (isWWWRequest) { string html = string.Empty; MemoryStream htmlStream = new MemoryStream(); using (StringReader stream = new StringReader(xml)) { XPathDocument myXPathDoc = new XPathDocument(stream); // for easy debug of xsl transform, we'll reload external file in DEBUG build #if DEBUG string xslFileName = Path.Combine(Utils.ServiceFilesLocation, "xml2html.xsl"); _xslTransform = new XslCompiledTransform(); if (File.Exists(xslFileName)) { _xslTransform.Load(xslFileName); } else { using (Stream resStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".xml2html.xsl")) { using (XmlReader reader = XmlReader.Create(resStream)) _xslTransform.Load(reader); } } #endif XmlTextWriter myWriter = new XmlTextWriter(htmlStream, null); _xslTransform.Transform(myXPathDoc, null, myWriter); htmlStream.Position = 0; using (StreamReader sr = new StreamReader(htmlStream)) html = sr.ReadToEnd(); } processor.WriteSuccess("text/html"); processor.OutputStream.Write(html); } else { processor.WriteSuccess("application/atom+xml;charset=utf-8"); processor.OutputStream.Write(xml); } } catch (Exception e) { Log.WriteLine(LogLevel.Error, "OPDS catalog exception {0}", e.Message); } return; } else if (request.Contains("opds-opensearch.xml")) { xml = new OpenSearch().OpenSearchDescription().ToStringWithDeclaration(); xml = xml.Insert(xml.IndexOf("<OpenSearchDescription") + 22, " xmlns=\"http://a9.com/-/spec/opensearch/1.1/\""); if (TinyOPDS.Properties.Settings.Default.UseAbsoluteUri) { try { string host = processor.HttpHeaders["Host"].ToString(); xml = xml.Replace("href=\"", "href=\"http://" + host.UrlCombine(TinyOPDS.Properties.Settings.Default.RootPrefix)); } catch { } } processor.WriteSuccess("application/atom+xml;charset=utf-8"); processor.OutputStream.Write(xml); return; } // fb2.zip book request else if ((request.Contains(".fb2.zip") && ext.Equals(".zip")) || ext.Equals(".epub")) { string bookID = request.Substring(1, request.IndexOf('/', 1) - 1).Replace("%7B", "{").Replace("%7D", "}"); Book book = Library.GetBook(bookID); if (book != null) { MemoryStream memStream = null; memStream = new MemoryStream(); if (request.Contains(".fb2.zip")) { try { if (book.FilePath.ToLower().Contains(".zip@")) { string[] pathParts = book.FilePath.Split('@'); using (ZipFile zipFile = new ZipFile(pathParts[0])) { ZipEntry entry = zipFile.Entries.First(e => e.FileName.Contains(pathParts[1])); if (entry != null) { entry.Extract(memStream); } } } else { using (FileStream stream = new FileStream(book.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) stream.CopyTo(memStream); } memStream.Position = 0; // Compress fb2 document to zip using (ZipFile zip = new ZipFile()) { zip.AddEntry(Transliteration.Front(string.Format("{0}_{1}.fb2", book.Authors.First(), book.Title)), memStream); using (MemoryStream outputStream = new MemoryStream()) { zip.Save(outputStream); outputStream.Position = 0; processor.WriteSuccess("application/fb2+zip"); outputStream.CopyTo(processor.OutputStream.BaseStream); } } HttpServer.ServerStatistics.BooksSent++; } catch (Exception e) { Log.WriteLine(LogLevel.Error, "FB2 file exception {0}", e.Message); } } else if (ext.Equals(".epub")) { try { if (book.FilePath.ToLower().Contains(".zip@")) { string[] pathParts = book.FilePath.Split('@'); using (ZipFile zipFile = new ZipFile(pathParts[0])) { ZipEntry entry = zipFile.Entries.First(e => e.FileName.Contains(pathParts[1])); if (entry != null) { entry.Extract(memStream); } entry = null; } } else { using (FileStream stream = new FileStream(book.FilePath, FileMode.Open, FileAccess.Read, FileShare.Read)) stream.CopyTo(memStream); } memStream.Position = 0; // At this moment, memStream has a copy of requested book // For fb2, we need convert book to epub if (book.BookType == BookType.FB2) { // No convertor found, return an error if (string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath)) { Log.WriteLine(LogLevel.Error, "No FB2 to EPUB convertor found, file request can not be completed!"); processor.WriteFailure(); return; } // Save fb2 book to the temp folder string inFileName = Path.Combine(Path.GetTempPath(), book.ID + ".fb2"); using (FileStream stream = new FileStream(inFileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite)) memStream.CopyTo(stream); // Run converter string outFileName = Path.Combine(Path.GetTempPath(), book.ID + ".epub"); string command = Path.Combine(TinyOPDS.Properties.Settings.Default.ConvertorPath, Utils.IsLinux ? "fb2toepub" : "Fb2ePub.exe"); string arguments = string.Format(Utils.IsLinux ? "{0} {1}" : "\"{0}\" \"{1}\"", inFileName, outFileName); using (ProcessHelper converter = new ProcessHelper(command, arguments)) { converter.Run(); if (File.Exists(outFileName)) { memStream = new MemoryStream(); using (FileStream fileStream = new FileStream(outFileName, FileMode.Open, FileAccess.Read, FileShare.Read)) fileStream.CopyTo(memStream); // Cleanup temp folder try { File.Delete(inFileName); } catch { } try { File.Delete(outFileName); } catch { } } else { string converterError = string.Empty; foreach (string s in converter.ProcessOutput) { converterError += s + " "; } Log.WriteLine(LogLevel.Error, "EPUB conversion error on file {0}. Error description: {1}", inFileName, converterError); processor.WriteFailure(); return; } } } // At this moment, memStream has a copy of epub processor.WriteSuccess("application/epub+zip"); memStream.Position = 0; memStream.CopyTo(processor.OutputStream.BaseStream); HttpServer.ServerStatistics.BooksSent++; } catch (Exception e) { Log.WriteLine(LogLevel.Error, "EPUB file exception {0}", e.Message); } } processor.OutputStream.BaseStream.Flush(); if (memStream != null) { memStream.Dispose(); } } else { Log.WriteLine(LogLevel.Error, "Book {0} not found in library.", bookID); } } // Cover image or thumbnail request else if (ext.Contains(".jpeg")) { bool getCover = true; string bookID = string.Empty; if (request.Contains("/cover/")) { bookID = Path.GetFileNameWithoutExtension(request.Substring(request.IndexOf("/cover/") + 7)); } else if (request.Contains("/thumbnail/")) { bookID = Path.GetFileNameWithoutExtension(request.Substring(request.IndexOf("/thumbnail/") + 11)); getCover = false; } bookID = bookID.Replace("%7B", "{").Replace("%7D", "}"); if (!string.IsNullOrEmpty(bookID)) { CoverImage image = null; Book book = Library.GetBook(bookID); if (book != null) { if (ImagesCache.HasImage(bookID)) { image = ImagesCache.GetImage(bookID); } else { image = new CoverImage(book); if (image != null && image.HasImages) { ImagesCache.Add(image); } } if (image != null && image.HasImages) { processor.WriteSuccess("image/jpeg"); (getCover ? image.CoverImageStream : image.ThumbnailImageStream).CopyTo(processor.OutputStream.BaseStream); processor.OutputStream.BaseStream.Flush(); HttpServer.ServerStatistics.ImagesSent++; return; } } } } // favicon.ico request else if (ext.Contains(".ico")) { string icon = Path.GetFileName(request); Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".Icons." + icon); if (stream != null && stream.Length > 0) { processor.WriteSuccess("image/x-icon"); stream.CopyTo(processor.OutputStream.BaseStream); processor.OutputStream.BaseStream.Flush(); return; } } processor.WriteFailure(); } catch (Exception e) { Log.WriteLine(LogLevel.Error, ".HandleGETRequest() exception {0}", e.Message); processor.WriteFailure(); } }
public void Should_Implement_IImagesCache() { object sut = new ImagesCache(A.Dummy <IDistributedCache>()); sut.Should().BeAssignableTo <IImagesCache>(); }