From 2e11ef1148c6458af36785999be510a2e41a3075 Mon Sep 17 00:00:00 2001 From: SeNS Date: Tue, 18 Dec 2018 12:47:31 -0500 Subject: [PATCH] code moved from "trunk" --- {trunk/TinyOPDS => TinyOPDS}/Data/Book.cs | 180 +- .../TinyOPDS => TinyOPDS}/Data/CoverImage.cs | 264 +- {trunk/TinyOPDS => TinyOPDS}/Data/Genre.cs | 54 +- .../TinyOPDS => TinyOPDS}/Data/ImagesCache.cs | 102 +- {trunk/TinyOPDS => TinyOPDS}/Data/Library.cs | 1446 +++---- .../TinyOPDS => TinyOPDS}/Icons/TinyOPDS.ico | Bin .../TinyOPDS => TinyOPDS}/Icons/authors.ico | Bin .../TinyOPDS => TinyOPDS}/Icons/authors.png | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/book.png | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/book2.png | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/book3.png | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/books.ico | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/books.png | Bin .../TinyOPDS => TinyOPDS}/Icons/favicon.ico | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/folder.png | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/genres.ico | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/genres.png | Bin .../TinyOPDS => TinyOPDS}/Icons/genres2.png | Bin {trunk/TinyOPDS => TinyOPDS}/Icons/lib.png | Bin .../TinyOPDS => TinyOPDS}/Libs/FB2Library.dll | Bin .../Libs/FB2Library.dll.gz | Bin .../Libs/Ionic.Zip.Reduced.dll | Bin .../Libs/Ionic.Zip.Reduced.dll.gz | Bin .../Libs/eBdb.EpubReader.dll | Bin .../Libs/eBdb.EpubReader.dll.gz | Bin {trunk/TinyOPDS => TinyOPDS}/Libs/gzip.cs | 126 +- {trunk/TinyOPDS => TinyOPDS}/Libs/gzip.exe | Bin {trunk/TinyOPDS => TinyOPDS}/License.txt | 58 +- .../MainForm.Designer.cs | 3404 ++++++++--------- {trunk/TinyOPDS => TinyOPDS}/MainForm.cs | 1878 ++++----- {trunk/TinyOPDS => TinyOPDS}/MainForm.resx | 244 +- {trunk/TinyOPDS => TinyOPDS}/Misc/Crypt.cs | 310 +- .../Misc/CustomSettingsProvider.cs | 550 +-- .../TinyOPDS => TinyOPDS}/Misc/Localizer.cs | 506 +-- {trunk/TinyOPDS => TinyOPDS}/Misc/Log.cs | 262 +- .../Misc/OPDSComparer.cs | 128 +- .../Misc/ProcessHelper.cs | 312 +- .../Misc/ServiceTools.cs | 1232 +++--- .../TinyOPDS => TinyOPDS}/Misc/StringUtils.cs | 396 +- {trunk/TinyOPDS => TinyOPDS}/Misc/UPnP.cs | 626 +-- {trunk/TinyOPDS => TinyOPDS}/Misc/Utils.cs | 588 +-- .../OPDS/AuthorsCatalog.cs | 250 +- .../OPDS/BooksCatalog.cs | 574 +-- .../OPDS/GenresCatalog.cs | 176 +- {trunk/TinyOPDS => TinyOPDS}/OPDS/Links.cs | 78 +- .../TinyOPDS => TinyOPDS}/OPDS/Namespaces.cs | 48 +- .../OPDS/NewBooksCatalog.cs | 364 +- .../TinyOPDS => TinyOPDS}/OPDS/OpenSearch.cs | 226 +- .../TinyOPDS => TinyOPDS}/OPDS/RootCatalog.cs | 186 +- .../OPDS/SequencesCatalog.cs | 182 +- .../Parsers/BookParser.cs | 126 +- .../Parsers/ePubParser.cs | 326 +- .../Parsers/fb2Parser.cs | 516 +-- {trunk/TinyOPDS => TinyOPDS}/ProcessHelper.cs | 314 +- {trunk/TinyOPDS => TinyOPDS}/Program.cs | 220 +- .../Properties/AssemblyInfo.cs | 72 +- .../Properties/Resources.Designer.cs | 182 +- .../Properties/Resources.resx | 264 +- .../Properties/Settings.Designer.cs | 798 ++-- .../Properties/Settings.settings | 196 +- .../Properties/app.manifest | 92 +- .../TinyOPDS => TinyOPDS}/Resources/fb2.dtd | 0 .../Resources/trayIcon.ico | Bin .../Scanner/FileScanner.cs | 336 +- .../Scanner/ScannerEvents.cs | 124 +- .../TinyOPDS => TinyOPDS}/Scanner/Watcher.cs | 494 +-- .../Scanner/ZipScanner.cs | 278 +- .../Server/HttpServer.cs | 1140 +++--- .../Server/OPDSServer.cs | 958 ++--- .../SgmlReader/SgmlParser.cs | 0 .../SgmlReader/SgmlReader.cs | 0 {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.csproj | 516 +-- {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.ico | Bin {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.png | Bin {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.sln | 146 +- {trunk/TinyOPDS => TinyOPDS}/a_aliases.txt.gz | Bin {trunk/TinyOPDS => TinyOPDS}/app.config | 218 +- {trunk/TinyOPDS => TinyOPDS}/convertGenres.cs | 146 +- .../TinyOPDS => TinyOPDS}/convertGenres.exe | Bin {trunk/TinyOPDS => TinyOPDS}/donate.png | Bin {trunk/TinyOPDS => TinyOPDS}/genres.rus.txt | 0 {trunk/TinyOPDS => TinyOPDS}/genres.xml | 582 +-- {trunk/TinyOPDS => TinyOPDS}/translation.xml | 1074 +++--- {trunk/TinyOPDS => TinyOPDS}/xml2html.xsl | 354 +- .../App.config | 12 +- .../Program.cs | 1198 +++--- .../Properties/AssemblyInfo.cs | 72 +- .../Properties/Settings.Designer.cs | 630 +-- .../Properties/Settings.settings | 154 +- .../Properties/app.manifest | 92 +- .../TinyOPDSConsole.csproj | 538 +-- releases/1.1/..svnbridge/TinyOPDS.ico | 1 - releases/1.1/..svnbridge/TinyOPDS.png | 1 - releases/1.1/..svnbridge/TinyOPDS.v11.suo | 1 - releases/1.1/..svnbridge/donate.png | 1 - trunk/TinyOPDS/..svnbridge/a_aliases.txt.gz | 1 - trunk/TinyOPDS/Icons/..svnbridge/TinyOPDS.ico | 1 - trunk/TinyOPDS/Icons/..svnbridge/authors.ico | 1 - trunk/TinyOPDS/Icons/..svnbridge/authors.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/book.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/book2.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/book3.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/books.ico | 1 - trunk/TinyOPDS/Icons/..svnbridge/books.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/favicon.ico | 1 - trunk/TinyOPDS/Icons/..svnbridge/folder.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/genres.ico | 1 - trunk/TinyOPDS/Icons/..svnbridge/genres.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/genres2.png | 1 - trunk/TinyOPDS/Icons/..svnbridge/lib.png | 1 - .../TinyOPDS/Libs/..svnbridge/FB2Library.dll | 1 - .../Libs/..svnbridge/FB2Library.dll.gz | 1 - .../Libs/..svnbridge/Ionic.Zip.Reduced.dll | 1 - .../Libs/..svnbridge/Ionic.Zip.Reduced.dll.gz | 1 - .../Libs/..svnbridge/eBdb.EpubReader.dll | 1 - .../Libs/..svnbridge/eBdb.EpubReader.dll.gz | 1 - trunk/TinyOPDS/Libs/..svnbridge/gzip.exe | 1 - .../Resources/..svnbridge/trayIcon.ico | 1 - 118 files changed, 13459 insertions(+), 13486 deletions(-) rename {trunk/TinyOPDS => TinyOPDS}/Data/Book.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Data/CoverImage.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Data/Genre.cs (96%) rename {trunk/TinyOPDS => TinyOPDS}/Data/ImagesCache.cs (96%) rename {trunk/TinyOPDS => TinyOPDS}/Data/Library.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/TinyOPDS.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/authors.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/authors.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/book.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/book2.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/book3.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/books.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/books.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/favicon.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/folder.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/genres.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/genres.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/genres2.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Icons/lib.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/FB2Library.dll (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/FB2Library.dll.gz (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/Ionic.Zip.Reduced.dll (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/Ionic.Zip.Reduced.dll.gz (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/eBdb.EpubReader.dll (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/eBdb.EpubReader.dll.gz (100%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/gzip.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Libs/gzip.exe (100%) rename {trunk/TinyOPDS => TinyOPDS}/License.txt (98%) rename {trunk/TinyOPDS => TinyOPDS}/MainForm.Designer.cs (98%) rename {trunk/TinyOPDS => TinyOPDS}/MainForm.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/MainForm.resx (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/Crypt.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/CustomSettingsProvider.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/Localizer.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/Log.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/OPDSComparer.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/ProcessHelper.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/ServiceTools.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/StringUtils.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/UPnP.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Misc/Utils.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/AuthorsCatalog.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/BooksCatalog.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/GenresCatalog.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/Links.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/Namespaces.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/NewBooksCatalog.cs (98%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/OpenSearch.cs (98%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/RootCatalog.cs (98%) rename {trunk/TinyOPDS => TinyOPDS}/OPDS/SequencesCatalog.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Parsers/BookParser.cs (96%) rename {trunk/TinyOPDS => TinyOPDS}/Parsers/ePubParser.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Parsers/fb2Parser.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/ProcessHelper.cs (96%) rename {trunk/TinyOPDS => TinyOPDS}/Program.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Properties/AssemblyInfo.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Properties/Resources.Designer.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Properties/Resources.resx (98%) rename {trunk/TinyOPDS => TinyOPDS}/Properties/Settings.Designer.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Properties/Settings.settings (97%) rename {trunk/TinyOPDSConsole => TinyOPDS}/Properties/app.manifest (98%) rename {trunk/TinyOPDS => TinyOPDS}/Resources/fb2.dtd (100%) rename {trunk/TinyOPDS => TinyOPDS}/Resources/trayIcon.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/Scanner/FileScanner.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Scanner/ScannerEvents.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Scanner/Watcher.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Scanner/ZipScanner.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Server/HttpServer.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/Server/OPDSServer.cs (98%) rename {trunk/TinyOPDS => TinyOPDS}/SgmlReader/SgmlParser.cs (100%) rename {trunk/TinyOPDS => TinyOPDS}/SgmlReader/SgmlReader.cs (100%) rename {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.csproj (97%) rename {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.ico (100%) rename {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/TinyOPDS.sln (98%) rename {trunk/TinyOPDS => TinyOPDS}/a_aliases.txt.gz (100%) rename {trunk/TinyOPDS => TinyOPDS}/app.config (97%) rename {trunk/TinyOPDS => TinyOPDS}/convertGenres.cs (97%) rename {trunk/TinyOPDS => TinyOPDS}/convertGenres.exe (100%) rename {trunk/TinyOPDS => TinyOPDS}/donate.png (100%) rename {trunk/TinyOPDS => TinyOPDS}/genres.rus.txt (100%) rename {trunk/TinyOPDS => TinyOPDS}/genres.xml (98%) rename {trunk/TinyOPDS => TinyOPDS}/translation.xml (97%) rename {trunk/TinyOPDS => TinyOPDS}/xml2html.xsl (96%) rename {trunk/TinyOPDSConsole => TinyOPDSConsole}/App.config (96%) rename {trunk/TinyOPDSConsole => TinyOPDSConsole}/Program.cs (97%) rename {trunk/TinyOPDSConsole => TinyOPDSConsole}/Properties/AssemblyInfo.cs (97%) rename {trunk/TinyOPDSConsole => TinyOPDSConsole}/Properties/Settings.Designer.cs (97%) rename {trunk/TinyOPDSConsole => TinyOPDSConsole}/Properties/Settings.settings (97%) rename {trunk/TinyOPDS => TinyOPDSConsole}/Properties/app.manifest (98%) rename {trunk/TinyOPDSConsole => TinyOPDSConsole}/TinyOPDSConsole.csproj (97%) delete mode 100644 releases/1.1/..svnbridge/TinyOPDS.ico delete mode 100644 releases/1.1/..svnbridge/TinyOPDS.png delete mode 100644 releases/1.1/..svnbridge/TinyOPDS.v11.suo delete mode 100644 releases/1.1/..svnbridge/donate.png delete mode 100644 trunk/TinyOPDS/..svnbridge/a_aliases.txt.gz delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/TinyOPDS.ico delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/authors.ico delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/authors.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/book.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/book2.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/book3.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/books.ico delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/books.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/favicon.ico delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/folder.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/genres.ico delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/genres.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/genres2.png delete mode 100644 trunk/TinyOPDS/Icons/..svnbridge/lib.png delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll.gz delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll.gz delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll.gz delete mode 100644 trunk/TinyOPDS/Libs/..svnbridge/gzip.exe delete mode 100644 trunk/TinyOPDS/Resources/..svnbridge/trayIcon.ico diff --git a/trunk/TinyOPDS/Data/Book.cs b/TinyOPDS/Data/Book.cs similarity index 97% rename from trunk/TinyOPDS/Data/Book.cs rename to TinyOPDS/Data/Book.cs index 6948dd4..9845a3f 100644 --- a/trunk/TinyOPDS/Data/Book.cs +++ b/TinyOPDS/Data/Book.cs @@ -1,90 +1,90 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the Book class - * - ************************************************************/ - -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Collections.Generic; -using System.Drawing; - -namespace TinyOPDS.Data -{ - /// - /// Supported book types - /// - public enum BookType - { - FB2, - EPUB, - } - - /// - /// Base data class - /// - public class Book - { - public Book(string fileName = "") - { - Version = 1; - FileName = fileName; - if (!string.IsNullOrEmpty(FileName) && FileName.IndexOf(Library.LibraryPath)==0) - { - FileName = FileName.Substring(Library.LibraryPath.Length+1); - } - Title = Sequence = Annotation = Language = string.Empty; - HasCover = false; - BookDate = DocumentDate = DateTime.MinValue; - NumberInSequence = 0; - Authors = new List(); - Translators = new List(); - Genres = new List(); - } - private string _id = string.Empty; - public string ID - { - get { return _id; } - set - { - // Book ID always must be in GUID form - Guid guid; - if (!string.IsNullOrEmpty(value) && Guid.TryParse(value, out guid)) _id = value; else _id = Utils.CreateGuid(Utils.IsoOidNamespace, FileName).ToString(); - _id = _id.Replace("{", "").Replace("}", ""); - } - } - public float Version { get; set; } - public string FileName { get; private set; } - public string FilePath - { - get - { - string path = Path.Combine(Library.LibraryPath, FileName).Replace ("\\\\", "\\"); - return Utils.IsLinux ? path.Replace('\\', '/') : path; - } - } - public string Title { get; set; } - public string Language { get; set; } - public bool HasCover { get; set; } - public DateTime BookDate { get; set; } - public DateTime DocumentDate { get; set; } - public string Sequence { get; set; } - public UInt32 NumberInSequence { get; set; } - public string Annotation { get; set; } - public UInt32 DocumentSize { get; set; } - public List Authors { get; set; } - public List Translators { get; set; } - public List Genres { get; set; } - public BookType BookType { get { return Path.GetExtension(FilePath).ToLower().Contains("epub") ? BookType.EPUB : Data.BookType.FB2; } } - public bool IsValid { get { return (!string.IsNullOrEmpty(Title) && Title.IsValidUTF() && Authors.Count > 0 && Genres.Count > 0); } } - public DateTime AddedDate { get; set; } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the Book class + * + ************************************************************/ + +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Collections.Generic; +using System.Drawing; + +namespace TinyOPDS.Data +{ + /// + /// Supported book types + /// + public enum BookType + { + FB2, + EPUB, + } + + /// + /// Base data class + /// + public class Book + { + public Book(string fileName = "") + { + Version = 1; + FileName = fileName; + if (!string.IsNullOrEmpty(FileName) && FileName.IndexOf(Library.LibraryPath)==0) + { + FileName = FileName.Substring(Library.LibraryPath.Length+1); + } + Title = Sequence = Annotation = Language = string.Empty; + HasCover = false; + BookDate = DocumentDate = DateTime.MinValue; + NumberInSequence = 0; + Authors = new List(); + Translators = new List(); + Genres = new List(); + } + private string _id = string.Empty; + public string ID + { + get { return _id; } + set + { + // Book ID always must be in GUID form + Guid guid; + if (!string.IsNullOrEmpty(value) && Guid.TryParse(value, out guid)) _id = value; else _id = Utils.CreateGuid(Utils.IsoOidNamespace, FileName).ToString(); + _id = _id.Replace("{", "").Replace("}", ""); + } + } + public float Version { get; set; } + public string FileName { get; private set; } + public string FilePath + { + get + { + string path = Path.Combine(Library.LibraryPath, FileName).Replace ("\\\\", "\\"); + return Utils.IsLinux ? path.Replace('\\', '/') : path; + } + } + public string Title { get; set; } + public string Language { get; set; } + public bool HasCover { get; set; } + public DateTime BookDate { get; set; } + public DateTime DocumentDate { get; set; } + public string Sequence { get; set; } + public UInt32 NumberInSequence { get; set; } + public string Annotation { get; set; } + public UInt32 DocumentSize { get; set; } + public List Authors { get; set; } + public List Translators { get; set; } + public List Genres { get; set; } + public BookType BookType { get { return Path.GetExtension(FilePath).ToLower().Contains("epub") ? BookType.EPUB : Data.BookType.FB2; } } + public bool IsValid { get { return (!string.IsNullOrEmpty(Title) && Title.IsValidUTF() && Authors.Count > 0 && Genres.Count > 0); } } + public DateTime AddedDate { get; set; } + } +} diff --git a/trunk/TinyOPDS/Data/CoverImage.cs b/TinyOPDS/Data/CoverImage.cs similarity index 97% rename from trunk/TinyOPDS/Data/CoverImage.cs rename to TinyOPDS/Data/CoverImage.cs index e147c83..462aebb 100644 --- a/trunk/TinyOPDS/Data/CoverImage.cs +++ b/TinyOPDS/Data/CoverImage.cs @@ -1,132 +1,132 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the CoverImage class and Image extensions - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Drawing; -using System.Drawing.Imaging; -using System.Drawing.Drawing2D; - -using Ionic.Zip; -using TinyOPDS.Parsers; - -namespace TinyOPDS.Data -{ - /// - /// - /// - public class CoverImage - { - public static Size CoverSize = new Size(480, 800); - public static Size ThumbnailSize = new Size(48, 80); - - private Image _cover; - private Image _thumbnail { get { return (_cover != null) ? _cover.Resize(ThumbnailSize) : null; } } - public Stream CoverImageStream { get { return _cover.ToStream(ImageFormat.Jpeg); } } - public Stream ThumbnailImageStream { get { return _thumbnail.ToStream(ImageFormat.Jpeg); } } - public bool HasImages { get { return _cover != null; } } - public string ID { get; set; } - - public CoverImage(Book book) - { - _cover = null; - ID = book.ID; - try - { - using (MemoryStream memStream = new MemoryStream()) - { - 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); - } - - _cover = (book.BookType == BookType.EPUB) ? new ePubParser().GetCoverImage(memStream, book.FilePath) - : new FB2Parser().GetCoverImage(memStream, book.FilePath); - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "file {0}, exception {1}", book.FilePath, e.Message); - } - } - } - - public static class ImageExtensions - { - public static Stream ToStream(this Image image, ImageFormat format) - { - MemoryStream stream = null; - try - { - stream = new MemoryStream(); - if (image != null) - { - image.Save(stream, format); - stream.Position = 0; - } - } - catch - { - if (stream != null) stream.Dispose(); - } - return stream; - } - - public static Image Resize(this Image image, Size size, bool preserveAspectRatio = true) - { - if (image == null) return null; - int newWidth, newHeight; - if (preserveAspectRatio) - { - int originalWidth = image.Width; - int originalHeight = image.Height; - float percentWidth = (float)size.Width / (float)originalWidth; - float percentHeight = (float)size.Height / (float)originalHeight; - float percent = percentHeight < percentWidth ? percentHeight : percentWidth; - newWidth = (int)(originalWidth * percent); - newHeight = (int)(originalHeight * percent); - } - else - { - newWidth = size.Width; - newHeight = size.Height; - } - Image newImage = null; - try - { - newImage = new Bitmap(newWidth, newHeight); - using (Graphics graphicsHandle = Graphics.FromImage(newImage)) - { - graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic; - graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight); - } - } - catch - { - if (newImage != null) newImage.Dispose(); - } - return newImage; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the CoverImage class and Image extensions + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Drawing; +using System.Drawing.Imaging; +using System.Drawing.Drawing2D; + +using Ionic.Zip; +using TinyOPDS.Parsers; + +namespace TinyOPDS.Data +{ + /// + /// + /// + public class CoverImage + { + public static Size CoverSize = new Size(480, 800); + public static Size ThumbnailSize = new Size(48, 80); + + private Image _cover; + private Image _thumbnail { get { return (_cover != null) ? _cover.Resize(ThumbnailSize) : null; } } + public Stream CoverImageStream { get { return _cover.ToStream(ImageFormat.Jpeg); } } + public Stream ThumbnailImageStream { get { return _thumbnail.ToStream(ImageFormat.Jpeg); } } + public bool HasImages { get { return _cover != null; } } + public string ID { get; set; } + + public CoverImage(Book book) + { + _cover = null; + ID = book.ID; + try + { + using (MemoryStream memStream = new MemoryStream()) + { + 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); + } + + _cover = (book.BookType == BookType.EPUB) ? new ePubParser().GetCoverImage(memStream, book.FilePath) + : new FB2Parser().GetCoverImage(memStream, book.FilePath); + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "file {0}, exception {1}", book.FilePath, e.Message); + } + } + } + + public static class ImageExtensions + { + public static Stream ToStream(this Image image, ImageFormat format) + { + MemoryStream stream = null; + try + { + stream = new MemoryStream(); + if (image != null) + { + image.Save(stream, format); + stream.Position = 0; + } + } + catch + { + if (stream != null) stream.Dispose(); + } + return stream; + } + + public static Image Resize(this Image image, Size size, bool preserveAspectRatio = true) + { + if (image == null) return null; + int newWidth, newHeight; + if (preserveAspectRatio) + { + int originalWidth = image.Width; + int originalHeight = image.Height; + float percentWidth = (float)size.Width / (float)originalWidth; + float percentHeight = (float)size.Height / (float)originalHeight; + float percent = percentHeight < percentWidth ? percentHeight : percentWidth; + newWidth = (int)(originalWidth * percent); + newHeight = (int)(originalHeight * percent); + } + else + { + newWidth = size.Width; + newHeight = size.Height; + } + Image newImage = null; + try + { + newImage = new Bitmap(newWidth, newHeight); + using (Graphics graphicsHandle = Graphics.FromImage(newImage)) + { + graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic; + graphicsHandle.DrawImage(image, 0, 0, newWidth, newHeight); + } + } + catch + { + if (newImage != null) newImage.Dispose(); + } + return newImage; + } + } +} diff --git a/trunk/TinyOPDS/Data/Genre.cs b/TinyOPDS/Data/Genre.cs similarity index 96% rename from trunk/TinyOPDS/Data/Genre.cs rename to TinyOPDS/Data/Genre.cs index 5e4c1fa..0b9c659 100644 --- a/trunk/TinyOPDS/Data/Genre.cs +++ b/TinyOPDS/Data/Genre.cs @@ -1,27 +1,27 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the Genre class (book genre) - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace TinyOPDS.Data -{ - public class Genre - { - public string Tag { get; set; } - public string Name { get; set; } - public string Translation { get; set; } - public List Subgenres = new List(); - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the Genre class (book genre) + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace TinyOPDS.Data +{ + public class Genre + { + public string Tag { get; set; } + public string Name { get; set; } + public string Translation { get; set; } + public List Subgenres = new List(); + } +} diff --git a/trunk/TinyOPDS/Data/ImagesCache.cs b/TinyOPDS/Data/ImagesCache.cs similarity index 96% rename from trunk/TinyOPDS/Data/ImagesCache.cs rename to TinyOPDS/Data/ImagesCache.cs index b8995ac..2e6e652 100644 --- a/trunk/TinyOPDS/Data/ImagesCache.cs +++ b/TinyOPDS/Data/ImagesCache.cs @@ -1,51 +1,51 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * Simple image caching class - * - * TODO: add disk caching - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.ComponentModel; -using System.Threading; -using System.Drawing; - -namespace TinyOPDS.Data -{ - public static class ImagesCache - { - private static Dictionary _cache; - - static ImagesCache() - { - _cache = new Dictionary(); - } - - public static void Add(CoverImage image) - { - // Add image to cache for "large memory" profile only - if (!TinyOPDS.Properties.Settings.Default.LowMemoryProfile) - { - if (!_cache.ContainsKey(image.ID)) - { - if (_cache.Count >= 1000) _cache.Remove(_cache.First().Key); - _cache[image.ID] = image; - } - } - } - - public static bool HasImage(string id) { return _cache.ContainsKey(id); } - - public static CoverImage GetImage(string id) { return _cache[id]; } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * Simple image caching class + * + * TODO: add disk caching + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; +using System.Threading; +using System.Drawing; + +namespace TinyOPDS.Data +{ + public static class ImagesCache + { + private static Dictionary _cache; + + static ImagesCache() + { + _cache = new Dictionary(); + } + + public static void Add(CoverImage image) + { + // Add image to cache for "large memory" profile only + if (!TinyOPDS.Properties.Settings.Default.LowMemoryProfile) + { + if (!_cache.ContainsKey(image.ID)) + { + if (_cache.Count >= 1000) _cache.Remove(_cache.First().Key); + _cache[image.ID] = image; + } + } + } + + public static bool HasImage(string id) { return _cache.ContainsKey(id); } + + public static CoverImage GetImage(string id) { return _cache[id]; } + } +} diff --git a/trunk/TinyOPDS/Data/Library.cs b/TinyOPDS/Data/Library.cs similarity index 97% rename from trunk/TinyOPDS/Data/Library.cs rename to TinyOPDS/Data/Library.cs index 4855c5e..1ae10f6 100644 --- a/trunk/TinyOPDS/Data/Library.cs +++ b/TinyOPDS/Data/Library.cs @@ -1,723 +1,723 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * One of the base project's classes, the Library class - * serves all database services using LINQ queries and - * static dictionaries - * - * TODO: refactor this class to work with SQLite database - * - ************************************************************/ - -using System; -using System.IO; -using System.IO.Compression; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Xml.Linq; -using System.Reflection; - -namespace TinyOPDS.Data -{ - public static class Library - { - public static event EventHandler LibraryLoaded; - private static Dictionary _paths = new Dictionary(); - private static Dictionary _books = new Dictionary(); - private static Dictionary _authors = new Dictionary(); - private static string _databaseFullPath; - private static List _genres; - private static Dictionary _soundexedGenres; - private static bool _converted = false; - private static TimeSpan[] _periods = new TimeSpan[7]; - private static Dictionary _aliases = new Dictionary(); - - /// - /// Default constructor - /// Opens library"books.db" from the executable file location - /// - static Library() - { - // Initialize "new books" periods - _periods[0] = TimeSpan.FromDays(7); - _periods[1] = TimeSpan.FromDays(14); - _periods[2] = TimeSpan.FromDays(21); - _periods[3] = TimeSpan.FromDays(30); - _periods[4] = TimeSpan.FromDays(44); - _periods[5] = TimeSpan.FromDays(60); - _periods[6] = TimeSpan.FromDays(90); - - // Load and parse genres - try - { - var doc = XDocument.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".genres.xml")); - _genres = doc.Root.Descendants("genre").Select(g => - new Genre() - { - Name = g.Attribute("name").Value, - Translation = g.Attribute("ru").Value, - Subgenres = g.Descendants("subgenre").Select(sg => - new Genre() - { - Name = sg.Value, - Tag = sg.Attribute("tag").Value, - Translation = sg.Attribute("ru").Value, - }).ToList() - }).ToList(); - - _soundexedGenres = new Dictionary(); - foreach (Genre genre in _genres) - foreach (Genre subgenre in genre.Subgenres) - { - _soundexedGenres[subgenre.Name.SoundexByWord()] = subgenre.Tag; - string reversed = string.Join(" ", subgenre.Name.Split(' ', ',').Reverse()).Trim(); - _soundexedGenres[reversed.SoundexByWord()] = subgenre.Tag; - } - - } - catch { } - - // Load gzipped authors aliases - string aliasesFileName = Path.Combine(Utils.ServiceFilesLocation, "a_aliases.txt"); - if (File.Exists(aliasesFileName)) - { - using (var stream = File.OpenRead(aliasesFileName)) - { - if (stream != null) - { - try - { - using (TextReader tr = new StreamReader(stream)) - { - string line = string.Empty; - while ((line = tr.ReadLine()) != null) - { - if (!string.IsNullOrEmpty(line)) - { - string[] parts = line.Split(new char[] { '\t', ',' }); - try - { - _aliases[string.Format("{2} {0} {1}", parts[5], parts[6], parts[7]).Trim()] = string.Format("{2} {0} {1}", parts[1], parts[2], parts[3]).Trim(); - } - catch - { - Log.WriteLine(LogLevel.Warning, "Error parsing alias '{0}'", line); - } - } - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "Error loading aliases: exception {0}", e.Message); - } - } - } - Log.WriteLine(LogLevel.Info, "Loaded {0} authors aliases from {1}", _aliases.Count, aliasesFileName); - } - else - { - using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".a_aliases.txt.gz")) - { - if (stream != null) - { - using (GZipStream decompress = new GZipStream(stream, CompressionMode.Decompress)) - { - using (TextReader tr = new StreamReader(decompress)) - { - string line = string.Empty; - while ((line = tr.ReadLine()) != null) - { - string[] parts = line.Split('\t'); - _aliases[string.Format("{2} {0} {1}", parts[5], parts[6], parts[7]).Trim()] = string.Format("{2} {0} {1}", parts[1], parts[2], parts[3]).Trim(); - } - } - } - } - } - } - - // Load library in separate thread (avoid UI blocking) - LoadAsync(); - } - - /// - /// Load library database in background - /// - public static void LoadAsync() - { - // Clear library and free memory - FB2Count = EPUBCount = 0; - _books.Clear(); - _paths.Clear(); - GC.Collect(); - - // Create unique database name, based on library path - LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); - string databaseFileName = Utils.CreateGuid(Utils.IsoOidNamespace, LibraryPath).ToString() + ".db"; - _databaseFullPath = Path.Combine(Utils.ServiceFilesLocation, databaseFileName); - - // Load database in the background thread - BackgroundWorker worker = new BackgroundWorker(); - worker.DoWork += (_, __) => - { - _converted = false; - Load(); - if (LibraryLoaded != null) LibraryLoaded(null, null); - if (_converted) - { - Save(); - Log.WriteLine(LogLevel.Info, "Database successfully converted to the format 1.1"); - } - worker.Dispose(); - }; - worker.RunWorkerAsync(); - } - - /// - /// Full path to the library folder - /// - public static string LibraryPath { get; set; } - - /// - /// Library changed flag (we should save!) - /// - public static bool IsChanged { get; set; } - - /// - /// - /// - /// - /// - public static bool Contains(string bookPath) - { - lock (_paths) - { - return _paths.ContainsKey(bookPath); - } - } - - /// - /// - /// - /// - /// - public static Book GetBook(string id) - { - lock (_books) - { - Book book = null; - if (_books.ContainsKey(id)) - { - book = _books[id]; - } - return book; - } - } - - /// - /// Add unique book descriptor to the library and saves the library - /// - /// - public static bool Add(Book book) - { - lock (_books) - { - // Prevent incorrect duplicates detection (same ID but different titles) - if (_books.ContainsKey(book.ID) && !book.Title.Equals(_books[book.ID].Title)) - { - book.ID = Utils.CreateGuid(Utils.IsoOidNamespace, book.FileName).ToString(); - } - - // Check for duplicates - if (!_books.ContainsKey(book.ID) || (_books.ContainsKey(book.ID) && _books[book.ID].Version < book.Version)) - { - // Remember duplicate flag - bool isDuplicate = _books.ContainsKey(book.ID); - book.AddedDate = DateTime.Now; - // Make relative path - _books[book.ID] = book; - lock (_paths) _paths[book.FileName] = book.ID; - if (!isDuplicate) - { - IsChanged = true; - if (book.BookType == BookType.FB2) FB2Count++; else EPUBCount++; - // Increase authors book count - foreach (var name in book.Authors) - if (!_authors.ContainsKey(name)) _authors[name] = 1; else _authors[name]++; - } - else - { - Log.WriteLine(LogLevel.Warning, "Replaced duplicate. File name {0}, book version {1}", book.FileName, book.Version); - } - return !isDuplicate; - } - Log.WriteLine(LogLevel.Warning, "Found duplicate. File name {0}, book version {1}", book.FileName, book.Version); - return false; - } - } - - /// - /// Delete all books with specific file path from the library - /// - /// - public static bool Delete(string fileName) - { - bool result = false; - lock (_books) - { - if (!string.IsNullOrEmpty(fileName) && fileName.Length > Library.LibraryPath.Length + 1) - { - // Extract relative file name - fileName = fileName.Substring(Library.LibraryPath.Length + 1); - string ext = Path.GetExtension(fileName.ToLower()); - - // Assume it's a single file - if (ext.Equals(".epub") || ext.Equals(".fb2") || (ext.Equals(".zip") && fileName.ToLower().Contains(".fb2.zip"))) - { - if (Contains(fileName)) - { - Book book = _books[_paths[fileName]]; - if (book != null) - { - _books.Remove(book.ID); - _paths.Remove(book.FileName); - if (book.BookType == BookType.FB2) FB2Count--; else EPUBCount--; - // Decrease authors book count - foreach (var name in book.Authors) - if (_authors.ContainsKey(name)) _authors[name]--; - result = IsChanged = true; - } - } - } - // removed object should be a zip archive; we have to remove all books contained in this archive - else - { - List booksForRemove = _books.Where(b => b.Value.FileName.StartsWith(fileName+"@")).Select(b => b.Value).ToList(); - foreach (Book book in booksForRemove) - { - _books.Remove(book.ID); - _paths.Remove(book.FileName); - if (book.BookType == BookType.FB2) FB2Count--; else EPUBCount--; - // Decrease authors book count - foreach (var name in book.Authors) - if (_authors.ContainsKey(name)) _authors[name]--; - } - if (booksForRemove.Count > 0) - { - result = IsChanged = true; - } - } - } - } - return result; - } - - /// - /// Total number of books in library - /// - public static int Count - { - get - { - lock (_books) return _books.Count; - } - } - - /// - /// New books count - /// - /// - public static int NewBooksCount - { - get - { - lock (_books) return _books.Values.Where(b => DateTime.Now.Subtract(b.AddedDate) <= _periods[TinyOPDS.Properties.Settings.Default.NewBooksPeriod]).Count(); - } - } - - /// - /// Returns FB2 books count - /// - public static int FB2Count { get; private set; } - - /// - /// Returns EPUB books count - /// - public static int EPUBCount { get; private set; } - - /// - /// Returns list of the books titles sorted in alphabetical order - /// - public static List Titles - { - get - { - lock (_books) - { - return _books.Values.Select(b => b.Title).Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList(); - } - } - } - - /// - /// Returns list of the authors sorted in alphabetical order - /// - public static List Authors - { - get - { - lock (_books) - { - return ((_books.Values.SelectMany(b => b.Authors)).ToList()).Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).Where(с => с.Length > 1).ToList(); - } - } - } - - /// - /// Returns list of the library books series sorted in alphabetical order - /// - public static List Sequences - { - get - { - lock (_books) - { - return ((_books.Values.Select(b => b.Sequence)).ToList()).Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).Where(с => с.Length > 1).ToList(); - } - } - } - - /// - /// All genres supported by fb2 format - /// - public static List FB2Genres - { - get - { - return _genres; - } - } - - public static Dictionary SoundexedGenres - { - get - { - return _soundexedGenres; - } - } - - /// - /// Returns sorted in alphabetical order list of library books genres - /// - public static List Genres - { - get - { - lock (_books) - { - var libGenres = _books.Values.SelectMany(b => b.Genres).ToList().Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).Where(с => с.Length > 1).Select(g => g.ToLower().Trim()).ToList(); - return _genres.SelectMany(g => g.Subgenres).Where(sg => libGenres.Contains(sg.Tag) || libGenres.Contains(sg.Name.ToLower()) || libGenres.Contains(sg.Translation.ToLower())).ToList(); - } - } - } - - /// - /// Return list of authors by name - /// - /// - /// - public static List GetAuthorsByName(string name, bool isOpenSearch) - { - List authors = new List(); - lock (_books) - { - if (isOpenSearch) authors = Authors.Where(a => a.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); - else authors = Authors.Where(a => a.StartsWith(name, StringComparison.OrdinalIgnoreCase)).ToList(); - if (isOpenSearch && authors.Count == 0) - { - string reversedName = name.Reverse(); - authors = Authors.Where(a => a.IndexOf(reversedName, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); - } - return authors; - } - } - - /// - /// Return list of books by title - /// - /// - /// - public static List GetBooksByTitle(string title) - { - lock (_books) return _books.Values.Where(b => b.Title.IndexOf(title, StringComparison.OrdinalIgnoreCase) >= 0 || b.Sequence.IndexOf(title, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); - } - - /// - /// Return list of books by selected author(s) - /// - /// - /// - public static List GetBooksByAuthor(string author) - { - lock (_books) return _books.Values.Where(b => b.Authors.Contains(author)).ToList(); - } - - /// - /// Return number of books by specific author - /// - /// - /// - public static int GetBooksByAuthorCount(string author) - { - return _authors.ContainsKey(author) ? _authors[author] : 0; - } - - /// - /// Return list of books by selected sequence - /// - /// - /// - public static List GetBooksBySequence(string sequence) - { - lock (_books) return _books.Values.Where(b => b.Sequence.Contains(sequence)).ToList(); - } - - /// - /// Return list of books by selected genre - /// - /// - /// - public static List GetBooksByGenre(string genre) - { - lock (_books) return _books.Values.Where(b => b.Genres.Contains(genre)).ToList(); - } - - /// - /// Return list of new books - /// - public static List NewBooks - { - get - { - TimeSpan period = _periods[TinyOPDS.Properties.Settings.Default.NewBooksPeriod]; - lock (_books) return _books.Values.Where(b => DateTime.Now.Subtract(b.AddedDate) <= period).ToList(); - } - } - - #region Serialization and deserialization - - /// - /// Load library - /// - public static void Load() - { - int numRecords = 0; - DateTime start = DateTime.Now; - - // MemoryStream can save us about 1 second on 106 Mb database load time - MemoryStream memStream = null; - if (File.Exists(_databaseFullPath)) - { - _books.Clear(); - _authors.Clear(); - FB2Count = EPUBCount = 0; - memStream = new MemoryStream(); - - try - { - using (Stream fileStream = new FileStream(_databaseFullPath, FileMode.Open, FileAccess.Read, FileShare.Read)) - { - fileStream.CopyTo(memStream); - } - memStream.Position = 0; - using (BinaryReader reader = new BinaryReader(memStream)) - { - bool newFormat = reader.ReadString().Equals("VER1.1"); - if (!newFormat) - { - reader.BaseStream.Position = 0; - _converted = true; - } - - DateTime now = DateTime.Now; - bool lowMemory = TinyOPDS.Properties.Settings.Default.LowMemoryProfile; - bool useAliases = TinyOPDS.Properties.Settings.Default.UseAuthorsAliases; - - while (reader.BaseStream.Position < reader.BaseStream.Length) - { - try - { - string fileName = reader.ReadString(); - Book book = new Book(Path.Combine(LibraryPath, fileName)); - string id = reader.ReadString(); - id = id.Replace("{", "").Replace("}", ""); - book.ID = id; - book.Version = reader.ReadSingle(); - book.Title = reader.ReadString(); - book.Language = reader.ReadString(); - book.HasCover = reader.ReadBoolean(); - book.BookDate = DateTime.FromBinary(reader.ReadInt64()); - book.DocumentDate = DateTime.FromBinary(reader.ReadInt64()); - book.Sequence = reader.ReadString(); - book.NumberInSequence = reader.ReadUInt32(); - if (lowMemory) { var s = reader.ReadString(); } else book.Annotation = reader.ReadString(); - book.DocumentSize = reader.ReadUInt32(); - int count = reader.ReadInt32(); - for (int i = 0; i < count; i++) - { - string a = reader.ReadString(); - // Replace author's alias (if any) by name - string name = (useAliases && _aliases.ContainsKey(a)) ? _aliases[a] : a; - book.Authors.Add(name); - if (!_authors.ContainsKey(name)) _authors[name] = 1; else _authors[name]++; - } - count = reader.ReadInt32(); - for (int i = 0; i < count; i++) book.Translators.Add(reader.ReadString()); - count = reader.ReadInt32(); - for (int i = 0; i < count; i++) book.Genres.Add(reader.ReadString()); - lock (_books) _books[book.ID] = book; - lock (_paths) _paths[book.FileName] = book.ID; - book.AddedDate = newFormat ? DateTime.FromBinary(reader.ReadInt64()) : now; - if (book.BookType == BookType.FB2) FB2Count++; else EPUBCount++; - - numRecords++; - } - catch (EndOfStreamException) - { - break; - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, e.Message); - break; - } - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "Load books exception {0}", e.Message); - } - finally - { - if (memStream != null) - { - memStream.Dispose(); - memStream = null; - } - // Call garbage collector now - GC.Collect(); - IsChanged = false; - } - } - - Log.WriteLine(LogLevel.Info, "Database load time = {0}, {1} book records loaded", DateTime.Now.Subtract(start), numRecords); - } - - /// - /// Save whole library - /// - /// Remark: new database format is used! - public static void Save() - { - // Do nothing if we have no records - if (_books.Count == 0) return; - - int numRecords = 0; - DateTime start = DateTime.Now; - Dictionary shallowCopy = null; - - Stream fileStream = null; - try - { - fileStream = new FileStream(_databaseFullPath, FileMode.Create, FileAccess.Write, FileShare.Write); - using (BinaryWriter writer = new BinaryWriter(fileStream)) - { - fileStream = null; - writer.Write("VER1.1"); - - // Create shallow copy (to prevent exception on dictionary modifications during foreach loop) - lock (_books) shallowCopy = new Dictionary(_books); - foreach (Book book in shallowCopy.Values) - { - writeBook(book, writer); - numRecords++; - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "Save books exception {0}", e.Message); - } - finally - { - shallowCopy = null; - if (fileStream != null) fileStream.Dispose(); - IsChanged = false; - Log.WriteLine(LogLevel.Info, "Database save time = {0}, {1} book records written to disk", DateTime.Now.Subtract(start), numRecords); - } - } - - /// - /// Append one book descriptor to the library file - /// - /// - public static void Append(Book book) - { - Stream fileStream = null; - try - { - fileStream = new FileStream(_databaseFullPath, FileMode.Append, FileAccess.Write, FileShare.Write); - using (BinaryWriter writer = new BinaryWriter(fileStream)) - { - fileStream = null; - writeBook(book, writer); - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "Can't append book {0}, exception {1}", book.FilePath, e.Message); - } - finally - { - if (fileStream != null) - { - fileStream.Dispose(); - } - IsChanged = false; - } - } - - private static void writeBook(Book book, BinaryWriter writer) - { - writer.Write(book.FileName); - writer.Write(book.ID); - writer.Write(book.Version); - writer.Write(book.Title); - writer.Write(book.Language); - writer.Write(book.HasCover); - writer.Write(book.BookDate.ToBinary()); - writer.Write(book.DocumentDate.ToBinary()); - writer.Write(book.Sequence); - writer.Write(book.NumberInSequence); - writer.Write(book.Annotation); - writer.Write(book.DocumentSize); - writer.Write((Int32)book.Authors.Count); - foreach (string author in book.Authors) writer.Write(author); - writer.Write((Int32)book.Translators.Count); - foreach (string translator in book.Translators) writer.Write(translator); - writer.Write((Int32)book.Genres.Count); - foreach (string genre in book.Genres) writer.Write(genre); - writer.Write(book.AddedDate.ToBinary()); - } - - #endregion - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * One of the base project's classes, the Library class + * serves all database services using LINQ queries and + * static dictionaries + * + * TODO: refactor this class to work with SQLite database + * + ************************************************************/ + +using System; +using System.IO; +using System.IO.Compression; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Xml.Linq; +using System.Reflection; + +namespace TinyOPDS.Data +{ + public static class Library + { + public static event EventHandler LibraryLoaded; + private static Dictionary _paths = new Dictionary(); + private static Dictionary _books = new Dictionary(); + private static Dictionary _authors = new Dictionary(); + private static string _databaseFullPath; + private static List _genres; + private static Dictionary _soundexedGenres; + private static bool _converted = false; + private static TimeSpan[] _periods = new TimeSpan[7]; + private static Dictionary _aliases = new Dictionary(); + + /// + /// Default constructor + /// Opens library"books.db" from the executable file location + /// + static Library() + { + // Initialize "new books" periods + _periods[0] = TimeSpan.FromDays(7); + _periods[1] = TimeSpan.FromDays(14); + _periods[2] = TimeSpan.FromDays(21); + _periods[3] = TimeSpan.FromDays(30); + _periods[4] = TimeSpan.FromDays(44); + _periods[5] = TimeSpan.FromDays(60); + _periods[6] = TimeSpan.FromDays(90); + + // Load and parse genres + try + { + var doc = XDocument.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".genres.xml")); + _genres = doc.Root.Descendants("genre").Select(g => + new Genre() + { + Name = g.Attribute("name").Value, + Translation = g.Attribute("ru").Value, + Subgenres = g.Descendants("subgenre").Select(sg => + new Genre() + { + Name = sg.Value, + Tag = sg.Attribute("tag").Value, + Translation = sg.Attribute("ru").Value, + }).ToList() + }).ToList(); + + _soundexedGenres = new Dictionary(); + foreach (Genre genre in _genres) + foreach (Genre subgenre in genre.Subgenres) + { + _soundexedGenres[subgenre.Name.SoundexByWord()] = subgenre.Tag; + string reversed = string.Join(" ", subgenre.Name.Split(' ', ',').Reverse()).Trim(); + _soundexedGenres[reversed.SoundexByWord()] = subgenre.Tag; + } + + } + catch { } + + // Load gzipped authors aliases + string aliasesFileName = Path.Combine(Utils.ServiceFilesLocation, "a_aliases.txt"); + if (File.Exists(aliasesFileName)) + { + using (var stream = File.OpenRead(aliasesFileName)) + { + if (stream != null) + { + try + { + using (TextReader tr = new StreamReader(stream)) + { + string line = string.Empty; + while ((line = tr.ReadLine()) != null) + { + if (!string.IsNullOrEmpty(line)) + { + string[] parts = line.Split(new char[] { '\t', ',' }); + try + { + _aliases[string.Format("{2} {0} {1}", parts[5], parts[6], parts[7]).Trim()] = string.Format("{2} {0} {1}", parts[1], parts[2], parts[3]).Trim(); + } + catch + { + Log.WriteLine(LogLevel.Warning, "Error parsing alias '{0}'", line); + } + } + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "Error loading aliases: exception {0}", e.Message); + } + } + } + Log.WriteLine(LogLevel.Info, "Loaded {0} authors aliases from {1}", _aliases.Count, aliasesFileName); + } + else + { + using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + ".a_aliases.txt.gz")) + { + if (stream != null) + { + using (GZipStream decompress = new GZipStream(stream, CompressionMode.Decompress)) + { + using (TextReader tr = new StreamReader(decompress)) + { + string line = string.Empty; + while ((line = tr.ReadLine()) != null) + { + string[] parts = line.Split('\t'); + _aliases[string.Format("{2} {0} {1}", parts[5], parts[6], parts[7]).Trim()] = string.Format("{2} {0} {1}", parts[1], parts[2], parts[3]).Trim(); + } + } + } + } + } + } + + // Load library in separate thread (avoid UI blocking) + LoadAsync(); + } + + /// + /// Load library database in background + /// + public static void LoadAsync() + { + // Clear library and free memory + FB2Count = EPUBCount = 0; + _books.Clear(); + _paths.Clear(); + GC.Collect(); + + // Create unique database name, based on library path + LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); + string databaseFileName = Utils.CreateGuid(Utils.IsoOidNamespace, LibraryPath).ToString() + ".db"; + _databaseFullPath = Path.Combine(Utils.ServiceFilesLocation, databaseFileName); + + // Load database in the background thread + BackgroundWorker worker = new BackgroundWorker(); + worker.DoWork += (_, __) => + { + _converted = false; + Load(); + if (LibraryLoaded != null) LibraryLoaded(null, null); + if (_converted) + { + Save(); + Log.WriteLine(LogLevel.Info, "Database successfully converted to the format 1.1"); + } + worker.Dispose(); + }; + worker.RunWorkerAsync(); + } + + /// + /// Full path to the library folder + /// + public static string LibraryPath { get; set; } + + /// + /// Library changed flag (we should save!) + /// + public static bool IsChanged { get; set; } + + /// + /// + /// + /// + /// + public static bool Contains(string bookPath) + { + lock (_paths) + { + return _paths.ContainsKey(bookPath); + } + } + + /// + /// + /// + /// + /// + public static Book GetBook(string id) + { + lock (_books) + { + Book book = null; + if (_books.ContainsKey(id)) + { + book = _books[id]; + } + return book; + } + } + + /// + /// Add unique book descriptor to the library and saves the library + /// + /// + public static bool Add(Book book) + { + lock (_books) + { + // Prevent incorrect duplicates detection (same ID but different titles) + if (_books.ContainsKey(book.ID) && !book.Title.Equals(_books[book.ID].Title)) + { + book.ID = Utils.CreateGuid(Utils.IsoOidNamespace, book.FileName).ToString(); + } + + // Check for duplicates + if (!_books.ContainsKey(book.ID) || (_books.ContainsKey(book.ID) && _books[book.ID].Version < book.Version)) + { + // Remember duplicate flag + bool isDuplicate = _books.ContainsKey(book.ID); + book.AddedDate = DateTime.Now; + // Make relative path + _books[book.ID] = book; + lock (_paths) _paths[book.FileName] = book.ID; + if (!isDuplicate) + { + IsChanged = true; + if (book.BookType == BookType.FB2) FB2Count++; else EPUBCount++; + // Increase authors book count + foreach (var name in book.Authors) + if (!_authors.ContainsKey(name)) _authors[name] = 1; else _authors[name]++; + } + else + { + Log.WriteLine(LogLevel.Warning, "Replaced duplicate. File name {0}, book version {1}", book.FileName, book.Version); + } + return !isDuplicate; + } + Log.WriteLine(LogLevel.Warning, "Found duplicate. File name {0}, book version {1}", book.FileName, book.Version); + return false; + } + } + + /// + /// Delete all books with specific file path from the library + /// + /// + public static bool Delete(string fileName) + { + bool result = false; + lock (_books) + { + if (!string.IsNullOrEmpty(fileName) && fileName.Length > Library.LibraryPath.Length + 1) + { + // Extract relative file name + fileName = fileName.Substring(Library.LibraryPath.Length + 1); + string ext = Path.GetExtension(fileName.ToLower()); + + // Assume it's a single file + if (ext.Equals(".epub") || ext.Equals(".fb2") || (ext.Equals(".zip") && fileName.ToLower().Contains(".fb2.zip"))) + { + if (Contains(fileName)) + { + Book book = _books[_paths[fileName]]; + if (book != null) + { + _books.Remove(book.ID); + _paths.Remove(book.FileName); + if (book.BookType == BookType.FB2) FB2Count--; else EPUBCount--; + // Decrease authors book count + foreach (var name in book.Authors) + if (_authors.ContainsKey(name)) _authors[name]--; + result = IsChanged = true; + } + } + } + // removed object should be a zip archive; we have to remove all books contained in this archive + else + { + List booksForRemove = _books.Where(b => b.Value.FileName.StartsWith(fileName+"@")).Select(b => b.Value).ToList(); + foreach (Book book in booksForRemove) + { + _books.Remove(book.ID); + _paths.Remove(book.FileName); + if (book.BookType == BookType.FB2) FB2Count--; else EPUBCount--; + // Decrease authors book count + foreach (var name in book.Authors) + if (_authors.ContainsKey(name)) _authors[name]--; + } + if (booksForRemove.Count > 0) + { + result = IsChanged = true; + } + } + } + } + return result; + } + + /// + /// Total number of books in library + /// + public static int Count + { + get + { + lock (_books) return _books.Count; + } + } + + /// + /// New books count + /// + /// + public static int NewBooksCount + { + get + { + lock (_books) return _books.Values.Where(b => DateTime.Now.Subtract(b.AddedDate) <= _periods[TinyOPDS.Properties.Settings.Default.NewBooksPeriod]).Count(); + } + } + + /// + /// Returns FB2 books count + /// + public static int FB2Count { get; private set; } + + /// + /// Returns EPUB books count + /// + public static int EPUBCount { get; private set; } + + /// + /// Returns list of the books titles sorted in alphabetical order + /// + public static List Titles + { + get + { + lock (_books) + { + return _books.Values.Select(b => b.Title).Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList(); + } + } + } + + /// + /// Returns list of the authors sorted in alphabetical order + /// + public static List Authors + { + get + { + lock (_books) + { + return ((_books.Values.SelectMany(b => b.Authors)).ToList()).Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).Where(с => с.Length > 1).ToList(); + } + } + } + + /// + /// Returns list of the library books series sorted in alphabetical order + /// + public static List Sequences + { + get + { + lock (_books) + { + return ((_books.Values.Select(b => b.Sequence)).ToList()).Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).Where(с => с.Length > 1).ToList(); + } + } + } + + /// + /// All genres supported by fb2 format + /// + public static List FB2Genres + { + get + { + return _genres; + } + } + + public static Dictionary SoundexedGenres + { + get + { + return _soundexedGenres; + } + } + + /// + /// Returns sorted in alphabetical order list of library books genres + /// + public static List Genres + { + get + { + lock (_books) + { + var libGenres = _books.Values.SelectMany(b => b.Genres).ToList().Distinct().OrderBy(a => a, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).Where(с => с.Length > 1).Select(g => g.ToLower().Trim()).ToList(); + return _genres.SelectMany(g => g.Subgenres).Where(sg => libGenres.Contains(sg.Tag) || libGenres.Contains(sg.Name.ToLower()) || libGenres.Contains(sg.Translation.ToLower())).ToList(); + } + } + } + + /// + /// Return list of authors by name + /// + /// + /// + public static List GetAuthorsByName(string name, bool isOpenSearch) + { + List authors = new List(); + lock (_books) + { + if (isOpenSearch) authors = Authors.Where(a => a.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); + else authors = Authors.Where(a => a.StartsWith(name, StringComparison.OrdinalIgnoreCase)).ToList(); + if (isOpenSearch && authors.Count == 0) + { + string reversedName = name.Reverse(); + authors = Authors.Where(a => a.IndexOf(reversedName, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); + } + return authors; + } + } + + /// + /// Return list of books by title + /// + /// + /// + public static List GetBooksByTitle(string title) + { + lock (_books) return _books.Values.Where(b => b.Title.IndexOf(title, StringComparison.OrdinalIgnoreCase) >= 0 || b.Sequence.IndexOf(title, StringComparison.OrdinalIgnoreCase) >= 0).ToList(); + } + + /// + /// Return list of books by selected author(s) + /// + /// + /// + public static List GetBooksByAuthor(string author) + { + lock (_books) return _books.Values.Where(b => b.Authors.Contains(author)).ToList(); + } + + /// + /// Return number of books by specific author + /// + /// + /// + public static int GetBooksByAuthorCount(string author) + { + return _authors.ContainsKey(author) ? _authors[author] : 0; + } + + /// + /// Return list of books by selected sequence + /// + /// + /// + public static List GetBooksBySequence(string sequence) + { + lock (_books) return _books.Values.Where(b => b.Sequence.Contains(sequence)).ToList(); + } + + /// + /// Return list of books by selected genre + /// + /// + /// + public static List GetBooksByGenre(string genre) + { + lock (_books) return _books.Values.Where(b => b.Genres.Contains(genre)).ToList(); + } + + /// + /// Return list of new books + /// + public static List NewBooks + { + get + { + TimeSpan period = _periods[TinyOPDS.Properties.Settings.Default.NewBooksPeriod]; + lock (_books) return _books.Values.Where(b => DateTime.Now.Subtract(b.AddedDate) <= period).ToList(); + } + } + + #region Serialization and deserialization + + /// + /// Load library + /// + public static void Load() + { + int numRecords = 0; + DateTime start = DateTime.Now; + + // MemoryStream can save us about 1 second on 106 Mb database load time + MemoryStream memStream = null; + if (File.Exists(_databaseFullPath)) + { + _books.Clear(); + _authors.Clear(); + FB2Count = EPUBCount = 0; + memStream = new MemoryStream(); + + try + { + using (Stream fileStream = new FileStream(_databaseFullPath, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + fileStream.CopyTo(memStream); + } + memStream.Position = 0; + using (BinaryReader reader = new BinaryReader(memStream)) + { + bool newFormat = reader.ReadString().Equals("VER1.1"); + if (!newFormat) + { + reader.BaseStream.Position = 0; + _converted = true; + } + + DateTime now = DateTime.Now; + bool lowMemory = TinyOPDS.Properties.Settings.Default.LowMemoryProfile; + bool useAliases = TinyOPDS.Properties.Settings.Default.UseAuthorsAliases; + + while (reader.BaseStream.Position < reader.BaseStream.Length) + { + try + { + string fileName = reader.ReadString(); + Book book = new Book(Path.Combine(LibraryPath, fileName)); + string id = reader.ReadString(); + id = id.Replace("{", "").Replace("}", ""); + book.ID = id; + book.Version = reader.ReadSingle(); + book.Title = reader.ReadString(); + book.Language = reader.ReadString(); + book.HasCover = reader.ReadBoolean(); + book.BookDate = DateTime.FromBinary(reader.ReadInt64()); + book.DocumentDate = DateTime.FromBinary(reader.ReadInt64()); + book.Sequence = reader.ReadString(); + book.NumberInSequence = reader.ReadUInt32(); + if (lowMemory) { var s = reader.ReadString(); } else book.Annotation = reader.ReadString(); + book.DocumentSize = reader.ReadUInt32(); + int count = reader.ReadInt32(); + for (int i = 0; i < count; i++) + { + string a = reader.ReadString(); + // Replace author's alias (if any) by name + string name = (useAliases && _aliases.ContainsKey(a)) ? _aliases[a] : a; + book.Authors.Add(name); + if (!_authors.ContainsKey(name)) _authors[name] = 1; else _authors[name]++; + } + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) book.Translators.Add(reader.ReadString()); + count = reader.ReadInt32(); + for (int i = 0; i < count; i++) book.Genres.Add(reader.ReadString()); + lock (_books) _books[book.ID] = book; + lock (_paths) _paths[book.FileName] = book.ID; + book.AddedDate = newFormat ? DateTime.FromBinary(reader.ReadInt64()) : now; + if (book.BookType == BookType.FB2) FB2Count++; else EPUBCount++; + + numRecords++; + } + catch (EndOfStreamException) + { + break; + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, e.Message); + break; + } + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "Load books exception {0}", e.Message); + } + finally + { + if (memStream != null) + { + memStream.Dispose(); + memStream = null; + } + // Call garbage collector now + GC.Collect(); + IsChanged = false; + } + } + + Log.WriteLine(LogLevel.Info, "Database load time = {0}, {1} book records loaded", DateTime.Now.Subtract(start), numRecords); + } + + /// + /// Save whole library + /// + /// Remark: new database format is used! + public static void Save() + { + // Do nothing if we have no records + if (_books.Count == 0) return; + + int numRecords = 0; + DateTime start = DateTime.Now; + Dictionary shallowCopy = null; + + Stream fileStream = null; + try + { + fileStream = new FileStream(_databaseFullPath, FileMode.Create, FileAccess.Write, FileShare.Write); + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + fileStream = null; + writer.Write("VER1.1"); + + // Create shallow copy (to prevent exception on dictionary modifications during foreach loop) + lock (_books) shallowCopy = new Dictionary(_books); + foreach (Book book in shallowCopy.Values) + { + writeBook(book, writer); + numRecords++; + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "Save books exception {0}", e.Message); + } + finally + { + shallowCopy = null; + if (fileStream != null) fileStream.Dispose(); + IsChanged = false; + Log.WriteLine(LogLevel.Info, "Database save time = {0}, {1} book records written to disk", DateTime.Now.Subtract(start), numRecords); + } + } + + /// + /// Append one book descriptor to the library file + /// + /// + public static void Append(Book book) + { + Stream fileStream = null; + try + { + fileStream = new FileStream(_databaseFullPath, FileMode.Append, FileAccess.Write, FileShare.Write); + using (BinaryWriter writer = new BinaryWriter(fileStream)) + { + fileStream = null; + writeBook(book, writer); + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "Can't append book {0}, exception {1}", book.FilePath, e.Message); + } + finally + { + if (fileStream != null) + { + fileStream.Dispose(); + } + IsChanged = false; + } + } + + private static void writeBook(Book book, BinaryWriter writer) + { + writer.Write(book.FileName); + writer.Write(book.ID); + writer.Write(book.Version); + writer.Write(book.Title); + writer.Write(book.Language); + writer.Write(book.HasCover); + writer.Write(book.BookDate.ToBinary()); + writer.Write(book.DocumentDate.ToBinary()); + writer.Write(book.Sequence); + writer.Write(book.NumberInSequence); + writer.Write(book.Annotation); + writer.Write(book.DocumentSize); + writer.Write((Int32)book.Authors.Count); + foreach (string author in book.Authors) writer.Write(author); + writer.Write((Int32)book.Translators.Count); + foreach (string translator in book.Translators) writer.Write(translator); + writer.Write((Int32)book.Genres.Count); + foreach (string genre in book.Genres) writer.Write(genre); + writer.Write(book.AddedDate.ToBinary()); + } + + #endregion + } +} diff --git a/trunk/TinyOPDS/Icons/TinyOPDS.ico b/TinyOPDS/Icons/TinyOPDS.ico similarity index 100% rename from trunk/TinyOPDS/Icons/TinyOPDS.ico rename to TinyOPDS/Icons/TinyOPDS.ico diff --git a/trunk/TinyOPDS/Icons/authors.ico b/TinyOPDS/Icons/authors.ico similarity index 100% rename from trunk/TinyOPDS/Icons/authors.ico rename to TinyOPDS/Icons/authors.ico diff --git a/trunk/TinyOPDS/Icons/authors.png b/TinyOPDS/Icons/authors.png similarity index 100% rename from trunk/TinyOPDS/Icons/authors.png rename to TinyOPDS/Icons/authors.png diff --git a/trunk/TinyOPDS/Icons/book.png b/TinyOPDS/Icons/book.png similarity index 100% rename from trunk/TinyOPDS/Icons/book.png rename to TinyOPDS/Icons/book.png diff --git a/trunk/TinyOPDS/Icons/book2.png b/TinyOPDS/Icons/book2.png similarity index 100% rename from trunk/TinyOPDS/Icons/book2.png rename to TinyOPDS/Icons/book2.png diff --git a/trunk/TinyOPDS/Icons/book3.png b/TinyOPDS/Icons/book3.png similarity index 100% rename from trunk/TinyOPDS/Icons/book3.png rename to TinyOPDS/Icons/book3.png diff --git a/trunk/TinyOPDS/Icons/books.ico b/TinyOPDS/Icons/books.ico similarity index 100% rename from trunk/TinyOPDS/Icons/books.ico rename to TinyOPDS/Icons/books.ico diff --git a/trunk/TinyOPDS/Icons/books.png b/TinyOPDS/Icons/books.png similarity index 100% rename from trunk/TinyOPDS/Icons/books.png rename to TinyOPDS/Icons/books.png diff --git a/trunk/TinyOPDS/Icons/favicon.ico b/TinyOPDS/Icons/favicon.ico similarity index 100% rename from trunk/TinyOPDS/Icons/favicon.ico rename to TinyOPDS/Icons/favicon.ico diff --git a/trunk/TinyOPDS/Icons/folder.png b/TinyOPDS/Icons/folder.png similarity index 100% rename from trunk/TinyOPDS/Icons/folder.png rename to TinyOPDS/Icons/folder.png diff --git a/trunk/TinyOPDS/Icons/genres.ico b/TinyOPDS/Icons/genres.ico similarity index 100% rename from trunk/TinyOPDS/Icons/genres.ico rename to TinyOPDS/Icons/genres.ico diff --git a/trunk/TinyOPDS/Icons/genres.png b/TinyOPDS/Icons/genres.png similarity index 100% rename from trunk/TinyOPDS/Icons/genres.png rename to TinyOPDS/Icons/genres.png diff --git a/trunk/TinyOPDS/Icons/genres2.png b/TinyOPDS/Icons/genres2.png similarity index 100% rename from trunk/TinyOPDS/Icons/genres2.png rename to TinyOPDS/Icons/genres2.png diff --git a/trunk/TinyOPDS/Icons/lib.png b/TinyOPDS/Icons/lib.png similarity index 100% rename from trunk/TinyOPDS/Icons/lib.png rename to TinyOPDS/Icons/lib.png diff --git a/trunk/TinyOPDS/Libs/FB2Library.dll b/TinyOPDS/Libs/FB2Library.dll similarity index 100% rename from trunk/TinyOPDS/Libs/FB2Library.dll rename to TinyOPDS/Libs/FB2Library.dll diff --git a/trunk/TinyOPDS/Libs/FB2Library.dll.gz b/TinyOPDS/Libs/FB2Library.dll.gz similarity index 100% rename from trunk/TinyOPDS/Libs/FB2Library.dll.gz rename to TinyOPDS/Libs/FB2Library.dll.gz diff --git a/trunk/TinyOPDS/Libs/Ionic.Zip.Reduced.dll b/TinyOPDS/Libs/Ionic.Zip.Reduced.dll similarity index 100% rename from trunk/TinyOPDS/Libs/Ionic.Zip.Reduced.dll rename to TinyOPDS/Libs/Ionic.Zip.Reduced.dll diff --git a/trunk/TinyOPDS/Libs/Ionic.Zip.Reduced.dll.gz b/TinyOPDS/Libs/Ionic.Zip.Reduced.dll.gz similarity index 100% rename from trunk/TinyOPDS/Libs/Ionic.Zip.Reduced.dll.gz rename to TinyOPDS/Libs/Ionic.Zip.Reduced.dll.gz diff --git a/trunk/TinyOPDS/Libs/eBdb.EpubReader.dll b/TinyOPDS/Libs/eBdb.EpubReader.dll similarity index 100% rename from trunk/TinyOPDS/Libs/eBdb.EpubReader.dll rename to TinyOPDS/Libs/eBdb.EpubReader.dll diff --git a/trunk/TinyOPDS/Libs/eBdb.EpubReader.dll.gz b/TinyOPDS/Libs/eBdb.EpubReader.dll.gz similarity index 100% rename from trunk/TinyOPDS/Libs/eBdb.EpubReader.dll.gz rename to TinyOPDS/Libs/eBdb.EpubReader.dll.gz diff --git a/trunk/TinyOPDS/Libs/gzip.cs b/TinyOPDS/Libs/gzip.cs similarity index 97% rename from trunk/TinyOPDS/Libs/gzip.cs rename to TinyOPDS/Libs/gzip.cs index 9e99965..bf6f56d 100644 --- a/trunk/TinyOPDS/Libs/gzip.cs +++ b/TinyOPDS/Libs/gzip.cs @@ -1,64 +1,64 @@ -using System; -using System.IO; -using System.IO.Compression; - -namespace zip -{ - public class Program - { - public static void Main(string[] args) - { - if (args.Length < 1) - { - Console.WriteLine("Usage: gzip [-d] file"); - } - else if (args[0] != "-d") Compress(new FileInfo(args[0])); else Decompress(new FileInfo(args[1])); - } - - public static void Compress(FileInfo fi) - { - // Get the stream of the source file. - using (FileStream inFile = fi.OpenRead()) - { - // Prevent compressing hidden and - // already compressed files. - if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden & fi.Extension != ".gz") - { - // Create the compressed file - using (FileStream outFile = File.Create(fi.FullName + ".gz")) - { - using (GZipStream Compress = new GZipStream(outFile, CompressionMode.Compress)) - { - // Copy the source file into the compression stream. - inFile.CopyTo(Compress); - Console.WriteLine("Compressed {0} from {1} to {2} bytes.", - fi.Name, fi.Length.ToString(), outFile.Length.ToString()); - } - } - } - } - } - - public static void Decompress(FileInfo fi) - { - // Get the stream of the source file. - using (FileStream inFile = fi.OpenRead()) - { - // Get original file extension - string curFile = fi.FullName; - string origName = curFile.Remove(curFile.Length - fi.Extension.Length); - - //Create the decompressed file. - using (FileStream outFile = File.Create(origName)) - { - using (GZipStream Decompress = new GZipStream(inFile, CompressionMode.Decompress)) - { - // Copy the decompression stream into the output file. - Decompress.CopyTo(outFile); - Console.WriteLine("Decompressed: {0}", fi.Name); - } - } - } - } - } +using System; +using System.IO; +using System.IO.Compression; + +namespace zip +{ + public class Program + { + public static void Main(string[] args) + { + if (args.Length < 1) + { + Console.WriteLine("Usage: gzip [-d] file"); + } + else if (args[0] != "-d") Compress(new FileInfo(args[0])); else Decompress(new FileInfo(args[1])); + } + + public static void Compress(FileInfo fi) + { + // Get the stream of the source file. + using (FileStream inFile = fi.OpenRead()) + { + // Prevent compressing hidden and + // already compressed files. + if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden) != FileAttributes.Hidden & fi.Extension != ".gz") + { + // Create the compressed file + using (FileStream outFile = File.Create(fi.FullName + ".gz")) + { + using (GZipStream Compress = new GZipStream(outFile, CompressionMode.Compress)) + { + // Copy the source file into the compression stream. + inFile.CopyTo(Compress); + Console.WriteLine("Compressed {0} from {1} to {2} bytes.", + fi.Name, fi.Length.ToString(), outFile.Length.ToString()); + } + } + } + } + } + + public static void Decompress(FileInfo fi) + { + // Get the stream of the source file. + using (FileStream inFile = fi.OpenRead()) + { + // Get original file extension + string curFile = fi.FullName; + string origName = curFile.Remove(curFile.Length - fi.Extension.Length); + + //Create the decompressed file. + using (FileStream outFile = File.Create(origName)) + { + using (GZipStream Decompress = new GZipStream(inFile, CompressionMode.Decompress)) + { + // Copy the decompression stream into the output file. + Decompress.CopyTo(outFile); + Console.WriteLine("Decompressed: {0}", fi.Name); + } + } + } + } + } } \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/gzip.exe b/TinyOPDS/Libs/gzip.exe similarity index 100% rename from trunk/TinyOPDS/Libs/gzip.exe rename to TinyOPDS/Libs/gzip.exe diff --git a/trunk/TinyOPDS/License.txt b/TinyOPDS/License.txt similarity index 98% rename from trunk/TinyOPDS/License.txt rename to TinyOPDS/License.txt index 73d6051..0fe812f 100644 --- a/trunk/TinyOPDS/License.txt +++ b/TinyOPDS/License.txt @@ -1,29 +1,29 @@ -Microsoft Public License (Ms-PL) - -This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. - -1. Definitions - -The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. - -A "contribution" is the original software, or any additions or changes to the software. - -A "contributor" is any person that distributes its contribution under this license. - -"Licensed patents" are a contributor's patent claims that read directly on its contribution. - -2. Grant of Rights - -(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. - -(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. - -3. Conditions and Limitations - -(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. - -(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. - -(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. - -(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the software. + +A "contributor" is any person that distributes its contribution under this license. + +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. diff --git a/trunk/TinyOPDS/MainForm.Designer.cs b/TinyOPDS/MainForm.Designer.cs similarity index 98% rename from trunk/TinyOPDS/MainForm.Designer.cs rename to TinyOPDS/MainForm.Designer.cs index 2d25bda..9767def 100644 --- a/trunk/TinyOPDS/MainForm.Designer.cs +++ b/TinyOPDS/MainForm.Designer.cs @@ -1,1702 +1,1702 @@ -namespace TinyOPDS -{ - partial class MainForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - if (_watcher != null) _watcher.Dispose(); - if (_upnpController != null) _upnpController.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.tabControl1 = new System.Windows.Forms.TabControl(); - this.tabPage1 = new System.Windows.Forms.TabPage(); - this.databaseFileName = new System.Windows.Forms.TextBox(); - this.label21 = new System.Windows.Forms.Label(); - this.useWatcher = new System.Windows.Forms.CheckBox(); - this.duplicates = new System.Windows.Forms.Label(); - this.label16 = new System.Windows.Forms.Label(); - this.label15 = new System.Windows.Forms.Label(); - this.status = new System.Windows.Forms.Label(); - this.label14 = new System.Windows.Forms.Label(); - this.rate = new System.Windows.Forms.Label(); - this.label12 = new System.Windows.Forms.Label(); - this.elapsedTime = new System.Windows.Forms.Label(); - this.label10 = new System.Windows.Forms.Label(); - this.startTime = new System.Windows.Forms.Label(); - this.label6 = new System.Windows.Forms.Label(); - this.booksProcessed = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.invalidBooks = new System.Windows.Forms.Label(); - this.label9 = new System.Windows.Forms.Label(); - this.skippedBooks = new System.Windows.Forms.Label(); - this.label7 = new System.Windows.Forms.Label(); - this.booksFound = new System.Windows.Forms.Label(); - this.booksInDB = new System.Windows.Forms.Label(); - this.folderButton = new System.Windows.Forms.Button(); - this.label2 = new System.Windows.Forms.Label(); - this.label1 = new System.Windows.Forms.Label(); - this.scannerButton = new System.Windows.Forms.Button(); - this.libraryPath = new System.Windows.Forms.TextBox(); - this.tabPage2 = new System.Windows.Forms.TabPage(); - this.useAbsoluteUri = new System.Windows.Forms.CheckBox(); - this.extWebLink = new System.Windows.Forms.LinkLabel(); - this.intWebLink = new System.Windows.Forms.LinkLabel(); - this.label34 = new System.Windows.Forms.Label(); - this.label35 = new System.Windows.Forms.Label(); - this.label33 = new System.Windows.Forms.Label(); - this.interfaceCombo = new System.Windows.Forms.ComboBox(); - this.label29 = new System.Windows.Forms.Label(); - this.statUniqueClients = new System.Windows.Forms.Label(); - this.label26 = new System.Windows.Forms.Label(); - this.statImages = new System.Windows.Forms.Label(); - this.label27 = new System.Windows.Forms.Label(); - this.statBooks = new System.Windows.Forms.Label(); - this.label25 = new System.Windows.Forms.Label(); - this.statRequests = new System.Windows.Forms.Label(); - this.label23 = new System.Windows.Forms.Label(); - this.extLink = new System.Windows.Forms.LinkLabel(); - this.intLink = new System.Windows.Forms.LinkLabel(); - this.label13 = new System.Windows.Forms.Label(); - this.extIPlabel = new System.Windows.Forms.Label(); - this.intIPlabel = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.serverButton = new System.Windows.Forms.Button(); - this.webPrefix = new System.Windows.Forms.TextBox(); - this.openPort = new System.Windows.Forms.CheckBox(); - this.serverPort = new System.Windows.Forms.TextBox(); - this.useUPnP = new System.Windows.Forms.CheckBox(); - this.rootPrefix = new System.Windows.Forms.TextBox(); - this.serverName = new System.Windows.Forms.TextBox(); - this.tabPage6 = new System.Windows.Forms.TabPage(); - this.checkBox2 = new System.Windows.Forms.CheckBox(); - this.checkBox1 = new System.Windows.Forms.CheckBox(); - this.newBooksPeriodCombo = new System.Windows.Forms.ComboBox(); - this.label39 = new System.Windows.Forms.Label(); - this.sortOrderCombo = new System.Windows.Forms.ComboBox(); - this.label38 = new System.Windows.Forms.Label(); - this.itemsPerWeb = new System.Windows.Forms.NumericUpDown(); - this.label37 = new System.Windows.Forms.Label(); - this.itemsPerOPDS = new System.Windows.Forms.NumericUpDown(); - this.label36 = new System.Windows.Forms.Label(); - this.tabPage5 = new System.Windows.Forms.TabPage(); - this.statBannedClients = new System.Windows.Forms.Label(); - this.label31 = new System.Windows.Forms.Label(); - this.label24 = new System.Windows.Forms.Label(); - this.statWrongLogins = new System.Windows.Forms.Label(); - this.label30 = new System.Windows.Forms.Label(); - this.statGoodLogins = new System.Windows.Forms.Label(); - this.label28 = new System.Windows.Forms.Label(); - this.dataGridView1 = new System.Windows.Forms.DataGridView(); - this.wrongAttemptsCount = new System.Windows.Forms.NumericUpDown(); - this.banClients = new System.Windows.Forms.CheckBox(); - this.rememberClients = new System.Windows.Forms.CheckBox(); - this.useHTTPAuth = new System.Windows.Forms.CheckBox(); - this.tabPage3 = new System.Windows.Forms.TabPage(); - this.viewLogFile = new System.Windows.Forms.Button(); - this.label32 = new System.Windows.Forms.Label(); - this.updateCombo = new System.Windows.Forms.ComboBox(); - this.label22 = new System.Windows.Forms.Label(); - this.logVerbosity = new System.Windows.Forms.ComboBox(); - this.converterLinkLabel = new System.Windows.Forms.LinkLabel(); - this.label11 = new System.Windows.Forms.Label(); - this.langCombo = new System.Windows.Forms.ComboBox(); - this.label8 = new System.Windows.Forms.Label(); - this.convertorFolder = new System.Windows.Forms.Button(); - this.convertorPath = new System.Windows.Forms.TextBox(); - this.saveLog = new System.Windows.Forms.CheckBox(); - this.closeToTray = new System.Windows.Forms.CheckBox(); - this.startMinimized = new System.Windows.Forms.CheckBox(); - this.startWithWindows = new System.Windows.Forms.CheckBox(); - this.tabPage4 = new System.Windows.Forms.TabPage(); - this.linkLabel6 = new System.Windows.Forms.LinkLabel(); - this.linkLabel5 = new System.Windows.Forms.LinkLabel(); - this.linkLabel4 = new System.Windows.Forms.LinkLabel(); - this.linkLabel3 = new System.Windows.Forms.LinkLabel(); - this.label20 = new System.Windows.Forms.Label(); - this.label19 = new System.Windows.Forms.Label(); - this.linkLabel2 = new System.Windows.Forms.LinkLabel(); - this.label18 = new System.Windows.Forms.Label(); - this.linkLabel1 = new System.Windows.Forms.LinkLabel(); - this.label17 = new System.Windows.Forms.Label(); - this.appVersion = new System.Windows.Forms.Label(); - this.appName = new System.Windows.Forms.Label(); - this.pictureBox1 = new System.Windows.Forms.PictureBox(); - this.donateButton = new System.Windows.Forms.Button(); - this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); - this.windowMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.serverMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); - this.exitMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.tabControl1.SuspendLayout(); - this.tabPage1.SuspendLayout(); - this.tabPage2.SuspendLayout(); - this.tabPage6.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.itemsPerWeb)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.itemsPerOPDS)).BeginInit(); - this.tabPage5.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.wrongAttemptsCount)).BeginInit(); - this.tabPage3.SuspendLayout(); - this.tabPage4.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); - this.contextMenuStrip.SuspendLayout(); - this.SuspendLayout(); - // - // tabControl1 - // - this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.tabControl1.Controls.Add(this.tabPage1); - this.tabControl1.Controls.Add(this.tabPage2); - this.tabControl1.Controls.Add(this.tabPage6); - this.tabControl1.Controls.Add(this.tabPage5); - this.tabControl1.Controls.Add(this.tabPage3); - this.tabControl1.Controls.Add(this.tabPage4); - this.tabControl1.ItemSize = new System.Drawing.Size(91, 30); - this.tabControl1.Location = new System.Drawing.Point(-3, -1); - this.tabControl1.Name = "tabControl1"; - this.tabControl1.SelectedIndex = 0; - this.tabControl1.Size = new System.Drawing.Size(481, 346); - this.tabControl1.TabIndex = 8; - // - // tabPage1 - // - this.tabPage1.BackColor = System.Drawing.SystemColors.Control; - this.tabPage1.Controls.Add(this.databaseFileName); - this.tabPage1.Controls.Add(this.label21); - this.tabPage1.Controls.Add(this.useWatcher); - this.tabPage1.Controls.Add(this.duplicates); - this.tabPage1.Controls.Add(this.label16); - this.tabPage1.Controls.Add(this.label15); - this.tabPage1.Controls.Add(this.status); - this.tabPage1.Controls.Add(this.label14); - this.tabPage1.Controls.Add(this.rate); - this.tabPage1.Controls.Add(this.label12); - this.tabPage1.Controls.Add(this.elapsedTime); - this.tabPage1.Controls.Add(this.label10); - this.tabPage1.Controls.Add(this.startTime); - this.tabPage1.Controls.Add(this.label6); - this.tabPage1.Controls.Add(this.booksProcessed); - this.tabPage1.Controls.Add(this.label5); - this.tabPage1.Controls.Add(this.invalidBooks); - this.tabPage1.Controls.Add(this.label9); - this.tabPage1.Controls.Add(this.skippedBooks); - this.tabPage1.Controls.Add(this.label7); - this.tabPage1.Controls.Add(this.booksFound); - this.tabPage1.Controls.Add(this.booksInDB); - this.tabPage1.Controls.Add(this.folderButton); - this.tabPage1.Controls.Add(this.label2); - this.tabPage1.Controls.Add(this.label1); - this.tabPage1.Controls.Add(this.scannerButton); - this.tabPage1.Controls.Add(this.libraryPath); - this.tabPage1.Location = new System.Drawing.Point(4, 34); - this.tabPage1.Name = "tabPage1"; - this.tabPage1.Padding = new System.Windows.Forms.Padding(3); - this.tabPage1.Size = new System.Drawing.Size(473, 308); - this.tabPage1.TabIndex = 0; - this.tabPage1.Text = "Scanner"; - // - // databaseFileName - // - this.databaseFileName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.databaseFileName.Location = new System.Drawing.Point(125, 66); - this.databaseFileName.Name = "databaseFileName"; - this.databaseFileName.ReadOnly = true; - this.databaseFileName.Size = new System.Drawing.Size(336, 20); - this.databaseFileName.TabIndex = 32; - // - // label21 - // - this.label21.AutoSize = true; - this.label21.Location = new System.Drawing.Point(15, 70); - this.label21.Name = "label21"; - this.label21.Size = new System.Drawing.Size(104, 13); - this.label21.TabIndex = 31; - this.label21.Text = "Database file name: "; - // - // useWatcher - // - this.useWatcher.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.useWatcher.AutoSize = true; - this.useWatcher.Checked = global::TinyOPDS.Properties.Settings.Default.WatchLibrary; - this.useWatcher.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "WatchLibrary", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.useWatcher.Location = new System.Drawing.Point(321, 34); - this.useWatcher.Name = "useWatcher"; - this.useWatcher.Size = new System.Drawing.Size(135, 17); - this.useWatcher.TabIndex = 30; - this.useWatcher.Text = "Monitor library changes"; - this.useWatcher.UseVisualStyleBackColor = true; - this.useWatcher.CheckedChanged += new System.EventHandler(this.useWatcher_CheckedChanged); - // - // duplicates - // - this.duplicates.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.duplicates.AutoSize = true; - this.duplicates.Location = new System.Drawing.Point(122, 228); - this.duplicates.MinimumSize = new System.Drawing.Size(50, 0); - this.duplicates.Name = "duplicates"; - this.duplicates.Size = new System.Drawing.Size(50, 13); - this.duplicates.TabIndex = 29; - this.duplicates.Text = "0"; - // - // label16 - // - this.label16.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label16.AutoSize = true; - this.label16.Location = new System.Drawing.Point(15, 228); - this.label16.Name = "label16"; - this.label16.Size = new System.Drawing.Size(60, 13); - this.label16.TabIndex = 28; - this.label16.Text = "Duplicates:"; - // - // label15 - // - this.label15.AutoSize = true; - this.label15.Location = new System.Drawing.Point(14, 16); - this.label15.Name = "label15"; - this.label15.Size = new System.Drawing.Size(105, 13); - this.label15.TabIndex = 27; - this.label15.Text = "Path to books folder:"; - // - // status - // - this.status.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.status.AutoSize = true; - this.status.Location = new System.Drawing.Point(360, 228); - this.status.MinimumSize = new System.Drawing.Size(50, 0); - this.status.Name = "status"; - this.status.Size = new System.Drawing.Size(58, 13); - this.status.TabIndex = 26; - this.status.Text = "STOPPED"; - // - // label14 - // - this.label14.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label14.AutoSize = true; - this.label14.Location = new System.Drawing.Point(253, 228); - this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(40, 13); - this.label14.TabIndex = 25; - this.label14.Text = "Status:"; - // - // rate - // - this.rate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.rate.AutoSize = true; - this.rate.Location = new System.Drawing.Point(360, 202); - this.rate.MinimumSize = new System.Drawing.Size(50, 0); - this.rate.Name = "rate"; - this.rate.Size = new System.Drawing.Size(66, 13); - this.rate.TabIndex = 24; - this.rate.Text = "0 books/min"; - // - // label12 - // - this.label12.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label12.AutoSize = true; - this.label12.Location = new System.Drawing.Point(253, 202); - this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(33, 13); - this.label12.TabIndex = 23; - this.label12.Text = "Rate:"; - // - // elapsedTime - // - this.elapsedTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.elapsedTime.AutoSize = true; - this.elapsedTime.Location = new System.Drawing.Point(360, 176); - this.elapsedTime.MinimumSize = new System.Drawing.Size(50, 0); - this.elapsedTime.Name = "elapsedTime"; - this.elapsedTime.Size = new System.Drawing.Size(50, 13); - this.elapsedTime.TabIndex = 22; - this.elapsedTime.Text = "00:00:00"; - // - // label10 - // - this.label10.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label10.AutoSize = true; - this.label10.Location = new System.Drawing.Point(253, 176); - this.label10.Name = "label10"; - this.label10.Size = new System.Drawing.Size(70, 13); - this.label10.TabIndex = 21; - this.label10.Text = "Elapsed time:"; - // - // startTime - // - this.startTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.startTime.AutoSize = true; - this.startTime.Location = new System.Drawing.Point(360, 150); - this.startTime.MinimumSize = new System.Drawing.Size(50, 0); - this.startTime.Name = "startTime"; - this.startTime.Size = new System.Drawing.Size(50, 13); - this.startTime.TabIndex = 20; - this.startTime.Text = "00:00:00"; - // - // label6 - // - this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(253, 150); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(54, 13); - this.label6.TabIndex = 19; - this.label6.Text = "Start time:"; - // - // booksProcessed - // - this.booksProcessed.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.booksProcessed.AutoSize = true; - this.booksProcessed.Location = new System.Drawing.Point(123, 254); - this.booksProcessed.MinimumSize = new System.Drawing.Size(50, 0); - this.booksProcessed.Name = "booksProcessed"; - this.booksProcessed.Size = new System.Drawing.Size(50, 13); - this.booksProcessed.TabIndex = 18; - this.booksProcessed.Text = "0"; - // - // label5 - // - this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(15, 254); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(92, 13); - this.label5.TabIndex = 17; - this.label5.Text = "Books processed:"; - // - // invalidBooks - // - this.invalidBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.invalidBooks.AutoSize = true; - this.invalidBooks.Location = new System.Drawing.Point(123, 176); - this.invalidBooks.MinimumSize = new System.Drawing.Size(50, 0); - this.invalidBooks.Name = "invalidBooks"; - this.invalidBooks.Size = new System.Drawing.Size(50, 13); - this.invalidBooks.TabIndex = 16; - this.invalidBooks.Text = "0"; - // - // label9 - // - this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(15, 176); - this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(73, 13); - this.label9.TabIndex = 15; - this.label9.Text = "Invalid books:"; - // - // skippedBooks - // - this.skippedBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.skippedBooks.AutoSize = true; - this.skippedBooks.Location = new System.Drawing.Point(123, 202); - this.skippedBooks.MinimumSize = new System.Drawing.Size(50, 0); - this.skippedBooks.Name = "skippedBooks"; - this.skippedBooks.Size = new System.Drawing.Size(50, 13); - this.skippedBooks.TabIndex = 14; - this.skippedBooks.Text = "0"; - // - // label7 - // - this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(15, 202); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(81, 13); - this.label7.TabIndex = 13; - this.label7.Text = "Skipped books:"; - // - // booksFound - // - this.booksFound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.booksFound.AutoSize = true; - this.booksFound.Location = new System.Drawing.Point(123, 150); - this.booksFound.MinimumSize = new System.Drawing.Size(50, 0); - this.booksFound.Name = "booksFound"; - this.booksFound.Size = new System.Drawing.Size(79, 13); - this.booksFound.TabIndex = 12; - this.booksFound.Text = "fb2: 0 epub: 0"; - // - // booksInDB - // - this.booksInDB.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.booksInDB.AutoSize = true; - this.booksInDB.Location = new System.Drawing.Point(123, 111); - this.booksInDB.MinimumSize = new System.Drawing.Size(50, 0); - this.booksInDB.Name = "booksInDB"; - this.booksInDB.Size = new System.Drawing.Size(127, 13); - this.booksInDB.TabIndex = 11; - this.booksInDB.Text = "0 fb2: 0 epub: 0"; - // - // folderButton - // - this.folderButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.folderButton.Image = global::TinyOPDS.Properties.Resources.folder; - this.folderButton.Location = new System.Drawing.Point(281, 30); - this.folderButton.Name = "folderButton"; - this.folderButton.Size = new System.Drawing.Size(29, 23); - this.folderButton.TabIndex = 10; - this.folderButton.UseVisualStyleBackColor = true; - this.folderButton.Click += new System.EventHandler(this.folderButton_Click); - // - // label2 - // - this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(15, 111); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(101, 13); - this.label2.TabIndex = 9; - this.label2.Text = "Books in database: "; - // - // label1 - // - this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(15, 150); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(70, 13); - this.label1.TabIndex = 8; - this.label1.Text = "Books found:"; - // - // scannerButton - // - this.scannerButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.scannerButton.Location = new System.Drawing.Point(255, 260); - this.scannerButton.Name = "scannerButton"; - this.scannerButton.Size = new System.Drawing.Size(210, 40); - this.scannerButton.TabIndex = 7; - this.scannerButton.Text = "Start scanning"; - this.scannerButton.UseVisualStyleBackColor = true; - this.scannerButton.Click += new System.EventHandler(this.scannerButton_Click); - // - // libraryPath - // - this.libraryPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.libraryPath.Location = new System.Drawing.Point(17, 32); - this.libraryPath.Name = "libraryPath"; - this.libraryPath.Size = new System.Drawing.Size(259, 20); - this.libraryPath.TabIndex = 6; - this.libraryPath.TextChanged += new System.EventHandler(this.libraryPath_TextChanged); - this.libraryPath.Validated += new System.EventHandler(this.libraryPath_Validated); - // - // tabPage2 - // - this.tabPage2.BackColor = System.Drawing.SystemColors.Control; - this.tabPage2.Controls.Add(this.useAbsoluteUri); - this.tabPage2.Controls.Add(this.extWebLink); - this.tabPage2.Controls.Add(this.intWebLink); - this.tabPage2.Controls.Add(this.label34); - this.tabPage2.Controls.Add(this.label35); - this.tabPage2.Controls.Add(this.label33); - this.tabPage2.Controls.Add(this.interfaceCombo); - this.tabPage2.Controls.Add(this.label29); - this.tabPage2.Controls.Add(this.statUniqueClients); - this.tabPage2.Controls.Add(this.label26); - this.tabPage2.Controls.Add(this.statImages); - this.tabPage2.Controls.Add(this.label27); - this.tabPage2.Controls.Add(this.statBooks); - this.tabPage2.Controls.Add(this.label25); - this.tabPage2.Controls.Add(this.statRequests); - this.tabPage2.Controls.Add(this.label23); - this.tabPage2.Controls.Add(this.extLink); - this.tabPage2.Controls.Add(this.intLink); - this.tabPage2.Controls.Add(this.label13); - this.tabPage2.Controls.Add(this.extIPlabel); - this.tabPage2.Controls.Add(this.intIPlabel); - this.tabPage2.Controls.Add(this.label4); - this.tabPage2.Controls.Add(this.label3); - this.tabPage2.Controls.Add(this.serverButton); - this.tabPage2.Controls.Add(this.webPrefix); - this.tabPage2.Controls.Add(this.openPort); - this.tabPage2.Controls.Add(this.serverPort); - this.tabPage2.Controls.Add(this.useUPnP); - this.tabPage2.Controls.Add(this.rootPrefix); - this.tabPage2.Controls.Add(this.serverName); - this.tabPage2.Location = new System.Drawing.Point(4, 34); - this.tabPage2.Name = "tabPage2"; - this.tabPage2.Padding = new System.Windows.Forms.Padding(3); - this.tabPage2.Size = new System.Drawing.Size(473, 308); - this.tabPage2.TabIndex = 1; - this.tabPage2.Text = "Server"; - // - // useAbsoluteUri - // - this.useAbsoluteUri.AutoSize = true; - this.useAbsoluteUri.Checked = global::TinyOPDS.Properties.Settings.Default.UseAbsoluteUri; - this.useAbsoluteUri.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseAbsoluteUri", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.useAbsoluteUri.Location = new System.Drawing.Point(340, 71); - this.useAbsoluteUri.Name = "useAbsoluteUri"; - this.useAbsoluteUri.Size = new System.Drawing.Size(91, 17); - this.useAbsoluteUri.TabIndex = 54; - this.useAbsoluteUri.Text = "Absolute links"; - this.useAbsoluteUri.UseVisualStyleBackColor = true; - // - // extWebLink - // - this.extWebLink.Location = new System.Drawing.Point(252, 188); - this.extWebLink.Name = "extWebLink"; - this.extWebLink.Size = new System.Drawing.Size(203, 13); - this.extWebLink.TabIndex = 53; - this.extWebLink.TabStop = true; - this.extWebLink.Text = "- - - - - -"; - this.extWebLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // intWebLink - // - this.intWebLink.Location = new System.Drawing.Point(252, 148); - this.intWebLink.Name = "intWebLink"; - this.intWebLink.Size = new System.Drawing.Size(203, 13); - this.intWebLink.TabIndex = 52; - this.intWebLink.TabStop = true; - this.intWebLink.Text = "- - - - - -"; - this.intWebLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // label34 - // - this.label34.AutoSize = true; - this.label34.Location = new System.Drawing.Point(252, 173); - this.label34.Name = "label34"; - this.label34.Size = new System.Drawing.Size(96, 13); - this.label34.TabIndex = 51; - this.label34.Text = "External web URL:"; - // - // label35 - // - this.label35.AutoSize = true; - this.label35.Location = new System.Drawing.Point(252, 133); - this.label35.Name = "label35"; - this.label35.Size = new System.Drawing.Size(84, 13); - this.label35.TabIndex = 50; - this.label35.Text = "Local web URL:"; - // - // label33 - // - this.label33.AutoSize = true; - this.label33.Location = new System.Drawing.Point(252, 106); - this.label33.Name = "label33"; - this.label33.Size = new System.Drawing.Size(54, 13); - this.label33.TabIndex = 48; - this.label33.Text = "Web root:"; - // - // interfaceCombo - // - this.interfaceCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.interfaceCombo.FormattingEnabled = true; - this.interfaceCombo.Location = new System.Drawing.Point(255, 35); - this.interfaceCombo.Name = "interfaceCombo"; - this.interfaceCombo.Size = new System.Drawing.Size(121, 21); - this.interfaceCombo.TabIndex = 47; - this.interfaceCombo.SelectedIndexChanged += new System.EventHandler(this.interfaceCombo_SelectedIndexChanged); - // - // label29 - // - this.label29.AutoSize = true; - this.label29.Location = new System.Drawing.Point(252, 16); - this.label29.Name = "label29"; - this.label29.Size = new System.Drawing.Size(94, 13); - this.label29.TabIndex = 46; - this.label29.Text = "Network interface:"; - // - // statUniqueClients - // - this.statUniqueClients.AutoSize = true; - this.statUniqueClients.Location = new System.Drawing.Point(148, 256); - this.statUniqueClients.Name = "statUniqueClients"; - this.statUniqueClients.Size = new System.Drawing.Size(13, 13); - this.statUniqueClients.TabIndex = 45; - this.statUniqueClients.Text = "0"; - // - // label26 - // - this.label26.AutoSize = true; - this.label26.Location = new System.Drawing.Point(19, 256); - this.label26.Name = "label26"; - this.label26.Size = new System.Drawing.Size(77, 13); - this.label26.TabIndex = 44; - this.label26.Text = "Unique clients:"; - // - // statImages - // - this.statImages.AutoSize = true; - this.statImages.Location = new System.Drawing.Point(445, 228); - this.statImages.Name = "statImages"; - this.statImages.Size = new System.Drawing.Size(13, 13); - this.statImages.TabIndex = 43; - this.statImages.Text = "0"; - // - // label27 - // - this.label27.AutoSize = true; - this.label27.Location = new System.Drawing.Point(352, 228); - this.label27.Name = "label27"; - this.label27.Size = new System.Drawing.Size(67, 13); - this.label27.TabIndex = 42; - this.label27.Text = "Images sent:"; - // - // statBooks - // - this.statBooks.AutoSize = true; - this.statBooks.Location = new System.Drawing.Point(289, 228); - this.statBooks.Name = "statBooks"; - this.statBooks.Size = new System.Drawing.Size(13, 13); - this.statBooks.TabIndex = 41; - this.statBooks.Text = "0"; - // - // label25 - // - this.label25.AutoSize = true; - this.label25.Location = new System.Drawing.Point(200, 228); - this.label25.Name = "label25"; - this.label25.Size = new System.Drawing.Size(63, 13); - this.label25.TabIndex = 40; - this.label25.Text = "Books sent:"; - // - // statRequests - // - this.statRequests.AutoSize = true; - this.statRequests.Location = new System.Drawing.Point(148, 228); - this.statRequests.Name = "statRequests"; - this.statRequests.Size = new System.Drawing.Size(13, 13); - this.statRequests.TabIndex = 39; - this.statRequests.Text = "0"; - // - // label23 - // - this.label23.AutoSize = true; - this.label23.Location = new System.Drawing.Point(19, 228); - this.label23.Name = "label23"; - this.label23.Size = new System.Drawing.Size(77, 13); - this.label23.TabIndex = 38; - this.label23.Text = "Total requests:"; - // - // extLink - // - this.extLink.Location = new System.Drawing.Point(19, 188); - this.extLink.Name = "extLink"; - this.extLink.Size = new System.Drawing.Size(214, 13); - this.extLink.TabIndex = 37; - this.extLink.TabStop = true; - this.extLink.Text = "- - - - - -"; - this.extLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // intLink - // - this.intLink.Location = new System.Drawing.Point(19, 148); - this.intLink.Name = "intLink"; - this.intLink.Size = new System.Drawing.Size(214, 13); - this.intLink.TabIndex = 36; - this.intLink.TabStop = true; - this.intLink.Text = "- - - - - -"; - this.intLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // label13 - // - this.label13.AutoSize = true; - this.label13.Location = new System.Drawing.Point(20, 106); - this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(61, 13); - this.label13.TabIndex = 18; - this.label13.Text = "OPDS root:"; - // - // extIPlabel - // - this.extIPlabel.AutoSize = true; - this.extIPlabel.Location = new System.Drawing.Point(19, 173); - this.extIPlabel.Name = "extIPlabel"; - this.extIPlabel.Size = new System.Drawing.Size(106, 13); - this.extIPlabel.TabIndex = 14; - this.extIPlabel.Text = "External OPDS URL:"; - // - // intIPlabel - // - this.intIPlabel.AutoSize = true; - this.intIPlabel.Location = new System.Drawing.Point(19, 133); - this.intIPlabel.Name = "intIPlabel"; - this.intIPlabel.Size = new System.Drawing.Size(94, 13); - this.intIPlabel.TabIndex = 13; - this.intIPlabel.Text = "Local OPDS URL:"; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(19, 16); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(70, 13); - this.label4.TabIndex = 11; - this.label4.Text = "Server name:"; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(405, 16); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(29, 13); - this.label3.TabIndex = 9; - this.label3.Text = "Port:"; - // - // serverButton - // - this.serverButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.serverButton.Location = new System.Drawing.Point(255, 260); - this.serverButton.Name = "serverButton"; - this.serverButton.Size = new System.Drawing.Size(210, 40); - this.serverButton.TabIndex = 8; - this.serverButton.Text = "Start server"; - this.serverButton.Click += new System.EventHandler(this.serverButton_Click); - // - // webPrefix - // - this.webPrefix.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "HttpPrefix", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.webPrefix.Location = new System.Drawing.Point(340, 103); - this.webPrefix.Name = "webPrefix"; - this.webPrefix.Size = new System.Drawing.Size(118, 20); - this.webPrefix.TabIndex = 49; - this.webPrefix.Text = global::TinyOPDS.Properties.Settings.Default.HttpPrefix; - this.webPrefix.TextChanged += new System.EventHandler(this.rootPrefix_TextChanged); - // - // openPort - // - this.openPort.AutoSize = true; - this.openPort.Checked = global::TinyOPDS.Properties.Settings.Default.OpenNATPort; - this.openPort.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "OpenNATPort", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.openPort.Enabled = false; - this.openPort.Location = new System.Drawing.Point(160, 71); - this.openPort.Name = "openPort"; - this.openPort.Size = new System.Drawing.Size(130, 17); - this.openPort.TabIndex = 15; - this.openPort.Text = "Forward port on router"; - this.openPort.UseVisualStyleBackColor = true; - this.openPort.CheckedChanged += new System.EventHandler(this.openPort_CheckedChanged); - // - // serverPort - // - this.serverPort.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "ServerPort", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.serverPort.Location = new System.Drawing.Point(408, 36); - this.serverPort.Name = "serverPort"; - this.serverPort.Size = new System.Drawing.Size(50, 20); - this.serverPort.TabIndex = 10; - this.serverPort.Text = global::TinyOPDS.Properties.Settings.Default.ServerPort; - this.serverPort.Validated += new System.EventHandler(this.serverPort_Validated); - // - // useUPnP - // - this.useUPnP.AutoSize = true; - this.useUPnP.Checked = global::TinyOPDS.Properties.Settings.Default.UseUPnP; - this.useUPnP.CheckState = System.Windows.Forms.CheckState.Checked; - this.useUPnP.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseUPnP", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.useUPnP.Location = new System.Drawing.Point(22, 71); - this.useUPnP.Name = "useUPnP"; - this.useUPnP.Size = new System.Drawing.Size(76, 17); - this.useUPnP.TabIndex = 35; - this.useUPnP.Text = "Use UPnP"; - this.useUPnP.UseVisualStyleBackColor = true; - this.useUPnP.CheckStateChanged += new System.EventHandler(this.useUPnP_CheckStateChanged); - // - // rootPrefix - // - this.rootPrefix.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "RootPrefix", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.rootPrefix.Location = new System.Drawing.Point(115, 103); - this.rootPrefix.Name = "rootPrefix"; - this.rootPrefix.Size = new System.Drawing.Size(118, 20); - this.rootPrefix.TabIndex = 19; - this.rootPrefix.Text = global::TinyOPDS.Properties.Settings.Default.RootPrefix; - this.rootPrefix.TextChanged += new System.EventHandler(this.rootPrefix_TextChanged); - // - // serverName - // - this.serverName.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "ServerName", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.serverName.Location = new System.Drawing.Point(22, 35); - this.serverName.Name = "serverName"; - this.serverName.Size = new System.Drawing.Size(211, 20); - this.serverName.TabIndex = 12; - this.serverName.Text = global::TinyOPDS.Properties.Settings.Default.ServerName; - // - // tabPage6 - // - this.tabPage6.BackColor = System.Drawing.SystemColors.Control; - this.tabPage6.Controls.Add(this.checkBox2); - this.tabPage6.Controls.Add(this.checkBox1); - this.tabPage6.Controls.Add(this.newBooksPeriodCombo); - this.tabPage6.Controls.Add(this.label39); - this.tabPage6.Controls.Add(this.sortOrderCombo); - this.tabPage6.Controls.Add(this.label38); - this.tabPage6.Controls.Add(this.itemsPerWeb); - this.tabPage6.Controls.Add(this.label37); - this.tabPage6.Controls.Add(this.itemsPerOPDS); - this.tabPage6.Controls.Add(this.label36); - this.tabPage6.Location = new System.Drawing.Point(4, 34); - this.tabPage6.Name = "tabPage6"; - this.tabPage6.Size = new System.Drawing.Size(473, 308); - this.tabPage6.TabIndex = 5; - this.tabPage6.Text = "OPDS catalog"; - // - // checkBox2 - // - this.checkBox2.AutoSize = true; - this.checkBox2.Checked = global::TinyOPDS.Properties.Settings.Default.UseAuthorsAliases; - this.checkBox2.CheckState = System.Windows.Forms.CheckState.Checked; - this.checkBox2.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseAuthorsAliases", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.checkBox2.Location = new System.Drawing.Point(25, 162); - this.checkBox2.Name = "checkBox2"; - this.checkBox2.Size = new System.Drawing.Size(118, 17); - this.checkBox2.TabIndex = 9; - this.checkBox2.Text = "Use authors aliases"; - this.checkBox2.UseVisualStyleBackColor = true; - this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox_CheckedChanged); - // - // checkBox1 - // - this.checkBox1.AutoSize = true; - this.checkBox1.Checked = global::TinyOPDS.Properties.Settings.Default.LowMemoryProfile; - this.checkBox1.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "LowMemoryProfile", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.checkBox1.Location = new System.Drawing.Point(25, 202); - this.checkBox1.Name = "checkBox1"; - this.checkBox1.Size = new System.Drawing.Size(274, 17); - this.checkBox1.TabIndex = 8; - this.checkBox1.Text = "\"Low memory\" model (do not load book descriptions)"; - this.checkBox1.UseVisualStyleBackColor = true; - this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox_CheckedChanged); - // - // newBooksPeriodCombo - // - this.newBooksPeriodCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.newBooksPeriodCombo.FormattingEnabled = true; - this.newBooksPeriodCombo.Items.AddRange(new object[] { - "one week", - "two weeks", - "three weeks", - "month", - "month and half", - "two month", - "three month"}); - this.newBooksPeriodCombo.Location = new System.Drawing.Point(254, 115); - this.newBooksPeriodCombo.Name = "newBooksPeriodCombo"; - this.newBooksPeriodCombo.Size = new System.Drawing.Size(176, 21); - this.newBooksPeriodCombo.TabIndex = 7; - this.newBooksPeriodCombo.SelectedIndexChanged += new System.EventHandler(this.newBooksPeriodCombo_SelectedIndexChanged); - // - // label39 - // - this.label39.AutoSize = true; - this.label39.Location = new System.Drawing.Point(251, 95); - this.label39.Name = "label39"; - this.label39.Size = new System.Drawing.Size(139, 13); - this.label39.TabIndex = 6; - this.label39.Text = "\"New books\" check period:"; - // - // sortOrderCombo - // - this.sortOrderCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.sortOrderCombo.FormattingEnabled = true; - this.sortOrderCombo.Items.AddRange(new object[] { - "Latin first", - "Cyrillic first"}); - this.sortOrderCombo.Location = new System.Drawing.Point(25, 115); - this.sortOrderCombo.Name = "sortOrderCombo"; - this.sortOrderCombo.Size = new System.Drawing.Size(176, 21); - this.sortOrderCombo.TabIndex = 5; - this.sortOrderCombo.SelectedIndexChanged += new System.EventHandler(this.sortOrderCombo_SelectedIndexChanged); - // - // label38 - // - this.label38.AutoSize = true; - this.label38.Location = new System.Drawing.Point(22, 95); - this.label38.Name = "label38"; - this.label38.Size = new System.Drawing.Size(82, 13); - this.label38.TabIndex = 4; - this.label38.Text = "Items sort order:"; - // - // itemsPerWeb - // - this.itemsPerWeb.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::TinyOPDS.Properties.Settings.Default, "ItemsPerWebPage", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.itemsPerWeb.Location = new System.Drawing.Point(254, 48); - this.itemsPerWeb.Maximum = new decimal(new int[] { - 1000, - 0, - 0, - 0}); - this.itemsPerWeb.Minimum = new decimal(new int[] { - 30, - 0, - 0, - 0}); - this.itemsPerWeb.Name = "itemsPerWeb"; - this.itemsPerWeb.Size = new System.Drawing.Size(55, 20); - this.itemsPerWeb.TabIndex = 3; - this.itemsPerWeb.Value = global::TinyOPDS.Properties.Settings.Default.ItemsPerWebPage; - // - // label37 - // - this.label37.AutoSize = true; - this.label37.Location = new System.Drawing.Point(251, 25); - this.label37.Name = "label37"; - this.label37.Size = new System.Drawing.Size(103, 13); - this.label37.TabIndex = 2; - this.label37.Text = "Items per web page:"; - // - // itemsPerOPDS - // - this.itemsPerOPDS.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::TinyOPDS.Properties.Settings.Default, "ItemsPerOPDSPage", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.itemsPerOPDS.Location = new System.Drawing.Point(25, 48); - this.itemsPerOPDS.Maximum = new decimal(new int[] { - 1000, - 0, - 0, - 0}); - this.itemsPerOPDS.Minimum = new decimal(new int[] { - 30, - 0, - 0, - 0}); - this.itemsPerOPDS.Name = "itemsPerOPDS"; - this.itemsPerOPDS.Size = new System.Drawing.Size(55, 20); - this.itemsPerOPDS.TabIndex = 1; - this.itemsPerOPDS.Value = global::TinyOPDS.Properties.Settings.Default.ItemsPerOPDSPage; - // - // label36 - // - this.label36.AutoSize = true; - this.label36.Location = new System.Drawing.Point(22, 26); - this.label36.Name = "label36"; - this.label36.Size = new System.Drawing.Size(113, 13); - this.label36.TabIndex = 0; - this.label36.Text = "Items per OPDS page:"; - // - // tabPage5 - // - this.tabPage5.BackColor = System.Drawing.SystemColors.Control; - this.tabPage5.Controls.Add(this.statBannedClients); - this.tabPage5.Controls.Add(this.label31); - this.tabPage5.Controls.Add(this.label24); - this.tabPage5.Controls.Add(this.statWrongLogins); - this.tabPage5.Controls.Add(this.label30); - this.tabPage5.Controls.Add(this.statGoodLogins); - this.tabPage5.Controls.Add(this.label28); - this.tabPage5.Controls.Add(this.dataGridView1); - this.tabPage5.Controls.Add(this.wrongAttemptsCount); - this.tabPage5.Controls.Add(this.banClients); - this.tabPage5.Controls.Add(this.rememberClients); - this.tabPage5.Controls.Add(this.useHTTPAuth); - this.tabPage5.Location = new System.Drawing.Point(4, 34); - this.tabPage5.Name = "tabPage5"; - this.tabPage5.Padding = new System.Windows.Forms.Padding(3); - this.tabPage5.Size = new System.Drawing.Size(473, 308); - this.tabPage5.TabIndex = 4; - this.tabPage5.Text = "Authentication"; - // - // statBannedClients - // - this.statBannedClients.AutoSize = true; - this.statBannedClients.Location = new System.Drawing.Point(429, 254); - this.statBannedClients.Name = "statBannedClients"; - this.statBannedClients.Size = new System.Drawing.Size(13, 13); - this.statBannedClients.TabIndex = 48; - this.statBannedClients.Text = "0"; - this.statBannedClients.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // label31 - // - this.label31.AutoSize = true; - this.label31.Location = new System.Drawing.Point(334, 254); - this.label31.Name = "label31"; - this.label31.Size = new System.Drawing.Size(80, 13); - this.label31.TabIndex = 47; - this.label31.Text = "Banned clients:"; - // - // label24 - // - this.label24.AutoSize = true; - this.label24.Location = new System.Drawing.Point(329, 51); - this.label24.Name = "label24"; - this.label24.Size = new System.Drawing.Size(75, 13); - this.label24.TabIndex = 46; - this.label24.Text = "failed attempts"; - // - // statWrongLogins - // - this.statWrongLogins.AutoSize = true; - this.statWrongLogins.Location = new System.Drawing.Point(279, 254); - this.statWrongLogins.Name = "statWrongLogins"; - this.statWrongLogins.Size = new System.Drawing.Size(13, 13); - this.statWrongLogins.TabIndex = 43; - this.statWrongLogins.Text = "0"; - this.statWrongLogins.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // label30 - // - this.label30.AutoSize = true; - this.label30.Location = new System.Drawing.Point(185, 254); - this.label30.Name = "label30"; - this.label30.Size = new System.Drawing.Size(68, 13); - this.label30.TabIndex = 42; - this.label30.Text = "Failed logins:"; - // - // statGoodLogins - // - this.statGoodLogins.AutoSize = true; - this.statGoodLogins.Location = new System.Drawing.Point(128, 254); - this.statGoodLogins.Name = "statGoodLogins"; - this.statGoodLogins.Size = new System.Drawing.Size(13, 13); - this.statGoodLogins.TabIndex = 41; - this.statGoodLogins.Text = "0"; - this.statGoodLogins.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // label28 - // - this.label28.AutoSize = true; - this.label28.Location = new System.Drawing.Point(23, 254); - this.label28.Name = "label28"; - this.label28.Size = new System.Drawing.Size(92, 13); - this.label28.TabIndex = 40; - this.label28.Text = "Successful logins:"; - // - // dataGridView1 - // - this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this.dataGridView1.Location = new System.Drawing.Point(26, 83); - this.dataGridView1.Name = "dataGridView1"; - this.dataGridView1.Size = new System.Drawing.Size(426, 150); - this.dataGridView1.TabIndex = 1; - this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView1_CellFormatting); - // - // wrongAttemptsCount - // - this.wrongAttemptsCount.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::TinyOPDS.Properties.Settings.Default, "WrongAttemptsCount", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.wrongAttemptsCount.Location = new System.Drawing.Point(283, 46); - this.wrongAttemptsCount.Minimum = new decimal(new int[] { - 3, - 0, - 0, - 0}); - this.wrongAttemptsCount.Name = "wrongAttemptsCount"; - this.wrongAttemptsCount.Size = new System.Drawing.Size(40, 20); - this.wrongAttemptsCount.TabIndex = 45; - this.wrongAttemptsCount.Value = global::TinyOPDS.Properties.Settings.Default.WrongAttemptsCount; - // - // banClients - // - this.banClients.AutoSize = true; - this.banClients.Checked = global::TinyOPDS.Properties.Settings.Default.BanClients; - this.banClients.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "BanClients", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.banClients.Location = new System.Drawing.Point(283, 23); - this.banClients.Name = "banClients"; - this.banClients.Size = new System.Drawing.Size(102, 17); - this.banClients.TabIndex = 44; - this.banClients.Text = "Ban clients after"; - this.banClients.UseVisualStyleBackColor = true; - this.banClients.CheckedChanged += new System.EventHandler(this.banClients_CheckedChanged); - // - // rememberClients - // - this.rememberClients.AutoSize = true; - this.rememberClients.Checked = global::TinyOPDS.Properties.Settings.Default.RememberClients; - this.rememberClients.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "RememberClients", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.rememberClients.Location = new System.Drawing.Point(26, 51); - this.rememberClients.Name = "rememberClients"; - this.rememberClients.Size = new System.Drawing.Size(162, 17); - this.rememberClients.TabIndex = 2; - this.rememberClients.Text = "Remember authorized clients"; - this.rememberClients.UseVisualStyleBackColor = true; - // - // useHTTPAuth - // - this.useHTTPAuth.AutoSize = true; - this.useHTTPAuth.Checked = global::TinyOPDS.Properties.Settings.Default.UseHTTPAuth; - this.useHTTPAuth.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseHTTPAuth", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.useHTTPAuth.Location = new System.Drawing.Point(26, 23); - this.useHTTPAuth.Name = "useHTTPAuth"; - this.useHTTPAuth.Size = new System.Drawing.Size(175, 17); - this.useHTTPAuth.TabIndex = 0; - this.useHTTPAuth.Text = "Use HTTP basic authentication"; - this.useHTTPAuth.UseVisualStyleBackColor = true; - this.useHTTPAuth.CheckedChanged += new System.EventHandler(this.useHTTPAuth_CheckedChanged); - // - // tabPage3 - // - this.tabPage3.BackColor = System.Drawing.SystemColors.Control; - this.tabPage3.Controls.Add(this.viewLogFile); - this.tabPage3.Controls.Add(this.label32); - this.tabPage3.Controls.Add(this.updateCombo); - this.tabPage3.Controls.Add(this.label22); - this.tabPage3.Controls.Add(this.logVerbosity); - this.tabPage3.Controls.Add(this.converterLinkLabel); - this.tabPage3.Controls.Add(this.label11); - this.tabPage3.Controls.Add(this.langCombo); - this.tabPage3.Controls.Add(this.label8); - this.tabPage3.Controls.Add(this.convertorFolder); - this.tabPage3.Controls.Add(this.convertorPath); - this.tabPage3.Controls.Add(this.saveLog); - this.tabPage3.Controls.Add(this.closeToTray); - this.tabPage3.Controls.Add(this.startMinimized); - this.tabPage3.Controls.Add(this.startWithWindows); - this.tabPage3.Location = new System.Drawing.Point(4, 34); - this.tabPage3.Name = "tabPage3"; - this.tabPage3.Padding = new System.Windows.Forms.Padding(3); - this.tabPage3.Size = new System.Drawing.Size(473, 308); - this.tabPage3.TabIndex = 2; - this.tabPage3.Text = "Miscellaneous"; - // - // viewLogFile - // - this.viewLogFile.Location = new System.Drawing.Point(299, 218); - this.viewLogFile.Name = "viewLogFile"; - this.viewLogFile.Size = new System.Drawing.Size(130, 23); - this.viewLogFile.TabIndex = 39; - this.viewLogFile.Text = "View log file"; - this.viewLogFile.UseVisualStyleBackColor = true; - this.viewLogFile.Click += new System.EventHandler(this.viewLogFile_Click); - // - // label32 - // - this.label32.AutoSize = true; - this.label32.Location = new System.Drawing.Point(298, 143); - this.label32.Name = "label32"; - this.label32.Size = new System.Drawing.Size(92, 13); - this.label32.TabIndex = 38; - this.label32.Text = "Check for update:"; - // - // updateCombo - // - this.updateCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.updateCombo.FormattingEnabled = true; - this.updateCombo.Items.AddRange(new object[] { - "Never", - "Once a week", - "Once a month"}); - this.updateCombo.Location = new System.Drawing.Point(299, 166); - this.updateCombo.Name = "updateCombo"; - this.updateCombo.Size = new System.Drawing.Size(127, 21); - this.updateCombo.TabIndex = 37; - this.updateCombo.SelectedIndexChanged += new System.EventHandler(this.updateCombo_SelectedIndexChanged); - // - // label22 - // - this.label22.AutoSize = true; - this.label22.Location = new System.Drawing.Point(13, 195); - this.label22.Name = "label22"; - this.label22.Size = new System.Drawing.Size(95, 13); - this.label22.TabIndex = 36; - this.label22.Text = "Log verbosity level"; - // - // logVerbosity - // - this.logVerbosity.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.logVerbosity.FormattingEnabled = true; - this.logVerbosity.Items.AddRange(new object[] { - "Info, warnings and errors", - "Warnings and errors", - "Errors only"}); - this.logVerbosity.Location = new System.Drawing.Point(14, 218); - this.logVerbosity.Name = "logVerbosity"; - this.logVerbosity.Size = new System.Drawing.Size(246, 21); - this.logVerbosity.TabIndex = 35; - this.logVerbosity.SelectedIndexChanged += new System.EventHandler(this.logVerbosity_SelectedIndexChanged); - // - // converterLinkLabel - // - this.converterLinkLabel.AutoSize = true; - this.converterLinkLabel.Location = new System.Drawing.Point(12, 55); - this.converterLinkLabel.Name = "converterLinkLabel"; - this.converterLinkLabel.Size = new System.Drawing.Size(268, 13); - this.converterLinkLabel.TabIndex = 34; - this.converterLinkLabel.TabStop = true; - this.converterLinkLabel.Text = "Click here to download latest version of ePub converter"; - this.converterLinkLabel.Visible = false; - this.converterLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); - // - // label11 - // - this.label11.AutoSize = true; - this.label11.Location = new System.Drawing.Point(296, 82); - this.label11.Name = "label11"; - this.label11.Size = new System.Drawing.Size(130, 13); - this.label11.TabIndex = 32; - this.label11.Text = "GUI and OPDS language:"; - // - // langCombo - // - this.langCombo.DisplayMember = "Value"; - this.langCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.langCombo.FormattingEnabled = true; - this.langCombo.Location = new System.Drawing.Point(299, 107); - this.langCombo.Name = "langCombo"; - this.langCombo.Size = new System.Drawing.Size(127, 21); - this.langCombo.TabIndex = 31; - this.langCombo.ValueMember = "Key"; - this.langCombo.SelectedValueChanged += new System.EventHandler(this.langCombo_SelectedValueChanged); - // - // label8 - // - this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(8, 12); - this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(138, 13); - this.label8.TabIndex = 30; - this.label8.Text = "Path to the ePub converter:"; - // - // convertorFolder - // - this.convertorFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.convertorFolder.Image = global::TinyOPDS.Properties.Resources.folder; - this.convertorFolder.Location = new System.Drawing.Point(431, 26); - this.convertorFolder.Name = "convertorFolder"; - this.convertorFolder.Size = new System.Drawing.Size(29, 23); - this.convertorFolder.TabIndex = 29; - this.convertorFolder.UseVisualStyleBackColor = true; - this.convertorFolder.Click += new System.EventHandler(this.folderButton_Click); - // - // convertorPath - // - this.convertorPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.convertorPath.Location = new System.Drawing.Point(11, 28); - this.convertorPath.Name = "convertorPath"; - this.convertorPath.Size = new System.Drawing.Size(413, 20); - this.convertorPath.TabIndex = 28; - this.convertorPath.Validated += new System.EventHandler(this.convertorPath_Validated); - // - // saveLog - // - this.saveLog.AutoSize = true; - this.saveLog.Checked = global::TinyOPDS.Properties.Settings.Default.SaveLogToDisk; - this.saveLog.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "SaveLogToDisk", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.saveLog.Location = new System.Drawing.Point(14, 166); - this.saveLog.Name = "saveLog"; - this.saveLog.Size = new System.Drawing.Size(96, 17); - this.saveLog.TabIndex = 33; - this.saveLog.Text = "Save log to file"; - this.saveLog.UseVisualStyleBackColor = true; - this.saveLog.CheckedChanged += new System.EventHandler(this.saveLog_CheckedChanged); - // - // closeToTray - // - this.closeToTray.AutoSize = true; - this.closeToTray.Checked = global::TinyOPDS.Properties.Settings.Default.CloseToTray; - this.closeToTray.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "CloseToTray", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.closeToTray.Location = new System.Drawing.Point(14, 138); - this.closeToTray.Name = "closeToTray"; - this.closeToTray.Size = new System.Drawing.Size(138, 17); - this.closeToTray.TabIndex = 2; - this.closeToTray.Text = "Close or minimize to tray"; - this.closeToTray.UseVisualStyleBackColor = true; - this.closeToTray.CheckedChanged += new System.EventHandler(this.closeToTray_CheckedChanged); - // - // startMinimized - // - this.startMinimized.AutoSize = true; - this.startMinimized.Checked = global::TinyOPDS.Properties.Settings.Default.StartMinimized; - this.startMinimized.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "StartMinimized", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.startMinimized.Location = new System.Drawing.Point(14, 110); - this.startMinimized.Name = "startMinimized"; - this.startMinimized.Size = new System.Drawing.Size(96, 17); - this.startMinimized.TabIndex = 1; - this.startMinimized.Text = "Start minimized"; - this.startMinimized.UseVisualStyleBackColor = true; - // - // startWithWindows - // - this.startWithWindows.AutoSize = true; - this.startWithWindows.Checked = global::TinyOPDS.Properties.Settings.Default.StartWithWindows; - this.startWithWindows.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "StartWithWindows", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); - this.startWithWindows.Location = new System.Drawing.Point(14, 82); - this.startWithWindows.Name = "startWithWindows"; - this.startWithWindows.Size = new System.Drawing.Size(117, 17); - this.startWithWindows.TabIndex = 0; - this.startWithWindows.Text = "Start with Windows"; - this.startWithWindows.UseVisualStyleBackColor = true; - this.startWithWindows.CheckedChanged += new System.EventHandler(this.startWithWindows_CheckedChanged); - // - // tabPage4 - // - this.tabPage4.BackColor = System.Drawing.SystemColors.Control; - this.tabPage4.Controls.Add(this.linkLabel6); - this.tabPage4.Controls.Add(this.linkLabel5); - this.tabPage4.Controls.Add(this.linkLabel4); - this.tabPage4.Controls.Add(this.linkLabel3); - this.tabPage4.Controls.Add(this.label20); - this.tabPage4.Controls.Add(this.label19); - this.tabPage4.Controls.Add(this.linkLabel2); - this.tabPage4.Controls.Add(this.label18); - this.tabPage4.Controls.Add(this.linkLabel1); - this.tabPage4.Controls.Add(this.label17); - this.tabPage4.Controls.Add(this.appVersion); - this.tabPage4.Controls.Add(this.appName); - this.tabPage4.Controls.Add(this.pictureBox1); - this.tabPage4.Controls.Add(this.donateButton); - this.tabPage4.Location = new System.Drawing.Point(4, 34); - this.tabPage4.Name = "tabPage4"; - this.tabPage4.Padding = new System.Windows.Forms.Padding(3); - this.tabPage4.Size = new System.Drawing.Size(473, 308); - this.tabPage4.TabIndex = 3; - this.tabPage4.Text = "About program"; - // - // linkLabel6 - // - this.linkLabel6.AutoSize = true; - this.linkLabel6.Location = new System.Drawing.Point(195, 258); - this.linkLabel6.Name = "linkLabel6"; - this.linkLabel6.Size = new System.Drawing.Size(165, 13); - this.linkLabel6.TabIndex = 13; - this.linkLabel6.TabStop = true; - this.linkLabel6.Text = "Gremlin2, author of Fb2Fix project"; - this.linkLabel6.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); - // - // linkLabel5 - // - this.linkLabel5.AutoSize = true; - this.linkLabel5.Location = new System.Drawing.Point(195, 212); - this.linkLabel5.Name = "linkLabel5"; - this.linkLabel5.Size = new System.Drawing.Size(97, 13); - this.linkLabel5.TabIndex = 12; - this.linkLabel5.TabStop = true; - this.linkLabel5.Text = "ePubReader library"; - this.linkLabel5.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); - // - // linkLabel4 - // - this.linkLabel4.AutoSize = true; - this.linkLabel4.Location = new System.Drawing.Point(195, 235); - this.linkLabel4.Name = "linkLabel4"; - this.linkLabel4.Size = new System.Drawing.Size(86, 13); - this.linkLabel4.TabIndex = 11; - this.linkLabel4.TabStop = true; - this.linkLabel4.Text = "DotNetZip library"; - this.linkLabel4.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); - // - // linkLabel3 - // - this.linkLabel3.AutoSize = true; - this.linkLabel3.Location = new System.Drawing.Point(193, 190); - this.linkLabel3.Name = "linkLabel3"; - this.linkLabel3.Size = new System.Drawing.Size(267, 13); - this.linkLabel3.TabIndex = 10; - this.linkLabel3.TabStop = true; - this.linkLabel3.Text = "Lord KiRon, author of fb2librarynet library and converter"; - this.linkLabel3.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); - // - // label20 - // - this.label20.Location = new System.Drawing.Point(9, 190); - this.label20.Name = "label20"; - this.label20.Size = new System.Drawing.Size(161, 13); - this.label20.TabIndex = 9; - this.label20.Text = "Special thanks:"; - this.label20.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // label19 - // - this.label19.AutoSize = true; - this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.label19.Location = new System.Drawing.Point(192, 89); - this.label19.Name = "label19"; - this.label19.Size = new System.Drawing.Size(220, 20); - this.label19.TabIndex = 8; - this.label19.Text = "Copyright © 2013, SeNSSoFT"; - // - // linkLabel2 - // - this.linkLabel2.AutoSize = true; - this.linkLabel2.Location = new System.Drawing.Point(193, 167); - this.linkLabel2.Name = "linkLabel2"; - this.linkLabel2.Size = new System.Drawing.Size(184, 13); - this.linkLabel2.TabIndex = 7; - this.linkLabel2.TabStop = true; - this.linkLabel2.Text = "http://tinyopds.codeplex.com/license"; - this.linkLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // label18 - // - this.label18.Location = new System.Drawing.Point(11, 167); - this.label18.Name = "label18"; - this.label18.Size = new System.Drawing.Size(159, 13); - this.label18.TabIndex = 6; - this.label18.Text = "Project license:"; - this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // linkLabel1 - // - this.linkLabel1.AutoSize = true; - this.linkLabel1.Location = new System.Drawing.Point(193, 144); - this.linkLabel1.Name = "linkLabel1"; - this.linkLabel1.Size = new System.Drawing.Size(151, 13); - this.linkLabel1.TabIndex = 5; - this.linkLabel1.TabStop = true; - this.linkLabel1.Text = "http://tinyopds.codeplex.com/"; - this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); - // - // label17 - // - this.label17.Location = new System.Drawing.Point(8, 144); - this.label17.Name = "label17"; - this.label17.Size = new System.Drawing.Size(162, 13); - this.label17.TabIndex = 4; - this.label17.Text = "Project home page:"; - this.label17.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // appVersion - // - this.appVersion.AutoSize = true; - this.appVersion.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.appVersion.Location = new System.Drawing.Point(262, 58); - this.appVersion.Name = "appVersion"; - this.appVersion.Size = new System.Drawing.Size(85, 20); - this.appVersion.TabIndex = 3; - this.appVersion.Text = "version 1.2"; - // - // appName - // - this.appName.AutoSize = true; - this.appName.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); - this.appName.Location = new System.Drawing.Point(190, 14); - this.appName.Name = "appName"; - this.appName.Size = new System.Drawing.Size(226, 31); - this.appName.TabIndex = 2; - this.appName.Text = "TinyOPDS server"; - // - // pictureBox1 - // - this.pictureBox1.ErrorImage = null; - this.pictureBox1.Image = global::TinyOPDS.Properties.Resources.TinyOPDS; - this.pictureBox1.Location = new System.Drawing.Point(8, 9); - this.pictureBox1.Name = "pictureBox1"; - this.pictureBox1.Size = new System.Drawing.Size(103, 103); - this.pictureBox1.TabIndex = 1; - this.pictureBox1.TabStop = false; - // - // donateButton - // - this.donateButton.Image = global::TinyOPDS.Properties.Resources.donate; - this.donateButton.Location = new System.Drawing.Point(9, 240); - this.donateButton.Name = "donateButton"; - this.donateButton.Size = new System.Drawing.Size(157, 56); - this.donateButton.TabIndex = 0; - this.donateButton.UseVisualStyleBackColor = true; - this.donateButton.Click += new System.EventHandler(this.donateButton_Click); - // - // contextMenuStrip - // - this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.windowMenuItem, - this.serverMenuItem, - this.toolStripMenuItem1, - this.exitMenuItem}); - this.contextMenuStrip.Name = "contextMenuStrip1"; - this.contextMenuStrip.Size = new System.Drawing.Size(145, 76); - // - // windowMenuItem - // - this.windowMenuItem.Name = "windowMenuItem"; - this.windowMenuItem.Size = new System.Drawing.Size(144, 22); - this.windowMenuItem.Text = "Hide window"; - this.windowMenuItem.Click += new System.EventHandler(this.windowMenuItem_Click); - // - // serverMenuItem - // - this.serverMenuItem.Name = "serverMenuItem"; - this.serverMenuItem.Size = new System.Drawing.Size(144, 22); - this.serverMenuItem.Text = "Stop server"; - this.serverMenuItem.Click += new System.EventHandler(this.serverButton_Click); - // - // toolStripMenuItem1 - // - this.toolStripMenuItem1.Name = "toolStripMenuItem1"; - this.toolStripMenuItem1.Size = new System.Drawing.Size(141, 6); - // - // exitMenuItem - // - this.exitMenuItem.Name = "exitMenuItem"; - this.exitMenuItem.Size = new System.Drawing.Size(144, 22); - this.exitMenuItem.Text = "Exit"; - this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click); - // - // MainForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(474, 341); - this.Controls.Add(this.tabControl1); - this.DoubleBuffered = true; - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.MaximizeBox = false; - this.Name = "MainForm"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "TinyOPDS server"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); - this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed); - this.Load += new System.EventHandler(this.MainForm_Load); - this.Resize += new System.EventHandler(this.MainForm_Resize); - this.tabControl1.ResumeLayout(false); - this.tabPage1.ResumeLayout(false); - this.tabPage1.PerformLayout(); - this.tabPage2.ResumeLayout(false); - this.tabPage2.PerformLayout(); - this.tabPage6.ResumeLayout(false); - this.tabPage6.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.itemsPerWeb)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.itemsPerOPDS)).EndInit(); - this.tabPage5.ResumeLayout(false); - this.tabPage5.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.wrongAttemptsCount)).EndInit(); - this.tabPage3.ResumeLayout(false); - this.tabPage3.PerformLayout(); - this.tabPage4.ResumeLayout(false); - this.tabPage4.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); - this.contextMenuStrip.ResumeLayout(false); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.TabControl tabControl1; - private System.Windows.Forms.TabPage tabPage1; - private System.Windows.Forms.Button folderButton; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Button scannerButton; - private System.Windows.Forms.TextBox libraryPath; - private System.Windows.Forms.TabPage tabPage2; - private System.Windows.Forms.TextBox serverPort; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Button serverButton; - private System.Windows.Forms.TabPage tabPage3; - private System.Windows.Forms.Label invalidBooks; - private System.Windows.Forms.Label label9; - private System.Windows.Forms.Label skippedBooks; - private System.Windows.Forms.Label label7; - private System.Windows.Forms.Label booksFound; - private System.Windows.Forms.Label booksInDB; - private System.Windows.Forms.Label label15; - private System.Windows.Forms.Label status; - private System.Windows.Forms.Label label14; - private System.Windows.Forms.Label rate; - private System.Windows.Forms.Label label12; - private System.Windows.Forms.Label elapsedTime; - private System.Windows.Forms.Label label10; - private System.Windows.Forms.Label startTime; - private System.Windows.Forms.Label label6; - private System.Windows.Forms.Label booksProcessed; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.TextBox serverName; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.CheckBox closeToTray; - private System.Windows.Forms.CheckBox startMinimized; - private System.Windows.Forms.CheckBox startWithWindows; - private System.Windows.Forms.Label label8; - private System.Windows.Forms.Button convertorFolder; - private System.Windows.Forms.TextBox convertorPath; - private System.Windows.Forms.Label label11; - private System.Windows.Forms.ComboBox langCombo; - private System.Windows.Forms.CheckBox openPort; - private System.Windows.Forms.Label extIPlabel; - private System.Windows.Forms.Label intIPlabel; - private System.Windows.Forms.ContextMenuStrip contextMenuStrip; - private System.Windows.Forms.ToolStripMenuItem windowMenuItem; - private System.Windows.Forms.ToolStripMenuItem serverMenuItem; - private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; - private System.Windows.Forms.ToolStripMenuItem exitMenuItem; - private System.Windows.Forms.CheckBox saveLog; - private System.Windows.Forms.Label duplicates; - private System.Windows.Forms.Label label16; - private System.Windows.Forms.TextBox rootPrefix; - private System.Windows.Forms.Label label13; - private System.Windows.Forms.CheckBox useWatcher; - private System.Windows.Forms.TabPage tabPage4; - private System.Windows.Forms.CheckBox useUPnP; - private System.Windows.Forms.LinkLabel converterLinkLabel; - private System.Windows.Forms.Button donateButton; - private System.Windows.Forms.PictureBox pictureBox1; - private System.Windows.Forms.Label label19; - private System.Windows.Forms.LinkLabel linkLabel2; - private System.Windows.Forms.Label label18; - private System.Windows.Forms.LinkLabel linkLabel1; - private System.Windows.Forms.Label label17; - private System.Windows.Forms.Label appVersion; - private System.Windows.Forms.Label appName; - private System.Windows.Forms.Label label20; - private System.Windows.Forms.LinkLabel intLink; - private System.Windows.Forms.LinkLabel extLink; - private System.Windows.Forms.LinkLabel linkLabel5; - private System.Windows.Forms.LinkLabel linkLabel4; - private System.Windows.Forms.LinkLabel linkLabel3; - private System.Windows.Forms.TextBox databaseFileName; - private System.Windows.Forms.Label label21; - private System.Windows.Forms.TabPage tabPage5; - private System.Windows.Forms.CheckBox useHTTPAuth; - private System.Windows.Forms.DataGridView dataGridView1; - private System.Windows.Forms.CheckBox rememberClients; - private System.Windows.Forms.Label label22; - private System.Windows.Forms.ComboBox logVerbosity; - private System.Windows.Forms.Label statImages; - private System.Windows.Forms.Label label27; - private System.Windows.Forms.Label statBooks; - private System.Windows.Forms.Label label25; - private System.Windows.Forms.Label statRequests; - private System.Windows.Forms.Label label23; - private System.Windows.Forms.Label statUniqueClients; - private System.Windows.Forms.Label label26; - private System.Windows.Forms.Label statGoodLogins; - private System.Windows.Forms.Label label28; - private System.Windows.Forms.Label statWrongLogins; - private System.Windows.Forms.Label label30; - private System.Windows.Forms.Label label24; - private System.Windows.Forms.NumericUpDown wrongAttemptsCount; - private System.Windows.Forms.CheckBox banClients; - private System.Windows.Forms.Label statBannedClients; - private System.Windows.Forms.Label label31; - private System.Windows.Forms.Label label32; - private System.Windows.Forms.ComboBox updateCombo; - private System.Windows.Forms.ComboBox interfaceCombo; - private System.Windows.Forms.Label label29; - private System.Windows.Forms.Label label33; - private System.Windows.Forms.TextBox webPrefix; - private System.Windows.Forms.LinkLabel extWebLink; - private System.Windows.Forms.LinkLabel intWebLink; - private System.Windows.Forms.Label label34; - private System.Windows.Forms.Label label35; - private System.Windows.Forms.CheckBox useAbsoluteUri; - private System.Windows.Forms.Button viewLogFile; - private System.Windows.Forms.TabPage tabPage6; - private System.Windows.Forms.ComboBox newBooksPeriodCombo; - private System.Windows.Forms.Label label39; - private System.Windows.Forms.ComboBox sortOrderCombo; - private System.Windows.Forms.Label label38; - private System.Windows.Forms.NumericUpDown itemsPerWeb; - private System.Windows.Forms.Label label37; - private System.Windows.Forms.NumericUpDown itemsPerOPDS; - private System.Windows.Forms.Label label36; - private System.Windows.Forms.CheckBox checkBox1; - private System.Windows.Forms.LinkLabel linkLabel6; - private System.Windows.Forms.CheckBox checkBox2; - } -} - +namespace TinyOPDS +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + if (_watcher != null) _watcher.Dispose(); + if (_upnpController != null) _upnpController.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.databaseFileName = new System.Windows.Forms.TextBox(); + this.label21 = new System.Windows.Forms.Label(); + this.useWatcher = new System.Windows.Forms.CheckBox(); + this.duplicates = new System.Windows.Forms.Label(); + this.label16 = new System.Windows.Forms.Label(); + this.label15 = new System.Windows.Forms.Label(); + this.status = new System.Windows.Forms.Label(); + this.label14 = new System.Windows.Forms.Label(); + this.rate = new System.Windows.Forms.Label(); + this.label12 = new System.Windows.Forms.Label(); + this.elapsedTime = new System.Windows.Forms.Label(); + this.label10 = new System.Windows.Forms.Label(); + this.startTime = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.booksProcessed = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.invalidBooks = new System.Windows.Forms.Label(); + this.label9 = new System.Windows.Forms.Label(); + this.skippedBooks = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.booksFound = new System.Windows.Forms.Label(); + this.booksInDB = new System.Windows.Forms.Label(); + this.folderButton = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.label1 = new System.Windows.Forms.Label(); + this.scannerButton = new System.Windows.Forms.Button(); + this.libraryPath = new System.Windows.Forms.TextBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.useAbsoluteUri = new System.Windows.Forms.CheckBox(); + this.extWebLink = new System.Windows.Forms.LinkLabel(); + this.intWebLink = new System.Windows.Forms.LinkLabel(); + this.label34 = new System.Windows.Forms.Label(); + this.label35 = new System.Windows.Forms.Label(); + this.label33 = new System.Windows.Forms.Label(); + this.interfaceCombo = new System.Windows.Forms.ComboBox(); + this.label29 = new System.Windows.Forms.Label(); + this.statUniqueClients = new System.Windows.Forms.Label(); + this.label26 = new System.Windows.Forms.Label(); + this.statImages = new System.Windows.Forms.Label(); + this.label27 = new System.Windows.Forms.Label(); + this.statBooks = new System.Windows.Forms.Label(); + this.label25 = new System.Windows.Forms.Label(); + this.statRequests = new System.Windows.Forms.Label(); + this.label23 = new System.Windows.Forms.Label(); + this.extLink = new System.Windows.Forms.LinkLabel(); + this.intLink = new System.Windows.Forms.LinkLabel(); + this.label13 = new System.Windows.Forms.Label(); + this.extIPlabel = new System.Windows.Forms.Label(); + this.intIPlabel = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.serverButton = new System.Windows.Forms.Button(); + this.webPrefix = new System.Windows.Forms.TextBox(); + this.openPort = new System.Windows.Forms.CheckBox(); + this.serverPort = new System.Windows.Forms.TextBox(); + this.useUPnP = new System.Windows.Forms.CheckBox(); + this.rootPrefix = new System.Windows.Forms.TextBox(); + this.serverName = new System.Windows.Forms.TextBox(); + this.tabPage6 = new System.Windows.Forms.TabPage(); + this.checkBox2 = new System.Windows.Forms.CheckBox(); + this.checkBox1 = new System.Windows.Forms.CheckBox(); + this.newBooksPeriodCombo = new System.Windows.Forms.ComboBox(); + this.label39 = new System.Windows.Forms.Label(); + this.sortOrderCombo = new System.Windows.Forms.ComboBox(); + this.label38 = new System.Windows.Forms.Label(); + this.itemsPerWeb = new System.Windows.Forms.NumericUpDown(); + this.label37 = new System.Windows.Forms.Label(); + this.itemsPerOPDS = new System.Windows.Forms.NumericUpDown(); + this.label36 = new System.Windows.Forms.Label(); + this.tabPage5 = new System.Windows.Forms.TabPage(); + this.statBannedClients = new System.Windows.Forms.Label(); + this.label31 = new System.Windows.Forms.Label(); + this.label24 = new System.Windows.Forms.Label(); + this.statWrongLogins = new System.Windows.Forms.Label(); + this.label30 = new System.Windows.Forms.Label(); + this.statGoodLogins = new System.Windows.Forms.Label(); + this.label28 = new System.Windows.Forms.Label(); + this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.wrongAttemptsCount = new System.Windows.Forms.NumericUpDown(); + this.banClients = new System.Windows.Forms.CheckBox(); + this.rememberClients = new System.Windows.Forms.CheckBox(); + this.useHTTPAuth = new System.Windows.Forms.CheckBox(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.viewLogFile = new System.Windows.Forms.Button(); + this.label32 = new System.Windows.Forms.Label(); + this.updateCombo = new System.Windows.Forms.ComboBox(); + this.label22 = new System.Windows.Forms.Label(); + this.logVerbosity = new System.Windows.Forms.ComboBox(); + this.converterLinkLabel = new System.Windows.Forms.LinkLabel(); + this.label11 = new System.Windows.Forms.Label(); + this.langCombo = new System.Windows.Forms.ComboBox(); + this.label8 = new System.Windows.Forms.Label(); + this.convertorFolder = new System.Windows.Forms.Button(); + this.convertorPath = new System.Windows.Forms.TextBox(); + this.saveLog = new System.Windows.Forms.CheckBox(); + this.closeToTray = new System.Windows.Forms.CheckBox(); + this.startMinimized = new System.Windows.Forms.CheckBox(); + this.startWithWindows = new System.Windows.Forms.CheckBox(); + this.tabPage4 = new System.Windows.Forms.TabPage(); + this.linkLabel6 = new System.Windows.Forms.LinkLabel(); + this.linkLabel5 = new System.Windows.Forms.LinkLabel(); + this.linkLabel4 = new System.Windows.Forms.LinkLabel(); + this.linkLabel3 = new System.Windows.Forms.LinkLabel(); + this.label20 = new System.Windows.Forms.Label(); + this.label19 = new System.Windows.Forms.Label(); + this.linkLabel2 = new System.Windows.Forms.LinkLabel(); + this.label18 = new System.Windows.Forms.Label(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); + this.label17 = new System.Windows.Forms.Label(); + this.appVersion = new System.Windows.Forms.Label(); + this.appName = new System.Windows.Forms.Label(); + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + this.donateButton = new System.Windows.Forms.Button(); + this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); + this.windowMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.serverMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.exitMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage6.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.itemsPerWeb)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.itemsPerOPDS)).BeginInit(); + this.tabPage5.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.wrongAttemptsCount)).BeginInit(); + this.tabPage3.SuspendLayout(); + this.tabPage4.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.contextMenuStrip.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl1 + // + this.tabControl1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Controls.Add(this.tabPage6); + this.tabControl1.Controls.Add(this.tabPage5); + this.tabControl1.Controls.Add(this.tabPage3); + this.tabControl1.Controls.Add(this.tabPage4); + this.tabControl1.ItemSize = new System.Drawing.Size(91, 30); + this.tabControl1.Location = new System.Drawing.Point(-3, -1); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(481, 346); + this.tabControl1.TabIndex = 8; + // + // tabPage1 + // + this.tabPage1.BackColor = System.Drawing.SystemColors.Control; + this.tabPage1.Controls.Add(this.databaseFileName); + this.tabPage1.Controls.Add(this.label21); + this.tabPage1.Controls.Add(this.useWatcher); + this.tabPage1.Controls.Add(this.duplicates); + this.tabPage1.Controls.Add(this.label16); + this.tabPage1.Controls.Add(this.label15); + this.tabPage1.Controls.Add(this.status); + this.tabPage1.Controls.Add(this.label14); + this.tabPage1.Controls.Add(this.rate); + this.tabPage1.Controls.Add(this.label12); + this.tabPage1.Controls.Add(this.elapsedTime); + this.tabPage1.Controls.Add(this.label10); + this.tabPage1.Controls.Add(this.startTime); + this.tabPage1.Controls.Add(this.label6); + this.tabPage1.Controls.Add(this.booksProcessed); + this.tabPage1.Controls.Add(this.label5); + this.tabPage1.Controls.Add(this.invalidBooks); + this.tabPage1.Controls.Add(this.label9); + this.tabPage1.Controls.Add(this.skippedBooks); + this.tabPage1.Controls.Add(this.label7); + this.tabPage1.Controls.Add(this.booksFound); + this.tabPage1.Controls.Add(this.booksInDB); + this.tabPage1.Controls.Add(this.folderButton); + this.tabPage1.Controls.Add(this.label2); + this.tabPage1.Controls.Add(this.label1); + this.tabPage1.Controls.Add(this.scannerButton); + this.tabPage1.Controls.Add(this.libraryPath); + this.tabPage1.Location = new System.Drawing.Point(4, 34); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(473, 308); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Scanner"; + // + // databaseFileName + // + this.databaseFileName.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.databaseFileName.Location = new System.Drawing.Point(125, 66); + this.databaseFileName.Name = "databaseFileName"; + this.databaseFileName.ReadOnly = true; + this.databaseFileName.Size = new System.Drawing.Size(336, 20); + this.databaseFileName.TabIndex = 32; + // + // label21 + // + this.label21.AutoSize = true; + this.label21.Location = new System.Drawing.Point(15, 70); + this.label21.Name = "label21"; + this.label21.Size = new System.Drawing.Size(104, 13); + this.label21.TabIndex = 31; + this.label21.Text = "Database file name: "; + // + // useWatcher + // + this.useWatcher.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.useWatcher.AutoSize = true; + this.useWatcher.Checked = global::TinyOPDS.Properties.Settings.Default.WatchLibrary; + this.useWatcher.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "WatchLibrary", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.useWatcher.Location = new System.Drawing.Point(321, 34); + this.useWatcher.Name = "useWatcher"; + this.useWatcher.Size = new System.Drawing.Size(135, 17); + this.useWatcher.TabIndex = 30; + this.useWatcher.Text = "Monitor library changes"; + this.useWatcher.UseVisualStyleBackColor = true; + this.useWatcher.CheckedChanged += new System.EventHandler(this.useWatcher_CheckedChanged); + // + // duplicates + // + this.duplicates.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.duplicates.AutoSize = true; + this.duplicates.Location = new System.Drawing.Point(122, 228); + this.duplicates.MinimumSize = new System.Drawing.Size(50, 0); + this.duplicates.Name = "duplicates"; + this.duplicates.Size = new System.Drawing.Size(50, 13); + this.duplicates.TabIndex = 29; + this.duplicates.Text = "0"; + // + // label16 + // + this.label16.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label16.AutoSize = true; + this.label16.Location = new System.Drawing.Point(15, 228); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(60, 13); + this.label16.TabIndex = 28; + this.label16.Text = "Duplicates:"; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Location = new System.Drawing.Point(14, 16); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(105, 13); + this.label15.TabIndex = 27; + this.label15.Text = "Path to books folder:"; + // + // status + // + this.status.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.status.AutoSize = true; + this.status.Location = new System.Drawing.Point(360, 228); + this.status.MinimumSize = new System.Drawing.Size(50, 0); + this.status.Name = "status"; + this.status.Size = new System.Drawing.Size(58, 13); + this.status.TabIndex = 26; + this.status.Text = "STOPPED"; + // + // label14 + // + this.label14.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(253, 228); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(40, 13); + this.label14.TabIndex = 25; + this.label14.Text = "Status:"; + // + // rate + // + this.rate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.rate.AutoSize = true; + this.rate.Location = new System.Drawing.Point(360, 202); + this.rate.MinimumSize = new System.Drawing.Size(50, 0); + this.rate.Name = "rate"; + this.rate.Size = new System.Drawing.Size(66, 13); + this.rate.TabIndex = 24; + this.rate.Text = "0 books/min"; + // + // label12 + // + this.label12.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(253, 202); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(33, 13); + this.label12.TabIndex = 23; + this.label12.Text = "Rate:"; + // + // elapsedTime + // + this.elapsedTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.elapsedTime.AutoSize = true; + this.elapsedTime.Location = new System.Drawing.Point(360, 176); + this.elapsedTime.MinimumSize = new System.Drawing.Size(50, 0); + this.elapsedTime.Name = "elapsedTime"; + this.elapsedTime.Size = new System.Drawing.Size(50, 13); + this.elapsedTime.TabIndex = 22; + this.elapsedTime.Text = "00:00:00"; + // + // label10 + // + this.label10.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(253, 176); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(70, 13); + this.label10.TabIndex = 21; + this.label10.Text = "Elapsed time:"; + // + // startTime + // + this.startTime.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.startTime.AutoSize = true; + this.startTime.Location = new System.Drawing.Point(360, 150); + this.startTime.MinimumSize = new System.Drawing.Size(50, 0); + this.startTime.Name = "startTime"; + this.startTime.Size = new System.Drawing.Size(50, 13); + this.startTime.TabIndex = 20; + this.startTime.Text = "00:00:00"; + // + // label6 + // + this.label6.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(253, 150); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(54, 13); + this.label6.TabIndex = 19; + this.label6.Text = "Start time:"; + // + // booksProcessed + // + this.booksProcessed.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.booksProcessed.AutoSize = true; + this.booksProcessed.Location = new System.Drawing.Point(123, 254); + this.booksProcessed.MinimumSize = new System.Drawing.Size(50, 0); + this.booksProcessed.Name = "booksProcessed"; + this.booksProcessed.Size = new System.Drawing.Size(50, 13); + this.booksProcessed.TabIndex = 18; + this.booksProcessed.Text = "0"; + // + // label5 + // + this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(15, 254); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(92, 13); + this.label5.TabIndex = 17; + this.label5.Text = "Books processed:"; + // + // invalidBooks + // + this.invalidBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.invalidBooks.AutoSize = true; + this.invalidBooks.Location = new System.Drawing.Point(123, 176); + this.invalidBooks.MinimumSize = new System.Drawing.Size(50, 0); + this.invalidBooks.Name = "invalidBooks"; + this.invalidBooks.Size = new System.Drawing.Size(50, 13); + this.invalidBooks.TabIndex = 16; + this.invalidBooks.Text = "0"; + // + // label9 + // + this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(15, 176); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(73, 13); + this.label9.TabIndex = 15; + this.label9.Text = "Invalid books:"; + // + // skippedBooks + // + this.skippedBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.skippedBooks.AutoSize = true; + this.skippedBooks.Location = new System.Drawing.Point(123, 202); + this.skippedBooks.MinimumSize = new System.Drawing.Size(50, 0); + this.skippedBooks.Name = "skippedBooks"; + this.skippedBooks.Size = new System.Drawing.Size(50, 13); + this.skippedBooks.TabIndex = 14; + this.skippedBooks.Text = "0"; + // + // label7 + // + this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(15, 202); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(81, 13); + this.label7.TabIndex = 13; + this.label7.Text = "Skipped books:"; + // + // booksFound + // + this.booksFound.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.booksFound.AutoSize = true; + this.booksFound.Location = new System.Drawing.Point(123, 150); + this.booksFound.MinimumSize = new System.Drawing.Size(50, 0); + this.booksFound.Name = "booksFound"; + this.booksFound.Size = new System.Drawing.Size(79, 13); + this.booksFound.TabIndex = 12; + this.booksFound.Text = "fb2: 0 epub: 0"; + // + // booksInDB + // + this.booksInDB.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.booksInDB.AutoSize = true; + this.booksInDB.Location = new System.Drawing.Point(123, 111); + this.booksInDB.MinimumSize = new System.Drawing.Size(50, 0); + this.booksInDB.Name = "booksInDB"; + this.booksInDB.Size = new System.Drawing.Size(127, 13); + this.booksInDB.TabIndex = 11; + this.booksInDB.Text = "0 fb2: 0 epub: 0"; + // + // folderButton + // + this.folderButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.folderButton.Image = global::TinyOPDS.Properties.Resources.folder; + this.folderButton.Location = new System.Drawing.Point(281, 30); + this.folderButton.Name = "folderButton"; + this.folderButton.Size = new System.Drawing.Size(29, 23); + this.folderButton.TabIndex = 10; + this.folderButton.UseVisualStyleBackColor = true; + this.folderButton.Click += new System.EventHandler(this.folderButton_Click); + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(15, 111); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(101, 13); + this.label2.TabIndex = 9; + this.label2.Text = "Books in database: "; + // + // label1 + // + this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(15, 150); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(70, 13); + this.label1.TabIndex = 8; + this.label1.Text = "Books found:"; + // + // scannerButton + // + this.scannerButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.scannerButton.Location = new System.Drawing.Point(255, 260); + this.scannerButton.Name = "scannerButton"; + this.scannerButton.Size = new System.Drawing.Size(210, 40); + this.scannerButton.TabIndex = 7; + this.scannerButton.Text = "Start scanning"; + this.scannerButton.UseVisualStyleBackColor = true; + this.scannerButton.Click += new System.EventHandler(this.scannerButton_Click); + // + // libraryPath + // + this.libraryPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.libraryPath.Location = new System.Drawing.Point(17, 32); + this.libraryPath.Name = "libraryPath"; + this.libraryPath.Size = new System.Drawing.Size(259, 20); + this.libraryPath.TabIndex = 6; + this.libraryPath.TextChanged += new System.EventHandler(this.libraryPath_TextChanged); + this.libraryPath.Validated += new System.EventHandler(this.libraryPath_Validated); + // + // tabPage2 + // + this.tabPage2.BackColor = System.Drawing.SystemColors.Control; + this.tabPage2.Controls.Add(this.useAbsoluteUri); + this.tabPage2.Controls.Add(this.extWebLink); + this.tabPage2.Controls.Add(this.intWebLink); + this.tabPage2.Controls.Add(this.label34); + this.tabPage2.Controls.Add(this.label35); + this.tabPage2.Controls.Add(this.label33); + this.tabPage2.Controls.Add(this.interfaceCombo); + this.tabPage2.Controls.Add(this.label29); + this.tabPage2.Controls.Add(this.statUniqueClients); + this.tabPage2.Controls.Add(this.label26); + this.tabPage2.Controls.Add(this.statImages); + this.tabPage2.Controls.Add(this.label27); + this.tabPage2.Controls.Add(this.statBooks); + this.tabPage2.Controls.Add(this.label25); + this.tabPage2.Controls.Add(this.statRequests); + this.tabPage2.Controls.Add(this.label23); + this.tabPage2.Controls.Add(this.extLink); + this.tabPage2.Controls.Add(this.intLink); + this.tabPage2.Controls.Add(this.label13); + this.tabPage2.Controls.Add(this.extIPlabel); + this.tabPage2.Controls.Add(this.intIPlabel); + this.tabPage2.Controls.Add(this.label4); + this.tabPage2.Controls.Add(this.label3); + this.tabPage2.Controls.Add(this.serverButton); + this.tabPage2.Controls.Add(this.webPrefix); + this.tabPage2.Controls.Add(this.openPort); + this.tabPage2.Controls.Add(this.serverPort); + this.tabPage2.Controls.Add(this.useUPnP); + this.tabPage2.Controls.Add(this.rootPrefix); + this.tabPage2.Controls.Add(this.serverName); + this.tabPage2.Location = new System.Drawing.Point(4, 34); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(473, 308); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Server"; + // + // useAbsoluteUri + // + this.useAbsoluteUri.AutoSize = true; + this.useAbsoluteUri.Checked = global::TinyOPDS.Properties.Settings.Default.UseAbsoluteUri; + this.useAbsoluteUri.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseAbsoluteUri", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.useAbsoluteUri.Location = new System.Drawing.Point(340, 71); + this.useAbsoluteUri.Name = "useAbsoluteUri"; + this.useAbsoluteUri.Size = new System.Drawing.Size(91, 17); + this.useAbsoluteUri.TabIndex = 54; + this.useAbsoluteUri.Text = "Absolute links"; + this.useAbsoluteUri.UseVisualStyleBackColor = true; + // + // extWebLink + // + this.extWebLink.Location = new System.Drawing.Point(252, 188); + this.extWebLink.Name = "extWebLink"; + this.extWebLink.Size = new System.Drawing.Size(203, 13); + this.extWebLink.TabIndex = 53; + this.extWebLink.TabStop = true; + this.extWebLink.Text = "- - - - - -"; + this.extWebLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + // + // intWebLink + // + this.intWebLink.Location = new System.Drawing.Point(252, 148); + this.intWebLink.Name = "intWebLink"; + this.intWebLink.Size = new System.Drawing.Size(203, 13); + this.intWebLink.TabIndex = 52; + this.intWebLink.TabStop = true; + this.intWebLink.Text = "- - - - - -"; + this.intWebLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + // + // label34 + // + this.label34.AutoSize = true; + this.label34.Location = new System.Drawing.Point(252, 173); + this.label34.Name = "label34"; + this.label34.Size = new System.Drawing.Size(96, 13); + this.label34.TabIndex = 51; + this.label34.Text = "External web URL:"; + // + // label35 + // + this.label35.AutoSize = true; + this.label35.Location = new System.Drawing.Point(252, 133); + this.label35.Name = "label35"; + this.label35.Size = new System.Drawing.Size(84, 13); + this.label35.TabIndex = 50; + this.label35.Text = "Local web URL:"; + // + // label33 + // + this.label33.AutoSize = true; + this.label33.Location = new System.Drawing.Point(252, 106); + this.label33.Name = "label33"; + this.label33.Size = new System.Drawing.Size(54, 13); + this.label33.TabIndex = 48; + this.label33.Text = "Web root:"; + // + // interfaceCombo + // + this.interfaceCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.interfaceCombo.FormattingEnabled = true; + this.interfaceCombo.Location = new System.Drawing.Point(255, 35); + this.interfaceCombo.Name = "interfaceCombo"; + this.interfaceCombo.Size = new System.Drawing.Size(121, 21); + this.interfaceCombo.TabIndex = 47; + this.interfaceCombo.SelectedIndexChanged += new System.EventHandler(this.interfaceCombo_SelectedIndexChanged); + // + // label29 + // + this.label29.AutoSize = true; + this.label29.Location = new System.Drawing.Point(252, 16); + this.label29.Name = "label29"; + this.label29.Size = new System.Drawing.Size(94, 13); + this.label29.TabIndex = 46; + this.label29.Text = "Network interface:"; + // + // statUniqueClients + // + this.statUniqueClients.AutoSize = true; + this.statUniqueClients.Location = new System.Drawing.Point(148, 256); + this.statUniqueClients.Name = "statUniqueClients"; + this.statUniqueClients.Size = new System.Drawing.Size(13, 13); + this.statUniqueClients.TabIndex = 45; + this.statUniqueClients.Text = "0"; + // + // label26 + // + this.label26.AutoSize = true; + this.label26.Location = new System.Drawing.Point(19, 256); + this.label26.Name = "label26"; + this.label26.Size = new System.Drawing.Size(77, 13); + this.label26.TabIndex = 44; + this.label26.Text = "Unique clients:"; + // + // statImages + // + this.statImages.AutoSize = true; + this.statImages.Location = new System.Drawing.Point(445, 228); + this.statImages.Name = "statImages"; + this.statImages.Size = new System.Drawing.Size(13, 13); + this.statImages.TabIndex = 43; + this.statImages.Text = "0"; + // + // label27 + // + this.label27.AutoSize = true; + this.label27.Location = new System.Drawing.Point(352, 228); + this.label27.Name = "label27"; + this.label27.Size = new System.Drawing.Size(67, 13); + this.label27.TabIndex = 42; + this.label27.Text = "Images sent:"; + // + // statBooks + // + this.statBooks.AutoSize = true; + this.statBooks.Location = new System.Drawing.Point(289, 228); + this.statBooks.Name = "statBooks"; + this.statBooks.Size = new System.Drawing.Size(13, 13); + this.statBooks.TabIndex = 41; + this.statBooks.Text = "0"; + // + // label25 + // + this.label25.AutoSize = true; + this.label25.Location = new System.Drawing.Point(200, 228); + this.label25.Name = "label25"; + this.label25.Size = new System.Drawing.Size(63, 13); + this.label25.TabIndex = 40; + this.label25.Text = "Books sent:"; + // + // statRequests + // + this.statRequests.AutoSize = true; + this.statRequests.Location = new System.Drawing.Point(148, 228); + this.statRequests.Name = "statRequests"; + this.statRequests.Size = new System.Drawing.Size(13, 13); + this.statRequests.TabIndex = 39; + this.statRequests.Text = "0"; + // + // label23 + // + this.label23.AutoSize = true; + this.label23.Location = new System.Drawing.Point(19, 228); + this.label23.Name = "label23"; + this.label23.Size = new System.Drawing.Size(77, 13); + this.label23.TabIndex = 38; + this.label23.Text = "Total requests:"; + // + // extLink + // + this.extLink.Location = new System.Drawing.Point(19, 188); + this.extLink.Name = "extLink"; + this.extLink.Size = new System.Drawing.Size(214, 13); + this.extLink.TabIndex = 37; + this.extLink.TabStop = true; + this.extLink.Text = "- - - - - -"; + this.extLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + // + // intLink + // + this.intLink.Location = new System.Drawing.Point(19, 148); + this.intLink.Name = "intLink"; + this.intLink.Size = new System.Drawing.Size(214, 13); + this.intLink.TabIndex = 36; + this.intLink.TabStop = true; + this.intLink.Text = "- - - - - -"; + this.intLink.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(20, 106); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(61, 13); + this.label13.TabIndex = 18; + this.label13.Text = "OPDS root:"; + // + // extIPlabel + // + this.extIPlabel.AutoSize = true; + this.extIPlabel.Location = new System.Drawing.Point(19, 173); + this.extIPlabel.Name = "extIPlabel"; + this.extIPlabel.Size = new System.Drawing.Size(106, 13); + this.extIPlabel.TabIndex = 14; + this.extIPlabel.Text = "External OPDS URL:"; + // + // intIPlabel + // + this.intIPlabel.AutoSize = true; + this.intIPlabel.Location = new System.Drawing.Point(19, 133); + this.intIPlabel.Name = "intIPlabel"; + this.intIPlabel.Size = new System.Drawing.Size(94, 13); + this.intIPlabel.TabIndex = 13; + this.intIPlabel.Text = "Local OPDS URL:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(19, 16); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(70, 13); + this.label4.TabIndex = 11; + this.label4.Text = "Server name:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(405, 16); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(29, 13); + this.label3.TabIndex = 9; + this.label3.Text = "Port:"; + // + // serverButton + // + this.serverButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.serverButton.Location = new System.Drawing.Point(255, 260); + this.serverButton.Name = "serverButton"; + this.serverButton.Size = new System.Drawing.Size(210, 40); + this.serverButton.TabIndex = 8; + this.serverButton.Text = "Start server"; + this.serverButton.Click += new System.EventHandler(this.serverButton_Click); + // + // webPrefix + // + this.webPrefix.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "HttpPrefix", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.webPrefix.Location = new System.Drawing.Point(340, 103); + this.webPrefix.Name = "webPrefix"; + this.webPrefix.Size = new System.Drawing.Size(118, 20); + this.webPrefix.TabIndex = 49; + this.webPrefix.Text = global::TinyOPDS.Properties.Settings.Default.HttpPrefix; + this.webPrefix.TextChanged += new System.EventHandler(this.rootPrefix_TextChanged); + // + // openPort + // + this.openPort.AutoSize = true; + this.openPort.Checked = global::TinyOPDS.Properties.Settings.Default.OpenNATPort; + this.openPort.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "OpenNATPort", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.openPort.Enabled = false; + this.openPort.Location = new System.Drawing.Point(160, 71); + this.openPort.Name = "openPort"; + this.openPort.Size = new System.Drawing.Size(130, 17); + this.openPort.TabIndex = 15; + this.openPort.Text = "Forward port on router"; + this.openPort.UseVisualStyleBackColor = true; + this.openPort.CheckedChanged += new System.EventHandler(this.openPort_CheckedChanged); + // + // serverPort + // + this.serverPort.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "ServerPort", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.serverPort.Location = new System.Drawing.Point(408, 36); + this.serverPort.Name = "serverPort"; + this.serverPort.Size = new System.Drawing.Size(50, 20); + this.serverPort.TabIndex = 10; + this.serverPort.Text = global::TinyOPDS.Properties.Settings.Default.ServerPort; + this.serverPort.Validated += new System.EventHandler(this.serverPort_Validated); + // + // useUPnP + // + this.useUPnP.AutoSize = true; + this.useUPnP.Checked = global::TinyOPDS.Properties.Settings.Default.UseUPnP; + this.useUPnP.CheckState = System.Windows.Forms.CheckState.Checked; + this.useUPnP.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseUPnP", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.useUPnP.Location = new System.Drawing.Point(22, 71); + this.useUPnP.Name = "useUPnP"; + this.useUPnP.Size = new System.Drawing.Size(76, 17); + this.useUPnP.TabIndex = 35; + this.useUPnP.Text = "Use UPnP"; + this.useUPnP.UseVisualStyleBackColor = true; + this.useUPnP.CheckStateChanged += new System.EventHandler(this.useUPnP_CheckStateChanged); + // + // rootPrefix + // + this.rootPrefix.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "RootPrefix", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rootPrefix.Location = new System.Drawing.Point(115, 103); + this.rootPrefix.Name = "rootPrefix"; + this.rootPrefix.Size = new System.Drawing.Size(118, 20); + this.rootPrefix.TabIndex = 19; + this.rootPrefix.Text = global::TinyOPDS.Properties.Settings.Default.RootPrefix; + this.rootPrefix.TextChanged += new System.EventHandler(this.rootPrefix_TextChanged); + // + // serverName + // + this.serverName.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::TinyOPDS.Properties.Settings.Default, "ServerName", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.serverName.Location = new System.Drawing.Point(22, 35); + this.serverName.Name = "serverName"; + this.serverName.Size = new System.Drawing.Size(211, 20); + this.serverName.TabIndex = 12; + this.serverName.Text = global::TinyOPDS.Properties.Settings.Default.ServerName; + // + // tabPage6 + // + this.tabPage6.BackColor = System.Drawing.SystemColors.Control; + this.tabPage6.Controls.Add(this.checkBox2); + this.tabPage6.Controls.Add(this.checkBox1); + this.tabPage6.Controls.Add(this.newBooksPeriodCombo); + this.tabPage6.Controls.Add(this.label39); + this.tabPage6.Controls.Add(this.sortOrderCombo); + this.tabPage6.Controls.Add(this.label38); + this.tabPage6.Controls.Add(this.itemsPerWeb); + this.tabPage6.Controls.Add(this.label37); + this.tabPage6.Controls.Add(this.itemsPerOPDS); + this.tabPage6.Controls.Add(this.label36); + this.tabPage6.Location = new System.Drawing.Point(4, 34); + this.tabPage6.Name = "tabPage6"; + this.tabPage6.Size = new System.Drawing.Size(473, 308); + this.tabPage6.TabIndex = 5; + this.tabPage6.Text = "OPDS catalog"; + // + // checkBox2 + // + this.checkBox2.AutoSize = true; + this.checkBox2.Checked = global::TinyOPDS.Properties.Settings.Default.UseAuthorsAliases; + this.checkBox2.CheckState = System.Windows.Forms.CheckState.Checked; + this.checkBox2.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseAuthorsAliases", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.checkBox2.Location = new System.Drawing.Point(25, 162); + this.checkBox2.Name = "checkBox2"; + this.checkBox2.Size = new System.Drawing.Size(118, 17); + this.checkBox2.TabIndex = 9; + this.checkBox2.Text = "Use authors aliases"; + this.checkBox2.UseVisualStyleBackColor = true; + this.checkBox2.CheckedChanged += new System.EventHandler(this.checkBox_CheckedChanged); + // + // checkBox1 + // + this.checkBox1.AutoSize = true; + this.checkBox1.Checked = global::TinyOPDS.Properties.Settings.Default.LowMemoryProfile; + this.checkBox1.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "LowMemoryProfile", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.checkBox1.Location = new System.Drawing.Point(25, 202); + this.checkBox1.Name = "checkBox1"; + this.checkBox1.Size = new System.Drawing.Size(274, 17); + this.checkBox1.TabIndex = 8; + this.checkBox1.Text = "\"Low memory\" model (do not load book descriptions)"; + this.checkBox1.UseVisualStyleBackColor = true; + this.checkBox1.CheckedChanged += new System.EventHandler(this.checkBox_CheckedChanged); + // + // newBooksPeriodCombo + // + this.newBooksPeriodCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.newBooksPeriodCombo.FormattingEnabled = true; + this.newBooksPeriodCombo.Items.AddRange(new object[] { + "one week", + "two weeks", + "three weeks", + "month", + "month and half", + "two month", + "three month"}); + this.newBooksPeriodCombo.Location = new System.Drawing.Point(254, 115); + this.newBooksPeriodCombo.Name = "newBooksPeriodCombo"; + this.newBooksPeriodCombo.Size = new System.Drawing.Size(176, 21); + this.newBooksPeriodCombo.TabIndex = 7; + this.newBooksPeriodCombo.SelectedIndexChanged += new System.EventHandler(this.newBooksPeriodCombo_SelectedIndexChanged); + // + // label39 + // + this.label39.AutoSize = true; + this.label39.Location = new System.Drawing.Point(251, 95); + this.label39.Name = "label39"; + this.label39.Size = new System.Drawing.Size(139, 13); + this.label39.TabIndex = 6; + this.label39.Text = "\"New books\" check period:"; + // + // sortOrderCombo + // + this.sortOrderCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.sortOrderCombo.FormattingEnabled = true; + this.sortOrderCombo.Items.AddRange(new object[] { + "Latin first", + "Cyrillic first"}); + this.sortOrderCombo.Location = new System.Drawing.Point(25, 115); + this.sortOrderCombo.Name = "sortOrderCombo"; + this.sortOrderCombo.Size = new System.Drawing.Size(176, 21); + this.sortOrderCombo.TabIndex = 5; + this.sortOrderCombo.SelectedIndexChanged += new System.EventHandler(this.sortOrderCombo_SelectedIndexChanged); + // + // label38 + // + this.label38.AutoSize = true; + this.label38.Location = new System.Drawing.Point(22, 95); + this.label38.Name = "label38"; + this.label38.Size = new System.Drawing.Size(82, 13); + this.label38.TabIndex = 4; + this.label38.Text = "Items sort order:"; + // + // itemsPerWeb + // + this.itemsPerWeb.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::TinyOPDS.Properties.Settings.Default, "ItemsPerWebPage", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.itemsPerWeb.Location = new System.Drawing.Point(254, 48); + this.itemsPerWeb.Maximum = new decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.itemsPerWeb.Minimum = new decimal(new int[] { + 30, + 0, + 0, + 0}); + this.itemsPerWeb.Name = "itemsPerWeb"; + this.itemsPerWeb.Size = new System.Drawing.Size(55, 20); + this.itemsPerWeb.TabIndex = 3; + this.itemsPerWeb.Value = global::TinyOPDS.Properties.Settings.Default.ItemsPerWebPage; + // + // label37 + // + this.label37.AutoSize = true; + this.label37.Location = new System.Drawing.Point(251, 25); + this.label37.Name = "label37"; + this.label37.Size = new System.Drawing.Size(103, 13); + this.label37.TabIndex = 2; + this.label37.Text = "Items per web page:"; + // + // itemsPerOPDS + // + this.itemsPerOPDS.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::TinyOPDS.Properties.Settings.Default, "ItemsPerOPDSPage", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.itemsPerOPDS.Location = new System.Drawing.Point(25, 48); + this.itemsPerOPDS.Maximum = new decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.itemsPerOPDS.Minimum = new decimal(new int[] { + 30, + 0, + 0, + 0}); + this.itemsPerOPDS.Name = "itemsPerOPDS"; + this.itemsPerOPDS.Size = new System.Drawing.Size(55, 20); + this.itemsPerOPDS.TabIndex = 1; + this.itemsPerOPDS.Value = global::TinyOPDS.Properties.Settings.Default.ItemsPerOPDSPage; + // + // label36 + // + this.label36.AutoSize = true; + this.label36.Location = new System.Drawing.Point(22, 26); + this.label36.Name = "label36"; + this.label36.Size = new System.Drawing.Size(113, 13); + this.label36.TabIndex = 0; + this.label36.Text = "Items per OPDS page:"; + // + // tabPage5 + // + this.tabPage5.BackColor = System.Drawing.SystemColors.Control; + this.tabPage5.Controls.Add(this.statBannedClients); + this.tabPage5.Controls.Add(this.label31); + this.tabPage5.Controls.Add(this.label24); + this.tabPage5.Controls.Add(this.statWrongLogins); + this.tabPage5.Controls.Add(this.label30); + this.tabPage5.Controls.Add(this.statGoodLogins); + this.tabPage5.Controls.Add(this.label28); + this.tabPage5.Controls.Add(this.dataGridView1); + this.tabPage5.Controls.Add(this.wrongAttemptsCount); + this.tabPage5.Controls.Add(this.banClients); + this.tabPage5.Controls.Add(this.rememberClients); + this.tabPage5.Controls.Add(this.useHTTPAuth); + this.tabPage5.Location = new System.Drawing.Point(4, 34); + this.tabPage5.Name = "tabPage5"; + this.tabPage5.Padding = new System.Windows.Forms.Padding(3); + this.tabPage5.Size = new System.Drawing.Size(473, 308); + this.tabPage5.TabIndex = 4; + this.tabPage5.Text = "Authentication"; + // + // statBannedClients + // + this.statBannedClients.AutoSize = true; + this.statBannedClients.Location = new System.Drawing.Point(429, 254); + this.statBannedClients.Name = "statBannedClients"; + this.statBannedClients.Size = new System.Drawing.Size(13, 13); + this.statBannedClients.TabIndex = 48; + this.statBannedClients.Text = "0"; + this.statBannedClients.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label31 + // + this.label31.AutoSize = true; + this.label31.Location = new System.Drawing.Point(334, 254); + this.label31.Name = "label31"; + this.label31.Size = new System.Drawing.Size(80, 13); + this.label31.TabIndex = 47; + this.label31.Text = "Banned clients:"; + // + // label24 + // + this.label24.AutoSize = true; + this.label24.Location = new System.Drawing.Point(329, 51); + this.label24.Name = "label24"; + this.label24.Size = new System.Drawing.Size(75, 13); + this.label24.TabIndex = 46; + this.label24.Text = "failed attempts"; + // + // statWrongLogins + // + this.statWrongLogins.AutoSize = true; + this.statWrongLogins.Location = new System.Drawing.Point(279, 254); + this.statWrongLogins.Name = "statWrongLogins"; + this.statWrongLogins.Size = new System.Drawing.Size(13, 13); + this.statWrongLogins.TabIndex = 43; + this.statWrongLogins.Text = "0"; + this.statWrongLogins.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label30 + // + this.label30.AutoSize = true; + this.label30.Location = new System.Drawing.Point(185, 254); + this.label30.Name = "label30"; + this.label30.Size = new System.Drawing.Size(68, 13); + this.label30.TabIndex = 42; + this.label30.Text = "Failed logins:"; + // + // statGoodLogins + // + this.statGoodLogins.AutoSize = true; + this.statGoodLogins.Location = new System.Drawing.Point(128, 254); + this.statGoodLogins.Name = "statGoodLogins"; + this.statGoodLogins.Size = new System.Drawing.Size(13, 13); + this.statGoodLogins.TabIndex = 41; + this.statGoodLogins.Text = "0"; + this.statGoodLogins.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label28 + // + this.label28.AutoSize = true; + this.label28.Location = new System.Drawing.Point(23, 254); + this.label28.Name = "label28"; + this.label28.Size = new System.Drawing.Size(92, 13); + this.label28.TabIndex = 40; + this.label28.Text = "Successful logins:"; + // + // dataGridView1 + // + this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dataGridView1.Location = new System.Drawing.Point(26, 83); + this.dataGridView1.Name = "dataGridView1"; + this.dataGridView1.Size = new System.Drawing.Size(426, 150); + this.dataGridView1.TabIndex = 1; + this.dataGridView1.CellFormatting += new System.Windows.Forms.DataGridViewCellFormattingEventHandler(this.dataGridView1_CellFormatting); + // + // wrongAttemptsCount + // + this.wrongAttemptsCount.DataBindings.Add(new System.Windows.Forms.Binding("Value", global::TinyOPDS.Properties.Settings.Default, "WrongAttemptsCount", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.wrongAttemptsCount.Location = new System.Drawing.Point(283, 46); + this.wrongAttemptsCount.Minimum = new decimal(new int[] { + 3, + 0, + 0, + 0}); + this.wrongAttemptsCount.Name = "wrongAttemptsCount"; + this.wrongAttemptsCount.Size = new System.Drawing.Size(40, 20); + this.wrongAttemptsCount.TabIndex = 45; + this.wrongAttemptsCount.Value = global::TinyOPDS.Properties.Settings.Default.WrongAttemptsCount; + // + // banClients + // + this.banClients.AutoSize = true; + this.banClients.Checked = global::TinyOPDS.Properties.Settings.Default.BanClients; + this.banClients.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "BanClients", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.banClients.Location = new System.Drawing.Point(283, 23); + this.banClients.Name = "banClients"; + this.banClients.Size = new System.Drawing.Size(102, 17); + this.banClients.TabIndex = 44; + this.banClients.Text = "Ban clients after"; + this.banClients.UseVisualStyleBackColor = true; + this.banClients.CheckedChanged += new System.EventHandler(this.banClients_CheckedChanged); + // + // rememberClients + // + this.rememberClients.AutoSize = true; + this.rememberClients.Checked = global::TinyOPDS.Properties.Settings.Default.RememberClients; + this.rememberClients.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "RememberClients", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.rememberClients.Location = new System.Drawing.Point(26, 51); + this.rememberClients.Name = "rememberClients"; + this.rememberClients.Size = new System.Drawing.Size(162, 17); + this.rememberClients.TabIndex = 2; + this.rememberClients.Text = "Remember authorized clients"; + this.rememberClients.UseVisualStyleBackColor = true; + // + // useHTTPAuth + // + this.useHTTPAuth.AutoSize = true; + this.useHTTPAuth.Checked = global::TinyOPDS.Properties.Settings.Default.UseHTTPAuth; + this.useHTTPAuth.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "UseHTTPAuth", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.useHTTPAuth.Location = new System.Drawing.Point(26, 23); + this.useHTTPAuth.Name = "useHTTPAuth"; + this.useHTTPAuth.Size = new System.Drawing.Size(175, 17); + this.useHTTPAuth.TabIndex = 0; + this.useHTTPAuth.Text = "Use HTTP basic authentication"; + this.useHTTPAuth.UseVisualStyleBackColor = true; + this.useHTTPAuth.CheckedChanged += new System.EventHandler(this.useHTTPAuth_CheckedChanged); + // + // tabPage3 + // + this.tabPage3.BackColor = System.Drawing.SystemColors.Control; + this.tabPage3.Controls.Add(this.viewLogFile); + this.tabPage3.Controls.Add(this.label32); + this.tabPage3.Controls.Add(this.updateCombo); + this.tabPage3.Controls.Add(this.label22); + this.tabPage3.Controls.Add(this.logVerbosity); + this.tabPage3.Controls.Add(this.converterLinkLabel); + this.tabPage3.Controls.Add(this.label11); + this.tabPage3.Controls.Add(this.langCombo); + this.tabPage3.Controls.Add(this.label8); + this.tabPage3.Controls.Add(this.convertorFolder); + this.tabPage3.Controls.Add(this.convertorPath); + this.tabPage3.Controls.Add(this.saveLog); + this.tabPage3.Controls.Add(this.closeToTray); + this.tabPage3.Controls.Add(this.startMinimized); + this.tabPage3.Controls.Add(this.startWithWindows); + this.tabPage3.Location = new System.Drawing.Point(4, 34); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Padding = new System.Windows.Forms.Padding(3); + this.tabPage3.Size = new System.Drawing.Size(473, 308); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "Miscellaneous"; + // + // viewLogFile + // + this.viewLogFile.Location = new System.Drawing.Point(299, 218); + this.viewLogFile.Name = "viewLogFile"; + this.viewLogFile.Size = new System.Drawing.Size(130, 23); + this.viewLogFile.TabIndex = 39; + this.viewLogFile.Text = "View log file"; + this.viewLogFile.UseVisualStyleBackColor = true; + this.viewLogFile.Click += new System.EventHandler(this.viewLogFile_Click); + // + // label32 + // + this.label32.AutoSize = true; + this.label32.Location = new System.Drawing.Point(298, 143); + this.label32.Name = "label32"; + this.label32.Size = new System.Drawing.Size(92, 13); + this.label32.TabIndex = 38; + this.label32.Text = "Check for update:"; + // + // updateCombo + // + this.updateCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.updateCombo.FormattingEnabled = true; + this.updateCombo.Items.AddRange(new object[] { + "Never", + "Once a week", + "Once a month"}); + this.updateCombo.Location = new System.Drawing.Point(299, 166); + this.updateCombo.Name = "updateCombo"; + this.updateCombo.Size = new System.Drawing.Size(127, 21); + this.updateCombo.TabIndex = 37; + this.updateCombo.SelectedIndexChanged += new System.EventHandler(this.updateCombo_SelectedIndexChanged); + // + // label22 + // + this.label22.AutoSize = true; + this.label22.Location = new System.Drawing.Point(13, 195); + this.label22.Name = "label22"; + this.label22.Size = new System.Drawing.Size(95, 13); + this.label22.TabIndex = 36; + this.label22.Text = "Log verbosity level"; + // + // logVerbosity + // + this.logVerbosity.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.logVerbosity.FormattingEnabled = true; + this.logVerbosity.Items.AddRange(new object[] { + "Info, warnings and errors", + "Warnings and errors", + "Errors only"}); + this.logVerbosity.Location = new System.Drawing.Point(14, 218); + this.logVerbosity.Name = "logVerbosity"; + this.logVerbosity.Size = new System.Drawing.Size(246, 21); + this.logVerbosity.TabIndex = 35; + this.logVerbosity.SelectedIndexChanged += new System.EventHandler(this.logVerbosity_SelectedIndexChanged); + // + // converterLinkLabel + // + this.converterLinkLabel.AutoSize = true; + this.converterLinkLabel.Location = new System.Drawing.Point(12, 55); + this.converterLinkLabel.Name = "converterLinkLabel"; + this.converterLinkLabel.Size = new System.Drawing.Size(268, 13); + this.converterLinkLabel.TabIndex = 34; + this.converterLinkLabel.TabStop = true; + this.converterLinkLabel.Text = "Click here to download latest version of ePub converter"; + this.converterLinkLabel.Visible = false; + this.converterLinkLabel.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(296, 82); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(130, 13); + this.label11.TabIndex = 32; + this.label11.Text = "GUI and OPDS language:"; + // + // langCombo + // + this.langCombo.DisplayMember = "Value"; + this.langCombo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.langCombo.FormattingEnabled = true; + this.langCombo.Location = new System.Drawing.Point(299, 107); + this.langCombo.Name = "langCombo"; + this.langCombo.Size = new System.Drawing.Size(127, 21); + this.langCombo.TabIndex = 31; + this.langCombo.ValueMember = "Key"; + this.langCombo.SelectedValueChanged += new System.EventHandler(this.langCombo_SelectedValueChanged); + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(8, 12); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(138, 13); + this.label8.TabIndex = 30; + this.label8.Text = "Path to the ePub converter:"; + // + // convertorFolder + // + this.convertorFolder.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.convertorFolder.Image = global::TinyOPDS.Properties.Resources.folder; + this.convertorFolder.Location = new System.Drawing.Point(431, 26); + this.convertorFolder.Name = "convertorFolder"; + this.convertorFolder.Size = new System.Drawing.Size(29, 23); + this.convertorFolder.TabIndex = 29; + this.convertorFolder.UseVisualStyleBackColor = true; + this.convertorFolder.Click += new System.EventHandler(this.folderButton_Click); + // + // convertorPath + // + this.convertorPath.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.convertorPath.Location = new System.Drawing.Point(11, 28); + this.convertorPath.Name = "convertorPath"; + this.convertorPath.Size = new System.Drawing.Size(413, 20); + this.convertorPath.TabIndex = 28; + this.convertorPath.Validated += new System.EventHandler(this.convertorPath_Validated); + // + // saveLog + // + this.saveLog.AutoSize = true; + this.saveLog.Checked = global::TinyOPDS.Properties.Settings.Default.SaveLogToDisk; + this.saveLog.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "SaveLogToDisk", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.saveLog.Location = new System.Drawing.Point(14, 166); + this.saveLog.Name = "saveLog"; + this.saveLog.Size = new System.Drawing.Size(96, 17); + this.saveLog.TabIndex = 33; + this.saveLog.Text = "Save log to file"; + this.saveLog.UseVisualStyleBackColor = true; + this.saveLog.CheckedChanged += new System.EventHandler(this.saveLog_CheckedChanged); + // + // closeToTray + // + this.closeToTray.AutoSize = true; + this.closeToTray.Checked = global::TinyOPDS.Properties.Settings.Default.CloseToTray; + this.closeToTray.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "CloseToTray", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.closeToTray.Location = new System.Drawing.Point(14, 138); + this.closeToTray.Name = "closeToTray"; + this.closeToTray.Size = new System.Drawing.Size(138, 17); + this.closeToTray.TabIndex = 2; + this.closeToTray.Text = "Close or minimize to tray"; + this.closeToTray.UseVisualStyleBackColor = true; + this.closeToTray.CheckedChanged += new System.EventHandler(this.closeToTray_CheckedChanged); + // + // startMinimized + // + this.startMinimized.AutoSize = true; + this.startMinimized.Checked = global::TinyOPDS.Properties.Settings.Default.StartMinimized; + this.startMinimized.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "StartMinimized", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.startMinimized.Location = new System.Drawing.Point(14, 110); + this.startMinimized.Name = "startMinimized"; + this.startMinimized.Size = new System.Drawing.Size(96, 17); + this.startMinimized.TabIndex = 1; + this.startMinimized.Text = "Start minimized"; + this.startMinimized.UseVisualStyleBackColor = true; + // + // startWithWindows + // + this.startWithWindows.AutoSize = true; + this.startWithWindows.Checked = global::TinyOPDS.Properties.Settings.Default.StartWithWindows; + this.startWithWindows.DataBindings.Add(new System.Windows.Forms.Binding("Checked", global::TinyOPDS.Properties.Settings.Default, "StartWithWindows", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.startWithWindows.Location = new System.Drawing.Point(14, 82); + this.startWithWindows.Name = "startWithWindows"; + this.startWithWindows.Size = new System.Drawing.Size(117, 17); + this.startWithWindows.TabIndex = 0; + this.startWithWindows.Text = "Start with Windows"; + this.startWithWindows.UseVisualStyleBackColor = true; + this.startWithWindows.CheckedChanged += new System.EventHandler(this.startWithWindows_CheckedChanged); + // + // tabPage4 + // + this.tabPage4.BackColor = System.Drawing.SystemColors.Control; + this.tabPage4.Controls.Add(this.linkLabel6); + this.tabPage4.Controls.Add(this.linkLabel5); + this.tabPage4.Controls.Add(this.linkLabel4); + this.tabPage4.Controls.Add(this.linkLabel3); + this.tabPage4.Controls.Add(this.label20); + this.tabPage4.Controls.Add(this.label19); + this.tabPage4.Controls.Add(this.linkLabel2); + this.tabPage4.Controls.Add(this.label18); + this.tabPage4.Controls.Add(this.linkLabel1); + this.tabPage4.Controls.Add(this.label17); + this.tabPage4.Controls.Add(this.appVersion); + this.tabPage4.Controls.Add(this.appName); + this.tabPage4.Controls.Add(this.pictureBox1); + this.tabPage4.Controls.Add(this.donateButton); + this.tabPage4.Location = new System.Drawing.Point(4, 34); + this.tabPage4.Name = "tabPage4"; + this.tabPage4.Padding = new System.Windows.Forms.Padding(3); + this.tabPage4.Size = new System.Drawing.Size(473, 308); + this.tabPage4.TabIndex = 3; + this.tabPage4.Text = "About program"; + // + // linkLabel6 + // + this.linkLabel6.AutoSize = true; + this.linkLabel6.Location = new System.Drawing.Point(195, 258); + this.linkLabel6.Name = "linkLabel6"; + this.linkLabel6.Size = new System.Drawing.Size(165, 13); + this.linkLabel6.TabIndex = 13; + this.linkLabel6.TabStop = true; + this.linkLabel6.Text = "Gremlin2, author of Fb2Fix project"; + this.linkLabel6.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); + // + // linkLabel5 + // + this.linkLabel5.AutoSize = true; + this.linkLabel5.Location = new System.Drawing.Point(195, 212); + this.linkLabel5.Name = "linkLabel5"; + this.linkLabel5.Size = new System.Drawing.Size(97, 13); + this.linkLabel5.TabIndex = 12; + this.linkLabel5.TabStop = true; + this.linkLabel5.Text = "ePubReader library"; + this.linkLabel5.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); + // + // linkLabel4 + // + this.linkLabel4.AutoSize = true; + this.linkLabel4.Location = new System.Drawing.Point(195, 235); + this.linkLabel4.Name = "linkLabel4"; + this.linkLabel4.Size = new System.Drawing.Size(86, 13); + this.linkLabel4.TabIndex = 11; + this.linkLabel4.TabStop = true; + this.linkLabel4.Text = "DotNetZip library"; + this.linkLabel4.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); + // + // linkLabel3 + // + this.linkLabel3.AutoSize = true; + this.linkLabel3.Location = new System.Drawing.Point(193, 190); + this.linkLabel3.Name = "linkLabel3"; + this.linkLabel3.Size = new System.Drawing.Size(267, 13); + this.linkLabel3.TabIndex = 10; + this.linkLabel3.TabStop = true; + this.linkLabel3.Text = "Lord KiRon, author of fb2librarynet library and converter"; + this.linkLabel3.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel3_LinkClicked); + // + // label20 + // + this.label20.Location = new System.Drawing.Point(9, 190); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(161, 13); + this.label20.TabIndex = 9; + this.label20.Text = "Special thanks:"; + this.label20.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.label19.Location = new System.Drawing.Point(192, 89); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(220, 20); + this.label19.TabIndex = 8; + this.label19.Text = "Copyright © 2013, SeNSSoFT"; + // + // linkLabel2 + // + this.linkLabel2.AutoSize = true; + this.linkLabel2.Location = new System.Drawing.Point(193, 167); + this.linkLabel2.Name = "linkLabel2"; + this.linkLabel2.Size = new System.Drawing.Size(184, 13); + this.linkLabel2.TabIndex = 7; + this.linkLabel2.TabStop = true; + this.linkLabel2.Text = "http://tinyopds.codeplex.com/license"; + this.linkLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + // + // label18 + // + this.label18.Location = new System.Drawing.Point(11, 167); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(159, 13); + this.label18.TabIndex = 6; + this.label18.Text = "Project license:"; + this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // linkLabel1 + // + this.linkLabel1.AutoSize = true; + this.linkLabel1.Location = new System.Drawing.Point(193, 144); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.Size = new System.Drawing.Size(151, 13); + this.linkLabel1.TabIndex = 5; + this.linkLabel1.TabStop = true; + this.linkLabel1.Text = "http://tinyopds.codeplex.com/"; + this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel_LinkClicked); + // + // label17 + // + this.label17.Location = new System.Drawing.Point(8, 144); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(162, 13); + this.label17.TabIndex = 4; + this.label17.Text = "Project home page:"; + this.label17.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // appVersion + // + this.appVersion.AutoSize = true; + this.appVersion.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.appVersion.Location = new System.Drawing.Point(262, 58); + this.appVersion.Name = "appVersion"; + this.appVersion.Size = new System.Drawing.Size(85, 20); + this.appVersion.TabIndex = 3; + this.appVersion.Text = "version 1.2"; + // + // appName + // + this.appName.AutoSize = true; + this.appName.Font = new System.Drawing.Font("Microsoft Sans Serif", 20F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); + this.appName.Location = new System.Drawing.Point(190, 14); + this.appName.Name = "appName"; + this.appName.Size = new System.Drawing.Size(226, 31); + this.appName.TabIndex = 2; + this.appName.Text = "TinyOPDS server"; + // + // pictureBox1 + // + this.pictureBox1.ErrorImage = null; + this.pictureBox1.Image = global::TinyOPDS.Properties.Resources.TinyOPDS; + this.pictureBox1.Location = new System.Drawing.Point(8, 9); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(103, 103); + this.pictureBox1.TabIndex = 1; + this.pictureBox1.TabStop = false; + // + // donateButton + // + this.donateButton.Image = global::TinyOPDS.Properties.Resources.donate; + this.donateButton.Location = new System.Drawing.Point(9, 240); + this.donateButton.Name = "donateButton"; + this.donateButton.Size = new System.Drawing.Size(157, 56); + this.donateButton.TabIndex = 0; + this.donateButton.UseVisualStyleBackColor = true; + this.donateButton.Click += new System.EventHandler(this.donateButton_Click); + // + // contextMenuStrip + // + this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.windowMenuItem, + this.serverMenuItem, + this.toolStripMenuItem1, + this.exitMenuItem}); + this.contextMenuStrip.Name = "contextMenuStrip1"; + this.contextMenuStrip.Size = new System.Drawing.Size(145, 76); + // + // windowMenuItem + // + this.windowMenuItem.Name = "windowMenuItem"; + this.windowMenuItem.Size = new System.Drawing.Size(144, 22); + this.windowMenuItem.Text = "Hide window"; + this.windowMenuItem.Click += new System.EventHandler(this.windowMenuItem_Click); + // + // serverMenuItem + // + this.serverMenuItem.Name = "serverMenuItem"; + this.serverMenuItem.Size = new System.Drawing.Size(144, 22); + this.serverMenuItem.Text = "Stop server"; + this.serverMenuItem.Click += new System.EventHandler(this.serverButton_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + this.toolStripMenuItem1.Size = new System.Drawing.Size(141, 6); + // + // exitMenuItem + // + this.exitMenuItem.Name = "exitMenuItem"; + this.exitMenuItem.Size = new System.Drawing.Size(144, 22); + this.exitMenuItem.Text = "Exit"; + this.exitMenuItem.Click += new System.EventHandler(this.exitMenuItem_Click); + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(474, 341); + this.Controls.Add(this.tabControl1); + this.DoubleBuffered = true; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.Name = "MainForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "TinyOPDS server"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); + this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.MainForm_FormClosed); + this.Load += new System.EventHandler(this.MainForm_Load); + this.Resize += new System.EventHandler(this.MainForm_Resize); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.tabPage6.ResumeLayout(false); + this.tabPage6.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.itemsPerWeb)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.itemsPerOPDS)).EndInit(); + this.tabPage5.ResumeLayout(false); + this.tabPage5.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.wrongAttemptsCount)).EndInit(); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.tabPage4.ResumeLayout(false); + this.tabPage4.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.contextMenuStrip.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.Button folderButton; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button scannerButton; + private System.Windows.Forms.TextBox libraryPath; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.TextBox serverPort; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Button serverButton; + private System.Windows.Forms.TabPage tabPage3; + private System.Windows.Forms.Label invalidBooks; + private System.Windows.Forms.Label label9; + private System.Windows.Forms.Label skippedBooks; + private System.Windows.Forms.Label label7; + private System.Windows.Forms.Label booksFound; + private System.Windows.Forms.Label booksInDB; + private System.Windows.Forms.Label label15; + private System.Windows.Forms.Label status; + private System.Windows.Forms.Label label14; + private System.Windows.Forms.Label rate; + private System.Windows.Forms.Label label12; + private System.Windows.Forms.Label elapsedTime; + private System.Windows.Forms.Label label10; + private System.Windows.Forms.Label startTime; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label booksProcessed; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TextBox serverName; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.CheckBox closeToTray; + private System.Windows.Forms.CheckBox startMinimized; + private System.Windows.Forms.CheckBox startWithWindows; + private System.Windows.Forms.Label label8; + private System.Windows.Forms.Button convertorFolder; + private System.Windows.Forms.TextBox convertorPath; + private System.Windows.Forms.Label label11; + private System.Windows.Forms.ComboBox langCombo; + private System.Windows.Forms.CheckBox openPort; + private System.Windows.Forms.Label extIPlabel; + private System.Windows.Forms.Label intIPlabel; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip; + private System.Windows.Forms.ToolStripMenuItem windowMenuItem; + private System.Windows.Forms.ToolStripMenuItem serverMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem exitMenuItem; + private System.Windows.Forms.CheckBox saveLog; + private System.Windows.Forms.Label duplicates; + private System.Windows.Forms.Label label16; + private System.Windows.Forms.TextBox rootPrefix; + private System.Windows.Forms.Label label13; + private System.Windows.Forms.CheckBox useWatcher; + private System.Windows.Forms.TabPage tabPage4; + private System.Windows.Forms.CheckBox useUPnP; + private System.Windows.Forms.LinkLabel converterLinkLabel; + private System.Windows.Forms.Button donateButton; + private System.Windows.Forms.PictureBox pictureBox1; + private System.Windows.Forms.Label label19; + private System.Windows.Forms.LinkLabel linkLabel2; + private System.Windows.Forms.Label label18; + private System.Windows.Forms.LinkLabel linkLabel1; + private System.Windows.Forms.Label label17; + private System.Windows.Forms.Label appVersion; + private System.Windows.Forms.Label appName; + private System.Windows.Forms.Label label20; + private System.Windows.Forms.LinkLabel intLink; + private System.Windows.Forms.LinkLabel extLink; + private System.Windows.Forms.LinkLabel linkLabel5; + private System.Windows.Forms.LinkLabel linkLabel4; + private System.Windows.Forms.LinkLabel linkLabel3; + private System.Windows.Forms.TextBox databaseFileName; + private System.Windows.Forms.Label label21; + private System.Windows.Forms.TabPage tabPage5; + private System.Windows.Forms.CheckBox useHTTPAuth; + private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.CheckBox rememberClients; + private System.Windows.Forms.Label label22; + private System.Windows.Forms.ComboBox logVerbosity; + private System.Windows.Forms.Label statImages; + private System.Windows.Forms.Label label27; + private System.Windows.Forms.Label statBooks; + private System.Windows.Forms.Label label25; + private System.Windows.Forms.Label statRequests; + private System.Windows.Forms.Label label23; + private System.Windows.Forms.Label statUniqueClients; + private System.Windows.Forms.Label label26; + private System.Windows.Forms.Label statGoodLogins; + private System.Windows.Forms.Label label28; + private System.Windows.Forms.Label statWrongLogins; + private System.Windows.Forms.Label label30; + private System.Windows.Forms.Label label24; + private System.Windows.Forms.NumericUpDown wrongAttemptsCount; + private System.Windows.Forms.CheckBox banClients; + private System.Windows.Forms.Label statBannedClients; + private System.Windows.Forms.Label label31; + private System.Windows.Forms.Label label32; + private System.Windows.Forms.ComboBox updateCombo; + private System.Windows.Forms.ComboBox interfaceCombo; + private System.Windows.Forms.Label label29; + private System.Windows.Forms.Label label33; + private System.Windows.Forms.TextBox webPrefix; + private System.Windows.Forms.LinkLabel extWebLink; + private System.Windows.Forms.LinkLabel intWebLink; + private System.Windows.Forms.Label label34; + private System.Windows.Forms.Label label35; + private System.Windows.Forms.CheckBox useAbsoluteUri; + private System.Windows.Forms.Button viewLogFile; + private System.Windows.Forms.TabPage tabPage6; + private System.Windows.Forms.ComboBox newBooksPeriodCombo; + private System.Windows.Forms.Label label39; + private System.Windows.Forms.ComboBox sortOrderCombo; + private System.Windows.Forms.Label label38; + private System.Windows.Forms.NumericUpDown itemsPerWeb; + private System.Windows.Forms.Label label37; + private System.Windows.Forms.NumericUpDown itemsPerOPDS; + private System.Windows.Forms.Label label36; + private System.Windows.Forms.CheckBox checkBox1; + private System.Windows.Forms.LinkLabel linkLabel6; + private System.Windows.Forms.CheckBox checkBox2; + } +} + diff --git a/trunk/TinyOPDS/MainForm.cs b/TinyOPDS/MainForm.cs similarity index 97% rename from trunk/TinyOPDS/MainForm.cs rename to TinyOPDS/MainForm.cs index a76c91e..6a97035 100644 --- a/trunk/TinyOPDS/MainForm.cs +++ b/TinyOPDS/MainForm.cs @@ -1,939 +1,939 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * TinyOPDS main UI thread - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Windows.Forms; -using System.Xml.Linq; -using System.Threading; -using System.Net; - -using TinyOPDS.Data; -using TinyOPDS.Scanner; -using TinyOPDS.OPDS; -using TinyOPDS.Server; -using UPnP; - -namespace TinyOPDS -{ - public partial class MainForm : Form - { - OPDSServer _server; - Thread _serverThread; - FileScanner _scanner = new FileScanner(); - Watcher _watcher; - DateTime _scanStartTime; - UPnPController _upnpController = new UPnPController(); - NotifyIcon _notifyIcon = new NotifyIcon(); - BindingSource bs = new BindingSource(); - System.Windows.Forms.Timer _updateChecker = new System.Windows.Forms.Timer(); - string _updateUrl = string.Empty; - - #region Statistical information - int _fb2Count, _epubCount, _skippedFiles, _invalidFiles, _duplicates; - #endregion - - private const string urlTemplate = "http://{0}:{1}/{2}"; - - #region Initialization and startup - - public MainForm() - { - Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; - - AppDomain currentDomain = AppDomain.CurrentDomain; - currentDomain.UnhandledException += currentDomain_UnhandledException; - - Log.WriteLine("TinyOPDS version {0}.{1} started", Utils.Version.Major, Utils.Version.Minor); - - InitializeComponent(); - - // Assign combo data source to the list of all available interfaces - interfaceCombo.DataSource = UPnPController.LocalInterfaces; - interfaceCombo.DataBindings.Add(new Binding("SelectedIndex", Properties.Settings.Default, "LocalInterfaceIndex", false, DataSourceUpdateMode.OnPropertyChanged)); - - logVerbosity.DataBindings.Add(new Binding("SelectedIndex", Properties.Settings.Default, "LogLevel", false, DataSourceUpdateMode.OnPropertyChanged)); - updateCombo.DataBindings.Add(new Binding("SelectedIndex", Properties.Settings.Default, "UpdatesCheck", false, DataSourceUpdateMode.OnPropertyChanged)); - - this.PerformLayout(); - - // Manually assign icons from resources (fix for Mono) - this.Icon = Properties.Resources.trayIcon; - _notifyIcon.ContextMenuStrip = this.contextMenuStrip; - _notifyIcon.Icon = Properties.Resources.trayIcon; - _notifyIcon.MouseClick += notifyIcon1_MouseClick; - _notifyIcon.BalloonTipClicked += _notifyIcon_BalloonTipClicked; - _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; - - // Init localization service - Localizer.Init(); - Localizer.AddMenu(contextMenuStrip); - langCombo.DataSource = Localizer.Languages.ToArray(); - - // Load application settings - LoadSettings(); - - // Initialize update checker timer to tick every minute - _updateChecker.Interval = 1000 * 60; - _updateChecker.Tick += _updateChecker_Tick; - - // Setup credentials grid - bs.AddingNew += bs_AddingNew; - bs.AllowNew = true; - bs.DataSource = HttpProcessor.Credentials; - dataGridView1.DataSource = bs; - bs.CurrentItemChanged += bs_CurrentItemChanged; - foreach (DataGridViewColumn col in dataGridView1.Columns) col.Width = 180; - - Library.LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); - Library.LibraryLoaded += (_, __) => - { - UpdateInfo(); - _watcher.DirectoryToWatch = Library.LibraryPath; - _watcher.IsEnabled = TinyOPDS.Properties.Settings.Default.WatchLibrary; - }; - - // Create file watcher - _watcher = new Watcher(Library.LibraryPath); - _watcher.OnBookAdded += (object sender, BookAddedEventArgs e) => - { - if (e.BookType == BookType.FB2) _fb2Count++; else _epubCount++; - UpdateInfo(); - Log.WriteLine(LogLevel.Info, "Added: \"{0}\"", e.BookPath); - }; - _watcher.OnInvalidBook += (_, __) => - { - _invalidFiles++; - UpdateInfo(); - }; - _watcher.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => - { - _skippedFiles = _e.Count; - UpdateInfo(); - }; - - _watcher.OnBookDeleted += (object sender, BookDeletedEventArgs e) => - { - UpdateInfo(); - Log.WriteLine(LogLevel.Info, "Deleted: \"{0}\"", e.BookPath); - }; - _watcher.IsEnabled = false; - - intLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); - intWebLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); - - // Start OPDS server - StartHttpServer(); - - // Set server statistics handler - HttpServer.ServerStatistics.StatisticsUpdated += (_, __) => - { - this.BeginInvoke((MethodInvoker)delegate - { - statRequests.Text = HttpServer.ServerStatistics.GetRequests.ToString(); - statBooks.Text = HttpServer.ServerStatistics.BooksSent.ToString(); - statImages.Text = HttpServer.ServerStatistics.ImagesSent.ToString(); - statUniqueClients.Text = HttpServer.ServerStatistics.UniqueClientsCount.ToString(); - statGoodLogins.Text = HttpServer.ServerStatistics.SuccessfulLoginAttempts.ToString(); - statWrongLogins.Text = HttpServer.ServerStatistics.WrongLoginAttempts.ToString(); - statBannedClients.Text = HttpServer.ServerStatistics.BannedClientsCount.ToString(); - }); - }; - - _scanStartTime = DateTime.Now; - _notifyIcon.Visible = TinyOPDS.Properties.Settings.Default.CloseToTray; - } - - /// - /// We should call DiscoverAsync when windows handle already created (invoker used) - /// - /// - /// - private void MainForm_Load(object sender, EventArgs e) - { - _upnpController.DiscoverCompleted += _upnpController_DiscoverCompleted; - _upnpController.DiscoverAsync(TinyOPDS.Properties.Settings.Default.UseUPnP); - } - - /// - /// Process unhandled exceptions - /// - /// - /// - void currentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs args) - { - if (args != null) - { - Exception e = (Exception)args.ExceptionObject; - if (e != null) - { - Log.WriteLine(LogLevel.Error, "{2}: {0}\nStack trace: {1}", e.Message, e.StackTrace, args.IsTerminating ? "Fatal exception" : "Unhandled exception"); - } - else - { - Log.WriteLine(LogLevel.Error, "Unhandled exception, args.ExceptionObject is null"); - } - } - else - { - Log.WriteLine(LogLevel.Error, "Unhandled exception, args is null"); - } - } - - void _upnpController_DiscoverCompleted(object sender, EventArgs e) - { - if (!IsDisposed && _upnpController != null) - { - this.BeginInvoke((MethodInvoker)delegate - { - extLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, TinyOPDS.Properties.Settings.Default.RootPrefix); - extWebLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, TinyOPDS.Properties.Settings.Default.HttpPrefix); - if (_upnpController.UPnPReady) - { - openPort.Enabled = true; - if (TinyOPDS.Properties.Settings.Default.OpenNATPort) openPort_CheckedChanged(this, null); - } - }); - } - } - - #endregion - - #region Application settings - - private void LoadSettings() - { - // Setup link labels - converterLinkLabel.Links.Add(0, converterLinkLabel.Text.Length, "http://fb2epub.net/files/Fb2ePubSetup_1_1_3.zip"); - linkLabel3.Links.Add(0, linkLabel3.Text.Length, "https://code.google.com/p/fb2librarynet/"); - linkLabel5.Links.Add(0, linkLabel5.Text.Length, "http://epubreader.codeplex.com/"); - linkLabel4.Links.Add(0, linkLabel4.Text.Length, "http://dotnetzip.codeplex.com/"); - linkLabel6.Links.Add(0, linkLabel6.Text.Length, "http://www.fb2library.net/projects/fb2fix"); - - // Setup settings controls - libraryPath.Text = TinyOPDS.Properties.Settings.Default.LibraryPath; - if (!string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.LibraryPath)) - { - databaseFileName.Text = Utils.CreateGuid(Utils.IsoOidNamespace, TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName()).ToString() + ".db"; - } - if (Utils.IsLinux) startWithWindows.Enabled = false; - if (string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath)) - { - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ProgramFiles"))) - { - if (File.Exists(Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "FB2ePub\\Fb2ePub.exe"))) - { - convertorPath.Text = TinyOPDS.Properties.Settings.Default.ConvertorPath = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "FB2ePub"); - } - } - } - else convertorPath.Text = TinyOPDS.Properties.Settings.Default.ConvertorPath; - converterLinkLabel.Visible = string.IsNullOrEmpty(convertorPath.Text); - - // We should update all invisible controls - interfaceCombo.SelectedIndex = Math.Min(UPnPController.LocalInterfaces.Count - 1, TinyOPDS.Properties.Settings.Default.LocalInterfaceIndex); - logVerbosity.SelectedIndex = Math.Min(2, TinyOPDS.Properties.Settings.Default.LogLevel); - updateCombo.SelectedIndex = Math.Min(2, TinyOPDS.Properties.Settings.Default.UpdatesCheck); - langCombo.SelectedValue = TinyOPDS.Properties.Settings.Default.Language; - sortOrderCombo.SelectedIndex = TinyOPDS.Properties.Settings.Default.SortOrder; - newBooksPeriodCombo.SelectedIndex = TinyOPDS.Properties.Settings.Default.NewBooksPeriod; - - openPort.Checked = TinyOPDS.Properties.Settings.Default.UseUPnP ? TinyOPDS.Properties.Settings.Default.OpenNATPort : false; - banClients.Enabled = rememberClients.Enabled = dataGridView1.Enabled = TinyOPDS.Properties.Settings.Default.UseHTTPAuth; - wrongAttemptsCount.Enabled = banClients.Checked && useHTTPAuth.Checked; - - _notifyIcon.Visible = TinyOPDS.Properties.Settings.Default.CloseToTray; - _updateChecker.Start(); - - // Load saved credentials - try - { - HttpProcessor.Credentials.Clear(); - string[] pairs = Crypt.DecryptStringAES(TinyOPDS.Properties.Settings.Default.Credentials, urlTemplate).Split(';'); - foreach (string pair in pairs) - { - string[] cred = pair.Split(':'); - if (cred.Length == 2) HttpProcessor.Credentials.Add( new Credential(cred[0], cred[1])); - } - } - catch { } - } - - private void SaveSettings() - { - TinyOPDS.Properties.Settings.Default.LibraryPath = libraryPath.Text.SanitizePathName(); - TinyOPDS.Properties.Settings.Default.Language = langCombo.SelectedValue as string; - TinyOPDS.Properties.Settings.Default.Save(); - } - - #endregion - - #region Credentials handling - - private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) - { - if (dataGridView1.Columns[e.ColumnIndex].Name == "Password" && e.Value != null) - { - dataGridView1.Rows[e.RowIndex].Tag = e.Value; - e.Value = new String('*', e.Value.ToString().Length); - } - } - - void bs_AddingNew(object sender, AddingNewEventArgs e) - { - e.NewObject = new Credential("", ""); - } - - void bs_CurrentItemChanged(object sender, EventArgs e) - { - string s = string.Empty; - foreach (Credential cred in HttpProcessor.Credentials) s += cred.User + ":" + cred.Password + ";"; - try - { - TinyOPDS.Properties.Settings.Default.Credentials = string.IsNullOrEmpty(s) ? string.Empty : Crypt.EncryptStringAES(s, urlTemplate); - } - finally - { - TinyOPDS.Properties.Settings.Default.Save(); - } - } - - #endregion - - #region Library scanning support - - private void libraryPath_TextChanged(object sender, EventArgs e) - { - databaseFileName.Text = Utils.CreateGuid(Utils.IsoOidNamespace, libraryPath.Text.SanitizePathName()).ToString() + ".db"; - } - - private void libraryPath_Validated(object sender, EventArgs e) - { - if (!string.IsNullOrEmpty(databaseFileName.Text) && !Library.LibraryPath.Equals(databaseFileName.Text.SanitizePathName()) && - Directory.Exists(libraryPath.Text.SanitizePathName())) - { - if (Library.IsChanged) Library.Save(); - Library.LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath = libraryPath.Text.SanitizePathName(); - booksInDB.Text = string.Format("{0} fb2: {1} epub: {2}", 0, 0, 0); - databaseFileName.Text = Utils.CreateGuid(Utils.IsoOidNamespace, TinyOPDS.Properties.Settings.Default.LibraryPath).ToString() + ".db"; - _watcher.IsEnabled = false; - // Reload library - Library.LoadAsync(); - } - else - { - if (!string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.LibraryPath)) libraryPath.Text = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); - else libraryPath.Undo(); - } - } - - private void folderButton_Click(object sender, EventArgs e) - { - using (FolderBrowserDialog dialog = new FolderBrowserDialog()) - { - dialog.SelectedPath = (sender as Button == folderButton) ? libraryPath.Text : convertorPath.Text; - if (dialog.ShowDialog() == DialogResult.OK) - { - if (sender as Button == folderButton) - { - libraryPath.Text = dialog.SelectedPath.SanitizePathName(); - libraryPath_Validated(sender, e); - } - else - { - convertorPath.Text = dialog.SelectedPath; - convertorPath_Validated(sender, e); - } - } - } - } - - private void scannerButton_Click(object sender, EventArgs e) - { - if (_scanner.Status != FileScannerStatus.SCANNING) - { - _scanner.OnBookFound += scanner_OnBookFound; - _scanner.OnInvalidBook += (_, __) => { _invalidFiles++; }; - _scanner.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => - { - _skippedFiles = _e.Count; - UpdateInfo(); - }; - _scanner.OnScanCompleted += (_, __) => - { - Library.Save(); - UpdateInfo(true); - - Log.WriteLine("Directory scanner completed"); - }; - _fb2Count = _epubCount = _skippedFiles = _invalidFiles = _duplicates = 0; - _scanStartTime = DateTime.Now; - startTime.Text = _scanStartTime.ToString(@"hh\:mm\:ss"); - _scanner.Start(libraryPath.Text.SanitizePathName()); - scannerButton.Text = Localizer.Text("Stop scanning"); - - Log.WriteLine("Directory scanner started"); - } - else - { - _scanner.Stop(); - Library.Save(); - UpdateInfo(true); - scannerButton.Text = Localizer.Text("Start scanning"); - - Log.WriteLine("Directory scanner stopped"); - } - } - - void scanner_OnBookFound(object sender, BookFoundEventArgs e) - { - if (Library.Add(e.Book)) - { - if (e.Book.BookType == BookType.FB2) _fb2Count++; else _epubCount++; - } - else _duplicates++; - if (Library.Count % 500 == 0) Library.Save(); - if (Library.Count % 20000 == 0) GC.Collect(); - UpdateInfo(); - } - - private void UpdateInfo(bool IsScanFinished = false) - { - if (this.InvokeRequired) { this.BeginInvoke((MethodInvoker)delegate { internalUpdateInfo(IsScanFinished); }); } - else { internalUpdateInfo(IsScanFinished); } - } - - private void internalUpdateInfo(bool IsScanFinished) - { - booksInDB.Text = string.Format("{0} fb2: {1} epub: {2}", Library.Count, Library.FB2Count, Library.EPUBCount); - booksFound.Text = string.Format("fb2: {0} epub: {1}", _fb2Count, _epubCount); - skippedBooks.Text = _skippedFiles.ToString(); - invalidBooks.Text = _invalidFiles.ToString(); - duplicates.Text = _duplicates.ToString(); - int totalBooksProcessed = _fb2Count + _epubCount + _skippedFiles + _invalidFiles + _duplicates; - booksProcessed.Text = totalBooksProcessed.ToString(); - - TimeSpan dt = DateTime.Now.Subtract(_scanStartTime); - elapsedTime.Text = dt.ToString(@"hh\:mm\:ss"); - rate.Text = (dt.TotalSeconds) > 0 ? string.Format("{0:0.} books/min", totalBooksProcessed / dt.TotalSeconds * 60) : "---"; - if (scannerButton.Enabled) - { - status.Text = IsScanFinished ? Localizer.Text("FINISHED") : (_scanner.Status == FileScannerStatus.SCANNING ? Localizer.Text("SCANNING") : Localizer.Text("STOPPED")); - scannerButton.Text = (_scanner.Status == FileScannerStatus.SCANNING) ? Localizer.Text("Stop scanning") : Localizer.Text("Start scanning"); - } - } - - #endregion - - #region HTTP (OPDS) server & network support - - private void serverButton_Click(object sender, EventArgs e) - { - if (_server == null) StartHttpServer(); else StopHttpServer(); - } - - private void StartHttpServer() - { - // Create and start HTTP server - HttpProcessor.AuthorizedClients.Clear(); - HttpProcessor.BannedClients.Clear(); - _server = new OPDSServer(_upnpController.LocalIP, int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort)); - - _serverThread = new Thread(new ThreadStart(_server.Listen)); - _serverThread.Priority = ThreadPriority.BelowNormal; - _serverThread.Start(); - _server.ServerReady.WaitOne(TimeSpan.FromMilliseconds(500)); - if (!_server.IsActive) - { - if (_server.ServerException != null) - { - if (_server.ServerException is System.Net.Sockets.SocketException) - { - MessageBox.Show(string.Format(Localizer.Text("Probably, port {0} is already in use. Please try different port value."), TinyOPDS.Properties.Settings.Default.ServerPort), Localizer.Text("Warning"), MessageBoxButtons.OK, MessageBoxIcon.Warning); - } - else - { - MessageBox.Show(_server.ServerException.Message, Localizer.Text("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error); - } - _server.StopServer(); - _serverThread = null; - _server = null; - } - } - else - { - serverButton.Text = serverMenuItem.Text = Localizer.Text("Stop server"); - Log.WriteLine("HTTP server started"); - } - } - - private void StopHttpServer() - { - if (_server != null) - { - _server.StopServer(); - _serverThread = null; - _server = null; - Log.WriteLine("HTTP server stopped"); - } - serverButton.Text = serverMenuItem.Text = Localizer.Text("Start server"); - } - - private void RestartHttpServer() - { - StopHttpServer(); - StartHttpServer(); - } - - private void useUPnP_CheckStateChanged(object sender, EventArgs e) - { - if (useUPnP.Checked) - { - // Re-detect IP addresses using UPnP - _upnpController.DiscoverAsync(true); - } - else - { - openPort.Checked = openPort.Enabled = false; - } - } - - private void openPort_CheckedChanged(object sender, EventArgs e) - { - if (_upnpController != null && _upnpController.UPnPReady) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - if (openPort.Checked) - { - _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); - - Log.WriteLine("Port {0} forwarded by UPnP", port); - } - else - { - _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); - - Log.WriteLine("Port {0} closed", port); - } - } - } - - private void interfaceCombo_SelectedIndexChanged(object sender, EventArgs e) - { - if (_upnpController != null && _upnpController.InterfaceIndex != interfaceCombo.SelectedIndex) - { - _upnpController.InterfaceIndex = interfaceCombo.SelectedIndex; - intLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); - intWebLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); - - if (TinyOPDS.Properties.Settings.Default.UseUPnP && openPort.Checked) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); - _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); - } - RestartHttpServer(); - } - } - - #endregion - - #region Form minimizing and closing - - private void MainForm_Resize(object sender, EventArgs e) - { - if (TinyOPDS.Properties.Settings.Default.CloseToTray) - { - Visible = (WindowState == FormWindowState.Normal); - windowMenuItem.Text = Localizer.Text("Show window"); - } - } - - private void windowMenuItem_Click(object sender, EventArgs e) - { - if (!ShowInTaskbar) ShowInTaskbar = true; else Visible = !Visible; - if (Visible) WindowState = FormWindowState.Normal; - windowMenuItem.Text = Localizer.Text(Visible ? "Hide window" : "Show window"); - } - - private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) - { - if (e.Button == System.Windows.Forms.MouseButtons.Left) windowMenuItem_Click(this, null); - } - - private bool realExit = false; - private void MainForm_FormClosing(object sender, FormClosingEventArgs e) - { - if (TinyOPDS.Properties.Settings.Default.CloseToTray && !realExit) - { - e.Cancel = true; - Visible = false; - WindowState = FormWindowState.Minimized; - windowMenuItem.Text = Localizer.Text("Show window"); - } - } - - private void MainForm_FormClosed(object sender, FormClosedEventArgs e) - { - SaveSettings(); - if (_server != null && _server._isActive) - { - _server.StopServer(); - _serverThread = null; - _server = null; - - if (_upnpController != null) - { - if (TinyOPDS.Properties.Settings.Default.UseUPnP) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); - } - _upnpController.DiscoverCompleted -= _upnpController_DiscoverCompleted; - _upnpController.Dispose(); - } - } - - if (_scanner.Status == FileScannerStatus.SCANNING) _scanner.Stop(); - if (Library.IsChanged) Library.Save(); - - _notifyIcon.Visible = false; - - Log.WriteLine("TinyOPDS closed\n"); - } - - private void exitMenuItem_Click(object sender, EventArgs e) - { - realExit = true; - Close(); - } - - #endregion - - #region Form controls handling - - private void convertorPath_Validated(object sender, EventArgs e) - { - if (!string.IsNullOrEmpty(convertorPath.Text) && Directory.Exists(convertorPath.Text) && File.Exists(Path.Combine(convertorPath.Text, Utils.IsLinux ? "fb2toepub" : "Fb2ePub.exe"))) - { - TinyOPDS.Properties.Settings.Default.ConvertorPath = convertorPath.Text; - } - else - { - convertorPath.Text = TinyOPDS.Properties.Settings.Default.ConvertorPath; - } - } - - private void useWatcher_CheckedChanged(object sender, EventArgs e) - { - if (_watcher != null && _watcher.IsEnabled != useWatcher.Checked) - { - _watcher.IsEnabled = useWatcher.Checked; - } - } - - private void closeToTray_CheckedChanged(object sender, EventArgs e) - { - _notifyIcon.Visible = closeToTray.Checked; - } - - private void startWithWindows_CheckedChanged(object sender, EventArgs e) - { - Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); - bool exists = (registryKey.GetValue("TinyOPDS") != null); - if (startWithWindows.Checked && !exists) registryKey.SetValue("TinyOPDS", Application.ExecutablePath); - else if (exists && !startWithWindows.Checked) registryKey.DeleteValue("TinyOPDS"); - } - - private void saveLog_CheckedChanged(object sender, EventArgs e) - { - Log.SaveToFile = label22.Enabled = logVerbosity.Enabled = saveLog.Checked; - } - - private void UpdateServerLinks() - { - if (_upnpController != null) - { - if (_upnpController.LocalIP != null) - { - intLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); - intWebLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); - } - if (_upnpController.ExternalIP != null) - { - extLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); - extWebLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); - } - } - } - - /// - /// Handle server's root prefix change - /// - /// - /// - private void rootPrefix_TextChanged(object sender, EventArgs e) - { - if (sender is TextBox && (sender as TextBox).CanUndo) - { - if (rootPrefix.Text.EndsWith("/")) rootPrefix.Text = rootPrefix.Text.Remove(rootPrefix.Text.Length - 1); - if (webPrefix.Text.EndsWith("/")) webPrefix.Text = webPrefix.Text.Remove(webPrefix.Text.Length - 1); - if (rootPrefix.Text.ToLower().Equals(webPrefix.Text.ToLower())) - { - MessageBox.Show(Localizer.Text("OPDS and web root prefixes can not be the same."), Localizer.Text("Warning"), MessageBoxButtons.OK, MessageBoxIcon.Warning); - (sender as TextBox).Undo(); - } - UpdateServerLinks(); - } - } - - /// - /// Validate server port - /// - /// - /// - private void serverPort_Validated(object sender, EventArgs e) - { - int port = 8080; - bool valid = int.TryParse(serverPort.Text, out port); - if (valid && port >= 1 && port <= 65535) - { - if (_upnpController != null && _upnpController.UPnPReady && openPort.Checked) - { - openPort.Checked = false; - TinyOPDS.Properties.Settings.Default.ServerPort = port.ToString(); - openPort.Checked = true; - } - else TinyOPDS.Properties.Settings.Default.ServerPort = port.ToString(); - if (_server != null && _server.IsActive) - { - RestartHttpServer(); - } - } - else - { - MessageBox.Show(Localizer.Text("Invalid port value: value must be numeric and in range from 1 to 65535"), Localizer.Text("Warning"), MessageBoxButtons.OK, MessageBoxIcon.Warning); - serverPort.Text = TinyOPDS.Properties.Settings.Default.ServerPort.ToString(); - } - // Update link labels - UpdateServerLinks(); - } - - /// - /// Set UI language - /// - /// - /// - private void langCombo_SelectedValueChanged(object sender, EventArgs e) - { - Localizer.SetLanguage(this, langCombo.SelectedValue as string); - appVersion.Text = string.Format(Localizer.Text("version {0}.{1} {2}"), Utils.Version.Major, Utils.Version.Minor, Utils.Version.Major == 0?" (beta)":""); - scannerButton.Text = Localizer.Text( (_scanner.Status == FileScannerStatus.STOPPED) ? "Start scanning" : "Stop scanning"); - serverButton.Text = Localizer.Text((_server == null) ? "Start server" : "Stop server"); - serverMenuItem.Text = Localizer.Text((_server == null) ? "Start server" : "Stop server"); - windowMenuItem.Text = Localizer.Text(Visible || ShowInTaskbar ? "Hide window" : "Show window"); - logVerbosity.Items[0] = Localizer.Text("Info, warnings and errors"); - logVerbosity.Items[1] = Localizer.Text("Warnings and errors"); - logVerbosity.Items[2] = Localizer.Text("Errors only"); - updateCombo.Items[0] = Localizer.Text("Never"); - updateCombo.Items[1] = Localizer.Text("Once a week"); - updateCombo.Items[2] = Localizer.Text("Once a month"); - sortOrderCombo.Items[0] = Localizer.Text("Latin first"); - sortOrderCombo.Items[1] = Localizer.Text("Cyrillic first"); - newBooksPeriodCombo.Items[0] = Localizer.Text("one week"); - newBooksPeriodCombo.Items[1] = Localizer.Text("two weeks"); - newBooksPeriodCombo.Items[2] = Localizer.Text("three weeks"); - newBooksPeriodCombo.Items[3] = Localizer.Text("month"); - newBooksPeriodCombo.Items[4] = Localizer.Text("month and half"); - newBooksPeriodCombo.Items[5] = Localizer.Text("two month"); - newBooksPeriodCombo.Items[6] = Localizer.Text("three month"); - } - - /// - /// Handle PayPal donation - /// - /// - /// - private void donateButton_Click(object sender, EventArgs e) - { - const string business = "sens.boston@gmail.com", description = "Donation%20for%20the%20TinyOPDS", country = "US", currency = "USD"; - string url = string.Format("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business={0}&lc={1}&item_name={2}¤cy_code={3}&bn=PP%2dDonationsBF", - business, country, description, currency); - System.Diagnostics.Process.Start(url); - } - - private bool checkUrl(string uriName) - { - Uri uriResult; - bool result = Uri.TryCreate(uriName, UriKind.Absolute, out uriResult); - return result && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); - } - - private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - if (sender is LinkLabel && checkUrl((sender as LinkLabel).Text)) - { - System.Diagnostics.Process.Start((sender as LinkLabel).Text); - } - } - - private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) - { - if (sender is LinkLabel && checkUrl((sender as LinkLabel).Links[0].LinkData as string)) - { - System.Diagnostics.Process.Start((sender as LinkLabel).Links[0].LinkData as string); - } - } - - private void useHTTPAuth_CheckedChanged(object sender, EventArgs e) - { - dataGridView1.Enabled = banClients.Enabled = rememberClients.Enabled = useHTTPAuth.Checked; - wrongAttemptsCount.Enabled = banClients.Enabled && banClients.Checked; - } - - private void banClients_CheckedChanged(object sender, EventArgs e) - { - wrongAttemptsCount.Enabled = banClients.Checked; - } - - private void logVerbosity_SelectedIndexChanged(object sender, EventArgs e) - { - Log.VerbosityLevel = (LogLevel)logVerbosity.SelectedIndex; - } - - private void updateCombo_SelectedIndexChanged(object sender, EventArgs e) - { - TinyOPDS.Properties.Settings.Default.UpdatesCheck = updateCombo.SelectedIndex; - } - - private void viewLogFile_Click(object sender, EventArgs e) - { - System.Diagnostics.Process.Start(Log.LogFileName); - } - - private void sortOrderCombo_SelectedIndexChanged(object sender, EventArgs e) - { - TinyOPDS.Properties.Settings.Default.SortOrder = sortOrderCombo.SelectedIndex; - } - - private void newBooksPeriodCombo_SelectedIndexChanged(object sender, EventArgs e) - { - TinyOPDS.Properties.Settings.Default.NewBooksPeriod = newBooksPeriodCombo.SelectedIndex; - } - - #endregion - - #region TinyOPDS updates checker - - /// - /// This timer event should be raised every hour - /// - /// - /// - static int[] checkIntervals = new int[] { 0, 60 * 24 * 7, 60 * 24 * 30, 1}; - static int _timerCallsCount = 0; - void _updateChecker_Tick(object sender, EventArgs e) - { - if (TinyOPDS.Properties.Settings.Default.UpdatesCheck > 0) - { - _updateUrl = string.Empty; - int minutesFromLastCheck = (int)Math.Round(DateTime.Now.Subtract(TinyOPDS.Properties.Settings.Default.LastCheck).TotalMinutes); - if (minutesFromLastCheck >= checkIntervals[TinyOPDS.Properties.Settings.Default.UpdatesCheck]) - { - Log.WriteLine(LogLevel.Info, "Checking software update. Minutes from the last check: {0}", minutesFromLastCheck); - WebClient wc = new WebClient(); - wc.DownloadStringCompleted += wc_DownloadStringCompleted; - wc.DownloadStringAsync(new Uri("http://senssoft.com/tinyopds.txt")); - } - } - - if (TinyOPDS.Properties.Settings.Default.UseUPnP && _timerCallsCount++ > 5) - { - _timerCallsCount = 0; - if (_server != null && _server.IsActive && _server.IsIdle && _upnpController != null && _upnpController.UPnPReady) - { - if (!_upnpController.Discovered) - { - _upnpController.DiscoverAsync(true); - } - else if (openPort.Checked) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); - } - } - } - } - - void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) - { - if (e.Error == null) - { - TinyOPDS.Properties.Settings.Default.LastCheck = DateTime.Now; - TinyOPDS.Properties.Settings.Default.Save(); - - string[] s = e.Result.Split('\n'); - if (s.Length == 2) - { - s[0] = s[0].Replace("\r", ""); - double currentVersion = 0, newVersion = 0; - if (double.TryParse(string.Format("{0}.{1}", Utils.Version.Major, Utils.Version.Minor), out currentVersion)) - { - if (double.TryParse(s[0], out newVersion)) - { - if (newVersion > currentVersion) - { - _updateUrl = s[1]; - _notifyIcon.Visible = true; - _notifyIcon.ShowBalloonTip(30000, Localizer.Text("TinyOPDS: update found"), string.Format(Localizer.Text("Click here to download update v {0}"), s[0]), ToolTipIcon.Info); - } - } - } - } - } - } - - void _notifyIcon_BalloonTipClicked(object sender, EventArgs e) - { - System.Diagnostics.Process.Start(_updateUrl); - } - - void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) - { - _notifyIcon.Visible = Properties.Settings.Default.CloseToTray; - } - - #endregion - - /// - /// This event raised on the checkbox change and should reload library - /// - /// - /// - private void checkBox_CheckedChanged(object sender, EventArgs e) - { - if (((sender as CheckBox).Name.Equals("checkBox1") && (sender as CheckBox).Checked != TinyOPDS.Properties.Settings.Default.LowMemoryProfile) || - ((sender as CheckBox).Name.Equals("checkBox2") && (sender as CheckBox).Checked != TinyOPDS.Properties.Settings.Default.UseAuthorsAliases)) - { - // Reload library - _watcher.IsEnabled = false; - Library.LoadAsync(); - } - } - - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * TinyOPDS main UI thread + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Xml.Linq; +using System.Threading; +using System.Net; + +using TinyOPDS.Data; +using TinyOPDS.Scanner; +using TinyOPDS.OPDS; +using TinyOPDS.Server; +using UPnP; + +namespace TinyOPDS +{ + public partial class MainForm : Form + { + OPDSServer _server; + Thread _serverThread; + FileScanner _scanner = new FileScanner(); + Watcher _watcher; + DateTime _scanStartTime; + UPnPController _upnpController = new UPnPController(); + NotifyIcon _notifyIcon = new NotifyIcon(); + BindingSource bs = new BindingSource(); + System.Windows.Forms.Timer _updateChecker = new System.Windows.Forms.Timer(); + string _updateUrl = string.Empty; + + #region Statistical information + int _fb2Count, _epubCount, _skippedFiles, _invalidFiles, _duplicates; + #endregion + + private const string urlTemplate = "http://{0}:{1}/{2}"; + + #region Initialization and startup + + public MainForm() + { + Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; + + AppDomain currentDomain = AppDomain.CurrentDomain; + currentDomain.UnhandledException += currentDomain_UnhandledException; + + Log.WriteLine("TinyOPDS version {0}.{1} started", Utils.Version.Major, Utils.Version.Minor); + + InitializeComponent(); + + // Assign combo data source to the list of all available interfaces + interfaceCombo.DataSource = UPnPController.LocalInterfaces; + interfaceCombo.DataBindings.Add(new Binding("SelectedIndex", Properties.Settings.Default, "LocalInterfaceIndex", false, DataSourceUpdateMode.OnPropertyChanged)); + + logVerbosity.DataBindings.Add(new Binding("SelectedIndex", Properties.Settings.Default, "LogLevel", false, DataSourceUpdateMode.OnPropertyChanged)); + updateCombo.DataBindings.Add(new Binding("SelectedIndex", Properties.Settings.Default, "UpdatesCheck", false, DataSourceUpdateMode.OnPropertyChanged)); + + this.PerformLayout(); + + // Manually assign icons from resources (fix for Mono) + this.Icon = Properties.Resources.trayIcon; + _notifyIcon.ContextMenuStrip = this.contextMenuStrip; + _notifyIcon.Icon = Properties.Resources.trayIcon; + _notifyIcon.MouseClick += notifyIcon1_MouseClick; + _notifyIcon.BalloonTipClicked += _notifyIcon_BalloonTipClicked; + _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; + + // Init localization service + Localizer.Init(); + Localizer.AddMenu(contextMenuStrip); + langCombo.DataSource = Localizer.Languages.ToArray(); + + // Load application settings + LoadSettings(); + + // Initialize update checker timer to tick every minute + _updateChecker.Interval = 1000 * 60; + _updateChecker.Tick += _updateChecker_Tick; + + // Setup credentials grid + bs.AddingNew += bs_AddingNew; + bs.AllowNew = true; + bs.DataSource = HttpProcessor.Credentials; + dataGridView1.DataSource = bs; + bs.CurrentItemChanged += bs_CurrentItemChanged; + foreach (DataGridViewColumn col in dataGridView1.Columns) col.Width = 180; + + Library.LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); + Library.LibraryLoaded += (_, __) => + { + UpdateInfo(); + _watcher.DirectoryToWatch = Library.LibraryPath; + _watcher.IsEnabled = TinyOPDS.Properties.Settings.Default.WatchLibrary; + }; + + // Create file watcher + _watcher = new Watcher(Library.LibraryPath); + _watcher.OnBookAdded += (object sender, BookAddedEventArgs e) => + { + if (e.BookType == BookType.FB2) _fb2Count++; else _epubCount++; + UpdateInfo(); + Log.WriteLine(LogLevel.Info, "Added: \"{0}\"", e.BookPath); + }; + _watcher.OnInvalidBook += (_, __) => + { + _invalidFiles++; + UpdateInfo(); + }; + _watcher.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => + { + _skippedFiles = _e.Count; + UpdateInfo(); + }; + + _watcher.OnBookDeleted += (object sender, BookDeletedEventArgs e) => + { + UpdateInfo(); + Log.WriteLine(LogLevel.Info, "Deleted: \"{0}\"", e.BookPath); + }; + _watcher.IsEnabled = false; + + intLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); + intWebLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); + + // Start OPDS server + StartHttpServer(); + + // Set server statistics handler + HttpServer.ServerStatistics.StatisticsUpdated += (_, __) => + { + this.BeginInvoke((MethodInvoker)delegate + { + statRequests.Text = HttpServer.ServerStatistics.GetRequests.ToString(); + statBooks.Text = HttpServer.ServerStatistics.BooksSent.ToString(); + statImages.Text = HttpServer.ServerStatistics.ImagesSent.ToString(); + statUniqueClients.Text = HttpServer.ServerStatistics.UniqueClientsCount.ToString(); + statGoodLogins.Text = HttpServer.ServerStatistics.SuccessfulLoginAttempts.ToString(); + statWrongLogins.Text = HttpServer.ServerStatistics.WrongLoginAttempts.ToString(); + statBannedClients.Text = HttpServer.ServerStatistics.BannedClientsCount.ToString(); + }); + }; + + _scanStartTime = DateTime.Now; + _notifyIcon.Visible = TinyOPDS.Properties.Settings.Default.CloseToTray; + } + + /// + /// We should call DiscoverAsync when windows handle already created (invoker used) + /// + /// + /// + private void MainForm_Load(object sender, EventArgs e) + { + _upnpController.DiscoverCompleted += _upnpController_DiscoverCompleted; + _upnpController.DiscoverAsync(TinyOPDS.Properties.Settings.Default.UseUPnP); + } + + /// + /// Process unhandled exceptions + /// + /// + /// + void currentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs args) + { + if (args != null) + { + Exception e = (Exception)args.ExceptionObject; + if (e != null) + { + Log.WriteLine(LogLevel.Error, "{2}: {0}\nStack trace: {1}", e.Message, e.StackTrace, args.IsTerminating ? "Fatal exception" : "Unhandled exception"); + } + else + { + Log.WriteLine(LogLevel.Error, "Unhandled exception, args.ExceptionObject is null"); + } + } + else + { + Log.WriteLine(LogLevel.Error, "Unhandled exception, args is null"); + } + } + + void _upnpController_DiscoverCompleted(object sender, EventArgs e) + { + if (!IsDisposed && _upnpController != null) + { + this.BeginInvoke((MethodInvoker)delegate + { + extLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, TinyOPDS.Properties.Settings.Default.RootPrefix); + extWebLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, TinyOPDS.Properties.Settings.Default.HttpPrefix); + if (_upnpController.UPnPReady) + { + openPort.Enabled = true; + if (TinyOPDS.Properties.Settings.Default.OpenNATPort) openPort_CheckedChanged(this, null); + } + }); + } + } + + #endregion + + #region Application settings + + private void LoadSettings() + { + // Setup link labels + converterLinkLabel.Links.Add(0, converterLinkLabel.Text.Length, "http://fb2epub.net/files/Fb2ePubSetup_1_1_3.zip"); + linkLabel3.Links.Add(0, linkLabel3.Text.Length, "https://code.google.com/p/fb2librarynet/"); + linkLabel5.Links.Add(0, linkLabel5.Text.Length, "http://epubreader.codeplex.com/"); + linkLabel4.Links.Add(0, linkLabel4.Text.Length, "http://dotnetzip.codeplex.com/"); + linkLabel6.Links.Add(0, linkLabel6.Text.Length, "http://www.fb2library.net/projects/fb2fix"); + + // Setup settings controls + libraryPath.Text = TinyOPDS.Properties.Settings.Default.LibraryPath; + if (!string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.LibraryPath)) + { + databaseFileName.Text = Utils.CreateGuid(Utils.IsoOidNamespace, TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName()).ToString() + ".db"; + } + if (Utils.IsLinux) startWithWindows.Enabled = false; + if (string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath)) + { + if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("ProgramFiles"))) + { + if (File.Exists(Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "FB2ePub\\Fb2ePub.exe"))) + { + convertorPath.Text = TinyOPDS.Properties.Settings.Default.ConvertorPath = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "FB2ePub"); + } + } + } + else convertorPath.Text = TinyOPDS.Properties.Settings.Default.ConvertorPath; + converterLinkLabel.Visible = string.IsNullOrEmpty(convertorPath.Text); + + // We should update all invisible controls + interfaceCombo.SelectedIndex = Math.Min(UPnPController.LocalInterfaces.Count - 1, TinyOPDS.Properties.Settings.Default.LocalInterfaceIndex); + logVerbosity.SelectedIndex = Math.Min(2, TinyOPDS.Properties.Settings.Default.LogLevel); + updateCombo.SelectedIndex = Math.Min(2, TinyOPDS.Properties.Settings.Default.UpdatesCheck); + langCombo.SelectedValue = TinyOPDS.Properties.Settings.Default.Language; + sortOrderCombo.SelectedIndex = TinyOPDS.Properties.Settings.Default.SortOrder; + newBooksPeriodCombo.SelectedIndex = TinyOPDS.Properties.Settings.Default.NewBooksPeriod; + + openPort.Checked = TinyOPDS.Properties.Settings.Default.UseUPnP ? TinyOPDS.Properties.Settings.Default.OpenNATPort : false; + banClients.Enabled = rememberClients.Enabled = dataGridView1.Enabled = TinyOPDS.Properties.Settings.Default.UseHTTPAuth; + wrongAttemptsCount.Enabled = banClients.Checked && useHTTPAuth.Checked; + + _notifyIcon.Visible = TinyOPDS.Properties.Settings.Default.CloseToTray; + _updateChecker.Start(); + + // Load saved credentials + try + { + HttpProcessor.Credentials.Clear(); + string[] pairs = Crypt.DecryptStringAES(TinyOPDS.Properties.Settings.Default.Credentials, urlTemplate).Split(';'); + foreach (string pair in pairs) + { + string[] cred = pair.Split(':'); + if (cred.Length == 2) HttpProcessor.Credentials.Add( new Credential(cred[0], cred[1])); + } + } + catch { } + } + + private void SaveSettings() + { + TinyOPDS.Properties.Settings.Default.LibraryPath = libraryPath.Text.SanitizePathName(); + TinyOPDS.Properties.Settings.Default.Language = langCombo.SelectedValue as string; + TinyOPDS.Properties.Settings.Default.Save(); + } + + #endregion + + #region Credentials handling + + private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) + { + if (dataGridView1.Columns[e.ColumnIndex].Name == "Password" && e.Value != null) + { + dataGridView1.Rows[e.RowIndex].Tag = e.Value; + e.Value = new String('*', e.Value.ToString().Length); + } + } + + void bs_AddingNew(object sender, AddingNewEventArgs e) + { + e.NewObject = new Credential("", ""); + } + + void bs_CurrentItemChanged(object sender, EventArgs e) + { + string s = string.Empty; + foreach (Credential cred in HttpProcessor.Credentials) s += cred.User + ":" + cred.Password + ";"; + try + { + TinyOPDS.Properties.Settings.Default.Credentials = string.IsNullOrEmpty(s) ? string.Empty : Crypt.EncryptStringAES(s, urlTemplate); + } + finally + { + TinyOPDS.Properties.Settings.Default.Save(); + } + } + + #endregion + + #region Library scanning support + + private void libraryPath_TextChanged(object sender, EventArgs e) + { + databaseFileName.Text = Utils.CreateGuid(Utils.IsoOidNamespace, libraryPath.Text.SanitizePathName()).ToString() + ".db"; + } + + private void libraryPath_Validated(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(databaseFileName.Text) && !Library.LibraryPath.Equals(databaseFileName.Text.SanitizePathName()) && + Directory.Exists(libraryPath.Text.SanitizePathName())) + { + if (Library.IsChanged) Library.Save(); + Library.LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath = libraryPath.Text.SanitizePathName(); + booksInDB.Text = string.Format("{0} fb2: {1} epub: {2}", 0, 0, 0); + databaseFileName.Text = Utils.CreateGuid(Utils.IsoOidNamespace, TinyOPDS.Properties.Settings.Default.LibraryPath).ToString() + ".db"; + _watcher.IsEnabled = false; + // Reload library + Library.LoadAsync(); + } + else + { + if (!string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.LibraryPath)) libraryPath.Text = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); + else libraryPath.Undo(); + } + } + + private void folderButton_Click(object sender, EventArgs e) + { + using (FolderBrowserDialog dialog = new FolderBrowserDialog()) + { + dialog.SelectedPath = (sender as Button == folderButton) ? libraryPath.Text : convertorPath.Text; + if (dialog.ShowDialog() == DialogResult.OK) + { + if (sender as Button == folderButton) + { + libraryPath.Text = dialog.SelectedPath.SanitizePathName(); + libraryPath_Validated(sender, e); + } + else + { + convertorPath.Text = dialog.SelectedPath; + convertorPath_Validated(sender, e); + } + } + } + } + + private void scannerButton_Click(object sender, EventArgs e) + { + if (_scanner.Status != FileScannerStatus.SCANNING) + { + _scanner.OnBookFound += scanner_OnBookFound; + _scanner.OnInvalidBook += (_, __) => { _invalidFiles++; }; + _scanner.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => + { + _skippedFiles = _e.Count; + UpdateInfo(); + }; + _scanner.OnScanCompleted += (_, __) => + { + Library.Save(); + UpdateInfo(true); + + Log.WriteLine("Directory scanner completed"); + }; + _fb2Count = _epubCount = _skippedFiles = _invalidFiles = _duplicates = 0; + _scanStartTime = DateTime.Now; + startTime.Text = _scanStartTime.ToString(@"hh\:mm\:ss"); + _scanner.Start(libraryPath.Text.SanitizePathName()); + scannerButton.Text = Localizer.Text("Stop scanning"); + + Log.WriteLine("Directory scanner started"); + } + else + { + _scanner.Stop(); + Library.Save(); + UpdateInfo(true); + scannerButton.Text = Localizer.Text("Start scanning"); + + Log.WriteLine("Directory scanner stopped"); + } + } + + void scanner_OnBookFound(object sender, BookFoundEventArgs e) + { + if (Library.Add(e.Book)) + { + if (e.Book.BookType == BookType.FB2) _fb2Count++; else _epubCount++; + } + else _duplicates++; + if (Library.Count % 500 == 0) Library.Save(); + if (Library.Count % 20000 == 0) GC.Collect(); + UpdateInfo(); + } + + private void UpdateInfo(bool IsScanFinished = false) + { + if (this.InvokeRequired) { this.BeginInvoke((MethodInvoker)delegate { internalUpdateInfo(IsScanFinished); }); } + else { internalUpdateInfo(IsScanFinished); } + } + + private void internalUpdateInfo(bool IsScanFinished) + { + booksInDB.Text = string.Format("{0} fb2: {1} epub: {2}", Library.Count, Library.FB2Count, Library.EPUBCount); + booksFound.Text = string.Format("fb2: {0} epub: {1}", _fb2Count, _epubCount); + skippedBooks.Text = _skippedFiles.ToString(); + invalidBooks.Text = _invalidFiles.ToString(); + duplicates.Text = _duplicates.ToString(); + int totalBooksProcessed = _fb2Count + _epubCount + _skippedFiles + _invalidFiles + _duplicates; + booksProcessed.Text = totalBooksProcessed.ToString(); + + TimeSpan dt = DateTime.Now.Subtract(_scanStartTime); + elapsedTime.Text = dt.ToString(@"hh\:mm\:ss"); + rate.Text = (dt.TotalSeconds) > 0 ? string.Format("{0:0.} books/min", totalBooksProcessed / dt.TotalSeconds * 60) : "---"; + if (scannerButton.Enabled) + { + status.Text = IsScanFinished ? Localizer.Text("FINISHED") : (_scanner.Status == FileScannerStatus.SCANNING ? Localizer.Text("SCANNING") : Localizer.Text("STOPPED")); + scannerButton.Text = (_scanner.Status == FileScannerStatus.SCANNING) ? Localizer.Text("Stop scanning") : Localizer.Text("Start scanning"); + } + } + + #endregion + + #region HTTP (OPDS) server & network support + + private void serverButton_Click(object sender, EventArgs e) + { + if (_server == null) StartHttpServer(); else StopHttpServer(); + } + + private void StartHttpServer() + { + // Create and start HTTP server + HttpProcessor.AuthorizedClients.Clear(); + HttpProcessor.BannedClients.Clear(); + _server = new OPDSServer(_upnpController.LocalIP, int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort)); + + _serverThread = new Thread(new ThreadStart(_server.Listen)); + _serverThread.Priority = ThreadPriority.BelowNormal; + _serverThread.Start(); + _server.ServerReady.WaitOne(TimeSpan.FromMilliseconds(500)); + if (!_server.IsActive) + { + if (_server.ServerException != null) + { + if (_server.ServerException is System.Net.Sockets.SocketException) + { + MessageBox.Show(string.Format(Localizer.Text("Probably, port {0} is already in use. Please try different port value."), TinyOPDS.Properties.Settings.Default.ServerPort), Localizer.Text("Warning"), MessageBoxButtons.OK, MessageBoxIcon.Warning); + } + else + { + MessageBox.Show(_server.ServerException.Message, Localizer.Text("Error"), MessageBoxButtons.OK, MessageBoxIcon.Error); + } + _server.StopServer(); + _serverThread = null; + _server = null; + } + } + else + { + serverButton.Text = serverMenuItem.Text = Localizer.Text("Stop server"); + Log.WriteLine("HTTP server started"); + } + } + + private void StopHttpServer() + { + if (_server != null) + { + _server.StopServer(); + _serverThread = null; + _server = null; + Log.WriteLine("HTTP server stopped"); + } + serverButton.Text = serverMenuItem.Text = Localizer.Text("Start server"); + } + + private void RestartHttpServer() + { + StopHttpServer(); + StartHttpServer(); + } + + private void useUPnP_CheckStateChanged(object sender, EventArgs e) + { + if (useUPnP.Checked) + { + // Re-detect IP addresses using UPnP + _upnpController.DiscoverAsync(true); + } + else + { + openPort.Checked = openPort.Enabled = false; + } + } + + private void openPort_CheckedChanged(object sender, EventArgs e) + { + if (_upnpController != null && _upnpController.UPnPReady) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + if (openPort.Checked) + { + _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); + + Log.WriteLine("Port {0} forwarded by UPnP", port); + } + else + { + _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); + + Log.WriteLine("Port {0} closed", port); + } + } + } + + private void interfaceCombo_SelectedIndexChanged(object sender, EventArgs e) + { + if (_upnpController != null && _upnpController.InterfaceIndex != interfaceCombo.SelectedIndex) + { + _upnpController.InterfaceIndex = interfaceCombo.SelectedIndex; + intLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); + intWebLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); + + if (TinyOPDS.Properties.Settings.Default.UseUPnP && openPort.Checked) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); + _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); + } + RestartHttpServer(); + } + } + + #endregion + + #region Form minimizing and closing + + private void MainForm_Resize(object sender, EventArgs e) + { + if (TinyOPDS.Properties.Settings.Default.CloseToTray) + { + Visible = (WindowState == FormWindowState.Normal); + windowMenuItem.Text = Localizer.Text("Show window"); + } + } + + private void windowMenuItem_Click(object sender, EventArgs e) + { + if (!ShowInTaskbar) ShowInTaskbar = true; else Visible = !Visible; + if (Visible) WindowState = FormWindowState.Normal; + windowMenuItem.Text = Localizer.Text(Visible ? "Hide window" : "Show window"); + } + + private void notifyIcon1_MouseClick(object sender, MouseEventArgs e) + { + if (e.Button == System.Windows.Forms.MouseButtons.Left) windowMenuItem_Click(this, null); + } + + private bool realExit = false; + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (TinyOPDS.Properties.Settings.Default.CloseToTray && !realExit) + { + e.Cancel = true; + Visible = false; + WindowState = FormWindowState.Minimized; + windowMenuItem.Text = Localizer.Text("Show window"); + } + } + + private void MainForm_FormClosed(object sender, FormClosedEventArgs e) + { + SaveSettings(); + if (_server != null && _server._isActive) + { + _server.StopServer(); + _serverThread = null; + _server = null; + + if (_upnpController != null) + { + if (TinyOPDS.Properties.Settings.Default.UseUPnP) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); + } + _upnpController.DiscoverCompleted -= _upnpController_DiscoverCompleted; + _upnpController.Dispose(); + } + } + + if (_scanner.Status == FileScannerStatus.SCANNING) _scanner.Stop(); + if (Library.IsChanged) Library.Save(); + + _notifyIcon.Visible = false; + + Log.WriteLine("TinyOPDS closed\n"); + } + + private void exitMenuItem_Click(object sender, EventArgs e) + { + realExit = true; + Close(); + } + + #endregion + + #region Form controls handling + + private void convertorPath_Validated(object sender, EventArgs e) + { + if (!string.IsNullOrEmpty(convertorPath.Text) && Directory.Exists(convertorPath.Text) && File.Exists(Path.Combine(convertorPath.Text, Utils.IsLinux ? "fb2toepub" : "Fb2ePub.exe"))) + { + TinyOPDS.Properties.Settings.Default.ConvertorPath = convertorPath.Text; + } + else + { + convertorPath.Text = TinyOPDS.Properties.Settings.Default.ConvertorPath; + } + } + + private void useWatcher_CheckedChanged(object sender, EventArgs e) + { + if (_watcher != null && _watcher.IsEnabled != useWatcher.Checked) + { + _watcher.IsEnabled = useWatcher.Checked; + } + } + + private void closeToTray_CheckedChanged(object sender, EventArgs e) + { + _notifyIcon.Visible = closeToTray.Checked; + } + + private void startWithWindows_CheckedChanged(object sender, EventArgs e) + { + Microsoft.Win32.RegistryKey registryKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); + bool exists = (registryKey.GetValue("TinyOPDS") != null); + if (startWithWindows.Checked && !exists) registryKey.SetValue("TinyOPDS", Application.ExecutablePath); + else if (exists && !startWithWindows.Checked) registryKey.DeleteValue("TinyOPDS"); + } + + private void saveLog_CheckedChanged(object sender, EventArgs e) + { + Log.SaveToFile = label22.Enabled = logVerbosity.Enabled = saveLog.Checked; + } + + private void UpdateServerLinks() + { + if (_upnpController != null) + { + if (_upnpController.LocalIP != null) + { + intLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); + intWebLink.Text = string.Format(urlTemplate, _upnpController.LocalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); + } + if (_upnpController.ExternalIP != null) + { + extLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, rootPrefix.Text); + extWebLink.Text = string.Format(urlTemplate, _upnpController.ExternalIP.ToString(), TinyOPDS.Properties.Settings.Default.ServerPort, webPrefix.Text); + } + } + } + + /// + /// Handle server's root prefix change + /// + /// + /// + private void rootPrefix_TextChanged(object sender, EventArgs e) + { + if (sender is TextBox && (sender as TextBox).CanUndo) + { + if (rootPrefix.Text.EndsWith("/")) rootPrefix.Text = rootPrefix.Text.Remove(rootPrefix.Text.Length - 1); + if (webPrefix.Text.EndsWith("/")) webPrefix.Text = webPrefix.Text.Remove(webPrefix.Text.Length - 1); + if (rootPrefix.Text.ToLower().Equals(webPrefix.Text.ToLower())) + { + MessageBox.Show(Localizer.Text("OPDS and web root prefixes can not be the same."), Localizer.Text("Warning"), MessageBoxButtons.OK, MessageBoxIcon.Warning); + (sender as TextBox).Undo(); + } + UpdateServerLinks(); + } + } + + /// + /// Validate server port + /// + /// + /// + private void serverPort_Validated(object sender, EventArgs e) + { + int port = 8080; + bool valid = int.TryParse(serverPort.Text, out port); + if (valid && port >= 1 && port <= 65535) + { + if (_upnpController != null && _upnpController.UPnPReady && openPort.Checked) + { + openPort.Checked = false; + TinyOPDS.Properties.Settings.Default.ServerPort = port.ToString(); + openPort.Checked = true; + } + else TinyOPDS.Properties.Settings.Default.ServerPort = port.ToString(); + if (_server != null && _server.IsActive) + { + RestartHttpServer(); + } + } + else + { + MessageBox.Show(Localizer.Text("Invalid port value: value must be numeric and in range from 1 to 65535"), Localizer.Text("Warning"), MessageBoxButtons.OK, MessageBoxIcon.Warning); + serverPort.Text = TinyOPDS.Properties.Settings.Default.ServerPort.ToString(); + } + // Update link labels + UpdateServerLinks(); + } + + /// + /// Set UI language + /// + /// + /// + private void langCombo_SelectedValueChanged(object sender, EventArgs e) + { + Localizer.SetLanguage(this, langCombo.SelectedValue as string); + appVersion.Text = string.Format(Localizer.Text("version {0}.{1} {2}"), Utils.Version.Major, Utils.Version.Minor, Utils.Version.Major == 0?" (beta)":""); + scannerButton.Text = Localizer.Text( (_scanner.Status == FileScannerStatus.STOPPED) ? "Start scanning" : "Stop scanning"); + serverButton.Text = Localizer.Text((_server == null) ? "Start server" : "Stop server"); + serverMenuItem.Text = Localizer.Text((_server == null) ? "Start server" : "Stop server"); + windowMenuItem.Text = Localizer.Text(Visible || ShowInTaskbar ? "Hide window" : "Show window"); + logVerbosity.Items[0] = Localizer.Text("Info, warnings and errors"); + logVerbosity.Items[1] = Localizer.Text("Warnings and errors"); + logVerbosity.Items[2] = Localizer.Text("Errors only"); + updateCombo.Items[0] = Localizer.Text("Never"); + updateCombo.Items[1] = Localizer.Text("Once a week"); + updateCombo.Items[2] = Localizer.Text("Once a month"); + sortOrderCombo.Items[0] = Localizer.Text("Latin first"); + sortOrderCombo.Items[1] = Localizer.Text("Cyrillic first"); + newBooksPeriodCombo.Items[0] = Localizer.Text("one week"); + newBooksPeriodCombo.Items[1] = Localizer.Text("two weeks"); + newBooksPeriodCombo.Items[2] = Localizer.Text("three weeks"); + newBooksPeriodCombo.Items[3] = Localizer.Text("month"); + newBooksPeriodCombo.Items[4] = Localizer.Text("month and half"); + newBooksPeriodCombo.Items[5] = Localizer.Text("two month"); + newBooksPeriodCombo.Items[6] = Localizer.Text("three month"); + } + + /// + /// Handle PayPal donation + /// + /// + /// + private void donateButton_Click(object sender, EventArgs e) + { + const string business = "sens.boston@gmail.com", description = "Donation%20for%20the%20TinyOPDS", country = "US", currency = "USD"; + string url = string.Format("https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business={0}&lc={1}&item_name={2}¤cy_code={3}&bn=PP%2dDonationsBF", + business, country, description, currency); + System.Diagnostics.Process.Start(url); + } + + private bool checkUrl(string uriName) + { + Uri uriResult; + bool result = Uri.TryCreate(uriName, UriKind.Absolute, out uriResult); + return result && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); + } + + private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + if (sender is LinkLabel && checkUrl((sender as LinkLabel).Text)) + { + System.Diagnostics.Process.Start((sender as LinkLabel).Text); + } + } + + private void linkLabel3_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + if (sender is LinkLabel && checkUrl((sender as LinkLabel).Links[0].LinkData as string)) + { + System.Diagnostics.Process.Start((sender as LinkLabel).Links[0].LinkData as string); + } + } + + private void useHTTPAuth_CheckedChanged(object sender, EventArgs e) + { + dataGridView1.Enabled = banClients.Enabled = rememberClients.Enabled = useHTTPAuth.Checked; + wrongAttemptsCount.Enabled = banClients.Enabled && banClients.Checked; + } + + private void banClients_CheckedChanged(object sender, EventArgs e) + { + wrongAttemptsCount.Enabled = banClients.Checked; + } + + private void logVerbosity_SelectedIndexChanged(object sender, EventArgs e) + { + Log.VerbosityLevel = (LogLevel)logVerbosity.SelectedIndex; + } + + private void updateCombo_SelectedIndexChanged(object sender, EventArgs e) + { + TinyOPDS.Properties.Settings.Default.UpdatesCheck = updateCombo.SelectedIndex; + } + + private void viewLogFile_Click(object sender, EventArgs e) + { + System.Diagnostics.Process.Start(Log.LogFileName); + } + + private void sortOrderCombo_SelectedIndexChanged(object sender, EventArgs e) + { + TinyOPDS.Properties.Settings.Default.SortOrder = sortOrderCombo.SelectedIndex; + } + + private void newBooksPeriodCombo_SelectedIndexChanged(object sender, EventArgs e) + { + TinyOPDS.Properties.Settings.Default.NewBooksPeriod = newBooksPeriodCombo.SelectedIndex; + } + + #endregion + + #region TinyOPDS updates checker + + /// + /// This timer event should be raised every hour + /// + /// + /// + static int[] checkIntervals = new int[] { 0, 60 * 24 * 7, 60 * 24 * 30, 1}; + static int _timerCallsCount = 0; + void _updateChecker_Tick(object sender, EventArgs e) + { + if (TinyOPDS.Properties.Settings.Default.UpdatesCheck > 0) + { + _updateUrl = string.Empty; + int minutesFromLastCheck = (int)Math.Round(DateTime.Now.Subtract(TinyOPDS.Properties.Settings.Default.LastCheck).TotalMinutes); + if (minutesFromLastCheck >= checkIntervals[TinyOPDS.Properties.Settings.Default.UpdatesCheck]) + { + Log.WriteLine(LogLevel.Info, "Checking software update. Minutes from the last check: {0}", minutesFromLastCheck); + WebClient wc = new WebClient(); + wc.DownloadStringCompleted += wc_DownloadStringCompleted; + wc.DownloadStringAsync(new Uri("http://senssoft.com/tinyopds.txt")); + } + } + + if (TinyOPDS.Properties.Settings.Default.UseUPnP && _timerCallsCount++ > 5) + { + _timerCallsCount = 0; + if (_server != null && _server.IsActive && _server.IsIdle && _upnpController != null && _upnpController.UPnPReady) + { + if (!_upnpController.Discovered) + { + _upnpController.DiscoverAsync(true); + } + else if (openPort.Checked) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); + } + } + } + } + + void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) + { + if (e.Error == null) + { + TinyOPDS.Properties.Settings.Default.LastCheck = DateTime.Now; + TinyOPDS.Properties.Settings.Default.Save(); + + string[] s = e.Result.Split('\n'); + if (s.Length == 2) + { + s[0] = s[0].Replace("\r", ""); + double currentVersion = 0, newVersion = 0; + if (double.TryParse(string.Format("{0}.{1}", Utils.Version.Major, Utils.Version.Minor), out currentVersion)) + { + if (double.TryParse(s[0], out newVersion)) + { + if (newVersion > currentVersion) + { + _updateUrl = s[1]; + _notifyIcon.Visible = true; + _notifyIcon.ShowBalloonTip(30000, Localizer.Text("TinyOPDS: update found"), string.Format(Localizer.Text("Click here to download update v {0}"), s[0]), ToolTipIcon.Info); + } + } + } + } + } + } + + void _notifyIcon_BalloonTipClicked(object sender, EventArgs e) + { + System.Diagnostics.Process.Start(_updateUrl); + } + + void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) + { + _notifyIcon.Visible = Properties.Settings.Default.CloseToTray; + } + + #endregion + + /// + /// This event raised on the checkbox change and should reload library + /// + /// + /// + private void checkBox_CheckedChanged(object sender, EventArgs e) + { + if (((sender as CheckBox).Name.Equals("checkBox1") && (sender as CheckBox).Checked != TinyOPDS.Properties.Settings.Default.LowMemoryProfile) || + ((sender as CheckBox).Name.Equals("checkBox2") && (sender as CheckBox).Checked != TinyOPDS.Properties.Settings.Default.UseAuthorsAliases)) + { + // Reload library + _watcher.IsEnabled = false; + Library.LoadAsync(); + } + } + + } +} diff --git a/trunk/TinyOPDS/MainForm.resx b/TinyOPDS/MainForm.resx similarity index 97% rename from trunk/TinyOPDS/MainForm.resx rename to TinyOPDS/MainForm.resx index acce9e6..2d8292b 100644 --- a/trunk/TinyOPDS/MainForm.resx +++ b/TinyOPDS/MainForm.resx @@ -1,123 +1,123 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + \ No newline at end of file diff --git a/trunk/TinyOPDS/Misc/Crypt.cs b/TinyOPDS/Misc/Crypt.cs similarity index 97% rename from trunk/TinyOPDS/Misc/Crypt.cs rename to TinyOPDS/Misc/Crypt.cs index b383fc4..89210ac 100644 --- a/trunk/TinyOPDS/Misc/Crypt.cs +++ b/TinyOPDS/Misc/Crypt.cs @@ -1,155 +1,155 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module contains string extensions and some helpers - * - ************************************************************/ - -using System; -using System.IO; -using System.Text; -using System.Security.Cryptography; - -namespace TinyOPDS -{ - public class Crypt - { - private static byte[] _salt = Encoding.ASCII.GetBytes("o6806642kbM7c5"); - - /// - /// Encrypt the given string using AES. The string can be decrypted using - /// DecryptStringAES(). The sharedSecret parameters must match. - /// - /// The text to encrypt. - /// A password used to generate a key for encryption. - public static string EncryptStringAES(string plainText, string sharedSecret) - { - if (string.IsNullOrEmpty(plainText)) - throw new ArgumentNullException("plainText"); - if (string.IsNullOrEmpty(sharedSecret)) - throw new ArgumentNullException("sharedSecret"); - - string outStr = null; // Encrypted string to return - RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data. - - try - { - // generate the key from the shared secret and the salt - Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); - - // Create a RijndaelManaged object - aesAlg = new RijndaelManaged(); - aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); - - // Create a decrytor to perform the stream transform. - ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); - - // Create the streams used for encryption. - using (MemoryStream msEncrypt = new MemoryStream()) - { - // prepend the IV - msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int)); - msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); - using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) - { - using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) - { - //Write all data to the stream. - swEncrypt.Write(plainText); - } - } - outStr = Convert.ToBase64String(msEncrypt.ToArray()); - } - } - finally - { - // Clear the RijndaelManaged object. - if (aesAlg != null) - aesAlg.Clear(); - } - - // Return the encrypted bytes from the memory stream. - return outStr; - } - - /// - /// Decrypt the given string. Assumes the string was encrypted using - /// EncryptStringAES(), using an identical sharedSecret. - /// - /// The text to decrypt. - /// A password used to generate a key for decryption. - public static string DecryptStringAES(string cipherText, string sharedSecret) - { - if (string.IsNullOrEmpty(cipherText)) - throw new ArgumentNullException("cipherText"); - if (string.IsNullOrEmpty(sharedSecret)) - throw new ArgumentNullException("sharedSecret"); - - // Declare the RijndaelManaged object - // used to decrypt the data. - RijndaelManaged aesAlg = null; - - // Declare the string used to hold - // the decrypted text. - string plaintext = null; - - try - { - // generate the key from the shared secret and the salt - Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); - - // Create the streams used for decryption. - byte[] bytes = Convert.FromBase64String(cipherText); - using (MemoryStream msDecrypt = new MemoryStream(bytes)) - { - // Create a RijndaelManaged object - // with the specified key and IV. - aesAlg = new RijndaelManaged(); - aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); - // Get the initialization vector from the encrypted stream - aesAlg.IV = ReadByteArray(msDecrypt); - // Create a decrytor to perform the stream transform. - ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); - using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) - { - using (StreamReader srDecrypt = new StreamReader(csDecrypt)) - - // Read the decrypted bytes from the decrypting stream - // and place them in a string. - plaintext = srDecrypt.ReadToEnd(); - } - } - } - finally - { - // Clear the RijndaelManaged object. - if (aesAlg != null) - aesAlg.Clear(); - } - - return plaintext; - } - - private static byte[] ReadByteArray(Stream s) - { - byte[] rawLength = new byte[sizeof(int)]; - if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length) - { - throw new SystemException("Stream did not contain properly formatted byte array"); - } - - byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)]; - if (s.Read(buffer, 0, buffer.Length) != buffer.Length) - { - throw new SystemException("Did not read byte array properly"); - } - - return buffer; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module contains string extensions and some helpers + * + ************************************************************/ + +using System; +using System.IO; +using System.Text; +using System.Security.Cryptography; + +namespace TinyOPDS +{ + public class Crypt + { + private static byte[] _salt = Encoding.ASCII.GetBytes("o6806642kbM7c5"); + + /// + /// Encrypt the given string using AES. The string can be decrypted using + /// DecryptStringAES(). The sharedSecret parameters must match. + /// + /// The text to encrypt. + /// A password used to generate a key for encryption. + public static string EncryptStringAES(string plainText, string sharedSecret) + { + if (string.IsNullOrEmpty(plainText)) + throw new ArgumentNullException("plainText"); + if (string.IsNullOrEmpty(sharedSecret)) + throw new ArgumentNullException("sharedSecret"); + + string outStr = null; // Encrypted string to return + RijndaelManaged aesAlg = null; // RijndaelManaged object used to encrypt the data. + + try + { + // generate the key from the shared secret and the salt + Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); + + // Create a RijndaelManaged object + aesAlg = new RijndaelManaged(); + aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); + + // Create a decrytor to perform the stream transform. + ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); + + // Create the streams used for encryption. + using (MemoryStream msEncrypt = new MemoryStream()) + { + // prepend the IV + msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int)); + msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length); + using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) + { + using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) + { + //Write all data to the stream. + swEncrypt.Write(plainText); + } + } + outStr = Convert.ToBase64String(msEncrypt.ToArray()); + } + } + finally + { + // Clear the RijndaelManaged object. + if (aesAlg != null) + aesAlg.Clear(); + } + + // Return the encrypted bytes from the memory stream. + return outStr; + } + + /// + /// Decrypt the given string. Assumes the string was encrypted using + /// EncryptStringAES(), using an identical sharedSecret. + /// + /// The text to decrypt. + /// A password used to generate a key for decryption. + public static string DecryptStringAES(string cipherText, string sharedSecret) + { + if (string.IsNullOrEmpty(cipherText)) + throw new ArgumentNullException("cipherText"); + if (string.IsNullOrEmpty(sharedSecret)) + throw new ArgumentNullException("sharedSecret"); + + // Declare the RijndaelManaged object + // used to decrypt the data. + RijndaelManaged aesAlg = null; + + // Declare the string used to hold + // the decrypted text. + string plaintext = null; + + try + { + // generate the key from the shared secret and the salt + Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt); + + // Create the streams used for decryption. + byte[] bytes = Convert.FromBase64String(cipherText); + using (MemoryStream msDecrypt = new MemoryStream(bytes)) + { + // Create a RijndaelManaged object + // with the specified key and IV. + aesAlg = new RijndaelManaged(); + aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8); + // Get the initialization vector from the encrypted stream + aesAlg.IV = ReadByteArray(msDecrypt); + // Create a decrytor to perform the stream transform. + ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); + using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (StreamReader srDecrypt = new StreamReader(csDecrypt)) + + // Read the decrypted bytes from the decrypting stream + // and place them in a string. + plaintext = srDecrypt.ReadToEnd(); + } + } + } + finally + { + // Clear the RijndaelManaged object. + if (aesAlg != null) + aesAlg.Clear(); + } + + return plaintext; + } + + private static byte[] ReadByteArray(Stream s) + { + byte[] rawLength = new byte[sizeof(int)]; + if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length) + { + throw new SystemException("Stream did not contain properly formatted byte array"); + } + + byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)]; + if (s.Read(buffer, 0, buffer.Length) != buffer.Length) + { + throw new SystemException("Did not read byte array properly"); + } + + return buffer; + } + } +} diff --git a/trunk/TinyOPDS/Misc/CustomSettingsProvider.cs b/TinyOPDS/Misc/CustomSettingsProvider.cs similarity index 97% rename from trunk/TinyOPDS/Misc/CustomSettingsProvider.cs rename to TinyOPDS/Misc/CustomSettingsProvider.cs index c0f39b3..b2a2e4e 100644 --- a/trunk/TinyOPDS/Misc/CustomSettingsProvider.cs +++ b/TinyOPDS/Misc/CustomSettingsProvider.cs @@ -1,275 +1,275 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines custom settings provider, to define - * straightforward settings folder - * - * That code was copied from the StackOverflow site: - * http://stackoverflow.com/questions/2265271/custom-path-of-the-user-config - * - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Configuration; -using System.Reflection; -using System.Xml.Linq; -using System.IO; - -namespace TinyOPDS -{ - class CustomSettingsProvider : SettingsProvider - { - const string NAME = "name"; - const string SERIALIZE_AS = "serializeAs"; - const string CONFIG = "configuration"; - const string USER_SETTINGS = "userSettings"; - const string SETTING = "setting"; - - /// - /// Loads the file into memory. - /// - public CustomSettingsProvider() - { - SettingsDictionary = new Dictionary(); - } - - /// - /// Override. - /// - public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) - { - base.Initialize(ApplicationName, config); - } - - /// - /// Override. - /// - public override string ApplicationName - { - get - { - return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name; - } - set - { - //do nothing - } - } - - /// - /// Must override this, this is the bit that matches up the designer properties to the dictionary values - /// - /// - /// - /// - public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) - { - //load the file - if (!_loaded) - { - _loaded = true; - LoadValuesFromFile(); - } - - //collection that will be returned. - SettingsPropertyValueCollection values = new SettingsPropertyValueCollection(); - - //iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary - foreach (SettingsProperty setting in collection) - { - SettingsPropertyValue value = new SettingsPropertyValue(setting); - value.IsDirty = false; - - //need the type of the value for the strong typing - var t = Type.GetType(setting.PropertyType.FullName); - - if (SettingsDictionary.ContainsKey(setting.Name)) - { - value.SerializedValue = SettingsDictionary[setting.Name].value; - try - { - value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t); - } - catch - { - value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t); - } - } - else //use defaults in the case where there are no settings yet - { - value.SerializedValue = setting.DefaultValue; - value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t); - } - - values.Add(value); - } - return values; - } - - /// - /// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called - /// - /// - /// - public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) - { - //grab the values from the collection parameter and update the values in our dictionary. - foreach (SettingsPropertyValue value in collection) - { - var setting = new SettingStruct() - { - value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()), - name = value.Name, - serializeAs = value.Property.SerializeAs.ToString() - }; - - if (!SettingsDictionary.ContainsKey(value.Name)) - { - SettingsDictionary.Add(value.Name, setting); - } - else - { - SettingsDictionary[value.Name] = setting; - } - } - - //now that our local dictionary is up-to-date, save it to disk. - SaveValuesToFile(); - } - - /// - /// Loads the values of the file into memory. - /// - private void LoadValuesFromFile() - { - if (!File.Exists(UserConfigPath)) - { - //if the config file is not where it's supposed to be create a new one. - CreateEmptyConfig(); - } - - //load the xml - var configXml = XDocument.Load(UserConfigPath); - - //get all of the elements. - var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING); - - //iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls) - //using "String" as default serializeAs...just in case, no real good reason. - foreach (var element in settingElements) - { - var newSetting = new SettingStruct() - { - name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value, - serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value, - value = element.Value ?? String.Empty - }; - SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting); - } - } - - /// - /// Creates an empty user.config file...looks like the one MS creates. - /// This could be overkill a simple key/value pairing would probably do. - /// - private void CreateEmptyConfig() - { - var doc = new XDocument(); - var declaration = new XDeclaration("1.0", "utf-8", "true"); - var config = new XElement(CONFIG); - var userSettings = new XElement(USER_SETTINGS); - var group = new XElement(typeof(Properties.Settings).FullName); - userSettings.Add(group); - config.Add(userSettings); - doc.Add(config); - doc.Declaration = declaration; - string dirName = Path.GetDirectoryName(UserConfigPath); - try - { - if (!Directory.Exists(dirName)) Directory.CreateDirectory(dirName); - doc.Save(UserConfigPath); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, e.Message); - } - } - - /// - /// Saves the in memory dictionary to the user config file - /// - private void SaveValuesToFile() - { - //load the current xml from the file. - var import = XDocument.Load(UserConfigPath); - - //get the settings group (e.g. ) - var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName); - - //iterate though the dictionary, either updating the value or adding the new setting. - foreach (var entry in SettingsDictionary) - { - var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key); - if (setting == null) //this can happen if a new setting is added via the .settings designer. - { - var newSetting = new XElement(SETTING); - newSetting.Add(new XAttribute(NAME, entry.Value.name)); - newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs)); - newSetting.Value = (entry.Value.value ?? String.Empty); - settingsSection.Add(newSetting); - } - else //update the value if it exists. - { - setting.Value = (entry.Value.value ?? String.Empty); - } - } - try - { - import.Save(UserConfigPath); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, e.Message); - } - } - - /// - /// The setting key this is returning must set before the settings are used. - /// e.g. Properties.Settings.Default.SettingsKey = @"C:\temp\user.config"; - /// - private string UserConfigPath - { - get - { - return Path.Combine(Utils.ServiceFilesLocation, "user.config"); - } - } - - /// - /// In memory storage of the settings values - /// - private Dictionary SettingsDictionary { get; set; } - - /// - /// Helper structure - /// - internal struct SettingStruct - { - internal string name; - internal string serializeAs; - internal string value; - } - - bool _loaded; - } -} - +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines custom settings provider, to define + * straightforward settings folder + * + * That code was copied from the StackOverflow site: + * http://stackoverflow.com/questions/2265271/custom-path-of-the-user-config + * + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Configuration; +using System.Reflection; +using System.Xml.Linq; +using System.IO; + +namespace TinyOPDS +{ + class CustomSettingsProvider : SettingsProvider + { + const string NAME = "name"; + const string SERIALIZE_AS = "serializeAs"; + const string CONFIG = "configuration"; + const string USER_SETTINGS = "userSettings"; + const string SETTING = "setting"; + + /// + /// Loads the file into memory. + /// + public CustomSettingsProvider() + { + SettingsDictionary = new Dictionary(); + } + + /// + /// Override. + /// + public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) + { + base.Initialize(ApplicationName, config); + } + + /// + /// Override. + /// + public override string ApplicationName + { + get + { + return System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name; + } + set + { + //do nothing + } + } + + /// + /// Must override this, this is the bit that matches up the designer properties to the dictionary values + /// + /// + /// + /// + public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection) + { + //load the file + if (!_loaded) + { + _loaded = true; + LoadValuesFromFile(); + } + + //collection that will be returned. + SettingsPropertyValueCollection values = new SettingsPropertyValueCollection(); + + //iterate thought the properties we get from the designer, checking to see if the setting is in the dictionary + foreach (SettingsProperty setting in collection) + { + SettingsPropertyValue value = new SettingsPropertyValue(setting); + value.IsDirty = false; + + //need the type of the value for the strong typing + var t = Type.GetType(setting.PropertyType.FullName); + + if (SettingsDictionary.ContainsKey(setting.Name)) + { + value.SerializedValue = SettingsDictionary[setting.Name].value; + try + { + value.PropertyValue = Convert.ChangeType(SettingsDictionary[setting.Name].value, t); + } + catch + { + value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t); + } + } + else //use defaults in the case where there are no settings yet + { + value.SerializedValue = setting.DefaultValue; + value.PropertyValue = Convert.ChangeType(setting.DefaultValue, t); + } + + values.Add(value); + } + return values; + } + + /// + /// Must override this, this is the bit that does the saving to file. Called when Settings.Save() is called + /// + /// + /// + public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection collection) + { + //grab the values from the collection parameter and update the values in our dictionary. + foreach (SettingsPropertyValue value in collection) + { + var setting = new SettingStruct() + { + value = (value.PropertyValue == null ? String.Empty : value.PropertyValue.ToString()), + name = value.Name, + serializeAs = value.Property.SerializeAs.ToString() + }; + + if (!SettingsDictionary.ContainsKey(value.Name)) + { + SettingsDictionary.Add(value.Name, setting); + } + else + { + SettingsDictionary[value.Name] = setting; + } + } + + //now that our local dictionary is up-to-date, save it to disk. + SaveValuesToFile(); + } + + /// + /// Loads the values of the file into memory. + /// + private void LoadValuesFromFile() + { + if (!File.Exists(UserConfigPath)) + { + //if the config file is not where it's supposed to be create a new one. + CreateEmptyConfig(); + } + + //load the xml + var configXml = XDocument.Load(UserConfigPath); + + //get all of the elements. + var settingElements = configXml.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName).Elements(SETTING); + + //iterate through, adding them to the dictionary, (checking for nulls, xml no likey nulls) + //using "String" as default serializeAs...just in case, no real good reason. + foreach (var element in settingElements) + { + var newSetting = new SettingStruct() + { + name = element.Attribute(NAME) == null ? String.Empty : element.Attribute(NAME).Value, + serializeAs = element.Attribute(SERIALIZE_AS) == null ? "String" : element.Attribute(SERIALIZE_AS).Value, + value = element.Value ?? String.Empty + }; + SettingsDictionary.Add(element.Attribute(NAME).Value, newSetting); + } + } + + /// + /// Creates an empty user.config file...looks like the one MS creates. + /// This could be overkill a simple key/value pairing would probably do. + /// + private void CreateEmptyConfig() + { + var doc = new XDocument(); + var declaration = new XDeclaration("1.0", "utf-8", "true"); + var config = new XElement(CONFIG); + var userSettings = new XElement(USER_SETTINGS); + var group = new XElement(typeof(Properties.Settings).FullName); + userSettings.Add(group); + config.Add(userSettings); + doc.Add(config); + doc.Declaration = declaration; + string dirName = Path.GetDirectoryName(UserConfigPath); + try + { + if (!Directory.Exists(dirName)) Directory.CreateDirectory(dirName); + doc.Save(UserConfigPath); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, e.Message); + } + } + + /// + /// Saves the in memory dictionary to the user config file + /// + private void SaveValuesToFile() + { + //load the current xml from the file. + var import = XDocument.Load(UserConfigPath); + + //get the settings group (e.g. ) + var settingsSection = import.Element(CONFIG).Element(USER_SETTINGS).Element(typeof(Properties.Settings).FullName); + + //iterate though the dictionary, either updating the value or adding the new setting. + foreach (var entry in SettingsDictionary) + { + var setting = settingsSection.Elements().FirstOrDefault(e => e.Attribute(NAME).Value == entry.Key); + if (setting == null) //this can happen if a new setting is added via the .settings designer. + { + var newSetting = new XElement(SETTING); + newSetting.Add(new XAttribute(NAME, entry.Value.name)); + newSetting.Add(new XAttribute(SERIALIZE_AS, entry.Value.serializeAs)); + newSetting.Value = (entry.Value.value ?? String.Empty); + settingsSection.Add(newSetting); + } + else //update the value if it exists. + { + setting.Value = (entry.Value.value ?? String.Empty); + } + } + try + { + import.Save(UserConfigPath); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, e.Message); + } + } + + /// + /// The setting key this is returning must set before the settings are used. + /// e.g. Properties.Settings.Default.SettingsKey = @"C:\temp\user.config"; + /// + private string UserConfigPath + { + get + { + return Path.Combine(Utils.ServiceFilesLocation, "user.config"); + } + } + + /// + /// In memory storage of the settings values + /// + private Dictionary SettingsDictionary { get; set; } + + /// + /// Helper structure + /// + internal struct SettingStruct + { + internal string name; + internal string serializeAs; + internal string value; + } + + bool _loaded; + } +} + diff --git a/trunk/TinyOPDS/Misc/Localizer.cs b/TinyOPDS/Misc/Localizer.cs similarity index 97% rename from trunk/TinyOPDS/Misc/Localizer.cs rename to TinyOPDS/Misc/Localizer.cs index ec13374..cf14f3f 100644 --- a/trunk/TinyOPDS/Misc/Localizer.cs +++ b/TinyOPDS/Misc/Localizer.cs @@ -1,253 +1,253 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * Very simple but very effective application localization - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -#if !CONSOLE -using System.Windows.Forms; -#endif -using System.Reflection; - -namespace TinyOPDS -{ - public static class Localizer - { - private static string _lang = "en"; - private static Dictionary _translations = new Dictionary(); - private static XDocument _xml = null; -#if !CONSOLE - private static List _menuItems = new List(); -#endif - /// - /// Static classes don't have a constructors but we need to initialize translations - /// - /// Name of xml translations, added to project as an embedded resource - public static void Init(string xmlFile = "translation.xml") - { - try - { - _xml = XDocument.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + "." + xmlFile)); - } - catch (Exception e) - { - Log.WriteLine("Localizer.Init({0}) exception: {1}", xmlFile, e.Message); - } - } - -#if !CONSOLE - /// - /// Add menu to translator - /// - /// - public static void AddMenu(ContextMenuStrip menu) - { - foreach (ToolStripItem item in menu.Items) _menuItems.Add(item); - } -#endif - - /// - /// Returns supported translations in Dictionary - /// - public static Dictionary Languages - { - get - { - return _xml != null ? _xml.Descendants("language").ToDictionary(d => d.Attribute("id").Value, d => d.Value) : null; - } - } - - /// - /// Current selected language - /// - public static string Language - { - get { return _lang; } - set - { - if (_lang != value && _xml != null) - { - _lang = value; - try - { - // Update localized string dictionary - List t = _xml.Descendants("property") // .Where(a => !a.HasAttributes) - .Descendants("text").Where(b => b.Attribute("lang").Value == "en" || b.Attribute("lang").Value == _lang) - .Select(c => c.Value).ToList(); - _translations.Clear(); - - if (_lang.Equals("en")) - { - for (int i = 0; i < t.Count; i++) - if (!string.IsNullOrEmpty(t[i])) - _translations.Add(t[i], t[i]); - } - else - { - for (int i = 0; i < t.Count / 2; i++) - if (!string.IsNullOrEmpty(t[i * 2])) - _translations.Add(t[i * 2], t[i * 2 + 1]); - } - } - catch (Exception e) - { - Log.WriteLine(".SetLanguage({0}) exception: {1}", _lang, e.Message); - } - } - } - } - -#if !CONSOLE - /// - /// Sets current language - /// - /// - /// - public static void SetLanguage(Form form, string lang) - { - if (_lang != lang && _xml != null) - { - _lang = lang; - try - { - // Update localized string dictionary - List t = _xml.Descendants("property") // .Where(a => !a.HasAttributes) - .Descendants("text").Where(b => b.Attribute("lang").Value == "en" || b.Attribute("lang").Value == _lang) - .Select(c => c.Value).ToList(); - _translations.Clear(); - - if (lang.Equals("en")) - { - for (int i = 0; i < t.Count; i++) - if (!string.IsNullOrEmpty(t[i])) - _translations.Add(t[i], t[i]); - } - else - { - for (int i = 0; i < t.Count / 2; i++) - if (!string.IsNullOrEmpty(t[i * 2])) - _translations.Add(t[i * 2], t[i * 2 + 1]); - } - - // Update form controls - UpdateControls(form); - } - catch (Exception e) - { - Log.WriteLine(".SetLanguage({0},{1}) exception: {2}", form, lang, e.Message); - } - } - } -#endif - - /// - /// Translation helper - /// - /// - /// - public static string Text(string source) - { - return (_translations.ContainsKey(source)) ? _translations[source] : source; - } - -#if !CONSOLE - /// - /// Updates texts for all form controls (if translation exists) - /// - /// Form to be updated - private static void UpdateControls(Form form) - { - // Find all controls - var controls = GetAllControls(form); - - foreach (Control ctrl in new IteratorIsolateCollection(controls)) - { - var xmlProp = _xml.Descendants("property").Where(e => e.Attribute("form") != null && e.Attribute("form").Value.Equals(form.Name) && - e.Attribute("ctrl") != null && e.Attribute("ctrl").Value == ctrl.Name); - if (xmlProp != null && xmlProp.Count() > 0) - { - var trans = xmlProp.FirstOrDefault().Descendants("text").Where(p => p.Attribute("lang").Value == _lang).Select(p => p.Value); - if (trans != null && trans.Count() > 0) ctrl.Text = trans.First() as string; - } - } - - foreach (ToolStripItem ctrl in _menuItems) - { - var xmlProp = _xml.Descendants("property").Where(e => e.Attribute("ctrl") != null && e.Attribute("ctrl").Value == ctrl.Name); - if (xmlProp != null && xmlProp.Count() > 0) - { - var trans = xmlProp.First().Descendants("text").Where(p => p.Attribute("lang").Value == _lang).Select(p => p.Value); - if (trans != null && trans.Count() > 0) ctrl.Text = trans.First() as string; - } - - } - } - - /// - /// Localization helper: scans form and return xml document with controls names and texts - /// - /// Form to localize - /// Current form language - /// Xml document - public static XDocument Setup(Form form, string srcLang = "en") - { - XDocument doc = new XDocument(); - doc.Add(new XElement("root", new XElement("languages"), new XElement("properties"))); - - if (form != null) - { - - var controls = GetAllControls(form); - - foreach (Control ctrl in controls) - { - foreach (var propInfo in ctrl.GetType().GetProperties()) - { - if (propInfo.Name == "Text") - { - try - { - var value = propInfo.GetValue(ctrl, null); - doc.Root.Element("properties").Add( - new XElement("property", - new XAttribute("form", form.Name), - new XAttribute("ctrl", ctrl.Name), - new XElement("text", - new XAttribute("lang", srcLang), value)) - ); - } - catch - { } - } - } - } - } - - return doc; - } - - /// - /// Enums all controls on the form - /// - /// - /// - private static List GetAllControls(Control control) - { - var controls = control.Controls.Cast(); - return (controls.SelectMany(ctrl => GetAllControls(ctrl)).Concat(controls)).ToList(); - } -#endif - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * Very simple but very effective application localization + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +#if !CONSOLE +using System.Windows.Forms; +#endif +using System.Reflection; + +namespace TinyOPDS +{ + public static class Localizer + { + private static string _lang = "en"; + private static Dictionary _translations = new Dictionary(); + private static XDocument _xml = null; +#if !CONSOLE + private static List _menuItems = new List(); +#endif + /// + /// Static classes don't have a constructors but we need to initialize translations + /// + /// Name of xml translations, added to project as an embedded resource + public static void Init(string xmlFile = "translation.xml") + { + try + { + _xml = XDocument.Load(Assembly.GetExecutingAssembly().GetManifestResourceStream(Assembly.GetExecutingAssembly().GetName().Name + "." + xmlFile)); + } + catch (Exception e) + { + Log.WriteLine("Localizer.Init({0}) exception: {1}", xmlFile, e.Message); + } + } + +#if !CONSOLE + /// + /// Add menu to translator + /// + /// + public static void AddMenu(ContextMenuStrip menu) + { + foreach (ToolStripItem item in menu.Items) _menuItems.Add(item); + } +#endif + + /// + /// Returns supported translations in Dictionary + /// + public static Dictionary Languages + { + get + { + return _xml != null ? _xml.Descendants("language").ToDictionary(d => d.Attribute("id").Value, d => d.Value) : null; + } + } + + /// + /// Current selected language + /// + public static string Language + { + get { return _lang; } + set + { + if (_lang != value && _xml != null) + { + _lang = value; + try + { + // Update localized string dictionary + List t = _xml.Descendants("property") // .Where(a => !a.HasAttributes) + .Descendants("text").Where(b => b.Attribute("lang").Value == "en" || b.Attribute("lang").Value == _lang) + .Select(c => c.Value).ToList(); + _translations.Clear(); + + if (_lang.Equals("en")) + { + for (int i = 0; i < t.Count; i++) + if (!string.IsNullOrEmpty(t[i])) + _translations.Add(t[i], t[i]); + } + else + { + for (int i = 0; i < t.Count / 2; i++) + if (!string.IsNullOrEmpty(t[i * 2])) + _translations.Add(t[i * 2], t[i * 2 + 1]); + } + } + catch (Exception e) + { + Log.WriteLine(".SetLanguage({0}) exception: {1}", _lang, e.Message); + } + } + } + } + +#if !CONSOLE + /// + /// Sets current language + /// + /// + /// + public static void SetLanguage(Form form, string lang) + { + if (_lang != lang && _xml != null) + { + _lang = lang; + try + { + // Update localized string dictionary + List t = _xml.Descendants("property") // .Where(a => !a.HasAttributes) + .Descendants("text").Where(b => b.Attribute("lang").Value == "en" || b.Attribute("lang").Value == _lang) + .Select(c => c.Value).ToList(); + _translations.Clear(); + + if (lang.Equals("en")) + { + for (int i = 0; i < t.Count; i++) + if (!string.IsNullOrEmpty(t[i])) + _translations.Add(t[i], t[i]); + } + else + { + for (int i = 0; i < t.Count / 2; i++) + if (!string.IsNullOrEmpty(t[i * 2])) + _translations.Add(t[i * 2], t[i * 2 + 1]); + } + + // Update form controls + UpdateControls(form); + } + catch (Exception e) + { + Log.WriteLine(".SetLanguage({0},{1}) exception: {2}", form, lang, e.Message); + } + } + } +#endif + + /// + /// Translation helper + /// + /// + /// + public static string Text(string source) + { + return (_translations.ContainsKey(source)) ? _translations[source] : source; + } + +#if !CONSOLE + /// + /// Updates texts for all form controls (if translation exists) + /// + /// Form to be updated + private static void UpdateControls(Form form) + { + // Find all controls + var controls = GetAllControls(form); + + foreach (Control ctrl in new IteratorIsolateCollection(controls)) + { + var xmlProp = _xml.Descendants("property").Where(e => e.Attribute("form") != null && e.Attribute("form").Value.Equals(form.Name) && + e.Attribute("ctrl") != null && e.Attribute("ctrl").Value == ctrl.Name); + if (xmlProp != null && xmlProp.Count() > 0) + { + var trans = xmlProp.FirstOrDefault().Descendants("text").Where(p => p.Attribute("lang").Value == _lang).Select(p => p.Value); + if (trans != null && trans.Count() > 0) ctrl.Text = trans.First() as string; + } + } + + foreach (ToolStripItem ctrl in _menuItems) + { + var xmlProp = _xml.Descendants("property").Where(e => e.Attribute("ctrl") != null && e.Attribute("ctrl").Value == ctrl.Name); + if (xmlProp != null && xmlProp.Count() > 0) + { + var trans = xmlProp.First().Descendants("text").Where(p => p.Attribute("lang").Value == _lang).Select(p => p.Value); + if (trans != null && trans.Count() > 0) ctrl.Text = trans.First() as string; + } + + } + } + + /// + /// Localization helper: scans form and return xml document with controls names and texts + /// + /// Form to localize + /// Current form language + /// Xml document + public static XDocument Setup(Form form, string srcLang = "en") + { + XDocument doc = new XDocument(); + doc.Add(new XElement("root", new XElement("languages"), new XElement("properties"))); + + if (form != null) + { + + var controls = GetAllControls(form); + + foreach (Control ctrl in controls) + { + foreach (var propInfo in ctrl.GetType().GetProperties()) + { + if (propInfo.Name == "Text") + { + try + { + var value = propInfo.GetValue(ctrl, null); + doc.Root.Element("properties").Add( + new XElement("property", + new XAttribute("form", form.Name), + new XAttribute("ctrl", ctrl.Name), + new XElement("text", + new XAttribute("lang", srcLang), value)) + ); + } + catch + { } + } + } + } + } + + return doc; + } + + /// + /// Enums all controls on the form + /// + /// + /// + private static List GetAllControls(Control control) + { + var controls = control.Controls.Cast(); + return (controls.SelectMany(ctrl => GetAllControls(ctrl)).Concat(controls)).ToList(); + } +#endif + } +} diff --git a/trunk/TinyOPDS/Misc/Log.cs b/TinyOPDS/Misc/Log.cs similarity index 97% rename from trunk/TinyOPDS/Misc/Log.cs rename to TinyOPDS/Misc/Log.cs index 892a5b1..d13f2ad 100644 --- a/trunk/TinyOPDS/Misc/Log.cs +++ b/TinyOPDS/Misc/Log.cs @@ -1,132 +1,132 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the Log class - * - * TODO: add threading for performance reason (may be, should check) - * - ************************************************************/ - -using System; -using System.IO; -using System.Diagnostics; - -namespace TinyOPDS -{ - /// - /// A lightweight logging class for Silverlight. - /// - internal static class Log - { - internal static string LogFileName = "TinyOPDS.log"; - - /// - /// - /// - internal static LogLevel VerbosityLevel = LogLevel.Info; - - /// - /// Enable or disable logging to file - /// - private static bool _saveToFile = false; - internal static bool SaveToFile - { - get { return _saveToFile; } - set - { - LogFileName = Path.Combine(Utils.ServiceFilesLocation, "TinyOPDS.log"); - _saveToFile = value; - } - } - - /// - /// Writes the args to the default logging output using the format provided. - /// - internal static void WriteLine(string format, params object[] args) - { - WriteLine(LogLevel.Info, format, args); - } - - /// - /// Writes the args to the default logging output using the format provided. - /// - internal static void WriteLine(LogLevel level, string format, params object[] args) - { - if (level >= VerbosityLevel || level == LogLevel.Authentication) - { - string caller = "---"; - - if (level != LogLevel.Authentication) - { - try - { - caller = new StackTrace().GetFrame(2).GetMethod().ReflectedType.Name; - if (caller.StartsWith("<>")) caller = new StackTrace().GetFrame(1).GetMethod().ReflectedType.Name; - } - catch { } - } - else caller = "HTTPServer"; - string prefix = string.Format("{0}\t{1}\t{2}", (level == LogLevel.Info) ? 'I' : ((level == LogLevel.Warning) ? 'W' : ((level == LogLevel.Authentication) ? 'A' : 'E')), caller, (caller.Length > 7 ? "" : "\t")); - - string message = string.Format(prefix + format, args); - Debug.WriteLine(message); - if (SaveToFile) WriteToFile(message); - } - } - - private static object fileSyncObject = new object(); - - /// - /// Writes a line to the current log file. - /// - /// - private static void WriteToFile(string message) - { - lock (fileSyncObject) - { - FileStream fileStream = null; - try - { - fileStream = new FileStream(LogFileName, (File.Exists(LogFileName) ? FileMode.Append : FileMode.Create), FileAccess.Write, FileShare.ReadWrite); - using (StreamWriter writer = new StreamWriter(fileStream)) - { - fileStream = null; - writer.WriteLine(string.Format("{0:MM/dd/yyyy HH:mm:ss.f}\t{1}", DateTime.Now, message), LogFileName); - } - } - finally - { - if (fileStream != null) fileStream.Dispose(); - } - } - } - } - - /// - /// The type of error to log - /// - public enum LogLevel - { - /// - /// A message containing information only. - /// - Info = 0, - /// - /// A non-critical warning error message. - /// - Warning = 1, - /// - /// A fatal error message. - /// - Error = 2, - /// - /// Authentication message, MUST be logged. - /// - Authentication = 3, - } +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the Log class + * + * TODO: add threading for performance reason (may be, should check) + * + ************************************************************/ + +using System; +using System.IO; +using System.Diagnostics; + +namespace TinyOPDS +{ + /// + /// A lightweight logging class for Silverlight. + /// + internal static class Log + { + internal static string LogFileName = "TinyOPDS.log"; + + /// + /// + /// + internal static LogLevel VerbosityLevel = LogLevel.Info; + + /// + /// Enable or disable logging to file + /// + private static bool _saveToFile = false; + internal static bool SaveToFile + { + get { return _saveToFile; } + set + { + LogFileName = Path.Combine(Utils.ServiceFilesLocation, "TinyOPDS.log"); + _saveToFile = value; + } + } + + /// + /// Writes the args to the default logging output using the format provided. + /// + internal static void WriteLine(string format, params object[] args) + { + WriteLine(LogLevel.Info, format, args); + } + + /// + /// Writes the args to the default logging output using the format provided. + /// + internal static void WriteLine(LogLevel level, string format, params object[] args) + { + if (level >= VerbosityLevel || level == LogLevel.Authentication) + { + string caller = "---"; + + if (level != LogLevel.Authentication) + { + try + { + caller = new StackTrace().GetFrame(2).GetMethod().ReflectedType.Name; + if (caller.StartsWith("<>")) caller = new StackTrace().GetFrame(1).GetMethod().ReflectedType.Name; + } + catch { } + } + else caller = "HTTPServer"; + string prefix = string.Format("{0}\t{1}\t{2}", (level == LogLevel.Info) ? 'I' : ((level == LogLevel.Warning) ? 'W' : ((level == LogLevel.Authentication) ? 'A' : 'E')), caller, (caller.Length > 7 ? "" : "\t")); + + string message = string.Format(prefix + format, args); + Debug.WriteLine(message); + if (SaveToFile) WriteToFile(message); + } + } + + private static object fileSyncObject = new object(); + + /// + /// Writes a line to the current log file. + /// + /// + private static void WriteToFile(string message) + { + lock (fileSyncObject) + { + FileStream fileStream = null; + try + { + fileStream = new FileStream(LogFileName, (File.Exists(LogFileName) ? FileMode.Append : FileMode.Create), FileAccess.Write, FileShare.ReadWrite); + using (StreamWriter writer = new StreamWriter(fileStream)) + { + fileStream = null; + writer.WriteLine(string.Format("{0:MM/dd/yyyy HH:mm:ss.f}\t{1}", DateTime.Now, message), LogFileName); + } + } + finally + { + if (fileStream != null) fileStream.Dispose(); + } + } + } + } + + /// + /// The type of error to log + /// + public enum LogLevel + { + /// + /// A message containing information only. + /// + Info = 0, + /// + /// A non-critical warning error message. + /// + Warning = 1, + /// + /// A fatal error message. + /// + Error = 2, + /// + /// Authentication message, MUST be logged. + /// + Authentication = 3, + } } \ No newline at end of file diff --git a/trunk/TinyOPDS/Misc/OPDSComparer.cs b/TinyOPDS/Misc/OPDSComparer.cs similarity index 97% rename from trunk/TinyOPDS/Misc/OPDSComparer.cs rename to TinyOPDS/Misc/OPDSComparer.cs index 391327a..c2b2c54 100644 --- a/trunk/TinyOPDS/Misc/OPDSComparer.cs +++ b/TinyOPDS/Misc/OPDSComparer.cs @@ -1,64 +1,64 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the custom comparer class - * - * TODO: should sort down some rare used chars - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using TinyOPDS.Data; - -namespace TinyOPDS -{ - public class OPDSComparer : IComparer - { - private bool _cyrillcFirst; - - public OPDSComparer(bool cyrillicFirst = true) - { - _cyrillcFirst = cyrillicFirst; - } - - public int Compare(object x, object y) - { - string x1 = string.Empty, y1 = string.Empty; - if (x is string) - { - x1 = x as string; - y1 = y as string; - } - else if (x is Genre) - { - x1 = _cyrillcFirst ? (x as Genre).Translation : (x as Genre).Name; - y1 = _cyrillcFirst ? (y as Genre).Translation : (y as Genre).Name; - } - // Shift "garbage" characters and digits to the end - if (x1.Length > 0 && y1.Length > 0) - { - if (char.IsLetter(x1[0]) && !char.IsLetter(y1[0])) return -1; - else if (!char.IsLetter(x1[0]) && char.IsLetter(y1[0])) return 1; - else if (char.IsLetterOrDigit(x1[0]) && !char.IsLetterOrDigit(y1[0])) return -1; - else if (!char.IsLetterOrDigit(x1[0]) && char.IsLetterOrDigit(y1[0])) return 1; - } - if (_cyrillcFirst && x1.Length > 0 && y1.Length > 0) - { - // Cyrillic letter came first - if (x1[0] > 400 && y1[0] < 400) return -1; - if (x1[0] < 400 && y1[0] > 400) return 1; - } - return string.Compare(x1, y1, true); - } - } - -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the custom comparer class + * + * TODO: should sort down some rare used chars + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using TinyOPDS.Data; + +namespace TinyOPDS +{ + public class OPDSComparer : IComparer + { + private bool _cyrillcFirst; + + public OPDSComparer(bool cyrillicFirst = true) + { + _cyrillcFirst = cyrillicFirst; + } + + public int Compare(object x, object y) + { + string x1 = string.Empty, y1 = string.Empty; + if (x is string) + { + x1 = x as string; + y1 = y as string; + } + else if (x is Genre) + { + x1 = _cyrillcFirst ? (x as Genre).Translation : (x as Genre).Name; + y1 = _cyrillcFirst ? (y as Genre).Translation : (y as Genre).Name; + } + // Shift "garbage" characters and digits to the end + if (x1.Length > 0 && y1.Length > 0) + { + if (char.IsLetter(x1[0]) && !char.IsLetter(y1[0])) return -1; + else if (!char.IsLetter(x1[0]) && char.IsLetter(y1[0])) return 1; + else if (char.IsLetterOrDigit(x1[0]) && !char.IsLetterOrDigit(y1[0])) return -1; + else if (!char.IsLetterOrDigit(x1[0]) && char.IsLetterOrDigit(y1[0])) return 1; + } + if (_cyrillcFirst && x1.Length > 0 && y1.Length > 0) + { + // Cyrillic letter came first + if (x1[0] > 400 && y1[0] < 400) return -1; + if (x1[0] < 400 && y1[0] > 400) return 1; + } + return string.Compare(x1, y1, true); + } + } + +} diff --git a/trunk/TinyOPDS/Misc/ProcessHelper.cs b/TinyOPDS/Misc/ProcessHelper.cs similarity index 97% rename from trunk/TinyOPDS/Misc/ProcessHelper.cs rename to TinyOPDS/Misc/ProcessHelper.cs index f8a10d3..21be954 100644 --- a/trunk/TinyOPDS/Misc/ProcessHelper.cs +++ b/TinyOPDS/Misc/ProcessHelper.cs @@ -1,156 +1,156 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines helper to run console processes in - * background, with output (including errout) collection - * - ************************************************************/ - -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.ComponentModel; -using System.Threading; -using System.Security.Permissions; -using System.Diagnostics; - -namespace TinyOPDS -{ - /// - /// Helper for the external console apps execution in background (no visible window) - /// Stores process output to the observable collection (so, we can bind output to the ListBox) - /// - public class ProcessHelper : IDisposable - { - private bool _disposed = false; - private Process _process = null; - private ObservableCollection _output = new ObservableCollection(); - - /// - /// Default constructor - /// - /// - /// - /// - /// - [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] - public ProcessHelper(string commandPath, string arguments) - { - _process = new Process(); - _process.StartInfo.FileName = commandPath; - _process.StartInfo.Arguments = arguments; - - // set up output redirection - _process.StartInfo.RedirectStandardOutput = true; - _process.StartInfo.RedirectStandardError = true; - _process.EnableRaisingEvents = true; - _process.StartInfo.CreateNoWindow = true; - _process.StartInfo.UseShellExecute = false; - // see below for output handler - _process.ErrorDataReceived += proc_DataReceived; - _process.OutputDataReceived += proc_DataReceived; - _process.Exited += (__, ____) => { if (OnExited != null) OnExited(this, new EventArgs()); }; - } - - [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] - protected virtual void Dispose(bool disposing) - { - if (!this._disposed && disposing) - { - if (_process != null) - { - if (!_process.HasExited && IsRunning) _process.Kill(); - _process.Dispose(); - } - _disposed = true; - } - } - - [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void proc_DataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data != null) - { - _output.Add(e.Data); - } - } - - public void Run() - { - if (_process.Start()) - { - _process.PriorityClass = ProcessPriorityClass.Normal; - _process.BeginErrorReadLine(); - _process.BeginOutputReadLine(); - _isRunning = true; - _process.WaitForExit(); - ExitCode = _process.ExitCode; - _isRunning = false; - } - } - - public void RunAsync(AutoResetEvent waitEvent) - { - BackgroundWorker worker = new BackgroundWorker(); - worker.DoWork += (_, __) => - { - try - { - if (_process.Start()) - { - _process.PriorityClass = ProcessPriorityClass.Normal; - _process.BeginErrorReadLine(); - _process.BeginOutputReadLine(); - _isRunning = true; - _process.WaitForExit(); - ExitCode = _process.ExitCode; - } - } - catch(Exception e) - { - Log.WriteLine(LogLevel.Error, "exception {0}", e.Message); - } - finally - { - if (waitEvent != null) waitEvent.Set(); - _isRunning = false; - worker.Dispose(); - } - }; - worker.RunWorkerAsync(); - } - - /// - /// Raised on process completion - /// - public event EventHandler OnExited; - - /// - /// Process output to stdout - /// - public ObservableCollection ProcessOutput { get { return _output; } } - - /// - /// Return current state of process - /// - private bool _isRunning = false; - public bool IsRunning { get { return _isRunning; } } - - /// - /// Return process exit code - /// - public int ExitCode { get; private set; } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines helper to run console processes in + * background, with output (including errout) collection + * + ************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Threading; +using System.Security.Permissions; +using System.Diagnostics; + +namespace TinyOPDS +{ + /// + /// Helper for the external console apps execution in background (no visible window) + /// Stores process output to the observable collection (so, we can bind output to the ListBox) + /// + public class ProcessHelper : IDisposable + { + private bool _disposed = false; + private Process _process = null; + private ObservableCollection _output = new ObservableCollection(); + + /// + /// Default constructor + /// + /// + /// + /// + /// + [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] + public ProcessHelper(string commandPath, string arguments) + { + _process = new Process(); + _process.StartInfo.FileName = commandPath; + _process.StartInfo.Arguments = arguments; + + // set up output redirection + _process.StartInfo.RedirectStandardOutput = true; + _process.StartInfo.RedirectStandardError = true; + _process.EnableRaisingEvents = true; + _process.StartInfo.CreateNoWindow = true; + _process.StartInfo.UseShellExecute = false; + // see below for output handler + _process.ErrorDataReceived += proc_DataReceived; + _process.OutputDataReceived += proc_DataReceived; + _process.Exited += (__, ____) => { if (OnExited != null) OnExited(this, new EventArgs()); }; + } + + [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] + protected virtual void Dispose(bool disposing) + { + if (!this._disposed && disposing) + { + if (_process != null) + { + if (!_process.HasExited && IsRunning) _process.Kill(); + _process.Dispose(); + } + _disposed = true; + } + } + + [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private void proc_DataReceived(object sender, DataReceivedEventArgs e) + { + if (e.Data != null) + { + _output.Add(e.Data); + } + } + + public void Run() + { + if (_process.Start()) + { + _process.PriorityClass = ProcessPriorityClass.Normal; + _process.BeginErrorReadLine(); + _process.BeginOutputReadLine(); + _isRunning = true; + _process.WaitForExit(); + ExitCode = _process.ExitCode; + _isRunning = false; + } + } + + public void RunAsync(AutoResetEvent waitEvent) + { + BackgroundWorker worker = new BackgroundWorker(); + worker.DoWork += (_, __) => + { + try + { + if (_process.Start()) + { + _process.PriorityClass = ProcessPriorityClass.Normal; + _process.BeginErrorReadLine(); + _process.BeginOutputReadLine(); + _isRunning = true; + _process.WaitForExit(); + ExitCode = _process.ExitCode; + } + } + catch(Exception e) + { + Log.WriteLine(LogLevel.Error, "exception {0}", e.Message); + } + finally + { + if (waitEvent != null) waitEvent.Set(); + _isRunning = false; + worker.Dispose(); + } + }; + worker.RunWorkerAsync(); + } + + /// + /// Raised on process completion + /// + public event EventHandler OnExited; + + /// + /// Process output to stdout + /// + public ObservableCollection ProcessOutput { get { return _output; } } + + /// + /// Return current state of process + /// + private bool _isRunning = false; + public bool IsRunning { get { return _isRunning; } } + + /// + /// Return process exit code + /// + public int ExitCode { get; private set; } + } +} diff --git a/trunk/TinyOPDS/Misc/ServiceTools.cs b/TinyOPDS/Misc/ServiceTools.cs similarity index 97% rename from trunk/TinyOPDS/Misc/ServiceTools.cs rename to TinyOPDS/Misc/ServiceTools.cs index 3e81a80..bd1d6c9 100644 --- a/trunk/TinyOPDS/Misc/ServiceTools.cs +++ b/TinyOPDS/Misc/ServiceTools.cs @@ -1,617 +1,617 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module provides system service manipulation routines - * Code copied from http://dl.dropboxusercontent.com/u/152585/ServiceInstaller.cs - * - ************************************************************/ - -using System; -using System.Text; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace TinyOPDS -{ - /// - /// - /// - [Flags] - public enum ServiceManagerRights - { - /// - /// - /// - Connect = 0x0001, - /// - /// - /// - CreateService = 0x0002, - /// - /// - /// - EnumerateService = 0x0004, - /// - /// - /// - Lock = 0x0008, - /// - /// - /// - QueryLockStatus = 0x0010, - /// - /// - /// - ModifyBootConfig = 0x0020, - /// - /// - /// - StandardRightsRequired = 0xF0000, - /// - /// - /// - AllAccess = (StandardRightsRequired | Connect | CreateService | - EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) - } - - /// - /// - /// - [Flags] - public enum ServiceRights - { - /// - /// - /// - QueryConfig = 0x1, - /// - /// - /// - ChangeConfig = 0x2, - /// - /// - /// - QueryStatus = 0x4, - /// - /// - /// - EnumerateDependants = 0x8, - /// - /// - /// - Start = 0x10, - /// - /// - /// - Stop = 0x20, - /// - /// - /// - PauseContinue = 0x40, - /// - /// - /// - Interrogate = 0x80, - /// - /// - /// - UserDefinedControl = 0x100, - /// - /// - /// - Delete = 0x00010000, - /// - /// - /// - StandardRightsRequired = 0xF0000, - /// - /// - /// - AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | - QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | - Interrogate | UserDefinedControl) - } - - /// - /// - /// - public enum ServiceBootFlag - { - /// - /// - /// - Start = 0x00000000, - /// - /// - /// - SystemStart = 0x00000001, - /// - /// - /// - AutoStart = 0x00000002, - /// - /// - /// - DemandStart = 0x00000003, - /// - /// - /// - Disabled = 0x00000004 - } - - /// - /// - /// - public enum ServiceState - { - /// - /// - /// - Unknown = -1, // The state cannot be (has not been) retrieved. - /// - /// - /// - NotFound = 0, // The service is not known on the host server. - /// - /// - /// - Stop = 1, // The service is NET stopped. - /// - /// - /// - Run = 2, // The service is NET started. - /// - /// - /// - Stopping = 3, - /// - /// - /// - Starting = 4, - } - - /// - /// - /// - public enum ServiceControl - { - /// - /// - /// - Stop = 0x00000001, - /// - /// - /// - Pause = 0x00000002, - /// - /// - /// - Continue = 0x00000003, - /// - /// - /// - Interrogate = 0x00000004, - /// - /// - /// - Shutdown = 0x00000005, - /// - /// - /// - ParamChange = 0x00000006, - /// - /// - /// - NetBindAdd = 0x00000007, - /// - /// - /// - NetBindRemove = 0x00000008, - /// - /// - /// - NetBindEnable = 0x00000009, - /// - /// - /// - NetBindDisable = 0x0000000A - } - - /// - /// - /// - public enum ServiceError - { - /// - /// - /// - Ignore = 0x00000000, - /// - /// - /// - Normal = 0x00000001, - /// - /// - /// - Severe = 0x00000002, - /// - /// - /// - Critical = 0x00000003 - } - - public enum InfoLevel - { - SERVICE_CONFIG_DESCRIPTION = 1, - SERVICE_CONFIG_FAILURE_ACTIONS = 2 - } - - /// - /// Installs and provides functionality for handling windows services - /// - public class ServiceInstaller - { - private const int STANDARD_RIGHTS_REQUIRED = 0xF0000; - private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; - - [StructLayout(LayoutKind.Sequential)] - private class SERVICE_STATUS - { - public int dwServiceType = 0; - public ServiceState dwCurrentState = 0; - public int dwControlsAccepted = 0; - public int dwWin32ExitCode = 0; - public int dwServiceSpecificExitCode = 0; - public int dwCheckPoint = 0; - public int dwWaitHint = 0; - } - - [StructLayout(LayoutKind.Sequential)] - private struct SERVICE_DESCRIPTION - { - public string lpDescription; - } - - [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerA")] - private static extern IntPtr OpenSCManager(string lpMachineName, string - lpDatabaseName, ServiceManagerRights dwDesiredAccess); - [DllImport("advapi32.dll", EntryPoint = "OpenServiceA", - CharSet = CharSet.Ansi)] - private static extern IntPtr OpenService(IntPtr hSCManager, string - lpServiceName, ServiceRights dwDesiredAccess); - [DllImport("advapi32.dll", EntryPoint = "CreateServiceA")] - private static extern IntPtr CreateService(IntPtr hSCManager, string - lpServiceName, string lpDisplayName, ServiceRights dwDesiredAccess, int - dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, - string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string - lpDependencies, string lp, string lpPassword); - [DllImport("advapi32.dll")] - private static extern int CloseServiceHandle(IntPtr hSCObject); - [DllImport("advapi32.dll")] - private static extern int QueryServiceStatus(IntPtr hService, - SERVICE_STATUS lpServiceStatus); - [DllImport("advapi32.dll", SetLastError = true)] - private static extern int DeleteService(IntPtr hService); - [DllImport("advapi32.dll")] - private static extern int ControlService(IntPtr hService, ServiceControl - dwControl, SERVICE_STATUS lpServiceStatus); - [DllImport("advapi32.dll", EntryPoint = "StartServiceA")] - private static extern int StartService(IntPtr hService, int - dwNumServiceArgs, int lpServiceArgVectors); - [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2A")] - private static extern int ChangeServiceConfig2(IntPtr hService, int dwServiceConfigDescription, ref SERVICE_DESCRIPTION lpInfo); - - /// - /// - /// - public ServiceInstaller() - { - } - - /// - /// Takes a service name and tries to stop and then uninstall the windows serviceError - /// - /// The windows service name to uninstall - public static void Uninstall(string ServiceName) - { - IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); - try - { - IntPtr service = OpenService(scman, ServiceName, - ServiceRights.StandardRightsRequired | ServiceRights.Stop | - ServiceRights.QueryStatus); - if (service == IntPtr.Zero) - { - throw new ApplicationException("Service not installed."); - } - try - { - StopService(service); - int ret = DeleteService(service); - if (ret == 0) - { - int error = Marshal.GetLastWin32Error(); - throw new ApplicationException("Could not delete service " + error); - } - } - finally - { - CloseServiceHandle(service); - } - } - finally - { - CloseServiceHandle(scman); - } - } - - /// - /// Accepts a service name and returns true if the service with that service name exists - /// - /// The service name that we will check for existence - /// True if that service exists false otherwise - public static bool ServiceIsInstalled(string ServiceName) - { - IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); - try - { - IntPtr service = OpenService(scman, ServiceName, - ServiceRights.QueryStatus); - if (service == IntPtr.Zero) return false; - CloseServiceHandle(service); - return true; - } - finally - { - CloseServiceHandle(scman); - } - } - - /// - /// Takes a service name, a service display name and the path to the service executable and installs / starts the windows service. - /// - /// The service name that this service will have - /// The display name that this service will have - /// The path to the executable of the service - public static void InstallAndStart(string ServiceName, string DisplayName, string FileName, string Description) - { - IntPtr scman = OpenSCManager(ServiceManagerRights.Connect | - ServiceManagerRights.CreateService); - try - { - IntPtr service = OpenService(scman, ServiceName, ServiceRights.QueryStatus | ServiceRights.Start); - if (service == IntPtr.Zero) - { - service = CreateService(scman, ServiceName, DisplayName, ServiceRights.QueryStatus | ServiceRights.Start, SERVICE_WIN32_OWN_PROCESS, - ServiceBootFlag.AutoStart, ServiceError.Normal, FileName, null, IntPtr.Zero, null, null, null); - - if (service != IntPtr.Zero) - { - // Set service description - SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION(); - desc.lpDescription = Description; - ChangeServiceConfig2(OpenService(scman, ServiceName, ServiceRights.ChangeConfig), (int)InfoLevel.SERVICE_CONFIG_DESCRIPTION, ref desc); - } - - } - if (service == IntPtr.Zero) - { - throw new ApplicationException("Failed to install service."); - } - try - { - StartService(service); - } - finally - { - CloseServiceHandle(service); - } - } - finally - { - CloseServiceHandle(scman); - } - } - - /// - /// Takes a service name and starts it - /// - /// The service name - public static void StartService(string Name) - { - IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); - try - { - IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus | - ServiceRights.Start); - if (hService == IntPtr.Zero) - { - throw new ApplicationException("Could not open service."); - } - try - { - StartService(hService); - } - finally - { - CloseServiceHandle(hService); - } - } - finally - { - CloseServiceHandle(scman); - } - } - - /// - /// Stops the provided windows service - /// - /// The service name that will be stopped - public static void StopService(string Name) - { - IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); - try - { - IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus | - ServiceRights.Stop); - if (hService == IntPtr.Zero) - { - throw new ApplicationException("Could not open service."); - } - try - { - StopService(hService); - } - finally - { - CloseServiceHandle(hService); - } - } - finally - { - CloseServiceHandle(scman); - } - } - - /// - /// Stars the provided windows service - /// - /// The handle to the windows service - private static void StartService(IntPtr hService) - { - StartService(hService, 0, 0); - WaitForServiceStatus(hService, ServiceState.Starting, ServiceState.Run); - } - - /// - /// Stops the provided windows service - /// - /// The handle to the windows service - private static void StopService(IntPtr hService) - { - SERVICE_STATUS status = new SERVICE_STATUS(); - ControlService(hService, ServiceControl.Stop, status); - WaitForServiceStatus(hService, ServiceState.Stopping, ServiceState.Stop); - } - - /// - /// Takes a service name and returns the ServiceState of the corresponding service - /// - /// The service name that we will check for his ServiceState - /// The ServiceState of the service we wanted to check - public static ServiceState GetServiceStatus(string ServiceName) - { - IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); - try - { - IntPtr hService = OpenService(scman, ServiceName, - ServiceRights.QueryStatus); - if (hService == IntPtr.Zero) - { - return ServiceState.NotFound; - } - try - { - return GetServiceStatus(hService); - } - finally - { - CloseServiceHandle(scman); - } - } - finally - { - CloseServiceHandle(scman); - } - } - - /// - /// Gets the service state by using the handle of the provided windows service - /// - /// The handle to the service - /// The ServiceState of the service - private static ServiceState GetServiceStatus(IntPtr hService) - { - SERVICE_STATUS ssStatus = new SERVICE_STATUS(); - if (QueryServiceStatus(hService, ssStatus) == 0) - { - throw new ApplicationException("Failed to query service status."); - } - return ssStatus.dwCurrentState; - } - - /// - /// Returns true when the service status has been changes from wait status to desired status - /// ,this method waits around 10 seconds for this operation. - /// - /// The handle to the service - /// The current state of the service - /// The desired state of the service - /// bool if the service has successfully changed states within the allowed timeline - private static bool WaitForServiceStatus(IntPtr hService, ServiceState WaitStatus, ServiceState DesiredStatus) - { - SERVICE_STATUS ssStatus = new SERVICE_STATUS(); - int dwOldCheckPoint; - int dwStartTickCount; - - QueryServiceStatus(hService, ssStatus); - if (ssStatus.dwCurrentState == DesiredStatus) return true; - dwStartTickCount = Environment.TickCount; - dwOldCheckPoint = ssStatus.dwCheckPoint; - - while (ssStatus.dwCurrentState == WaitStatus) - { - // Do not wait longer than the wait hint. A good interval is - // one tenth the wait hint, but no less than 1 second and no - // more than 10 seconds. - - int dwWaitTime = ssStatus.dwWaitHint / 10; - - if (dwWaitTime < 1000) dwWaitTime = 1000; - else if (dwWaitTime > 10000) dwWaitTime = 10000; - - System.Threading.Thread.Sleep(dwWaitTime); - - // Check the status again. - - if (QueryServiceStatus(hService, ssStatus) == 0) break; - - if (ssStatus.dwCheckPoint > dwOldCheckPoint) - { - // The service is making progress. - dwStartTickCount = Environment.TickCount; - dwOldCheckPoint = ssStatus.dwCheckPoint; - } - else - { - if (Environment.TickCount - dwStartTickCount > ssStatus.dwWaitHint) - { - // No progress made within the wait hint - break; - } - } - } - return (ssStatus.dwCurrentState == DesiredStatus); - } - - /// - /// Opens the service manager - /// - /// The service manager rights - /// the handle to the service manager - private static IntPtr OpenSCManager(ServiceManagerRights Rights) - { - IntPtr scman = OpenSCManager(null, null, Rights); - if (scman == IntPtr.Zero) - { - throw new ApplicationException("Could not connect to service control manager."); - } - return scman; - } - } +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module provides system service manipulation routines + * Code copied from http://dl.dropboxusercontent.com/u/152585/ServiceInstaller.cs + * + ************************************************************/ + +using System; +using System.Text; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace TinyOPDS +{ + /// + /// + /// + [Flags] + public enum ServiceManagerRights + { + /// + /// + /// + Connect = 0x0001, + /// + /// + /// + CreateService = 0x0002, + /// + /// + /// + EnumerateService = 0x0004, + /// + /// + /// + Lock = 0x0008, + /// + /// + /// + QueryLockStatus = 0x0010, + /// + /// + /// + ModifyBootConfig = 0x0020, + /// + /// + /// + StandardRightsRequired = 0xF0000, + /// + /// + /// + AllAccess = (StandardRightsRequired | Connect | CreateService | + EnumerateService | Lock | QueryLockStatus | ModifyBootConfig) + } + + /// + /// + /// + [Flags] + public enum ServiceRights + { + /// + /// + /// + QueryConfig = 0x1, + /// + /// + /// + ChangeConfig = 0x2, + /// + /// + /// + QueryStatus = 0x4, + /// + /// + /// + EnumerateDependants = 0x8, + /// + /// + /// + Start = 0x10, + /// + /// + /// + Stop = 0x20, + /// + /// + /// + PauseContinue = 0x40, + /// + /// + /// + Interrogate = 0x80, + /// + /// + /// + UserDefinedControl = 0x100, + /// + /// + /// + Delete = 0x00010000, + /// + /// + /// + StandardRightsRequired = 0xF0000, + /// + /// + /// + AllAccess = (StandardRightsRequired | QueryConfig | ChangeConfig | + QueryStatus | EnumerateDependants | Start | Stop | PauseContinue | + Interrogate | UserDefinedControl) + } + + /// + /// + /// + public enum ServiceBootFlag + { + /// + /// + /// + Start = 0x00000000, + /// + /// + /// + SystemStart = 0x00000001, + /// + /// + /// + AutoStart = 0x00000002, + /// + /// + /// + DemandStart = 0x00000003, + /// + /// + /// + Disabled = 0x00000004 + } + + /// + /// + /// + public enum ServiceState + { + /// + /// + /// + Unknown = -1, // The state cannot be (has not been) retrieved. + /// + /// + /// + NotFound = 0, // The service is not known on the host server. + /// + /// + /// + Stop = 1, // The service is NET stopped. + /// + /// + /// + Run = 2, // The service is NET started. + /// + /// + /// + Stopping = 3, + /// + /// + /// + Starting = 4, + } + + /// + /// + /// + public enum ServiceControl + { + /// + /// + /// + Stop = 0x00000001, + /// + /// + /// + Pause = 0x00000002, + /// + /// + /// + Continue = 0x00000003, + /// + /// + /// + Interrogate = 0x00000004, + /// + /// + /// + Shutdown = 0x00000005, + /// + /// + /// + ParamChange = 0x00000006, + /// + /// + /// + NetBindAdd = 0x00000007, + /// + /// + /// + NetBindRemove = 0x00000008, + /// + /// + /// + NetBindEnable = 0x00000009, + /// + /// + /// + NetBindDisable = 0x0000000A + } + + /// + /// + /// + public enum ServiceError + { + /// + /// + /// + Ignore = 0x00000000, + /// + /// + /// + Normal = 0x00000001, + /// + /// + /// + Severe = 0x00000002, + /// + /// + /// + Critical = 0x00000003 + } + + public enum InfoLevel + { + SERVICE_CONFIG_DESCRIPTION = 1, + SERVICE_CONFIG_FAILURE_ACTIONS = 2 + } + + /// + /// Installs and provides functionality for handling windows services + /// + public class ServiceInstaller + { + private const int STANDARD_RIGHTS_REQUIRED = 0xF0000; + private const int SERVICE_WIN32_OWN_PROCESS = 0x00000010; + + [StructLayout(LayoutKind.Sequential)] + private class SERVICE_STATUS + { + public int dwServiceType = 0; + public ServiceState dwCurrentState = 0; + public int dwControlsAccepted = 0; + public int dwWin32ExitCode = 0; + public int dwServiceSpecificExitCode = 0; + public int dwCheckPoint = 0; + public int dwWaitHint = 0; + } + + [StructLayout(LayoutKind.Sequential)] + private struct SERVICE_DESCRIPTION + { + public string lpDescription; + } + + [DllImport("advapi32.dll", EntryPoint = "OpenSCManagerA")] + private static extern IntPtr OpenSCManager(string lpMachineName, string + lpDatabaseName, ServiceManagerRights dwDesiredAccess); + [DllImport("advapi32.dll", EntryPoint = "OpenServiceA", + CharSet = CharSet.Ansi)] + private static extern IntPtr OpenService(IntPtr hSCManager, string + lpServiceName, ServiceRights dwDesiredAccess); + [DllImport("advapi32.dll", EntryPoint = "CreateServiceA")] + private static extern IntPtr CreateService(IntPtr hSCManager, string + lpServiceName, string lpDisplayName, ServiceRights dwDesiredAccess, int + dwServiceType, ServiceBootFlag dwStartType, ServiceError dwErrorControl, + string lpBinaryPathName, string lpLoadOrderGroup, IntPtr lpdwTagId, string + lpDependencies, string lp, string lpPassword); + [DllImport("advapi32.dll")] + private static extern int CloseServiceHandle(IntPtr hSCObject); + [DllImport("advapi32.dll")] + private static extern int QueryServiceStatus(IntPtr hService, + SERVICE_STATUS lpServiceStatus); + [DllImport("advapi32.dll", SetLastError = true)] + private static extern int DeleteService(IntPtr hService); + [DllImport("advapi32.dll")] + private static extern int ControlService(IntPtr hService, ServiceControl + dwControl, SERVICE_STATUS lpServiceStatus); + [DllImport("advapi32.dll", EntryPoint = "StartServiceA")] + private static extern int StartService(IntPtr hService, int + dwNumServiceArgs, int lpServiceArgVectors); + [DllImport("advapi32.dll", EntryPoint = "ChangeServiceConfig2A")] + private static extern int ChangeServiceConfig2(IntPtr hService, int dwServiceConfigDescription, ref SERVICE_DESCRIPTION lpInfo); + + /// + /// + /// + public ServiceInstaller() + { + } + + /// + /// Takes a service name and tries to stop and then uninstall the windows serviceError + /// + /// The windows service name to uninstall + public static void Uninstall(string ServiceName) + { + IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); + try + { + IntPtr service = OpenService(scman, ServiceName, + ServiceRights.StandardRightsRequired | ServiceRights.Stop | + ServiceRights.QueryStatus); + if (service == IntPtr.Zero) + { + throw new ApplicationException("Service not installed."); + } + try + { + StopService(service); + int ret = DeleteService(service); + if (ret == 0) + { + int error = Marshal.GetLastWin32Error(); + throw new ApplicationException("Could not delete service " + error); + } + } + finally + { + CloseServiceHandle(service); + } + } + finally + { + CloseServiceHandle(scman); + } + } + + /// + /// Accepts a service name and returns true if the service with that service name exists + /// + /// The service name that we will check for existence + /// True if that service exists false otherwise + public static bool ServiceIsInstalled(string ServiceName) + { + IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); + try + { + IntPtr service = OpenService(scman, ServiceName, + ServiceRights.QueryStatus); + if (service == IntPtr.Zero) return false; + CloseServiceHandle(service); + return true; + } + finally + { + CloseServiceHandle(scman); + } + } + + /// + /// Takes a service name, a service display name and the path to the service executable and installs / starts the windows service. + /// + /// The service name that this service will have + /// The display name that this service will have + /// The path to the executable of the service + public static void InstallAndStart(string ServiceName, string DisplayName, string FileName, string Description) + { + IntPtr scman = OpenSCManager(ServiceManagerRights.Connect | + ServiceManagerRights.CreateService); + try + { + IntPtr service = OpenService(scman, ServiceName, ServiceRights.QueryStatus | ServiceRights.Start); + if (service == IntPtr.Zero) + { + service = CreateService(scman, ServiceName, DisplayName, ServiceRights.QueryStatus | ServiceRights.Start, SERVICE_WIN32_OWN_PROCESS, + ServiceBootFlag.AutoStart, ServiceError.Normal, FileName, null, IntPtr.Zero, null, null, null); + + if (service != IntPtr.Zero) + { + // Set service description + SERVICE_DESCRIPTION desc = new SERVICE_DESCRIPTION(); + desc.lpDescription = Description; + ChangeServiceConfig2(OpenService(scman, ServiceName, ServiceRights.ChangeConfig), (int)InfoLevel.SERVICE_CONFIG_DESCRIPTION, ref desc); + } + + } + if (service == IntPtr.Zero) + { + throw new ApplicationException("Failed to install service."); + } + try + { + StartService(service); + } + finally + { + CloseServiceHandle(service); + } + } + finally + { + CloseServiceHandle(scman); + } + } + + /// + /// Takes a service name and starts it + /// + /// The service name + public static void StartService(string Name) + { + IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); + try + { + IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus | + ServiceRights.Start); + if (hService == IntPtr.Zero) + { + throw new ApplicationException("Could not open service."); + } + try + { + StartService(hService); + } + finally + { + CloseServiceHandle(hService); + } + } + finally + { + CloseServiceHandle(scman); + } + } + + /// + /// Stops the provided windows service + /// + /// The service name that will be stopped + public static void StopService(string Name) + { + IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); + try + { + IntPtr hService = OpenService(scman, Name, ServiceRights.QueryStatus | + ServiceRights.Stop); + if (hService == IntPtr.Zero) + { + throw new ApplicationException("Could not open service."); + } + try + { + StopService(hService); + } + finally + { + CloseServiceHandle(hService); + } + } + finally + { + CloseServiceHandle(scman); + } + } + + /// + /// Stars the provided windows service + /// + /// The handle to the windows service + private static void StartService(IntPtr hService) + { + StartService(hService, 0, 0); + WaitForServiceStatus(hService, ServiceState.Starting, ServiceState.Run); + } + + /// + /// Stops the provided windows service + /// + /// The handle to the windows service + private static void StopService(IntPtr hService) + { + SERVICE_STATUS status = new SERVICE_STATUS(); + ControlService(hService, ServiceControl.Stop, status); + WaitForServiceStatus(hService, ServiceState.Stopping, ServiceState.Stop); + } + + /// + /// Takes a service name and returns the ServiceState of the corresponding service + /// + /// The service name that we will check for his ServiceState + /// The ServiceState of the service we wanted to check + public static ServiceState GetServiceStatus(string ServiceName) + { + IntPtr scman = OpenSCManager(ServiceManagerRights.Connect); + try + { + IntPtr hService = OpenService(scman, ServiceName, + ServiceRights.QueryStatus); + if (hService == IntPtr.Zero) + { + return ServiceState.NotFound; + } + try + { + return GetServiceStatus(hService); + } + finally + { + CloseServiceHandle(scman); + } + } + finally + { + CloseServiceHandle(scman); + } + } + + /// + /// Gets the service state by using the handle of the provided windows service + /// + /// The handle to the service + /// The ServiceState of the service + private static ServiceState GetServiceStatus(IntPtr hService) + { + SERVICE_STATUS ssStatus = new SERVICE_STATUS(); + if (QueryServiceStatus(hService, ssStatus) == 0) + { + throw new ApplicationException("Failed to query service status."); + } + return ssStatus.dwCurrentState; + } + + /// + /// Returns true when the service status has been changes from wait status to desired status + /// ,this method waits around 10 seconds for this operation. + /// + /// The handle to the service + /// The current state of the service + /// The desired state of the service + /// bool if the service has successfully changed states within the allowed timeline + private static bool WaitForServiceStatus(IntPtr hService, ServiceState WaitStatus, ServiceState DesiredStatus) + { + SERVICE_STATUS ssStatus = new SERVICE_STATUS(); + int dwOldCheckPoint; + int dwStartTickCount; + + QueryServiceStatus(hService, ssStatus); + if (ssStatus.dwCurrentState == DesiredStatus) return true; + dwStartTickCount = Environment.TickCount; + dwOldCheckPoint = ssStatus.dwCheckPoint; + + while (ssStatus.dwCurrentState == WaitStatus) + { + // Do not wait longer than the wait hint. A good interval is + // one tenth the wait hint, but no less than 1 second and no + // more than 10 seconds. + + int dwWaitTime = ssStatus.dwWaitHint / 10; + + if (dwWaitTime < 1000) dwWaitTime = 1000; + else if (dwWaitTime > 10000) dwWaitTime = 10000; + + System.Threading.Thread.Sleep(dwWaitTime); + + // Check the status again. + + if (QueryServiceStatus(hService, ssStatus) == 0) break; + + if (ssStatus.dwCheckPoint > dwOldCheckPoint) + { + // The service is making progress. + dwStartTickCount = Environment.TickCount; + dwOldCheckPoint = ssStatus.dwCheckPoint; + } + else + { + if (Environment.TickCount - dwStartTickCount > ssStatus.dwWaitHint) + { + // No progress made within the wait hint + break; + } + } + } + return (ssStatus.dwCurrentState == DesiredStatus); + } + + /// + /// Opens the service manager + /// + /// The service manager rights + /// the handle to the service manager + private static IntPtr OpenSCManager(ServiceManagerRights Rights) + { + IntPtr scman = OpenSCManager(null, null, Rights); + if (scman == IntPtr.Zero) + { + throw new ApplicationException("Could not connect to service control manager."); + } + return scman; + } + } } \ No newline at end of file diff --git a/trunk/TinyOPDS/Misc/StringUtils.cs b/TinyOPDS/Misc/StringUtils.cs similarity index 97% rename from trunk/TinyOPDS/Misc/StringUtils.cs rename to TinyOPDS/Misc/StringUtils.cs index c6ae191..bb0b15d 100644 --- a/trunk/TinyOPDS/Misc/StringUtils.cs +++ b/TinyOPDS/Misc/StringUtils.cs @@ -1,198 +1,198 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines some specific String extensions classes: - * Soundex and Transliteration - * - ************************************************************/ - -using System; -using System.Text; -using System.Collections.Generic; -using System.Linq; -using System.Xml; -using System.Xml.Linq; - -namespace TinyOPDS -{ - public static class StringExtensions - { - public static string ToStringWithDeclaration(this XDocument doc) - { - StringBuilder sb = new StringBuilder(); - XmlWriterSettings xws = new XmlWriterSettings(); - xws.OmitXmlDeclaration = false; - xws.Indent = true; - using (XmlWriter xw = XmlWriter.Create(sb, xws)) doc.WriteTo(xw); - return sb.ToString().Replace("utf-16","utf-8"); - } - - public static string Reverse(this string sentence) - { - string[] words = sentence.Split(' '); - Array.Reverse(words); - return string.Join(" ", words); - } - - public static string DecodeFromBase64(this string encodedData) - { - byte[] encodedDataAsBytes = Convert.FromBase64String(encodedData); - return Encoding.UTF8.GetString(encodedDataAsBytes); - } - - public static string SanitizeFileName(this string fileName) - { - return String.Join("", fileName.Split(System.IO.Path.GetInvalidFileNameChars())); - } - - public static string SanitizePathName(this string pathName) - { - while (pathName.IndexOf("\\\\") >= 0) pathName = pathName.Replace("\\\\", "\\"); - while (pathName.IndexOf("//") >= 0) pathName = pathName.Replace("//", "/"); - if ((pathName.EndsWith("\\") || pathName.EndsWith("/")) && (pathName.Length > 2)) pathName = pathName.Remove(pathName.Length-1); - return pathName; - } - - public static int WordsCount(this string s) - { - return s.Split(' ', ',').Length; - } - - public static string UrlCombine(this string uri1, string uri2) - { - uri1 = uri1.TrimEnd('/'); - uri2 = uri2.TrimStart('/'); - return string.Format("{0}/{1}", uri1, uri2).TrimEnd('/'); - } - - public static bool IsValidUTF(this string s) - { - bool valid = true; - foreach (char c in s) valid &= c != 0xFFFD; - return valid; - } - - public static string SoundexByWord(this string data) - { - var soundexes = new List(); - foreach (var str in data.Split(' ', ',')) - { - soundexes.Add(Soundex(str)); - } - return string.Join(" ", soundexes); - } - - public static string Soundex(string word) - { - word = Transliteration.Front(word, TransliterationType.ISO); - StringBuilder result = new StringBuilder(); - if (word != null && word.Length > 0) - { - string previousCode = "", currentCode = "", currentLetter = ""; - result.Append(word.Substring(0, 1)); - for (int i = 1; i < word.Length; i++) - { - currentLetter = word.Substring(i, 1).ToLower(); - currentCode = ""; - - if ("bfpv".IndexOf(currentLetter) > -1) currentCode = "1"; - else if ("cgjkqsxz".IndexOf(currentLetter) > -1) currentCode = "2"; - else if ("dt".IndexOf(currentLetter) > -1) currentCode = "3"; - else if (currentLetter == "l") currentCode = "4"; - else if ("mn".IndexOf(currentLetter) > -1) currentCode = "5"; - else if (currentLetter == "r") currentCode = "6"; - - if (currentCode != previousCode) result.Append(currentCode); - if (result.Length == 4) break; - if (currentCode != "") previousCode = currentCode; - } - } - - if (result.Length < 4) - result.Append(new String('0', 4 - result.Length)); - - return result.ToString().ToUpper(); - } - } - - #region Transliteration - - public enum TransliterationType - { - GOST, - ISO - } - - public static class Transliteration - { - //ГОСТ 16876-71 - private static Dictionary gostFront = new Dictionary() { - {'Є', "Eh"}, {'І', "I"}, {'і', "i"}, {'№', "#"}, {'є', "eh"}, {'А', "A"}, {'Б', "B"}, {'В', "V"}, {'Г', "G"}, {'Д', "D"}, {'Е', "E"}, {'Ё', "Jo"}, - {'Ж', "Zh"}, {'З', "Z"}, {'И', "I"}, {'Й', "JJ"}, {'К', "K"}, {'Л', "L"}, {'М', "M"}, {'Н', "N"}, {'О', "O"}, {'П', "P"}, {'Р', "R"}, {'С', "S"}, - {'Т', "T"}, {'У', "U"}, {'Ф', "F"}, {'Х', "Kh"}, {'Ц', "C"}, {'Ч', "Ch"}, {'Ш', "Sh"}, {'Щ', "Shh"}, {'Ъ', "'"}, {'Ы', "Y"}, {'Ь', ""}, {'Э', "Eh"}, - {'Ю', "Yu"}, {'Я', "Ya"}, {'а', "a"}, {'б', "b"}, {'в', "v"}, {'г', "g"}, {'д', "d"}, {'е', "e"}, {'ё', "jo"}, {'ж', "zh"}, {'з', "z"}, {'и', "i"}, - {'й', "jj"}, {'к', "k"}, {'л', "l"}, {'м', "m"}, {'н', "n"}, {'о', "o"}, {'п', "p"}, {'р', "r"}, {'с', "s"}, {'т', "t"}, {'у', "u"}, {'ф', "f"}, - {'х', "kh"}, {'ц', "c"}, {'ч', "ch"}, {'ш', "sh"}, {'щ', "shh"}, {'ъ', ""}, {'ы', "y"}, {'ь', ""}, {'э', "eh"}, {'ю', "yu"}, {'я', "ya"}, - {'«', ""}, {'»', ""}, {'—', "-"}, {' ', "_"} - }; - - private static Dictionary gostBack = new Dictionary(); - - //ISO 9-95 - private static Dictionary isoFront = new Dictionary() { - { 'Є', "Ye" }, { 'І', "I" }, { 'Ѓ', "G" }, { 'і', "i" }, { '№', "#" }, { 'є', "ye" }, { 'ѓ', "g" }, { 'А', "A" }, { 'Б', "B" }, { 'В', "V" }, { 'Г', "G" }, - { 'Д', "D" }, { 'Е', "E" }, { 'Ё', "Yo" }, { 'Ж', "Zh" }, { 'З', "Z" }, { 'И', "I" }, { 'Й', "J" }, { 'К', "K" }, { 'Л', "L" }, { 'М', "M" }, { 'Н', "N" }, - { 'О', "O" }, { 'П', "P" }, { 'Р', "R" }, { 'С', "S" }, { 'Т', "T" }, { 'У', "U" }, { 'Ф', "F" }, { 'Х', "X" }, { 'Ц', "C" }, { 'Ч', "Ch" }, { 'Ш', "Sh" }, - { 'Щ', "Shh" }, { 'Ъ', "'" }, { 'Ы', "Y" }, { 'Ь', "" }, { 'Э', "E" }, { 'Ю', "YU" }, { 'Я', "YA" }, { 'а', "a" }, { 'б', "b" }, { 'в', "v" }, { 'г', "g" }, - { 'д', "d" }, { 'е', "e" }, { 'ё', "yo" }, { 'ж', "zh" }, { 'з', "z" }, { 'и', "i" }, { 'й', "j" }, { 'к', "k" }, { 'л', "l" }, { 'м', "m" }, { 'н', "n" }, - { 'о', "o" }, { 'п', "p" }, { 'р', "r" }, { 'с', "s" }, { 'т', "t" }, { 'у', "u" }, { 'ф', "f" }, { 'х', "x" }, { 'ц', "c" }, { 'ч', "ch" }, { 'ш', "sh" }, - { 'щ', "shh" }, { 'ъ', "" }, { 'ы', "y" }, { 'ь', "" }, { 'э', "e" }, { 'ю', "yu" }, { 'я', "ya" }, { '«', "" }, { '»', "" }, { '—', "-" }, { ' ', "_" } - }; - - private static Dictionary isoBack = new Dictionary(); - - static Transliteration() - { - foreach (KeyValuePair pair in gostFront) gostBack[pair.Value] = pair.Key; - foreach (KeyValuePair pair in isoFront) isoBack[pair.Value] = pair.Key; - } - - public static string Front(string text, TransliterationType type = TransliterationType.GOST) - { - string output = string.Empty; - Dictionary dict = (type == TransliterationType.ISO) ? isoFront : gostFront; - foreach (char c in text) output += dict.ContainsKey(c) ? dict[c] : c.ToString(); - return output; - } - - public static string Back(string text, TransliterationType type = TransliterationType.GOST) - { - int l = text.Length; - string output = string.Empty; - Dictionary dict = (type == TransliterationType.ISO) ? isoBack : gostBack; - int i = 0; - while (i < l) - { - string s = text.Substring(i, Math.Min(3, l - i)); - do - { - if (dict.ContainsKey(s)) - { - output += dict[s]; - i += s.Length; - break; - } - s = s.Remove(s.Length - 1); - } while (s.Length > 0); - i += s.Length == 0 ? 3 : 0; - } - return output; - } - } - #endregion -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines some specific String extensions classes: + * Soundex and Transliteration + * + ************************************************************/ + +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using System.Xml; +using System.Xml.Linq; + +namespace TinyOPDS +{ + public static class StringExtensions + { + public static string ToStringWithDeclaration(this XDocument doc) + { + StringBuilder sb = new StringBuilder(); + XmlWriterSettings xws = new XmlWriterSettings(); + xws.OmitXmlDeclaration = false; + xws.Indent = true; + using (XmlWriter xw = XmlWriter.Create(sb, xws)) doc.WriteTo(xw); + return sb.ToString().Replace("utf-16","utf-8"); + } + + public static string Reverse(this string sentence) + { + string[] words = sentence.Split(' '); + Array.Reverse(words); + return string.Join(" ", words); + } + + public static string DecodeFromBase64(this string encodedData) + { + byte[] encodedDataAsBytes = Convert.FromBase64String(encodedData); + return Encoding.UTF8.GetString(encodedDataAsBytes); + } + + public static string SanitizeFileName(this string fileName) + { + return String.Join("", fileName.Split(System.IO.Path.GetInvalidFileNameChars())); + } + + public static string SanitizePathName(this string pathName) + { + while (pathName.IndexOf("\\\\") >= 0) pathName = pathName.Replace("\\\\", "\\"); + while (pathName.IndexOf("//") >= 0) pathName = pathName.Replace("//", "/"); + if ((pathName.EndsWith("\\") || pathName.EndsWith("/")) && (pathName.Length > 2)) pathName = pathName.Remove(pathName.Length-1); + return pathName; + } + + public static int WordsCount(this string s) + { + return s.Split(' ', ',').Length; + } + + public static string UrlCombine(this string uri1, string uri2) + { + uri1 = uri1.TrimEnd('/'); + uri2 = uri2.TrimStart('/'); + return string.Format("{0}/{1}", uri1, uri2).TrimEnd('/'); + } + + public static bool IsValidUTF(this string s) + { + bool valid = true; + foreach (char c in s) valid &= c != 0xFFFD; + return valid; + } + + public static string SoundexByWord(this string data) + { + var soundexes = new List(); + foreach (var str in data.Split(' ', ',')) + { + soundexes.Add(Soundex(str)); + } + return string.Join(" ", soundexes); + } + + public static string Soundex(string word) + { + word = Transliteration.Front(word, TransliterationType.ISO); + StringBuilder result = new StringBuilder(); + if (word != null && word.Length > 0) + { + string previousCode = "", currentCode = "", currentLetter = ""; + result.Append(word.Substring(0, 1)); + for (int i = 1; i < word.Length; i++) + { + currentLetter = word.Substring(i, 1).ToLower(); + currentCode = ""; + + if ("bfpv".IndexOf(currentLetter) > -1) currentCode = "1"; + else if ("cgjkqsxz".IndexOf(currentLetter) > -1) currentCode = "2"; + else if ("dt".IndexOf(currentLetter) > -1) currentCode = "3"; + else if (currentLetter == "l") currentCode = "4"; + else if ("mn".IndexOf(currentLetter) > -1) currentCode = "5"; + else if (currentLetter == "r") currentCode = "6"; + + if (currentCode != previousCode) result.Append(currentCode); + if (result.Length == 4) break; + if (currentCode != "") previousCode = currentCode; + } + } + + if (result.Length < 4) + result.Append(new String('0', 4 - result.Length)); + + return result.ToString().ToUpper(); + } + } + + #region Transliteration + + public enum TransliterationType + { + GOST, + ISO + } + + public static class Transliteration + { + //ГОСТ 16876-71 + private static Dictionary gostFront = new Dictionary() { + {'Є', "Eh"}, {'І', "I"}, {'і', "i"}, {'№', "#"}, {'є', "eh"}, {'А', "A"}, {'Б', "B"}, {'В', "V"}, {'Г', "G"}, {'Д', "D"}, {'Е', "E"}, {'Ё', "Jo"}, + {'Ж', "Zh"}, {'З', "Z"}, {'И', "I"}, {'Й', "JJ"}, {'К', "K"}, {'Л', "L"}, {'М', "M"}, {'Н', "N"}, {'О', "O"}, {'П', "P"}, {'Р', "R"}, {'С', "S"}, + {'Т', "T"}, {'У', "U"}, {'Ф', "F"}, {'Х', "Kh"}, {'Ц', "C"}, {'Ч', "Ch"}, {'Ш', "Sh"}, {'Щ', "Shh"}, {'Ъ', "'"}, {'Ы', "Y"}, {'Ь', ""}, {'Э', "Eh"}, + {'Ю', "Yu"}, {'Я', "Ya"}, {'а', "a"}, {'б', "b"}, {'в', "v"}, {'г', "g"}, {'д', "d"}, {'е', "e"}, {'ё', "jo"}, {'ж', "zh"}, {'з', "z"}, {'и', "i"}, + {'й', "jj"}, {'к', "k"}, {'л', "l"}, {'м', "m"}, {'н', "n"}, {'о', "o"}, {'п', "p"}, {'р', "r"}, {'с', "s"}, {'т', "t"}, {'у', "u"}, {'ф', "f"}, + {'х', "kh"}, {'ц', "c"}, {'ч', "ch"}, {'ш', "sh"}, {'щ', "shh"}, {'ъ', ""}, {'ы', "y"}, {'ь', ""}, {'э', "eh"}, {'ю', "yu"}, {'я', "ya"}, + {'«', ""}, {'»', ""}, {'—', "-"}, {' ', "_"} + }; + + private static Dictionary gostBack = new Dictionary(); + + //ISO 9-95 + private static Dictionary isoFront = new Dictionary() { + { 'Є', "Ye" }, { 'І', "I" }, { 'Ѓ', "G" }, { 'і', "i" }, { '№', "#" }, { 'є', "ye" }, { 'ѓ', "g" }, { 'А', "A" }, { 'Б', "B" }, { 'В', "V" }, { 'Г', "G" }, + { 'Д', "D" }, { 'Е', "E" }, { 'Ё', "Yo" }, { 'Ж', "Zh" }, { 'З', "Z" }, { 'И', "I" }, { 'Й', "J" }, { 'К', "K" }, { 'Л', "L" }, { 'М', "M" }, { 'Н', "N" }, + { 'О', "O" }, { 'П', "P" }, { 'Р', "R" }, { 'С', "S" }, { 'Т', "T" }, { 'У', "U" }, { 'Ф', "F" }, { 'Х', "X" }, { 'Ц', "C" }, { 'Ч', "Ch" }, { 'Ш', "Sh" }, + { 'Щ', "Shh" }, { 'Ъ', "'" }, { 'Ы', "Y" }, { 'Ь', "" }, { 'Э', "E" }, { 'Ю', "YU" }, { 'Я', "YA" }, { 'а', "a" }, { 'б', "b" }, { 'в', "v" }, { 'г', "g" }, + { 'д', "d" }, { 'е', "e" }, { 'ё', "yo" }, { 'ж', "zh" }, { 'з', "z" }, { 'и', "i" }, { 'й', "j" }, { 'к', "k" }, { 'л', "l" }, { 'м', "m" }, { 'н', "n" }, + { 'о', "o" }, { 'п', "p" }, { 'р', "r" }, { 'с', "s" }, { 'т', "t" }, { 'у', "u" }, { 'ф', "f" }, { 'х', "x" }, { 'ц', "c" }, { 'ч', "ch" }, { 'ш', "sh" }, + { 'щ', "shh" }, { 'ъ', "" }, { 'ы', "y" }, { 'ь', "" }, { 'э', "e" }, { 'ю', "yu" }, { 'я', "ya" }, { '«', "" }, { '»', "" }, { '—', "-" }, { ' ', "_" } + }; + + private static Dictionary isoBack = new Dictionary(); + + static Transliteration() + { + foreach (KeyValuePair pair in gostFront) gostBack[pair.Value] = pair.Key; + foreach (KeyValuePair pair in isoFront) isoBack[pair.Value] = pair.Key; + } + + public static string Front(string text, TransliterationType type = TransliterationType.GOST) + { + string output = string.Empty; + Dictionary dict = (type == TransliterationType.ISO) ? isoFront : gostFront; + foreach (char c in text) output += dict.ContainsKey(c) ? dict[c] : c.ToString(); + return output; + } + + public static string Back(string text, TransliterationType type = TransliterationType.GOST) + { + int l = text.Length; + string output = string.Empty; + Dictionary dict = (type == TransliterationType.ISO) ? isoBack : gostBack; + int i = 0; + while (i < l) + { + string s = text.Substring(i, Math.Min(3, l - i)); + do + { + if (dict.ContainsKey(s)) + { + output += dict[s]; + i += s.Length; + break; + } + s = s.Remove(s.Length - 1); + } while (s.Length > 0); + i += s.Length == 0 ? 3 : 0; + } + return output; + } + } + #endregion +} diff --git a/trunk/TinyOPDS/Misc/UPnP.cs b/TinyOPDS/Misc/UPnP.cs similarity index 97% rename from trunk/TinyOPDS/Misc/UPnP.cs rename to TinyOPDS/Misc/UPnP.cs index 830bc50..7dbe5ba 100644 --- a/trunk/TinyOPDS/Misc/UPnP.cs +++ b/TinyOPDS/Misc/UPnP.cs @@ -1,313 +1,313 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * Simple implementation of UPnP controller. Works fine with - * some D-Link and NetGear router models (need more tests) - * - * Based on the Harold Aptroot article & code - * http://www.codeproject.com/Articles/27992/ - * - * TODO: check compatibility with other routers - * - ************************************************************/ - -using System; -using System.Linq; -using System.Collections.Generic; -using System.Text; -using System.Text.RegularExpressions; -using System.Net.Sockets; -using System.Net; -using System.Xml; -using System.IO; -using System.Net.NetworkInformation; -using System.ComponentModel; - -namespace UPnP -{ - public class UPnPController : IDisposable - { - private bool _disposed = false; - private string _serviceUrl; - private BackgroundWorker _worker; - private WebClient _webClient; - - public bool Discovered { get; private set; } - public event EventHandler DiscoverCompleted; - - public bool UPnPReady { get { return !string.IsNullOrEmpty(_serviceUrl); } } - - public UPnPController () - { - Discovered = false; - } - - protected virtual void Dispose(bool disposing) - { - if (!this._disposed && disposing) - { - if (_webClient != null && _webClient.IsBusy) - { - _webClient.CancelAsync(); - _webClient.Dispose(); - } - if (_worker != null) - { - _worker.Dispose(); - } - DiscoverCompleted = null; - _disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void DiscoverAsync(bool useUPnP) - { - _worker = new BackgroundWorker(); - _worker.DoWork += new DoWorkEventHandler(_worker_DoWork); - _worker.RunWorkerAsync(useUPnP); - } - - void _worker_DoWork(object sender, DoWorkEventArgs e) - { - bool detectUPnP = (bool) e.Argument; - if (detectUPnP) - { - NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); - foreach (NetworkInterface adapter in adapters) - { - IPInterfaceProperties properties = adapter.GetIPProperties(); - if (properties.GatewayAddresses != null && properties.GatewayAddresses.Count > 0) - { - foreach (IPAddressInformation uniAddr in properties.UnicastAddresses) - { - if (uniAddr.Address.AddressFamily == AddressFamily.InterNetwork) - { - using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) - { - socket.Bind(new IPEndPoint(uniAddr.Address, 0)); - socket.ReceiveTimeout = 2000; - socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); - string req = "M-SEARCH * HTTP/1.1\r\n" + - "HOST: 239.255.255.250:1900\r\n" + - "ST:upnp:rootdevice\r\n" + - "MAN:\"ssdp:discover\"\r\n" + - "MX:3\r\n\r\n"; - byte[] data = Encoding.ASCII.GetBytes(req); - IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900); - byte[] buffer = new byte[0x1000]; - - for (int i = 0; i < 3; i++) socket.SendTo(data, ipe); - - int length = 0; - do - { - try { length = socket.Receive(buffer); } - catch { break; } - string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower(); - if (resp.Contains("upnp:rootdevice")) - { - resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); - resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); - if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp))) - { - break; - } - } - } while (length > 0); - } - if (UPnPReady) - { - string ip = "127.0.0.0"; - XmlDocument xdoc = SOAPRequest(_serviceUrl, - "" + - "", "GetExternalIPAddress"); - if (xdoc.OuterXml.Contains("NewExternalIPAddress")) - { - XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); - nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); - ip = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; - } - ExternalIP = IPAddress.Parse(ip); - } - Discovered = true; - if (UPnPReady && DiscoverCompleted != null) DiscoverCompleted(this, new EventArgs()); - } - } - } - } - } - // Just detect external IP address - else - { - _webClient = new WebClient(); - _webClient.DownloadStringCompleted += (object o, DownloadStringCompletedEventArgs ea) => - { - if (!_disposed && ea.Error == null && ea.Result != null) - { - Regex ip = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"); - MatchCollection result = ip.Matches(ea.Result); - try { ExternalIP = IPAddress.Parse(result[0].Value); } - catch { ExternalIP = IPAddress.Parse("0.0.0.0"); } - if (DiscoverCompleted != null) DiscoverCompleted(this, new EventArgs()); - } - }; - _webClient.DownloadStringAsync(new Uri("http://myip.dnsdynamic.org")); //new Uri("http://checkip.dyndns.org")); - } - } - - private string GetServiceUrl(string resp) - { -#if false - // UPDATE: registry fix eliminate the IOException but completely ruins UPnP detection (after reboot) - // Prevent IOException - // See https://connect.microsoft.com/VisualStudio/feedback/details/773666/webrequest-create-eats-an-ioexception-on-the-first-call#details - RegistryKey registryKey = null; - registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\.NETFramework", true); - if (registryKey == null) registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework", true); - if (registryKey.GetValue("LegacyWPADSupport") == null) registryKey.SetValue("LegacyWPADSupport", 0); -#endif - try - { - XmlDocument desc = new XmlDocument(); - desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream()); - XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); - nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); - XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); - if (!typen.Value.Contains("InternetGatewayDevice")) return null; - XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr); - if (node == null) return null; - return CombineUrls(resp, node.Value); - } - catch - { - return null; - } - } - - private string CombineUrls(string resp, string p) - { - int n = resp.IndexOf("://"); - n = resp.IndexOf('/', n + 3); - return resp.Substring(0, n) + p; - } - - public void ForwardPort(int port, ProtocolType protocol, string description) - { - if (UPnPReady) - { - SOAPRequest(_serviceUrl, - "" + - "" + port.ToString() + "" + protocol.ToString().ToUpper() + "" + - "" + port.ToString() + "" + LocalIP.ToString() + - "1" + description + - "0", "AddPortMapping"); - } - } - - public void DeleteForwardingRule(int port, ProtocolType protocol) - { - if (UPnPReady) - { - SOAPRequest(_serviceUrl, - "" + - "" + - "" + - "" + port + "" + - "" + protocol.ToString().ToUpper() + "" + - "", "DeletePortMapping"); - } - } - - /// - /// Local network interface index - /// - private int _interfaceIndex = 0; - public int InterfaceIndex - { - get { return _interfaceIndex; } - set { if (value >= 0 && value < LocalInterfaces.Count) _interfaceIndex = value; } - } - - /// - /// Local IP address - /// - /// - public IPAddress LocalIP { get { return LocalInterfaces[InterfaceIndex]; } } - - /// - /// List of all local network interfaces with gateways - /// - private static List _localInterfaces = null; - public static List LocalInterfaces - { - get - { - if (_localInterfaces == null) - { - _localInterfaces = new List(); - foreach (NetworkInterface netif in NetworkInterface.GetAllNetworkInterfaces()) - { - IPInterfaceProperties properties = netif.GetIPProperties(); - foreach (GatewayIPAddressInformation gw in properties.GatewayAddresses) - { - if (!gw.Address.ToString().Equals("0.0.0.0")) - { - foreach (IPAddressInformation unicast in properties.UnicastAddresses) - { - // Lets skip "link local" addresses (RFC 3927), probably this address is disabled - if (unicast.Address.ToString().StartsWith("169.254")) break; - if (unicast.Address.AddressFamily == AddressFamily.InterNetwork) - { - _localInterfaces.Add(unicast.Address); - } - } - } - } - } - // If no network interface detected, add at least a local loopback (127.0.0.1) - if (_localInterfaces.Count == 0) _localInterfaces.Add(IPAddress.Loopback); - } - return _localInterfaces; - } - } - - public IPAddress ExternalIP { get; private set;} - - private static XmlDocument SOAPRequest(string url, string soap, string function) - { - XmlDocument resp = new XmlDocument(); - try - { - string req = "" + - "" + - "" + - soap + - "" + - ""; - WebRequest r = HttpWebRequest.Create(url); - r.Method = "POST"; - byte[] b = Encoding.UTF8.GetBytes(req); - r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\""); - r.ContentType = "text/xml; charset=\"utf-8\""; - r.ContentLength = b.Length; - r.GetRequestStream().Write(b, 0, b.Length); - WebResponse wres = r.GetResponse(); - Stream ress = wres.GetResponseStream(); - resp.Load(ress); - } - catch { } - return resp; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * Simple implementation of UPnP controller. Works fine with + * some D-Link and NetGear router models (need more tests) + * + * Based on the Harold Aptroot article & code + * http://www.codeproject.com/Articles/27992/ + * + * TODO: check compatibility with other routers + * + ************************************************************/ + +using System; +using System.Linq; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; +using System.Net.Sockets; +using System.Net; +using System.Xml; +using System.IO; +using System.Net.NetworkInformation; +using System.ComponentModel; + +namespace UPnP +{ + public class UPnPController : IDisposable + { + private bool _disposed = false; + private string _serviceUrl; + private BackgroundWorker _worker; + private WebClient _webClient; + + public bool Discovered { get; private set; } + public event EventHandler DiscoverCompleted; + + public bool UPnPReady { get { return !string.IsNullOrEmpty(_serviceUrl); } } + + public UPnPController () + { + Discovered = false; + } + + protected virtual void Dispose(bool disposing) + { + if (!this._disposed && disposing) + { + if (_webClient != null && _webClient.IsBusy) + { + _webClient.CancelAsync(); + _webClient.Dispose(); + } + if (_worker != null) + { + _worker.Dispose(); + } + DiscoverCompleted = null; + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public void DiscoverAsync(bool useUPnP) + { + _worker = new BackgroundWorker(); + _worker.DoWork += new DoWorkEventHandler(_worker_DoWork); + _worker.RunWorkerAsync(useUPnP); + } + + void _worker_DoWork(object sender, DoWorkEventArgs e) + { + bool detectUPnP = (bool) e.Argument; + if (detectUPnP) + { + NetworkInterface[] adapters = NetworkInterface.GetAllNetworkInterfaces(); + foreach (NetworkInterface adapter in adapters) + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + if (properties.GatewayAddresses != null && properties.GatewayAddresses.Count > 0) + { + foreach (IPAddressInformation uniAddr in properties.UnicastAddresses) + { + if (uniAddr.Address.AddressFamily == AddressFamily.InterNetwork) + { + using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + { + socket.Bind(new IPEndPoint(uniAddr.Address, 0)); + socket.ReceiveTimeout = 2000; + socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); + string req = "M-SEARCH * HTTP/1.1\r\n" + + "HOST: 239.255.255.250:1900\r\n" + + "ST:upnp:rootdevice\r\n" + + "MAN:\"ssdp:discover\"\r\n" + + "MX:3\r\n\r\n"; + byte[] data = Encoding.ASCII.GetBytes(req); + IPEndPoint ipe = new IPEndPoint(IPAddress.Broadcast, 1900); + byte[] buffer = new byte[0x1000]; + + for (int i = 0; i < 3; i++) socket.SendTo(data, ipe); + + int length = 0; + do + { + try { length = socket.Receive(buffer); } + catch { break; } + string resp = Encoding.ASCII.GetString(buffer, 0, length).ToLower(); + if (resp.Contains("upnp:rootdevice")) + { + resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); + resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); + if (!string.IsNullOrEmpty(_serviceUrl = GetServiceUrl(resp))) + { + break; + } + } + } while (length > 0); + } + if (UPnPReady) + { + string ip = "127.0.0.0"; + XmlDocument xdoc = SOAPRequest(_serviceUrl, + "" + + "", "GetExternalIPAddress"); + if (xdoc.OuterXml.Contains("NewExternalIPAddress")) + { + XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); + nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); + ip = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; + } + ExternalIP = IPAddress.Parse(ip); + } + Discovered = true; + if (UPnPReady && DiscoverCompleted != null) DiscoverCompleted(this, new EventArgs()); + } + } + } + } + } + // Just detect external IP address + else + { + _webClient = new WebClient(); + _webClient.DownloadStringCompleted += (object o, DownloadStringCompletedEventArgs ea) => + { + if (!_disposed && ea.Error == null && ea.Result != null) + { + Regex ip = new Regex(@"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"); + MatchCollection result = ip.Matches(ea.Result); + try { ExternalIP = IPAddress.Parse(result[0].Value); } + catch { ExternalIP = IPAddress.Parse("0.0.0.0"); } + if (DiscoverCompleted != null) DiscoverCompleted(this, new EventArgs()); + } + }; + _webClient.DownloadStringAsync(new Uri("http://myip.dnsdynamic.org")); //new Uri("http://checkip.dyndns.org")); + } + } + + private string GetServiceUrl(string resp) + { +#if false + // UPDATE: registry fix eliminate the IOException but completely ruins UPnP detection (after reboot) + // Prevent IOException + // See https://connect.microsoft.com/VisualStudio/feedback/details/773666/webrequest-create-eats-an-ioexception-on-the-first-call#details + RegistryKey registryKey = null; + registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\.NETFramework", true); + if (registryKey == null) registryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework", true); + if (registryKey.GetValue("LegacyWPADSupport") == null) registryKey.SetValue("LegacyWPADSupport", 0); +#endif + try + { + XmlDocument desc = new XmlDocument(); + desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream()); + XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); + nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); + XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); + if (!typen.Value.Contains("InternetGatewayDevice")) return null; + XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr); + if (node == null) return null; + return CombineUrls(resp, node.Value); + } + catch + { + return null; + } + } + + private string CombineUrls(string resp, string p) + { + int n = resp.IndexOf("://"); + n = resp.IndexOf('/', n + 3); + return resp.Substring(0, n) + p; + } + + public void ForwardPort(int port, ProtocolType protocol, string description) + { + if (UPnPReady) + { + SOAPRequest(_serviceUrl, + "" + + "" + port.ToString() + "" + protocol.ToString().ToUpper() + "" + + "" + port.ToString() + "" + LocalIP.ToString() + + "1" + description + + "0", "AddPortMapping"); + } + } + + public void DeleteForwardingRule(int port, ProtocolType protocol) + { + if (UPnPReady) + { + SOAPRequest(_serviceUrl, + "" + + "" + + "" + + "" + port + "" + + "" + protocol.ToString().ToUpper() + "" + + "", "DeletePortMapping"); + } + } + + /// + /// Local network interface index + /// + private int _interfaceIndex = 0; + public int InterfaceIndex + { + get { return _interfaceIndex; } + set { if (value >= 0 && value < LocalInterfaces.Count) _interfaceIndex = value; } + } + + /// + /// Local IP address + /// + /// + public IPAddress LocalIP { get { return LocalInterfaces[InterfaceIndex]; } } + + /// + /// List of all local network interfaces with gateways + /// + private static List _localInterfaces = null; + public static List LocalInterfaces + { + get + { + if (_localInterfaces == null) + { + _localInterfaces = new List(); + foreach (NetworkInterface netif in NetworkInterface.GetAllNetworkInterfaces()) + { + IPInterfaceProperties properties = netif.GetIPProperties(); + foreach (GatewayIPAddressInformation gw in properties.GatewayAddresses) + { + if (!gw.Address.ToString().Equals("0.0.0.0")) + { + foreach (IPAddressInformation unicast in properties.UnicastAddresses) + { + // Lets skip "link local" addresses (RFC 3927), probably this address is disabled + if (unicast.Address.ToString().StartsWith("169.254")) break; + if (unicast.Address.AddressFamily == AddressFamily.InterNetwork) + { + _localInterfaces.Add(unicast.Address); + } + } + } + } + } + // If no network interface detected, add at least a local loopback (127.0.0.1) + if (_localInterfaces.Count == 0) _localInterfaces.Add(IPAddress.Loopback); + } + return _localInterfaces; + } + } + + public IPAddress ExternalIP { get; private set;} + + private static XmlDocument SOAPRequest(string url, string soap, string function) + { + XmlDocument resp = new XmlDocument(); + try + { + string req = "" + + "" + + "" + + soap + + "" + + ""; + WebRequest r = HttpWebRequest.Create(url); + r.Method = "POST"; + byte[] b = Encoding.UTF8.GetBytes(req); + r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\""); + r.ContentType = "text/xml; charset=\"utf-8\""; + r.ContentLength = b.Length; + r.GetRequestStream().Write(b, 0, b.Length); + WebResponse wres = r.GetResponse(); + Stream ress = wres.GetResponseStream(); + resp.Load(ress); + } + catch { } + return resp; + } + } +} diff --git a/trunk/TinyOPDS/Misc/Utils.cs b/TinyOPDS/Misc/Utils.cs similarity index 97% rename from trunk/TinyOPDS/Misc/Utils.cs rename to TinyOPDS/Misc/Utils.cs index afd1805..a4a5710 100644 --- a/trunk/TinyOPDS/Misc/Utils.cs +++ b/TinyOPDS/Misc/Utils.cs @@ -1,294 +1,294 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module contains string extensions and some helpers - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Linq; -using System.Text; -using System.Security.Principal; - -namespace TinyOPDS -{ - public static class StringExtension - { - /// - /// Capitalize words in the string (for example, author's name: first, middle last), by converting first char of every word to uppercase - /// - /// source string - /// capitalize first word only - /// - public static string Capitalize(this string str, bool onlyFirstWord = false) - { - string[] words = str.Split(' '); - str = string.Empty; - for (int i = 0; i < words.Length; i++) - { - if (!onlyFirstWord || (onlyFirstWord && i == 0)) - { - if (words[i].Length > 1) - { - if (words[i].IsUpper()) words[i] = words[i].ToLower(); - words[i] = char.ToUpper(words[i][0]) + words[i].Substring(1); - } - else - { - words[i] = words[i].ToUpper(); - } - } - str += words[i] + " "; - } - return str.Trim(); - } - - public static bool IsUpper(this string str) - { - bool isUpper = true; - foreach (char c in str) isUpper &= char.IsUpper(c); - return isUpper; - } - } - - public class Utils - { - - /// - /// Check current account privileges - /// - /// if we are not an Administrator, return false - public static bool IsElevated - { - get - { - WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); - return principal.IsInRole(WindowsBuiltInRole.Administrator); - } - } - - private static string[] fb2Clients = new string[] { "fbreader", "moon+ reader" }; - /// - /// Detect eBook readers with fb2 support - /// - /// - /// true if reader supports fb2 format - public static bool DetectFB2Reader(string userAgent) - { - if (!string.IsNullOrEmpty(userAgent)) - { - foreach (string s in fb2Clients) - { - if (userAgent.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0) return true; - } - } - return false; - } - - private static string[] browsers = new string[] { "opera", "aol", "msie", "firefox", "chrome", "mozilla", "safari", "netscape", "navigator", "mosaic", "lynx", - "amaya", "omniweb", "avant", "camino", "flock", "seamonkey", "konqueror", "gecko", "yandex.browser" }; - /// - /// Detect browsers by User-Agent - /// - /// - /// true if it's browser request - public static bool DetectBrowser(string userAgent) - { - if (!string.IsNullOrEmpty(userAgent)) - { - foreach (string s in browsers) - { - if (userAgent.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0) return true; - } - } - return false; - } - - /// - /// Helper for project Mono - /// - public static bool IsLinux - { - get - { - int p = (int)Environment.OSVersion.Platform; - return (p == 4) || (p == 6) || (p == 128); - } - } - - // Default path to service files: databases, log, setting - public static string ServiceFilesLocation - { - get - { - //return Properties.Settings.Default.ServiceFilesPath; - return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - } - } - - // Assembly version - public static Version Version - { - get - { - return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; - } - } - - public static string ServerVersionName - { - get - { - return string.Format("running on TinyOPDS server version {0}.{1}", Version.Major, Version.Minor); - } - } - - /// - /// Creates a name-based UUID using the algorithm from RFC 4122 §4.3. - /// - /// The ID of the namespace. - /// The name (within that namespace). - /// The version number of the UUID to create; this value must be either - /// A UUID derived from the namespace and name. - public static Guid CreateGuid(Guid namespaceId, string name) - { - if (name == null) throw new ArgumentNullException("name"); - // convert the name to a sequence of octets (as defined by the standard or conventions of its namespace) (step 3) - // ASSUME: UTF-8 encoding is always appropriate - byte[] nameBytes = Encoding.UTF8.GetBytes(name); - - // convert the namespace UUID to network order (step 3) - byte[] namespaceBytes = namespaceId.ToByteArray(); - SwapByteOrder(namespaceBytes); - - // compute the hash of the name space ID concatenated with the name (step 4) - byte[] hash = namespaceId.ToByteArray(); - using (SHA256 algorithm = new SHA256Managed()) - { - algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, hash, 0); - algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); - hash = algorithm.Hash; - } - - // most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12) - byte[] newGuid = new byte[16]; - Array.Copy(hash, 0, newGuid, 0, 16); - - // set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8) - newGuid[6] = (byte) ((newGuid[6] & 0x0F) | (5 << 4)); - - // set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively (step 10) - newGuid[8] = (byte) ((newGuid[8] & 0x3F) | 0x80); - - // convert the resulting UUID to local byte order (step 13) - SwapByteOrder(newGuid); - return new Guid(newGuid); - } - - /// - /// The namespace for fully-qualified domain names (from RFC 4122, Appendix C). - /// - public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); - - /// - /// The namespace for URLs (from RFC 4122, Appendix C). - /// - public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); - - /// - /// The namespace for ISO OIDs (from RFC 4122, Appendix C). - /// - public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); - - // Converts a GUID (expressed as a byte array) to/from network order (MSB-first). - internal static void SwapByteOrder(byte[] guid) - { - SwapBytes(guid, 0, 3); - SwapBytes(guid, 1, 2); - SwapBytes(guid, 4, 5); - SwapBytes(guid, 6, 7); - } - - private static void SwapBytes(byte[] guid, int left, int right) - { - byte temp = guid[left]; - guid[left] = guid[right]; - guid[right] = temp; - } - } - - /// - /// Gives us a handy way to modify a collection while we're iterating through it. - /// - /// - /// Example of usage: - /// foreach (Book book in new IteratorIsolateCollection(Library.Books.Values)) - /// { - /// book.Title = book.Title.ToUpper(); - /// } - public class IteratorIsolateCollection : IEnumerable - { - IEnumerable _enumerable; - - public IteratorIsolateCollection(IEnumerable enumerable) - { - _enumerable = enumerable; - } - - public IEnumerator GetEnumerator() - { - return new IteratorIsolateEnumerator(_enumerable.GetEnumerator()); - } - - internal class IteratorIsolateEnumerator : IEnumerator - { - ArrayList items = new ArrayList(); - int currentItem; - - internal IteratorIsolateEnumerator(IEnumerator enumerator) - { - while (enumerator.MoveNext() != false) - { - items.Add(enumerator.Current); - } - IDisposable disposable = enumerator as IDisposable; - if (disposable != null) - { - disposable.Dispose(); - } - currentItem = -1; - } - - public void Reset() - { - currentItem = -1; - } - - public bool MoveNext() - { - currentItem++; - if (currentItem == items.Count) - return false; - - return true; - } - - public object Current - { - get - { - return items[currentItem]; - } - } - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module contains string extensions and some helpers + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Linq; +using System.Text; +using System.Security.Principal; + +namespace TinyOPDS +{ + public static class StringExtension + { + /// + /// Capitalize words in the string (for example, author's name: first, middle last), by converting first char of every word to uppercase + /// + /// source string + /// capitalize first word only + /// + public static string Capitalize(this string str, bool onlyFirstWord = false) + { + string[] words = str.Split(' '); + str = string.Empty; + for (int i = 0; i < words.Length; i++) + { + if (!onlyFirstWord || (onlyFirstWord && i == 0)) + { + if (words[i].Length > 1) + { + if (words[i].IsUpper()) words[i] = words[i].ToLower(); + words[i] = char.ToUpper(words[i][0]) + words[i].Substring(1); + } + else + { + words[i] = words[i].ToUpper(); + } + } + str += words[i] + " "; + } + return str.Trim(); + } + + public static bool IsUpper(this string str) + { + bool isUpper = true; + foreach (char c in str) isUpper &= char.IsUpper(c); + return isUpper; + } + } + + public class Utils + { + + /// + /// Check current account privileges + /// + /// if we are not an Administrator, return false + public static bool IsElevated + { + get + { + WindowsPrincipal principal = new WindowsPrincipal(WindowsIdentity.GetCurrent()); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + } + + private static string[] fb2Clients = new string[] { "fbreader", "moon+ reader" }; + /// + /// Detect eBook readers with fb2 support + /// + /// + /// true if reader supports fb2 format + public static bool DetectFB2Reader(string userAgent) + { + if (!string.IsNullOrEmpty(userAgent)) + { + foreach (string s in fb2Clients) + { + if (userAgent.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0) return true; + } + } + return false; + } + + private static string[] browsers = new string[] { "opera", "aol", "msie", "firefox", "chrome", "mozilla", "safari", "netscape", "navigator", "mosaic", "lynx", + "amaya", "omniweb", "avant", "camino", "flock", "seamonkey", "konqueror", "gecko", "yandex.browser" }; + /// + /// Detect browsers by User-Agent + /// + /// + /// true if it's browser request + public static bool DetectBrowser(string userAgent) + { + if (!string.IsNullOrEmpty(userAgent)) + { + foreach (string s in browsers) + { + if (userAgent.IndexOf(s, StringComparison.OrdinalIgnoreCase) >= 0) return true; + } + } + return false; + } + + /// + /// Helper for project Mono + /// + public static bool IsLinux + { + get + { + int p = (int)Environment.OSVersion.Platform; + return (p == 4) || (p == 6) || (p == 128); + } + } + + // Default path to service files: databases, log, setting + public static string ServiceFilesLocation + { + get + { + //return Properties.Settings.Default.ServiceFilesPath; + return Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); + } + } + + // Assembly version + public static Version Version + { + get + { + return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; + } + } + + public static string ServerVersionName + { + get + { + return string.Format("running on TinyOPDS server version {0}.{1}", Version.Major, Version.Minor); + } + } + + /// + /// Creates a name-based UUID using the algorithm from RFC 4122 §4.3. + /// + /// The ID of the namespace. + /// The name (within that namespace). + /// The version number of the UUID to create; this value must be either + /// A UUID derived from the namespace and name. + public static Guid CreateGuid(Guid namespaceId, string name) + { + if (name == null) throw new ArgumentNullException("name"); + // convert the name to a sequence of octets (as defined by the standard or conventions of its namespace) (step 3) + // ASSUME: UTF-8 encoding is always appropriate + byte[] nameBytes = Encoding.UTF8.GetBytes(name); + + // convert the namespace UUID to network order (step 3) + byte[] namespaceBytes = namespaceId.ToByteArray(); + SwapByteOrder(namespaceBytes); + + // compute the hash of the name space ID concatenated with the name (step 4) + byte[] hash = namespaceId.ToByteArray(); + using (SHA256 algorithm = new SHA256Managed()) + { + algorithm.TransformBlock(namespaceBytes, 0, namespaceBytes.Length, hash, 0); + algorithm.TransformFinalBlock(nameBytes, 0, nameBytes.Length); + hash = algorithm.Hash; + } + + // most bytes from the hash are copied straight to the bytes of the new GUID (steps 5-7, 9, 11-12) + byte[] newGuid = new byte[16]; + Array.Copy(hash, 0, newGuid, 0, 16); + + // set the four most significant bits (bits 12 through 15) of the time_hi_and_version field to the appropriate 4-bit version number from Section 4.1.3 (step 8) + newGuid[6] = (byte) ((newGuid[6] & 0x0F) | (5 << 4)); + + // set the two most significant bits (bits 6 and 7) of the clock_seq_hi_and_reserved to zero and one, respectively (step 10) + newGuid[8] = (byte) ((newGuid[8] & 0x3F) | 0x80); + + // convert the resulting UUID to local byte order (step 13) + SwapByteOrder(newGuid); + return new Guid(newGuid); + } + + /// + /// The namespace for fully-qualified domain names (from RFC 4122, Appendix C). + /// + public static readonly Guid DnsNamespace = new Guid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"); + + /// + /// The namespace for URLs (from RFC 4122, Appendix C). + /// + public static readonly Guid UrlNamespace = new Guid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); + + /// + /// The namespace for ISO OIDs (from RFC 4122, Appendix C). + /// + public static readonly Guid IsoOidNamespace = new Guid("6ba7b812-9dad-11d1-80b4-00c04fd430c8"); + + // Converts a GUID (expressed as a byte array) to/from network order (MSB-first). + internal static void SwapByteOrder(byte[] guid) + { + SwapBytes(guid, 0, 3); + SwapBytes(guid, 1, 2); + SwapBytes(guid, 4, 5); + SwapBytes(guid, 6, 7); + } + + private static void SwapBytes(byte[] guid, int left, int right) + { + byte temp = guid[left]; + guid[left] = guid[right]; + guid[right] = temp; + } + } + + /// + /// Gives us a handy way to modify a collection while we're iterating through it. + /// + /// + /// Example of usage: + /// foreach (Book book in new IteratorIsolateCollection(Library.Books.Values)) + /// { + /// book.Title = book.Title.ToUpper(); + /// } + public class IteratorIsolateCollection : IEnumerable + { + IEnumerable _enumerable; + + public IteratorIsolateCollection(IEnumerable enumerable) + { + _enumerable = enumerable; + } + + public IEnumerator GetEnumerator() + { + return new IteratorIsolateEnumerator(_enumerable.GetEnumerator()); + } + + internal class IteratorIsolateEnumerator : IEnumerator + { + ArrayList items = new ArrayList(); + int currentItem; + + internal IteratorIsolateEnumerator(IEnumerator enumerator) + { + while (enumerator.MoveNext() != false) + { + items.Add(enumerator.Current); + } + IDisposable disposable = enumerator as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + currentItem = -1; + } + + public void Reset() + { + currentItem = -1; + } + + public bool MoveNext() + { + currentItem++; + if (currentItem == items.Count) + return false; + + return true; + } + + public object Current + { + get + { + return items[currentItem]; + } + } + } + } +} diff --git a/trunk/TinyOPDS/OPDS/AuthorsCatalog.cs b/TinyOPDS/OPDS/AuthorsCatalog.cs similarity index 97% rename from trunk/TinyOPDS/OPDS/AuthorsCatalog.cs rename to TinyOPDS/OPDS/AuthorsCatalog.cs index 06cab6e..905d7ec 100644 --- a/trunk/TinyOPDS/OPDS/AuthorsCatalog.cs +++ b/TinyOPDS/OPDS/AuthorsCatalog.cs @@ -1,125 +1,125 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the OPDS AuthorsCatalog class - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using System.Web; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - /// - /// Authors acquisition feed class - /// - public class AuthorsCatalog - { - /// - /// - /// - /// - /// - /// - public XDocument GetCatalog(string searchPattern, bool isOpenSearch = false, int threshold = 100) - { - if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower(); - - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - new XElement("id", "tag:authors"), - new XElement("title", Localizer.Text("Books by authors")), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/authors.ico"), - // Add links - Links.opensearch, Links.search, Links.start) - ); - - // Get all authors names starting with searchPattern - List Authors = Library.GetAuthorsByName(searchPattern, isOpenSearch); - - // For search, also check transliterated names - if (isOpenSearch) - { - // Try transliteration - string translit = Transliteration.Back(searchPattern, TransliterationType.GOST); - if (!string.IsNullOrEmpty(translit)) - { - List transAuthors = Library.GetAuthorsByName(translit, isOpenSearch); - if (transAuthors.Count > 0) Authors.AddRange(transAuthors); - } - } - - // if there are more authors then threshold, try to collapse them into groups - // and render these groups first and authors after them - if (Authors.Count > threshold) - { - Dictionary catalogGroups = null; - do - { - catalogGroups = (from a in Authors - group a by (a.Length > searchPattern.Length ? a.Substring(0, searchPattern.Length + 1).Capitalize(true) - : a.Capitalize(true)) into g - where g.Count() > 1 - select new { Name = g, Count = g.Count() }).ToDictionary(x => x.Name.Key, y => y.Count); - - if (catalogGroups.Count == 1) searchPattern = catalogGroups.First().Key; - else break; - } while (true); - - // remove entry that exactly matches search pattern to avoid recursion - catalogGroups.Remove(searchPattern.Capitalize(true)); - // remove entries that are groupped ( if any ) - foreach (var kv in catalogGroups) - { - Authors.RemoveAll(a => a.StartsWith(kv.Key, StringComparison.InvariantCultureIgnoreCase)); - } - - // Add catalog groups - foreach (KeyValuePair cg in catalogGroups) - { - doc.Root.Add( - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:authors:" + cg.Key), - new XElement("title", cg.Key), - new XElement("content", string.Format(Localizer.Text("Total authors on {0}: {1}"), cg.Key, cg.Value), - new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/authorsindex/" + Uri.EscapeDataString(cg.Key)), - new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ) - ); - } - } - - // Add catalog entries - foreach (string author in Authors) - { - var booksCount = Library.GetBooksByAuthorCount(author); - - doc.Root.Add( - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:authors:" + author), - new XElement("title", author), - new XElement("content", string.Format(Localizer.Text("Books: {0}"), booksCount), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ) - ); - } - return doc; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the OPDS AuthorsCatalog class + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Web; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + /// + /// Authors acquisition feed class + /// + public class AuthorsCatalog + { + /// + /// + /// + /// + /// + /// + public XDocument GetCatalog(string searchPattern, bool isOpenSearch = false, int threshold = 100) + { + if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower(); + + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + new XElement("id", "tag:authors"), + new XElement("title", Localizer.Text("Books by authors")), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/authors.ico"), + // Add links + Links.opensearch, Links.search, Links.start) + ); + + // Get all authors names starting with searchPattern + List Authors = Library.GetAuthorsByName(searchPattern, isOpenSearch); + + // For search, also check transliterated names + if (isOpenSearch) + { + // Try transliteration + string translit = Transliteration.Back(searchPattern, TransliterationType.GOST); + if (!string.IsNullOrEmpty(translit)) + { + List transAuthors = Library.GetAuthorsByName(translit, isOpenSearch); + if (transAuthors.Count > 0) Authors.AddRange(transAuthors); + } + } + + // if there are more authors then threshold, try to collapse them into groups + // and render these groups first and authors after them + if (Authors.Count > threshold) + { + Dictionary catalogGroups = null; + do + { + catalogGroups = (from a in Authors + group a by (a.Length > searchPattern.Length ? a.Substring(0, searchPattern.Length + 1).Capitalize(true) + : a.Capitalize(true)) into g + where g.Count() > 1 + select new { Name = g, Count = g.Count() }).ToDictionary(x => x.Name.Key, y => y.Count); + + if (catalogGroups.Count == 1) searchPattern = catalogGroups.First().Key; + else break; + } while (true); + + // remove entry that exactly matches search pattern to avoid recursion + catalogGroups.Remove(searchPattern.Capitalize(true)); + // remove entries that are groupped ( if any ) + foreach (var kv in catalogGroups) + { + Authors.RemoveAll(a => a.StartsWith(kv.Key, StringComparison.InvariantCultureIgnoreCase)); + } + + // Add catalog groups + foreach (KeyValuePair cg in catalogGroups) + { + doc.Root.Add( + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:authors:" + cg.Key), + new XElement("title", cg.Key), + new XElement("content", string.Format(Localizer.Text("Total authors on {0}: {1}"), cg.Key, cg.Value), + new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/authorsindex/" + Uri.EscapeDataString(cg.Key)), + new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ) + ); + } + } + + // Add catalog entries + foreach (string author in Authors) + { + var booksCount = Library.GetBooksByAuthorCount(author); + + doc.Root.Add( + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:authors:" + author), + new XElement("title", author), + new XElement("content", string.Format(Localizer.Text("Books: {0}"), booksCount), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ) + ); + } + return doc; + } + } +} diff --git a/trunk/TinyOPDS/OPDS/BooksCatalog.cs b/TinyOPDS/OPDS/BooksCatalog.cs similarity index 97% rename from trunk/TinyOPDS/OPDS/BooksCatalog.cs rename to TinyOPDS/OPDS/BooksCatalog.cs index e26fe99..d0357e6 100644 --- a/trunk/TinyOPDS/OPDS/BooksCatalog.cs +++ b/TinyOPDS/OPDS/BooksCatalog.cs @@ -1,288 +1,288 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the OPDS BookCatalog class - * - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using System.Web; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - public class BooksCatalog - { - private enum SearchFor - { - Author = 0, - Sequence, - Genre, - Title, - } - - /// - /// Returns books catalog by selected author - /// - /// - /// - public XDocument GetCatalogByAuthor(string author, bool fb2Only, int threshold = 100) - { - return GetCatalog(author, SearchFor.Author, fb2Only, threshold); - } - - /// - /// Returns books catalog by selected sequence (series) - /// - /// - /// - public XDocument GetCatalogBySequence(string sequence, bool fb2Only, int threshold = 100) - { - return GetCatalog(sequence, SearchFor.Sequence, fb2Only, threshold); - } - - /// - /// Returns books catalog by selected genre - /// - /// - /// - public XDocument GetCatalogByGenre(string genre, bool fb2Only, int threshold = 100) - { - return GetCatalog(genre, SearchFor.Genre, fb2Only, threshold); - } - - /// - /// Returns books catalog by selected genre - /// - /// - /// - public XDocument GetCatalogByTitle(string title, bool fb2Only, int pageNumber = 0, int threshold = 100) - { - return GetCatalog(title, SearchFor.Title, fb2Only, threshold); - } - - /// - /// Returns books catalog for specific search - /// - /// Keyword to search - /// Type of search - /// Client can accept fb2 files - /// Items per page - /// - private XDocument GetCatalog(string searchPattern, SearchFor searchFor, bool acceptFB2, int threshold = 100) - { - if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' '); - - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - new XElement("id", "tag:books"), - new XElement("title", Localizer.Text("Books by author ") + searchPattern), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/icons/books.ico"), - // Add links - Links.opensearch, Links.search, Links.start) - ); - - int pageNumber = 0; - // Extract and remove page number from the search pattern - int j = searchPattern.IndexOf('/'); - if (j > 0) - { - int.TryParse(searchPattern.Substring(j + 1), out pageNumber); - searchPattern = searchPattern.Substring(0, j); - } - - // Get author's books - string catalogType = string.Empty; - List books = new List(); - switch (searchFor) - { - case SearchFor.Author: - books = Library.GetBooksByAuthor(searchPattern); - catalogType = "/author/" + Uri.EscapeDataString(searchPattern); - break; - case SearchFor.Sequence: - books = Library.GetBooksBySequence(searchPattern); - catalogType = "/sequence/" + Uri.EscapeDataString(searchPattern); - break; - case SearchFor.Genre: - books = Library.GetBooksByGenre(searchPattern); - catalogType = "/genre/" + Uri.EscapeDataString(searchPattern); - break; - case SearchFor.Title: - books = Library.GetBooksByTitle(searchPattern); - // For search, also return books by - if (threshold > 50) - { - string translit = Transliteration.Back(searchPattern, TransliterationType.GOST); - if (!string.IsNullOrEmpty(translit)) - { - List transTitles = Library.GetBooksByTitle(translit); - if (transTitles.Count > 0) books.AddRange(transTitles); - } - } - break; - } - - // For sequences, sort books by sequence number - if (searchFor == SearchFor.Sequence) - { - books = books.OrderBy(b => b.NumberInSequence).ToList(); - } - // else sort by title - else - { - books = books.OrderBy(b => b.Title, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList(); - } - - int startIndex = pageNumber * threshold; - int endIndex = startIndex + ((books.Count / threshold == 0) ? books.Count : Math.Min(threshold, books.Count - startIndex)); - - if (searchFor == SearchFor.Title) - { - if ((pageNumber + 1) * threshold < books.Count) - { - catalogType = string.Format("/search?searchType=books&searchTerm={0}&pageNumber={1}", Uri.EscapeDataString(searchPattern), pageNumber + 1); - doc.Root.Add(new XElement("link", - new XAttribute("href", catalogType), - new XAttribute("rel", "next"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"))); - } - } - else if ((pageNumber + 1) * threshold < books.Count) - { - catalogType += "/" + (pageNumber + 1); - doc.Root.Add(new XElement("link", - new XAttribute("href", catalogType), - new XAttribute("rel", "next"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"))); - - } - - bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0; - - List genres = Library.Genres; - - // Add catalog entries - for (int i = startIndex; i < endIndex; i++) - { - Book book = books.ElementAt(i); - - XElement entry = - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:book:" + book.ID), - new XElement("title", book.Title) - ); - - foreach (string author in book.Authors) - { - entry.Add( - new XElement("author", - new XElement("name", author), - new XElement("uri", "/author/" + Uri.EscapeDataString(author) - ))); - } - - foreach (string genreStr in book.Genres) - { - Genre genre = genres.Where(g => g.Tag.Equals(genreStr)).FirstOrDefault(); - if (genre != null) - entry.Add(new XElement("category", new XAttribute("term", (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("label", (useCyrillic ? genre.Translation : genre.Name)))); - } - - // Build a content entry (translator(s), year, size, annotation etc.) - string bookInfo = string.Empty; - - if (!string.IsNullOrEmpty(book.Annotation)) - { - bookInfo += string.Format(@"

{0}

", System.Security.SecurityElement.Escape(book.Annotation.Trim())); - } - if (book.Translators.Count > 0) - { - bookInfo += string.Format("{0} ", Localizer.Text("Translation:")); - foreach (string translator in book.Translators) bookInfo += translator + " "; - bookInfo += "
"; - } - if (book.BookDate != DateTime.MinValue) - { - bookInfo += string.Format("{0} {1}
", Localizer.Text("Year of publication:"), book.BookDate.Year); - } - - if (!string.IsNullOrEmpty(book.Sequence)) - { - bookInfo += string.Format("{0} {1} #{2}
", Localizer.Text("Series:"), book.Sequence, book.NumberInSequence); - } - - entry.Add( - new XElement(Namespaces.dc + "language", book.Language), - new XElement(Namespaces.dc + "format", book.BookType == BookType.FB2 ? "fb2+zip" : "epub+zip"), - new XElement("content", new XAttribute("type", "text/html"), XElement.Parse("
" + bookInfo + "
")), - new XElement( "format", book.BookType == BookType.EPUB ? "epub" : "fb2"), - new XElement( "size", string.Format("{0} Kb", (int)book.DocumentSize / 1024))); - - - if (book.HasCover) - { - entry.Add( - // Adding cover page and thumbnail links - new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/image"), new XAttribute("type", "image/jpeg")), - new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image"), new XAttribute("type", "image/jpeg")), - new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/thumbnail"), new XAttribute("type", "image/jpeg")), - new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image-thumbnail"), new XAttribute("type", "image/jpeg")) - // Adding download links - ); - } - - string fileName = Uri.EscapeDataString(Transliteration.Front(string.Format("{0}_{1}", book.Authors.First(), book.Title)).SanitizeFileName()); - string url = "/" + string.Format("{0}/{1}", book.ID, fileName); - if (book.BookType == BookType.EPUB || (book.BookType == BookType.FB2 && !acceptFB2 && !string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath))) - { - entry.Add(new XElement("link", new XAttribute("href", url+".epub"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/epub+zip"))); - } - - if (book.BookType == BookType.FB2) - { - entry.Add(new XElement("link", new XAttribute("href", url+".fb2.zip"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/fb2+zip"))); - } - - // For search requests, lets add navigation links for author and series (if any) - if (searchFor != SearchFor.Author) - { - foreach (string author in book.Authors) - { - entry.Add(new XElement("link", - new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), - new XAttribute("rel", "related"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"), - new XAttribute("title", string.Format(Localizer.Text("All books by author {0}"), author)))); - } - } - - if (searchFor != SearchFor.Sequence && !string.IsNullOrEmpty(book.Sequence)) - { - entry.Add(new XElement("link", - new XAttribute("href", "/sequence/" + Uri.EscapeDataString(book.Sequence)), - new XAttribute("rel", "related"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"), - new XAttribute("title", string.Format(Localizer.Text("All books by series {0}"), book.Sequence)))); - } - - doc.Root.Add(entry); - } - return doc; - } - } +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the OPDS BookCatalog class + * + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Web; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + public class BooksCatalog + { + private enum SearchFor + { + Author = 0, + Sequence, + Genre, + Title, + } + + /// + /// Returns books catalog by selected author + /// + /// + /// + public XDocument GetCatalogByAuthor(string author, bool fb2Only, int threshold = 100) + { + return GetCatalog(author, SearchFor.Author, fb2Only, threshold); + } + + /// + /// Returns books catalog by selected sequence (series) + /// + /// + /// + public XDocument GetCatalogBySequence(string sequence, bool fb2Only, int threshold = 100) + { + return GetCatalog(sequence, SearchFor.Sequence, fb2Only, threshold); + } + + /// + /// Returns books catalog by selected genre + /// + /// + /// + public XDocument GetCatalogByGenre(string genre, bool fb2Only, int threshold = 100) + { + return GetCatalog(genre, SearchFor.Genre, fb2Only, threshold); + } + + /// + /// Returns books catalog by selected genre + /// + /// + /// + public XDocument GetCatalogByTitle(string title, bool fb2Only, int pageNumber = 0, int threshold = 100) + { + return GetCatalog(title, SearchFor.Title, fb2Only, threshold); + } + + /// + /// Returns books catalog for specific search + /// + /// Keyword to search + /// Type of search + /// Client can accept fb2 files + /// Items per page + /// + private XDocument GetCatalog(string searchPattern, SearchFor searchFor, bool acceptFB2, int threshold = 100) + { + if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' '); + + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + new XElement("id", "tag:books"), + new XElement("title", Localizer.Text("Books by author ") + searchPattern), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/icons/books.ico"), + // Add links + Links.opensearch, Links.search, Links.start) + ); + + int pageNumber = 0; + // Extract and remove page number from the search pattern + int j = searchPattern.IndexOf('/'); + if (j > 0) + { + int.TryParse(searchPattern.Substring(j + 1), out pageNumber); + searchPattern = searchPattern.Substring(0, j); + } + + // Get author's books + string catalogType = string.Empty; + List books = new List(); + switch (searchFor) + { + case SearchFor.Author: + books = Library.GetBooksByAuthor(searchPattern); + catalogType = "/author/" + Uri.EscapeDataString(searchPattern); + break; + case SearchFor.Sequence: + books = Library.GetBooksBySequence(searchPattern); + catalogType = "/sequence/" + Uri.EscapeDataString(searchPattern); + break; + case SearchFor.Genre: + books = Library.GetBooksByGenre(searchPattern); + catalogType = "/genre/" + Uri.EscapeDataString(searchPattern); + break; + case SearchFor.Title: + books = Library.GetBooksByTitle(searchPattern); + // For search, also return books by + if (threshold > 50) + { + string translit = Transliteration.Back(searchPattern, TransliterationType.GOST); + if (!string.IsNullOrEmpty(translit)) + { + List transTitles = Library.GetBooksByTitle(translit); + if (transTitles.Count > 0) books.AddRange(transTitles); + } + } + break; + } + + // For sequences, sort books by sequence number + if (searchFor == SearchFor.Sequence) + { + books = books.OrderBy(b => b.NumberInSequence).ToList(); + } + // else sort by title + else + { + books = books.OrderBy(b => b.Title, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList(); + } + + int startIndex = pageNumber * threshold; + int endIndex = startIndex + ((books.Count / threshold == 0) ? books.Count : Math.Min(threshold, books.Count - startIndex)); + + if (searchFor == SearchFor.Title) + { + if ((pageNumber + 1) * threshold < books.Count) + { + catalogType = string.Format("/search?searchType=books&searchTerm={0}&pageNumber={1}", Uri.EscapeDataString(searchPattern), pageNumber + 1); + doc.Root.Add(new XElement("link", + new XAttribute("href", catalogType), + new XAttribute("rel", "next"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"))); + } + } + else if ((pageNumber + 1) * threshold < books.Count) + { + catalogType += "/" + (pageNumber + 1); + doc.Root.Add(new XElement("link", + new XAttribute("href", catalogType), + new XAttribute("rel", "next"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"))); + + } + + bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0; + + List genres = Library.Genres; + + // Add catalog entries + for (int i = startIndex; i < endIndex; i++) + { + Book book = books.ElementAt(i); + + XElement entry = + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:book:" + book.ID), + new XElement("title", book.Title) + ); + + foreach (string author in book.Authors) + { + entry.Add( + new XElement("author", + new XElement("name", author), + new XElement("uri", "/author/" + Uri.EscapeDataString(author) + ))); + } + + foreach (string genreStr in book.Genres) + { + Genre genre = genres.Where(g => g.Tag.Equals(genreStr)).FirstOrDefault(); + if (genre != null) + entry.Add(new XElement("category", new XAttribute("term", (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("label", (useCyrillic ? genre.Translation : genre.Name)))); + } + + // Build a content entry (translator(s), year, size, annotation etc.) + string bookInfo = string.Empty; + + if (!string.IsNullOrEmpty(book.Annotation)) + { + bookInfo += string.Format(@"

{0}

", System.Security.SecurityElement.Escape(book.Annotation.Trim())); + } + if (book.Translators.Count > 0) + { + bookInfo += string.Format("{0} ", Localizer.Text("Translation:")); + foreach (string translator in book.Translators) bookInfo += translator + " "; + bookInfo += "
"; + } + if (book.BookDate != DateTime.MinValue) + { + bookInfo += string.Format("{0} {1}
", Localizer.Text("Year of publication:"), book.BookDate.Year); + } + + if (!string.IsNullOrEmpty(book.Sequence)) + { + bookInfo += string.Format("{0} {1} #{2}
", Localizer.Text("Series:"), book.Sequence, book.NumberInSequence); + } + + entry.Add( + new XElement(Namespaces.dc + "language", book.Language), + new XElement(Namespaces.dc + "format", book.BookType == BookType.FB2 ? "fb2+zip" : "epub+zip"), + new XElement("content", new XAttribute("type", "text/html"), XElement.Parse("
" + bookInfo + "
")), + new XElement( "format", book.BookType == BookType.EPUB ? "epub" : "fb2"), + new XElement( "size", string.Format("{0} Kb", (int)book.DocumentSize / 1024))); + + + if (book.HasCover) + { + entry.Add( + // Adding cover page and thumbnail links + new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/image"), new XAttribute("type", "image/jpeg")), + new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image"), new XAttribute("type", "image/jpeg")), + new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/thumbnail"), new XAttribute("type", "image/jpeg")), + new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image-thumbnail"), new XAttribute("type", "image/jpeg")) + // Adding download links + ); + } + + string fileName = Uri.EscapeDataString(Transliteration.Front(string.Format("{0}_{1}", book.Authors.First(), book.Title)).SanitizeFileName()); + string url = "/" + string.Format("{0}/{1}", book.ID, fileName); + if (book.BookType == BookType.EPUB || (book.BookType == BookType.FB2 && !acceptFB2 && !string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath))) + { + entry.Add(new XElement("link", new XAttribute("href", url+".epub"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/epub+zip"))); + } + + if (book.BookType == BookType.FB2) + { + entry.Add(new XElement("link", new XAttribute("href", url+".fb2.zip"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/fb2+zip"))); + } + + // For search requests, lets add navigation links for author and series (if any) + if (searchFor != SearchFor.Author) + { + foreach (string author in book.Authors) + { + entry.Add(new XElement("link", + new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), + new XAttribute("rel", "related"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"), + new XAttribute("title", string.Format(Localizer.Text("All books by author {0}"), author)))); + } + } + + if (searchFor != SearchFor.Sequence && !string.IsNullOrEmpty(book.Sequence)) + { + entry.Add(new XElement("link", + new XAttribute("href", "/sequence/" + Uri.EscapeDataString(book.Sequence)), + new XAttribute("rel", "related"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"), + new XAttribute("title", string.Format(Localizer.Text("All books by series {0}"), book.Sequence)))); + } + + doc.Root.Add(entry); + } + return doc; + } + } } \ No newline at end of file diff --git a/trunk/TinyOPDS/OPDS/GenresCatalog.cs b/TinyOPDS/OPDS/GenresCatalog.cs similarity index 97% rename from trunk/TinyOPDS/OPDS/GenresCatalog.cs rename to TinyOPDS/OPDS/GenresCatalog.cs index 4c7be39..dc1b14d 100644 --- a/trunk/TinyOPDS/OPDS/GenresCatalog.cs +++ b/TinyOPDS/OPDS/GenresCatalog.cs @@ -1,88 +1,88 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the OPDS GenresCatalog class - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using System.Web; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - /// - /// Genres acquisition feed class - /// - public class GenresCatalog - { - public XDocument GetCatalog(string searchPattern, int threshold = 100) - { - if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' '); - - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - new XElement("id", "tag:genres"), - new XElement("title", Localizer.Text("Books by genres")), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/genres.ico"), - // Add links - Links.opensearch, Links.search, Links.start) - ); - - bool topLevel = true; - bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0; - - List libGenres = Library.Genres; - List genres = null; - - // Is it top level (main genres)? - if (string.IsNullOrEmpty(searchPattern)) - { - genres = (from g in Library.FB2Genres from sg in g.Subgenres where libGenres.Contains(sg) select g).Distinct().ToList(); - } - // Is it a second level (subgenres)? - else - { - Genre genre = Library.FB2Genres.Where(g => g.Name.Equals(searchPattern) || g.Translation.Equals(searchPattern)).FirstOrDefault(); - if (genre != null) - { - genres = (from g in libGenres where genre.Subgenres.Contains(g) select g).Distinct().ToList(); - topLevel = false; - } - } - - if (genres != null) - { - genres.Sort(new OPDSComparer(useCyrillic)); - - // Add catalog entries - foreach (Genre genre in genres) - { - doc.Root.Add( - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:root:genre:" + (useCyrillic ? genre.Translation : genre.Name)), - new XElement("title", (useCyrillic ? genre.Translation : genre.Name)), - new XElement("content", string.Format(Localizer.Text("Books in genre «{0}»"), (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/" + (topLevel ? "genres/" : "genre/") + (topLevel ? Uri.EscapeDataString((useCyrillic ? genre.Translation : genre.Name)) : genre.Tag)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ) - ); - } - } - - return doc; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the OPDS GenresCatalog class + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Web; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + /// + /// Genres acquisition feed class + /// + public class GenresCatalog + { + public XDocument GetCatalog(string searchPattern, int threshold = 100) + { + if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' '); + + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + new XElement("id", "tag:genres"), + new XElement("title", Localizer.Text("Books by genres")), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/genres.ico"), + // Add links + Links.opensearch, Links.search, Links.start) + ); + + bool topLevel = true; + bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0; + + List libGenres = Library.Genres; + List genres = null; + + // Is it top level (main genres)? + if (string.IsNullOrEmpty(searchPattern)) + { + genres = (from g in Library.FB2Genres from sg in g.Subgenres where libGenres.Contains(sg) select g).Distinct().ToList(); + } + // Is it a second level (subgenres)? + else + { + Genre genre = Library.FB2Genres.Where(g => g.Name.Equals(searchPattern) || g.Translation.Equals(searchPattern)).FirstOrDefault(); + if (genre != null) + { + genres = (from g in libGenres where genre.Subgenres.Contains(g) select g).Distinct().ToList(); + topLevel = false; + } + } + + if (genres != null) + { + genres.Sort(new OPDSComparer(useCyrillic)); + + // Add catalog entries + foreach (Genre genre in genres) + { + doc.Root.Add( + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:root:genre:" + (useCyrillic ? genre.Translation : genre.Name)), + new XElement("title", (useCyrillic ? genre.Translation : genre.Name)), + new XElement("content", string.Format(Localizer.Text("Books in genre «{0}»"), (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/" + (topLevel ? "genres/" : "genre/") + (topLevel ? Uri.EscapeDataString((useCyrillic ? genre.Translation : genre.Name)) : genre.Tag)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ) + ); + } + } + + return doc; + } + } +} diff --git a/trunk/TinyOPDS/OPDS/Links.cs b/TinyOPDS/OPDS/Links.cs similarity index 97% rename from trunk/TinyOPDS/OPDS/Links.cs rename to TinyOPDS/OPDS/Links.cs index 41c22ee..8944b91 100644 --- a/trunk/TinyOPDS/OPDS/Links.cs +++ b/TinyOPDS/OPDS/Links.cs @@ -1,39 +1,39 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines static OPDS links - * - ************************************************************/ - -using System.Xml.Linq; - -namespace TinyOPDS.OPDS -{ - public class Links - { - public static XElement opensearch = new XElement("link", - new XAttribute("href", "/opds-opensearch.xml"), - new XAttribute("rel", "search"), - new XAttribute("type", "application/opensearchdescription+xml")); - - public static XElement search = new XElement("link", - new XAttribute("href","/search?searchTerm={searchTerms}"), - new XAttribute("rel","search"), - new XAttribute("type","application/atom+xml")); - - public static XElement start = new XElement("link", - new XAttribute("href", ""), - new XAttribute("rel","start"), - new XAttribute("type","application/atom+xml")); - - public static XElement self = new XElement("link", - new XAttribute("href", ""), - new XAttribute("rel","self"), - new XAttribute("type","application/atom+xml")); - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines static OPDS links + * + ************************************************************/ + +using System.Xml.Linq; + +namespace TinyOPDS.OPDS +{ + public class Links + { + public static XElement opensearch = new XElement("link", + new XAttribute("href", "/opds-opensearch.xml"), + new XAttribute("rel", "search"), + new XAttribute("type", "application/opensearchdescription+xml")); + + public static XElement search = new XElement("link", + new XAttribute("href","/search?searchTerm={searchTerms}"), + new XAttribute("rel","search"), + new XAttribute("type","application/atom+xml")); + + public static XElement start = new XElement("link", + new XAttribute("href", ""), + new XAttribute("rel","start"), + new XAttribute("type","application/atom+xml")); + + public static XElement self = new XElement("link", + new XAttribute("href", ""), + new XAttribute("rel","self"), + new XAttribute("type","application/atom+xml")); + } +} diff --git a/trunk/TinyOPDS/OPDS/Namespaces.cs b/TinyOPDS/OPDS/Namespaces.cs similarity index 97% rename from trunk/TinyOPDS/OPDS/Namespaces.cs rename to TinyOPDS/OPDS/Namespaces.cs index 5ffdae4..56aba62 100644 --- a/trunk/TinyOPDS/OPDS/Namespaces.cs +++ b/TinyOPDS/OPDS/Namespaces.cs @@ -1,24 +1,24 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * OPDS xml namespaces - * - ************************************************************/ - -using System.Xml.Linq; - -namespace TinyOPDS.OPDS -{ - internal class Namespaces - { - internal static XNamespace xmlns = XNamespace.Get("http://www.w3.org/2005/Atom"); - internal static XNamespace dc = XNamespace.Get("http://purl.org/dc/terms/"); - internal static XNamespace os = XNamespace.Get("http://a9.com/-/spec/opensearch/1.1/"); - internal static XNamespace opds = XNamespace.Get("http://opds-spec.org/2010/catalog"); - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * OPDS xml namespaces + * + ************************************************************/ + +using System.Xml.Linq; + +namespace TinyOPDS.OPDS +{ + internal class Namespaces + { + internal static XNamespace xmlns = XNamespace.Get("http://www.w3.org/2005/Atom"); + internal static XNamespace dc = XNamespace.Get("http://purl.org/dc/terms/"); + internal static XNamespace os = XNamespace.Get("http://a9.com/-/spec/opensearch/1.1/"); + internal static XNamespace opds = XNamespace.Get("http://opds-spec.org/2010/catalog"); + } +} diff --git a/trunk/TinyOPDS/OPDS/NewBooksCatalog.cs b/TinyOPDS/OPDS/NewBooksCatalog.cs similarity index 98% rename from trunk/TinyOPDS/OPDS/NewBooksCatalog.cs rename to TinyOPDS/OPDS/NewBooksCatalog.cs index 799e0f5..469a8f2 100644 --- a/trunk/TinyOPDS/OPDS/NewBooksCatalog.cs +++ b/TinyOPDS/OPDS/NewBooksCatalog.cs @@ -1,183 +1,183 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the OPDS NewBooksCatalog class - * - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using System.Web; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - public class NewBooksCatalog - { - /// - /// Returns books catalog for specific search - /// - /// Keyword to search - /// Type of search - /// Client can accept fb2 files - /// Items per page - /// - public XDocument GetCatalog(string searchPattern, bool sortByDate, bool acceptFB2, int threshold = 100) - { - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - new XElement("id", "tag:new"), - new XElement("title", string.Format(Localizer.Text("New books sorted by {0}"), (sortByDate ? Localizer.Text("date") : Localizer.Text("titles")))), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/icons/books.ico"), - // Add links - Links.opensearch, Links.search, Links.start) - ); - - int pageNumber = 0; - // Extract and remove page number from the search patter - int j = searchPattern.IndexOf("?pageNumber="); - if (j >= 0) - { - int.TryParse(searchPattern.Substring(j + 12), out pageNumber); - } - - // Get list of new books - string catalogType = string.Empty; - List books = Library.NewBooks; - - if (sortByDate) books = books.OrderBy(b => b.AddedDate).ToList(); - else books = books.OrderBy(b => b.Title, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList(); - - int startIndex = pageNumber * threshold; - int endIndex = startIndex + ((books.Count / threshold == 0) ? books.Count : Math.Min(threshold, books.Count - startIndex)); - - if ((pageNumber + 1) * threshold < books.Count) - { - catalogType = string.Format("/{0}?pageNumber={1}", (sortByDate ? "newdate" : "newtitle"), pageNumber + 1); - doc.Root.Add(new XElement("link", - new XAttribute("href", catalogType), - new XAttribute("rel", "next"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"))); - } - - bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0; - - List genres = Library.Genres; - - // Add catalog entries - for (int i = startIndex; i < endIndex; i++) - { - Book book = books.ElementAt(i); - - XElement entry = - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:book:" + book.ID), - new XElement("title", book.Title) - ); - - foreach (string author in book.Authors) - { - entry.Add( - new XElement("author", - new XElement("name", author), - new XElement("uri", "/author/" + Uri.EscapeDataString(author) - ))); - } - - foreach (string genreStr in book.Genres) - { - Genre genre = genres.Where(g => g.Tag.Equals(genreStr)).FirstOrDefault(); - if (genre != null) - entry.Add(new XElement("category", new XAttribute("term", (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("label", (useCyrillic ? genre.Translation : genre.Name)))); - } - - // Build a content entry (translator(s), year, size, annotation etc.) - string bookInfo = string.Empty; - - if (!string.IsNullOrEmpty(book.Annotation)) - { - bookInfo += string.Format("

{0}


", book.Annotation); - } - if (book.Translators.Count > 0) - { - bookInfo += string.Format("{0} ", Localizer.Text("Translation:")); - foreach (string translator in book.Translators) bookInfo += translator + " "; - bookInfo += "
"; - } - if (book.BookDate != DateTime.MinValue) - { - bookInfo += string.Format("{0} {1}
", Localizer.Text("Year of publication:"), book.BookDate.Year); - } - bookInfo += string.Format("{0} {1}
", Localizer.Text("Format:"), book.BookType == BookType.EPUB ? "epub" : "fb2"); - bookInfo += string.Format("{0} {1} Kb
", Localizer.Text("Size:"), (int)book.DocumentSize / 1024); - if (!string.IsNullOrEmpty(book.Sequence)) - { - bookInfo += string.Format("{0} {1} #{2}
", Localizer.Text("Series:"), book.Sequence, book.NumberInSequence); - } - - entry.Add( - new XElement(Namespaces.dc + "language", book.Language), - new XElement(Namespaces.dc + "format", book.BookType == BookType.FB2 ? "fb2+zip" : "epub+zip"), - new XElement("content", new XAttribute("type", "text/html"), bookInfo)); - - if (book.HasCover) - { - entry.Add( - // Adding cover page and thumbnail links - new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/image"), new XAttribute("type", "image/jpeg")), - new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image"), new XAttribute("type", "image/jpeg")), - new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/thumbnail"), new XAttribute("type", "image/jpeg")), - new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image-thumbnail"), new XAttribute("type", "image/jpeg")) - // Adding download links - ); - } - - string fileName = Uri.EscapeDataString(Transliteration.Front(string.Format("{0}_{1}", book.Authors.First(), book.Title)).SanitizeFileName()); - string url = "/" + string.Format("{0}/{1}", book.ID, fileName); - if (book.BookType == BookType.EPUB || (book.BookType == BookType.FB2 && !acceptFB2 && !string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath))) - { - entry.Add(new XElement("link", new XAttribute("href", url + ".epub"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/epub+zip"))); - } - - if (book.BookType == BookType.FB2) - { - entry.Add(new XElement("link", new XAttribute("href", url + ".fb2.zip"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/fb2+zip"))); - } - - foreach (string author in book.Authors) - { - entry.Add(new XElement("link", - new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), - new XAttribute("rel", "related"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"), - new XAttribute("title", string.Format(Localizer.Text("All books by author {0}"), author)))); - } - - if (!string.IsNullOrEmpty(book.Sequence)) - { - entry.Add(new XElement("link", - new XAttribute("href", "/sequence/" + Uri.EscapeDataString(book.Sequence)), - new XAttribute("rel", "related"), - new XAttribute("type", "application/atom+xml;profile=opds-catalog"), - new XAttribute("title", string.Format(Localizer.Text("All books by series {0}"), book.Sequence)))); - } - - doc.Root.Add(entry); - } - return doc; - } - } +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the OPDS NewBooksCatalog class + * + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Web; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + public class NewBooksCatalog + { + /// + /// Returns books catalog for specific search + /// + /// Keyword to search + /// Type of search + /// Client can accept fb2 files + /// Items per page + /// + public XDocument GetCatalog(string searchPattern, bool sortByDate, bool acceptFB2, int threshold = 100) + { + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + new XElement("id", "tag:new"), + new XElement("title", string.Format(Localizer.Text("New books sorted by {0}"), (sortByDate ? Localizer.Text("date") : Localizer.Text("titles")))), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/icons/books.ico"), + // Add links + Links.opensearch, Links.search, Links.start) + ); + + int pageNumber = 0; + // Extract and remove page number from the search patter + int j = searchPattern.IndexOf("?pageNumber="); + if (j >= 0) + { + int.TryParse(searchPattern.Substring(j + 12), out pageNumber); + } + + // Get list of new books + string catalogType = string.Empty; + List books = Library.NewBooks; + + if (sortByDate) books = books.OrderBy(b => b.AddedDate).ToList(); + else books = books.OrderBy(b => b.Title, new OPDSComparer(TinyOPDS.Properties.Settings.Default.SortOrder > 0)).ToList(); + + int startIndex = pageNumber * threshold; + int endIndex = startIndex + ((books.Count / threshold == 0) ? books.Count : Math.Min(threshold, books.Count - startIndex)); + + if ((pageNumber + 1) * threshold < books.Count) + { + catalogType = string.Format("/{0}?pageNumber={1}", (sortByDate ? "newdate" : "newtitle"), pageNumber + 1); + doc.Root.Add(new XElement("link", + new XAttribute("href", catalogType), + new XAttribute("rel", "next"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"))); + } + + bool useCyrillic = TinyOPDS.Properties.Settings.Default.SortOrder > 0; + + List genres = Library.Genres; + + // Add catalog entries + for (int i = startIndex; i < endIndex; i++) + { + Book book = books.ElementAt(i); + + XElement entry = + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:book:" + book.ID), + new XElement("title", book.Title) + ); + + foreach (string author in book.Authors) + { + entry.Add( + new XElement("author", + new XElement("name", author), + new XElement("uri", "/author/" + Uri.EscapeDataString(author) + ))); + } + + foreach (string genreStr in book.Genres) + { + Genre genre = genres.Where(g => g.Tag.Equals(genreStr)).FirstOrDefault(); + if (genre != null) + entry.Add(new XElement("category", new XAttribute("term", (useCyrillic ? genre.Translation : genre.Name)), new XAttribute("label", (useCyrillic ? genre.Translation : genre.Name)))); + } + + // Build a content entry (translator(s), year, size, annotation etc.) + string bookInfo = string.Empty; + + if (!string.IsNullOrEmpty(book.Annotation)) + { + bookInfo += string.Format("

{0}


", book.Annotation); + } + if (book.Translators.Count > 0) + { + bookInfo += string.Format("{0} ", Localizer.Text("Translation:")); + foreach (string translator in book.Translators) bookInfo += translator + " "; + bookInfo += "
"; + } + if (book.BookDate != DateTime.MinValue) + { + bookInfo += string.Format("{0} {1}
", Localizer.Text("Year of publication:"), book.BookDate.Year); + } + bookInfo += string.Format("{0} {1}
", Localizer.Text("Format:"), book.BookType == BookType.EPUB ? "epub" : "fb2"); + bookInfo += string.Format("{0} {1} Kb
", Localizer.Text("Size:"), (int)book.DocumentSize / 1024); + if (!string.IsNullOrEmpty(book.Sequence)) + { + bookInfo += string.Format("{0} {1} #{2}
", Localizer.Text("Series:"), book.Sequence, book.NumberInSequence); + } + + entry.Add( + new XElement(Namespaces.dc + "language", book.Language), + new XElement(Namespaces.dc + "format", book.BookType == BookType.FB2 ? "fb2+zip" : "epub+zip"), + new XElement("content", new XAttribute("type", "text/html"), bookInfo)); + + if (book.HasCover) + { + entry.Add( + // Adding cover page and thumbnail links + new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/image"), new XAttribute("type", "image/jpeg")), + new XElement("link", new XAttribute("href", "/cover/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image"), new XAttribute("type", "image/jpeg")), + new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "http://opds-spec.org/thumbnail"), new XAttribute("type", "image/jpeg")), + new XElement("link", new XAttribute("href", "/thumbnail/" + book.ID + ".jpeg"), new XAttribute("rel", "x-stanza-cover-image-thumbnail"), new XAttribute("type", "image/jpeg")) + // Adding download links + ); + } + + string fileName = Uri.EscapeDataString(Transliteration.Front(string.Format("{0}_{1}", book.Authors.First(), book.Title)).SanitizeFileName()); + string url = "/" + string.Format("{0}/{1}", book.ID, fileName); + if (book.BookType == BookType.EPUB || (book.BookType == BookType.FB2 && !acceptFB2 && !string.IsNullOrEmpty(TinyOPDS.Properties.Settings.Default.ConvertorPath))) + { + entry.Add(new XElement("link", new XAttribute("href", url + ".epub"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/epub+zip"))); + } + + if (book.BookType == BookType.FB2) + { + entry.Add(new XElement("link", new XAttribute("href", url + ".fb2.zip"), new XAttribute("rel", "http://opds-spec.org/acquisition/open-access"), new XAttribute("type", "application/fb2+zip"))); + } + + foreach (string author in book.Authors) + { + entry.Add(new XElement("link", + new XAttribute("href", "/author/" + Uri.EscapeDataString(author)), + new XAttribute("rel", "related"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"), + new XAttribute("title", string.Format(Localizer.Text("All books by author {0}"), author)))); + } + + if (!string.IsNullOrEmpty(book.Sequence)) + { + entry.Add(new XElement("link", + new XAttribute("href", "/sequence/" + Uri.EscapeDataString(book.Sequence)), + new XAttribute("rel", "related"), + new XAttribute("type", "application/atom+xml;profile=opds-catalog"), + new XAttribute("title", string.Format(Localizer.Text("All books by series {0}"), book.Sequence)))); + } + + doc.Root.Add(entry); + } + return doc; + } + } } \ No newline at end of file diff --git a/trunk/TinyOPDS/OPDS/OpenSearch.cs b/TinyOPDS/OPDS/OpenSearch.cs similarity index 98% rename from trunk/TinyOPDS/OPDS/OpenSearch.cs rename to TinyOPDS/OPDS/OpenSearch.cs index 7e7d291..9dc086f 100644 --- a/trunk/TinyOPDS/OPDS/OpenSearch.cs +++ b/TinyOPDS/OPDS/OpenSearch.cs @@ -1,113 +1,113 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module contains OPDS OpenSearch implementation - * - * TODO: implement SOUNDEX search - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using System.Web; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - public class OpenSearch - { - public XDocument OpenSearchDescription() - { - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("OpenSearchDescription", - new XElement("ShortName", "TinyOPDS"), - new XElement("LongName", "TinyOPDS"), - new XElement("Url", new XAttribute("type", "application/atom+xml"), new XAttribute("template", "/search?searchTerm={searchTerms}")), - new XElement("Image", "/favicon.ico", new XAttribute("width", "16"), new XAttribute("height", "16")), - new XElement("Tags"), - new XElement("Contact"), - new XElement("Developer"), - new XElement("Attribution"), - new XElement("SyndicationRight", "open"), - new XElement("AdultContent", "false"), - new XElement("Language", "*"), - new XElement("OutputEncoding", "UTF-8"), - new XElement("InputEncoding", "UTF-8"))); - - return doc; - } - - public XDocument Search(string searchPattern, string searchType = "", bool fb2Only = false, int pageNumber = 0, int threshold = 100) - { - if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower(); - - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - new XElement("id", "tag:search:"+searchPattern), - new XElement("title", Localizer.Text("Search results")), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/series.ico"), - // Add links - Links.opensearch, Links.search, Links.start, Links.self) - ); - - List authors = new List(); - List titles = new List(); - - if (string.IsNullOrEmpty(searchType)) - { - string transSearchPattern = Transliteration.Back(searchPattern, TransliterationType.GOST); - authors = Library.GetAuthorsByName(searchPattern, true); - if (authors.Count == 0 && !string.IsNullOrEmpty(transSearchPattern)) - { - authors = Library.GetAuthorsByName(transSearchPattern, true); - } - titles = Library.GetBooksByTitle(searchPattern); - if (titles.Count == 0 && !string.IsNullOrEmpty(transSearchPattern)) - { - titles = Library.GetBooksByTitle(transSearchPattern); - } - } - - if (string.IsNullOrEmpty(searchType) && authors.Count > 0 && titles.Count > 0) - { - // Add two navigation entries: search by authors name and book title - doc.Root.Add( - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:search:author"), - new XElement("title", Localizer.Text("Search authors")), - new XElement("content", Localizer.Text("Search authors by name"), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/search?searchType=authors&searchTerm=" + Uri.EscapeDataString(searchPattern)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))), - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:search:title"), - new XElement("title", Localizer.Text("Search books")), - new XElement("content", Localizer.Text("Search books by title"), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/search?searchType=books&searchTerm=" + Uri.EscapeDataString(searchPattern)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))) - ); - } - else if (searchType.Equals("authors") || (authors.Count > 0 && titles.Count == 0)) - { - return new AuthorsCatalog().GetCatalog(searchPattern, true); - } - else if (searchType.Equals("books") || (titles.Count > 0 && authors.Count == 0)) - { - if (pageNumber > 0) searchPattern += "/" + pageNumber; - return new BooksCatalog().GetCatalogByTitle(searchPattern, fb2Only, 0, 1000); - } - return doc; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module contains OPDS OpenSearch implementation + * + * TODO: implement SOUNDEX search + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Web; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + public class OpenSearch + { + public XDocument OpenSearchDescription() + { + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("OpenSearchDescription", + new XElement("ShortName", "TinyOPDS"), + new XElement("LongName", "TinyOPDS"), + new XElement("Url", new XAttribute("type", "application/atom+xml"), new XAttribute("template", "/search?searchTerm={searchTerms}")), + new XElement("Image", "/favicon.ico", new XAttribute("width", "16"), new XAttribute("height", "16")), + new XElement("Tags"), + new XElement("Contact"), + new XElement("Developer"), + new XElement("Attribution"), + new XElement("SyndicationRight", "open"), + new XElement("AdultContent", "false"), + new XElement("Language", "*"), + new XElement("OutputEncoding", "UTF-8"), + new XElement("InputEncoding", "UTF-8"))); + + return doc; + } + + public XDocument Search(string searchPattern, string searchType = "", bool fb2Only = false, int pageNumber = 0, int threshold = 100) + { + if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' ').ToLower(); + + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + new XElement("id", "tag:search:"+searchPattern), + new XElement("title", Localizer.Text("Search results")), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/series.ico"), + // Add links + Links.opensearch, Links.search, Links.start, Links.self) + ); + + List authors = new List(); + List titles = new List(); + + if (string.IsNullOrEmpty(searchType)) + { + string transSearchPattern = Transliteration.Back(searchPattern, TransliterationType.GOST); + authors = Library.GetAuthorsByName(searchPattern, true); + if (authors.Count == 0 && !string.IsNullOrEmpty(transSearchPattern)) + { + authors = Library.GetAuthorsByName(transSearchPattern, true); + } + titles = Library.GetBooksByTitle(searchPattern); + if (titles.Count == 0 && !string.IsNullOrEmpty(transSearchPattern)) + { + titles = Library.GetBooksByTitle(transSearchPattern); + } + } + + if (string.IsNullOrEmpty(searchType) && authors.Count > 0 && titles.Count > 0) + { + // Add two navigation entries: search by authors name and book title + doc.Root.Add( + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:search:author"), + new XElement("title", Localizer.Text("Search authors")), + new XElement("content", Localizer.Text("Search authors by name"), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/search?searchType=authors&searchTerm=" + Uri.EscapeDataString(searchPattern)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))), + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:search:title"), + new XElement("title", Localizer.Text("Search books")), + new XElement("content", Localizer.Text("Search books by title"), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/search?searchType=books&searchTerm=" + Uri.EscapeDataString(searchPattern)), new XAttribute("type", "application/atom+xml;profile=opds-catalog"))) + ); + } + else if (searchType.Equals("authors") || (authors.Count > 0 && titles.Count == 0)) + { + return new AuthorsCatalog().GetCatalog(searchPattern, true); + } + else if (searchType.Equals("books") || (titles.Count > 0 && authors.Count == 0)) + { + if (pageNumber > 0) searchPattern += "/" + pageNumber; + return new BooksCatalog().GetCatalogByTitle(searchPattern, fb2Only, 0, 1000); + } + return doc; + } + } +} diff --git a/trunk/TinyOPDS/OPDS/RootCatalog.cs b/TinyOPDS/OPDS/RootCatalog.cs similarity index 98% rename from trunk/TinyOPDS/OPDS/RootCatalog.cs rename to TinyOPDS/OPDS/RootCatalog.cs index cfaf9eb..2c2e369 100644 --- a/trunk/TinyOPDS/OPDS/RootCatalog.cs +++ b/TinyOPDS/OPDS/RootCatalog.cs @@ -1,93 +1,93 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the OPDS RootCatalog class - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - /// - /// Root catalog class - /// - class RootCatalog - { - public XDocument GetCatalog() - { - return new XDocument( - // Add root element with namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), - new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), - new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - - new XElement("id", "tag:root"), - new XElement("title", TinyOPDS.Properties.Settings.Default.ServerName), - new XElement("subtitle", Utils.ServerVersionName), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/favicon.ico"), - - // Add links - Links.opensearch, - Links.search, - Links.start, - Links.self, - - // Add new books entry (if we have a new books of course!) - Library.NewBooksCount == 0 ? null : - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:root:new"), - new XElement("title", Localizer.Text("New books (by date added)"), new XAttribute("type", "text")), - new XElement("content", string.Format(Localizer.Text("{0} new books ordered by date"), Library.NewBooksCount), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/newdate"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ), - - Library.NewBooksCount == 0 ? null : - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:root:new"), - new XElement("title", Localizer.Text("New books (alphabetically)"), new XAttribute("type", "text")), - new XElement("content", string.Format(Localizer.Text("{0} new books ordered alphabetically"), Library.NewBooksCount), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/newtitle"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ), - - // Add catalog entries - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:root:authors"), - new XElement("title", Localizer.Text("By authors"), new XAttribute("type", "text")), - new XElement("content", string.Format(Localizer.Text("{0} books by {1} authors"), Library.Count, Library.Authors.Count), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/authorsindex"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ), - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:root:sequences"), - new XElement("title", Localizer.Text("By series"), new XAttribute("type", "text")), - new XElement("content", string.Format(Localizer.Text("{0} books by {1} series"), Library.Count, Library.Sequences.Count), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/sequencesindex"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ), - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:root:genre"), - new XElement("title", Localizer.Text("By genres"), new XAttribute("type", "text")), - new XElement("content", Localizer.Text("Books grouped by genres"), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/genres"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ) - ) - ); - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the OPDS RootCatalog class + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + /// + /// Root catalog class + /// + class RootCatalog + { + public XDocument GetCatalog() + { + return new XDocument( + // Add root element with namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), + new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), + new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + + new XElement("id", "tag:root"), + new XElement("title", TinyOPDS.Properties.Settings.Default.ServerName), + new XElement("subtitle", Utils.ServerVersionName), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/favicon.ico"), + + // Add links + Links.opensearch, + Links.search, + Links.start, + Links.self, + + // Add new books entry (if we have a new books of course!) + Library.NewBooksCount == 0 ? null : + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:root:new"), + new XElement("title", Localizer.Text("New books (by date added)"), new XAttribute("type", "text")), + new XElement("content", string.Format(Localizer.Text("{0} new books ordered by date"), Library.NewBooksCount), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/newdate"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ), + + Library.NewBooksCount == 0 ? null : + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:root:new"), + new XElement("title", Localizer.Text("New books (alphabetically)"), new XAttribute("type", "text")), + new XElement("content", string.Format(Localizer.Text("{0} new books ordered alphabetically"), Library.NewBooksCount), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/newtitle"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ), + + // Add catalog entries + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:root:authors"), + new XElement("title", Localizer.Text("By authors"), new XAttribute("type", "text")), + new XElement("content", string.Format(Localizer.Text("{0} books by {1} authors"), Library.Count, Library.Authors.Count), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/authorsindex"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ), + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:root:sequences"), + new XElement("title", Localizer.Text("By series"), new XAttribute("type", "text")), + new XElement("content", string.Format(Localizer.Text("{0} books by {1} series"), Library.Count, Library.Sequences.Count), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/sequencesindex"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ), + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:root:genre"), + new XElement("title", Localizer.Text("By genres"), new XAttribute("type", "text")), + new XElement("content", Localizer.Text("Books grouped by genres"), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/genres"), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ) + ) + ); + } + } +} diff --git a/trunk/TinyOPDS/OPDS/SequencesCatalog.cs b/TinyOPDS/OPDS/SequencesCatalog.cs similarity index 97% rename from trunk/TinyOPDS/OPDS/SequencesCatalog.cs rename to TinyOPDS/OPDS/SequencesCatalog.cs index c066a21..b88cd5a 100644 --- a/trunk/TinyOPDS/OPDS/SequencesCatalog.cs +++ b/TinyOPDS/OPDS/SequencesCatalog.cs @@ -1,91 +1,91 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the OPDS SequencesCatalog class (book series) - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; -using System.Web; - -using TinyOPDS.Data; - -namespace TinyOPDS.OPDS -{ - /// - /// Sequences acquisition feed class - /// - public class SequencesCatalog - { - public XDocument GetCatalog(string searchPattern, int threshold = 100) - { - if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' '); - - XDocument doc = new XDocument( - // Add root element and namespaces - new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), - new XElement("id", "tag:sequences"), - new XElement("title", Localizer.Text("Book series")), - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("icon", "/series.ico"), - // Add links - Links.opensearch, Links.search, Links.start) - ); - - // Get all authors names starting with searchPattern - List Sequences = (from s in Library.Sequences where s.StartsWith(searchPattern) && s.Length > searchPattern.Length + 1 select s).ToList(); - - if (Sequences.Count > threshold) - { - Dictionary sequences = (from a in Sequences - group a by (a.Length > searchPattern.Length ? a.Substring(0, searchPattern.Length + 1) : a) into g - where g.Count() > 1 - select new { Name = g, Count = g.Count() }).ToDictionary(x => x.Name.Key, y => y.Count); - - // Add catalog entries - foreach (KeyValuePair sequence in sequences) - { - doc.Root.Add( - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:sequences:" + sequence.Key), - new XElement("title", sequence.Key), - new XElement("content", string.Format(Localizer.Text("Total series on {0}: {1}"), sequence.Key, sequence.Value), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/sequencesindex/" + Uri.EscapeDataString(sequence.Key)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ) - ); - } - } - // - else - { - List sequences = (from s in Sequences where s.StartsWith(searchPattern) select s).ToList(); - // Add catalog entries - foreach (string sequence in sequences) - { - var seriesCount = Library.GetBooksBySequence(sequence).Count; - - doc.Root.Add( - new XElement("entry", - new XElement("updated", DateTime.UtcNow.ToUniversalTime()), - new XElement("id", "tag:sequences:" + sequence), - new XElement("title", sequence), - new XElement("content", string.Format(Localizer.Text("{0} books in {1}"), seriesCount, sequence), new XAttribute("type", "text")), - new XElement("link", new XAttribute("href", "/sequence/" + Uri.EscapeDataString(sequence)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) - ) - ); - } - } - return doc; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the OPDS SequencesCatalog class (book series) + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; +using System.Web; + +using TinyOPDS.Data; + +namespace TinyOPDS.OPDS +{ + /// + /// Sequences acquisition feed class + /// + public class SequencesCatalog + { + public XDocument GetCatalog(string searchPattern, int threshold = 100) + { + if (!string.IsNullOrEmpty(searchPattern)) searchPattern = Uri.UnescapeDataString(searchPattern).Replace('+', ' '); + + XDocument doc = new XDocument( + // Add root element and namespaces + new XElement("feed", new XAttribute(XNamespace.Xmlns + "dc", Namespaces.dc), new XAttribute(XNamespace.Xmlns + "os", Namespaces.os), new XAttribute(XNamespace.Xmlns + "opds", Namespaces.opds), + new XElement("id", "tag:sequences"), + new XElement("title", Localizer.Text("Book series")), + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("icon", "/series.ico"), + // Add links + Links.opensearch, Links.search, Links.start) + ); + + // Get all authors names starting with searchPattern + List Sequences = (from s in Library.Sequences where s.StartsWith(searchPattern) && s.Length > searchPattern.Length + 1 select s).ToList(); + + if (Sequences.Count > threshold) + { + Dictionary sequences = (from a in Sequences + group a by (a.Length > searchPattern.Length ? a.Substring(0, searchPattern.Length + 1) : a) into g + where g.Count() > 1 + select new { Name = g, Count = g.Count() }).ToDictionary(x => x.Name.Key, y => y.Count); + + // Add catalog entries + foreach (KeyValuePair sequence in sequences) + { + doc.Root.Add( + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:sequences:" + sequence.Key), + new XElement("title", sequence.Key), + new XElement("content", string.Format(Localizer.Text("Total series on {0}: {1}"), sequence.Key, sequence.Value), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/sequencesindex/" + Uri.EscapeDataString(sequence.Key)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ) + ); + } + } + // + else + { + List sequences = (from s in Sequences where s.StartsWith(searchPattern) select s).ToList(); + // Add catalog entries + foreach (string sequence in sequences) + { + var seriesCount = Library.GetBooksBySequence(sequence).Count; + + doc.Root.Add( + new XElement("entry", + new XElement("updated", DateTime.UtcNow.ToUniversalTime()), + new XElement("id", "tag:sequences:" + sequence), + new XElement("title", sequence), + new XElement("content", string.Format(Localizer.Text("{0} books in {1}"), seriesCount, sequence), new XAttribute("type", "text")), + new XElement("link", new XAttribute("href", "/sequence/" + Uri.EscapeDataString(sequence)), new XAttribute("type", "application/atom+xml;profile=opds-catalog")) + ) + ); + } + } + return doc; + } + } +} diff --git a/trunk/TinyOPDS/Parsers/BookParser.cs b/TinyOPDS/Parsers/BookParser.cs similarity index 96% rename from trunk/TinyOPDS/Parsers/BookParser.cs rename to TinyOPDS/Parsers/BookParser.cs index 40146be..d54e89c 100644 --- a/trunk/TinyOPDS/Parsers/BookParser.cs +++ b/TinyOPDS/Parsers/BookParser.cs @@ -1,63 +1,63 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * Base class for book parsers - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Drawing; - -using TinyOPDS.Data; - -namespace TinyOPDS.Parsers -{ - public abstract class BookParser - { - /// - /// - /// - /// - /// - /// - public abstract Book Parse(Stream stream, string fileName); - - /// - /// - /// - /// - /// - public Book Parse(string fileName) - { - using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - return Parse(stream, fileName); - } - - /// - /// - /// - /// - /// - public abstract Image GetCoverImage(Stream stream, string fileName); - /// - /// - /// - /// - /// - /// - public Image GetCoverImage(string fileName) - { - using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) - return GetCoverImage(stream, fileName); - } - - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * Base class for book parsers + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Drawing; + +using TinyOPDS.Data; + +namespace TinyOPDS.Parsers +{ + public abstract class BookParser + { + /// + /// + /// + /// + /// + /// + public abstract Book Parse(Stream stream, string fileName); + + /// + /// + /// + /// + /// + public Book Parse(string fileName) + { + using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + return Parse(stream, fileName); + } + + /// + /// + /// + /// + /// + public abstract Image GetCoverImage(Stream stream, string fileName); + /// + /// + /// + /// + /// + /// + public Image GetCoverImage(string fileName) + { + using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + return GetCoverImage(stream, fileName); + } + + } +} diff --git a/trunk/TinyOPDS/Parsers/ePubParser.cs b/TinyOPDS/Parsers/ePubParser.cs similarity index 97% rename from trunk/TinyOPDS/Parsers/ePubParser.cs rename to TinyOPDS/Parsers/ePubParser.cs index 907e31b..41426a2 100644 --- a/trunk/TinyOPDS/Parsers/ePubParser.cs +++ b/TinyOPDS/Parsers/ePubParser.cs @@ -1,163 +1,163 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * ePub parser class implementation - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Drawing; -using System.Drawing.Imaging; - -using TinyOPDS.Data; -using eBdb.EpubReader; - -namespace TinyOPDS.Parsers -{ - public class ePubParser : BookParser - { - /// - /// - /// - /// - /// - /// - public override Book Parse(Stream stream, string fileName) - { - Book book = new Book(fileName); - try - { - book.DocumentSize = (UInt32)stream.Length; - stream.Position = 0; - Epub epub = new Epub(stream); - book.ID = epub.UUID; - if (epub.Date != null && epub.Date.Count > 0) - { - try { book.BookDate = DateTime.Parse(epub.Date.First().Date); } - catch - { - int year; - if (int.TryParse(epub.Date.First().Date, out year)) book.BookDate = new DateTime(year, 1, 1); - } - } - book.Title = epub.Title[0]; - book.Authors = new List(); - book.Authors.AddRange(epub.Creator); - for (int i = 0; i < book.Authors.Count; i++) book.Authors[i] = book.Authors[i].Capitalize(); - book.Genres = LookupGenres(epub.Subject); - if (epub.Description != null && epub.Description.Count > 0) book.Annotation = epub.Description.First(); - if (epub.Language != null && epub.Language.Count > 0) book.Language = epub.Language.First(); - - // Lookup cover - if (epub.ExtendedData != null) - { - foreach (ExtendedData value in epub.ExtendedData.Values) - { - string s = value.FileName.ToLower(); - if (s.Contains(".jpeg") || s.Contains(".jpg") || s.Contains(".png")) - { - if (value.ID.ToLower().Contains("cover") || s.Contains("cover")) - { - book.HasCover = true; - break; - } - } - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "exception {0}" , e.Message); - } - finally - { - if (stream != null) - { - stream.Dispose(); - stream = null; - } - } - return book; - } - - /// - /// Epub's "subjects" are non-formal and extremely messy :( - /// This function will try to find a corresponding genres from the FB2 standard genres by using Soundex algorithm - /// - /// - /// - private List LookupGenres(List subjects) - { - List genres = new List(); - if (subjects == null || subjects.Count < 1) - { - genres.Add("prose"); - } - else - { - foreach (string subj in subjects) - { - var genre = Library.SoundexedGenres.Where(g => g.Key.StartsWith(subj.SoundexByWord()) && g.Key.WordsCount() <= subj.WordsCount()+1).FirstOrDefault(); - if (genre.Key != null) genres.Add(genre.Value); - } - if (genres.Count < 1) genres.Add("prose"); - } - return genres; - } - - /// - /// - /// - /// - /// - /// - public override Image GetCoverImage(Stream stream, string fileName) - { - Image image = null; - try - { - stream.Position = 0; - Epub epub = new Epub(stream); - if (epub.ExtendedData != null) - { - foreach (ExtendedData value in epub.ExtendedData.Values) - { - string s = value.FileName.ToLower(); - if (s.Contains(".jpeg") || s.Contains(".jpg") || s.Contains(".png")) - { - if (value.ID.ToLower().Contains("cover") || s.Contains("cover")) - { - using (MemoryStream memStream = new MemoryStream(value.GetContentAsBinary())) - { - image = Image.FromStream(memStream); - // Convert image to jpeg - string mimeType = value.MimeType.ToLower(); - ImageFormat fmt = mimeType.Contains("png") ? ImageFormat.Png : ImageFormat.Gif; - if (!mimeType.Contains("jpeg")) - { - image = Image.FromStream(image.ToStream(fmt)); - } - image = image.Resize(CoverImage.CoverSize); - } - break; - } - } - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "GetCoverImage exception {0}", e.Message); - } - return image; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * ePub parser class implementation + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Drawing; +using System.Drawing.Imaging; + +using TinyOPDS.Data; +using eBdb.EpubReader; + +namespace TinyOPDS.Parsers +{ + public class ePubParser : BookParser + { + /// + /// + /// + /// + /// + /// + public override Book Parse(Stream stream, string fileName) + { + Book book = new Book(fileName); + try + { + book.DocumentSize = (UInt32)stream.Length; + stream.Position = 0; + Epub epub = new Epub(stream); + book.ID = epub.UUID; + if (epub.Date != null && epub.Date.Count > 0) + { + try { book.BookDate = DateTime.Parse(epub.Date.First().Date); } + catch + { + int year; + if (int.TryParse(epub.Date.First().Date, out year)) book.BookDate = new DateTime(year, 1, 1); + } + } + book.Title = epub.Title[0]; + book.Authors = new List(); + book.Authors.AddRange(epub.Creator); + for (int i = 0; i < book.Authors.Count; i++) book.Authors[i] = book.Authors[i].Capitalize(); + book.Genres = LookupGenres(epub.Subject); + if (epub.Description != null && epub.Description.Count > 0) book.Annotation = epub.Description.First(); + if (epub.Language != null && epub.Language.Count > 0) book.Language = epub.Language.First(); + + // Lookup cover + if (epub.ExtendedData != null) + { + foreach (ExtendedData value in epub.ExtendedData.Values) + { + string s = value.FileName.ToLower(); + if (s.Contains(".jpeg") || s.Contains(".jpg") || s.Contains(".png")) + { + if (value.ID.ToLower().Contains("cover") || s.Contains("cover")) + { + book.HasCover = true; + break; + } + } + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "exception {0}" , e.Message); + } + finally + { + if (stream != null) + { + stream.Dispose(); + stream = null; + } + } + return book; + } + + /// + /// Epub's "subjects" are non-formal and extremely messy :( + /// This function will try to find a corresponding genres from the FB2 standard genres by using Soundex algorithm + /// + /// + /// + private List LookupGenres(List subjects) + { + List genres = new List(); + if (subjects == null || subjects.Count < 1) + { + genres.Add("prose"); + } + else + { + foreach (string subj in subjects) + { + var genre = Library.SoundexedGenres.Where(g => g.Key.StartsWith(subj.SoundexByWord()) && g.Key.WordsCount() <= subj.WordsCount()+1).FirstOrDefault(); + if (genre.Key != null) genres.Add(genre.Value); + } + if (genres.Count < 1) genres.Add("prose"); + } + return genres; + } + + /// + /// + /// + /// + /// + /// + public override Image GetCoverImage(Stream stream, string fileName) + { + Image image = null; + try + { + stream.Position = 0; + Epub epub = new Epub(stream); + if (epub.ExtendedData != null) + { + foreach (ExtendedData value in epub.ExtendedData.Values) + { + string s = value.FileName.ToLower(); + if (s.Contains(".jpeg") || s.Contains(".jpg") || s.Contains(".png")) + { + if (value.ID.ToLower().Contains("cover") || s.Contains("cover")) + { + using (MemoryStream memStream = new MemoryStream(value.GetContentAsBinary())) + { + image = Image.FromStream(memStream); + // Convert image to jpeg + string mimeType = value.MimeType.ToLower(); + ImageFormat fmt = mimeType.Contains("png") ? ImageFormat.Png : ImageFormat.Gif; + if (!mimeType.Contains("jpeg")) + { + image = Image.FromStream(image.ToStream(fmt)); + } + image = image.Resize(CoverImage.CoverSize); + } + break; + } + } + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "GetCoverImage exception {0}", e.Message); + } + return image; + } + } +} diff --git a/trunk/TinyOPDS/Parsers/fb2Parser.cs b/TinyOPDS/Parsers/fb2Parser.cs similarity index 97% rename from trunk/TinyOPDS/Parsers/fb2Parser.cs rename to TinyOPDS/Parsers/fb2Parser.cs index e7c9c9b..64dc930 100644 --- a/trunk/TinyOPDS/Parsers/fb2Parser.cs +++ b/TinyOPDS/Parsers/fb2Parser.cs @@ -1,258 +1,258 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * FB2 parser implementation - * - ************************************************************/ - -using System; -using System.Diagnostics.Contracts; -using System.IO; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Xml; -using System.Xml.Linq; -using System.Drawing; -using System.Drawing.Imaging; - -using FB2Library; -using FB2Library.HeaderItems; -using FB2Library.Elements; -using TinyOPDS.Data; -using TinyOPDS.Sgml; - -namespace TinyOPDS.Parsers -{ - public class FB2Parser : BookParser - { - private XDocument xml = null; - - private static SgmlDtd LoadFb2Dtd(SgmlReader sgml) - { - Contract.Requires(sgml != null); - Contract.Ensures(Contract.Result() != null); - - Assembly assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".Resources.fb2.dtd")) - { - using (StreamReader reader = new StreamReader(stream)) - { - return SgmlDtd.Parse(new Uri("http://localhost"), sgml.DocType, null, reader, null, sgml.WebProxy, sgml.NameTable); - } - } - } - - /// - /// - /// - /// - /// - /// - public override Book Parse(Stream stream, string fileName) - { - Book book = new Book(fileName); - book.DocumentSize = (UInt32)stream.Length; - - try - { - FB2File fb2 = new FB2File(); - // Load header only - stream.Position = 0; - - // Project Mono has a bug: Xdocument.Load() can't detect encoding - string encoding = string.Empty; - if (Utils.IsLinux) - { - using (StreamReader sr = new StreamReader(stream)) - { - encoding = sr.ReadLine(); - int idx = encoding.ToLower().IndexOf("encoding=\""); - if (idx > 0) - { - encoding = encoding.Substring(idx + 10); - encoding = encoding.Substring(0, encoding.IndexOf('"')); - stream.Position = 0; - using (StreamReader esr = new StreamReader(stream, Encoding.GetEncoding(encoding))) - { - string xmlStr = esr.ReadToEnd(); - try - { - xml = XDocument.Parse(xmlStr, LoadOptions.PreserveWhitespace); - } - catch - { - stream.Position = 0; - - using (HtmlStream reader = new HtmlStream(stream, Encoding.Default)) - { - using (SgmlReader sgmlReader = new SgmlReader()) - { - sgmlReader.InputStream = reader; - sgmlReader.Dtd = LoadFb2Dtd(sgmlReader); - - xml = XDocument.Load(sgmlReader); - } - } - } - } - } - } - } - - if (xml == null) - { - try - { - xml = XDocument.Load(stream); - } - catch - { - stream.Position = 0; - - // This code will try to use the sgml based reader for not well-formed xml files - using (HtmlStream reader = new HtmlStream(stream, Encoding.Default)) - { - using (SgmlReader sgmlReader = new SgmlReader()) - { - sgmlReader.InputStream = reader; - sgmlReader.Dtd = LoadFb2Dtd(sgmlReader); - - xml = XDocument.Load(sgmlReader); - } - } - } - } - - fb2.Load(xml, true); - - if (fb2.DocumentInfo != null) - { - book.ID = fb2.DocumentInfo.ID; - if (fb2.DocumentInfo.DocumentVersion != null) book.Version = (float)fb2.DocumentInfo.DocumentVersion; - if (fb2.DocumentInfo.DocumentDate != null) book.DocumentDate = fb2.DocumentInfo.DocumentDate.DateValue; - } - - if (fb2.TitleInfo != null) - { - if (fb2.TitleInfo.Cover != null && fb2.TitleInfo.Cover.HasImages()) book.HasCover = true; - if (fb2.TitleInfo.BookTitle != null) book.Title = fb2.TitleInfo.BookTitle.Text; - if (fb2.TitleInfo.Annotation != null) book.Annotation = fb2.TitleInfo.Annotation.ToString(); - if (fb2.TitleInfo.Sequences != null && fb2.TitleInfo.Sequences.Count > 0) - { - book.Sequence = fb2.TitleInfo.Sequences.First().Name.Capitalize(true); - if (fb2.TitleInfo.Sequences.First().Number != null) - { - book.NumberInSequence = (UInt32)(fb2.TitleInfo.Sequences.First().Number); - } - } - if (fb2.TitleInfo.Language != null) book.Language = fb2.TitleInfo.Language; - if (fb2.TitleInfo.BookDate != null) book.BookDate = fb2.TitleInfo.BookDate.DateValue; - if (fb2.TitleInfo.BookAuthors != null && fb2.TitleInfo.BookAuthors.Any()) - { - book.Authors = new List(); - book.Authors.AddRange(from ba in fb2.TitleInfo.BookAuthors select string.Concat(ba.LastName, " ", ba.FirstName, " ", ba.MiddleName).Replace(" ", " ").Capitalize()); - } - if (fb2.TitleInfo.Translators != null && fb2.TitleInfo.Translators.Any()) - { - book.Translators = new List(); - book.Translators.AddRange(from ba in fb2.TitleInfo.Translators select string.Concat(ba.LastName, " ", ba.FirstName, " ", ba.MiddleName).Replace(" ", " ").Capitalize()); - } - if (fb2.TitleInfo.Genres != null && fb2.TitleInfo.Genres.Any()) - { - book.Genres = new List(); - book.Genres.AddRange((from g in fb2.TitleInfo.Genres select g.Genre).ToList()); - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "Book.Parse() exception {0} on file: {1}", e.Message, fileName); - } - finally - { - if (stream != null) - { - stream.Dispose(); - stream = null; - } - } - - return book; - } - - /// - /// - /// - /// - /// - public override Image GetCoverImage(Stream stream, string fileName) - { - Image image = null; - try - { - FB2File fb2 = new FB2File(); - stream.Position = 0; - xml = XDocument.Load(stream); - fb2.Load(xml, false); - - if (fb2.TitleInfo != null && fb2.TitleInfo.Cover != null && fb2.TitleInfo.Cover.HasImages() && fb2.Images.Count > 0) - { - string coverHRef = fb2.TitleInfo.Cover.CoverpageImages.First().HRef.Substring(1); - var binaryObject = fb2.Images.First(item => item.Value.Id == coverHRef); - if (binaryObject.Value.BinaryData != null && binaryObject.Value.BinaryData.Length > 0) - { - using (MemoryStream memStream = new MemoryStream(binaryObject.Value.BinaryData)) - { - image = Image.FromStream(memStream); - // Convert image to jpeg - ImageFormat fmt = binaryObject.Value.ContentType == ContentTypeEnum.ContentTypePng ? ImageFormat.Png : ImageFormat.Gif; - if (binaryObject.Value.ContentType != ContentTypeEnum.ContentTypeJpeg) - { - image = Image.FromStream(image.ToStream(fmt)); - } - image = image.Resize(CoverImage.CoverSize); - } - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, "Book.GetCoverImage() exception {0} on file: {1}", e.Message, fileName); - } - return image; - } - - /// - /// Remove illegal XML characters from a string. - /// - public string SanitizeXmlString(string xml) - { - StringBuilder buffer = new StringBuilder(xml.Length); - foreach (char c in xml) if (IsLegalXmlChar(c)) buffer.Append(c); - return buffer.ToString(); - } - - /// - /// Whether a given character is allowed by XML 1.0. - /// - public bool IsLegalXmlChar(int character) - { - return - ( - character == 0x9 /* == '\t' == 9 */ || - character == 0xA /* == '\n' == 10 */ || - character == 0xD /* == '\r' == 13 */ || - (character >= 0x20 && character <= 0xD7FF) || - (character >= 0xE000 && character <= 0xFFFD) || - (character >= 0x10000 && character <= 0x10FFFF) - ); - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * FB2 parser implementation + * + ************************************************************/ + +using System; +using System.Diagnostics.Contracts; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Drawing; +using System.Drawing.Imaging; + +using FB2Library; +using FB2Library.HeaderItems; +using FB2Library.Elements; +using TinyOPDS.Data; +using TinyOPDS.Sgml; + +namespace TinyOPDS.Parsers +{ + public class FB2Parser : BookParser + { + private XDocument xml = null; + + private static SgmlDtd LoadFb2Dtd(SgmlReader sgml) + { + Contract.Requires(sgml != null); + Contract.Ensures(Contract.Result() != null); + + Assembly assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream(assembly.GetName().Name + ".Resources.fb2.dtd")) + { + using (StreamReader reader = new StreamReader(stream)) + { + return SgmlDtd.Parse(new Uri("http://localhost"), sgml.DocType, null, reader, null, sgml.WebProxy, sgml.NameTable); + } + } + } + + /// + /// + /// + /// + /// + /// + public override Book Parse(Stream stream, string fileName) + { + Book book = new Book(fileName); + book.DocumentSize = (UInt32)stream.Length; + + try + { + FB2File fb2 = new FB2File(); + // Load header only + stream.Position = 0; + + // Project Mono has a bug: Xdocument.Load() can't detect encoding + string encoding = string.Empty; + if (Utils.IsLinux) + { + using (StreamReader sr = new StreamReader(stream)) + { + encoding = sr.ReadLine(); + int idx = encoding.ToLower().IndexOf("encoding=\""); + if (idx > 0) + { + encoding = encoding.Substring(idx + 10); + encoding = encoding.Substring(0, encoding.IndexOf('"')); + stream.Position = 0; + using (StreamReader esr = new StreamReader(stream, Encoding.GetEncoding(encoding))) + { + string xmlStr = esr.ReadToEnd(); + try + { + xml = XDocument.Parse(xmlStr, LoadOptions.PreserveWhitespace); + } + catch + { + stream.Position = 0; + + using (HtmlStream reader = new HtmlStream(stream, Encoding.Default)) + { + using (SgmlReader sgmlReader = new SgmlReader()) + { + sgmlReader.InputStream = reader; + sgmlReader.Dtd = LoadFb2Dtd(sgmlReader); + + xml = XDocument.Load(sgmlReader); + } + } + } + } + } + } + } + + if (xml == null) + { + try + { + xml = XDocument.Load(stream); + } + catch + { + stream.Position = 0; + + // This code will try to use the sgml based reader for not well-formed xml files + using (HtmlStream reader = new HtmlStream(stream, Encoding.Default)) + { + using (SgmlReader sgmlReader = new SgmlReader()) + { + sgmlReader.InputStream = reader; + sgmlReader.Dtd = LoadFb2Dtd(sgmlReader); + + xml = XDocument.Load(sgmlReader); + } + } + } + } + + fb2.Load(xml, true); + + if (fb2.DocumentInfo != null) + { + book.ID = fb2.DocumentInfo.ID; + if (fb2.DocumentInfo.DocumentVersion != null) book.Version = (float)fb2.DocumentInfo.DocumentVersion; + if (fb2.DocumentInfo.DocumentDate != null) book.DocumentDate = fb2.DocumentInfo.DocumentDate.DateValue; + } + + if (fb2.TitleInfo != null) + { + if (fb2.TitleInfo.Cover != null && fb2.TitleInfo.Cover.HasImages()) book.HasCover = true; + if (fb2.TitleInfo.BookTitle != null) book.Title = fb2.TitleInfo.BookTitle.Text; + if (fb2.TitleInfo.Annotation != null) book.Annotation = fb2.TitleInfo.Annotation.ToString(); + if (fb2.TitleInfo.Sequences != null && fb2.TitleInfo.Sequences.Count > 0) + { + book.Sequence = fb2.TitleInfo.Sequences.First().Name.Capitalize(true); + if (fb2.TitleInfo.Sequences.First().Number != null) + { + book.NumberInSequence = (UInt32)(fb2.TitleInfo.Sequences.First().Number); + } + } + if (fb2.TitleInfo.Language != null) book.Language = fb2.TitleInfo.Language; + if (fb2.TitleInfo.BookDate != null) book.BookDate = fb2.TitleInfo.BookDate.DateValue; + if (fb2.TitleInfo.BookAuthors != null && fb2.TitleInfo.BookAuthors.Any()) + { + book.Authors = new List(); + book.Authors.AddRange(from ba in fb2.TitleInfo.BookAuthors select string.Concat(ba.LastName, " ", ba.FirstName, " ", ba.MiddleName).Replace(" ", " ").Capitalize()); + } + if (fb2.TitleInfo.Translators != null && fb2.TitleInfo.Translators.Any()) + { + book.Translators = new List(); + book.Translators.AddRange(from ba in fb2.TitleInfo.Translators select string.Concat(ba.LastName, " ", ba.FirstName, " ", ba.MiddleName).Replace(" ", " ").Capitalize()); + } + if (fb2.TitleInfo.Genres != null && fb2.TitleInfo.Genres.Any()) + { + book.Genres = new List(); + book.Genres.AddRange((from g in fb2.TitleInfo.Genres select g.Genre).ToList()); + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "Book.Parse() exception {0} on file: {1}", e.Message, fileName); + } + finally + { + if (stream != null) + { + stream.Dispose(); + stream = null; + } + } + + return book; + } + + /// + /// + /// + /// + /// + public override Image GetCoverImage(Stream stream, string fileName) + { + Image image = null; + try + { + FB2File fb2 = new FB2File(); + stream.Position = 0; + xml = XDocument.Load(stream); + fb2.Load(xml, false); + + if (fb2.TitleInfo != null && fb2.TitleInfo.Cover != null && fb2.TitleInfo.Cover.HasImages() && fb2.Images.Count > 0) + { + string coverHRef = fb2.TitleInfo.Cover.CoverpageImages.First().HRef.Substring(1); + var binaryObject = fb2.Images.First(item => item.Value.Id == coverHRef); + if (binaryObject.Value.BinaryData != null && binaryObject.Value.BinaryData.Length > 0) + { + using (MemoryStream memStream = new MemoryStream(binaryObject.Value.BinaryData)) + { + image = Image.FromStream(memStream); + // Convert image to jpeg + ImageFormat fmt = binaryObject.Value.ContentType == ContentTypeEnum.ContentTypePng ? ImageFormat.Png : ImageFormat.Gif; + if (binaryObject.Value.ContentType != ContentTypeEnum.ContentTypeJpeg) + { + image = Image.FromStream(image.ToStream(fmt)); + } + image = image.Resize(CoverImage.CoverSize); + } + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, "Book.GetCoverImage() exception {0} on file: {1}", e.Message, fileName); + } + return image; + } + + /// + /// Remove illegal XML characters from a string. + /// + public string SanitizeXmlString(string xml) + { + StringBuilder buffer = new StringBuilder(xml.Length); + foreach (char c in xml) if (IsLegalXmlChar(c)) buffer.Append(c); + return buffer.ToString(); + } + + /// + /// Whether a given character is allowed by XML 1.0. + /// + public bool IsLegalXmlChar(int character) + { + return + ( + character == 0x9 /* == '\t' == 9 */ || + character == 0xA /* == '\n' == 10 */ || + character == 0xD /* == '\r' == 13 */ || + (character >= 0x20 && character <= 0xD7FF) || + (character >= 0xE000 && character <= 0xFFFD) || + (character >= 0x10000 && character <= 0x10FFFF) + ); + } + } +} diff --git a/trunk/TinyOPDS/ProcessHelper.cs b/TinyOPDS/ProcessHelper.cs similarity index 96% rename from trunk/TinyOPDS/ProcessHelper.cs rename to TinyOPDS/ProcessHelper.cs index 29c8b13..0fd7169 100644 --- a/trunk/TinyOPDS/ProcessHelper.cs +++ b/TinyOPDS/ProcessHelper.cs @@ -1,157 +1,157 @@ -using System; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.ComponentModel; -using System.Windows; -using System.Threading; -using System.Diagnostics; - -namespace TinyOPDS -{ - /// - /// Helper for the external console apps execution in background (no visible window) - /// Stores process output to the observable collection (so, we can bind output to the ListBox) - /// - public class ProcessHelper : IDisposable - { - private bool disposed = false; - private Process process = new Process(); - private ObservableCollection output = new ObservableCollection(); - ProcessPriorityClass priority; - public AutoResetEvent WaitForOutput = new AutoResetEvent(false); - - /// - /// Default constructor - /// - /// - /// - /// - /// - public ProcessHelper(string CommandPath, string Arguments, bool ParseOutput = false, ProcessPriorityClass Priority = ProcessPriorityClass.Normal) - { - process.StartInfo.FileName = CommandPath; - process.StartInfo.Arguments = Arguments; - - DoParseOutput = ParseOutput; - - // set up output redirection - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.EnableRaisingEvents = true; - process.StartInfo.CreateNoWindow = true; - process.StartInfo.UseShellExecute = false; - priority = Priority; - // see below for output handler - process.ErrorDataReceived += proc_DataReceived; - process.OutputDataReceived += proc_DataReceived; - process.Exited += (__, ____) => - { - if (OnExited != null) - { - if (DoParseOutput) WaitForOutput.WaitOne(3000); - OnExited(this, new EventArgs()); - } - }; - } - - /// - /// Default destructor - /// - ~ProcessHelper() - { - Dispose(); - } - - protected virtual void Dispose(bool disposing) - { - if (!this.disposed && disposing) - { - if (!process.HasExited && IsRunning) process.Kill(); - disposed = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public bool DoParseOutput { set; get; } - - public virtual void ParseOutput(string outString) { } - - private void proc_DataReceived(object sender, DataReceivedEventArgs e) - { - if (e.Data != null) - { - output.Add(e.Data); - ParseOutput(e.Data); - } - } - - public void RunAsync() - { - BackgroundWorker worker = new BackgroundWorker(); - worker.DoWork += (_, __) => - { - try - { - if (process.Start()) - { - process.PriorityClass = priority; - process.BeginErrorReadLine(); - process.BeginOutputReadLine(); - _isRunning = true; - process.WaitForExit(); - } - } - catch(Exception e) - { - Debug.WriteLine("ProcessHelper exception: " + e.ToString()); - } - finally - { - _isRunning = false; - } - }; - worker.RunWorkerAsync(); - } - - /// - /// Raised on process completion - /// - public event EventHandler OnExited; - - /// - /// Process output to stdout - /// - public ObservableCollection ProcessOutput { get { return output; } } - - /// - /// Return current state of process - /// - bool _isRunning = false; - public bool IsRunning { get { return _isRunning; } } - - /// - /// Return status of the current process - /// - public bool IsCompleted { get { return IsRunning?process.HasExited:false; } } - - /// - /// Return process exit code - /// - public int ExitCode { get { return IsCompleted ? process.ExitCode : 0; } } - - /// - /// Associated process priority class - /// - public ProcessPriorityClass PriorityClass - { - get { return process.PriorityClass; } - set { process.PriorityClass = value; } - } - } -} +using System; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Windows; +using System.Threading; +using System.Diagnostics; + +namespace TinyOPDS +{ + /// + /// Helper for the external console apps execution in background (no visible window) + /// Stores process output to the observable collection (so, we can bind output to the ListBox) + /// + public class ProcessHelper : IDisposable + { + private bool disposed = false; + private Process process = new Process(); + private ObservableCollection output = new ObservableCollection(); + ProcessPriorityClass priority; + public AutoResetEvent WaitForOutput = new AutoResetEvent(false); + + /// + /// Default constructor + /// + /// + /// + /// + /// + public ProcessHelper(string CommandPath, string Arguments, bool ParseOutput = false, ProcessPriorityClass Priority = ProcessPriorityClass.Normal) + { + process.StartInfo.FileName = CommandPath; + process.StartInfo.Arguments = Arguments; + + DoParseOutput = ParseOutput; + + // set up output redirection + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.EnableRaisingEvents = true; + process.StartInfo.CreateNoWindow = true; + process.StartInfo.UseShellExecute = false; + priority = Priority; + // see below for output handler + process.ErrorDataReceived += proc_DataReceived; + process.OutputDataReceived += proc_DataReceived; + process.Exited += (__, ____) => + { + if (OnExited != null) + { + if (DoParseOutput) WaitForOutput.WaitOne(3000); + OnExited(this, new EventArgs()); + } + }; + } + + /// + /// Default destructor + /// + ~ProcessHelper() + { + Dispose(); + } + + protected virtual void Dispose(bool disposing) + { + if (!this.disposed && disposing) + { + if (!process.HasExited && IsRunning) process.Kill(); + disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + public bool DoParseOutput { set; get; } + + public virtual void ParseOutput(string outString) { } + + private void proc_DataReceived(object sender, DataReceivedEventArgs e) + { + if (e.Data != null) + { + output.Add(e.Data); + ParseOutput(e.Data); + } + } + + public void RunAsync() + { + BackgroundWorker worker = new BackgroundWorker(); + worker.DoWork += (_, __) => + { + try + { + if (process.Start()) + { + process.PriorityClass = priority; + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); + _isRunning = true; + process.WaitForExit(); + } + } + catch(Exception e) + { + Debug.WriteLine("ProcessHelper exception: " + e.ToString()); + } + finally + { + _isRunning = false; + } + }; + worker.RunWorkerAsync(); + } + + /// + /// Raised on process completion + /// + public event EventHandler OnExited; + + /// + /// Process output to stdout + /// + public ObservableCollection ProcessOutput { get { return output; } } + + /// + /// Return current state of process + /// + bool _isRunning = false; + public bool IsRunning { get { return _isRunning; } } + + /// + /// Return status of the current process + /// + public bool IsCompleted { get { return IsRunning?process.HasExited:false; } } + + /// + /// Return process exit code + /// + public int ExitCode { get { return IsCompleted ? process.ExitCode : 0; } } + + /// + /// Associated process priority class + /// + public ProcessPriorityClass PriorityClass + { + get { return process.PriorityClass; } + set { process.PriorityClass = value; } + } + } +} diff --git a/trunk/TinyOPDS/Program.cs b/TinyOPDS/Program.cs similarity index 97% rename from trunk/TinyOPDS/Program.cs rename to TinyOPDS/Program.cs index df9278f..0f7eda0 100644 --- a/trunk/TinyOPDS/Program.cs +++ b/TinyOPDS/Program.cs @@ -1,110 +1,110 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * TinyOPDS application entry point - * - ************************************************************/ - -using System; -using System.IO; -using System.IO.Compression; -using System.Collections.Generic; -using System.Linq; -using System.Windows.Forms; -using System.Reflection; -using System.Threading; -using System.Diagnostics; - -namespace TinyOPDS -{ - static class Program - { - static Mutex mutex = new Mutex(false, "tiny_opds_mutex"); - - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - - // Check for single instance - if (Utils.IsLinux) - { - if (IsApplicationRunningOnMono("TinyOPDS.exe")) return; - } - else - { - if (!mutex.WaitOne(TimeSpan.FromSeconds(1), false)) return; - } - - try - { - using (MainForm mainForm = new MainForm()) - { - mainForm.WindowState = (TinyOPDS.Properties.Settings.Default.StartMinimized) ? FormWindowState.Minimized : FormWindowState.Normal; - mainForm.ShowInTaskbar = (TinyOPDS.Properties.Settings.Default.StartMinimized && TinyOPDS.Properties.Settings.Default.CloseToTray) ? false : true; - Application.Run(mainForm); - } - } - finally - { - if (!Utils.IsLinux) mutex.ReleaseMutex(); - } - } - - static bool IsApplicationRunningOnMono(string processName) - { - var processFound = 0; - - Process[] monoProcesses; - ProcessModuleCollection processModuleCollection; - - // find all processes called 'mono', that's necessary because our app runs under the mono process! - monoProcesses = Process.GetProcessesByName("mono"); - - for (var i = 0; i <= monoProcesses.GetUpperBound(0); ++i) - { - processModuleCollection = monoProcesses[i].Modules; - - for (var j = 0; j < processModuleCollection.Count; ++j) - { - if (processModuleCollection[j].FileName.EndsWith(processName)) - { - processFound++; - } - } - } - - //we don't find the current process, but if there is already another one running, return true! - return (processFound == 1); - } - - static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - Assembly asm = Assembly.GetExecutingAssembly(); - String resourceName = asm.GetName().Name + ".Libs." + new AssemblyName(args.Name).Name + ".dll.gz"; - using (var stream = asm.GetManifestResourceStream(resourceName)) - { - if (stream != null) - { - using (MemoryStream memStream = new MemoryStream()) - { - GZipStream decompress = new GZipStream(stream, CompressionMode.Decompress); - decompress.CopyTo(memStream); - return Assembly.Load(memStream.GetBuffer()); - } - } - else return null; - } - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * TinyOPDS application entry point + * + ************************************************************/ + +using System; +using System.IO; +using System.IO.Compression; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using System.Reflection; +using System.Threading; +using System.Diagnostics; + +namespace TinyOPDS +{ + static class Program + { + static Mutex mutex = new Mutex(false, "tiny_opds_mutex"); + + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + // Check for single instance + if (Utils.IsLinux) + { + if (IsApplicationRunningOnMono("TinyOPDS.exe")) return; + } + else + { + if (!mutex.WaitOne(TimeSpan.FromSeconds(1), false)) return; + } + + try + { + using (MainForm mainForm = new MainForm()) + { + mainForm.WindowState = (TinyOPDS.Properties.Settings.Default.StartMinimized) ? FormWindowState.Minimized : FormWindowState.Normal; + mainForm.ShowInTaskbar = (TinyOPDS.Properties.Settings.Default.StartMinimized && TinyOPDS.Properties.Settings.Default.CloseToTray) ? false : true; + Application.Run(mainForm); + } + } + finally + { + if (!Utils.IsLinux) mutex.ReleaseMutex(); + } + } + + static bool IsApplicationRunningOnMono(string processName) + { + var processFound = 0; + + Process[] monoProcesses; + ProcessModuleCollection processModuleCollection; + + // find all processes called 'mono', that's necessary because our app runs under the mono process! + monoProcesses = Process.GetProcessesByName("mono"); + + for (var i = 0; i <= monoProcesses.GetUpperBound(0); ++i) + { + processModuleCollection = monoProcesses[i].Modules; + + for (var j = 0; j < processModuleCollection.Count; ++j) + { + if (processModuleCollection[j].FileName.EndsWith(processName)) + { + processFound++; + } + } + } + + //we don't find the current process, but if there is already another one running, return true! + return (processFound == 1); + } + + static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + Assembly asm = Assembly.GetExecutingAssembly(); + String resourceName = asm.GetName().Name + ".Libs." + new AssemblyName(args.Name).Name + ".dll.gz"; + using (var stream = asm.GetManifestResourceStream(resourceName)) + { + if (stream != null) + { + using (MemoryStream memStream = new MemoryStream()) + { + GZipStream decompress = new GZipStream(stream, CompressionMode.Decompress); + decompress.CopyTo(memStream); + return Assembly.Load(memStream.GetBuffer()); + } + } + else return null; + } + } + } +} diff --git a/trunk/TinyOPDS/Properties/AssemblyInfo.cs b/TinyOPDS/Properties/AssemblyInfo.cs similarity index 97% rename from trunk/TinyOPDS/Properties/AssemblyInfo.cs rename to TinyOPDS/Properties/AssemblyInfo.cs index 95e3759..776796d 100644 --- a/trunk/TinyOPDS/Properties/AssemblyInfo.cs +++ b/TinyOPDS/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TinyOPDS server")] -[assembly: AssemblyDescription("Standalone and small OPDS server for Windows")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("SeNSSoFT")] -[assembly: AssemblyProduct("TinyOPDS server")] -[assembly: AssemblyCopyright("Copyright © 2013, SeNSSoFT Ltd.")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("627a3331-e8e6-4085-8a68-64322d5d4c3e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TinyOPDS server")] +[assembly: AssemblyDescription("Standalone and small OPDS server for Windows")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("SeNSSoFT")] +[assembly: AssemblyProduct("TinyOPDS server")] +[assembly: AssemblyCopyright("Copyright © 2013, SeNSSoFT Ltd.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("627a3331-e8e6-4085-8a68-64322d5d4c3e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/trunk/TinyOPDS/Properties/Resources.Designer.cs b/TinyOPDS/Properties/Resources.Designer.cs similarity index 97% rename from trunk/TinyOPDS/Properties/Resources.Designer.cs rename to TinyOPDS/Properties/Resources.Designer.cs index 27ac690..d9a22bf 100644 --- a/trunk/TinyOPDS/Properties/Resources.Designer.cs +++ b/TinyOPDS/Properties/Resources.Designer.cs @@ -1,91 +1,91 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.586 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace TinyOPDS.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TinyOPDS.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - internal static System.Drawing.Bitmap donate { - get { - object obj = ResourceManager.GetObject("donate", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - internal static System.Drawing.Bitmap folder { - get { - object obj = ResourceManager.GetObject("folder", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - internal static System.Drawing.Bitmap TinyOPDS { - get { - object obj = ResourceManager.GetObject("TinyOPDS", resourceCulture); - return ((System.Drawing.Bitmap)(obj)); - } - } - - internal static System.Drawing.Icon trayIcon { - get { - object obj = ResourceManager.GetObject("trayIcon", resourceCulture); - return ((System.Drawing.Icon)(obj)); - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.586 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TinyOPDS.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TinyOPDS.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Bitmap donate { + get { + object obj = ResourceManager.GetObject("donate", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap folder { + get { + object obj = ResourceManager.GetObject("folder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Bitmap TinyOPDS { + get { + object obj = ResourceManager.GetObject("TinyOPDS", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + internal static System.Drawing.Icon trayIcon { + get { + object obj = ResourceManager.GetObject("trayIcon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/trunk/TinyOPDS/Properties/Resources.resx b/TinyOPDS/Properties/Resources.resx similarity index 98% rename from trunk/TinyOPDS/Properties/Resources.resx rename to TinyOPDS/Properties/Resources.resx index 89d56a5..2f7fb2c 100644 --- a/trunk/TinyOPDS/Properties/Resources.resx +++ b/TinyOPDS/Properties/Resources.resx @@ -1,133 +1,133 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\TinyOPDS.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\Icons\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\donate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - - ..\TinyOPDS.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\TinyOPDS.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Icons\folder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\donate.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\TinyOPDS.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/trunk/TinyOPDS/Properties/Settings.Designer.cs b/TinyOPDS/Properties/Settings.Designer.cs similarity index 97% rename from trunk/TinyOPDS/Properties/Settings.Designer.cs rename to TinyOPDS/Properties/Settings.Designer.cs index 1f13f3d..0d6e53e 100644 --- a/trunk/TinyOPDS/Properties/Settings.Designer.cs +++ b/TinyOPDS/Properties/Settings.Designer.cs @@ -1,399 +1,399 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34003 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace TinyOPDS.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - [System.Configuration.SettingsProvider(typeof(TinyOPDS.CustomSettingsProvider))] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string LibraryPath { - get { - return ((string)(this["LibraryPath"])); - } - set { - this["LibraryPath"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Моя домашняя библиотека")] - public string ServerName { - get { - return ((string)(this["ServerName"])); - } - set { - this["ServerName"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("8080")] - public string ServerPort { - get { - return ((string)(this["ServerPort"])); - } - set { - this["ServerPort"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool StartWithWindows { - get { - return ((bool)(this["StartWithWindows"])); - } - set { - this["StartWithWindows"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool StartMinimized { - get { - return ((bool)(this["StartMinimized"])); - } - set { - this["StartMinimized"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool CloseToTray { - get { - return ((bool)(this["CloseToTray"])); - } - set { - this["CloseToTray"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string ConvertorPath { - get { - return ((string)(this["ConvertorPath"])); - } - set { - this["ConvertorPath"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("en")] - public string Language { - get { - return ((string)(this["Language"])); - } - set { - this["Language"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool OpenNATPort { - get { - return ((bool)(this["OpenNATPort"])); - } - set { - this["OpenNATPort"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string ServiceFilesPath { - get { - return ((string)(this["ServiceFilesPath"])); - } - set { - this["ServiceFilesPath"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool SaveLogToDisk { - get { - return ((bool)(this["SaveLogToDisk"])); - } - set { - this["SaveLogToDisk"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool WatchLibrary { - get { - return ((bool)(this["WatchLibrary"])); - } - set { - this["WatchLibrary"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool UseUPnP { - get { - return ((bool)(this["UseUPnP"])); - } - set { - this["UseUPnP"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseHTTPAuth { - get { - return ((bool)(this["UseHTTPAuth"])); - } - set { - this["UseHTTPAuth"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string Credentials { - get { - return ((string)(this["Credentials"])); - } - set { - this["Credentials"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool BanClients { - get { - return ((bool)(this["BanClients"])); - } - set { - this["BanClients"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LogLevel { - get { - return ((int)(this["LogLevel"])); - } - set { - this["LogLevel"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool RememberClients { - get { - return ((bool)(this["RememberClients"])); - } - set { - this["RememberClients"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public int UpdatesCheck { - get { - return ((int)(this["UpdatesCheck"])); - } - set { - this["UpdatesCheck"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("2013-01-01")] - public global::System.DateTime LastCheck { - get { - return ((global::System.DateTime)(this["LastCheck"])); - } - set { - this["LastCheck"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("5")] - public decimal WrongAttemptsCount { - get { - return ((decimal)(this["WrongAttemptsCount"])); - } - set { - this["WrongAttemptsCount"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LocalInterfaceIndex { - get { - return ((int)(this["LocalInterfaceIndex"])); - } - set { - this["LocalInterfaceIndex"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("lib")] - public string HttpPrefix { - get { - return ((string)(this["HttpPrefix"])); - } - set { - this["HttpPrefix"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseAbsoluteUri { - get { - return ((bool)(this["UseAbsoluteUri"])); - } - set { - this["UseAbsoluteUri"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool LowMemoryProfile { - get { - return ((bool)(this["LowMemoryProfile"])); - } - set { - this["LowMemoryProfile"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("opds")] - public string RootPrefix { - get { - return ((string)(this["RootPrefix"])); - } - set { - this["RootPrefix"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("50")] - public decimal ItemsPerOPDSPage { - get { - return ((decimal)(this["ItemsPerOPDSPage"])); - } - set { - this["ItemsPerOPDSPage"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1000")] - public decimal ItemsPerWebPage { - get { - return ((decimal)(this["ItemsPerWebPage"])); - } - set { - this["ItemsPerWebPage"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public int SortOrder { - get { - return ((int)(this["SortOrder"])); - } - set { - this["SortOrder"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("2")] - public int NewBooksPeriod { - get { - return ((int)(this["NewBooksPeriod"])); - } - set { - this["NewBooksPeriod"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool UseAuthorsAliases { - get { - return ((bool)(this["UseAuthorsAliases"])); - } - set { - this["UseAuthorsAliases"] = value; - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34003 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TinyOPDS.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + [System.Configuration.SettingsProvider(typeof(TinyOPDS.CustomSettingsProvider))] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string LibraryPath { + get { + return ((string)(this["LibraryPath"])); + } + set { + this["LibraryPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Моя домашняя библиотека")] + public string ServerName { + get { + return ((string)(this["ServerName"])); + } + set { + this["ServerName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("8080")] + public string ServerPort { + get { + return ((string)(this["ServerPort"])); + } + set { + this["ServerPort"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool StartWithWindows { + get { + return ((bool)(this["StartWithWindows"])); + } + set { + this["StartWithWindows"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool StartMinimized { + get { + return ((bool)(this["StartMinimized"])); + } + set { + this["StartMinimized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool CloseToTray { + get { + return ((bool)(this["CloseToTray"])); + } + set { + this["CloseToTray"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ConvertorPath { + get { + return ((string)(this["ConvertorPath"])); + } + set { + this["ConvertorPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("en")] + public string Language { + get { + return ((string)(this["Language"])); + } + set { + this["Language"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OpenNATPort { + get { + return ((bool)(this["OpenNATPort"])); + } + set { + this["OpenNATPort"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ServiceFilesPath { + get { + return ((string)(this["ServiceFilesPath"])); + } + set { + this["ServiceFilesPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool SaveLogToDisk { + get { + return ((bool)(this["SaveLogToDisk"])); + } + set { + this["SaveLogToDisk"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool WatchLibrary { + get { + return ((bool)(this["WatchLibrary"])); + } + set { + this["WatchLibrary"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool UseUPnP { + get { + return ((bool)(this["UseUPnP"])); + } + set { + this["UseUPnP"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseHTTPAuth { + get { + return ((bool)(this["UseHTTPAuth"])); + } + set { + this["UseHTTPAuth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Credentials { + get { + return ((string)(this["Credentials"])); + } + set { + this["Credentials"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool BanClients { + get { + return ((bool)(this["BanClients"])); + } + set { + this["BanClients"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int LogLevel { + get { + return ((int)(this["LogLevel"])); + } + set { + this["LogLevel"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool RememberClients { + get { + return ((bool)(this["RememberClients"])); + } + set { + this["RememberClients"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int UpdatesCheck { + get { + return ((int)(this["UpdatesCheck"])); + } + set { + this["UpdatesCheck"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2013-01-01")] + public global::System.DateTime LastCheck { + get { + return ((global::System.DateTime)(this["LastCheck"])); + } + set { + this["LastCheck"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public decimal WrongAttemptsCount { + get { + return ((decimal)(this["WrongAttemptsCount"])); + } + set { + this["WrongAttemptsCount"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int LocalInterfaceIndex { + get { + return ((int)(this["LocalInterfaceIndex"])); + } + set { + this["LocalInterfaceIndex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("lib")] + public string HttpPrefix { + get { + return ((string)(this["HttpPrefix"])); + } + set { + this["HttpPrefix"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseAbsoluteUri { + get { + return ((bool)(this["UseAbsoluteUri"])); + } + set { + this["UseAbsoluteUri"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool LowMemoryProfile { + get { + return ((bool)(this["LowMemoryProfile"])); + } + set { + this["LowMemoryProfile"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("opds")] + public string RootPrefix { + get { + return ((string)(this["RootPrefix"])); + } + set { + this["RootPrefix"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("50")] + public decimal ItemsPerOPDSPage { + get { + return ((decimal)(this["ItemsPerOPDSPage"])); + } + set { + this["ItemsPerOPDSPage"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1000")] + public decimal ItemsPerWebPage { + get { + return ((decimal)(this["ItemsPerWebPage"])); + } + set { + this["ItemsPerWebPage"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int SortOrder { + get { + return ((int)(this["SortOrder"])); + } + set { + this["SortOrder"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2")] + public int NewBooksPeriod { + get { + return ((int)(this["NewBooksPeriod"])); + } + set { + this["NewBooksPeriod"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool UseAuthorsAliases { + get { + return ((bool)(this["UseAuthorsAliases"])); + } + set { + this["UseAuthorsAliases"] = value; + } + } + } +} diff --git a/trunk/TinyOPDS/Properties/Settings.settings b/TinyOPDS/Properties/Settings.settings similarity index 97% rename from trunk/TinyOPDS/Properties/Settings.settings rename to TinyOPDS/Properties/Settings.settings index 6fa7082..963f3af 100644 --- a/trunk/TinyOPDS/Properties/Settings.settings +++ b/TinyOPDS/Properties/Settings.settings @@ -1,99 +1,99 @@ - - - - - - - - - Моя домашняя библиотека - - - 8080 - - - False - - - False - - - False - - - - - - en - - - False - - - - - - False - - - False - - - True - - - False - - - - - - False - - - 0 - - - False - - - 1 - - - 2013-01-01 - - - 5 - - - 0 - - - lib - - - False - - - False - - - opds - - - 50 - - - 1000 - - - 1 - - - 2 - - - True - - + + + + + + + + + Моя домашняя библиотека + + + 8080 + + + False + + + False + + + False + + + + + + en + + + False + + + + + + False + + + False + + + True + + + False + + + + + + False + + + 0 + + + False + + + 1 + + + 2013-01-01 + + + 5 + + + 0 + + + lib + + + False + + + False + + + opds + + + 50 + + + 1000 + + + 1 + + + 2 + + + True + + \ No newline at end of file diff --git a/trunk/TinyOPDSConsole/Properties/app.manifest b/TinyOPDS/Properties/app.manifest similarity index 98% rename from trunk/TinyOPDSConsole/Properties/app.manifest rename to TinyOPDS/Properties/app.manifest index 8df6208..2856403 100644 --- a/trunk/TinyOPDSConsole/Properties/app.manifest +++ b/TinyOPDS/Properties/app.manifest @@ -1,47 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/TinyOPDS/Resources/fb2.dtd b/TinyOPDS/Resources/fb2.dtd similarity index 100% rename from trunk/TinyOPDS/Resources/fb2.dtd rename to TinyOPDS/Resources/fb2.dtd diff --git a/trunk/TinyOPDS/Resources/trayIcon.ico b/TinyOPDS/Resources/trayIcon.ico similarity index 100% rename from trunk/TinyOPDS/Resources/trayIcon.ico rename to TinyOPDS/Resources/trayIcon.ico diff --git a/trunk/TinyOPDS/Scanner/FileScanner.cs b/TinyOPDS/Scanner/FileScanner.cs similarity index 97% rename from trunk/TinyOPDS/Scanner/FileScanner.cs rename to TinyOPDS/Scanner/FileScanner.cs index 9e7a74e..3ed1efa 100644 --- a/trunk/TinyOPDS/Scanner/FileScanner.cs +++ b/TinyOPDS/Scanner/FileScanner.cs @@ -1,168 +1,168 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the FileScanner class - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using System.Text; -using System.ComponentModel; -#if !CONSOLE -using System.Windows.Forms; -#endif -using System.Threading; - -using TinyOPDS.Data; -using TinyOPDS.Parsers; - -namespace TinyOPDS.Scanner -{ - public enum FileScannerStatus - { - SCANNING, - STOPPED - } - - public class FileScanner - { - public int SkippedFiles { get; set; } - public FileScannerStatus Status { get; set; } - - public event BookFoundEventHandler OnBookFound; - private IEnumerable BookFoundEventHandlers() { return from d in OnBookFound.GetInvocationList() select (BookFoundEventHandler)d; } - - public event InvalidBookEventHandler OnInvalidBook; - private IEnumerable InvalidBookEventHandlers() { return from d in OnInvalidBook.GetInvocationList() select (InvalidBookEventHandler)d; } - - public event FileSkippedEventHandler OnFileSkipped; - private IEnumerable FileSkippedEventHandlers() { return from d in OnFileSkipped.GetInvocationList() select (FileSkippedEventHandler)d; } - - public event ScanCompletedEventHandler OnScanCompleted; - private IEnumerable ScanCompletedEventHandlers() { return from d in OnScanCompleted.GetInvocationList() select (ScanCompletedEventHandler)d; } - - private ZipScanner _zipScanner = null; - private bool _isRecursive; - - public FileScanner(bool IsRecursive = true) - { - Status = FileScannerStatus.STOPPED; - _isRecursive = IsRecursive; - } - - public void Stop() - { - Status = FileScannerStatus.STOPPED; - if (_zipScanner != null) _zipScanner.Status = FileScannerStatus.STOPPED; - - if (OnBookFound != null) OnBookFound -= BookFoundEventHandlers().Last(); - if (OnInvalidBook != null) OnInvalidBook -= InvalidBookEventHandlers().Last(); - if (OnFileSkipped != null) OnFileSkipped -= FileSkippedEventHandlers().Last(); - if (OnScanCompleted != null) OnScanCompleted -= ScanCompletedEventHandlers().Last(); - } - - /// - /// - /// - /// - public void Start(string Path) - { - SkippedFiles = 0; - - BackgroundWorker scanner = new BackgroundWorker(); - scanner.DoWork += (__, ___) => - { - ScanDirectory(new DirectoryInfo(Path)); - Status = FileScannerStatus.STOPPED; - if (OnScanCompleted != null) OnScanCompleted(this, new EventArgs()); - }; - Status = FileScannerStatus.SCANNING; - scanner.RunWorkerAsync(); - } - - /// - /// - /// - /// - /// - private void ScanDirectory(DirectoryInfo directory) - { - foreach (FileInfo file in directory.GetFiles()) - { - if (!Utils.IsLinux && Status == FileScannerStatus.STOPPED) break; - ScanFile(file.FullName); - } - - // Recursively scan all subdirectories - DirectoryInfo[] subDirectories = directory.GetDirectories(); - if (_isRecursive) - foreach (DirectoryInfo subDirectory in subDirectories) - if (Status == FileScannerStatus.SCANNING) - ScanDirectory(subDirectory); - } - - /// - /// - /// - /// - public void ScanFile(string fullName) - { - Book book = null; - string ext = Path.GetExtension(fullName).ToLower(); - - // Process accepted files - try - { - if (Library.Contains(fullName.Substring(Library.LibraryPath.Length + 1))) - { - SkippedFiles++; - if (OnFileSkipped != null) OnFileSkipped(this, new FileSkippedEventArgs(SkippedFiles)); - } - else if (ext.Contains(".epub")) - { - book = new ePubParser().Parse(fullName); - } - else if (ext.Contains(".fb2")) - { - book = new FB2Parser().Parse(fullName); - } - else if (ext.Contains(".zip")) - { - _zipScanner = new ZipScanner(fullName); - _zipScanner.OnBookFound += (object sender, BookFoundEventArgs e) => { if (OnBookFound != null) OnBookFound(sender, e); }; - _zipScanner.OnInvalidBook += (object sender, InvalidBookEventArgs e) => { if (OnInvalidBook != null) OnInvalidBook(sender, e); }; - _zipScanner.OnFileSkipped += (object sender, FileSkippedEventArgs e) => - { - SkippedFiles++; - if (OnFileSkipped != null) OnFileSkipped(sender, new FileSkippedEventArgs(SkippedFiles)); - }; - _zipScanner.Scan(); - } - - // Inform caller - if (book != null) - { - if (book.IsValid && OnBookFound != null) OnBookFound(this, new BookFoundEventArgs(book)); - else if (!book.IsValid && OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(fullName)); - } - - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".ScanFile: exception {0} on file: {1}", e.Message, fullName); - if (OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(fullName)); - } - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the FileScanner class + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using System.Text; +using System.ComponentModel; +#if !CONSOLE +using System.Windows.Forms; +#endif +using System.Threading; + +using TinyOPDS.Data; +using TinyOPDS.Parsers; + +namespace TinyOPDS.Scanner +{ + public enum FileScannerStatus + { + SCANNING, + STOPPED + } + + public class FileScanner + { + public int SkippedFiles { get; set; } + public FileScannerStatus Status { get; set; } + + public event BookFoundEventHandler OnBookFound; + private IEnumerable BookFoundEventHandlers() { return from d in OnBookFound.GetInvocationList() select (BookFoundEventHandler)d; } + + public event InvalidBookEventHandler OnInvalidBook; + private IEnumerable InvalidBookEventHandlers() { return from d in OnInvalidBook.GetInvocationList() select (InvalidBookEventHandler)d; } + + public event FileSkippedEventHandler OnFileSkipped; + private IEnumerable FileSkippedEventHandlers() { return from d in OnFileSkipped.GetInvocationList() select (FileSkippedEventHandler)d; } + + public event ScanCompletedEventHandler OnScanCompleted; + private IEnumerable ScanCompletedEventHandlers() { return from d in OnScanCompleted.GetInvocationList() select (ScanCompletedEventHandler)d; } + + private ZipScanner _zipScanner = null; + private bool _isRecursive; + + public FileScanner(bool IsRecursive = true) + { + Status = FileScannerStatus.STOPPED; + _isRecursive = IsRecursive; + } + + public void Stop() + { + Status = FileScannerStatus.STOPPED; + if (_zipScanner != null) _zipScanner.Status = FileScannerStatus.STOPPED; + + if (OnBookFound != null) OnBookFound -= BookFoundEventHandlers().Last(); + if (OnInvalidBook != null) OnInvalidBook -= InvalidBookEventHandlers().Last(); + if (OnFileSkipped != null) OnFileSkipped -= FileSkippedEventHandlers().Last(); + if (OnScanCompleted != null) OnScanCompleted -= ScanCompletedEventHandlers().Last(); + } + + /// + /// + /// + /// + public void Start(string Path) + { + SkippedFiles = 0; + + BackgroundWorker scanner = new BackgroundWorker(); + scanner.DoWork += (__, ___) => + { + ScanDirectory(new DirectoryInfo(Path)); + Status = FileScannerStatus.STOPPED; + if (OnScanCompleted != null) OnScanCompleted(this, new EventArgs()); + }; + Status = FileScannerStatus.SCANNING; + scanner.RunWorkerAsync(); + } + + /// + /// + /// + /// + /// + private void ScanDirectory(DirectoryInfo directory) + { + foreach (FileInfo file in directory.GetFiles()) + { + if (!Utils.IsLinux && Status == FileScannerStatus.STOPPED) break; + ScanFile(file.FullName); + } + + // Recursively scan all subdirectories + DirectoryInfo[] subDirectories = directory.GetDirectories(); + if (_isRecursive) + foreach (DirectoryInfo subDirectory in subDirectories) + if (Status == FileScannerStatus.SCANNING) + ScanDirectory(subDirectory); + } + + /// + /// + /// + /// + public void ScanFile(string fullName) + { + Book book = null; + string ext = Path.GetExtension(fullName).ToLower(); + + // Process accepted files + try + { + if (Library.Contains(fullName.Substring(Library.LibraryPath.Length + 1))) + { + SkippedFiles++; + if (OnFileSkipped != null) OnFileSkipped(this, new FileSkippedEventArgs(SkippedFiles)); + } + else if (ext.Contains(".epub")) + { + book = new ePubParser().Parse(fullName); + } + else if (ext.Contains(".fb2")) + { + book = new FB2Parser().Parse(fullName); + } + else if (ext.Contains(".zip")) + { + _zipScanner = new ZipScanner(fullName); + _zipScanner.OnBookFound += (object sender, BookFoundEventArgs e) => { if (OnBookFound != null) OnBookFound(sender, e); }; + _zipScanner.OnInvalidBook += (object sender, InvalidBookEventArgs e) => { if (OnInvalidBook != null) OnInvalidBook(sender, e); }; + _zipScanner.OnFileSkipped += (object sender, FileSkippedEventArgs e) => + { + SkippedFiles++; + if (OnFileSkipped != null) OnFileSkipped(sender, new FileSkippedEventArgs(SkippedFiles)); + }; + _zipScanner.Scan(); + } + + // Inform caller + if (book != null) + { + if (book.IsValid && OnBookFound != null) OnBookFound(this, new BookFoundEventArgs(book)); + else if (!book.IsValid && OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(fullName)); + } + + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".ScanFile: exception {0} on file: {1}", e.Message, fullName); + if (OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(fullName)); + } + } + } +} diff --git a/trunk/TinyOPDS/Scanner/ScannerEvents.cs b/TinyOPDS/Scanner/ScannerEvents.cs similarity index 97% rename from trunk/TinyOPDS/Scanner/ScannerEvents.cs rename to TinyOPDS/Scanner/ScannerEvents.cs index 57a4c79..4e54c5e 100644 --- a/trunk/TinyOPDS/Scanner/ScannerEvents.cs +++ b/TinyOPDS/Scanner/ScannerEvents.cs @@ -1,62 +1,62 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * Events and handlers classes for scanners - * - ************************************************************/ - -using System; -using TinyOPDS.Data; - -namespace TinyOPDS.Scanner -{ - /// - /// Scanner delegated events and arguments declarations - /// - - public class BookFoundEventArgs : EventArgs - { - public Book Book; - public BookFoundEventArgs(Book book) { Book = book; } - } - - public class InvalidBookEventArgs : EventArgs - { - public string BookName; - public InvalidBookEventArgs(string bookName) { BookName = bookName; } - } - - public class FileSkippedEventArgs : EventArgs - { - public int Count; - public FileSkippedEventArgs(int count) { Count = count; } - } - - public class BookAddedEventArgs : EventArgs - { - public string BookPath; - public BookType BookType; - public BookAddedEventArgs(string bookPath) - { - BookPath = bookPath; - BookType = BookPath.ToLower().Contains(".epub") ? BookType.EPUB : BookType.FB2; - } - } - - public class BookDeletedEventArgs : BookAddedEventArgs - { - public BookDeletedEventArgs(string bookPath) : base(bookPath) {} - } - - public delegate void BookFoundEventHandler(object sender, BookFoundEventArgs e); - public delegate void InvalidBookEventHandler(object sender, InvalidBookEventArgs e); - public delegate void FileSkippedEventHandler(object sender, FileSkippedEventArgs e); - public delegate void ScanCompletedEventHandler(object sender, EventArgs e); - public delegate void BookAddedEventHandler(object sender, BookAddedEventArgs e); - public delegate void BookDeletedEventHandler(object sender, BookDeletedEventArgs e); -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * Events and handlers classes for scanners + * + ************************************************************/ + +using System; +using TinyOPDS.Data; + +namespace TinyOPDS.Scanner +{ + /// + /// Scanner delegated events and arguments declarations + /// + + public class BookFoundEventArgs : EventArgs + { + public Book Book; + public BookFoundEventArgs(Book book) { Book = book; } + } + + public class InvalidBookEventArgs : EventArgs + { + public string BookName; + public InvalidBookEventArgs(string bookName) { BookName = bookName; } + } + + public class FileSkippedEventArgs : EventArgs + { + public int Count; + public FileSkippedEventArgs(int count) { Count = count; } + } + + public class BookAddedEventArgs : EventArgs + { + public string BookPath; + public BookType BookType; + public BookAddedEventArgs(string bookPath) + { + BookPath = bookPath; + BookType = BookPath.ToLower().Contains(".epub") ? BookType.EPUB : BookType.FB2; + } + } + + public class BookDeletedEventArgs : BookAddedEventArgs + { + public BookDeletedEventArgs(string bookPath) : base(bookPath) {} + } + + public delegate void BookFoundEventHandler(object sender, BookFoundEventArgs e); + public delegate void InvalidBookEventHandler(object sender, InvalidBookEventArgs e); + public delegate void FileSkippedEventHandler(object sender, FileSkippedEventArgs e); + public delegate void ScanCompletedEventHandler(object sender, EventArgs e); + public delegate void BookAddedEventHandler(object sender, BookAddedEventArgs e); + public delegate void BookDeletedEventHandler(object sender, BookDeletedEventArgs e); +} diff --git a/trunk/TinyOPDS/Scanner/Watcher.cs b/TinyOPDS/Scanner/Watcher.cs similarity index 97% rename from trunk/TinyOPDS/Scanner/Watcher.cs rename to TinyOPDS/Scanner/Watcher.cs index 6d9f07a..b290cbd 100644 --- a/trunk/TinyOPDS/Scanner/Watcher.cs +++ b/TinyOPDS/Scanner/Watcher.cs @@ -1,247 +1,247 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This is a file watcher class - * - * TODO: should disable UI "scan" button during Watcher's - * operations - * - ************************************************************/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; -using System.Threading; -using System.ComponentModel; -using System.Security.Permissions; - -using TinyOPDS.Data; - -namespace TinyOPDS.Scanner -{ - public class Watcher : IDisposable - { - private FileSystemWatcher _fileWatcher; - private bool _disposed = false; - private string[] _extensions = { ".zip", ".fb2", ".epub" }; - - private List _addedBooks = new List(); - private List _deletedBooks = new List(); - private BackgroundWorker _booksManager; - private FileScanner _scanner; - - public event BookAddedEventHandler OnBookAdded; - public event BookDeletedEventHandler OnBookDeleted; - public event InvalidBookEventHandler OnInvalidBook; - public event FileSkippedEventHandler OnFileSkipped; - - [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] - public Watcher(string path = "") - { - DirectoryToWatch = path; - _booksManager = new BackgroundWorker(); - _booksManager.DoWork += _booksManager_DoWork; - _scanner = new FileScanner(false); - _scanner.OnBookFound += (object s, BookFoundEventArgs be) => - { - if (Library.Add(be.Book)) - { - //Library.Append(be.Book); - if (OnBookAdded != null) OnBookAdded(this, new BookAddedEventArgs(be.Book.FileName)); - } - }; - _scanner.OnInvalidBook += (object _sender, InvalidBookEventArgs _e) => { if (OnInvalidBook != null) OnInvalidBook(_sender, _e); }; - _scanner.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => { if (OnFileSkipped != null) OnFileSkipped(_sender, _e); }; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - if (!this._disposed) - { - if (disposing) - { - if (_booksManager != null) - { - _isEnabled = false; - _booksManager.Dispose(); - } - if (_fileWatcher != null) _fileWatcher.Dispose(); - } - _disposed = true; - } - } - - public string DirectoryToWatch - { - [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)] - get - { - return (_fileWatcher == null) ? string.Empty : _fileWatcher.Path; - } - - [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] - set - { - if (!string.IsNullOrEmpty(value) && Directory.Exists(value)) - { - if (_fileWatcher != null) - { - _fileWatcher.Created -= _fileWatcher_Created; - _fileWatcher.Deleted -= _fileWatcher_Deleted; - _fileWatcher.Renamed -= _fileWatcher_Renamed; - _fileWatcher.Dispose(); - } - _fileWatcher = new FileSystemWatcher(value, "*"); - _fileWatcher.InternalBufferSize = 1024 * 64; - _fileWatcher.Created += new FileSystemEventHandler(_fileWatcher_Created); - _fileWatcher.Deleted += new FileSystemEventHandler(_fileWatcher_Deleted); - _fileWatcher.Renamed += new RenamedEventHandler(_fileWatcher_Renamed); - _fileWatcher.IncludeSubdirectories = true; - _fileWatcher.NotifyFilter = NotifyFilters.FileName; - _fileWatcher.EnableRaisingEvents = _isEnabled; - } - } - } - - private bool _isEnabled = false; - public bool IsEnabled - { - get { return _isEnabled; } - set - { - if (_fileWatcher != null) - { - _fileWatcher.EnableRaisingEvents = _isEnabled = value; - if (_isEnabled) - { - if (!_booksManager.IsBusy) _booksManager.RunWorkerAsync(); - } - else - { - _addedBooks.Clear(); - _deletedBooks.Clear(); - } - } - } - } - - /// - /// Book manager thread - /// - /// - /// - void _booksManager_DoWork(object sender, DoWorkEventArgs e) - { - string fileName = string.Empty; - while (_isEnabled && !_disposed) - { - // First, check added books - if (_addedBooks.Count > 0) - { - fileName = _addedBooks.First(); - // If book scheduled for deletion, do not add it - if (_deletedBooks.Contains(fileName)) - { - _deletedBooks.Remove(fileName); - _addedBooks.Remove(fileName); - } - else - { - if (!IsFileInUse(fileName)) - { - _scanner.ScanFile(fileName); - _addedBooks.Remove(fileName); - } - else - { - _addedBooks.Remove(fileName); - _addedBooks.Add(fileName); - } - } - } - // Delete book from library (we don't care about actual file existence) - else if (_deletedBooks.Count > 0) - { - fileName = _deletedBooks.First(); - if (Library.Delete(fileName)) - { - if (OnBookDeleted != null) OnBookDeleted(this, new BookDeletedEventArgs(fileName)); - } - _deletedBooks.Remove(fileName); - } - // Get some rest for UI - else - { - Thread.Sleep(100); - } - } - } - - /// - /// New file (book or zip archive) added to the library - /// - /// - /// - private void _fileWatcher_Created(object sender, FileSystemEventArgs e) - { - if (_extensions.Contains(Path.GetExtension(e.FullPath).ToLower())) - { - lock (_addedBooks) _addedBooks.Add(e.FullPath); - } - } - - /// - /// Library file (book or zip archive) is renamed - /// - /// - /// - private void _fileWatcher_Renamed(object sender, RenamedEventArgs e) - { - if (_extensions.Contains(Path.GetExtension(e.FullPath).ToLower())) - { - lock (_deletedBooks) _deletedBooks.Add(e.FullPath); - } - } - - /// - /// Library file (book or zip archive) deleted from the library - /// - /// - /// - private void _fileWatcher_Deleted(object sender, FileSystemEventArgs e) - { - if (_extensions.Contains(Path.GetExtension(e.FullPath).ToLower())) - { - lock (_deletedBooks) _deletedBooks.Add(e.FullPath); - } - } - - private bool IsFileInUse(string path) - { - if (string.IsNullOrEmpty(path)) return true; - try - { - using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { } - } - catch (IOException) - { - return true; - } - return false; - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This is a file watcher class + * + * TODO: should disable UI "scan" button during Watcher's + * operations + * + ************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Threading; +using System.ComponentModel; +using System.Security.Permissions; + +using TinyOPDS.Data; + +namespace TinyOPDS.Scanner +{ + public class Watcher : IDisposable + { + private FileSystemWatcher _fileWatcher; + private bool _disposed = false; + private string[] _extensions = { ".zip", ".fb2", ".epub" }; + + private List _addedBooks = new List(); + private List _deletedBooks = new List(); + private BackgroundWorker _booksManager; + private FileScanner _scanner; + + public event BookAddedEventHandler OnBookAdded; + public event BookDeletedEventHandler OnBookDeleted; + public event InvalidBookEventHandler OnInvalidBook; + public event FileSkippedEventHandler OnFileSkipped; + + [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] + public Watcher(string path = "") + { + DirectoryToWatch = path; + _booksManager = new BackgroundWorker(); + _booksManager.DoWork += _booksManager_DoWork; + _scanner = new FileScanner(false); + _scanner.OnBookFound += (object s, BookFoundEventArgs be) => + { + if (Library.Add(be.Book)) + { + //Library.Append(be.Book); + if (OnBookAdded != null) OnBookAdded(this, new BookAddedEventArgs(be.Book.FileName)); + } + }; + _scanner.OnInvalidBook += (object _sender, InvalidBookEventArgs _e) => { if (OnInvalidBook != null) OnInvalidBook(_sender, _e); }; + _scanner.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => { if (OnFileSkipped != null) OnFileSkipped(_sender, _e); }; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + if (!this._disposed) + { + if (disposing) + { + if (_booksManager != null) + { + _isEnabled = false; + _booksManager.Dispose(); + } + if (_fileWatcher != null) _fileWatcher.Dispose(); + } + _disposed = true; + } + } + + public string DirectoryToWatch + { + [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted=true)] + get + { + return (_fileWatcher == null) ? string.Empty : _fileWatcher.Path; + } + + [PermissionSetAttribute(SecurityAction.LinkDemand, Unrestricted = true)] + set + { + if (!string.IsNullOrEmpty(value) && Directory.Exists(value)) + { + if (_fileWatcher != null) + { + _fileWatcher.Created -= _fileWatcher_Created; + _fileWatcher.Deleted -= _fileWatcher_Deleted; + _fileWatcher.Renamed -= _fileWatcher_Renamed; + _fileWatcher.Dispose(); + } + _fileWatcher = new FileSystemWatcher(value, "*"); + _fileWatcher.InternalBufferSize = 1024 * 64; + _fileWatcher.Created += new FileSystemEventHandler(_fileWatcher_Created); + _fileWatcher.Deleted += new FileSystemEventHandler(_fileWatcher_Deleted); + _fileWatcher.Renamed += new RenamedEventHandler(_fileWatcher_Renamed); + _fileWatcher.IncludeSubdirectories = true; + _fileWatcher.NotifyFilter = NotifyFilters.FileName; + _fileWatcher.EnableRaisingEvents = _isEnabled; + } + } + } + + private bool _isEnabled = false; + public bool IsEnabled + { + get { return _isEnabled; } + set + { + if (_fileWatcher != null) + { + _fileWatcher.EnableRaisingEvents = _isEnabled = value; + if (_isEnabled) + { + if (!_booksManager.IsBusy) _booksManager.RunWorkerAsync(); + } + else + { + _addedBooks.Clear(); + _deletedBooks.Clear(); + } + } + } + } + + /// + /// Book manager thread + /// + /// + /// + void _booksManager_DoWork(object sender, DoWorkEventArgs e) + { + string fileName = string.Empty; + while (_isEnabled && !_disposed) + { + // First, check added books + if (_addedBooks.Count > 0) + { + fileName = _addedBooks.First(); + // If book scheduled for deletion, do not add it + if (_deletedBooks.Contains(fileName)) + { + _deletedBooks.Remove(fileName); + _addedBooks.Remove(fileName); + } + else + { + if (!IsFileInUse(fileName)) + { + _scanner.ScanFile(fileName); + _addedBooks.Remove(fileName); + } + else + { + _addedBooks.Remove(fileName); + _addedBooks.Add(fileName); + } + } + } + // Delete book from library (we don't care about actual file existence) + else if (_deletedBooks.Count > 0) + { + fileName = _deletedBooks.First(); + if (Library.Delete(fileName)) + { + if (OnBookDeleted != null) OnBookDeleted(this, new BookDeletedEventArgs(fileName)); + } + _deletedBooks.Remove(fileName); + } + // Get some rest for UI + else + { + Thread.Sleep(100); + } + } + } + + /// + /// New file (book or zip archive) added to the library + /// + /// + /// + private void _fileWatcher_Created(object sender, FileSystemEventArgs e) + { + if (_extensions.Contains(Path.GetExtension(e.FullPath).ToLower())) + { + lock (_addedBooks) _addedBooks.Add(e.FullPath); + } + } + + /// + /// Library file (book or zip archive) is renamed + /// + /// + /// + private void _fileWatcher_Renamed(object sender, RenamedEventArgs e) + { + if (_extensions.Contains(Path.GetExtension(e.FullPath).ToLower())) + { + lock (_deletedBooks) _deletedBooks.Add(e.FullPath); + } + } + + /// + /// Library file (book or zip archive) deleted from the library + /// + /// + /// + private void _fileWatcher_Deleted(object sender, FileSystemEventArgs e) + { + if (_extensions.Contains(Path.GetExtension(e.FullPath).ToLower())) + { + lock (_deletedBooks) _deletedBooks.Add(e.FullPath); + } + } + + private bool IsFileInUse(string path) + { + if (string.IsNullOrEmpty(path)) return true; + try + { + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read)) { } + } + catch (IOException) + { + return true; + } + return false; + } + } +} diff --git a/trunk/TinyOPDS/Scanner/ZipScanner.cs b/TinyOPDS/Scanner/ZipScanner.cs similarity index 97% rename from trunk/TinyOPDS/Scanner/ZipScanner.cs rename to TinyOPDS/Scanner/ZipScanner.cs index 5bd11a6..6c52761 100644 --- a/trunk/TinyOPDS/Scanner/ZipScanner.cs +++ b/TinyOPDS/Scanner/ZipScanner.cs @@ -1,139 +1,139 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines the ZipScanner class (FileScanner analog - * for zip archives) - * - ************************************************************/ - -using System; -using System.IO; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Xml; -using System.Xml.Linq; -using System.Text; -using System.ComponentModel; -using System.Threading; - -using Ionic.Zip; -using TinyOPDS.Data; -using TinyOPDS.Parsers; - -namespace TinyOPDS.Scanner -{ - public class ZipScanner - { - public string ZipFileName { get; set; } - public FileScannerStatus Status { get; set; } - public int SkippedFiles { get; set; } - - public event BookFoundEventHandler OnBookFound; - private IEnumerable BookFoundEventHandlers() { return from d in OnBookFound.GetInvocationList() select (BookFoundEventHandler)d; } - - public event InvalidBookEventHandler OnInvalidBook; - private IEnumerable InvalidBookEventHandlers() { return from d in OnInvalidBook.GetInvocationList() select (InvalidBookEventHandler)d; } - - public event FileSkippedEventHandler OnFileSkipped; - private IEnumerable FileSkippedEventHandlers() { return from d in OnFileSkipped.GetInvocationList() select (FileSkippedEventHandler)d; } - - public ZipScanner(string zipFileName) - { - ZipFileName = zipFileName; - Status = FileScannerStatus.STOPPED; - SkippedFiles = 0; - } - - public void Stop() - { - Status = FileScannerStatus.STOPPED; - if (OnBookFound != null) OnBookFound -= BookFoundEventHandlers().Last(); - if (OnFileSkipped != null) OnFileSkipped -= FileSkippedEventHandlers().Last(); - } - - /// - /// Scan zip file - /// - public void Scan() - { - Status = FileScannerStatus.SCANNING; - ZipFile zipFile = null; - string entryFileName = string.Empty; - MemoryStream memStream = null; - - try - { - zipFile = new ZipFile(ZipFileName); - - foreach (ZipEntry entry in zipFile.Entries) - { - if (Status != FileScannerStatus.SCANNING) break; - - if (!string.IsNullOrEmpty(entry.FileName)) - { - entryFileName = entry.FileName; - - // Process accepted files - try - { - Book book = null; - memStream = new MemoryStream(); - - string ext = Path.GetExtension(entry.FileName).ToLower(); - - if (Library.Contains(ZipFileName.Substring(Library.LibraryPath.Length+1) + "@" + entryFileName)) - { - SkippedFiles++; - if (OnFileSkipped != null) OnFileSkipped(this, new FileSkippedEventArgs(SkippedFiles)); - } - else if (ext.Contains(".epub")) - { - entry.Extract(memStream); - book = new ePubParser().Parse(memStream, ZipFileName + "@" + entryFileName); - } - else if (ext.Contains(".fb2")) - { - entry.Extract(memStream); - book = new FB2Parser().Parse(memStream, ZipFileName + "@" + entryFileName); - } - - if (book != null) - { - if (book.IsValid && OnBookFound != null) { OnBookFound(this, new BookFoundEventArgs(book)); } - else if (!book.IsValid && OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(ZipFileName + "@" + entryFileName)); - } - - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".ScanDirectory: exception {0} on file: {1}", e.Message, ZipFileName + "@" + entryFileName); - if (OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(ZipFileName + "@" + entryFileName)); - } - finally - { - if (memStream != null) - { - memStream.Dispose(); - memStream = null; - } - } - } - } - } - finally - { - if (zipFile != null) - { - zipFile.Dispose(); - zipFile = null; - } - } - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines the ZipScanner class (FileScanner analog + * for zip archives) + * + ************************************************************/ + +using System; +using System.IO; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Xml; +using System.Xml.Linq; +using System.Text; +using System.ComponentModel; +using System.Threading; + +using Ionic.Zip; +using TinyOPDS.Data; +using TinyOPDS.Parsers; + +namespace TinyOPDS.Scanner +{ + public class ZipScanner + { + public string ZipFileName { get; set; } + public FileScannerStatus Status { get; set; } + public int SkippedFiles { get; set; } + + public event BookFoundEventHandler OnBookFound; + private IEnumerable BookFoundEventHandlers() { return from d in OnBookFound.GetInvocationList() select (BookFoundEventHandler)d; } + + public event InvalidBookEventHandler OnInvalidBook; + private IEnumerable InvalidBookEventHandlers() { return from d in OnInvalidBook.GetInvocationList() select (InvalidBookEventHandler)d; } + + public event FileSkippedEventHandler OnFileSkipped; + private IEnumerable FileSkippedEventHandlers() { return from d in OnFileSkipped.GetInvocationList() select (FileSkippedEventHandler)d; } + + public ZipScanner(string zipFileName) + { + ZipFileName = zipFileName; + Status = FileScannerStatus.STOPPED; + SkippedFiles = 0; + } + + public void Stop() + { + Status = FileScannerStatus.STOPPED; + if (OnBookFound != null) OnBookFound -= BookFoundEventHandlers().Last(); + if (OnFileSkipped != null) OnFileSkipped -= FileSkippedEventHandlers().Last(); + } + + /// + /// Scan zip file + /// + public void Scan() + { + Status = FileScannerStatus.SCANNING; + ZipFile zipFile = null; + string entryFileName = string.Empty; + MemoryStream memStream = null; + + try + { + zipFile = new ZipFile(ZipFileName); + + foreach (ZipEntry entry in zipFile.Entries) + { + if (Status != FileScannerStatus.SCANNING) break; + + if (!string.IsNullOrEmpty(entry.FileName)) + { + entryFileName = entry.FileName; + + // Process accepted files + try + { + Book book = null; + memStream = new MemoryStream(); + + string ext = Path.GetExtension(entry.FileName).ToLower(); + + if (Library.Contains(ZipFileName.Substring(Library.LibraryPath.Length+1) + "@" + entryFileName)) + { + SkippedFiles++; + if (OnFileSkipped != null) OnFileSkipped(this, new FileSkippedEventArgs(SkippedFiles)); + } + else if (ext.Contains(".epub")) + { + entry.Extract(memStream); + book = new ePubParser().Parse(memStream, ZipFileName + "@" + entryFileName); + } + else if (ext.Contains(".fb2")) + { + entry.Extract(memStream); + book = new FB2Parser().Parse(memStream, ZipFileName + "@" + entryFileName); + } + + if (book != null) + { + if (book.IsValid && OnBookFound != null) { OnBookFound(this, new BookFoundEventArgs(book)); } + else if (!book.IsValid && OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(ZipFileName + "@" + entryFileName)); + } + + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".ScanDirectory: exception {0} on file: {1}", e.Message, ZipFileName + "@" + entryFileName); + if (OnInvalidBook != null) OnInvalidBook(this, new InvalidBookEventArgs(ZipFileName + "@" + entryFileName)); + } + finally + { + if (memStream != null) + { + memStream.Dispose(); + memStream = null; + } + } + } + } + } + finally + { + if (zipFile != null) + { + zipFile.Dispose(); + zipFile = null; + } + } + } + } +} diff --git a/trunk/TinyOPDS/Server/HttpServer.cs b/TinyOPDS/Server/HttpServer.cs similarity index 97% rename from trunk/TinyOPDS/Server/HttpServer.cs rename to TinyOPDS/Server/HttpServer.cs index b284347..dd8abce 100644 --- a/trunk/TinyOPDS/Server/HttpServer.cs +++ b/TinyOPDS/Server/HttpServer.cs @@ -1,570 +1,570 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module contains simple HTTP processor implementation - * and abstract class for HTTP server - * Also, couple additional service classes are specified - * - * - ************************************************************/ - -using System; -using System.Linq; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.ComponentModel; - -namespace TinyOPDS.Server -{ - /// - /// Basic credentials (unencrypted) - /// - public class Credential - { - public string User { get; set; } - public string Password { get; set; } - public Credential(string user, string password) { User = user; Password = password; } - } - - /// - /// Simple HTTP processor - /// - public class HttpProcessor : IDisposable - { - public TcpClient Socket; - public HttpServer Server; - - private Stream _inputStream; - public StreamWriter OutputStream; - - public String HttpMethod; - public String HttpUrl; - public String HttpProtocolVersion; - public Hashtable HttpHeaders = new Hashtable(); - - public static BindingList Credentials = new BindingList(); - public static List AuthorizedClients = new List(); - public static Dictionary BannedClients = new Dictionary(); - - // Maximum post size, 1 Mb - private const int MAX_POST_SIZE = 1024 * 1024; - - // Output buffer size, 64 Kb max - private const int OUTPUT_BUFFER_SIZE = 1024 * 1024; - - private bool _disposed = false; - - public HttpProcessor(TcpClient socket, HttpServer server) - { - this.Socket = socket; - this.Server = server; - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - // Check to see if Dispose has already been called. - - if (!this._disposed) - { - if (disposing) - { - if (OutputStream != null) OutputStream.Dispose(); - if (_inputStream != null) _inputStream.Dispose(); - } - _disposed = true; - } - } - - private string StreamReadLine(Stream inputStream) - { - int next_char = -1; - string data = string.Empty; - if (inputStream.CanRead) - { - while (true) - { - try { next_char = inputStream.ReadByte(); } catch { break; } - if (next_char == '\n') { break; } - if (next_char == '\r') { continue; } - if (next_char == -1) { Thread.Sleep(10); continue; }; - data += Convert.ToChar(next_char); - } - } - return data; - } - - public void Process(object param) - { - // We can't use a StreamReader for input, because it buffers up extra data on us inside it's - // "processed" view of the world, and we want the data raw after the headers - _inputStream = new BufferedStream(Socket.GetStream()); - - if (ParseRequest()) - { - // We probably shouldn't be using a StreamWriter for all output from handlers either - OutputStream = new StreamWriter(new BufferedStream(Socket.GetStream(), OUTPUT_BUFFER_SIZE)); - OutputStream.AutoFlush = true; - - try - { - ReadHeaders(); - - bool authorized = true; - bool checkLogin = true; - - // Compute client hash string based on User-Agent + IP address - string clientHash = string.Empty; - if (HttpHeaders.ContainsKey("User-Agent")) clientHash += HttpHeaders["User-Agent"]; - string remoteIP = (Socket.Client.RemoteEndPoint as IPEndPoint).Address.ToString(); - clientHash += remoteIP; - clientHash = Utils.CreateGuid(Utils.IsoOidNamespace, clientHash).ToString(); - - if (TinyOPDS.Properties.Settings.Default.UseHTTPAuth) - { - authorized = false; - - // Is remote IP banned? - if (TinyOPDS.Properties.Settings.Default.BanClients) - { - lock (BannedClients) - { - if (BannedClients.Count > 0 && BannedClients.ContainsKey(remoteIP) && BannedClients[remoteIP] >= TinyOPDS.Properties.Settings.Default.WrongAttemptsCount) - { - checkLogin = false; - } - } - } - - if (checkLogin) - { - // First, check authorized client list (if enabled) - if (TinyOPDS.Properties.Settings.Default.RememberClients) - { - if (AuthorizedClients.Contains(clientHash)) - { - authorized = true; - } - } - - if (!authorized && HttpHeaders.ContainsKey("Authorization")) - { - string hash = HttpHeaders["Authorization"].ToString(); - - if (hash.StartsWith("Basic ")) - { - try - { - string[] credential = hash.Substring(6).DecodeFromBase64().Split(':'); - - if (credential.Length == 2) - { - string user = credential[0]; - string password = credential[1]; - - foreach (Credential cred in Credentials) - { - if (cred.User.Equals(user)) - { - authorized = cred.Password.Equals(password); - if (authorized) - { - AuthorizedClients.Add(clientHash); - HttpServer.ServerStatistics.SuccessfulLoginAttempts++; - } - break; - } - } - - if (!authorized) - { - Log.WriteLine(LogLevel.Authentication, "Authentication failed! IP: {0} user: {1} pass: {2}", remoteIP, user, password); - } - else - { - Log.WriteLine(LogLevel.Authentication, "User {0} from {1} successfully logged in", user, remoteIP); - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Authentication, "Authentication exception: IP: {0}, {1}", remoteIP, e.Message); - } - } - } - } - } - - if (authorized) - { - HttpServer.ServerStatistics.AddClient(clientHash); - - if (HttpMethod.Equals("GET")) - { - HttpServer.ServerStatistics.GetRequests++; - HandleGETRequest(); - } - else if (HttpMethod.Equals("POST")) - { - HttpServer.ServerStatistics.PostRequests++; - HandlePOSTRequest(); - } - } - else - { - if (TinyOPDS.Properties.Settings.Default.BanClients) - { - lock (BannedClients) - { - if (!BannedClients.ContainsKey(remoteIP)) BannedClients[remoteIP] = 0; - BannedClients[remoteIP]++; - } - if (!checkLogin) - { - Log.WriteLine(LogLevel.Authentication, "IP address {0} is banned!", remoteIP); - WriteForbidden(); - } - } - if (checkLogin) - { - HttpServer.ServerStatistics.WrongLoginAttempts++; - WriteNotAuthorized(); - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".Process(object param) exception: {0}", e.Message); - WriteFailure(); - } - } - - try - { - if (OutputStream != null && OutputStream.BaseStream.CanWrite) - { - try - { - OutputStream.Flush(); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".Process(object param): outputStream.Flush() exception: {0}", e.Message); - } - } - } - finally - { - Socket.Close(); - _inputStream = null; - OutputStream = null; - Socket = null; - } - } - - public bool ParseRequest() - { - String request = StreamReadLine(_inputStream); - if (string.IsNullOrEmpty(request)) return false; - string[] tokens = request.Split(' '); - if (tokens.Length != 3) return false; - HttpMethod = tokens[0].ToUpper(); - HttpUrl = tokens[1]; - HttpProtocolVersion = tokens[2]; - return true; - } - - public void ReadHeaders() - { - string line = string.Empty; - while ((line = StreamReadLine(_inputStream)) != null) - { - if (string.IsNullOrEmpty(line)) return; - - int separator = line.IndexOf(':'); - if (separator == -1) - { - throw new Exception("ReadHeaders(): invalid HTTP header line: " + line); - } - String name = line.Substring(0, separator); - int pos = separator + 1; - // strip spaces - while ((pos < line.Length) && (line[pos] == ' ')) pos++; - - string value = line.Substring(pos, line.Length - pos); - HttpHeaders[name] = value; - } - } - - public void HandleGETRequest() - { - Server.HandleGETRequest(this); - } - - private const int BUF_SIZE = 1024; - public void HandlePOSTRequest() - { - int content_len = 0; - MemoryStream memStream = null; - - try - { - memStream = new MemoryStream(); - if (this.HttpHeaders.ContainsKey("Content-Length")) - { - content_len = Convert.ToInt32(this.HttpHeaders["Content-Length"]); - if (content_len > MAX_POST_SIZE) - { - throw new Exception(String.Format("POST Content-Length({0}) too big for this simple server", content_len)); - } - byte[] buf = new byte[BUF_SIZE]; - int to_read = content_len; - while (to_read > 0) - { - int numread = this._inputStream.Read(buf, 0, Math.Min(BUF_SIZE, to_read)); - if (numread == 0) - { - if (to_read == 0) break; - else throw new Exception("Client disconnected during post"); - } - to_read -= numread; - memStream.Write(buf, 0, numread); - } - memStream.Seek(0, SeekOrigin.Begin); - } - using (StreamReader reader = new StreamReader(memStream)) - { - memStream = null; - Server.HandlePOSTRequest(this, reader); - } - } - finally - { - if (memStream != null) memStream.Dispose(); - } - } - - public void WriteSuccess(string contentType = "text/xml", bool isGZip = false) - { - try - { - OutputStream.Write("HTTP/1.1 200 OK\n"); - OutputStream.Write("Content-Type: " + contentType + "\n"); - if (isGZip) OutputStream.Write("Content-Encoding: gzip\n"); - OutputStream.Write("Connection: close\n\n"); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".WriteSuccess() exception: {0}", e.Message); - } - } - - public void WriteFailure() - { - try - { - OutputStream.Write("HTTP/1.1 404 Bad request\n"); - OutputStream.Write("Connection: close\n\n"); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".WriteFailure() exception: {0}", e.Message); - } - } - - public void WriteNotAuthorized() - { - try - { - OutputStream.Write("HTTP/1.1 401 Unauthorized\n"); - OutputStream.Write("WWW-Authenticate: Basic realm=TinyOPDS\n"); - OutputStream.Write("Connection: close\n\n"); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".WriteNotAuthorized() exception: {0}", e.Message); - } - } - - public void WriteForbidden() - { - try - { - OutputStream.Write("HTTP/1.1 403 Forbidden\n"); - OutputStream.Write("Connection: close\n\n"); - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".WriteForbidden() exception: {0}", e.Message); - } - } - } - - /// - /// Server statistics class - /// - public class Statistics - { - public event EventHandler StatisticsUpdated; - private int _booksSent = 0; - private int _imagesSent = 0; - private int _getRequests = 0; - private int _postRequests = 0; - private int _successfulLoginAttempts = 0; - private int _wrongLoginAttempts = 0; - public int BooksSent { get { return _booksSent; } set { _booksSent = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } - public int ImagesSent { get { return _imagesSent; } set { _imagesSent = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } - public int GetRequests { get { return _getRequests; } set { _getRequests = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } - public int PostRequests { get { return _postRequests; } set { _postRequests = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } - public int SuccessfulLoginAttempts { get { return _successfulLoginAttempts; } set { _successfulLoginAttempts = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } - public int WrongLoginAttempts { get { return _wrongLoginAttempts; } set { _wrongLoginAttempts = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } - public int UniqueClientsCount { get { return _uniqueClients.Count; } } - public int BannedClientsCount { get { lock (HttpProcessor.BannedClients) { return HttpProcessor.BannedClients.Count(сlient => сlient.Value >= TinyOPDS.Properties.Settings.Default.WrongAttemptsCount); } } } - public void AddClient(string newClient) { _uniqueClients[newClient] = true; } - private Dictionary _uniqueClients = new Dictionary(); - public void Clear() - { - _booksSent = _imagesSent = _getRequests = _postRequests = _successfulLoginAttempts = _wrongLoginAttempts = 0; - _uniqueClients.Clear(); - if (StatisticsUpdated != null) StatisticsUpdated(this, null); - } - } - - /// - /// Simple HTTP server - /// - public abstract class HttpServer - { - protected int _port; - protected int _timeout; - protected IPAddress _interfaceIP = IPAddress.Any; - TcpListener _listener; - internal bool _isActive = false; - public bool IsActive { get { return _isActive; } } - public Exception ServerException = null; - public AutoResetEvent ServerReady = null; - public static Statistics ServerStatistics = new Statistics(); - - private bool _isIdle = true; - private TimeSpan _idleTimeout = TimeSpan.FromMinutes(10); - public bool IsIdle { get { return _isIdle; } } - - public HttpServer(int Port, int Timeout = 10000) - { - _port = Port; - _timeout = Timeout; - ServerReady = new AutoResetEvent(false); - ServerStatistics.Clear(); - } - - public HttpServer(IPAddress InterfaceIP, int Port, int Timeout = 10000) - { - _interfaceIP = InterfaceIP; - _port = Port; - _timeout = Timeout; - ServerReady = new AutoResetEvent(false); - ServerStatistics.Clear(); - } - - ~HttpServer() - { - StopServer(); - } - - public virtual void StopServer() - { - _isActive = false; - if (_listener != null) - { - _listener.Stop(); - _listener = null; - } - if (ServerReady != null) - { - ServerReady.Dispose(); - ServerReady = null; - } - } - - /// - /// Server listener - /// - public void Listen() - { - DateTime requestTime = DateTime.Now; - int loopCount = 0; - HttpProcessor processor = null; - ServerException = null; - try - { - _listener = new TcpListener(_interfaceIP, _port); - _listener.Start(); - _isActive = true; - ServerReady.Set(); - while (_isActive) - { - if (_listener.Pending()) - { - TcpClient socket = _listener.AcceptTcpClient(); - socket.SendTimeout = socket.ReceiveTimeout = _timeout; - socket.SendBufferSize = 1024 * 1024; - socket.NoDelay = true; - processor = new HttpProcessor(socket, this); - // Reset idle state - _isIdle = false; - requestTime = DateTime.Now; - loopCount = 0; - ThreadPool.QueueUserWorkItem(new WaitCallback(processor.Process)); - } - else Thread.Sleep(100); - - // Check the idle state once a minute - if (loopCount++ > 600) - { - loopCount = 0; - if (DateTime.Now.Subtract(requestTime) > _idleTimeout) _isIdle = true; - } - } - } - catch (Exception e) - { - Log.WriteLine(LogLevel.Error, ".Listen() exception: {0}", e.Message); - ServerException = e; - _isActive = false; - ServerReady.Set(); - } - finally - { - if (processor != null) processor.Dispose(); - _isActive = false; - } - } - - /// - /// Abstract method to handle GET request - /// - /// - public abstract void HandleGETRequest(HttpProcessor processor); - - /// - /// Abstract method to handle POST request - /// - /// - /// - public abstract void HandlePOSTRequest(HttpProcessor processor, StreamReader inputData); - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module contains simple HTTP processor implementation + * and abstract class for HTTP server + * Also, couple additional service classes are specified + * + * + ************************************************************/ + +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.ComponentModel; + +namespace TinyOPDS.Server +{ + /// + /// Basic credentials (unencrypted) + /// + public class Credential + { + public string User { get; set; } + public string Password { get; set; } + public Credential(string user, string password) { User = user; Password = password; } + } + + /// + /// Simple HTTP processor + /// + public class HttpProcessor : IDisposable + { + public TcpClient Socket; + public HttpServer Server; + + private Stream _inputStream; + public StreamWriter OutputStream; + + public String HttpMethod; + public String HttpUrl; + public String HttpProtocolVersion; + public Hashtable HttpHeaders = new Hashtable(); + + public static BindingList Credentials = new BindingList(); + public static List AuthorizedClients = new List(); + public static Dictionary BannedClients = new Dictionary(); + + // Maximum post size, 1 Mb + private const int MAX_POST_SIZE = 1024 * 1024; + + // Output buffer size, 64 Kb max + private const int OUTPUT_BUFFER_SIZE = 1024 * 1024; + + private bool _disposed = false; + + public HttpProcessor(TcpClient socket, HttpServer server) + { + this.Socket = socket; + this.Server = server; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + // Check to see if Dispose has already been called. + + if (!this._disposed) + { + if (disposing) + { + if (OutputStream != null) OutputStream.Dispose(); + if (_inputStream != null) _inputStream.Dispose(); + } + _disposed = true; + } + } + + private string StreamReadLine(Stream inputStream) + { + int next_char = -1; + string data = string.Empty; + if (inputStream.CanRead) + { + while (true) + { + try { next_char = inputStream.ReadByte(); } catch { break; } + if (next_char == '\n') { break; } + if (next_char == '\r') { continue; } + if (next_char == -1) { Thread.Sleep(10); continue; }; + data += Convert.ToChar(next_char); + } + } + return data; + } + + public void Process(object param) + { + // We can't use a StreamReader for input, because it buffers up extra data on us inside it's + // "processed" view of the world, and we want the data raw after the headers + _inputStream = new BufferedStream(Socket.GetStream()); + + if (ParseRequest()) + { + // We probably shouldn't be using a StreamWriter for all output from handlers either + OutputStream = new StreamWriter(new BufferedStream(Socket.GetStream(), OUTPUT_BUFFER_SIZE)); + OutputStream.AutoFlush = true; + + try + { + ReadHeaders(); + + bool authorized = true; + bool checkLogin = true; + + // Compute client hash string based on User-Agent + IP address + string clientHash = string.Empty; + if (HttpHeaders.ContainsKey("User-Agent")) clientHash += HttpHeaders["User-Agent"]; + string remoteIP = (Socket.Client.RemoteEndPoint as IPEndPoint).Address.ToString(); + clientHash += remoteIP; + clientHash = Utils.CreateGuid(Utils.IsoOidNamespace, clientHash).ToString(); + + if (TinyOPDS.Properties.Settings.Default.UseHTTPAuth) + { + authorized = false; + + // Is remote IP banned? + if (TinyOPDS.Properties.Settings.Default.BanClients) + { + lock (BannedClients) + { + if (BannedClients.Count > 0 && BannedClients.ContainsKey(remoteIP) && BannedClients[remoteIP] >= TinyOPDS.Properties.Settings.Default.WrongAttemptsCount) + { + checkLogin = false; + } + } + } + + if (checkLogin) + { + // First, check authorized client list (if enabled) + if (TinyOPDS.Properties.Settings.Default.RememberClients) + { + if (AuthorizedClients.Contains(clientHash)) + { + authorized = true; + } + } + + if (!authorized && HttpHeaders.ContainsKey("Authorization")) + { + string hash = HttpHeaders["Authorization"].ToString(); + + if (hash.StartsWith("Basic ")) + { + try + { + string[] credential = hash.Substring(6).DecodeFromBase64().Split(':'); + + if (credential.Length == 2) + { + string user = credential[0]; + string password = credential[1]; + + foreach (Credential cred in Credentials) + { + if (cred.User.Equals(user)) + { + authorized = cred.Password.Equals(password); + if (authorized) + { + AuthorizedClients.Add(clientHash); + HttpServer.ServerStatistics.SuccessfulLoginAttempts++; + } + break; + } + } + + if (!authorized) + { + Log.WriteLine(LogLevel.Authentication, "Authentication failed! IP: {0} user: {1} pass: {2}", remoteIP, user, password); + } + else + { + Log.WriteLine(LogLevel.Authentication, "User {0} from {1} successfully logged in", user, remoteIP); + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Authentication, "Authentication exception: IP: {0}, {1}", remoteIP, e.Message); + } + } + } + } + } + + if (authorized) + { + HttpServer.ServerStatistics.AddClient(clientHash); + + if (HttpMethod.Equals("GET")) + { + HttpServer.ServerStatistics.GetRequests++; + HandleGETRequest(); + } + else if (HttpMethod.Equals("POST")) + { + HttpServer.ServerStatistics.PostRequests++; + HandlePOSTRequest(); + } + } + else + { + if (TinyOPDS.Properties.Settings.Default.BanClients) + { + lock (BannedClients) + { + if (!BannedClients.ContainsKey(remoteIP)) BannedClients[remoteIP] = 0; + BannedClients[remoteIP]++; + } + if (!checkLogin) + { + Log.WriteLine(LogLevel.Authentication, "IP address {0} is banned!", remoteIP); + WriteForbidden(); + } + } + if (checkLogin) + { + HttpServer.ServerStatistics.WrongLoginAttempts++; + WriteNotAuthorized(); + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".Process(object param) exception: {0}", e.Message); + WriteFailure(); + } + } + + try + { + if (OutputStream != null && OutputStream.BaseStream.CanWrite) + { + try + { + OutputStream.Flush(); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".Process(object param): outputStream.Flush() exception: {0}", e.Message); + } + } + } + finally + { + Socket.Close(); + _inputStream = null; + OutputStream = null; + Socket = null; + } + } + + public bool ParseRequest() + { + String request = StreamReadLine(_inputStream); + if (string.IsNullOrEmpty(request)) return false; + string[] tokens = request.Split(' '); + if (tokens.Length != 3) return false; + HttpMethod = tokens[0].ToUpper(); + HttpUrl = tokens[1]; + HttpProtocolVersion = tokens[2]; + return true; + } + + public void ReadHeaders() + { + string line = string.Empty; + while ((line = StreamReadLine(_inputStream)) != null) + { + if (string.IsNullOrEmpty(line)) return; + + int separator = line.IndexOf(':'); + if (separator == -1) + { + throw new Exception("ReadHeaders(): invalid HTTP header line: " + line); + } + String name = line.Substring(0, separator); + int pos = separator + 1; + // strip spaces + while ((pos < line.Length) && (line[pos] == ' ')) pos++; + + string value = line.Substring(pos, line.Length - pos); + HttpHeaders[name] = value; + } + } + + public void HandleGETRequest() + { + Server.HandleGETRequest(this); + } + + private const int BUF_SIZE = 1024; + public void HandlePOSTRequest() + { + int content_len = 0; + MemoryStream memStream = null; + + try + { + memStream = new MemoryStream(); + if (this.HttpHeaders.ContainsKey("Content-Length")) + { + content_len = Convert.ToInt32(this.HttpHeaders["Content-Length"]); + if (content_len > MAX_POST_SIZE) + { + throw new Exception(String.Format("POST Content-Length({0}) too big for this simple server", content_len)); + } + byte[] buf = new byte[BUF_SIZE]; + int to_read = content_len; + while (to_read > 0) + { + int numread = this._inputStream.Read(buf, 0, Math.Min(BUF_SIZE, to_read)); + if (numread == 0) + { + if (to_read == 0) break; + else throw new Exception("Client disconnected during post"); + } + to_read -= numread; + memStream.Write(buf, 0, numread); + } + memStream.Seek(0, SeekOrigin.Begin); + } + using (StreamReader reader = new StreamReader(memStream)) + { + memStream = null; + Server.HandlePOSTRequest(this, reader); + } + } + finally + { + if (memStream != null) memStream.Dispose(); + } + } + + public void WriteSuccess(string contentType = "text/xml", bool isGZip = false) + { + try + { + OutputStream.Write("HTTP/1.1 200 OK\n"); + OutputStream.Write("Content-Type: " + contentType + "\n"); + if (isGZip) OutputStream.Write("Content-Encoding: gzip\n"); + OutputStream.Write("Connection: close\n\n"); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".WriteSuccess() exception: {0}", e.Message); + } + } + + public void WriteFailure() + { + try + { + OutputStream.Write("HTTP/1.1 404 Bad request\n"); + OutputStream.Write("Connection: close\n\n"); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".WriteFailure() exception: {0}", e.Message); + } + } + + public void WriteNotAuthorized() + { + try + { + OutputStream.Write("HTTP/1.1 401 Unauthorized\n"); + OutputStream.Write("WWW-Authenticate: Basic realm=TinyOPDS\n"); + OutputStream.Write("Connection: close\n\n"); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".WriteNotAuthorized() exception: {0}", e.Message); + } + } + + public void WriteForbidden() + { + try + { + OutputStream.Write("HTTP/1.1 403 Forbidden\n"); + OutputStream.Write("Connection: close\n\n"); + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".WriteForbidden() exception: {0}", e.Message); + } + } + } + + /// + /// Server statistics class + /// + public class Statistics + { + public event EventHandler StatisticsUpdated; + private int _booksSent = 0; + private int _imagesSent = 0; + private int _getRequests = 0; + private int _postRequests = 0; + private int _successfulLoginAttempts = 0; + private int _wrongLoginAttempts = 0; + public int BooksSent { get { return _booksSent; } set { _booksSent = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } + public int ImagesSent { get { return _imagesSent; } set { _imagesSent = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } + public int GetRequests { get { return _getRequests; } set { _getRequests = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } + public int PostRequests { get { return _postRequests; } set { _postRequests = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } + public int SuccessfulLoginAttempts { get { return _successfulLoginAttempts; } set { _successfulLoginAttempts = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } + public int WrongLoginAttempts { get { return _wrongLoginAttempts; } set { _wrongLoginAttempts = value; if (StatisticsUpdated != null) StatisticsUpdated(this, null); } } + public int UniqueClientsCount { get { return _uniqueClients.Count; } } + public int BannedClientsCount { get { lock (HttpProcessor.BannedClients) { return HttpProcessor.BannedClients.Count(сlient => сlient.Value >= TinyOPDS.Properties.Settings.Default.WrongAttemptsCount); } } } + public void AddClient(string newClient) { _uniqueClients[newClient] = true; } + private Dictionary _uniqueClients = new Dictionary(); + public void Clear() + { + _booksSent = _imagesSent = _getRequests = _postRequests = _successfulLoginAttempts = _wrongLoginAttempts = 0; + _uniqueClients.Clear(); + if (StatisticsUpdated != null) StatisticsUpdated(this, null); + } + } + + /// + /// Simple HTTP server + /// + public abstract class HttpServer + { + protected int _port; + protected int _timeout; + protected IPAddress _interfaceIP = IPAddress.Any; + TcpListener _listener; + internal bool _isActive = false; + public bool IsActive { get { return _isActive; } } + public Exception ServerException = null; + public AutoResetEvent ServerReady = null; + public static Statistics ServerStatistics = new Statistics(); + + private bool _isIdle = true; + private TimeSpan _idleTimeout = TimeSpan.FromMinutes(10); + public bool IsIdle { get { return _isIdle; } } + + public HttpServer(int Port, int Timeout = 10000) + { + _port = Port; + _timeout = Timeout; + ServerReady = new AutoResetEvent(false); + ServerStatistics.Clear(); + } + + public HttpServer(IPAddress InterfaceIP, int Port, int Timeout = 10000) + { + _interfaceIP = InterfaceIP; + _port = Port; + _timeout = Timeout; + ServerReady = new AutoResetEvent(false); + ServerStatistics.Clear(); + } + + ~HttpServer() + { + StopServer(); + } + + public virtual void StopServer() + { + _isActive = false; + if (_listener != null) + { + _listener.Stop(); + _listener = null; + } + if (ServerReady != null) + { + ServerReady.Dispose(); + ServerReady = null; + } + } + + /// + /// Server listener + /// + public void Listen() + { + DateTime requestTime = DateTime.Now; + int loopCount = 0; + HttpProcessor processor = null; + ServerException = null; + try + { + _listener = new TcpListener(_interfaceIP, _port); + _listener.Start(); + _isActive = true; + ServerReady.Set(); + while (_isActive) + { + if (_listener.Pending()) + { + TcpClient socket = _listener.AcceptTcpClient(); + socket.SendTimeout = socket.ReceiveTimeout = _timeout; + socket.SendBufferSize = 1024 * 1024; + socket.NoDelay = true; + processor = new HttpProcessor(socket, this); + // Reset idle state + _isIdle = false; + requestTime = DateTime.Now; + loopCount = 0; + ThreadPool.QueueUserWorkItem(new WaitCallback(processor.Process)); + } + else Thread.Sleep(100); + + // Check the idle state once a minute + if (loopCount++ > 600) + { + loopCount = 0; + if (DateTime.Now.Subtract(requestTime) > _idleTimeout) _isIdle = true; + } + } + } + catch (Exception e) + { + Log.WriteLine(LogLevel.Error, ".Listen() exception: {0}", e.Message); + ServerException = e; + _isActive = false; + ServerReady.Set(); + } + finally + { + if (processor != null) processor.Dispose(); + _isActive = false; + } + } + + /// + /// Abstract method to handle GET request + /// + /// + public abstract void HandleGETRequest(HttpProcessor processor); + + /// + /// Abstract method to handle POST request + /// + /// + /// + public abstract void HandlePOSTRequest(HttpProcessor processor, StreamReader inputData); + } +} diff --git a/trunk/TinyOPDS/Server/OPDSServer.cs b/TinyOPDS/Server/OPDSServer.cs similarity index 98% rename from trunk/TinyOPDS/Server/OPDSServer.cs rename to TinyOPDS/Server/OPDSServer.cs index 6e83c1f..96a23e4 100644 --- a/trunk/TinyOPDS/Server/OPDSServer.cs +++ b/TinyOPDS/Server/OPDSServer.cs @@ -1,479 +1,479 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This module defines OPDS HTTP server class - * - ************************************************************/ - -using System; -using System.IO; -using System.IO.Compression; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Net; -using System.Xml; -using System.Xml.Xsl; -using System.Xml.XPath; -using System.Reflection; - -using Ionic.Zip; -using TinyOPDS.OPDS; -using TinyOPDS.Data; - -namespace TinyOPDS.Server -{ - public class OPDSServer : HttpServer - { - private string[] _extensions = { ".zip", ".epub", ".jpeg", ".ico", ".xml" }; - private XslCompiledTransform _xslTransform = new XslCompiledTransform(); - - public OPDSServer(IPAddress interfaceIP, int port, int timeout = 5000) : base(interfaceIP, port, timeout) - { - // Check external template (will be used instead of built-in) - string xslFileName = Path.Combine(Utils.ServiceFilesLocation, "xml2html.xsl"); - - 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); - } - } - } - - /// - /// Dummy for POST requests - /// - /// - /// - public override void HandlePOSTRequest(HttpProcessor processor, StreamReader inputData) - { - Log.WriteLine(LogLevel.Warning, "HTTP POST request from {0}: {1} : NOT IMPLEMENTED", ((System.Net.IPEndPoint)processor.Socket.Client.RemoteEndPoint).Address, processor.HttpUrl); - } - - /// - /// POST requests handler - /// - /// - 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(" 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(); - } - } - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This module defines OPDS HTTP server class + * + ************************************************************/ + +using System; +using System.IO; +using System.IO.Compression; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Net; +using System.Xml; +using System.Xml.Xsl; +using System.Xml.XPath; +using System.Reflection; + +using Ionic.Zip; +using TinyOPDS.OPDS; +using TinyOPDS.Data; + +namespace TinyOPDS.Server +{ + public class OPDSServer : HttpServer + { + private string[] _extensions = { ".zip", ".epub", ".jpeg", ".ico", ".xml" }; + private XslCompiledTransform _xslTransform = new XslCompiledTransform(); + + public OPDSServer(IPAddress interfaceIP, int port, int timeout = 5000) : base(interfaceIP, port, timeout) + { + // Check external template (will be used instead of built-in) + string xslFileName = Path.Combine(Utils.ServiceFilesLocation, "xml2html.xsl"); + + 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); + } + } + } + + /// + /// Dummy for POST requests + /// + /// + /// + public override void HandlePOSTRequest(HttpProcessor processor, StreamReader inputData) + { + Log.WriteLine(LogLevel.Warning, "HTTP POST request from {0}: {1} : NOT IMPLEMENTED", ((System.Net.IPEndPoint)processor.Socket.Client.RemoteEndPoint).Address, processor.HttpUrl); + } + + /// + /// POST requests handler + /// + /// + 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(" 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(); + } + } + } +} diff --git a/trunk/TinyOPDS/SgmlReader/SgmlParser.cs b/TinyOPDS/SgmlReader/SgmlParser.cs similarity index 100% rename from trunk/TinyOPDS/SgmlReader/SgmlParser.cs rename to TinyOPDS/SgmlReader/SgmlParser.cs diff --git a/trunk/TinyOPDS/SgmlReader/SgmlReader.cs b/TinyOPDS/SgmlReader/SgmlReader.cs similarity index 100% rename from trunk/TinyOPDS/SgmlReader/SgmlReader.cs rename to TinyOPDS/SgmlReader/SgmlReader.cs diff --git a/trunk/TinyOPDS/TinyOPDS.csproj b/TinyOPDS/TinyOPDS.csproj similarity index 97% rename from trunk/TinyOPDS/TinyOPDS.csproj rename to TinyOPDS/TinyOPDS.csproj index fa5aad5..d0a0ab5 100644 --- a/trunk/TinyOPDS/TinyOPDS.csproj +++ b/TinyOPDS/TinyOPDS.csproj @@ -1,259 +1,259 @@ - - - - Debug - AnyCPU - 8.0.30703 - 2.0 - {D4508020-1E2C-4D8E-B879-77D5C213E8EC} - WinExe - Properties - TinyOPDS - TinyOPDS - v4.0 - 512 - false - Client - Svn - Svn - Svn - SubversionScc - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true - - - true - bin\Debug\ - TRACE;DEBUG - full - AnyCPU - bin\Debug\TinyOPDS.exe.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - false - - - bin\Release\ - TRACE - true - pdbonly - AnyCPU - bin\Release\TinyOPDS.exe.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - true - false - 4 - - - TinyOPDS.ico - - - true - bin\Debug\ - CODE_ANALYSIS;DEBUG;TRACE - full - AnyCPU - false - bin\Debug\TinyOPDS.exe.CodeAnalysisLog.xml - true - GlobalSuppressions.cs - prompt - MinimumRecommendedRules.ruleset - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets - true - ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules - true - - - TinyOPDS.Program - - - false - - - - - - - LocalIntranet - - - false - - - Properties\app.manifest - - - - False - Libs\eBdb.EpubReader.dll - - - False - Libs\FB2Library.dll - - - False - Libs\Ionic.Zip.Reduced.dll - - - - - - - - - - - - - - - - - - - - - Form - - - MainForm.cs - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - MainForm.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - SQL Server Compact 3.5 SP2 - true - - - False - Windows Installer 3.1 - true - - - - - - - - - - - - - - - - - - - - - - - - - - if exist $(ProjectDir)\Sign\\ copy $(TargetPath) $(ProjectDir)\Sign - - - - - + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {D4508020-1E2C-4D8E-B879-77D5C213E8EC} + WinExe + Properties + TinyOPDS + TinyOPDS + v4.0 + 512 + false + Client + Svn + Svn + Svn + SubversionScc + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + bin\Debug\ + TRACE;DEBUG + full + AnyCPU + bin\Debug\TinyOPDS.exe.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + false + + + bin\Release\ + TRACE + true + pdbonly + AnyCPU + bin\Release\TinyOPDS.exe.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + true + false + 4 + + + TinyOPDS.ico + + + true + bin\Debug\ + CODE_ANALYSIS;DEBUG;TRACE + full + AnyCPU + false + bin\Debug\TinyOPDS.exe.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets + true + ;C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules + true + + + TinyOPDS.Program + + + false + + + + + + + LocalIntranet + + + false + + + Properties\app.manifest + + + + False + Libs\eBdb.EpubReader.dll + + + False + Libs\FB2Library.dll + + + False + Libs\Ionic.Zip.Reduced.dll + + + + + + + + + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + SQL Server Compact 3.5 SP2 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + + + + + + + + + + + + + + + if exist $(ProjectDir)\Sign\\ copy $(TargetPath) $(ProjectDir)\Sign + + + + + \ No newline at end of file diff --git a/trunk/TinyOPDS/TinyOPDS.ico b/TinyOPDS/TinyOPDS.ico similarity index 100% rename from trunk/TinyOPDS/TinyOPDS.ico rename to TinyOPDS/TinyOPDS.ico diff --git a/trunk/TinyOPDS/TinyOPDS.png b/TinyOPDS/TinyOPDS.png similarity index 100% rename from trunk/TinyOPDS/TinyOPDS.png rename to TinyOPDS/TinyOPDS.png diff --git a/trunk/TinyOPDS/TinyOPDS.sln b/TinyOPDS/TinyOPDS.sln similarity index 98% rename from trunk/TinyOPDS/TinyOPDS.sln rename to TinyOPDS/TinyOPDS.sln index 52fd309..5c394cd 100644 --- a/trunk/TinyOPDS/TinyOPDS.sln +++ b/TinyOPDS/TinyOPDS.sln @@ -1,73 +1,73 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyOPDS", "TinyOPDS.csproj", "{D4508020-1E2C-4D8E-B879-77D5C213E8EC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyOPDSConsole", "..\TinyOPDSConsole\TinyOPDSConsole.csproj", "{5A92FA9B-B91C-48F4-9488-77103868D226}" -EndProject -Global - GlobalSection(SubversionScc) = preSolution - Svn-Managed = True - Manager = AnkhSVN - Subversion Support for Visual Studio - EndGlobalSection - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|ARM = Debug|ARM - Debug|Mixed Platforms = Debug|Mixed Platforms - Debug|Win32 = Debug|Win32 - Debug|x86 = Debug|x86 - Full debug|Any CPU = Full debug|Any CPU - Full debug|ARM = Full debug|ARM - Full debug|Mixed Platforms = Full debug|Mixed Platforms - Full debug|Win32 = Full debug|Win32 - Full debug|x86 = Full debug|x86 - Release|Any CPU = Release|Any CPU - Release|ARM = Release|ARM - Release|Mixed Platforms = Release|Mixed Platforms - Release|Win32 = Release|Win32 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|ARM.ActiveCfg = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Win32.ActiveCfg = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|x86.ActiveCfg = Debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Any CPU.ActiveCfg = Full debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|ARM.ActiveCfg = Full debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Mixed Platforms.ActiveCfg = Full debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Mixed Platforms.Build.0 = Full debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Win32.ActiveCfg = Full debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|x86.ActiveCfg = Full debug|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Any CPU.Build.0 = Release|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|ARM.ActiveCfg = Release|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Win32.ActiveCfg = Release|Any CPU - {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|x86.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|ARM.ActiveCfg = Debug|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Win32.ActiveCfg = Debug|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|x86.ActiveCfg = Debug|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Any CPU.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Any CPU.Build.0 = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|ARM.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Mixed Platforms.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Win32.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|x86.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Any CPU.Build.0 = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|ARM.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Win32.ActiveCfg = Release|Any CPU - {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|x86.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2012 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyOPDS", "TinyOPDS.csproj", "{D4508020-1E2C-4D8E-B879-77D5C213E8EC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TinyOPDSConsole", "..\TinyOPDSConsole\TinyOPDSConsole.csproj", "{5A92FA9B-B91C-48F4-9488-77103868D226}" +EndProject +Global + GlobalSection(SubversionScc) = preSolution + Svn-Managed = True + Manager = AnkhSVN - Subversion Support for Visual Studio + EndGlobalSection + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|Win32 = Debug|Win32 + Debug|x86 = Debug|x86 + Full debug|Any CPU = Full debug|Any CPU + Full debug|ARM = Full debug|ARM + Full debug|Mixed Platforms = Full debug|Mixed Platforms + Full debug|Win32 = Full debug|Win32 + Full debug|x86 = Full debug|x86 + Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|Mixed Platforms = Release|Mixed Platforms + Release|Win32 = Release|Win32 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|ARM.ActiveCfg = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|Win32.ActiveCfg = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Debug|x86.ActiveCfg = Debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Any CPU.ActiveCfg = Full debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|ARM.ActiveCfg = Full debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Mixed Platforms.ActiveCfg = Full debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Mixed Platforms.Build.0 = Full debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|Win32.ActiveCfg = Full debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Full debug|x86.ActiveCfg = Full debug|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Any CPU.Build.0 = Release|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|ARM.ActiveCfg = Release|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|Win32.ActiveCfg = Release|Any CPU + {D4508020-1E2C-4D8E-B879-77D5C213E8EC}.Release|x86.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|ARM.ActiveCfg = Debug|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|Win32.ActiveCfg = Debug|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Debug|x86.ActiveCfg = Debug|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Any CPU.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Any CPU.Build.0 = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|ARM.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Mixed Platforms.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|Win32.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Full debug|x86.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Any CPU.Build.0 = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|ARM.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|Win32.ActiveCfg = Release|Any CPU + {5A92FA9B-B91C-48F4-9488-77103868D226}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/TinyOPDS/a_aliases.txt.gz b/TinyOPDS/a_aliases.txt.gz similarity index 100% rename from trunk/TinyOPDS/a_aliases.txt.gz rename to TinyOPDS/a_aliases.txt.gz diff --git a/trunk/TinyOPDS/app.config b/TinyOPDS/app.config similarity index 97% rename from trunk/TinyOPDS/app.config rename to TinyOPDS/app.config index 70fefc8..ea19127 100644 --- a/trunk/TinyOPDS/app.config +++ b/TinyOPDS/app.config @@ -1,109 +1,109 @@ - - - - -
- - - - - - - - - - - - - Моя домашняя библиотека - - - 8080 - - - False - - - False - - - False - - - - - - en - - - False - - - - - - False - - - False - - - True - - - False - - - - - - False - - - 0 - - - False - - - 1 - - - 2013-01-01 - - - 5 - - - 0 - - - lib - - - False - - - False - - - opds - - - 50 - - - 1000 - - - 1 - - - 2 - - - True - - - - + + + + +
+ + + + + + + + + + + + + Моя домашняя библиотека + + + 8080 + + + False + + + False + + + False + + + + + + en + + + False + + + + + + False + + + False + + + True + + + False + + + + + + False + + + 0 + + + False + + + 1 + + + 2013-01-01 + + + 5 + + + 0 + + + lib + + + False + + + False + + + opds + + + 50 + + + 1000 + + + 1 + + + 2 + + + True + + + + diff --git a/trunk/TinyOPDS/convertGenres.cs b/TinyOPDS/convertGenres.cs similarity index 97% rename from trunk/TinyOPDS/convertGenres.cs rename to TinyOPDS/convertGenres.cs index 17c0b2e..89fc50b 100644 --- a/trunk/TinyOPDS/convertGenres.cs +++ b/TinyOPDS/convertGenres.cs @@ -1,74 +1,74 @@ -using System; -using System.IO; - -namespace convertGenres -{ - // Simple program to convert genres from text file to xml - public class Program - { - static bool firstTime = true; - - public static void Main(string[] args) - { - if (args.Length < 1) - { - Console.WriteLine("Usage: convertGenres file"); - return; - } - - if (File.Exists(args[0])) - { - using (StreamReader sr = new StreamReader(args[0])) - { - using (StreamWriter sw = new StreamWriter(Path.GetFileNameWithoutExtension(args[0]) + ".xml")) - { - sw.WriteLine("\n"); - - while (!sr.EndOfStream) - { - string s = sr.ReadLine().Replace("&", "&"); - - if (!s.Contains("codepage")) - { - // Is it multilingual file (en+ru)? - int lang_div = s.IndexOf((char)65533); - //Console.WriteLine("lang_div = {0}", lang_div); - - if (s[0] != ' ') - { - if (lang_div > 0) - { - string ru_name = s.Substring(0, lang_div - 1).Trim(); - string en_name = s.Substring(lang_div + 1).Trim(); - sw.WriteLine( (firstTime ? "" :"\t\n") + "\t"); - } - else sw.WriteLine("\t"); - firstTime = false; - } - else - { - int name_idx = s.IndexOf(' ', 2); - string subgenre = s.Substring(1, name_idx - 1); - - if (lang_div > 0) - { - string ru_name = s.Substring(name_idx + 1, lang_div - name_idx - 2).Trim(); - string en_name = s.Substring(lang_div + 1).Trim(); - sw.WriteLine("\t\t" + en_name + ""); - } - else - { - string name = s.Substring(name_idx + 1); - sw.WriteLine("\t\t" + name + ""); - } - } - } - } - - sw.WriteLine("\t\n"); - } - } - } - } - } +using System; +using System.IO; + +namespace convertGenres +{ + // Simple program to convert genres from text file to xml + public class Program + { + static bool firstTime = true; + + public static void Main(string[] args) + { + if (args.Length < 1) + { + Console.WriteLine("Usage: convertGenres file"); + return; + } + + if (File.Exists(args[0])) + { + using (StreamReader sr = new StreamReader(args[0])) + { + using (StreamWriter sw = new StreamWriter(Path.GetFileNameWithoutExtension(args[0]) + ".xml")) + { + sw.WriteLine("\n"); + + while (!sr.EndOfStream) + { + string s = sr.ReadLine().Replace("&", "&"); + + if (!s.Contains("codepage")) + { + // Is it multilingual file (en+ru)? + int lang_div = s.IndexOf((char)65533); + //Console.WriteLine("lang_div = {0}", lang_div); + + if (s[0] != ' ') + { + if (lang_div > 0) + { + string ru_name = s.Substring(0, lang_div - 1).Trim(); + string en_name = s.Substring(lang_div + 1).Trim(); + sw.WriteLine( (firstTime ? "" :"\t\n") + "\t"); + } + else sw.WriteLine("\t"); + firstTime = false; + } + else + { + int name_idx = s.IndexOf(' ', 2); + string subgenre = s.Substring(1, name_idx - 1); + + if (lang_div > 0) + { + string ru_name = s.Substring(name_idx + 1, lang_div - name_idx - 2).Trim(); + string en_name = s.Substring(lang_div + 1).Trim(); + sw.WriteLine("\t\t" + en_name + ""); + } + else + { + string name = s.Substring(name_idx + 1); + sw.WriteLine("\t\t" + name + ""); + } + } + } + } + + sw.WriteLine("\t\n"); + } + } + } + } + } } \ No newline at end of file diff --git a/trunk/TinyOPDS/convertGenres.exe b/TinyOPDS/convertGenres.exe similarity index 100% rename from trunk/TinyOPDS/convertGenres.exe rename to TinyOPDS/convertGenres.exe diff --git a/trunk/TinyOPDS/donate.png b/TinyOPDS/donate.png similarity index 100% rename from trunk/TinyOPDS/donate.png rename to TinyOPDS/donate.png diff --git a/trunk/TinyOPDS/genres.rus.txt b/TinyOPDS/genres.rus.txt similarity index 100% rename from trunk/TinyOPDS/genres.rus.txt rename to TinyOPDS/genres.rus.txt diff --git a/trunk/TinyOPDS/genres.xml b/TinyOPDS/genres.xml similarity index 98% rename from trunk/TinyOPDS/genres.xml rename to TinyOPDS/genres.xml index 4a481be..bbe7a09 100644 --- a/trunk/TinyOPDS/genres.xml +++ b/TinyOPDS/genres.xml @@ -1,313 +1,313 @@ - - - Alternative history - Action SF - Epic SF - Heroic SF - Detective SF - Cyberpunk - Space SF - Social SF - Horror - Humor SF - Fantasy - Science Fiction - Science Fiction for Kids - Fantasy city - Postapocalyptic - Love SF - Gothic novel - Non Science Fiction - Fairy SF - Other SF - Ironical SF - Ironyc female fantasy - Mystic - Space Opera - Stimpank - Technofantasy - popadanec - Historical fantasy - humor_fantasy + + + Alternative history + Action SF + Epic SF + Heroic SF + Detective SF + Cyberpunk + Space SF + Social SF + Horror + Humor SF + Fantasy + Science Fiction + Science Fiction for Kids + Fantasy city + Postapocalyptic + Love SF + Gothic novel + Non Science Fiction + Fairy SF + Other SF + Ironical SF + Ironyc female fantasy + Mystic + Space Opera + Stimpank + Technofantasy + popadanec + Historical fantasy + humor_fantasy - - Detective SF - Classical Detective - Police Stories - Action - Ironical Detective - Historical Detective - Espionage Detective - Crime Detective - Political Detective - Maniacs - Hard-boiled Detective - Thrillers - Detective - Detective Romance - Detective for Kids - Legal thriller - Medical thriller - Techno Thriller - Cozy Mysteries + + Detective SF + Classical Detective + Police Stories + Action + Ironical Detective + Historical Detective + Espionage Detective + Crime Detective + Political Detective + Maniacs + Hard-boiled Detective + Thrillers + Detective + Detective Romance + Detective for Kids + Legal thriller + Medical thriller + Techno Thriller + Cozy Mysteries - - Prose - Classical Prose - Historical Prose - Contemporary Prose - Counterculture - Russian Classics - Soviet Classics - Military - Aphorisms - Essay - Story - Great story - Short story - Roman - Extravaganza - Epistolary - Epic - Magic realism - Sagas - Anti-Soviet fiction - prose_sentimental + + Prose + Classical Prose + Historical Prose + Contemporary Prose + Counterculture + Russian Classics + Soviet Classics + Military + Aphorisms + Essay + Story + Great story + Short story + Roman + Extravaganza + Epistolary + Epic + Magic realism + Sagas + Anti-Soviet fiction + prose_sentimental - - Contemporary Romance - Historical Romance - Detective Romance - Short Romance - Erotica - About love - Love SF - Porno - Cozy Mysteries + + Contemporary Romance + Historical Romance + Detective Romance + Short Romance + Erotica + About love + Love SF + Porno + Cozy Mysteries - - Western - History - Indians - Maritime Fiction - Travel & Geography - Nature & Animals - Misk Adventures - Kids Adventures + + Western + History + Indians + Maritime Fiction + Travel & Geography + Nature & Animals + Misk Adventures + Kids Adventures - - Fairy Tales - Verses for Kids - Prose for Kids - Science Fiction for Kids - Detective for Kids - Kids Adventures - Education for Kids - For Kids: Misk - Child Folklore - Game book + + Fairy Tales + Verses for Kids + Prose for Kids + Science Fiction for Kids + Detective for Kids + Kids Adventures + Education for Kids + For Kids: Misk + Child Folklore + Game book - - Verses for Kids - Poetry - Humor Verses - Fable - Vers libre - Visual Poetry - Lyrics - Palindromes - Song Poetry - Experimental Poetry - Epic Poetry - In verse + + Verses for Kids + Poetry + Humor Verses + Fable + Vers libre + Visual Poetry + Lyrics + Palindromes + Song Poetry + Experimental Poetry + Epic Poetry + In verse - - Antique Literature - Antique European Literature - Antique Russian Literature - Antique East Literature - Myths. Legends. Epos - Other Antique + + Antique Literature + Antique European Literature + Antique Russian Literature + Antique East Literature + Myths. Legends. Epos + Other Antique - - History - Psychology - Cultural Science - Religious Studies - Philosophy - Politics - Business - Jurisprudence - Linguistics - Medicine - Physics - Mathematics - Chemistry - Biology - Technical - Misk Science, Education - Biochemistry - Physical chemistry - Analitic Chemistry - Organic Chemistry - Economy - State science - Biophysics - Geology - Cosmos - Alternative medicine - Philology - Pedagogy - Social studies - Ecology - Military History - Veterinary - Zoology - Botany - Textbook - Cribs - Abstract - Foreign languages - psy_childs - psy_theraphy - Sex and family + + History + Psychology + Cultural Science + Religious Studies + Philosophy + Politics + Business + Jurisprudence + Linguistics + Medicine + Physics + Mathematics + Chemistry + Biology + Technical + Misk Science, Education + Biochemistry + Physical chemistry + Analitic Chemistry + Organic Chemistry + Economy + State science + Biophysics + Geology + Cosmos + Alternative medicine + Philology + Pedagogy + Social studies + Ecology + Military History + Veterinary + Zoology + Botany + Textbook + Cribs + Abstract + Foreign languages + psy_childs + psy_theraphy + Sex and family - - Internet - Programming - Hardware - Software - Databases - OS & Networking - Computers: Misk - DSP + + Internet + Programming + Hardware + Software + Databases + OS & Networking + Computers: Misk + DSP - - Encyclopedias - Dictionaries - Reference - Guidebooks - Misk References - Art, Design - Guides + + Encyclopedias + Dictionaries + Reference + Guidebooks + Misk References + Art, Design + Guides - - Religion - Esoterics - Self-perfection - Religion: Other - Buddha - Christianity - Orthodoxy - Catholicism - Protestantism - Hinduism - Islam - Judaism - Astrology - Palmistry - Paganism + + Religion + Esoterics + Self-perfection + Religion: Other + Buddha + Christianity + Orthodoxy + Catholicism + Protestantism + Hinduism + Islam + Judaism + Astrology + Palmistry + Paganism - - Humor SF - Anecdote - Humor Prose - Humor Verses - Misk Humor - Comedy - Satire + + Humor SF + Anecdote + Humor Prose + Humor Verses + Misk Humor + Comedy + Satire - - Cooking - Pets - Hobbies & Crafts - Entertaining - Health - Garden - Do it yourself - Sports - Erotica, Sex - Home: Other - Collecting + + Cooking + Pets + Hobbies & Crafts + Entertaining + Health + Garden + Do it yourself + Sports + Erotica, Sex + Home: Other + Collecting - - Technical - Transport - Metallurgy - Radio - Building - Auto regulations - Architecture + + Technical + Transport + Metallurgy + Radio + Building + Auto regulations + Architecture - - Other - Notes - Periodic - Music - Cine - Theatre - Fan fiction - Unfinished - Visual Arts + + Other + Notes + Periodic + Music + Cine + Theatre + Fan fiction + Unfinished + Visual Arts - - Banking - Accounting - Global Economy - Paper Work - Corporate Culture - Personal Finance - Small Business - Marketing, PR, Adv - Real Estate - Popular Business - Industries - Job Hunting - Young-adult fiction - Management - Stock - Economics - Trade + + Banking + Accounting + Global Economy + Paper Work + Corporate Culture + Personal Finance + Small Business + Marketing, PR, Adv + Real Estate + Popular Business + Industries + Job Hunting + Young-adult fiction + Management + Stock + Economics + Trade - - Travel & Geography - Nature & Animals - Biography & Memoirs - Publicism - Criticism - Misk Nonfiction - Military docs - Science popular + + Travel & Geography + Nature & Animals + Biography & Memoirs + Publicism + Criticism + Misk Nonfiction + Military docs + Science popular - - Dramaturgy - Drama - Screenplays - Comedy - Mystery - Scenarios - Tragedy - Vaudeville + + Dramaturgy + Drama + Screenplays + Comedy + Mystery + Scenarios + Tragedy + Vaudeville - - Anecdote - Epic - Child Folklore - Riddles - Folk Songs - Folk tales - Proverbs - Folklore - Limerick + + Anecdote + Epic + Child Folklore + Riddles + Folk Songs + Folk tales + Proverbs + Folklore + Limerick - - Military - Military docs - Military History - Weapon - Military Arts - Military special - Military + + Military + Military docs + Military History + Weapon + Military Arts + Military special + Military - + diff --git a/trunk/TinyOPDS/translation.xml b/TinyOPDS/translation.xml similarity index 97% rename from trunk/TinyOPDS/translation.xml rename to TinyOPDS/translation.xml index 9200597..f88a97d 100644 --- a/trunk/TinyOPDS/translation.xml +++ b/TinyOPDS/translation.xml @@ -1,538 +1,538 @@ - - - - English - Русский - - - - Path to books folder: - Путь к книжному каталогу: - - - Monitor library changes - Следить за каталогом - - - Duplicates: - Дубликаты: - - - STOPPED - ОСТАНОВЛЕН - - - Status: - Статус - - - 0 books/min - 0 книг/мин - - - {0} books/min - {0} книг/мин - - - Rate: - Скорость: - - - Elapsed time: - Прошло времени: - - - Start time: - Стартовал в: - - - Books in database: - Всего книг в базе: - - - Books found: - Найдено книг: - - - Books processed: - Обработано книг: - - - Invalid books: - Ошибочных книг: - - - Skipped books: - Пропущено книг: - - - Start scanning - Начать сканирование - - - Server name: - Название сервера: - - - Port: - Порт: - - - Start server - Стартовать сервер - - - SCANNING - СКАНИРУЮ - - - FINISHED - ЗАКОНЧИЛ - - - Stop scanning - Остановить сканирование - - - Stop server - Остановить сервер - - - Path to the ePub converter: - Путь к ePub конвертору: - - - GUI and OPDS language: - Язык программы и OPDS: - - - Close or minimize to tray - Скрывать в трей - - - Start minimized - Стартовать минимизированным - - - Start with Windows - Стартовать вместе с Windows - - - Save log to file - Сохранять лог в файл - - - Use UPnP - Использовать UPnP - - - Scanner - Сканер - - - Server - Сервер - - - OPDS catalog - OPDS каталог - - - Miscellaneous - Разное - - - About program - О программе - - - Authentication - Авторизация - - - Exit - Выход - - - Forward port on router - Форвардить порт на роутере - - - Absolute links - Абсолютные ссылки - - - OPDS root: - Префикс OPDS: - - - Web root: - Префикс web: - - - Local OPDS URL: - Локальная ссылка OPDS: - - - External OPDS URL: - Внешняя ссылка OPDS: - - - Local web URL: - Локальная ссылка: - - - External web URL: - Внешняя ссылка: - - - Click here to download latest version of ePub converter - Кликните для закачки последней версии конвертера ePub - - - TinyOPDS server - TinyOPDS сервер - - - Project home page: - Домашняя страница: - - - Project license: - Лицензия проекта: - - - Special thanks: - Отдельное спасибо: - - - Database file name: - Файл базы данных: - - - Books by authors - Книги по авторам - - - Total authors on {0}: {1} - Всего авторов на {0}: {1} - - - Books: {0} - Книг: {0} - - - Books by author - Книги автора - - - By authors - По авторам - - - {0} books by {1} authors - {0} книг от {1} авторов - - - By series - По сериям - - - {0} books by {1} series - {0} книг в {1} сериях - - - By genres - По жанрам - - - Books grouped by genres - Книги, сгруппированные по жанрам - - - Book series - Книжные серии - - - Total series on {0}: {1} - Всего серий на {0}: {1} - - - {0} books in {1} - {0} книг в {1} - - - Books in genre «{0}» - Книги в жанре «{0}» - - - Hide window - Скрыть окно - - - Show window - Показать окно - - - Minimize window - Свернуть окно - - - Restore window - Показать окно - - - Can't find UPnP router, forwarding is not available - Не могу найти UPnP роутер, форвардинг не доступен - - - Invalid port value: value must be numeric and in range from 1 to 65535 - Неверный номер порта: значение должно быть в интервале от 1 до 65535 - - - Invalid port value. Default value 8080 will be used - Неверный номер порта. Будет использован номер 8080 - - - version {0}.{1} {2} - версия {0}.{1} {2} - - - Search authors - Поиск авторов - - - Search authors by name - Поиск авторов по имени - - - Search books - Поиск книг - - - Search books by title - Поиск книг по названию - - - Translation: - Перевод: - - - Year of publication: - Год публикации: - - - Format: - Формат: - - - Size: - Размер: - - - Series: - Серия: - - - All books by author {0} - Все книги автора {0} - - - All books by series {0} - Все книги серии {0} - - - Use HTTP basic authentication - Использовать базовую авторизацию HTTP - - - Remember authorized clients - Помнить авторизованных клиентов - - - Probably, port {0} is already in use. Please try different port value. - Вероятно, порт {0} уже используется. Попробуйте другое значение. - - - Log verbosity level - Детализация лог-файла - - - Info, warnings and errors - Информация, предупреждения и ошибки - - - Warnings and errors - Предупреждения и ошибки - - - Errors only - Только ошибки - - - Total requests: - Всего запросов: - - - Books sent: - Отдано книг: - - - Images sent: - Отдано картинок: - - - Unique clients: - Уникальных клиентов: - - - Successful logins: - Успешных входов: - - - Failed logins: - Неверных входов: - - - Banned clients: - Заблокировано: - - - Ban clients after - Блокировать клиентов после - - - failed attempts - неверных попыток - - - Check for update: - Проверять обновление: - - - Never - Никогда - - - Once a week - Раз в неделю - - - Once a month - Раз в месяц - - - View log file - Открыть лог-файл - - - TinyOPDS: update found - TinyOPDS: обнаружено обновление - - - Click here to download update v {0} - Кликните для загрузки обновления v {0} - - - New version {0} is available!\nWould you like to download now? - Новая версия [OK} доступна для загрузки.\nЖелаете загрузить сейчас? - - - OPDS and web root prefixes can not be the same. - Префиксы OPDS и web не могут быть одинаковыми. - - - Error - Ошибка - - - Warning - Предупреждение - - - Items per OPDS page: - Число элементов на странице OPDS: - - - Items per web page: - Число элементов на web-странице: - - - Items sort order: - Порядок сортировки: - - - Latin first - Сначала латинские - - - Cyrillic first - Сначала русские - - - "New books" check period: - Период для "новых книг": - - - one week - неделя - - - two weeks - две недели - - - three weeks - три недели - - - month - месяц - - - month and half - полтора месяца - - - two month - два месяца - - - three month - три месяца - - - "Low memory" model (do not load book descriptions) - Режим "экономии памяти" (не загружать сразу описания книг) - - - New books (by date added) - Новые книги (по дате добавления) - - - {0} new books ordered by date - {0} новых книг, упорядоченных по дате - - - New books (alphabetically) - Новые книги (в алфавитном порядке) - - - {0} new books ordered alphabetically - {0} новых книг, упорядоченных по алфавиту - - - Use authors aliases - Использовать алиасы для авторов - - - - - - - - - - - + + + + English + Русский + + + + Path to books folder: + Путь к книжному каталогу: + + + Monitor library changes + Следить за каталогом + + + Duplicates: + Дубликаты: + + + STOPPED + ОСТАНОВЛЕН + + + Status: + Статус + + + 0 books/min + 0 книг/мин + + + {0} books/min + {0} книг/мин + + + Rate: + Скорость: + + + Elapsed time: + Прошло времени: + + + Start time: + Стартовал в: + + + Books in database: + Всего книг в базе: + + + Books found: + Найдено книг: + + + Books processed: + Обработано книг: + + + Invalid books: + Ошибочных книг: + + + Skipped books: + Пропущено книг: + + + Start scanning + Начать сканирование + + + Server name: + Название сервера: + + + Port: + Порт: + + + Start server + Стартовать сервер + + + SCANNING + СКАНИРУЮ + + + FINISHED + ЗАКОНЧИЛ + + + Stop scanning + Остановить сканирование + + + Stop server + Остановить сервер + + + Path to the ePub converter: + Путь к ePub конвертору: + + + GUI and OPDS language: + Язык программы и OPDS: + + + Close or minimize to tray + Скрывать в трей + + + Start minimized + Стартовать минимизированным + + + Start with Windows + Стартовать вместе с Windows + + + Save log to file + Сохранять лог в файл + + + Use UPnP + Использовать UPnP + + + Scanner + Сканер + + + Server + Сервер + + + OPDS catalog + OPDS каталог + + + Miscellaneous + Разное + + + About program + О программе + + + Authentication + Авторизация + + + Exit + Выход + + + Forward port on router + Форвардить порт на роутере + + + Absolute links + Абсолютные ссылки + + + OPDS root: + Префикс OPDS: + + + Web root: + Префикс web: + + + Local OPDS URL: + Локальная ссылка OPDS: + + + External OPDS URL: + Внешняя ссылка OPDS: + + + Local web URL: + Локальная ссылка: + + + External web URL: + Внешняя ссылка: + + + Click here to download latest version of ePub converter + Кликните для закачки последней версии конвертера ePub + + + TinyOPDS server + TinyOPDS сервер + + + Project home page: + Домашняя страница: + + + Project license: + Лицензия проекта: + + + Special thanks: + Отдельное спасибо: + + + Database file name: + Файл базы данных: + + + Books by authors + Книги по авторам + + + Total authors on {0}: {1} + Всего авторов на {0}: {1} + + + Books: {0} + Книг: {0} + + + Books by author + Книги автора + + + By authors + По авторам + + + {0} books by {1} authors + {0} книг от {1} авторов + + + By series + По сериям + + + {0} books by {1} series + {0} книг в {1} сериях + + + By genres + По жанрам + + + Books grouped by genres + Книги, сгруппированные по жанрам + + + Book series + Книжные серии + + + Total series on {0}: {1} + Всего серий на {0}: {1} + + + {0} books in {1} + {0} книг в {1} + + + Books in genre «{0}» + Книги в жанре «{0}» + + + Hide window + Скрыть окно + + + Show window + Показать окно + + + Minimize window + Свернуть окно + + + Restore window + Показать окно + + + Can't find UPnP router, forwarding is not available + Не могу найти UPnP роутер, форвардинг не доступен + + + Invalid port value: value must be numeric and in range from 1 to 65535 + Неверный номер порта: значение должно быть в интервале от 1 до 65535 + + + Invalid port value. Default value 8080 will be used + Неверный номер порта. Будет использован номер 8080 + + + version {0}.{1} {2} + версия {0}.{1} {2} + + + Search authors + Поиск авторов + + + Search authors by name + Поиск авторов по имени + + + Search books + Поиск книг + + + Search books by title + Поиск книг по названию + + + Translation: + Перевод: + + + Year of publication: + Год публикации: + + + Format: + Формат: + + + Size: + Размер: + + + Series: + Серия: + + + All books by author {0} + Все книги автора {0} + + + All books by series {0} + Все книги серии {0} + + + Use HTTP basic authentication + Использовать базовую авторизацию HTTP + + + Remember authorized clients + Помнить авторизованных клиентов + + + Probably, port {0} is already in use. Please try different port value. + Вероятно, порт {0} уже используется. Попробуйте другое значение. + + + Log verbosity level + Детализация лог-файла + + + Info, warnings and errors + Информация, предупреждения и ошибки + + + Warnings and errors + Предупреждения и ошибки + + + Errors only + Только ошибки + + + Total requests: + Всего запросов: + + + Books sent: + Отдано книг: + + + Images sent: + Отдано картинок: + + + Unique clients: + Уникальных клиентов: + + + Successful logins: + Успешных входов: + + + Failed logins: + Неверных входов: + + + Banned clients: + Заблокировано: + + + Ban clients after + Блокировать клиентов после + + + failed attempts + неверных попыток + + + Check for update: + Проверять обновление: + + + Never + Никогда + + + Once a week + Раз в неделю + + + Once a month + Раз в месяц + + + View log file + Открыть лог-файл + + + TinyOPDS: update found + TinyOPDS: обнаружено обновление + + + Click here to download update v {0} + Кликните для загрузки обновления v {0} + + + New version {0} is available!\nWould you like to download now? + Новая версия [OK} доступна для загрузки.\nЖелаете загрузить сейчас? + + + OPDS and web root prefixes can not be the same. + Префиксы OPDS и web не могут быть одинаковыми. + + + Error + Ошибка + + + Warning + Предупреждение + + + Items per OPDS page: + Число элементов на странице OPDS: + + + Items per web page: + Число элементов на web-странице: + + + Items sort order: + Порядок сортировки: + + + Latin first + Сначала латинские + + + Cyrillic first + Сначала русские + + + "New books" check period: + Период для "новых книг": + + + one week + неделя + + + two weeks + две недели + + + three weeks + три недели + + + month + месяц + + + month and half + полтора месяца + + + two month + два месяца + + + three month + три месяца + + + "Low memory" model (do not load book descriptions) + Режим "экономии памяти" (не загружать сразу описания книг) + + + New books (by date added) + Новые книги (по дате добавления) + + + {0} new books ordered by date + {0} новых книг, упорядоченных по дате + + + New books (alphabetically) + Новые книги (в алфавитном порядке) + + + {0} new books ordered alphabetically + {0} новых книг, упорядоченных по алфавиту + + + Use authors aliases + Использовать алиасы для авторов + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/TinyOPDS/xml2html.xsl b/TinyOPDS/xml2html.xsl similarity index 96% rename from trunk/TinyOPDS/xml2html.xsl rename to TinyOPDS/xml2html.xsl index f3f5b43..d712949 100644 --- a/trunk/TinyOPDS/xml2html.xsl +++ b/TinyOPDS/xml2html.xsl @@ -1,177 +1,177 @@ - - - - - - - - - - -

TinyOPDS

- - - - - - - - - - - - -

- -

- -
- - - -

- -

- -
- - - -

- -

- -
- - -
- -
+ + + + + + + + + + +

TinyOPDS

+ + + + + + + + + + + + +

+ +

+ +
+ + + +

+ +

+ +
+ + + +

+ +

+ +
+ + +
+ +
diff --git a/trunk/TinyOPDSConsole/App.config b/TinyOPDSConsole/App.config similarity index 96% rename from trunk/TinyOPDSConsole/App.config rename to TinyOPDSConsole/App.config index 337fe97..70e9998 100644 --- a/trunk/TinyOPDSConsole/App.config +++ b/TinyOPDSConsole/App.config @@ -1,6 +1,6 @@ - - - - - - + + + + + + diff --git a/trunk/TinyOPDSConsole/Program.cs b/TinyOPDSConsole/Program.cs similarity index 97% rename from trunk/TinyOPDSConsole/Program.cs rename to TinyOPDSConsole/Program.cs index a19c0a0..a92e06d 100644 --- a/trunk/TinyOPDSConsole/Program.cs +++ b/TinyOPDSConsole/Program.cs @@ -1,599 +1,599 @@ -/*********************************************************** - * This file is a part of TinyOPDS server project - * - * Copyright (c) 2013 SeNSSoFT - * - * This code is licensed under the Microsoft Public License, - * see http://tinyopds.codeplex.com/license for the details. - * - * This is a console server/service application - * - ************************************************************/ - -using System; -using System.IO; -using System.IO.Compression; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Net; -using System.ServiceProcess; -using System.Threading; -using System.Threading.Tasks; -using System.Reflection; -using System.Diagnostics; - -using TinyOPDS; -using TinyOPDS.Data; -using TinyOPDS.Scanner; -using TinyOPDS.OPDS; -using TinyOPDS.Server; -using UPnP; - -namespace TinyOPDSConsole -{ - class Program : ServiceBase - { - private static readonly string _exePath = Assembly.GetExecutingAssembly().Location; - private const string SERVICE_NAME = "TinyOPDSSvc"; - private const string SERVICE_DISPLAY_NAME = "TinyOPDS service"; - private const string SERVICE_DESCRIPTION = "Simple, fast and portable OPDS service and HTTP server"; - private const string _urlTemplate = "http://{0}:{1}/{2}"; - - private static OPDSServer _server; - private static Thread _serverThread; - private static FileScanner _scanner; - private static Watcher _watcher; - private static DateTime _scanStartTime; - private static UPnPController _upnpController = new UPnPController(); - private static Timer _upnpRefreshTimer = null; - - #region Statistical information - private static int _fb2Count, _epubCount, _skippedFiles, _invalidFiles, _duplicates; - #endregion - - #region Startup, command line processing and service overrides - - /// - /// Extra assembly loader - /// - /// - /// - /// - static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - Assembly asm = Assembly.GetExecutingAssembly(); - String resourceName = asm.GetName().Name + ".Libs." + new AssemblyName(args.Name).Name + ".dll.gz"; - using (var stream = asm.GetManifestResourceStream(resourceName)) - { - if (stream != null) - { - using (MemoryStream memStream = new MemoryStream()) - { - GZipStream decompress = new GZipStream(stream, CompressionMode.Decompress); - decompress.CopyTo(memStream); - return Assembly.Load(memStream.GetBuffer()); - } - } - else return null; - } - } - - /// - /// Process unhandled exceptions - /// - /// - /// - static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs args) - { - if (args != null) - { - Exception e = (Exception)args.ExceptionObject; - if (e != null) - { - Log.WriteLine(LogLevel.Error, "{2}: {0}\nStack trace: {1}", e.Message, e.StackTrace, args.IsTerminating ? "Fatal exception" : "Unhandled exception"); - } - else - { - Log.WriteLine(LogLevel.Error, "Unhandled exception, args.ExceptionObject is null"); - } - } - else - { - Log.WriteLine(LogLevel.Error, "Unhandled exception, args is null"); - } - } - - static int Main(string[] args) - { - AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; - AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; - Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; - - if (Utils.IsLinux || System.Environment.UserInteractive) - { - // Add Ctrl+c handler - Console.CancelKeyPress += (sender, eventArgs) => - { - eventArgs.Cancel = true; - StopServer(); - - if (_scanner != null) - { - _scanner.Stop(); - Library.Save(); - Console.WriteLine("\nScanner interruped by user."); - Log.WriteLine("Directory scanner stopped"); - } - }; - - // On Linux, we need clear console (terminal) window first - if (Utils.IsLinux) Console.Write("\u001b[1J\u001b[0;0H"); - Console.WriteLine("TinyOPDS console, {0}, copyright (c) 2013 SeNSSoFT", string.Format(Localizer.Text("version {0}.{1} {2}"), Utils.Version.Major, Utils.Version.Minor, Utils.Version.Major == 0 ? " (beta)" : "")); - - if (args.Length > 0) - { - switch (args[0].ToLower()) - { - // Install & run service command - case "install": - { - if (Utils.IsElevated) - { - try - { - TinyOPDS.ServiceInstaller.InstallAndStart(SERVICE_NAME, SERVICE_DISPLAY_NAME, _exePath, SERVICE_DESCRIPTION); - Console.WriteLine(SERVICE_DISPLAY_NAME + " installed"); - } - catch (Exception e) - { - Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to install with exception: \"{0}\"", e.Message); - return (-1); - } - } - else - { - // Re-run app with elevated privileges - if (RunElevated("install")) Console.WriteLine(SERVICE_DISPLAY_NAME + " installed"); - else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to install"); - } - return (0); - } - - // Uninstall service command - case "uninstall": - { - if (!TinyOPDS.ServiceInstaller.ServiceIsInstalled(SERVICE_NAME)) - { - Console.WriteLine(SERVICE_DISPLAY_NAME + " is not installed"); - return (-1); - } - - if (Utils.IsElevated) - { - try - { - TinyOPDS.ServiceInstaller.Uninstall(SERVICE_NAME); - Console.WriteLine(SERVICE_DISPLAY_NAME + " uninstalled"); - - // Let's close service process (except ourselves) - Process[] localByName = Process.GetProcessesByName("TinyOPDSConsole"); - foreach (Process p in localByName) - { - // Don't kill ourselves! - if (!p.StartInfo.Arguments.Contains("uninstall")) p.Kill(); - } - } - catch (Exception e) - { - Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to uninstall with exception: \"{0}\"", e.Message); - return (-1); - } - } - else - { - // Re-run app with elevated privileges - if (RunElevated("uninstall")) Console.WriteLine(SERVICE_DISPLAY_NAME + " uninstalled"); - else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to uninstall"); - } - return (0); - } - - // Start service command - case "start": - { - if (!Utils.IsLinux && TinyOPDS.ServiceInstaller.ServiceIsInstalled(SERVICE_NAME)) - { - if (Utils.IsElevated) - { - try - { - TinyOPDS.ServiceInstaller.StartService(SERVICE_NAME); - Console.WriteLine(SERVICE_DISPLAY_NAME + " started"); - } - catch (Exception e) - { - Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to start with exception: \"{0}\"", e.Message); - return (-1); - } - } - else - { - // Re-run app with elevated privileges - if (RunElevated("start")) Console.WriteLine(SERVICE_DISPLAY_NAME + " started"); - else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to start"); - } - } - else StartServer(); - return (0); - } - - // Stop service command - case "stop": - { - if (!TinyOPDS.ServiceInstaller.ServiceIsInstalled(SERVICE_NAME)) - { - Console.WriteLine(SERVICE_DISPLAY_NAME + " is not installed"); - return (-1); - } - - if (Utils.IsElevated) - { - try - { - TinyOPDS.ServiceInstaller.StopService(SERVICE_NAME); - Console.WriteLine(SERVICE_DISPLAY_NAME + " stopped"); - } - catch (Exception e) - { - Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to stop with exception: \"{0}\"", e.Message); - return (-1); - } - } - else - { - // Re-run app with elevated privileges - if (RunElevated("stop")) Console.WriteLine(SERVICE_DISPLAY_NAME + " stopped"); - else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to stop"); - } - return (0); - } - - case "scan": - { - ScanFolder(); - return (0); - } - - case "encred": - { - if ((args.Length - 1) % 2 == 0) - { - string s = string.Empty; - for (int i = 0; i < (args.Length - 1) / 2; i++) s += args[(i * 2) + 1] + ":" + args[(i * 2) + 2] + ";"; - Console.WriteLine(Crypt.EncryptStringAES(s, _urlTemplate)); - } - else - { - Console.WriteLine("To encode credentials, please provide additional parameters: user1 password1 user2 password2 ..."); - } - return (0); - } - - case "encpath": - { - if (args.Length > 1) - { - string libName = Utils.CreateGuid(Utils.IsoOidNamespace, args[1].SanitizePathName()).ToString() + ".db"; - Console.WriteLine("Library name for the path \"{0}\" is: {1}", args[1], libName); - } - else - { - Console.WriteLine("Please provide a path to the library files (without closing slash), for example: TinyOPDSConsole.exe \"C:\\My Documents\\My ebooks\""); - } - return (0); - } - } - } - - bool l = Utils.IsLinux; - Console.WriteLine("Use: TinyOPDSConsole.exe [command], where [command] is \n\n" + - (l ? "" : "\t install \t - install and run TinyOPDS service\n") + - (l ? "" : "\t uninstall \t - uninstall TinyOPDS service\n") + - "\t start \t\t - start service\n" + - (l ? "" : "\t stop \t\t - stop service\n") + - "\t scan \t\t - scan book directory\n" + - "\t encred usr pwd\t - encode credentials\n" + - "\t encpath path\t - get library file name from path\n\n" + - "For more info please visit https://tinyopds.codeplex.com"); - } - else - { - if (!Utils.IsLinux) - { - ServiceBase[] ServicesToRun; - ServicesToRun = new ServiceBase[] { new Program() }; - ServiceBase.Run(ServicesToRun); - } - } - - return (0); - } - - private static bool RunElevated(string param) - { - var info = new ProcessStartInfo(_exePath, param) - { - Verb = "runas", // indicates to elevate privileges - WindowStyle = ProcessWindowStyle.Hidden, - }; - var process = new Process - { - EnableRaisingEvents = true, // enable WaitForExit() - StartInfo = info - }; - process.Start(); - process.WaitForExit(); - return process.ExitCode == 0; - } - - protected override void OnStart(string[] args) - { - StartServer(); - } - - protected override void OnStop() - { - StopServer(); - } - - #endregion - - #region OPDS server routines - - /// - /// - /// - private static void StartServer() - { - // Init log file settings - Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; - - // Init localization service - Localizer.Init(); - Localizer.Language = TinyOPDS.Properties.Settings.Default.Language; - - // Create timer for periodical refresh UPnP forwarding - _upnpRefreshTimer = new Timer(UpdateUPnPForwarding, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); - - // Create file watcher - _watcher = new Watcher(Library.LibraryPath); - _watcher.OnBookAdded += (object sender, BookAddedEventArgs e) => - { - if (e.BookType == BookType.FB2) _fb2Count++; else _epubCount++; - Log.WriteLine(LogLevel.Info, "Added: \"{0}\"", e.BookPath); - }; - _watcher.OnInvalidBook += (_, __) => - { - _invalidFiles++; - }; - _watcher.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => - { - _skippedFiles = _e.Count; - }; - - _watcher.OnBookDeleted += (object sender, BookDeletedEventArgs e) => - { - Log.WriteLine(LogLevel.Info, "Deleted: \"{0}\"", e.BookPath); - }; - _watcher.IsEnabled = false; - - _upnpController.DiscoverCompleted += _upnpController_DiscoverCompleted; - _upnpController.DiscoverAsync(TinyOPDS.Properties.Settings.Default.UseUPnP); - - Library.LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); - Library.LibraryLoaded += (_, __) => - { - _watcher.DirectoryToWatch = Library.LibraryPath; - _watcher.IsEnabled = TinyOPDS.Properties.Settings.Default.WatchLibrary; - }; - - // Load saved credentials - try - { - HttpProcessor.Credentials.Clear(); - string[] pairs = Crypt.DecryptStringAES(TinyOPDS.Properties.Settings.Default.Credentials, _urlTemplate).Split(';'); - foreach (string pair in pairs) - { - string[] cred = pair.Split(':'); - if (cred.Length == 2) HttpProcessor.Credentials.Add(new Credential(cred[0], cred[1])); - } - } - catch { } - - // Create and start HTTP server - HttpProcessor.AuthorizedClients.Clear(); - HttpProcessor.BannedClients.Clear(); - _server = new OPDSServer(_upnpController.LocalIP, int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort)); - - _serverThread = new Thread(new ThreadStart(_server.Listen)); - _serverThread.Priority = ThreadPriority.BelowNormal; - _serverThread.Start(); - _server.ServerReady.WaitOne(TimeSpan.FromMilliseconds(500)); - if (!_server.IsActive) - { - if (_server.ServerException != null) - { - if (_server.ServerException is System.Net.Sockets.SocketException) - { - string msg = string.Format("Probably, port {0} is already in use. Please try the different port.", TinyOPDS.Properties.Settings.Default.ServerPort); - Console.WriteLine(msg); - Log.WriteLine(msg); - } - else - { - Console.WriteLine(_server.ServerException.Message); - Log.WriteLine(LogLevel.Error, _server.ServerException.Message); - } - - StopServer(); - } - } - else - { - Log.WriteLine("HTTP server started"); - if (Utils.IsLinux || System.Environment.UserInteractive) - { - Console.WriteLine("Server is running... Press to shutdown server."); - while (_server != null && _server.IsActive) Thread.Sleep(500); - } - } - } - - /// - /// - /// - private static void StopServer() - { - if (_upnpRefreshTimer != null) - { - _upnpRefreshTimer.Dispose(); - } - - if (_server != null) - { - _server.StopServer(); - _serverThread = null; - _server = null; - Log.WriteLine("HTTP server stopped"); - } - - if (_watcher != null) - { - _watcher.IsEnabled = false; - _watcher = null; - } - - if (_upnpController != null) - { - if (_upnpController.UPnPReady) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); - Log.WriteLine("Port {0} closed", port); - } - _upnpController.DiscoverCompleted -= _upnpController_DiscoverCompleted; - _upnpController.Dispose(); - } - } - - private static void _upnpController_DiscoverCompleted(object sender, EventArgs e) - { - if (_upnpController != null && _upnpController.UPnPReady) - { - if (TinyOPDS.Properties.Settings.Default.OpenNATPort) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); - Log.WriteLine("Port {0} forwarded by UPnP", port); - } - } - } - - private static void UpdateUPnPForwarding(Object state) - { - if (TinyOPDS.Properties.Settings.Default.UseUPnP) - { - if (_server != null && _server.IsActive && _server.IsIdle && _upnpController != null && _upnpController.UPnPReady) - { - if (!_upnpController.Discovered) - { - _upnpController.DiscoverAsync(true); - } - else if (TinyOPDS.Properties.Settings.Default.OpenNATPort && _upnpController.UPnPReady) - { - int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); - _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); - } - } - } - } - - private static void ScanFolder() - { - // Init log file settings - Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; - - _scanner = new FileScanner(); - _scanner.OnBookFound += scanner_OnBookFound; - _scanner.OnInvalidBook += (_, __) => { _invalidFiles++; }; - _scanner.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => - { - _skippedFiles = _e.Count; - UpdateInfo(); - }; - _scanner.OnScanCompleted += (_, __) => - { - UpdateInfo(true); - Log.WriteLine("Directory scanner completed"); - }; - _fb2Count = _epubCount = _skippedFiles = _invalidFiles = _duplicates = 0; - _scanStartTime = DateTime.Now; - _scanner.Start(Library.LibraryPath); - Console.WriteLine("Scanning directory {0}", Library.LibraryPath); - Log.WriteLine("Directory scanner started"); - UpdateInfo(); - while (_scanner != null && _scanner.Status == FileScannerStatus.SCANNING) Thread.Sleep(500); - // Save library in the main thread - Library.Save(); - } - - static void scanner_OnBookFound(object sender, BookFoundEventArgs e) - { - if (Library.Add(e.Book)) - { - if (e.Book.BookType == BookType.FB2) _fb2Count++; else _epubCount++; - } - else _duplicates++; - if (Library.Count % 500 == 0) Library.Save(); - if (Library.Count % 20000 == 0) GC.Collect(); - UpdateInfo(); - } - - private static void UpdateInfo(bool IsScanFinished = false) - { - int totalBooksProcessed = _fb2Count + _epubCount + _skippedFiles + _invalidFiles + _duplicates; - - if (IsScanFinished || totalBooksProcessed % 10 == 0) - { - TimeSpan dt = DateTime.Now.Subtract(_scanStartTime); - string rate = (dt.TotalSeconds) > 0 ? string.Format("{0:0.} books/min", totalBooksProcessed / dt.TotalSeconds * 60) : "---"; - - string info = string.Format("Elapsed time: {1}, rate: {2}, found fb2: {3}, epub: {4}, skipped: {5}, dups: {6}, invalid: {7}, total: {8} ", - _scanStartTime.ToString(@"hh\:mm\:ss"), - dt.ToString(@"hh\:mm\:ss"), - rate, - _fb2Count, - _epubCount, - _skippedFiles, - _duplicates, - _invalidFiles, - totalBooksProcessed); - - if (!Utils.IsLinux) - { - Console.Write(info + "\r"); - float dy = (float)info.Length / (float)Console.WindowWidth; - Console.SetCursorPosition(0, Console.CursorTop - (int)dy); - } - // For Linux we should use ANSI escape sequences to control cursor - else - { - Console.Write("\u001b[s" + info + "\u001b[u"); - } - } - } - - #endregion - } -} +/*********************************************************** + * This file is a part of TinyOPDS server project + * + * Copyright (c) 2013 SeNSSoFT + * + * This code is licensed under the Microsoft Public License, + * see http://tinyopds.codeplex.com/license for the details. + * + * This is a console server/service application + * + ************************************************************/ + +using System; +using System.IO; +using System.IO.Compression; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Net; +using System.ServiceProcess; +using System.Threading; +using System.Threading.Tasks; +using System.Reflection; +using System.Diagnostics; + +using TinyOPDS; +using TinyOPDS.Data; +using TinyOPDS.Scanner; +using TinyOPDS.OPDS; +using TinyOPDS.Server; +using UPnP; + +namespace TinyOPDSConsole +{ + class Program : ServiceBase + { + private static readonly string _exePath = Assembly.GetExecutingAssembly().Location; + private const string SERVICE_NAME = "TinyOPDSSvc"; + private const string SERVICE_DISPLAY_NAME = "TinyOPDS service"; + private const string SERVICE_DESCRIPTION = "Simple, fast and portable OPDS service and HTTP server"; + private const string _urlTemplate = "http://{0}:{1}/{2}"; + + private static OPDSServer _server; + private static Thread _serverThread; + private static FileScanner _scanner; + private static Watcher _watcher; + private static DateTime _scanStartTime; + private static UPnPController _upnpController = new UPnPController(); + private static Timer _upnpRefreshTimer = null; + + #region Statistical information + private static int _fb2Count, _epubCount, _skippedFiles, _invalidFiles, _duplicates; + #endregion + + #region Startup, command line processing and service overrides + + /// + /// Extra assembly loader + /// + /// + /// + /// + static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + Assembly asm = Assembly.GetExecutingAssembly(); + String resourceName = asm.GetName().Name + ".Libs." + new AssemblyName(args.Name).Name + ".dll.gz"; + using (var stream = asm.GetManifestResourceStream(resourceName)) + { + if (stream != null) + { + using (MemoryStream memStream = new MemoryStream()) + { + GZipStream decompress = new GZipStream(stream, CompressionMode.Decompress); + decompress.CopyTo(memStream); + return Assembly.Load(memStream.GetBuffer()); + } + } + else return null; + } + } + + /// + /// Process unhandled exceptions + /// + /// + /// + static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs args) + { + if (args != null) + { + Exception e = (Exception)args.ExceptionObject; + if (e != null) + { + Log.WriteLine(LogLevel.Error, "{2}: {0}\nStack trace: {1}", e.Message, e.StackTrace, args.IsTerminating ? "Fatal exception" : "Unhandled exception"); + } + else + { + Log.WriteLine(LogLevel.Error, "Unhandled exception, args.ExceptionObject is null"); + } + } + else + { + Log.WriteLine(LogLevel.Error, "Unhandled exception, args is null"); + } + } + + static int Main(string[] args) + { + AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; + + if (Utils.IsLinux || System.Environment.UserInteractive) + { + // Add Ctrl+c handler + Console.CancelKeyPress += (sender, eventArgs) => + { + eventArgs.Cancel = true; + StopServer(); + + if (_scanner != null) + { + _scanner.Stop(); + Library.Save(); + Console.WriteLine("\nScanner interruped by user."); + Log.WriteLine("Directory scanner stopped"); + } + }; + + // On Linux, we need clear console (terminal) window first + if (Utils.IsLinux) Console.Write("\u001b[1J\u001b[0;0H"); + Console.WriteLine("TinyOPDS console, {0}, copyright (c) 2013 SeNSSoFT", string.Format(Localizer.Text("version {0}.{1} {2}"), Utils.Version.Major, Utils.Version.Minor, Utils.Version.Major == 0 ? " (beta)" : "")); + + if (args.Length > 0) + { + switch (args[0].ToLower()) + { + // Install & run service command + case "install": + { + if (Utils.IsElevated) + { + try + { + TinyOPDS.ServiceInstaller.InstallAndStart(SERVICE_NAME, SERVICE_DISPLAY_NAME, _exePath, SERVICE_DESCRIPTION); + Console.WriteLine(SERVICE_DISPLAY_NAME + " installed"); + } + catch (Exception e) + { + Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to install with exception: \"{0}\"", e.Message); + return (-1); + } + } + else + { + // Re-run app with elevated privileges + if (RunElevated("install")) Console.WriteLine(SERVICE_DISPLAY_NAME + " installed"); + else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to install"); + } + return (0); + } + + // Uninstall service command + case "uninstall": + { + if (!TinyOPDS.ServiceInstaller.ServiceIsInstalled(SERVICE_NAME)) + { + Console.WriteLine(SERVICE_DISPLAY_NAME + " is not installed"); + return (-1); + } + + if (Utils.IsElevated) + { + try + { + TinyOPDS.ServiceInstaller.Uninstall(SERVICE_NAME); + Console.WriteLine(SERVICE_DISPLAY_NAME + " uninstalled"); + + // Let's close service process (except ourselves) + Process[] localByName = Process.GetProcessesByName("TinyOPDSConsole"); + foreach (Process p in localByName) + { + // Don't kill ourselves! + if (!p.StartInfo.Arguments.Contains("uninstall")) p.Kill(); + } + } + catch (Exception e) + { + Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to uninstall with exception: \"{0}\"", e.Message); + return (-1); + } + } + else + { + // Re-run app with elevated privileges + if (RunElevated("uninstall")) Console.WriteLine(SERVICE_DISPLAY_NAME + " uninstalled"); + else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to uninstall"); + } + return (0); + } + + // Start service command + case "start": + { + if (!Utils.IsLinux && TinyOPDS.ServiceInstaller.ServiceIsInstalled(SERVICE_NAME)) + { + if (Utils.IsElevated) + { + try + { + TinyOPDS.ServiceInstaller.StartService(SERVICE_NAME); + Console.WriteLine(SERVICE_DISPLAY_NAME + " started"); + } + catch (Exception e) + { + Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to start with exception: \"{0}\"", e.Message); + return (-1); + } + } + else + { + // Re-run app with elevated privileges + if (RunElevated("start")) Console.WriteLine(SERVICE_DISPLAY_NAME + " started"); + else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to start"); + } + } + else StartServer(); + return (0); + } + + // Stop service command + case "stop": + { + if (!TinyOPDS.ServiceInstaller.ServiceIsInstalled(SERVICE_NAME)) + { + Console.WriteLine(SERVICE_DISPLAY_NAME + " is not installed"); + return (-1); + } + + if (Utils.IsElevated) + { + try + { + TinyOPDS.ServiceInstaller.StopService(SERVICE_NAME); + Console.WriteLine(SERVICE_DISPLAY_NAME + " stopped"); + } + catch (Exception e) + { + Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to stop with exception: \"{0}\"", e.Message); + return (-1); + } + } + else + { + // Re-run app with elevated privileges + if (RunElevated("stop")) Console.WriteLine(SERVICE_DISPLAY_NAME + " stopped"); + else Console.WriteLine(SERVICE_DISPLAY_NAME + " failed to stop"); + } + return (0); + } + + case "scan": + { + ScanFolder(); + return (0); + } + + case "encred": + { + if ((args.Length - 1) % 2 == 0) + { + string s = string.Empty; + for (int i = 0; i < (args.Length - 1) / 2; i++) s += args[(i * 2) + 1] + ":" + args[(i * 2) + 2] + ";"; + Console.WriteLine(Crypt.EncryptStringAES(s, _urlTemplate)); + } + else + { + Console.WriteLine("To encode credentials, please provide additional parameters: user1 password1 user2 password2 ..."); + } + return (0); + } + + case "encpath": + { + if (args.Length > 1) + { + string libName = Utils.CreateGuid(Utils.IsoOidNamespace, args[1].SanitizePathName()).ToString() + ".db"; + Console.WriteLine("Library name for the path \"{0}\" is: {1}", args[1], libName); + } + else + { + Console.WriteLine("Please provide a path to the library files (without closing slash), for example: TinyOPDSConsole.exe \"C:\\My Documents\\My ebooks\""); + } + return (0); + } + } + } + + bool l = Utils.IsLinux; + Console.WriteLine("Use: TinyOPDSConsole.exe [command], where [command] is \n\n" + + (l ? "" : "\t install \t - install and run TinyOPDS service\n") + + (l ? "" : "\t uninstall \t - uninstall TinyOPDS service\n") + + "\t start \t\t - start service\n" + + (l ? "" : "\t stop \t\t - stop service\n") + + "\t scan \t\t - scan book directory\n" + + "\t encred usr pwd\t - encode credentials\n" + + "\t encpath path\t - get library file name from path\n\n" + + "For more info please visit https://tinyopds.codeplex.com"); + } + else + { + if (!Utils.IsLinux) + { + ServiceBase[] ServicesToRun; + ServicesToRun = new ServiceBase[] { new Program() }; + ServiceBase.Run(ServicesToRun); + } + } + + return (0); + } + + private static bool RunElevated(string param) + { + var info = new ProcessStartInfo(_exePath, param) + { + Verb = "runas", // indicates to elevate privileges + WindowStyle = ProcessWindowStyle.Hidden, + }; + var process = new Process + { + EnableRaisingEvents = true, // enable WaitForExit() + StartInfo = info + }; + process.Start(); + process.WaitForExit(); + return process.ExitCode == 0; + } + + protected override void OnStart(string[] args) + { + StartServer(); + } + + protected override void OnStop() + { + StopServer(); + } + + #endregion + + #region OPDS server routines + + /// + /// + /// + private static void StartServer() + { + // Init log file settings + Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; + + // Init localization service + Localizer.Init(); + Localizer.Language = TinyOPDS.Properties.Settings.Default.Language; + + // Create timer for periodical refresh UPnP forwarding + _upnpRefreshTimer = new Timer(UpdateUPnPForwarding, null, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); + + // Create file watcher + _watcher = new Watcher(Library.LibraryPath); + _watcher.OnBookAdded += (object sender, BookAddedEventArgs e) => + { + if (e.BookType == BookType.FB2) _fb2Count++; else _epubCount++; + Log.WriteLine(LogLevel.Info, "Added: \"{0}\"", e.BookPath); + }; + _watcher.OnInvalidBook += (_, __) => + { + _invalidFiles++; + }; + _watcher.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => + { + _skippedFiles = _e.Count; + }; + + _watcher.OnBookDeleted += (object sender, BookDeletedEventArgs e) => + { + Log.WriteLine(LogLevel.Info, "Deleted: \"{0}\"", e.BookPath); + }; + _watcher.IsEnabled = false; + + _upnpController.DiscoverCompleted += _upnpController_DiscoverCompleted; + _upnpController.DiscoverAsync(TinyOPDS.Properties.Settings.Default.UseUPnP); + + Library.LibraryPath = TinyOPDS.Properties.Settings.Default.LibraryPath.SanitizePathName(); + Library.LibraryLoaded += (_, __) => + { + _watcher.DirectoryToWatch = Library.LibraryPath; + _watcher.IsEnabled = TinyOPDS.Properties.Settings.Default.WatchLibrary; + }; + + // Load saved credentials + try + { + HttpProcessor.Credentials.Clear(); + string[] pairs = Crypt.DecryptStringAES(TinyOPDS.Properties.Settings.Default.Credentials, _urlTemplate).Split(';'); + foreach (string pair in pairs) + { + string[] cred = pair.Split(':'); + if (cred.Length == 2) HttpProcessor.Credentials.Add(new Credential(cred[0], cred[1])); + } + } + catch { } + + // Create and start HTTP server + HttpProcessor.AuthorizedClients.Clear(); + HttpProcessor.BannedClients.Clear(); + _server = new OPDSServer(_upnpController.LocalIP, int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort)); + + _serverThread = new Thread(new ThreadStart(_server.Listen)); + _serverThread.Priority = ThreadPriority.BelowNormal; + _serverThread.Start(); + _server.ServerReady.WaitOne(TimeSpan.FromMilliseconds(500)); + if (!_server.IsActive) + { + if (_server.ServerException != null) + { + if (_server.ServerException is System.Net.Sockets.SocketException) + { + string msg = string.Format("Probably, port {0} is already in use. Please try the different port.", TinyOPDS.Properties.Settings.Default.ServerPort); + Console.WriteLine(msg); + Log.WriteLine(msg); + } + else + { + Console.WriteLine(_server.ServerException.Message); + Log.WriteLine(LogLevel.Error, _server.ServerException.Message); + } + + StopServer(); + } + } + else + { + Log.WriteLine("HTTP server started"); + if (Utils.IsLinux || System.Environment.UserInteractive) + { + Console.WriteLine("Server is running... Press to shutdown server."); + while (_server != null && _server.IsActive) Thread.Sleep(500); + } + } + } + + /// + /// + /// + private static void StopServer() + { + if (_upnpRefreshTimer != null) + { + _upnpRefreshTimer.Dispose(); + } + + if (_server != null) + { + _server.StopServer(); + _serverThread = null; + _server = null; + Log.WriteLine("HTTP server stopped"); + } + + if (_watcher != null) + { + _watcher.IsEnabled = false; + _watcher = null; + } + + if (_upnpController != null) + { + if (_upnpController.UPnPReady) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + _upnpController.DeleteForwardingRule(port, System.Net.Sockets.ProtocolType.Tcp); + Log.WriteLine("Port {0} closed", port); + } + _upnpController.DiscoverCompleted -= _upnpController_DiscoverCompleted; + _upnpController.Dispose(); + } + } + + private static void _upnpController_DiscoverCompleted(object sender, EventArgs e) + { + if (_upnpController != null && _upnpController.UPnPReady) + { + if (TinyOPDS.Properties.Settings.Default.OpenNATPort) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); + Log.WriteLine("Port {0} forwarded by UPnP", port); + } + } + } + + private static void UpdateUPnPForwarding(Object state) + { + if (TinyOPDS.Properties.Settings.Default.UseUPnP) + { + if (_server != null && _server.IsActive && _server.IsIdle && _upnpController != null && _upnpController.UPnPReady) + { + if (!_upnpController.Discovered) + { + _upnpController.DiscoverAsync(true); + } + else if (TinyOPDS.Properties.Settings.Default.OpenNATPort && _upnpController.UPnPReady) + { + int port = int.Parse(TinyOPDS.Properties.Settings.Default.ServerPort); + _upnpController.ForwardPort(port, System.Net.Sockets.ProtocolType.Tcp, "TinyOPDS server"); + } + } + } + } + + private static void ScanFolder() + { + // Init log file settings + Log.SaveToFile = TinyOPDS.Properties.Settings.Default.SaveLogToDisk; + + _scanner = new FileScanner(); + _scanner.OnBookFound += scanner_OnBookFound; + _scanner.OnInvalidBook += (_, __) => { _invalidFiles++; }; + _scanner.OnFileSkipped += (object _sender, FileSkippedEventArgs _e) => + { + _skippedFiles = _e.Count; + UpdateInfo(); + }; + _scanner.OnScanCompleted += (_, __) => + { + UpdateInfo(true); + Log.WriteLine("Directory scanner completed"); + }; + _fb2Count = _epubCount = _skippedFiles = _invalidFiles = _duplicates = 0; + _scanStartTime = DateTime.Now; + _scanner.Start(Library.LibraryPath); + Console.WriteLine("Scanning directory {0}", Library.LibraryPath); + Log.WriteLine("Directory scanner started"); + UpdateInfo(); + while (_scanner != null && _scanner.Status == FileScannerStatus.SCANNING) Thread.Sleep(500); + // Save library in the main thread + Library.Save(); + } + + static void scanner_OnBookFound(object sender, BookFoundEventArgs e) + { + if (Library.Add(e.Book)) + { + if (e.Book.BookType == BookType.FB2) _fb2Count++; else _epubCount++; + } + else _duplicates++; + if (Library.Count % 500 == 0) Library.Save(); + if (Library.Count % 20000 == 0) GC.Collect(); + UpdateInfo(); + } + + private static void UpdateInfo(bool IsScanFinished = false) + { + int totalBooksProcessed = _fb2Count + _epubCount + _skippedFiles + _invalidFiles + _duplicates; + + if (IsScanFinished || totalBooksProcessed % 10 == 0) + { + TimeSpan dt = DateTime.Now.Subtract(_scanStartTime); + string rate = (dt.TotalSeconds) > 0 ? string.Format("{0:0.} books/min", totalBooksProcessed / dt.TotalSeconds * 60) : "---"; + + string info = string.Format("Elapsed time: {1}, rate: {2}, found fb2: {3}, epub: {4}, skipped: {5}, dups: {6}, invalid: {7}, total: {8} ", + _scanStartTime.ToString(@"hh\:mm\:ss"), + dt.ToString(@"hh\:mm\:ss"), + rate, + _fb2Count, + _epubCount, + _skippedFiles, + _duplicates, + _invalidFiles, + totalBooksProcessed); + + if (!Utils.IsLinux) + { + Console.Write(info + "\r"); + float dy = (float)info.Length / (float)Console.WindowWidth; + Console.SetCursorPosition(0, Console.CursorTop - (int)dy); + } + // For Linux we should use ANSI escape sequences to control cursor + else + { + Console.Write("\u001b[s" + info + "\u001b[u"); + } + } + } + + #endregion + } +} diff --git a/trunk/TinyOPDSConsole/Properties/AssemblyInfo.cs b/TinyOPDSConsole/Properties/AssemblyInfo.cs similarity index 97% rename from trunk/TinyOPDSConsole/Properties/AssemblyInfo.cs rename to TinyOPDSConsole/Properties/AssemblyInfo.cs index c176323..953b8c5 100644 --- a/trunk/TinyOPDSConsole/Properties/AssemblyInfo.cs +++ b/TinyOPDSConsole/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TinyOPDSConsole")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TinyOPDSConsole")] -[assembly: AssemblyCopyright("Copyright © 2013")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9831531b-99f1-4a2c-8059-50b39bf76d8e")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TinyOPDSConsole")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TinyOPDSConsole")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9831531b-99f1-4a2c-8059-50b39bf76d8e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/trunk/TinyOPDSConsole/Properties/Settings.Designer.cs b/TinyOPDSConsole/Properties/Settings.Designer.cs similarity index 97% rename from trunk/TinyOPDSConsole/Properties/Settings.Designer.cs rename to TinyOPDSConsole/Properties/Settings.Designer.cs index 0242218..08c4a1f 100644 --- a/trunk/TinyOPDSConsole/Properties/Settings.Designer.cs +++ b/TinyOPDSConsole/Properties/Settings.Designer.cs @@ -1,315 +1,315 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.18046 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace TinyOPDS.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - [System.Configuration.SettingsProvider(typeof(TinyOPDS.CustomSettingsProvider))] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string LibraryPath { - get { - return ((string)(this["LibraryPath"])); - } - set { - this["LibraryPath"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("Моя домашняя библиотека")] - public string ServerName { - get { - return ((string)(this["ServerName"])); - } - set { - this["ServerName"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("8080")] - public string ServerPort { - get { - return ((string)(this["ServerPort"])); - } - set { - this["ServerPort"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool StartWithWindows { - get { - return ((bool)(this["StartWithWindows"])); - } - set { - this["StartWithWindows"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool StartMinimized { - get { - return ((bool)(this["StartMinimized"])); - } - set { - this["StartMinimized"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool CloseToTray { - get { - return ((bool)(this["CloseToTray"])); - } - set { - this["CloseToTray"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string ConvertorPath { - get { - return ((string)(this["ConvertorPath"])); - } - set { - this["ConvertorPath"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("en")] - public string Language { - get { - return ((string)(this["Language"])); - } - set { - this["Language"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool OpenNATPort { - get { - return ((bool)(this["OpenNATPort"])); - } - set { - this["OpenNATPort"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string ServiceFilesPath { - get { - return ((string)(this["ServiceFilesPath"])); - } - set { - this["ServiceFilesPath"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool SaveLogToDisk { - get { - return ((bool)(this["SaveLogToDisk"])); - } - set { - this["SaveLogToDisk"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string RootPrefix { - get { - return ((string)(this["RootPrefix"])); - } - set { - this["RootPrefix"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool WatchLibrary { - get { - return ((bool)(this["WatchLibrary"])); - } - set { - this["WatchLibrary"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool UseUPnP { - get { - return ((bool)(this["UseUPnP"])); - } - set { - this["UseUPnP"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseHTTPAuth { - get { - return ((bool)(this["UseHTTPAuth"])); - } - set { - this["UseHTTPAuth"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string Credentials { - get { - return ((string)(this["Credentials"])); - } - set { - this["Credentials"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool BanClients { - get { - return ((bool)(this["BanClients"])); - } - set { - this["BanClients"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LogLevel { - get { - return ((int)(this["LogLevel"])); - } - set { - this["LogLevel"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool RememberClients { - get { - return ((bool)(this["RememberClients"])); - } - set { - this["RememberClients"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("1")] - public int UpdatesCheck { - get { - return ((int)(this["UpdatesCheck"])); - } - set { - this["UpdatesCheck"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("2013-01-01")] - public global::System.DateTime LastCheck { - get { - return ((global::System.DateTime)(this["LastCheck"])); - } - set { - this["LastCheck"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("5")] - public decimal WrongAttemptsCount { - get { - return ((decimal)(this["WrongAttemptsCount"])); - } - set { - this["WrongAttemptsCount"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LocalInterfaceIndex { - get { - return ((int)(this["LocalInterfaceIndex"])); - } - set { - this["LocalInterfaceIndex"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool UseAbsoluteUri { - get { - return ((bool)(this["UseAbsoluteUri"])); - } - set { - this["UseAbsoluteUri"] = value; - } - } - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.18046 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace TinyOPDS.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + [System.Configuration.SettingsProvider(typeof(TinyOPDS.CustomSettingsProvider))] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string LibraryPath { + get { + return ((string)(this["LibraryPath"])); + } + set { + this["LibraryPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Моя домашняя библиотека")] + public string ServerName { + get { + return ((string)(this["ServerName"])); + } + set { + this["ServerName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("8080")] + public string ServerPort { + get { + return ((string)(this["ServerPort"])); + } + set { + this["ServerPort"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool StartWithWindows { + get { + return ((bool)(this["StartWithWindows"])); + } + set { + this["StartWithWindows"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool StartMinimized { + get { + return ((bool)(this["StartMinimized"])); + } + set { + this["StartMinimized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool CloseToTray { + get { + return ((bool)(this["CloseToTray"])); + } + set { + this["CloseToTray"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ConvertorPath { + get { + return ((string)(this["ConvertorPath"])); + } + set { + this["ConvertorPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("en")] + public string Language { + get { + return ((string)(this["Language"])); + } + set { + this["Language"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool OpenNATPort { + get { + return ((bool)(this["OpenNATPort"])); + } + set { + this["OpenNATPort"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ServiceFilesPath { + get { + return ((string)(this["ServiceFilesPath"])); + } + set { + this["ServiceFilesPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool SaveLogToDisk { + get { + return ((bool)(this["SaveLogToDisk"])); + } + set { + this["SaveLogToDisk"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string RootPrefix { + get { + return ((string)(this["RootPrefix"])); + } + set { + this["RootPrefix"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool WatchLibrary { + get { + return ((bool)(this["WatchLibrary"])); + } + set { + this["WatchLibrary"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool UseUPnP { + get { + return ((bool)(this["UseUPnP"])); + } + set { + this["UseUPnP"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseHTTPAuth { + get { + return ((bool)(this["UseHTTPAuth"])); + } + set { + this["UseHTTPAuth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Credentials { + get { + return ((string)(this["Credentials"])); + } + set { + this["Credentials"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool BanClients { + get { + return ((bool)(this["BanClients"])); + } + set { + this["BanClients"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int LogLevel { + get { + return ((int)(this["LogLevel"])); + } + set { + this["LogLevel"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool RememberClients { + get { + return ((bool)(this["RememberClients"])); + } + set { + this["RememberClients"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1")] + public int UpdatesCheck { + get { + return ((int)(this["UpdatesCheck"])); + } + set { + this["UpdatesCheck"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("2013-01-01")] + public global::System.DateTime LastCheck { + get { + return ((global::System.DateTime)(this["LastCheck"])); + } + set { + this["LastCheck"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("5")] + public decimal WrongAttemptsCount { + get { + return ((decimal)(this["WrongAttemptsCount"])); + } + set { + this["WrongAttemptsCount"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("0")] + public int LocalInterfaceIndex { + get { + return ((int)(this["LocalInterfaceIndex"])); + } + set { + this["LocalInterfaceIndex"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool UseAbsoluteUri { + get { + return ((bool)(this["UseAbsoluteUri"])); + } + set { + this["UseAbsoluteUri"] = value; + } + } + } +} diff --git a/trunk/TinyOPDSConsole/Properties/Settings.settings b/TinyOPDSConsole/Properties/Settings.settings similarity index 97% rename from trunk/TinyOPDSConsole/Properties/Settings.settings rename to TinyOPDSConsole/Properties/Settings.settings index a162b81..61c1d1b 100644 --- a/trunk/TinyOPDSConsole/Properties/Settings.settings +++ b/TinyOPDSConsole/Properties/Settings.settings @@ -1,78 +1,78 @@ - - - - - - - - - Моя домашняя библиотека - - - 8080 - - - False - - - False - - - False - - - - - - en - - - False - - - - - - False - - - - - - False - - - True - - - False - - - - - - False - - - 0 - - - False - - - 1 - - - 2013-01-01 - - - 5 - - - 0 - - - False - - + + + + + + + + + Моя домашняя библиотека + + + 8080 + + + False + + + False + + + False + + + + + + en + + + False + + + + + + False + + + + + + False + + + True + + + False + + + + + + False + + + 0 + + + False + + + 1 + + + 2013-01-01 + + + 5 + + + 0 + + + False + + \ No newline at end of file diff --git a/trunk/TinyOPDS/Properties/app.manifest b/TinyOPDSConsole/Properties/app.manifest similarity index 98% rename from trunk/TinyOPDS/Properties/app.manifest rename to TinyOPDSConsole/Properties/app.manifest index 8df6208..2856403 100644 --- a/trunk/TinyOPDS/Properties/app.manifest +++ b/TinyOPDSConsole/Properties/app.manifest @@ -1,47 +1,47 @@ - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/TinyOPDSConsole/TinyOPDSConsole.csproj b/TinyOPDSConsole/TinyOPDSConsole.csproj similarity index 97% rename from trunk/TinyOPDSConsole/TinyOPDSConsole.csproj rename to TinyOPDSConsole/TinyOPDSConsole.csproj index 008e8fe..8e5dcd3 100644 --- a/trunk/TinyOPDSConsole/TinyOPDSConsole.csproj +++ b/TinyOPDSConsole/TinyOPDSConsole.csproj @@ -1,270 +1,270 @@ - - - - - Debug - AnyCPU - {5A92FA9B-B91C-48F4-9488-77103868D226} - Exe - Properties - TinyOPDSConsole - TinyOPDSConsole - v4.0 - 512 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - Svn - Svn - Svn - SubversionScc - Client - - - AnyCPU - true - full - false - bin\Debug\ - TRACE;DEBUG;CONSOLE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE;CONSOLE - prompt - 4 - - - - False - ..\TinyOPDS\Libs\eBdb.EpubReader.dll - - - False - ..\TinyOPDS\Libs\FB2Library.dll - - - False - ..\TinyOPDS\Libs\Ionic.Zip.Reduced.dll - - - - - - - - - - - - - - - Data\Book.cs - - - Data\CoverImage.cs - - - Data\Genre.cs - - - Data\ImagesCache.cs - - - Data\Library.cs - - - Misc\Crypt.cs - - - Misc\CustomSettingsProvider.cs - - - Misc\Localizer.cs - - - Misc\Log.cs - - - Misc\OPDSComparer.cs - - - Misc\ProcessHelper.cs - - - Misc\ServiceTools.cs - - - Misc\StringUtils.cs - - - Misc\UPnP.cs - - - Misc\Utils.cs - - - OPDS\AuthorsCatalog.cs - - - OPDS\BooksCatalog.cs - - - OPDS\GenresCatalog.cs - - - OPDS\Links.cs - - - OPDS\Namespaces.cs - - - OPDS\NewBooksCatalog.cs - - - OPDS\OpenSearch.cs - - - OPDS\RootCatalog.cs - - - OPDS\SequencesCatalog.cs - - - Parsers\BookParser.cs - - - Parsers\ePubParser.cs - - - Parsers\fb2Parser.cs - - - Scanner\FileScanner.cs - - - Scanner\ScannerEvents.cs - - - Scanner\Watcher.cs - - - Scanner\ZipScanner.cs - - - Server\HttpServer.cs - - - Server\OPDSServer.cs - - - SgmlReader\SgmlParser.cs - - - SgmlReader\SgmlReader.cs - - - Component - - - - a_aliases.txt.gz - - - Resources\fb2.dtd - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - Libs\eBdb.EpubReader.dll.gz - - - Libs\FB2Library.dll.gz - - - Libs\Ionic.Zip.Reduced.dll.gz - - - - - - - - xml2html.xsl - Designer - - - translation.xml - - - genres.xml - - - Libs\eBdb.EpubReader.dll - - - Libs\FB2Library.dll - - - Libs\Ionic.Zip.Reduced.dll - - - - - False - Microsoft .NET Framework 4.5 %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - false - - - - - - if exist $(SolutionDir)\Sign\\ copy $(TargetPath) $(SolutionDir)\Sign - - - + + + + + Debug + AnyCPU + {5A92FA9B-B91C-48F4-9488-77103868D226} + Exe + Properties + TinyOPDSConsole + TinyOPDSConsole + v4.0 + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + Svn + Svn + Svn + SubversionScc + Client + + + AnyCPU + true + full + false + bin\Debug\ + TRACE;DEBUG;CONSOLE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE;CONSOLE + prompt + 4 + + + + False + ..\TinyOPDS\Libs\eBdb.EpubReader.dll + + + False + ..\TinyOPDS\Libs\FB2Library.dll + + + False + ..\TinyOPDS\Libs\Ionic.Zip.Reduced.dll + + + + + + + + + + + + + + + Data\Book.cs + + + Data\CoverImage.cs + + + Data\Genre.cs + + + Data\ImagesCache.cs + + + Data\Library.cs + + + Misc\Crypt.cs + + + Misc\CustomSettingsProvider.cs + + + Misc\Localizer.cs + + + Misc\Log.cs + + + Misc\OPDSComparer.cs + + + Misc\ProcessHelper.cs + + + Misc\ServiceTools.cs + + + Misc\StringUtils.cs + + + Misc\UPnP.cs + + + Misc\Utils.cs + + + OPDS\AuthorsCatalog.cs + + + OPDS\BooksCatalog.cs + + + OPDS\GenresCatalog.cs + + + OPDS\Links.cs + + + OPDS\Namespaces.cs + + + OPDS\NewBooksCatalog.cs + + + OPDS\OpenSearch.cs + + + OPDS\RootCatalog.cs + + + OPDS\SequencesCatalog.cs + + + Parsers\BookParser.cs + + + Parsers\ePubParser.cs + + + Parsers\fb2Parser.cs + + + Scanner\FileScanner.cs + + + Scanner\ScannerEvents.cs + + + Scanner\Watcher.cs + + + Scanner\ZipScanner.cs + + + Server\HttpServer.cs + + + Server\OPDSServer.cs + + + SgmlReader\SgmlParser.cs + + + SgmlReader\SgmlReader.cs + + + Component + + + + a_aliases.txt.gz + + + Resources\fb2.dtd + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + Libs\eBdb.EpubReader.dll.gz + + + Libs\FB2Library.dll.gz + + + Libs\Ionic.Zip.Reduced.dll.gz + + + + + + + + xml2html.xsl + Designer + + + translation.xml + + + genres.xml + + + Libs\eBdb.EpubReader.dll + + + Libs\FB2Library.dll + + + Libs\Ionic.Zip.Reduced.dll + + + + + False + Microsoft .NET Framework 4.5 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + if exist $(SolutionDir)\Sign\\ copy $(TargetPath) $(SolutionDir)\Sign + + + \ No newline at end of file diff --git a/releases/1.1/..svnbridge/TinyOPDS.ico b/releases/1.1/..svnbridge/TinyOPDS.ico deleted file mode 100644 index 81efb3c..0000000 --- a/releases/1.1/..svnbridge/TinyOPDS.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/releases/1.1/..svnbridge/TinyOPDS.png b/releases/1.1/..svnbridge/TinyOPDS.png deleted file mode 100644 index 81efb3c..0000000 --- a/releases/1.1/..svnbridge/TinyOPDS.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/releases/1.1/..svnbridge/TinyOPDS.v11.suo b/releases/1.1/..svnbridge/TinyOPDS.v11.suo deleted file mode 100644 index 81efb3c..0000000 --- a/releases/1.1/..svnbridge/TinyOPDS.v11.suo +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/releases/1.1/..svnbridge/donate.png b/releases/1.1/..svnbridge/donate.png deleted file mode 100644 index 81efb3c..0000000 --- a/releases/1.1/..svnbridge/donate.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/..svnbridge/a_aliases.txt.gz b/trunk/TinyOPDS/..svnbridge/a_aliases.txt.gz deleted file mode 100644 index 81efb3c..0000000 --- a/trunk/TinyOPDS/..svnbridge/a_aliases.txt.gz +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/TinyOPDS.ico b/trunk/TinyOPDS/Icons/..svnbridge/TinyOPDS.ico deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/TinyOPDS.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/authors.ico b/trunk/TinyOPDS/Icons/..svnbridge/authors.ico deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/authors.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/authors.png b/trunk/TinyOPDS/Icons/..svnbridge/authors.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/authors.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/book.png b/trunk/TinyOPDS/Icons/..svnbridge/book.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/book.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/book2.png b/trunk/TinyOPDS/Icons/..svnbridge/book2.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/book2.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/book3.png b/trunk/TinyOPDS/Icons/..svnbridge/book3.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/book3.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/books.ico b/trunk/TinyOPDS/Icons/..svnbridge/books.ico deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/books.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/books.png b/trunk/TinyOPDS/Icons/..svnbridge/books.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/books.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/favicon.ico b/trunk/TinyOPDS/Icons/..svnbridge/favicon.ico deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/favicon.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/folder.png b/trunk/TinyOPDS/Icons/..svnbridge/folder.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/folder.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/genres.ico b/trunk/TinyOPDS/Icons/..svnbridge/genres.ico deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/genres.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/genres.png b/trunk/TinyOPDS/Icons/..svnbridge/genres.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/genres.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/genres2.png b/trunk/TinyOPDS/Icons/..svnbridge/genres2.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/genres2.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Icons/..svnbridge/lib.png b/trunk/TinyOPDS/Icons/..svnbridge/lib.png deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Icons/..svnbridge/lib.png +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll b/trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll deleted file mode 100644 index 60559c8..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-streamsvn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll.gz b/trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll.gz deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/FB2Library.dll.gz +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll b/trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll deleted file mode 100644 index 60559c8..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-streamsvn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll.gz b/trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll.gz deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/Ionic.Zip.Reduced.dll.gz +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll b/trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll deleted file mode 100644 index 60559c8..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-streamsvn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll.gz b/trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll.gz deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/eBdb.EpubReader.dll.gz +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Libs/..svnbridge/gzip.exe b/trunk/TinyOPDS/Libs/..svnbridge/gzip.exe deleted file mode 100644 index 0814930..0000000 --- a/trunk/TinyOPDS/Libs/..svnbridge/gzip.exe +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file diff --git a/trunk/TinyOPDS/Resources/..svnbridge/trayIcon.ico b/trunk/TinyOPDS/Resources/..svnbridge/trayIcon.ico deleted file mode 100644 index 81efb3c..0000000 --- a/trunk/TinyOPDS/Resources/..svnbridge/trayIcon.ico +++ /dev/null @@ -1 +0,0 @@ -svn:mime-typeapplication/octet-stream \ No newline at end of file