Browse Source

Merge pull request #2931 from MediaBrowser/beta

Beta
Luke 7 năm trước cách đây
mục cha
commit
a73da532d5
100 tập tin đã thay đổi với 1444 bổ sung8979 xóa
  1. 1 1
      Emby.Dlna/ContentDirectory/ControlHandler.cs
  2. 15 15
      Emby.Dlna/Didl/DidlBuilder.cs
  3. 28 2
      Emby.Dlna/PlayTo/Device.cs
  4. 4 3
      Emby.Dlna/PlayTo/PlayToController.cs
  5. 3 3
      Emby.Dlna/Profiles/LgTvProfile.cs
  6. 3 3
      Emby.Dlna/Profiles/Xml/LG Smart TV.xml
  7. 0 9
      Emby.Drawing.ImageMagick/ImageMagickEncoder.cs
  8. 4 2
      Emby.Drawing.Skia/SkiaEncoder.cs
  9. 13 148
      Emby.Drawing/ImageProcessor.cs
  10. 104 85
      Emby.Photos/PhotoProvider.cs
  11. 20 1
      Emby.Server.Implementations/Activity/ActivityRepository.cs
  12. 51 35
      Emby.Server.Implementations/ApplicationHost.cs
  13. 17 0
      Emby.Server.Implementations/Archiving/ZipClient.cs
  14. 0 340
      Emby.Server.Implementations/Cryptography/ASN1.cs
  15. 0 207
      Emby.Server.Implementations/Cryptography/ASN1Convert.cs
  16. 0 239
      Emby.Server.Implementations/Cryptography/BitConverterLE.cs
  17. 0 109
      Emby.Server.Implementations/Cryptography/CertificateGenerator.cs
  18. 0 745
      Emby.Server.Implementations/Cryptography/CryptoConvert.cs
  19. 0 491
      Emby.Server.Implementations/Cryptography/PKCS1.cs
  20. 0 1934
      Emby.Server.Implementations/Cryptography/PKCS12.cs
  21. 0 1012
      Emby.Server.Implementations/Cryptography/PKCS7.cs
  22. 0 495
      Emby.Server.Implementations/Cryptography/PKCS8.cs
  23. 0 75
      Emby.Server.Implementations/Cryptography/PfxGenerator.cs
  24. 0 393
      Emby.Server.Implementations/Cryptography/X501Name.cs
  25. 0 153
      Emby.Server.Implementations/Cryptography/X509Builder.cs
  26. 0 563
      Emby.Server.Implementations/Cryptography/X509Certificate.cs
  27. 0 245
      Emby.Server.Implementations/Cryptography/X509CertificateBuilder.cs
  28. 0 201
      Emby.Server.Implementations/Cryptography/X509CertificateCollection.cs
  29. 0 208
      Emby.Server.Implementations/Cryptography/X509Extension.cs
  30. 0 195
      Emby.Server.Implementations/Cryptography/X509Extensions.cs
  31. 0 346
      Emby.Server.Implementations/Cryptography/X520Attributes.cs
  32. 55 38
      Emby.Server.Implementations/Data/BaseSqliteRepository.cs
  33. 20 2
      Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs
  34. 4 4
      Emby.Server.Implementations/Data/SqliteItemRepository.cs
  35. 4 17
      Emby.Server.Implementations/Dto/DtoService.cs
  36. 3 22
      Emby.Server.Implementations/Emby.Server.Implementations.csproj
  37. 1 1
      Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs
  38. 2 2
      Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs
  39. 1 9
      Emby.Server.Implementations/EntryPoints/UsageReporter.cs
  40. 41 2
      Emby.Server.Implementations/HttpServer/HttpListenerHost.cs
  41. 29 43
      Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
  42. 1 7
      Emby.Server.Implementations/IO/LibraryMonitor.cs
  43. 9 52
      Emby.Server.Implementations/Library/LibraryManager.cs
  44. 2 2
      Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
  45. 6 6
      Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
  46. 19 35
      Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
  47. 1 1
      Emby.Server.Implementations/LiveTv/LiveTvManager.cs
  48. 5 2
      Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs
  49. 10 1
      Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
  50. 153 0
      Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs
  51. 2 2
      Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs
  52. 0 81
      Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs
  53. 0 45
      Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs
  54. 1 1
      Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs
  55. 11 3
      Emby.Server.Implementations/Session/SessionManager.cs
  56. 22 2
      Emby.Server.Implementations/Social/SharingRepository.cs
  57. 7 29
      Emby.Server.Implementations/TV/TVSeriesManager.cs
  58. 1 1
      Emby.Server.Implementations/packages.config
  59. 52 0
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  60. 19 5
      MediaBrowser.Common/Updates/GithubUpdater.cs
  61. 8 0
      MediaBrowser.Controller/Channels/Channel.cs
  62. 0 2
      MediaBrowser.Controller/Drawing/IImageProcessor.cs
  63. 0 5
      MediaBrowser.Controller/Drawing/ImageHelper.cs
  64. 1 1
      MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs
  65. 8 0
      MediaBrowser.Controller/Entities/BasePluginFolder.cs
  66. 8 0
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  67. 5 0
      MediaBrowser.Controller/Entities/GameGenre.cs
  68. 8 0
      MediaBrowser.Controller/Entities/GameSystem.cs
  69. 5 0
      MediaBrowser.Controller/Entities/Genre.cs
  70. 29 0
      MediaBrowser.Controller/Entities/Photo.cs
  71. 5 0
      MediaBrowser.Controller/Entities/PhotoAlbum.cs
  72. 5 0
      MediaBrowser.Controller/Entities/User.cs
  73. 8 0
      MediaBrowser.Controller/Entities/UserView.cs
  74. 6 9
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  75. 8 0
      MediaBrowser.Controller/Entities/Video.cs
  76. 8 0
      MediaBrowser.Controller/Entities/Year.cs
  77. 0 5
      MediaBrowser.Controller/IO/StreamHelper.cs
  78. 1 1
      MediaBrowser.Controller/LiveTv/ILiveTvManager.cs
  79. 14 1
      MediaBrowser.Controller/LiveTv/ITunerHost.cs
  80. 0 87
      MediaBrowser.Controller/LiveTv/LiveStream.cs
  81. 0 1
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  82. 41 50
      MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
  83. 145 14
      MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
  84. 36 10
      MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
  85. 1 1
      MediaBrowser.Controller/TV/ITVSeriesManager.cs
  86. 2 1
      MediaBrowser.Model/Configuration/EncodingOptions.cs
  87. 3 0
      MediaBrowser.Model/Configuration/LibraryOptions.cs
  88. 2 8
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  89. 19 2
      MediaBrowser.Model/Dlna/CodecProfile.cs
  90. 136 57
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  91. 176 34
      MediaBrowser.Model/Dlna/StreamInfo.cs
  92. 2 0
      MediaBrowser.Model/IO/IZipClient.cs
  93. 1 3
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  94. 1 1
      MediaBrowser.Providers/TV/DummySeasonProvider.cs
  95. 4 4
      MediaBrowser.Providers/TV/SeasonMetadataService.cs
  96. 0 1
      MediaBrowser.Server.Mono/ImageEncoderHelper.cs
  97. 2 3
      MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj
  98. 1 2
      MediaBrowser.Server.Mono/MonoAppHost.cs
  99. 0 2
      MediaBrowser.Server.Mono/Program.cs
  100. 1 1
      MediaBrowser.Server.Mono/packages.config

+ 1 - 1
Emby.Dlna/ContentDirectory/ControlHandler.cs

@@ -1101,7 +1101,7 @@ namespace Emby.Dlna.ContentDirectory
                 StartIndex = query.StartIndex,
                 StartIndex = query.StartIndex,
                 UserId = query.User.Id.ToString("N")
                 UserId = query.User.Id.ToString("N")
 
 
-            }, new List<Folder> { (Folder)parent }, query.DtoOptions);
+            }, new List<BaseItem> { parent }, query.DtoOptions);
 
 
             return ToResult(result);
             return ToResult(result);
         }
         }

+ 15 - 15
Emby.Dlna/Didl/DidlBuilder.cs

@@ -209,8 +209,8 @@ namespace Emby.Dlna.Didl
             var targetHeight = streamInfo.TargetHeight;
             var targetHeight = streamInfo.TargetHeight;
 
 
             var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
             var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
-                streamInfo.TargetVideoCodec,
-                streamInfo.TargetAudioCodec,
+                streamInfo.TargetVideoCodec.FirstOrDefault(),
+                streamInfo.TargetAudioCodec.FirstOrDefault(),
                 targetWidth,
                 targetWidth,
                 targetHeight,
                 targetHeight,
                 streamInfo.TargetVideoBitDepth,
                 streamInfo.TargetVideoBitDepth,
@@ -353,8 +353,8 @@ namespace Emby.Dlna.Didl
             }
             }
 
 
             var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
             var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
-                streamInfo.TargetAudioCodec,
-                streamInfo.TargetVideoCodec,
+                streamInfo.TargetAudioCodec.FirstOrDefault(),
+                streamInfo.TargetVideoCodec.FirstOrDefault(),
                 streamInfo.TargetAudioBitrate,
                 streamInfo.TargetAudioBitrate,
                 targetWidth,
                 targetWidth,
                 targetHeight,
                 targetHeight,
@@ -554,7 +554,7 @@ namespace Emby.Dlna.Didl
             }
             }
 
 
             var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
             var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
-                streamInfo.TargetAudioCodec,
+                streamInfo.TargetAudioCodec.FirstOrDefault(),
                 targetChannels,
                 targetChannels,
                 targetAudioBitrate,
                 targetAudioBitrate,
                 targetSampleRate,
                 targetSampleRate,
@@ -567,7 +567,7 @@ namespace Emby.Dlna.Didl
                 : mediaProfile.MimeType;
                 : mediaProfile.MimeType;
 
 
             var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
             var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
-                streamInfo.TargetAudioCodec,
+                streamInfo.TargetAudioCodec.FirstOrDefault(),
                 targetAudioBitrate,
                 targetAudioBitrate,
                 targetSampleRate,
                 targetSampleRate,
                 targetChannels,
                 targetChannels,
@@ -1141,17 +1141,17 @@ namespace Emby.Dlna.Didl
             int? width = null;
             int? width = null;
             int? height = null;
             int? height = null;
 
 
-            try
-            {
-                var size = _imageProcessor.GetImageSize(imageInfo);
+            //try
+            //{
+            //    var size = _imageProcessor.GetImageSize(imageInfo);
 
 
-                width = Convert.ToInt32(size.Width);
-                height = Convert.ToInt32(size.Height);
-            }
-            catch
-            {
+            //    width = Convert.ToInt32(size.Width);
+            //    height = Convert.ToInt32(size.Height);
+            //}
+            //catch
+            //{
 
 
-            }
+            //}
 
 
             var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty)
             var inputFormat = (Path.GetExtension(imageInfo.Path) ?? string.Empty)
                 .TrimStart('.')
                 .TrimStart('.')

+ 28 - 2
Emby.Dlna/PlayTo/Device.cs

@@ -662,7 +662,33 @@ namespace Emby.Dlna.PlayTo
 
 
             var e = track.Element(uPnpNamespaces.items) ?? track;
             var e = track.Element(uPnpNamespaces.items) ?? track;
 
 
-            return UpnpContainer.Create(e);
+            var elementString = (string)e;
+
+            if (!string.IsNullOrWhiteSpace(elementString))
+            {
+                return UpnpContainer.Create(e);
+            }
+
+            track = result.Document.Descendants("CurrentURI").FirstOrDefault();
+
+            if (track == null)
+            {
+                return null;
+            }
+
+            e = track.Element(uPnpNamespaces.items) ?? track;
+
+            elementString = (string)e;
+
+            if (!string.IsNullOrWhiteSpace(elementString))
+            {
+                return new uBaseObject
+                {
+                    Url = elementString
+                };
+            }
+
+            return null;
         }
         }
 
 
         private async Task<Tuple<bool, uBaseObject>> GetPositionInfo()
         private async Task<Tuple<bool, uBaseObject>> GetPositionInfo()
@@ -720,7 +746,7 @@ namespace Emby.Dlna.PlayTo
 
 
             if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
             if (string.IsNullOrWhiteSpace(trackString) || string.Equals(trackString, "NOT_IMPLEMENTED", StringComparison.OrdinalIgnoreCase))
             {
             {
-                return new Tuple<bool, uBaseObject>(false, null);
+                return new Tuple<bool, uBaseObject>(true, null);
             }
             }
 
 
             XElement uPnpResponse;
             XElement uPnpResponse;

+ 4 - 3
Emby.Dlna/PlayTo/PlayToController.cs

@@ -13,6 +13,7 @@ using MediaBrowser.Model.System;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
+using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
@@ -515,7 +516,7 @@ namespace Emby.Dlna.PlayTo
             {
             {
                 return new ContentFeatureBuilder(profile)
                 return new ContentFeatureBuilder(profile)
                     .BuildAudioHeader(streamInfo.Container,
                     .BuildAudioHeader(streamInfo.Container,
-                    streamInfo.TargetAudioCodec,
+                    streamInfo.TargetAudioCodec.FirstOrDefault(),
                     streamInfo.TargetAudioBitrate,
                     streamInfo.TargetAudioBitrate,
                     streamInfo.TargetAudioSampleRate,
                     streamInfo.TargetAudioSampleRate,
                     streamInfo.TargetAudioChannels,
                     streamInfo.TargetAudioChannels,
@@ -529,8 +530,8 @@ namespace Emby.Dlna.PlayTo
             {
             {
                 var list = new ContentFeatureBuilder(profile)
                 var list = new ContentFeatureBuilder(profile)
                     .BuildVideoHeader(streamInfo.Container,
                     .BuildVideoHeader(streamInfo.Container,
-                    streamInfo.TargetVideoCodec,
-                    streamInfo.TargetAudioCodec,
+                    streamInfo.TargetVideoCodec.FirstOrDefault(),
+                    streamInfo.TargetAudioCodec.FirstOrDefault(),
                     streamInfo.TargetWidth,
                     streamInfo.TargetWidth,
                     streamInfo.TargetHeight,
                     streamInfo.TargetHeight,
                     streamInfo.TargetVideoBitDepth,
                     streamInfo.TargetVideoBitDepth,

+ 3 - 3
Emby.Dlna/Profiles/LgTvProfile.cs

@@ -55,14 +55,14 @@ namespace Emby.Dlna.Profiles
                 {
                 {
                     Container = "ts,mpegts,avi,mkv",
                     Container = "ts,mpegts,avi,mkv",
                     VideoCodec = "h264",
                     VideoCodec = "h264",
-                    AudioCodec = "aac,ac3,mp3,dca,dts",
+                    AudioCodec = "aac,ac3,eac3,mp3,dca,dts",
                     Type = DlnaProfileType.Video
                     Type = DlnaProfileType.Video
                 },
                 },
                 new DirectPlayProfile
                 new DirectPlayProfile
                 {
                 {
                     Container = "mp4,m4v",
                     Container = "mp4,m4v",
                     VideoCodec = "h264,mpeg4",
                     VideoCodec = "h264,mpeg4",
-                    AudioCodec = "aac,ac3,mp3,dca,dts",
+                    AudioCodec = "aac,ac3,eac3,mp3,dca,dts",
                     Type = DlnaProfileType.Video
                     Type = DlnaProfileType.Video
                 },
                 },
                 new DirectPlayProfile
                 new DirectPlayProfile
@@ -168,7 +168,7 @@ namespace Emby.Dlna.Profiles
                new CodecProfile
                new CodecProfile
                {
                {
                    Type = CodecType.VideoAudio,
                    Type = CodecType.VideoAudio,
-                   Codec = "ac3,aac,mp3",
+                   Codec = "ac3,eac3,aac,mp3",
 
 
                    Conditions = new[]
                    Conditions = new[]
                    {
                    {

+ 3 - 3
Emby.Dlna/Profiles/Xml/LG Smart TV.xml

@@ -35,8 +35,8 @@
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
   <XmlRootAttributes />
   <XmlRootAttributes />
   <DirectPlayProfiles>
   <DirectPlayProfiles>
-    <DirectPlayProfile container="ts,mpegts,avi,mkv" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264" type="Video" />
-    <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
+    <DirectPlayProfile container="ts,mpegts,avi,mkv" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264" type="Video" />
+    <DirectPlayProfile container="mp4,m4v" audioCodec="aac,ac3,eac3,mp3,dca,dts" videoCodec="h264,mpeg4" type="Video" />
     <DirectPlayProfile container="mp3" type="Audio" />
     <DirectPlayProfile container="mp3" type="Audio" />
     <DirectPlayProfile container="jpeg" type="Photo" />
     <DirectPlayProfile container="jpeg" type="Photo" />
   </DirectPlayProfiles>
   </DirectPlayProfiles>
@@ -71,7 +71,7 @@
       </Conditions>
       </Conditions>
       <ApplyConditions />
       <ApplyConditions />
     </CodecProfile>
     </CodecProfile>
-    <CodecProfile type="VideoAudio" codec="ac3,aac,mp3">
+    <CodecProfile type="VideoAudio" codec="ac3,eac3,aac,mp3">
       <Conditions>
       <Conditions>
         <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
         <ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
       </Conditions>
       </Conditions>

+ 0 - 9
Emby.Drawing.ImageMagick/ImageMagickEncoder.cs

@@ -148,7 +148,6 @@ namespace Emby.Drawing.ImageMagick
                     }
                     }
 
 
                     var originalImageSize = new ImageSize(originalImage.CurrentImage.Width, originalImage.CurrentImage.Height);
                     var originalImageSize = new ImageSize(originalImage.CurrentImage.Width, originalImage.CurrentImage.Height);
-                    ImageHelper.SaveImageSize(inputPath, dateModified, originalImageSize);
 
 
                     if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize))
                     if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize))
                     {
                     {
@@ -182,7 +181,6 @@ namespace Emby.Drawing.ImageMagick
                 using (var originalImage = new MagickWand(inputPath))
                 using (var originalImage = new MagickWand(inputPath))
                 {
                 {
                     var originalImageSize = new ImageSize(originalImage.CurrentImage.Width, originalImage.CurrentImage.Height);
                     var originalImageSize = new ImageSize(originalImage.CurrentImage.Width, originalImage.CurrentImage.Height);
-                    ImageHelper.SaveImageSize(inputPath, dateModified, originalImageSize);
 
 
                     var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize);
                     var newImageSize = ImageHelper.GetNewImageSize(options, originalImageSize);
 
 
@@ -343,13 +341,6 @@ namespace Emby.Drawing.ImageMagick
         {
         {
             get
             get
             {
             {
-                // too heavy. seeing crashes on RPI.
-                if (_environment.SystemArchitecture == Architecture.Arm ||
-                    _environment.SystemArchitecture == Architecture.Arm64)
-                {
-                    return false;
-                }
-
                 return true;
                 return true;
             }
             }
         }
         }

+ 4 - 2
Emby.Drawing.Skia/SkiaEncoder.cs

@@ -40,7 +40,9 @@ namespace Emby.Drawing.Skia
                     "jpeg",
                     "jpeg",
                     "jpg",
                     "jpg",
                     "png",
                     "png",
+
                     "dng",
                     "dng",
+
                     "webp",
                     "webp",
                     "gif",
                     "gif",
                     "bmp",
                     "bmp",
@@ -51,7 +53,8 @@ namespace Emby.Drawing.Skia
                     "wbmp",
                     "wbmp",
 
 
                     // working on windows at least
                     // working on windows at least
-                    "cr2"
+                    "cr2",
+                    "nef"
                 };
                 };
             }
             }
         }
         }
@@ -459,7 +462,6 @@ namespace Emby.Drawing.Skia
                 //_logger.Info("Color type {0}", bitmap.Info.ColorType);
                 //_logger.Info("Color type {0}", bitmap.Info.ColorType);
 
 
                 var originalImageSize = new ImageSize(bitmap.Width, bitmap.Height);
                 var originalImageSize = new ImageSize(bitmap.Width, bitmap.Height);
-                ImageHelper.SaveImageSize(inputPath, dateModified, originalImageSize);
 
 
                 if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize) && !autoOrient)
                 if (!options.CropWhiteSpace && options.HasDefaultOptions(inputPath, originalImageSize) && !autoOrient)
                 {
                 {

+ 13 - 148
Emby.Drawing/ImageProcessor.cs

@@ -35,11 +35,6 @@ namespace Emby.Drawing
         /// </summary>
         /// </summary>
         protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
         protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
 
 
-        /// <summary>
-        /// The _cached imaged sizes
-        /// </summary>
-        private readonly ConcurrentDictionary<Guid, ImageSize> _cachedImagedSizes;
-
         /// <summary>
         /// <summary>
         /// Gets the list of currently registered image processors
         /// Gets the list of currently registered image processors
         /// Image processors are specialized metadata providers that run after the normal ones
         /// Image processors are specialized metadata providers that run after the normal ones
@@ -75,34 +70,7 @@ namespace Emby.Drawing
             _appPaths = appPaths;
             _appPaths = appPaths;
 
 
             ImageEnhancers = new IImageEnhancer[] { };
             ImageEnhancers = new IImageEnhancer[] { };
-            _saveImageSizeTimer = timerFactory.Create(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
             ImageHelper.ImageProcessor = this;
             ImageHelper.ImageProcessor = this;
-
-            Dictionary<Guid, ImageSize> sizeDictionary;
-
-            try
-            {
-                sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile) ??
-                    new Dictionary<Guid, ImageSize>();
-            }
-            catch (FileNotFoundException)
-            {
-                // No biggie
-                sizeDictionary = new Dictionary<Guid, ImageSize>();
-            }
-            catch (IOException)
-            {
-                // No biggie
-                sizeDictionary = new Dictionary<Guid, ImageSize>();
-            }
-            catch (Exception ex)
-            {
-                logger.ErrorException("Error parsing image size cache file", ex);
-
-                sizeDictionary = new Dictionary<Guid, ImageSize>();
-            }
-
-            _cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
         }
         }
 
 
         public IImageEncoder ImageEncoder
         public IImageEncoder ImageEncoder
@@ -133,7 +101,6 @@ namespace Emby.Drawing
                     "aiff",
                     "aiff",
                     "cr2",
                     "cr2",
                     "crw",
                     "crw",
-                    "dng", 
 
 
                     // Remove until supported
                     // Remove until supported
                     //"nef", 
                     //"nef", 
@@ -275,15 +242,15 @@ namespace Emby.Drawing
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
                 return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
             }
 
 
-            ImageSize? originalImageSize = GetSavedImageSize(originalImagePath, dateModified);
-            if (originalImageSize.HasValue && options.HasDefaultOptions(originalImagePath, originalImageSize.Value) && !autoOrient)
-            {
-                // Just spit out the original file if all the options are default
-                _logger.Info("Returning original image {0}", originalImagePath);
-                return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
-            }
+            //ImageSize? originalImageSize = GetSavedImageSize(originalImagePath, dateModified);
+            //if (originalImageSize.HasValue && options.HasDefaultOptions(originalImagePath, originalImageSize.Value) && !autoOrient)
+            //{
+            //    // Just spit out the original file if all the options are default
+            //    _logger.Info("Returning original image {0}", originalImagePath);
+            //    return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+            //}
 
 
-            var newSize = ImageHelper.GetNewImageSize(options, originalImageSize);
+            var newSize = ImageHelper.GetNewImageSize(options, null);
             var quality = options.Quality;
             var quality = options.Quality;
 
 
             var outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
             var outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
@@ -477,98 +444,30 @@ namespace Emby.Drawing
 
 
         public ImageSize GetImageSize(ItemImageInfo info, bool allowSlowMethods)
         public ImageSize GetImageSize(ItemImageInfo info, bool allowSlowMethods)
         {
         {
-            return GetImageSize(info.Path, info.DateModified, allowSlowMethods);
+            return GetImageSize(info.Path, allowSlowMethods);
         }
         }
 
 
         public ImageSize GetImageSize(ItemImageInfo info)
         public ImageSize GetImageSize(ItemImageInfo info)
         {
         {
-            return GetImageSize(info.Path, info.DateModified, false);
+            return GetImageSize(info.Path, false);
         }
         }
 
 
         public ImageSize GetImageSize(string path)
         public ImageSize GetImageSize(string path)
         {
         {
-            return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
+            return GetImageSize(path, false);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Gets the size of the image.
         /// Gets the size of the image.
         /// </summary>
         /// </summary>
-        /// <param name="path">The path.</param>
-        /// <param name="imageDateModified">The image date modified.</param>
-        /// <param name="allowSlowMethod">if set to <c>true</c> [allow slow method].</param>
-        /// <returns>ImageSize.</returns>
-        /// <exception cref="System.ArgumentNullException">path</exception>
-        private ImageSize GetImageSize(string path, DateTime imageDateModified, bool allowSlowMethod)
+        private ImageSize GetImageSize(string path, bool allowSlowMethod)
         {
         {
             if (string.IsNullOrEmpty(path))
             if (string.IsNullOrEmpty(path))
             {
             {
                 throw new ArgumentNullException("path");
                 throw new ArgumentNullException("path");
             }
             }
 
 
-            ImageSize size;
-
-            var cacheHash = GetImageSizeKey(path, imageDateModified);
-
-            if (!_cachedImagedSizes.TryGetValue(cacheHash, out size))
-            {
-                size = GetImageSizeInternal(path, allowSlowMethod);
-
-                SaveImageSize(size, cacheHash, false);
-            }
-
-            return size;
-        }
-
-        public void SaveImageSize(string path, DateTime imageDateModified, ImageSize size)
-        {
-            var cacheHash = GetImageSizeKey(path, imageDateModified);
-            SaveImageSize(size, cacheHash, true);
-        }
-
-        private void SaveImageSize(ImageSize size, Guid cacheHash, bool checkExists)
-        {
-            if (size.Width <= 0 || size.Height <= 0)
-            {
-                return;
-            }
-
-            if (checkExists && _cachedImagedSizes.ContainsKey(cacheHash))
-            {
-                return;
-            }
-
-            if (checkExists)
-            {
-                if (_cachedImagedSizes.TryAdd(cacheHash, size))
-                {
-                    StartSaveImageSizeTimer();
-                }
-            }
-            else
-            {
-                StartSaveImageSizeTimer();
-                _cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size);
-            }
-        }
-
-        private Guid GetImageSizeKey(string path, DateTime imageDateModified)
-        {
-            var name = path + "datemodified=" + imageDateModified.Ticks;
-            return name.GetMD5();
-        }
-
-        public ImageSize? GetSavedImageSize(string path, DateTime imageDateModified)
-        {
-            ImageSize size;
-
-            var cacheHash = GetImageSizeKey(path, imageDateModified);
-
-            if (_cachedImagedSizes.TryGetValue(cacheHash, out size))
-            {
-                return size;
-            }
-
-            return null;
+            return GetImageSizeInternal(path, allowSlowMethod);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -619,39 +518,6 @@ namespace Emby.Drawing
             }
             }
         }
         }
 
 
-        private readonly ITimer _saveImageSizeTimer;
-        private const int SaveImageSizeTimeout = 5000;
-        private readonly object _saveImageSizeLock = new object();
-        private void StartSaveImageSizeTimer()
-        {
-            _saveImageSizeTimer.Change(SaveImageSizeTimeout, Timeout.Infinite);
-        }
-
-        private void SaveImageSizeCallback(object state)
-        {
-            lock (_saveImageSizeLock)
-            {
-                try
-                {
-                    var path = ImageSizeFile;
-                    _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(path));
-                    _jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error saving image size file", ex);
-                }
-            }
-        }
-
-        private string ImageSizeFile
-        {
-            get
-            {
-                return Path.Combine(_appPaths.DataPath, "imagesizes.json");
-            }
-        }
-
         /// <summary>
         /// <summary>
         /// Gets the image cache tag.
         /// Gets the image cache tag.
         /// </summary>
         /// </summary>
@@ -1016,7 +882,6 @@ namespace Emby.Drawing
                 disposable.Dispose();
                 disposable.Dispose();
             }
             }
 
 
-            _saveImageSizeTimer.Dispose();
             GC.SuppressFinalize(this);
             GC.SuppressFinalize(this);
         }
         }
 
 

+ 104 - 85
Emby.Photos/PhotoProvider.cs

@@ -3,6 +3,7 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Controller.Providers;
@@ -20,134 +21,152 @@ namespace Emby.Photos
     {
     {
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
+        private IImageProcessor _imageProcessor;
 
 
-        public PhotoProvider(ILogger logger, IFileSystem fileSystem)
+        public PhotoProvider(ILogger logger, IFileSystem fileSystem, IImageProcessor imageProcessor)
         {
         {
             _logger = logger;
             _logger = logger;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
+            _imageProcessor = imageProcessor;
         }
         }
 
 
+        // These are causing taglib to hang
+        private string[] _excludeExtensions = new string[] { ".dng" };
+
         public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
         public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken)
         {
         {
             item.SetImagePath(ImageType.Primary, item.Path);
             item.SetImagePath(ImageType.Primary, item.Path);
 
 
             // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
             // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
-
-            try
+            if (!_excludeExtensions.Contains(Path.GetExtension(item.Path) ?? string.Empty, StringComparer.OrdinalIgnoreCase))
             {
             {
-                using (var fileStream = _fileSystem.OpenRead(item.Path))
+                try
                 {
                 {
-                    using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(item.Path), fileStream, null)))
+                    using (var fileStream = _fileSystem.OpenRead(item.Path))
                     {
                     {
-                        var image = file as TagLib.Image.File;
-
-                        var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag;
-
-                        if (tag != null)
+                        using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(item.Path), fileStream, null)))
                         {
                         {
-                            var structure = tag.Structure;
+                            var image = file as TagLib.Image.File;
 
 
-                            if (structure != null)
+                            var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag;
+
+                            if (tag != null)
                             {
                             {
-                                var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry;
+                                var structure = tag.Structure;
 
 
-                                if (exif != null)
+                                if (structure != null)
                                 {
                                 {
-                                    var exifStructure = exif.Structure;
+                                    var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry;
 
 
-                                    if (exifStructure != null)
+                                    if (exif != null)
                                     {
                                     {
-                                        var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry;
+                                        var exifStructure = exif.Structure;
 
 
-                                        if (entry != null)
+                                        if (exifStructure != null)
                                         {
                                         {
-                                            double val = entry.Value.Numerator;
-                                            val /= entry.Value.Denominator;
-                                            item.Aperture = val;
-                                        }
-
-                                        entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry;
-
-                                        if (entry != null)
-                                        {
-                                            double val = entry.Value.Numerator;
-                                            val /= entry.Value.Denominator;
-                                            item.ShutterSpeed = val;
+                                            var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry;
+
+                                            if (entry != null)
+                                            {
+                                                double val = entry.Value.Numerator;
+                                                val /= entry.Value.Denominator;
+                                                item.Aperture = val;
+                                            }
+
+                                            entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry;
+
+                                            if (entry != null)
+                                            {
+                                                double val = entry.Value.Numerator;
+                                                val /= entry.Value.Denominator;
+                                                item.ShutterSpeed = val;
+                                            }
                                         }
                                         }
                                     }
                                     }
                                 }
                                 }
                             }
                             }
-                        }
 
 
-                        item.CameraMake = image.ImageTag.Make;
-                        item.CameraModel = image.ImageTag.Model;
+                            if (image != null)
+                            {
+                                item.CameraMake = image.ImageTag.Make;
+                                item.CameraModel = image.ImageTag.Model;
 
 
-                        item.Width = image.Properties.PhotoWidth;
-                        item.Height = image.Properties.PhotoHeight;
+                                item.Width = image.Properties.PhotoWidth;
+                                item.Height = image.Properties.PhotoHeight;
 
 
-                        var rating = image.ImageTag.Rating;
-                        if (rating.HasValue)
-                        {
-                            item.CommunityRating = rating;
-                        }
-                        else
-                        {
-                            item.CommunityRating = null;
-                        }
+                                var rating = image.ImageTag.Rating;
+                                if (rating.HasValue)
+                                {
+                                    item.CommunityRating = rating;
+                                }
+                                else
+                                {
+                                    item.CommunityRating = null;
+                                }
 
 
-                        item.Overview = image.ImageTag.Comment;
+                                item.Overview = image.ImageTag.Comment;
 
 
-                        if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
-                        {
-                            item.Name = image.ImageTag.Title;
-                        }
+                                if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
+                                {
+                                    item.Name = image.ImageTag.Title;
+                                }
 
 
-                        var dateTaken = image.ImageTag.DateTime;
-                        if (dateTaken.HasValue)
-                        {
-                            item.DateCreated = dateTaken.Value;
-                            item.PremiereDate = dateTaken.Value;
-                            item.ProductionYear = dateTaken.Value.Year;
-                        }
+                                var dateTaken = image.ImageTag.DateTime;
+                                if (dateTaken.HasValue)
+                                {
+                                    item.DateCreated = dateTaken.Value;
+                                    item.PremiereDate = dateTaken.Value;
+                                    item.ProductionYear = dateTaken.Value.Year;
+                                }
 
 
-                        item.Genres = image.ImageTag.Genres.ToList();
-                        item.Tags = image.ImageTag.Keywords;
-                        item.Software = image.ImageTag.Software;
+                                item.Genres = image.ImageTag.Genres.ToList();
+                                item.Tags = image.ImageTag.Keywords;
+                                item.Software = image.ImageTag.Software;
 
 
-                        if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None)
-                        {
-                            item.Orientation = null;
-                        }
-                        else
-                        {
-                            MediaBrowser.Model.Drawing.ImageOrientation orientation;
-                            if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
-                            {
-                                item.Orientation = orientation;
-                            }
-                        }
+                                if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None)
+                                {
+                                    item.Orientation = null;
+                                }
+                                else
+                                {
+                                    MediaBrowser.Model.Drawing.ImageOrientation orientation;
+                                    if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
+                                    {
+                                        item.Orientation = orientation;
+                                    }
+                                }
 
 
-                        item.ExposureTime = image.ImageTag.ExposureTime;
-                        item.FocalLength = image.ImageTag.FocalLength;
+                                item.ExposureTime = image.ImageTag.ExposureTime;
+                                item.FocalLength = image.ImageTag.FocalLength;
 
 
-                        item.Latitude = image.ImageTag.Latitude;
-                        item.Longitude = image.ImageTag.Longitude;
-                        item.Altitude = image.ImageTag.Altitude;
+                                item.Latitude = image.ImageTag.Latitude;
+                                item.Longitude = image.ImageTag.Longitude;
+                                item.Altitude = image.ImageTag.Altitude;
 
 
-                        if (image.ImageTag.ISOSpeedRatings.HasValue)
-                        {
-                            item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value);
-                        }
-                        else
-                        {
-                            item.IsoSpeedRating = null;
+                                if (image.ImageTag.ISOSpeedRatings.HasValue)
+                                {
+                                    item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value);
+                                }
+                                else
+                                {
+                                    item.IsoSpeedRating = null;
+                                }
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
+                catch (Exception e)
+                {
+                    _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
+                }
             }
             }
-            catch (Exception e)
+
+            if (!item.Width.HasValue || !item.Height.HasValue)
             {
             {
-                _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
+                var size = _imageProcessor.GetImageSize(item.Path);
+
+                item.Width = Convert.ToInt32(size.Width);
+                item.Height = Convert.ToInt32(size.Height);
             }
             }
 
 
             const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport;
             const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport;

+ 20 - 1
Emby.Server.Implementations/Activity/ActivityRepository.cs

@@ -10,20 +10,39 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 using SQLitePCL.pretty;
 using SQLitePCL.pretty;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
 
 
 namespace Emby.Server.Implementations.Activity
 namespace Emby.Server.Implementations.Activity
 {
 {
     public class ActivityRepository : BaseSqliteRepository, IActivityRepository
     public class ActivityRepository : BaseSqliteRepository, IActivityRepository
     {
     {
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
         private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+        protected IFileSystem FileSystem { get; private set; }
 
 
-        public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths)
+        public ActivityRepository(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem)
             : base(logger)
             : base(logger)
         {
         {
             DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
             DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
+            FileSystem = fileSystem;
         }
         }
 
 
         public void Initialize()
         public void Initialize()
+        {
+            try
+            {
+                InitializeInternal();
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
+
+                FileSystem.DeleteFile(DbFilePath);
+
+                InitializeInternal();
+            }
+        }
+
+        private void InitializeInternal()
         {
         {
             using (var connection = CreateConnection())
             using (var connection = CreateConnection())
             {
             {

+ 51 - 35
Emby.Server.Implementations/ApplicationHost.cs

@@ -7,7 +7,6 @@ using Emby.Dlna.MediaReceiverRegistrar;
 using Emby.Dlna.Ssdp;
 using Emby.Dlna.Ssdp;
 using Emby.Drawing;
 using Emby.Drawing;
 using Emby.Photos;
 using Emby.Photos;
-using Emby.Server.Core.Cryptography;
 using Emby.Server.Implementations.Activity;
 using Emby.Server.Implementations.Activity;
 using Emby.Server.Implementations.Archiving;
 using Emby.Server.Implementations.Archiving;
 using Emby.Server.Implementations.Channels;
 using Emby.Server.Implementations.Channels;
@@ -879,7 +878,7 @@ namespace Emby.Server.Implementations
             // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
             // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
             RegisterSingleInstance(UserRepository);
             RegisterSingleInstance(UserRepository);
 
 
-            var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, MemoryStreamFactory);
+            var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, MemoryStreamFactory, FileSystemManager);
             DisplayPreferencesRepository = displayPreferencesRepo;
             DisplayPreferencesRepository = displayPreferencesRepo;
             RegisterSingleInstance(DisplayPreferencesRepository);
             RegisterSingleInstance(DisplayPreferencesRepository);
 
 
@@ -997,7 +996,7 @@ namespace Emby.Server.Implementations
             EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager);
             EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager);
             RegisterSingleInstance(EncodingManager);
             RegisterSingleInstance(EncodingManager);
 
 
-            var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths);
+            var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths, FileSystemManager);
             sharingRepo.Initialize();
             sharingRepo.Initialize();
             // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
             // This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
             RegisterSingleInstance<ISharingRepository>(sharingRepo);
             RegisterSingleInstance<ISharingRepository>(sharingRepo);
@@ -1351,7 +1350,7 @@ namespace Emby.Server.Implementations
 
 
         private IActivityRepository GetActivityLogRepository()
         private IActivityRepository GetActivityLogRepository()
         {
         {
-            var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths);
+            var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
 
 
             repo.Initialize();
             repo.Initialize();
 
 
@@ -1640,23 +1639,23 @@ namespace Emby.Server.Implementations
             var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx");
             var certPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.ProgramDataPath, "ssl", "cert_" + (certHost + "2").GetMD5().ToString("N") + ".pfx");
             var password = "embycert";
             var password = "embycert";
 
 
-            if (generateCertificate)
-            {
-                if (!FileSystemManager.FileExists(certPath))
-                {
-                    FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath));
-
-                    try
-                    {
-                        CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, password, Logger);
-                    }
-                    catch (Exception ex)
-                    {
-                        Logger.ErrorException("Error creating ssl cert", ex);
-                        return null;
-                    }
-                }
-            }
+            //if (generateCertificate)
+            //{
+            //    if (!FileSystemManager.FileExists(certPath))
+            //    {
+            //        FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath));
+
+            //        try
+            //        {
+            //            CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, password, Logger);
+            //        }
+            //        catch (Exception ex)
+            //        {
+            //            Logger.ErrorException("Error creating ssl cert", ex);
+            //            return null;
+            //        }
+            //    }
+            //}
 
 
             return new CertificateInfo
             return new CertificateInfo
             {
             {
@@ -1932,13 +1931,13 @@ namespace Emby.Server.Implementations
         {
         {
             get
             get
             {
             {
-                return SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps;
+                return SupportsHttps && (ServerConfigurationManager.Configuration.EnableHttps || ServerConfigurationManager.Configuration.RequireHttps);
             }
             }
         }
         }
 
 
         public bool SupportsHttps
         public bool SupportsHttps
         {
         {
-            get { return Certificate != null; }
+            get { return Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy; }
         }
         }
 
 
         public async Task<string> GetLocalApiUrl()
         public async Task<string> GetLocalApiUrl()
@@ -2208,22 +2207,39 @@ namespace Emby.Server.Implementations
         {
         {
             var updateLevel = SystemUpdateLevel;
             var updateLevel = SystemUpdateLevel;
             var cacheLength = updateLevel == PackageVersionClass.Release ?
             var cacheLength = updateLevel == PackageVersionClass.Release ?
-                TimeSpan.FromHours(4) :
+                TimeSpan.FromHours(12) :
                 TimeSpan.FromMinutes(5);
                 TimeSpan.FromMinutes(5);
 
 
-            var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser",
-                "Emby",
-                ApplicationVersion,
-                updateLevel,
-                ReleaseAssetFilename,
-                "MBServer",
-                UpdateTargetFileName,
-                cacheLength,
-                cancellationToken).ConfigureAwait(false);
+            try
+            {
+                var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser",
+                    "Emby",
+                    ApplicationVersion,
+                    updateLevel,
+                    ReleaseAssetFilename,
+                    "MBServer",
+                    UpdateTargetFileName,
+                    cacheLength,
+                    cancellationToken).ConfigureAwait(false);
+
+                HasUpdateAvailable = result.IsUpdateAvailable;
 
 
-            HasUpdateAvailable = result.IsUpdateAvailable;
+                return result;
+            }
+            catch (HttpException ex)
+            {
+                // users are overreacting to this occasionally failing
+                if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.Forbidden)
+                {
+                    HasUpdateAvailable = false;
+                    return new CheckForUpdateResult
+                    {
+                        IsUpdateAvailable = false
+                    };
+                }
 
 
-            return result;
+                throw;
+            }
         }
         }
 
 
         protected virtual string UpdateTargetFileName
         protected virtual string UpdateTargetFileName

+ 17 - 0
Emby.Server.Implementations/Archiving/ZipClient.cs

@@ -4,6 +4,7 @@ using SharpCompress.Archives.Rar;
 using SharpCompress.Archives.SevenZip;
 using SharpCompress.Archives.SevenZip;
 using SharpCompress.Archives.Tar;
 using SharpCompress.Archives.Tar;
 using SharpCompress.Readers;
 using SharpCompress.Readers;
+using SharpCompress.Readers.GZip;
 using SharpCompress.Readers.Zip;
 using SharpCompress.Readers.Zip;
 
 
 namespace Emby.Server.Implementations.Archiving
 namespace Emby.Server.Implementations.Archiving
@@ -72,6 +73,22 @@ namespace Emby.Server.Implementations.Archiving
             }
             }
         }
         }
 
 
+        public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles)
+        {
+            using (var reader = GZipReader.Open(source))
+            {
+                var options = new ExtractionOptions();
+                options.ExtractFullPath = true;
+
+                if (overwriteExistingFiles)
+                {
+                    options.Overwrite = true;
+                }
+
+                reader.WriteAllToDirectory(targetPath, options);
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Extracts all from7z.
         /// Extracts all from7z.
         /// </summary>
         /// </summary>

+ 0 - 340
Emby.Server.Implementations/Cryptography/ASN1.cs

@@ -1,340 +0,0 @@
-//
-// ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator
-//
-// Authors:
-//	Sebastien Pouliot  <sebastien@ximian.com>
-//	Jesper Pedersen  <jep@itplus.dk>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
-// (C) 2004 IT+ A/S (http://www.itplus.dk)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections;
-using System.IO;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-	// References:
-	// a.	ITU ASN.1 standards (free download)
-	//	http://www.itu.int/ITU-T/studygroups/com17/languages/
-
-    public class ASN1 {
-
-		private byte m_nTag;
-		private byte[] m_aValue;
-		private ArrayList elist;
-
-		public ASN1 () : this (0x00, null) {}
-
-		public ASN1 (byte tag) : this (tag, null) {}
-
-		public ASN1 (byte tag, byte[] data) 
-		{
-			m_nTag = tag;
-			m_aValue = data;
-		}
-
-		public ASN1 (byte[] data) 
-		{
-			m_nTag = data [0];
-
-			int nLenLength = 0;
-			int nLength = data [1];
-
-			if (nLength > 0x80) {
-				// composed length
-				nLenLength = nLength - 0x80;
-				nLength = 0;
-				for (int i = 0; i < nLenLength; i++) {
-					nLength *= 256;
-					nLength += data [i + 2];
-				}
-			}
-			else if (nLength == 0x80) {
-				// undefined length encoding
-				throw new NotSupportedException ("Undefined length encoding.");
-			}
-
-			m_aValue = new byte [nLength];
-			Buffer.BlockCopy (data, (2 + nLenLength), m_aValue, 0, nLength);
-
-			if ((m_nTag & 0x20) == 0x20) {
-				int nStart = (2 + nLenLength);
-				Decode (data, ref nStart, data.Length);
-			}
-		}
-
-		public int Count {
-			get { 
-				if (elist == null)
-					return 0;
-				return elist.Count; 
-			}
-		}
-
-		public byte Tag {
-			get { return m_nTag; }
-		}
-
-		public int Length {
-			get { 
-				if (m_aValue != null)
-					return m_aValue.Length; 
-				else
-					return 0;
-			}
-		}
-
-		public byte[] Value {
-			get { 
-				if (m_aValue == null)
-					GetBytes ();
-				return (byte[]) m_aValue.Clone (); 
-			}
-			set { 
-				if (value != null)
-					m_aValue = (byte[]) value.Clone (); 
-			}
-		}
-
-		private bool CompareArray (byte[] array1, byte[] array2)
-		{
-			bool bResult = (array1.Length == array2.Length);
-			if (bResult) {
-				for (int i = 0; i < array1.Length; i++) {
-					if (array1[i] != array2[i])
-						return false;
-				}
-			}
-			return bResult;
-		}
-
-		public bool Equals (byte[] asn1) 
-		{
-			return CompareArray (this.GetBytes (), asn1);
-		}
-
-		public bool CompareValue (byte[] value) 
-		{
-			return CompareArray (m_aValue, value);
-		}
-
-		public ASN1 Add (ASN1 asn1) 
-		{
-			if (asn1 != null) {
-				if (elist == null)
-					elist = new ArrayList ();
-				elist.Add (asn1);
-			}
-			return asn1;
-		}
-
-		public virtual byte[] GetBytes () 
-		{
-			byte[] val = null;
-			
-			if (Count > 0) {
-				int esize = 0;
-				ArrayList al = new ArrayList ();
-				foreach (ASN1 a in elist) {
-					byte[] item = a.GetBytes ();
-					al.Add (item);
-					esize += item.Length;
-				}
-				val = new byte [esize];
-				int pos = 0;
-				for (int i=0; i < elist.Count; i++) {
-					byte[] item = (byte[]) al[i];
-					Buffer.BlockCopy (item, 0, val, pos, item.Length);
-					pos += item.Length;
-				}
-			} else if (m_aValue != null) {
-				val = m_aValue;
-			}
-
-			byte[] der;
-			int nLengthLen = 0;
-
-			if (val != null) {
-				int nLength = val.Length;
-				// special for length > 127
-				if (nLength > 127) {
-					if (nLength <= Byte.MaxValue) {
-						der = new byte [3 + nLength];
-						Buffer.BlockCopy (val, 0, der, 3, nLength);
-						nLengthLen = 0x81;
-						der[2] = (byte)(nLength);
-					}
-					else if (nLength <= UInt16.MaxValue) {
-						der = new byte [4 + nLength];
-						Buffer.BlockCopy (val, 0, der, 4, nLength);
-						nLengthLen = 0x82;
-						der[2] = (byte)(nLength >> 8);
-						der[3] = (byte)(nLength);
-					}
-					else if (nLength <= 0xFFFFFF) {
-						// 24 bits
-						der = new byte [5 + nLength];
-						Buffer.BlockCopy (val, 0, der, 5, nLength);
-						nLengthLen = 0x83;
-						der [2] = (byte)(nLength >> 16);
-						der [3] = (byte)(nLength >> 8);
-						der [4] = (byte)(nLength);
-					}
-					else {
-						// max (Length is an integer) 32 bits
-						der = new byte [6 + nLength];
-						Buffer.BlockCopy (val, 0, der, 6, nLength);
-						nLengthLen = 0x84;
-						der [2] = (byte)(nLength >> 24);
-						der [3] = (byte)(nLength >> 16);
-						der [4] = (byte)(nLength >> 8);
-						der [5] = (byte)(nLength);
-					}
-				}
-				else {
-					// basic case (no encoding)
-					der = new byte [2 + nLength];
-					Buffer.BlockCopy (val, 0, der, 2, nLength);
-					nLengthLen = nLength;
-				}
-				if (m_aValue == null)
-					m_aValue = val;
-			}
-			else
-				der = new byte[2];
-
-			der[0] = m_nTag;
-			der[1] = (byte)nLengthLen;
-
-			return der;
-		}
-
-		// Note: Recursive
-		protected void Decode (byte[] asn1, ref int anPos, int anLength) 
-		{
-			byte nTag;
-			int nLength;
-			byte[] aValue;
-
-			// minimum is 2 bytes (tag + length of 0)
-			while (anPos < anLength - 1) {
-				DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue);
-				// sometimes we get trailing 0
-				if (nTag == 0)
-					continue;
-
-				ASN1 elm = Add (new ASN1 (nTag, aValue));
-
-				if ((nTag & 0x20) == 0x20) {
-					int nConstructedPos = anPos;
-					elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength);
-				}
-				anPos += nLength; // value length
-			}
-		}
-
-		// TLV : Tag - Length - Value
-		protected void DecodeTLV (byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content) 
-		{
-			tag = asn1 [pos++];
-			length = asn1 [pos++];
-
-			// special case where L contains the Length of the Length + 0x80
-			if ((length & 0x80) == 0x80) {
-				int nLengthLen = length & 0x7F;
-				length = 0;
-				for (int i = 0; i < nLengthLen; i++)
-					length = length * 256 + asn1 [pos++];
-			}
-
-			content = new byte [length];
-			Buffer.BlockCopy (asn1, pos, content, 0, length);
-		}
-
-		public ASN1 this [int index] {
-			get { 		
-				try {
-					if ((elist == null) || (index >= elist.Count))
-						return null;
-					return (ASN1) elist [index];
-				}
-				catch (ArgumentOutOfRangeException) {
-					return null;
-				}
-			}
-		}
-
-		public ASN1 Element (int index, byte anTag) 
-		{
-			try {
-				if ((elist == null) || (index >= elist.Count))
-					return null;
-
-				ASN1 elm = (ASN1) elist [index];
-				if (elm.Tag == anTag)
-					return elm;
-				else
-					return null;
-			}
-			catch (ArgumentOutOfRangeException) {
-				return null;
-			}
-		}
-
-		public override string ToString()
-		{
-			StringBuilder hexLine = new StringBuilder ();
-            
-			// Add tag
-			hexLine.AppendFormat ("Tag: {0} {1}", m_nTag.ToString ("X2"), Environment.NewLine);
-
-			// Add length
-			hexLine.AppendFormat ("Length: {0} {1}", Value.Length, Environment.NewLine);
-
-			// Add value
-			hexLine.Append ("Value: ");
-			hexLine.Append (Environment.NewLine);
-			for (int i = 0; i < Value.Length; i++) {
-				hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2"));
-				if ((i+1) % 16 == 0)
-					hexLine.AppendFormat (Environment.NewLine);
-			}
-			return hexLine.ToString ();
-		}
-
-		public void SaveToFile (string filename)
-		{
-			if (filename == null)
-				throw new ArgumentNullException ("filename");
-
-			using (FileStream fs = File.Create (filename)) {
-				byte[] data = GetBytes ();
-				fs.Write (data, 0, data.Length);
-			}
-		}
-	}
-}

+ 0 - 207
Emby.Server.Implementations/Cryptography/ASN1Convert.cs

@@ -1,207 +0,0 @@
-//
-// ASN1Convert.cs: Abstract Syntax Notation 1 convertion routines
-//
-// Authors:
-//	Sebastien Pouliot  <sebastien@ximian.com>
-//	Jesper Pedersen  <jep@itplus.dk>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 IT+ A/S (http://www.itplus.dk)
-// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Globalization;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    // References:
-    // a.	ITU ASN.1 standards (free download)
-    //	http://www.itu.int/ITU-T/studygroups/com17/languages/
-
-    public static class ASN1Convert {
-		// RFC3280, section 4.2.1.5
-		// CAs conforming to this profile MUST always encode certificate
-		// validity dates through the year 2049 as UTCTime; certificate validity
-		// dates in 2050 or later MUST be encoded as GeneralizedTime.
-
-		// Under 1.x this API requires a Local datetime to be provided
-		// Under 2.0 it will also accept a Utc datetime
-		static public ASN1 FromDateTime (DateTime dt) 
-		{
-			if (dt.Year < 2050) {
-				// UTCTIME
-				return new ASN1 (0x17, Encoding.ASCII.GetBytes (
-					dt.ToUniversalTime ().ToString ("yyMMddHHmmss",
-					CultureInfo.InvariantCulture) + "Z"));
-			}
-			else {
-				// GENERALIZEDTIME
-				return new ASN1 (0x18, Encoding.ASCII.GetBytes (
-					dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss", 
-					CultureInfo.InvariantCulture) + "Z"));
-			}
-		}
-
-		static public ASN1 FromInt32 (Int32 value) 
-		{
-			byte[] integer = BitConverterLE.GetBytes (value);
-			Array.Reverse (integer);
-			int x = 0;
-			while ((x < integer.Length) && (integer [x] == 0x00))
-				x++;
-			ASN1 asn1 = new ASN1 (0x02);
-			switch (x) {
-			case 0:
-				asn1.Value = integer;
-				break;
-			case 4:
-				asn1.Value = new byte [1];
-				break;
-			default:
-				byte[] smallerInt = new byte [4 - x];
-				Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length);
-				asn1.Value = smallerInt;
-				break;
-			}
-			return asn1;
-		}
-
-		static public ASN1 FromOid (string oid) 
-		{
-			if (oid == null)
-				throw new ArgumentNullException ("oid");
-
-			return new ASN1 (CryptoConfig.EncodeOID (oid));
-		}
-
-		static public ASN1 FromUnsignedBigInteger (byte[] big) 
-		{
-			if (big == null)
-				throw new ArgumentNullException ("big");
-
-			// check for numbers that could be interpreted as negative (first bit)
-			if (big [0] >= 0x80) {
-				// in thie cas we add a new, empty, byte (position 0) so we're
-				// sure this will always be interpreted an unsigned integer.
-				// However we can't feed it into RSAParameters or DSAParameters
-				int length = big.Length + 1;
-				byte[] uinteger = new byte [length];
-				Buffer.BlockCopy (big, 0, uinteger, 1, length - 1);
-				big = uinteger;
-			}
-			return new ASN1 (0x02, big);
-		}
-
-		static public int ToInt32 (ASN1 asn1) 
-		{
-			if (asn1 == null)
-				throw new ArgumentNullException ("asn1");
-			if (asn1.Tag != 0x02)
-				throw new FormatException ("Only integer can be converted");
-
-			int x = 0;
-			for (int i=0; i < asn1.Value.Length; i++)
-				x = (x << 8) + asn1.Value [i];
-			return x;
-		}
-
-		// Convert a binary encoded OID to human readable string representation of 
-		// an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
-		static public string ToOid (ASN1 asn1) 
-		{
-			if (asn1 == null)
-				throw new ArgumentNullException ("asn1");
-
-			byte[] aOID = asn1.Value;
-			StringBuilder sb = new StringBuilder ();
-			// Pick apart the OID
-			byte x = (byte) (aOID[0] / 40);
-			byte y = (byte) (aOID[0] % 40);
-			if (x > 2) {
-				// Handle special case for large y if x = 2
-				y += (byte) ((x - 2) * 40);
-				x = 2;
-			}
-			sb.Append (x.ToString (CultureInfo.InvariantCulture));
-			sb.Append (".");
-			sb.Append (y.ToString (CultureInfo.InvariantCulture));
-			ulong val = 0;
-			for (x = 1; x < aOID.Length; x++) {
-				val = ((val << 7) | ((byte) (aOID [x] & 0x7F)));
-				if ( !((aOID [x] & 0x80) == 0x80)) {
-					sb.Append (".");
-					sb.Append (val.ToString (CultureInfo.InvariantCulture));
-					val = 0;
-				}
-			}
-			return sb.ToString ();
-		}
-
-		static public DateTime ToDateTime (ASN1 time) 
-		{
-			if (time == null)
-				throw new ArgumentNullException ("time");
-
-			string t = Encoding.ASCII.GetString (time.Value);
-			// to support both UTCTime and GeneralizedTime (and not so common format)
-			string mask = null;
-			int year;
-			switch (t.Length) {
-				case 11:
-					// illegal format, still it's supported for compatibility
-					mask = "yyMMddHHmmZ";
-					break;
-				case 13: 
-					// RFC3280: 4.1.2.5.1  UTCTime
-					year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
-					// Where YY is greater than or equal to 50, the 
-					// year SHALL be interpreted as 19YY; and 
-					// Where YY is less than 50, the year SHALL be 
-					// interpreted as 20YY.
-					if (year >= 50)
-						t = "19" + t;
-					else
-						t = "20" + t;
-					mask = "yyyyMMddHHmmssZ";
-					break;
-				case 15:
-					mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
-					break;
-				case 17:
-					// another illegal format (990630000000+1000), again supported for compatibility
-					year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
-					string century = (year >= 50) ? "19" : "20";
-					// ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET
-					char sign = (t[12] == '+') ? '-' : '+';
-					t = String.Format ("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring (0, 12), sign, 
-						t[13], t[14], t[15], t[16]);
-					mask = "yyyyMMddHHmmsszzz";
-					break;
-			}
-			return DateTime.ParseExact (t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
-		}
-	}
-}

+ 0 - 239
Emby.Server.Implementations/Cryptography/BitConverterLE.cs

@@ -1,239 +0,0 @@
-//
-// Mono.Security.BitConverterLE.cs
-//  Like System.BitConverter but always little endian
-//
-// Author:
-//   Bernie Solomon
-//
-
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-
-namespace Emby.Server.Core.Cryptography
-{
-    internal sealed class BitConverterLE
-	{
-		private BitConverterLE ()
-		{
-		}
-
-		unsafe private static byte[] GetUShortBytes (byte *bytes)
-		{
-			if (BitConverter.IsLittleEndian)
-				return new byte [] { bytes [0], bytes [1] };
-			else
-				return new byte [] { bytes [1], bytes [0] };
-		}
-
-		unsafe private static byte[] GetUIntBytes (byte *bytes)
-		{
-			if (BitConverter.IsLittleEndian)
-				return new byte [] { bytes [0], bytes [1], bytes [2], bytes [3] };
-			else
-				return new byte [] { bytes [3], bytes [2], bytes [1], bytes [0] };
-		}
-
-		unsafe private static byte[] GetULongBytes (byte *bytes)
-		{
-			if (BitConverter.IsLittleEndian)
-				return new byte [] { bytes [0], bytes [1], bytes [2], bytes [3],
-						     bytes [4], bytes [5], bytes [6], bytes [7] };
-			else
-				return new byte [] { bytes [7], bytes [6], bytes [5], bytes [4],
-						     bytes [3], bytes [2], bytes [1], bytes [0] };
-		}
-
-		unsafe internal static byte[] GetBytes (bool value)
-		{
-			return new byte [] { value ? (byte)1 : (byte)0 };
-		}
-
-		unsafe internal static byte[] GetBytes (char value)
-		{
-			return GetUShortBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (short value)
-		{
-			return GetUShortBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (int value)
-		{
-			return GetUIntBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (long value)
-		{
-			return GetULongBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (ushort value)
-		{
-			return GetUShortBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (uint value)
-		{
-			return GetUIntBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (ulong value)
-		{
-			return GetULongBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (float value)
-		{
-			return GetUIntBytes ((byte *) &value);
-		}
-
-		unsafe internal static byte[] GetBytes (double value)
-		{
-			return GetULongBytes ((byte *) &value);
-		}
-
-		unsafe private static void UShortFromBytes (byte *dst, byte[] src, int startIndex)
-		{
-			if (BitConverter.IsLittleEndian) {
-				dst [0] = src [startIndex];
-				dst [1] = src [startIndex + 1];
-			} else {
-				dst [0] = src [startIndex + 1];
-				dst [1] = src [startIndex];
-			}
-		}
-
-		unsafe private static void UIntFromBytes (byte *dst, byte[] src, int startIndex)
-		{
-			if (BitConverter.IsLittleEndian) {
-				dst [0] = src [startIndex];
-				dst [1] = src [startIndex + 1];
-				dst [2] = src [startIndex + 2];
-				dst [3] = src [startIndex + 3];
-			} else {
-				dst [0] = src [startIndex + 3];
-				dst [1] = src [startIndex + 2];
-				dst [2] = src [startIndex + 1];
-				dst [3] = src [startIndex];
-			}
-		}
-
-		unsafe private static void ULongFromBytes (byte *dst, byte[] src, int startIndex)
-		{
-			if (BitConverter.IsLittleEndian) {
-				for (int i = 0; i < 8; ++i)
-					dst [i] = src [startIndex + i];
-			} else {
-				for (int i = 0; i < 8; ++i)
-					dst [i] = src [startIndex + (7 - i)];
-			}
-		}
-
-		unsafe internal static bool ToBoolean (byte[] value, int startIndex)
-		{
-			return value [startIndex] != 0;
-		}
-
-		unsafe internal static char ToChar (byte[] value, int startIndex)
-		{
-			char ret;
-
-			UShortFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static short ToInt16 (byte[] value, int startIndex)
-		{
-			short ret;
-
-			UShortFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static int ToInt32 (byte[] value, int startIndex)
-		{
-			int ret;
-
-			UIntFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static long ToInt64 (byte[] value, int startIndex)
-		{
-			long ret;
-
-			ULongFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static ushort ToUInt16 (byte[] value, int startIndex)
-		{
-			ushort ret;
-
-			UShortFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static uint ToUInt32 (byte[] value, int startIndex)
-		{
-			uint ret;
-
-			UIntFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static ulong ToUInt64 (byte[] value, int startIndex)
-		{
-			ulong ret;
-
-			ULongFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static float ToSingle (byte[] value, int startIndex)
-		{
-			float ret;
-
-			UIntFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-
-		unsafe internal static double ToDouble (byte[] value, int startIndex)
-		{
-			double ret;
-
-			ULongFromBytes ((byte *) &ret, value, startIndex);
-
-			return ret;
-		}
-	}
-}

+ 0 - 109
Emby.Server.Implementations/Cryptography/CertificateGenerator.cs

@@ -1,109 +0,0 @@
-using MediaBrowser.Model.Logging;
-using System;
-using System.Collections;
-using System.Security.Cryptography;
-using System.Xml;
-
-namespace Emby.Server.Core.Cryptography
-{
-    public class CertificateGenerator
-    {
-        private const string MonoTestRootAgency = "<RSAKeyValue><Modulus>v/4nALBxCE+9JgEC0LnDUvKh6e96PwTpN4Rj+vWnqKT7IAp1iK/JjuqvAg6DQ2vTfv0dTlqffmHH51OyioprcT5nzxcSTsZb/9jcHScG0s3/FRIWnXeLk/fgm7mSYhjUaHNI0m1/NTTktipicjKxo71hGIg9qucCWnDum+Krh/k=</Modulus><Exponent>AQAB</Exponent><P>9jbKxMXEruW2CfZrzhxtull4O8P47+mNsEL+9gf9QsRO1jJ77C+jmzfU6zbzjf8+ViK+q62tCMdC1ZzulwdpXQ==</P><Q>x5+p198l1PkK0Ga2mRh0SIYSykENpY2aLXoyZD/iUpKYAvATm0/wvKNrE4dKJyPCA+y3hfTdgVag+SP9avvDTQ==</Q><DP>ISSjCvXsUfbOGG05eddN1gXxL2pj+jegQRfjpk7RAsnWKvNExzhqd5x+ZuNQyc6QH5wxun54inP4RTUI0P/IaQ==</DP><DQ>R815VQmR3RIbPqzDXzv5j6CSH6fYlcTiQRtkBsUnzhWmkd/y3XmamO+a8zJFjOCCx9CcjpVuGziivBqi65lVPQ==</DQ><InverseQ>iYiu0KwMWI/dyqN3RJYUzuuLj02/oTD1pYpwo2rvNCXU1Q5VscOeu2DpNg1gWqI+1RrRCsEoaTNzXB1xtKNlSw==</InverseQ><D>nIfh1LYF8fjRBgMdAH/zt9UKHWiaCnc+jXzq5tkR8HVSKTVdzitD8bl1JgAfFQD8VjSXiCJqluexy/B5SGrCXQ49c78NIQj0hD+J13Y8/E0fUbW1QYbhj6Ff7oHyhaYe1WOQfkp2t/h+llHOdt1HRf7bt7dUknYp7m8bQKGxoYE=</D></RSAKeyValue>";
-
-        public static void CreateSelfSignCertificatePfx(
-            string fileName,
-            string hostname,
-            string password,
-            ILogger logger)
-        {
-            if (string.IsNullOrWhiteSpace(fileName))
-            {
-                throw new ArgumentNullException("fileName");
-            }
-
-            byte[] sn = Guid.NewGuid().ToByteArray();
-            string subject = string.Format("CN={0}", hostname);
-            string issuer = subject;
-            DateTime notBefore = DateTime.Now.AddDays(-2);
-            DateTime notAfter = DateTime.Now.AddYears(10);
-
-            RSA issuerKey = RSA.Create();
-#if NET46
-            issuerKey.FromXmlString(MonoTestRootAgency);
-#else
-            RSACryptoServiceProviderExtensions.FromXmlString(issuerKey, MonoTestRootAgency);
-#endif
-            RSA subjectKey = RSA.Create();
-
-            // serial number MUST be positive
-            if ((sn[0] & 0x80) == 0x80)
-                sn[0] -= 0x80;
-
-            issuer = subject;
-            issuerKey = subjectKey;
-
-            X509CertificateBuilder cb = new X509CertificateBuilder(3);
-            cb.SerialNumber = sn;
-            cb.IssuerName = issuer;
-            cb.NotBefore = notBefore;
-            cb.NotAfter = notAfter;
-            cb.SubjectName = subject;
-            cb.SubjectPublicKey = subjectKey;
-
-            // signature
-            cb.Hash = "SHA256";
-            byte[] rawcert = cb.Sign(issuerKey);
-
-            PKCS12 p12 = new PKCS12();
-
-
-            ArrayList list = new ArrayList();
-            // we use a fixed array to avoid endianess issues 
-            // (in case some tools requires the ID to be 1).
-            list.Add(new byte[4] { 1, 0, 0, 0 });
-            Hashtable attributes = new Hashtable(1);
-            attributes.Add(PKCS9.localKeyId, list);
-
-            p12.AddCertificate(new X509Certificate(rawcert), attributes);
-            p12.Password = password;
-
-            p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes);
-            p12.SaveToFile(fileName);
-        }
-    }
-
-    public static class RSACryptoServiceProviderExtensions
-    {
-        public static void FromXmlString(RSA rsa, string xmlString)
-        {
-            RSAParameters parameters = new RSAParameters();
-
-            XmlDocument xmlDoc = new XmlDocument();
-            xmlDoc.LoadXml(xmlString);
-
-            if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
-            {
-                foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes)
-                {
-                    switch (node.Name)
-                    {
-                        case "Modulus": parameters.Modulus = Convert.FromBase64String(node.InnerText); break;
-                        case "Exponent": parameters.Exponent = Convert.FromBase64String(node.InnerText); break;
-                        case "P": parameters.P = Convert.FromBase64String(node.InnerText); break;
-                        case "Q": parameters.Q = Convert.FromBase64String(node.InnerText); break;
-                        case "DP": parameters.DP = Convert.FromBase64String(node.InnerText); break;
-                        case "DQ": parameters.DQ = Convert.FromBase64String(node.InnerText); break;
-                        case "InverseQ": parameters.InverseQ = Convert.FromBase64String(node.InnerText); break;
-                        case "D": parameters.D = Convert.FromBase64String(node.InnerText); break;
-                    }
-                }
-            }
-            else
-            {
-                throw new Exception("Invalid XML RSA key.");
-            }
-
-            rsa.ImportParameters(parameters);
-        }
-    }
-}

+ 0 - 745
Emby.Server.Implementations/Cryptography/CryptoConvert.cs

@@ -1,745 +0,0 @@
-//
-// CryptoConvert.cs - Crypto Convertion Routines
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@ximian.com>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Globalization;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    public sealed class CryptoConvert {
-
-		private CryptoConvert () 
-		{
-		}
-
-		static private int ToInt32LE (byte [] bytes, int offset)
-		{
-			return (bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset];
-		}
-
-		static private uint ToUInt32LE (byte [] bytes, int offset)
-		{
-			return (uint)((bytes [offset+3] << 24) | (bytes [offset+2] << 16) | (bytes [offset+1] << 8) | bytes [offset]);
-		}
-
-		static private byte [] GetBytesLE (int val)
-		{
-			return new byte [] { 
-				(byte) (val & 0xff), 
-				(byte) ((val >> 8) & 0xff), 
-				(byte) ((val >> 16) & 0xff), 
-				(byte) ((val >> 24) & 0xff)
-			};
-                }
-
-		static private byte[] Trim (byte[] array) 
-		{
-			for (int i=0; i < array.Length; i++) {
-				if (array [i] != 0x00) {
-					byte[] result = new byte [array.Length - i];
-					Buffer.BlockCopy (array, i, result, 0, result.Length);
-					return result;
-				}
-			}
-			return null;
-		}
-
-		// convert the key from PRIVATEKEYBLOB to RSA
-		// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/security/Security/private_key_blobs.asp
-		// e.g. SNK files, PVK files
-		static public RSA FromCapiPrivateKeyBlob (byte[] blob) 
-		{
-			return FromCapiPrivateKeyBlob (blob, 0);
-		}
-
-		static public RSA FromCapiPrivateKeyBlob (byte[] blob, int offset) 
-		{
-			if (blob == null)
-				throw new ArgumentNullException ("blob");
-			if (offset >= blob.Length)
-				throw new ArgumentException ("blob is too small.");
-
-			RSAParameters rsap = new RSAParameters ();
-			try {
-				if ((blob [offset]   != 0x07) ||				// PRIVATEKEYBLOB (0x07)
-				    (blob [offset+1] != 0x02) ||				// Version (0x02)
-				    (blob [offset+2] != 0x00) ||				// Reserved (word)
-				    (blob [offset+3] != 0x00) ||
-				    (ToUInt32LE (blob, offset+8) != 0x32415352))	// DWORD magic = RSA2
-					throw new CryptographicException ("Invalid blob header");
-				
-				// ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
-				// int algId = ToInt32LE (blob, offset+4);
-
-				// DWORD bitlen
-				int bitLen = ToInt32LE (blob, offset+12);
-
-				// DWORD public exponent
-				byte[] exp = new byte [4];
-				Buffer.BlockCopy (blob, offset+16, exp, 0, 4);
-				Array.Reverse (exp);
-				rsap.Exponent = Trim (exp);
-			
-				int pos = offset+20;
-				// BYTE modulus[rsapubkey.bitlen/8];
-				int byteLen = (bitLen >> 3);
-				rsap.Modulus = new byte [byteLen];
-				Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
-				Array.Reverse (rsap.Modulus);
-				pos += byteLen;
-
-				// BYTE prime1[rsapubkey.bitlen/16];
-				int byteHalfLen = (byteLen >> 1);
-				rsap.P = new byte [byteHalfLen];
-				Buffer.BlockCopy (blob, pos, rsap.P, 0, byteHalfLen);
-				Array.Reverse (rsap.P);
-				pos += byteHalfLen;
-
-				// BYTE prime2[rsapubkey.bitlen/16];
-				rsap.Q = new byte [byteHalfLen];
-				Buffer.BlockCopy (blob, pos, rsap.Q, 0, byteHalfLen);
-				Array.Reverse (rsap.Q);
-				pos += byteHalfLen;
-
-				// BYTE exponent1[rsapubkey.bitlen/16];
-				rsap.DP = new byte [byteHalfLen];
-				Buffer.BlockCopy (blob, pos, rsap.DP, 0, byteHalfLen);
-				Array.Reverse (rsap.DP);
-				pos += byteHalfLen;
-
-				// BYTE exponent2[rsapubkey.bitlen/16];
-				rsap.DQ = new byte [byteHalfLen];
-				Buffer.BlockCopy (blob, pos, rsap.DQ, 0, byteHalfLen);
-				Array.Reverse (rsap.DQ);
-				pos += byteHalfLen;
-
-				// BYTE coefficient[rsapubkey.bitlen/16];
-				rsap.InverseQ = new byte [byteHalfLen];
-				Buffer.BlockCopy (blob, pos, rsap.InverseQ, 0, byteHalfLen);
-				Array.Reverse (rsap.InverseQ);
-				pos += byteHalfLen;
-
-				// ok, this is hackish but CryptoAPI support it so...
-				// note: only works because CRT is used by default
-				// http://bugzilla.ximian.com/show_bug.cgi?id=57941
-				rsap.D = new byte [byteLen]; // must be allocated
-				if (pos + byteLen + offset <= blob.Length) {
-					// BYTE privateExponent[rsapubkey.bitlen/8];
-					Buffer.BlockCopy (blob, pos, rsap.D, 0, byteLen);
-					Array.Reverse (rsap.D);
-				}
-			}
-			catch (Exception e) {
-				throw new CryptographicException ("Invalid blob.", e);
-			}
-
-            RSA rsa = null;
-            try
-            {
-                rsa = RSA.Create();
-                rsa.ImportParameters(rsap);
-            }
-            catch (CryptographicException ce)
-            {
-                // this may cause problem when this code is run under
-                // the SYSTEM identity on Windows (e.g. ASP.NET). See
-                // http://bugzilla.ximian.com/show_bug.cgi?id=77559
-                try
-                {
-                    CspParameters csp = new CspParameters();
-                    csp.Flags = CspProviderFlags.UseMachineKeyStore;
-                    rsa = new RSACryptoServiceProvider(csp);
-                    rsa.ImportParameters(rsap);
-                }
-                catch
-                {
-                    // rethrow original, not the later, exception if this fails
-                    throw ce;
-                }
-            }
-            return rsa;
-		}
-
-		static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob)
-		{
-			return FromCapiPrivateKeyBlobDSA (blob, 0);
-		}
-
-		static public DSA FromCapiPrivateKeyBlobDSA (byte[] blob, int offset)
-		{
-			if (blob == null)
-				throw new ArgumentNullException ("blob");
-			if (offset >= blob.Length)
-				throw new ArgumentException ("blob is too small.");
-
-			DSAParameters dsap = new DSAParameters ();
-			try {
-				if ((blob [offset] != 0x07) ||				// PRIVATEKEYBLOB (0x07)
-				    (blob [offset + 1] != 0x02) ||			// Version (0x02)
-				    (blob [offset + 2] != 0x00) ||			// Reserved (word)
-				    (blob [offset + 3] != 0x00) ||
-				    (ToUInt32LE (blob, offset + 8) != 0x32535344))	// DWORD magic
-					throw new CryptographicException ("Invalid blob header");
-
-				int bitlen = ToInt32LE (blob, offset + 12);
-				int bytelen = bitlen >> 3;
-				int pos = offset + 16;
-
-				dsap.P = new byte [bytelen];
-				Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen);
-				Array.Reverse (dsap.P);
-				pos += bytelen;
-
-				dsap.Q = new byte [20];
-				Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20);
-				Array.Reverse (dsap.Q);
-				pos += 20;
-
-				dsap.G = new byte [bytelen];
-				Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen);
-				Array.Reverse (dsap.G);
-				pos += bytelen;
-
-				dsap.X = new byte [20];
-				Buffer.BlockCopy (blob, pos, dsap.X, 0, 20);
-				Array.Reverse (dsap.X);
-				pos += 20;
-
-				dsap.Counter = ToInt32LE (blob, pos);
-				pos += 4;
-
-				dsap.Seed = new byte [20];
-				Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20);
-				Array.Reverse (dsap.Seed);
-				pos += 20;
-			}
-			catch (Exception e) {
-				throw new CryptographicException ("Invalid blob.", e);
-			}
-
-            DSA dsa = null;
-            try
-            {
-                dsa = (DSA)DSA.Create();
-                dsa.ImportParameters(dsap);
-            }
-            catch (CryptographicException ce)
-            {
-                // this may cause problem when this code is run under
-                // the SYSTEM identity on Windows (e.g. ASP.NET). See
-                // http://bugzilla.ximian.com/show_bug.cgi?id=77559
-                try
-                {
-                    CspParameters csp = new CspParameters();
-                    csp.Flags = CspProviderFlags.UseMachineKeyStore;
-                    dsa = new DSACryptoServiceProvider(csp);
-                    dsa.ImportParameters(dsap);
-                }
-                catch
-                {
-                    // rethrow original, not the later, exception if this fails
-                    throw ce;
-                }
-            }
-            return dsa;
-		}
-
-		static public byte[] ToCapiPrivateKeyBlob (RSA rsa) 
-		{
-			RSAParameters p = rsa.ExportParameters (true);
-			int keyLength = p.Modulus.Length; // in bytes
-			byte[] blob = new byte [20 + (keyLength << 2) + (keyLength >> 1)];
-
-			blob [0] = 0x07;	// Type - PRIVATEKEYBLOB (0x07)
-			blob [1] = 0x02;	// Version - Always CUR_BLOB_VERSION (0x02)
-			// [2], [3]		// RESERVED - Always 0
-			blob [5] = 0x24;	// ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
-			blob [8] = 0x52;	// Magic - RSA2 (ASCII in hex)
-			blob [9] = 0x53;
-			blob [10] = 0x41;
-			blob [11] = 0x32;
-
-			byte[] bitlen = GetBytesLE (keyLength << 3);
-			blob [12] = bitlen [0];	// bitlen
-			blob [13] = bitlen [1];	
-			blob [14] = bitlen [2];	
-			blob [15] = bitlen [3];
-
-			// public exponent (DWORD)
-			int pos = 16;
-			int n = p.Exponent.Length;
-			while (n > 0)
-				blob [pos++] = p.Exponent [--n];
-			// modulus
-			pos = 20;
-			byte[] part = p.Modulus;
-			int len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-			// private key
-			part = p.P;
-			len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-
-			part = p.Q;
-			len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-
-			part = p.DP;
-			len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-
-			part = p.DQ;
-			len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-
-			part = p.InverseQ;
-			len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-
-			part = p.D;
-			len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-
-			return blob;
-		}
-
-		static public byte[] ToCapiPrivateKeyBlob (DSA dsa)
-		{
-			DSAParameters p = dsa.ExportParameters (true);
-			int keyLength = p.P.Length; // in bytes
-
-			// header + P + Q + G + X + count + seed
-			byte[] blob = new byte [16 + keyLength + 20 + keyLength + 20 + 4 + 20];
-
-			blob [0] = 0x07;	// Type - PRIVATEKEYBLOB (0x07)
-			blob [1] = 0x02;	// Version - Always CUR_BLOB_VERSION (0x02)
-			// [2], [3]		// RESERVED - Always 0
-			blob [5] = 0x22;	// ALGID
-			blob [8] = 0x44;	// Magic
-			blob [9] = 0x53;
-			blob [10] = 0x53;
-			blob [11] = 0x32;
-
-			byte[] bitlen = GetBytesLE (keyLength << 3);
-			blob [12] = bitlen [0];
-			blob [13] = bitlen [1];
-			blob [14] = bitlen [2];
-			blob [15] = bitlen [3];
-
-			int pos = 16;
-			byte[] part = p.P;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, keyLength);
-			pos += keyLength;
-
-			part = p.Q;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, 20);
-			pos += 20;
-
-			part = p.G;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, keyLength);
-			pos += keyLength;
-
-			part = p.X;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, 20);
-			pos += 20;
-
-			Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4);
-			pos += 4;
-
-			part = p.Seed;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, 20);
-
-			return blob;
-		}
-
-		static public RSA FromCapiPublicKeyBlob (byte[] blob) 
-		{
-			return FromCapiPublicKeyBlob (blob, 0);
-		}
-
-		static public RSA FromCapiPublicKeyBlob (byte[] blob, int offset) 
-		{
-			if (blob == null)
-				throw new ArgumentNullException ("blob");
-			if (offset >= blob.Length)
-				throw new ArgumentException ("blob is too small.");
-
-			try {
-				if ((blob [offset]   != 0x06) ||				// PUBLICKEYBLOB (0x06)
-				    (blob [offset+1] != 0x02) ||				// Version (0x02)
-				    (blob [offset+2] != 0x00) ||				// Reserved (word)
-				    (blob [offset+3] != 0x00) || 
-				    (ToUInt32LE (blob, offset+8) != 0x31415352))	// DWORD magic = RSA1
-					throw new CryptographicException ("Invalid blob header");
-
-				// ALGID (CALG_RSA_SIGN, CALG_RSA_KEYX, ...)
-				// int algId = ToInt32LE (blob, offset+4);
-
-				// DWORD bitlen
-				int bitLen = ToInt32LE (blob, offset+12);
-
-				// DWORD public exponent
-				RSAParameters rsap = new RSAParameters ();
-				rsap.Exponent = new byte [3];
-				rsap.Exponent [0] = blob [offset+18];
-				rsap.Exponent [1] = blob [offset+17];
-				rsap.Exponent [2] = blob [offset+16];
-			
-				int pos = offset+20;
-				// BYTE modulus[rsapubkey.bitlen/8];
-				int byteLen = (bitLen >> 3);
-				rsap.Modulus = new byte [byteLen];
-				Buffer.BlockCopy (blob, pos, rsap.Modulus, 0, byteLen);
-				Array.Reverse (rsap.Modulus);
-                RSA rsa = null;
-                try
-                {
-                    rsa = RSA.Create();
-                    rsa.ImportParameters(rsap);
-                }
-                catch (CryptographicException)
-                {
-                    // this may cause problem when this code is run under
-                    // the SYSTEM identity on Windows (e.g. ASP.NET). See
-                    // http://bugzilla.ximian.com/show_bug.cgi?id=77559
-                    CspParameters csp = new CspParameters();
-                    csp.Flags = CspProviderFlags.UseMachineKeyStore;
-                    rsa = new RSACryptoServiceProvider(csp);
-                    rsa.ImportParameters(rsap);
-                }
-                return rsa;
-			}
-			catch (Exception e) {
-				throw new CryptographicException ("Invalid blob.", e);
-			}
-		}
-
-		static public DSA FromCapiPublicKeyBlobDSA (byte[] blob)
-		{
-			return FromCapiPublicKeyBlobDSA (blob, 0);
-		}
-
-		static public DSA FromCapiPublicKeyBlobDSA (byte[] blob, int offset)
-		{
-			if (blob == null)
-				throw new ArgumentNullException ("blob");
-			if (offset >= blob.Length)
-				throw new ArgumentException ("blob is too small.");
-
-			try {
-				if ((blob [offset] != 0x06) ||				// PUBLICKEYBLOB (0x06)
-				    (blob [offset + 1] != 0x02) ||			// Version (0x02)
-				    (blob [offset + 2] != 0x00) ||			// Reserved (word)
-				    (blob [offset + 3] != 0x00) ||
-				    (ToUInt32LE (blob, offset + 8) != 0x31535344))	// DWORD magic
-					throw new CryptographicException ("Invalid blob header");
-
-				int bitlen = ToInt32LE (blob, offset + 12);
-				DSAParameters dsap = new DSAParameters ();
-				int bytelen = bitlen >> 3;
-				int pos = offset + 16;
-
-				dsap.P = new byte [bytelen];
-				Buffer.BlockCopy (blob, pos, dsap.P, 0, bytelen);
-				Array.Reverse (dsap.P);
-				pos += bytelen;
-
-				dsap.Q = new byte [20];
-				Buffer.BlockCopy (blob, pos, dsap.Q, 0, 20);
-				Array.Reverse (dsap.Q);
-				pos += 20;
-
-				dsap.G = new byte [bytelen];
-				Buffer.BlockCopy (blob, pos, dsap.G, 0, bytelen);
-				Array.Reverse (dsap.G);
-				pos += bytelen;
-
-				dsap.Y = new byte [bytelen];
-				Buffer.BlockCopy (blob, pos, dsap.Y, 0, bytelen);
-				Array.Reverse (dsap.Y);
-				pos += bytelen;
-
-				dsap.Counter = ToInt32LE (blob, pos);
-				pos += 4;
-
-				dsap.Seed = new byte [20];
-				Buffer.BlockCopy (blob, pos, dsap.Seed, 0, 20);
-				Array.Reverse (dsap.Seed);
-				pos += 20;
-
-				DSA dsa = (DSA)DSA.Create ();
-				dsa.ImportParameters (dsap);
-				return dsa;
-			}
-			catch (Exception e) {
-				throw new CryptographicException ("Invalid blob.", e);
-			}
-		}
-
-		static public byte[] ToCapiPublicKeyBlob (RSA rsa) 
-		{
-			RSAParameters p = rsa.ExportParameters (false);
-			int keyLength = p.Modulus.Length; // in bytes
-			byte[] blob = new byte [20 + keyLength];
-
-			blob [0] = 0x06;	// Type - PUBLICKEYBLOB (0x06)
-			blob [1] = 0x02;	// Version - Always CUR_BLOB_VERSION (0x02)
-			// [2], [3]		// RESERVED - Always 0
-			blob [5] = 0x24;	// ALGID - Always 00 24 00 00 (for CALG_RSA_SIGN)
-			blob [8] = 0x52;	// Magic - RSA1 (ASCII in hex)
-			blob [9] = 0x53;
-			blob [10] = 0x41;
-			blob [11] = 0x31;
-
-			byte[] bitlen = GetBytesLE (keyLength << 3);
-			blob [12] = bitlen [0];	// bitlen
-			blob [13] = bitlen [1];	
-			blob [14] = bitlen [2];	
-			blob [15] = bitlen [3];
-
-			// public exponent (DWORD)
-			int pos = 16;
-			int n = p.Exponent.Length;
-			while (n > 0)
-				blob [pos++] = p.Exponent [--n];
-			// modulus
-			pos = 20;
-			byte[] part = p.Modulus;
-			int len = part.Length;
-			Array.Reverse (part, 0, len);
-			Buffer.BlockCopy (part, 0, blob, pos, len);
-			pos += len;
-			return blob;
-		}
-
-		static public byte[] ToCapiPublicKeyBlob (DSA dsa)
-		{
-			DSAParameters p = dsa.ExportParameters (false);
-			int keyLength = p.P.Length; // in bytes
-
-			// header + P + Q + G + Y + count + seed
-			byte[] blob = new byte [16 + keyLength + 20 + keyLength + keyLength + 4 + 20];
-
-			blob [0] = 0x06;	// Type - PUBLICKEYBLOB (0x06)
-			blob [1] = 0x02;	// Version - Always CUR_BLOB_VERSION (0x02)
-			// [2], [3]		// RESERVED - Always 0
-			blob [5] = 0x22;	// ALGID
-			blob [8] = 0x44;	// Magic
-			blob [9] = 0x53;
-			blob [10] = 0x53;
-			blob [11] = 0x31;
-
-			byte[] bitlen = GetBytesLE (keyLength << 3);
-			blob [12] = bitlen [0];
-			blob [13] = bitlen [1];
-			blob [14] = bitlen [2];
-			blob [15] = bitlen [3];
-
-			int pos = 16;
-			byte[] part;
-
-			part = p.P;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, keyLength);
-			pos += keyLength;
-
-			part = p.Q;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, 20);
-			pos += 20;
-
-			part = p.G;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, keyLength);
-			pos += keyLength;
-
-			part = p.Y;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, keyLength);
-			pos += keyLength;
-
-			Buffer.BlockCopy (GetBytesLE (p.Counter), 0, blob, pos, 4);
-			pos += 4;
-
-			part = p.Seed;
-			Array.Reverse (part);
-			Buffer.BlockCopy (part, 0, blob, pos, 20);
-
-			return blob;
-		}
-
-		// PRIVATEKEYBLOB
-		// PUBLICKEYBLOB
-		static public RSA FromCapiKeyBlob (byte[] blob) 
-		{
-			return FromCapiKeyBlob (blob, 0);
-		}
-
-		static public RSA FromCapiKeyBlob (byte[] blob, int offset) 
-		{
-			if (blob == null)
-				throw new ArgumentNullException ("blob");
-			if (offset >= blob.Length)
-				throw new ArgumentException ("blob is too small.");
-
-			switch (blob [offset]) {
-				case 0x00:
-					// this could be a public key inside an header
-					// like "sn -e" would produce
-					if (blob [offset + 12] == 0x06) {
-						return FromCapiPublicKeyBlob (blob, offset + 12);
-					}
-					break;
-				case 0x06:
-					return FromCapiPublicKeyBlob (blob, offset);
-				case 0x07:
-					return FromCapiPrivateKeyBlob (blob, offset);
-			}
-			throw new CryptographicException ("Unknown blob format.");
-		}
-
-		static public DSA FromCapiKeyBlobDSA (byte[] blob)
-		{
-			return FromCapiKeyBlobDSA (blob, 0);
-		}
-
-		static public DSA FromCapiKeyBlobDSA (byte[] blob, int offset)
-		{
-			if (blob == null)
-				throw new ArgumentNullException ("blob");
-			if (offset >= blob.Length)
-				throw new ArgumentException ("blob is too small.");
-
-			switch (blob [offset]) {
-				case 0x06:
-					return FromCapiPublicKeyBlobDSA (blob, offset);
-				case 0x07:
-					return FromCapiPrivateKeyBlobDSA (blob, offset);
-			}
-			throw new CryptographicException ("Unknown blob format.");
-		}
-
-		static public byte[] ToCapiKeyBlob (AsymmetricAlgorithm keypair, bool includePrivateKey) 
-		{
-			if (keypair == null)
-				throw new ArgumentNullException ("keypair");
-
-			// check between RSA and DSA (and potentially others like DH)
-			if (keypair is RSA)
-				return ToCapiKeyBlob ((RSA)keypair, includePrivateKey);
-			else if (keypair is DSA)
-				return ToCapiKeyBlob ((DSA)keypair, includePrivateKey);
-			else
-				return null;	// TODO
-		}
-
-		static public byte[] ToCapiKeyBlob (RSA rsa, bool includePrivateKey) 
-		{
-			if (rsa == null)
-				throw new ArgumentNullException ("rsa");
-
-			if (includePrivateKey)
-				return ToCapiPrivateKeyBlob (rsa);
-			else
-				return ToCapiPublicKeyBlob (rsa);
-		}
-
-		static public byte[] ToCapiKeyBlob (DSA dsa, bool includePrivateKey)
-		{
-			if (dsa == null)
-				throw new ArgumentNullException ("dsa");
-
-			if (includePrivateKey)
-				return ToCapiPrivateKeyBlob (dsa);
-			else
-				return ToCapiPublicKeyBlob (dsa);
-		}
-
-		static public string ToHex (byte[] input) 
-		{
-			if (input == null)
-				return null;
-
-			StringBuilder sb = new StringBuilder (input.Length * 2);
-			foreach (byte b in input) {
-				sb.Append (b.ToString ("X2", CultureInfo.InvariantCulture));
-			}
-			return sb.ToString ();
-		}
-
-		static private byte FromHexChar (char c) 
-		{
-			if ((c >= 'a') && (c <= 'f'))
-				return (byte) (c - 'a' + 10);
-			if ((c >= 'A') && (c <= 'F'))
-				return (byte) (c - 'A' + 10);
-			if ((c >= '0') && (c <= '9'))
-				return (byte) (c - '0');
-			throw new ArgumentException ("invalid hex char");
-		}
-
-		static public byte[] FromHex (string hex) 
-		{
-			if (hex == null)
-				return null;
-			if ((hex.Length & 0x1) == 0x1)
-				throw new ArgumentException ("Length must be a multiple of 2");
-
-			byte[] result = new byte [hex.Length >> 1];
-			int n = 0;
-			int i = 0;
-			while (n < result.Length) {
-				result [n] = (byte) (FromHexChar (hex [i++]) << 4);
-				result [n++] += FromHexChar (hex [i++]);
-			}
-			return result;
-		}
-	}
-}

+ 0 - 491
Emby.Server.Implementations/Cryptography/PKCS1.cs

@@ -1,491 +0,0 @@
-//
-// PKCS1.cs - Implements PKCS#1 primitives.
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@xamarin.com>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
-// Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Security.Cryptography;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    // References:
-    // a.	PKCS#1: RSA Cryptography Standard 
-    //	http://www.rsasecurity.com/rsalabs/pkcs/pkcs-1/index.html
-
-    public sealed class PKCS1 {
-
-		private PKCS1 () 
-		{
-		}
-
-		private static bool Compare (byte[] array1, byte[] array2) 
-		{
-			bool result = (array1.Length == array2.Length);
-			if (result) {
-				for (int i=0; i < array1.Length; i++)
-					if (array1[i] != array2[i])
-						return false;
-			}
-			return result;
-		}
-	
-		private static byte[] xor (byte[] array1, byte[] array2) 
-		{
-			byte[] result = new byte [array1.Length];
-			for (int i=0; i < result.Length; i++)
-				result[i] = (byte) (array1[i] ^ array2[i]);
-			return result;
-		}
-	
-		private static byte[] emptySHA1   = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 };
-		private static byte[] emptySHA256 = { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 };
-		private static byte[] emptySHA384 = { 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, 0x98, 0xb9, 0x5b };
-		private static byte[] emptySHA512 = { 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, 0x3e };
-	
-		private static byte[] GetEmptyHash (HashAlgorithm hash) 
-		{
-			if (hash is SHA1)
-				return emptySHA1;
-			else if (hash is SHA256)
-				return emptySHA256;
-			else if (hash is SHA384)
-				return emptySHA384;
-			else if (hash is SHA512)
-				return emptySHA512;
-			else
-				return hash.ComputeHash ((byte[])null);
-		}
-	
-		// PKCS #1 v.2.1, Section 4.1
-		// I2OSP converts a non-negative integer to an octet string of a specified length.
-		public static byte[] I2OSP (int x, int size) 
-		{
-			byte[] array = BitConverterLE.GetBytes (x);
-			Array.Reverse (array, 0, array.Length);
-			return I2OSP (array, size);
-		}
-	
-		public static byte[] I2OSP (byte[] x, int size) 
-		{
-			byte[] result = new byte [size];
-			Buffer.BlockCopy (x, 0, result, (result.Length - x.Length), x.Length);
-			return result;
-		}
-	
-		// PKCS #1 v.2.1, Section 4.2
-		// OS2IP converts an octet string to a nonnegative integer.
-		public static byte[] OS2IP (byte[] x) 
-		{
-			int i = 0;
-			while ((x [i++] == 0x00) && (i < x.Length)) {
-				// confuse compiler into reporting a warning with {}
-			}
-			i--;
-			if (i > 0) {
-				byte[] result = new byte [x.Length - i];
-				Buffer.BlockCopy (x, i, result, 0, result.Length);
-				return result;
-			}
-			else
-				return x;
-		}
-	
-		// PKCS #1 v.2.1, Section 5.1.1
-		public static byte[] RSAEP (RSA rsa, byte[] m) 
-		{
-			// c = m^e mod n
-			return rsa.EncryptValue (m);
-		}
-	
-		// PKCS #1 v.2.1, Section 5.1.2
-		public static byte[] RSADP (RSA rsa, byte[] c) 
-		{
-			// m = c^d mod n
-			// Decrypt value may apply CRT optimizations
-			return rsa.DecryptValue (c);
-		}
-	
-		// PKCS #1 v.2.1, Section 5.2.1
-		public static byte[] RSASP1 (RSA rsa, byte[] m) 
-		{
-			// first form: s = m^d mod n
-			// Decrypt value may apply CRT optimizations
-			return rsa.DecryptValue (m);
-		}
-	
-		// PKCS #1 v.2.1, Section 5.2.2
-		public static byte[] RSAVP1 (RSA rsa, byte[] s) 
-		{
-			// m = s^e mod n
-			return rsa.EncryptValue (s);
-		}
-	
-		// PKCS #1 v.2.1, Section 7.1.1
-		// RSAES-OAEP-ENCRYPT ((n, e), M, L)
-		public static byte[] Encrypt_OAEP (RSA rsa, HashAlgorithm hash, RandomNumberGenerator rng, byte[] M) 
-		{
-			int size = rsa.KeySize / 8;
-			int hLen = hash.HashSize / 8;
-			if (M.Length > size - 2 * hLen - 2)
-				throw new CryptographicException ("message too long");
-			// empty label L SHA1 hash
-			byte[] lHash = GetEmptyHash (hash);
-			int PSLength = (size - M.Length - 2 * hLen - 2);
-			// DB = lHash || PS || 0x01 || M
-			byte[] DB = new byte [lHash.Length + PSLength + 1 + M.Length];
-			Buffer.BlockCopy (lHash, 0, DB, 0, lHash.Length);
-			DB [(lHash.Length + PSLength)] = 0x01;
-			Buffer.BlockCopy (M, 0, DB, (DB.Length - M.Length), M.Length);
-	
-			byte[] seed = new byte [hLen];
-			rng.GetBytes (seed);
-	
-			byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
-			byte[] maskedDB = xor (DB, dbMask);
-			byte[] seedMask = MGF1 (hash, maskedDB, hLen);
-			byte[] maskedSeed = xor (seed, seedMask);
-			// EM = 0x00 || maskedSeed || maskedDB
-			byte[] EM = new byte [maskedSeed.Length + maskedDB.Length + 1];
-			Buffer.BlockCopy (maskedSeed, 0, EM, 1, maskedSeed.Length);
-			Buffer.BlockCopy (maskedDB, 0, EM, maskedSeed.Length + 1, maskedDB.Length);
-	
-			byte[] m = OS2IP (EM);
-			byte[] c = RSAEP (rsa, m);
-			return I2OSP (c, size);
-		}
-	
-		// PKCS #1 v.2.1, Section 7.1.2
-		// RSAES-OAEP-DECRYPT (K, C, L)
-		public static byte[] Decrypt_OAEP (RSA rsa, HashAlgorithm hash, byte[] C) 
-		{
-			int size = rsa.KeySize / 8;
-			int hLen = hash.HashSize / 8;
-			if ((size < (2 * hLen + 2)) || (C.Length != size))
-				throw new CryptographicException ("decryption error");
-	
-			byte[] c = OS2IP (C);
-			byte[] m = RSADP (rsa, c);
-			byte[] EM = I2OSP (m, size);
-	
-			// split EM = Y || maskedSeed || maskedDB
-			byte[] maskedSeed = new byte [hLen];
-			Buffer.BlockCopy (EM, 1, maskedSeed, 0, maskedSeed.Length);
-			byte[] maskedDB = new byte [size - hLen - 1];
-			Buffer.BlockCopy (EM, (EM.Length - maskedDB.Length), maskedDB, 0, maskedDB.Length);
-	
-			byte[] seedMask = MGF1 (hash, maskedDB, hLen);
-			byte[] seed = xor (maskedSeed, seedMask);
-			byte[] dbMask = MGF1 (hash, seed, size - hLen - 1);
-			byte[] DB = xor (maskedDB, dbMask);
-	
-			byte[] lHash = GetEmptyHash (hash);
-			// split DB = lHash' || PS || 0x01 || M
-			byte[] dbHash = new byte [lHash.Length];
-			Buffer.BlockCopy (DB, 0, dbHash, 0, dbHash.Length);
-			bool h = Compare (lHash, dbHash);
-	
-			// find separator 0x01
-			int nPos = lHash.Length;
-			while (DB[nPos] == 0)
-				nPos++;
-	
-			int Msize = DB.Length - nPos - 1;
-			byte[] M = new byte [Msize];
-			Buffer.BlockCopy (DB, (nPos + 1), M, 0, Msize);
-	
-			// we could have returned EM[0] sooner but would be helping a timing attack
-			if ((EM[0] != 0) || (!h) || (DB[nPos] != 0x01))
-				return null;
-			return M;
-		}
-	
-		// PKCS #1 v.2.1, Section 7.2.1
-		// RSAES-PKCS1-V1_5-ENCRYPT ((n, e), M)
-		public static byte[] Encrypt_v15 (RSA rsa, RandomNumberGenerator rng, byte[] M) 
-		{
-			int size = rsa.KeySize / 8;
-			if (M.Length > size - 11)
-				throw new CryptographicException ("message too long");
-			int PSLength = System.Math.Max (8, (size - M.Length - 3));
-			byte[] PS = new byte [PSLength];
-			rng.GetNonZeroBytes (PS);
-			byte[] EM = new byte [size];
-			EM [1] = 0x02;
-			Buffer.BlockCopy (PS, 0, EM, 2, PSLength);
-			Buffer.BlockCopy (M, 0, EM, (size - M.Length), M.Length);
-	
-			byte[] m = OS2IP (EM);
-			byte[] c = RSAEP (rsa, m);
-			byte[] C = I2OSP (c, size);
-			return C;
-		}
-	
-		// PKCS #1 v.2.1, Section 7.2.2
-		// RSAES-PKCS1-V1_5-DECRYPT (K, C)
-		public static byte[] Decrypt_v15 (RSA rsa, byte[] C) 
-		{
-			int size = rsa.KeySize >> 3; // div by 8
-			if ((size < 11) || (C.Length > size))
-				throw new CryptographicException ("decryption error");
-			byte[] c = OS2IP (C);
-			byte[] m = RSADP (rsa, c);
-			byte[] EM = I2OSP (m, size);
-	
-			if ((EM [0] != 0x00) || (EM [1] != 0x02))
-				return null;
-	
-			int mPos = 10;
-			// PS is a minimum of 8 bytes + 2 bytes for header
-			while ((EM [mPos] != 0x00) && (mPos < EM.Length))
-				mPos++;
-			if (EM [mPos] != 0x00)
-				return null;
-			mPos++;
-			byte[] M = new byte [EM.Length - mPos];
-			Buffer.BlockCopy (EM, mPos, M, 0, M.Length);
-			return M;
-		}
-	
-		// PKCS #1 v.2.1, Section 8.2.1
-		// RSASSA-PKCS1-V1_5-SIGN (K, M)
-		public static byte[] Sign_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue) 
-		{
-			int size = (rsa.KeySize >> 3); // div 8
-			byte[] EM = Encode_v15 (hash, hashValue, size);
-			byte[] m = OS2IP (EM);
-			byte[] s = RSASP1 (rsa, m);
-			byte[] S = I2OSP (s, size);
-			return S;
-		}
-
-		internal static byte[] Sign_v15 (RSA rsa, string hashName, byte[] hashValue) 
-		{
-			using (var hash = CreateFromName (hashName))
-				return Sign_v15 (rsa, hash, hashValue);
-		}
-
-		// PKCS #1 v.2.1, Section 8.2.2
-		// RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)
-		public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte[] hashValue, byte[] signature) 
-		{
-			return Verify_v15 (rsa, hash, hashValue, signature, false);
-		}
-
-		internal static bool Verify_v15 (RSA rsa, string hashName, byte[] hashValue, byte[] signature) 
-		{
-			using (var hash = CreateFromName (hashName))
-				return Verify_v15 (rsa, hash, hashValue, signature, false);
-		}
-
-		// DO NOT USE WITHOUT A VERY GOOD REASON
-		public static bool Verify_v15 (RSA rsa, HashAlgorithm hash, byte [] hashValue, byte [] signature, bool tryNonStandardEncoding)
-		{
-			int size = (rsa.KeySize >> 3); // div 8
-			byte[] s = OS2IP (signature);
-			byte[] m = RSAVP1 (rsa, s);
-			byte[] EM2 = I2OSP (m, size);
-			byte[] EM = Encode_v15 (hash, hashValue, size);
-			bool result = Compare (EM, EM2);
-			if (result || !tryNonStandardEncoding)
-				return result;
-
-			// NOTE: some signatures don't include the hash OID (pretty lame but real)
-			// and compatible with MS implementation. E.g. Verisign Authenticode Timestamps
-
-			// we're making this "as safe as possible"
-			if ((EM2 [0] != 0x00) || (EM2 [1] != 0x01))
-				return false;
-			int i;
-			for (i = 2; i < EM2.Length - hashValue.Length - 1; i++) {
-				if (EM2 [i] != 0xFF)
-					return false;
-			}
-			if (EM2 [i++] != 0x00)
-				return false;
-
-			byte [] decryptedHash = new byte [hashValue.Length];
-			Buffer.BlockCopy (EM2, i, decryptedHash, 0, decryptedHash.Length);
-			return Compare (decryptedHash, hashValue);
-		}
-	
-		// PKCS #1 v.2.1, Section 9.2
-		// EMSA-PKCS1-v1_5-Encode
-		public static byte[] Encode_v15 (HashAlgorithm hash, byte[] hashValue, int emLength) 
-		{
-			if (hashValue.Length != (hash.HashSize >> 3))
-				throw new CryptographicException ("bad hash length for " + hash.ToString ());
-
-			// DigestInfo ::= SEQUENCE {
-			//	digestAlgorithm AlgorithmIdentifier,
-			//	digest OCTET STRING
-			// }
-		
-			byte[] t = null;
-
-			string oid = CryptoConfig.MapNameToOID (hash.ToString ());
-			if (oid != null)
-			{
-				ASN1 digestAlgorithm = new ASN1 (0x30);
-				digestAlgorithm.Add (new ASN1 (CryptoConfig.EncodeOID (oid)));
-				digestAlgorithm.Add (new ASN1 (0x05));		// NULL
-				ASN1 digest = new ASN1 (0x04, hashValue);
-				ASN1 digestInfo = new ASN1 (0x30);
-				digestInfo.Add (digestAlgorithm);
-				digestInfo.Add (digest);
-
-				t = digestInfo.GetBytes ();
-			}
-			else
-			{
-				// There are no valid OID, in this case t = hashValue
-				// This is the case of the MD5SHA hash algorithm
-				t = hashValue;
-			}
-
-			Buffer.BlockCopy (hashValue, 0, t, t.Length - hashValue.Length, hashValue.Length);
-	
-			int PSLength = System.Math.Max (8, emLength - t.Length - 3);
-			// PS = PSLength of 0xff
-	
-			// EM = 0x00 | 0x01 | PS | 0x00 | T
-			byte[] EM = new byte [PSLength + t.Length + 3];
-			EM [1] = 0x01;
-			for (int i=2; i < PSLength + 2; i++)
-				EM[i] = 0xff;
-			Buffer.BlockCopy (t, 0, EM, PSLength + 3, t.Length);
-	
-			return EM;
-		}
-	
-		// PKCS #1 v.2.1, Section B.2.1
-		public static byte[] MGF1 (HashAlgorithm hash, byte[] mgfSeed, int maskLen) 
-		{
-			// 1. If maskLen > 2^32 hLen, output "mask too long" and stop.
-			// easy - this is impossible by using a int (31bits) as parameter ;-)
-			// BUT with a signed int we do have to check for negative values!
-			if (maskLen < 0)
-				throw new OverflowException();
-	
-			int mgfSeedLength = mgfSeed.Length;
-			int hLen = (hash.HashSize >> 3); // from bits to bytes
-			int iterations = (maskLen / hLen);
-			if (maskLen % hLen != 0)
-				iterations++;
-			// 2. Let T be the empty octet string.
-			byte[] T = new byte [iterations * hLen];
-	
-			byte[] toBeHashed = new byte [mgfSeedLength + 4];
-			int pos = 0;
-			// 3. For counter from 0 to \ceil (maskLen / hLen) - 1, do the following:
-			for (int counter = 0; counter < iterations; counter++) {
-				// a.	Convert counter to an octet string C of length 4 octets
-				byte[] C = I2OSP (counter, 4); 
-	
-				// b.	Concatenate the hash of the seed mgfSeed and C to the octet string T:
-				//	T = T || Hash (mgfSeed || C)
-				Buffer.BlockCopy (mgfSeed, 0, toBeHashed, 0, mgfSeedLength);
-				Buffer.BlockCopy (C, 0, toBeHashed, mgfSeedLength, 4);
-				byte[] output = hash.ComputeHash (toBeHashed);
-				Buffer.BlockCopy (output, 0, T, pos, hLen);
-				pos += hLen;
-			}
-			
-			// 4. Output the leading maskLen octets of T as the octet string mask.
-			byte[] mask = new byte [maskLen];
-			Buffer.BlockCopy (T, 0, mask, 0, maskLen);
-			return mask;
-		}
-
-		static internal string HashNameFromOid (string oid, bool throwOnError = true)
-		{
-			switch (oid) {
-			case "1.2.840.113549.1.1.2":	// MD2 with RSA encryption 
-				return "MD2";
-			case "1.2.840.113549.1.1.3":	// MD4 with RSA encryption 
-				return "MD4";
-			case "1.2.840.113549.1.1.4":	// MD5 with RSA encryption 
-				return "MD5";
-			case "1.2.840.113549.1.1.5":	// SHA-1 with RSA Encryption 
-			case "1.3.14.3.2.29":		// SHA1 with RSA signature 
-			case "1.2.840.10040.4.3":	// SHA1-1 with DSA
-				return "SHA1";
-			case "1.2.840.113549.1.1.11":	// SHA-256 with RSA Encryption
-				return "SHA256";
-			case "1.2.840.113549.1.1.12":	// SHA-384 with RSA Encryption
-				return "SHA384";
-			case "1.2.840.113549.1.1.13":	// SHA-512 with RSA Encryption
-				return "SHA512";
-			case "1.3.36.3.3.1.2":
-				return "RIPEMD160";
-			default:
-				if (throwOnError)
-					throw new CryptographicException ("Unsupported hash algorithm: " + oid);
-				return null;
-			}
-		}
-		
-		static internal HashAlgorithm CreateFromOid (string oid)
-		{
-			return CreateFromName (HashNameFromOid (oid));
-		}
-		
-		static internal HashAlgorithm CreateFromName (string name)
-		{
-#if FULL_AOT_RUNTIME
-			switch (name) {
-			case "MD2":
-				return MD2.Create ();
-			case "MD4":
-				return MD4.Create ();
-			case "MD5":
-				return MD5.Create ();
-			case "SHA1":
-				return SHA1.Create ();
-			case "SHA256":
-				return SHA256.Create ();
-			case "SHA384":
-				return SHA384.Create ();
-			case "SHA512":
-				return SHA512.Create ();
-			case "RIPEMD160":
-				return RIPEMD160.Create ();
-			default:
-				try {
-					return (HashAlgorithm) Activator.CreateInstance (Type.GetType (name));
-				}
-				catch {
-					throw new CryptographicException ("Unsupported hash algorithm: " + name);
-				}
-			}
-#else
-			return HashAlgorithm.Create (name);
-#endif
-		}
-	}
-}

+ 0 - 1934
Emby.Server.Implementations/Cryptography/PKCS12.cs

@@ -1,1934 +0,0 @@
-//
-// PKCS12.cs: PKCS 12 - Personal Information Exchange Syntax
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@xamarin.com>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004,2005,2006 Novell Inc. (http://www.novell.com)
-// Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
-//
-// Key derivation translated from Bouncy Castle JCE (http://www.bouncycastle.org/)
-// See bouncycastle.txt for license.
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections;
-using System.IO;
-using System.Security.Cryptography;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    public class PKCS5 {
-
-		public const string pbeWithMD2AndDESCBC = "1.2.840.113549.1.5.1";
-		public const string pbeWithMD5AndDESCBC = "1.2.840.113549.1.5.3";
-		public const string pbeWithMD2AndRC2CBC = "1.2.840.113549.1.5.4";
-		public const string pbeWithMD5AndRC2CBC = "1.2.840.113549.1.5.6";
-		public const string pbeWithSHA1AndDESCBC = "1.2.840.113549.1.5.10";
-		public const string pbeWithSHA1AndRC2CBC = "1.2.840.113549.1.5.11";
-
-		public PKCS5 () {}
-	}
-
-    public class PKCS9 {
-
-		public const string friendlyName = "1.2.840.113549.1.9.20";
-		public const string localKeyId = "1.2.840.113549.1.9.21";
-
-		public PKCS9 () {}
-	}
-
-
-	internal class SafeBag {
-		private string _bagOID;
-		private ASN1 _asn1;
-
-		public SafeBag(string bagOID, ASN1 asn1) {
-			_bagOID = bagOID;
-			_asn1 = asn1;
-		}
-
-		public string BagOID {
-			get { return _bagOID; }
-		}
-
-		public ASN1 ASN1 {
-			get { return _asn1; }
-		}
-	}
-
-
-    public class PKCS12 : ICloneable {
-
-		public const string pbeWithSHAAnd128BitRC4 = "1.2.840.113549.1.12.1.1";
-		public const string pbeWithSHAAnd40BitRC4 = "1.2.840.113549.1.12.1.2";
-		public const string pbeWithSHAAnd3KeyTripleDESCBC = "1.2.840.113549.1.12.1.3";
-		public const string pbeWithSHAAnd2KeyTripleDESCBC = "1.2.840.113549.1.12.1.4";
-		public const string pbeWithSHAAnd128BitRC2CBC = "1.2.840.113549.1.12.1.5";
-		public const string pbeWithSHAAnd40BitRC2CBC = "1.2.840.113549.1.12.1.6";
-
-		// bags
-		public const string keyBag  = "1.2.840.113549.1.12.10.1.1";
-		public const string pkcs8ShroudedKeyBag  = "1.2.840.113549.1.12.10.1.2";
-		public const string certBag  = "1.2.840.113549.1.12.10.1.3";
-		public const string crlBag  = "1.2.840.113549.1.12.10.1.4";
-		public const string secretBag  = "1.2.840.113549.1.12.10.1.5";
-		public const string safeContentsBag  = "1.2.840.113549.1.12.10.1.6";
-
-		// types
-		public const string x509Certificate = "1.2.840.113549.1.9.22.1";
- 		public const string sdsiCertificate = "1.2.840.113549.1.9.22.2";
-		public const string x509Crl = "1.2.840.113549.1.9.23.1";
-
-		// Adapted from BouncyCastle PKCS12ParametersGenerator.java
-		public class DeriveBytes {
-
-			public enum Purpose {
-				Key,
-				IV,
-				MAC
-			}
-
-			static private byte[] keyDiversifier = { 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 };
-			static private byte[] ivDiversifier  = { 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 };
-			static private byte[] macDiversifier = { 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3 };
-
-			private string _hashName;
-			private int _iterations;
-			private byte[] _password;
-			private byte[] _salt;
-
-			public DeriveBytes () {}
-
-			public string HashName {
-				get { return _hashName; } 
-				set { _hashName = value; }
-			}
-
-			public int IterationCount {
-				get { return _iterations; }
-				set { _iterations = value; }
-			}
-
-			public byte[] Password {
-				get { return (byte[]) _password.Clone (); }
-				set { 
-					if (value == null)
-						_password = new byte [0];
-					else
-						_password = (byte[]) value.Clone ();
-				}
-			}
-
-			public byte[] Salt {
-				get { return (byte[]) _salt.Clone ();  }
-				set {
-					if (value != null)
-						_salt = (byte[]) value.Clone ();
-					else
-						_salt = null;
-				}
-			}
-
-			private void Adjust (byte[] a, int aOff, byte[] b) 
-			{
-				int x = (b[b.Length - 1] & 0xff) + (a [aOff + b.Length - 1] & 0xff) + 1;
-
-				a [aOff + b.Length - 1] = (byte) x;
-				x >>= 8;
-
-				for (int i = b.Length - 2; i >= 0; i--) {
-					x += (b [i] & 0xff) + (a [aOff + i] & 0xff);
-					a [aOff + i] = (byte) x;
-					x >>= 8;
-				}
-			}
-
-			private byte[] Derive (byte[] diversifier, int n) 
-			{
-				HashAlgorithm digest = PKCS1.CreateFromName (_hashName);
-				int u = (digest.HashSize >> 3); // div 8
-				int v = 64;
-				byte[] dKey = new byte [n];
-
-				byte[] S;
-				if ((_salt != null) && (_salt.Length != 0)) {
-					S = new byte[v * ((_salt.Length + v - 1) / v)];
-
-					for (int i = 0; i != S.Length; i++) {
-						S[i] = _salt[i % _salt.Length];
-					}
-				}
-				else {
-					S = new byte[0];
-				}
-
-				byte[] P;
-				if ((_password != null) && (_password.Length != 0)) {
-					P = new byte[v * ((_password.Length + v - 1) / v)];
-
-					for (int i = 0; i != P.Length; i++) {
-						P[i] = _password[i % _password.Length];
-					}
-				}
-				else {
-					P = new byte[0];
-				}
-
-				byte[] I = new byte [S.Length + P.Length];
-
-				Buffer.BlockCopy (S, 0, I, 0, S.Length);
-				Buffer.BlockCopy (P, 0, I, S.Length, P.Length);
-
-				byte[]  B = new byte[v];
-				int     c = (n + u - 1) / u;
-
-				for (int i = 1; i <= c; i++) {
-					digest.TransformBlock (diversifier, 0, diversifier.Length, diversifier, 0);
-					digest.TransformFinalBlock (I, 0, I.Length);
-					byte[] A = digest.Hash;
-					digest.Initialize ();
-					for (int j = 1; j != _iterations; j++) {
-						A = digest.ComputeHash (A, 0, A.Length);
-					}
-
-					for (int j = 0; j != B.Length; j++) {
-						B [j] = A [j % A.Length];
-					}
-
-					for (int j = 0; j != I.Length / v; j++) {
-						Adjust (I, j * v, B);
-					}
-
-					if (i == c) {
-						Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, dKey.Length - ((i - 1) * u));
-					}
-					else {
-						Buffer.BlockCopy(A, 0, dKey, (i - 1) * u, A.Length);
-					}
-				}
-
-				return dKey;
-			}
-
-			public byte[] DeriveKey (int size) 
-			{
-				return Derive (keyDiversifier, size);
-			}
-
-			public byte[] DeriveIV (int size) 
-			{
-				return Derive (ivDiversifier, size);
-			}
-
-			public byte[] DeriveMAC (int size) 
-			{
-				return Derive (macDiversifier, size);
-			}
-		}
-
-		const int recommendedIterationCount = 2000;
-
-		//private int _version;
-		private byte[] _password;
-		private ArrayList _keyBags;
-		private ArrayList _secretBags;
-		private X509CertificateCollection _certs;
-		private bool _keyBagsChanged;
-		private bool _secretBagsChanged;
-		private bool _certsChanged;
-		private int _iterations;
-		private ArrayList _safeBags;
-		private RandomNumberGenerator _rng;
-
-		// constructors
-
-		public PKCS12 () 
-		{
-			_iterations = recommendedIterationCount;
-			_keyBags = new ArrayList ();
-			_secretBags = new ArrayList ();
-			_certs = new X509CertificateCollection ();
-			_keyBagsChanged = false;
-			_secretBagsChanged = false;
-			_certsChanged = false;
-			_safeBags = new ArrayList ();
-		}
-
-		public PKCS12 (byte[] data)
-			: this ()
-		{
-			Password = null;
-			Decode (data);
-		}
-
-		/*
-		 * PFX ::= SEQUENCE {
-		 *	version INTEGER {v3(3)}(v3,...),
-		 *	authSafe ContentInfo,
-		 *	macData MacData OPTIONAL
-		 * }
-		 * 
-		 * MacData ::= SEQUENCE {
-		 *	mac DigestInfo,
-		 *	macSalt OCTET STRING,
-		 *	iterations INTEGER DEFAULT 1
-		 *	-- Note: The default is for historical reasons and its use is deprecated. A higher
-		 *	-- value, like 1024 is recommended.
-		 * }
-		 * 
-		 * SafeContents ::= SEQUENCE OF SafeBag
-		 * 
-		 * SafeBag ::= SEQUENCE {
-		 *	bagId BAG-TYPE.&id ({PKCS12BagSet}),
-		 *	bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
-		 *	bagAttributes SET OF PKCS12Attribute OPTIONAL
-		 * }
-		 */
-		public PKCS12 (byte[] data, string password)
-			: this ()
-		{
-			Password = password;
-			Decode (data);
-		}
-
-		public PKCS12 (byte[] data, byte[] password)
-			: this ()
-		{
-			_password = password;
-			Decode (data);
-		}
-
-		private void Decode (byte[] data)
-		{
-			ASN1 pfx = new ASN1 (data);
-			if (pfx.Tag != 0x30)
-				throw new ArgumentException ("invalid data");
-			
-			ASN1 version = pfx [0];
-			if (version.Tag != 0x02)
-				throw new ArgumentException ("invalid PFX version");
-			//_version = version.Value [0];
-
-			PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (pfx [1]);
-			if (authSafe.ContentType != PKCS7.Oid.data)
-				throw new ArgumentException ("invalid authenticated safe");
-
-			// now that we know it's a PKCS#12 file, check the (optional) MAC
-			// before decoding anything else in the file
-			if (pfx.Count > 2) {
-				ASN1 macData = pfx [2];
-				if (macData.Tag != 0x30)
-					throw new ArgumentException ("invalid MAC");
-				
-				ASN1 mac = macData [0];
-				if (mac.Tag != 0x30)
-					throw new ArgumentException ("invalid MAC");
-				ASN1 macAlgorithm = mac [0];
-				string macOid = ASN1Convert.ToOid (macAlgorithm [0]);
-				if (macOid != "1.3.14.3.2.26")
-					throw new ArgumentException ("unsupported HMAC");
-				byte[] macValue = mac [1].Value;
-
-				ASN1 macSalt = macData [1];
-				if (macSalt.Tag != 0x04)
-					throw new ArgumentException ("missing MAC salt");
-
-				_iterations = 1; // default value
-				if (macData.Count > 2) {
-					ASN1 iters = macData [2];
-					if (iters.Tag != 0x02)
-						throw new ArgumentException ("invalid MAC iteration");
-					_iterations = ASN1Convert.ToInt32 (iters);
-				}
-
-				byte[] authSafeData = authSafe.Content [0].Value;
-				byte[] calculatedMac = MAC (_password, macSalt.Value, _iterations, authSafeData);
-				if (!Compare (macValue, calculatedMac)) {
-					byte[] nullPassword = {0, 0};
-					calculatedMac = MAC(nullPassword, macSalt.Value, _iterations, authSafeData);
-					if (!Compare (macValue, calculatedMac))
-						throw new CryptographicException ("Invalid MAC - file may have been tampe red!");
-					_password = nullPassword;
-				}
-			}
-
-			// we now returns to our original presentation - PFX
-			ASN1 authenticatedSafe = new ASN1 (authSafe.Content [0].Value);
-			for (int i=0; i < authenticatedSafe.Count; i++) {
-				PKCS7.ContentInfo ci = new PKCS7.ContentInfo (authenticatedSafe [i]);
-				switch (ci.ContentType) {
-					case PKCS7.Oid.data:
-						// unencrypted (by PKCS#12)
-						ASN1 safeContents = new ASN1 (ci.Content [0].Value);
-						for (int j=0; j < safeContents.Count; j++) {
-							ASN1 safeBag = safeContents [j];
-							ReadSafeBag (safeBag);
-						}
-						break;
-					case PKCS7.Oid.encryptedData:
-						// password encrypted
-						PKCS7.EncryptedData ed = new PKCS7.EncryptedData (ci.Content [0]);
-						ASN1 decrypted = new ASN1 (Decrypt (ed));
-						for (int j=0; j < decrypted.Count; j++) {
-							ASN1 safeBag = decrypted [j];
-							ReadSafeBag (safeBag);
-						}
-						break;
-					case PKCS7.Oid.envelopedData:
-						// public key encrypted
-						throw new NotImplementedException ("public key encrypted");
-					default:
-						throw new ArgumentException ("unknown authenticatedSafe");
-				}
-			}
-		}
-
-		~PKCS12 () 
-		{
-			if (_password != null) {
-				Array.Clear (_password, 0, _password.Length);
-			}
-			_password = null;
-		}
-
-		// properties
-
-		public string Password {
-			set {
-				// Clear old password.
-				if (_password != null)
-					Array.Clear (_password, 0, _password.Length);
-				_password = null;
-				if (value != null) {
-					if (value.Length > 0) {
-						int size = value.Length;
-						int nul = 0;
-						if (size < MaximumPasswordLength) {
-							// if not present, add space for a NULL (0x00) character
-							if (value[size - 1] != 0x00)
-								nul = 1;
-						} else {
-							size = MaximumPasswordLength;
-						}
-						_password = new byte[(size + nul) << 1]; // double for unicode
-						Encoding.BigEndianUnicode.GetBytes (value, 0, size, _password, 0);
-					} else {
-						// double-byte (Unicode) NULL (0x00) - see bug #79617
-						_password = new byte[2];
-					}
-				}
-			}
-		}
-
-		public int IterationCount {
-			get { return _iterations; }
-			set { _iterations = value; }
-		}
-
-		public ArrayList Keys {
-			get {
-				if (_keyBagsChanged) {
-					_keyBags.Clear ();
-					foreach (SafeBag sb in _safeBags) {
-						if (sb.BagOID.Equals (keyBag)) {
-							ASN1 safeBag = sb.ASN1;
-							ASN1 bagValue = safeBag [1];
-							PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
-							byte[] privateKey = pki.PrivateKey;
-							switch (privateKey [0]) {
-							case 0x02:
-								DSAParameters p = new DSAParameters (); // FIXME
-								_keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
-								break;
-							case 0x30:
-								_keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
-								break;
-							default:
-								break;
-							}
-							Array.Clear (privateKey, 0, privateKey.Length);
-
-						} else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-							ASN1 safeBag = sb.ASN1;
-							ASN1 bagValue = safeBag [1];
-							PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
-							byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
-							PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
-							byte[] privateKey = pki.PrivateKey;
-							switch (privateKey [0]) {
-							case 0x02:
-								DSAParameters p = new DSAParameters (); // FIXME
-								_keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
-								break;
-							case 0x30:
-								_keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
-								break;
-							default:
-								break;
-							}
-							Array.Clear (privateKey, 0, privateKey.Length);
-							Array.Clear (decrypted, 0, decrypted.Length);
-						}
-					}
-					_keyBagsChanged = false;
-				}
-				return ArrayList.ReadOnly(_keyBags);
-			}
-		}
-
-		public ArrayList Secrets {
-			get {
-				if (_secretBagsChanged) {
-					_secretBags.Clear ();
-					foreach (SafeBag sb in _safeBags) {
-						if (sb.BagOID.Equals (secretBag)) {
-							ASN1 safeBag = sb.ASN1;
-							ASN1 bagValue = safeBag [1];
-							byte[] secret = bagValue.Value;
-							_secretBags.Add(secret);
-						}
-					}
-					_secretBagsChanged = false;
-				}
-				return ArrayList.ReadOnly(_secretBags);
-			}
-		}
-
-		public X509CertificateCollection Certificates {
-			get {
-				if (_certsChanged) {
-					_certs.Clear ();
-					foreach (SafeBag sb in _safeBags) {
-						if (sb.BagOID.Equals (certBag)) {
-							ASN1 safeBag = sb.ASN1;
-							ASN1 bagValue = safeBag [1];
-							PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
-							_certs.Add (new X509Certificate (cert.Content [0].Value));
-						}
-					}
-					_certsChanged = false;
-				}
-				return _certs;
-			}
-		}
-
-		internal RandomNumberGenerator RNG {
-			get {
-				if (_rng == null)
-					_rng = RandomNumberGenerator.Create ();
-				return _rng;
-			}
-		}
-
-		// private methods
-
-		private bool Compare (byte[] expected, byte[] actual) 
-		{
-			bool compare = false;
-			if (expected.Length == actual.Length) {
-				for (int i=0; i < expected.Length; i++) {
-					if (expected [i] != actual [i])
-						return false;
-				}
-				compare = true;
-			}
-			return compare;
-		}
-
-		private SymmetricAlgorithm GetSymmetricAlgorithm (string algorithmOid, byte[] salt, int iterationCount)
-		{
-			string algorithm = null;
-			int keyLength = 8;	// 64 bits (default)
-			int ivLength = 8;	// 64 bits (default)
-
-			PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
-			pd.Password = _password; 
-			pd.Salt = salt;
-			pd.IterationCount = iterationCount;
-
-			switch (algorithmOid) {
-				case PKCS5.pbeWithMD2AndDESCBC:			// no unit test available
-					pd.HashName = "MD2";
-					algorithm = "DES";
-					break;
-				case PKCS5.pbeWithMD5AndDESCBC:			// no unit test available
-					pd.HashName = "MD5";
-					algorithm = "DES";
-					break;
-				case PKCS5.pbeWithMD2AndRC2CBC:			// no unit test available
-					// TODO - RC2-CBC-Parameter (PKCS5)
-					// if missing default to 32 bits !!!
-					pd.HashName = "MD2";
-					algorithm = "RC2";
-					keyLength = 4;		// default
-					break;
-				case PKCS5.pbeWithMD5AndRC2CBC:			// no unit test available
-					// TODO - RC2-CBC-Parameter (PKCS5)
-					// if missing default to 32 bits !!!
-					pd.HashName = "MD5";
-					algorithm = "RC2";
-					keyLength = 4;		// default
-					break;
-				case PKCS5.pbeWithSHA1AndDESCBC: 		// no unit test available
-					pd.HashName = "SHA1";
-					algorithm = "DES";
-					break;
-				case PKCS5.pbeWithSHA1AndRC2CBC:		// no unit test available
-					// TODO - RC2-CBC-Parameter (PKCS5)
-					// if missing default to 32 bits !!!
-					pd.HashName = "SHA1";
-					algorithm = "RC2";
-					keyLength = 4;		// default
-					break;
-				case PKCS12.pbeWithSHAAnd128BitRC4: 		// no unit test available
-					pd.HashName = "SHA1";
-					algorithm = "RC4";
-					keyLength = 16;
-					ivLength = 0;		// N/A
-					break;
-				case PKCS12.pbeWithSHAAnd40BitRC4: 		// no unit test available
-					pd.HashName = "SHA1";
-					algorithm = "RC4";
-					keyLength = 5;
-					ivLength = 0;		// N/A
-					break;
-				case PKCS12.pbeWithSHAAnd3KeyTripleDESCBC: 
-					pd.HashName = "SHA1";
-					algorithm = "TripleDES";
-					keyLength = 24;
-					break;
-				case PKCS12.pbeWithSHAAnd2KeyTripleDESCBC:	// no unit test available
-					pd.HashName = "SHA1";
-					algorithm = "TripleDES";
-					keyLength = 16;
-					break;
-				case PKCS12.pbeWithSHAAnd128BitRC2CBC: 		// no unit test available
-					pd.HashName = "SHA1";
-					algorithm = "RC2";
-					keyLength = 16;
-					break;
-				case PKCS12.pbeWithSHAAnd40BitRC2CBC: 
-					pd.HashName = "SHA1";
-					algorithm = "RC2";
-					keyLength = 5;
-					break;
-				default:
-					throw new NotSupportedException ("unknown oid " + algorithm);
-			}
-
-			SymmetricAlgorithm sa = null;
-            sa = SymmetricAlgorithm.Create(algorithm);
-            sa.Key = pd.DeriveKey (keyLength);
-			// IV required only for block ciphers (not stream ciphers)
-			if (ivLength > 0) {
-				sa.IV = pd.DeriveIV (ivLength);
-				sa.Mode = CipherMode.CBC;
-			}
-			return sa;
-		}
-
-		public byte[] Decrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] encryptedData) 
-		{
-			SymmetricAlgorithm sa = null;
-			byte[] result = null;
-			try {
-				sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount);
-				ICryptoTransform ct = sa.CreateDecryptor ();
-				result = ct.TransformFinalBlock (encryptedData, 0, encryptedData.Length);
-			}
-			finally {
-				if (sa != null)
-					sa.Clear ();
-			}
-			return result;
-		}
-
-		public byte[] Decrypt (PKCS7.EncryptedData ed)
-		{
-			return Decrypt (ed.EncryptionAlgorithm.ContentType, 
-				ed.EncryptionAlgorithm.Content [0].Value, 
-				ASN1Convert.ToInt32 (ed.EncryptionAlgorithm.Content [1]),
-				ed.EncryptedContent);
-		}
-
-		public byte[] Encrypt (string algorithmOid, byte[] salt, int iterationCount, byte[] data) 
-		{
-			byte[] result = null;
-			using (SymmetricAlgorithm sa = GetSymmetricAlgorithm (algorithmOid, salt, iterationCount)) {
-				ICryptoTransform ct = sa.CreateEncryptor ();
-				result = ct.TransformFinalBlock (data, 0, data.Length);
-			}
-			return result;
-		}
-
-		private DSAParameters GetExistingParameters (out bool found)
-		{
-			foreach (X509Certificate cert in Certificates) {
-				// FIXME: that won't work if parts of the parameters are missing
-				if (cert.KeyAlgorithmParameters != null) {
-					DSA dsa = cert.DSA;
-					if (dsa != null) {
-						found = true;
-						return dsa.ExportParameters (false);
-					}
-				}
-			}
-			found = false;
-			return new DSAParameters ();
-		}
-
-		private void AddPrivateKey (PKCS8.PrivateKeyInfo pki) 
-		{
-			byte[] privateKey = pki.PrivateKey;
-			switch (privateKey [0]) {
-				case 0x02:
-					bool found;
-					DSAParameters p = GetExistingParameters (out found);
-					if (found) {
-						_keyBags.Add (PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p));
-					}
-					break;
-				case 0x30:
-					_keyBags.Add (PKCS8.PrivateKeyInfo.DecodeRSA (privateKey));
-					break;
-				default:
-					Array.Clear (privateKey, 0, privateKey.Length);
-					throw new CryptographicException ("Unknown private key format");
-			}
-			Array.Clear (privateKey, 0, privateKey.Length);
-		}
-
-		private void ReadSafeBag (ASN1 safeBag) 
-		{
-			if (safeBag.Tag != 0x30)
-				throw new ArgumentException ("invalid safeBag");
-
-			ASN1 bagId = safeBag [0];
-			if (bagId.Tag != 0x06)
-				throw new ArgumentException ("invalid safeBag id");
-
-			ASN1 bagValue = safeBag [1];
-			string oid = ASN1Convert.ToOid (bagId);
-			switch (oid) {
-				case keyBag:
-					// NEED UNIT TEST
-					AddPrivateKey (new PKCS8.PrivateKeyInfo (bagValue.Value));
-					break;
-				case pkcs8ShroudedKeyBag:
-					PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
-					byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
-					AddPrivateKey (new PKCS8.PrivateKeyInfo (decrypted));
-					Array.Clear (decrypted, 0, decrypted.Length);
-					break;
-				case certBag:
-					PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
-					if (cert.ContentType != x509Certificate)
-						throw new NotSupportedException ("unsupport certificate type");
-					X509Certificate x509 = new X509Certificate (cert.Content [0].Value);
-					_certs.Add (x509);
-					break;
-				case crlBag:
-					// TODO
-					break;
-				case secretBag: 
-					byte[] secret = bagValue.Value;
-					_secretBags.Add(secret);
-					break;
-				case safeContentsBag:
-					// TODO - ? recurse ?
-					break;
-				default:
-					throw new ArgumentException ("unknown safeBag oid");
-			}
-
-			if (safeBag.Count > 2) {
-				ASN1 bagAttributes = safeBag [2];
-				if (bagAttributes.Tag != 0x31)
-					throw new ArgumentException ("invalid safeBag attributes id");
-
-				for (int i = 0; i < bagAttributes.Count; i++) {
-					ASN1 pkcs12Attribute = bagAttributes[i];
-						
-					if (pkcs12Attribute.Tag != 0x30)
-						throw new ArgumentException ("invalid PKCS12 attributes id");
-
-					ASN1 attrId = pkcs12Attribute [0];
-					if (attrId.Tag != 0x06)
-						throw new ArgumentException ("invalid attribute id");
-						
-					string attrOid = ASN1Convert.ToOid (attrId);
-
-					ASN1 attrValues = pkcs12Attribute[1];
-					for (int j = 0; j < attrValues.Count; j++) {
-						ASN1 attrValue = attrValues[j];
-
-						switch (attrOid) {
-						case PKCS9.friendlyName:
-							if (attrValue.Tag != 0x1e)
-								throw new ArgumentException ("invalid attribute value id");
-							break;
-						case PKCS9.localKeyId:
-							if (attrValue.Tag != 0x04)
-								throw new ArgumentException ("invalid attribute value id");
-							break;
-						default:
-							// Unknown OID -- don't check Tag
-							break;
-						}
-					}
-				}
-			}
-
-			_safeBags.Add (new SafeBag(oid, safeBag));
-		}
-
-		private ASN1 Pkcs8ShroudedKeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
-		{
-			PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
-			if (aa is RSA) {
-				pki.Algorithm = "1.2.840.113549.1.1.1";
-				pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
-			}
-			else if (aa is DSA) {
-				pki.Algorithm = null;
-				pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
-			}
-			else
-				throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
-
-			PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo ();
-			epki.Algorithm = pbeWithSHAAnd3KeyTripleDESCBC;
-			epki.IterationCount = _iterations;
-			epki.EncryptedData = Encrypt (pbeWithSHAAnd3KeyTripleDESCBC, epki.Salt, _iterations, pki.GetBytes ());
-
-			ASN1 safeBag = new ASN1 (0x30);
-			safeBag.Add (ASN1Convert.FromOid (pkcs8ShroudedKeyBag));
-			ASN1 bagValue = new ASN1 (0xA0);
-			bagValue.Add (new ASN1 (epki.GetBytes ()));
-			safeBag.Add (bagValue);
-
-			if (attributes != null) {
-				ASN1 bagAttributes = new ASN1 (0x31);
-				IDictionaryEnumerator de = attributes.GetEnumerator ();
-
-				while (de.MoveNext ()) {
-					string oid = (string)de.Key;
-					switch (oid) {
-					case PKCS9.friendlyName:
-						ArrayList names = (ArrayList)de.Value;
-						if (names.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] name in names) {
-								ASN1 attrValue = new ASN1 (0x1e);
-								attrValue.Value = name;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					case PKCS9.localKeyId:
-						ArrayList keys = (ArrayList)de.Value;
-						if (keys.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] key in keys) {
-								ASN1 attrValue = new ASN1 (0x04);
-								attrValue.Value = key;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					default:
-						break;
-					}
-				}
-
-				if (bagAttributes.Count > 0) {
-					safeBag.Add (bagAttributes);
-				}
-			}
-
-			return safeBag;
-		}
-
-		private ASN1 KeyBagSafeBag (AsymmetricAlgorithm aa, IDictionary attributes) 
-		{
-			PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo ();
-			if (aa is RSA) {
-				pki.Algorithm = "1.2.840.113549.1.1.1";
-				pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((RSA)aa);
-			}
-			else if (aa is DSA) {
-				pki.Algorithm = null;
-				pki.PrivateKey = PKCS8.PrivateKeyInfo.Encode ((DSA)aa);
-			}
-			else
-				throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
-
-			ASN1 safeBag = new ASN1 (0x30);
-			safeBag.Add (ASN1Convert.FromOid (keyBag));
-			ASN1 bagValue = new ASN1 (0xA0);
-			bagValue.Add (new ASN1 (pki.GetBytes ()));
-			safeBag.Add (bagValue);
-
-			if (attributes != null) {
-				ASN1 bagAttributes = new ASN1 (0x31);
-				IDictionaryEnumerator de = attributes.GetEnumerator ();
-
-				while (de.MoveNext ()) {
-					string oid = (string)de.Key;
-					switch (oid) {
-					case PKCS9.friendlyName:
-						ArrayList names = (ArrayList)de.Value;
-						if (names.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] name in names) {
-								ASN1 attrValue = new ASN1 (0x1e);
-								attrValue.Value = name;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					case PKCS9.localKeyId:
-						ArrayList keys = (ArrayList)de.Value;
-						if (keys.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] key in keys) {
-								ASN1 attrValue = new ASN1 (0x04);
-								attrValue.Value = key;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					default:
-						break;
-					}
-				}
-
-				if (bagAttributes.Count > 0) {
-					safeBag.Add (bagAttributes);
-				}
-			}
-
-			return safeBag;
-		}
-
-		private ASN1 SecretBagSafeBag (byte[] secret, IDictionary attributes) 
-		{
-			ASN1 safeBag = new ASN1 (0x30);
-			safeBag.Add (ASN1Convert.FromOid (secretBag));
-			ASN1 bagValue = new ASN1 (0x80, secret);
-			safeBag.Add (bagValue);
-
-			if (attributes != null) {
-				ASN1 bagAttributes = new ASN1 (0x31);
-				IDictionaryEnumerator de = attributes.GetEnumerator ();
-
-				while (de.MoveNext ()) {
-					string oid = (string)de.Key;
-					switch (oid) {
-					case PKCS9.friendlyName:
-						ArrayList names = (ArrayList)de.Value;
-						if (names.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] name in names) {
-								ASN1 attrValue = new ASN1 (0x1e);
-								attrValue.Value = name;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					case PKCS9.localKeyId:
-						ArrayList keys = (ArrayList)de.Value;
-						if (keys.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] key in keys) {
-								ASN1 attrValue = new ASN1 (0x04);
-								attrValue.Value = key;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					default:
-						break;
-					}
-				}
-
-				if (bagAttributes.Count > 0) {
-					safeBag.Add (bagAttributes);
-				}
-			}
-
-			return safeBag;
-		}
-
-		private ASN1 CertificateSafeBag (X509Certificate x509, IDictionary attributes) 
-		{
-			ASN1 encapsulatedCertificate = new ASN1 (0x04, x509.RawData);
-
-			PKCS7.ContentInfo ci = new PKCS7.ContentInfo ();
-			ci.ContentType = x509Certificate;
-			ci.Content.Add (encapsulatedCertificate);
-
-			ASN1 bagValue = new ASN1 (0xA0);
-			bagValue.Add (ci.ASN1);
-
-			ASN1 safeBag = new ASN1 (0x30);
-			safeBag.Add (ASN1Convert.FromOid (certBag));
-			safeBag.Add (bagValue);
-
-			if (attributes != null) {
-				ASN1 bagAttributes = new ASN1 (0x31);
-				IDictionaryEnumerator de = attributes.GetEnumerator ();
-
-				while (de.MoveNext ()) {
-					string oid = (string)de.Key;
-					switch (oid) {
-					case PKCS9.friendlyName:
-						ArrayList names = (ArrayList)de.Value;
-						if (names.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.friendlyName));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] name in names) {
-								ASN1 attrValue = new ASN1 (0x1e);
-								attrValue.Value = name;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					case PKCS9.localKeyId:
-						ArrayList keys = (ArrayList)de.Value;
-						if (keys.Count > 0) {
-							ASN1 pkcs12Attribute = new ASN1 (0x30);
-							pkcs12Attribute.Add (ASN1Convert.FromOid (PKCS9.localKeyId));
-							ASN1 attrValues = new ASN1 (0x31);
-							foreach (byte[] key in keys) {
-								ASN1 attrValue = new ASN1 (0x04);
-								attrValue.Value = key;
-								attrValues.Add (attrValue);
-							}
-							pkcs12Attribute.Add (attrValues);
-							bagAttributes.Add (pkcs12Attribute);
-						}
-						break;
-					default:
-						break;
-					}
-				}
-
-				if (bagAttributes.Count > 0) {
-					safeBag.Add (bagAttributes);
-				}
-			}
-
-			return safeBag;
-		}
-
-		private byte[] MAC (byte[] password, byte[] salt, int iterations, byte[] data) 
-		{
-			PKCS12.DeriveBytes pd = new PKCS12.DeriveBytes ();
-			pd.HashName = "SHA1";
-			pd.Password = password;
-			pd.Salt = salt;
-			pd.IterationCount = iterations;
-
-			HMACSHA1 hmac = (HMACSHA1) HMACSHA1.Create ();
-			hmac.Key = pd.DeriveMAC (20);
-			return hmac.ComputeHash (data, 0, data.Length);
-		}
-
-		/*
-		 * SafeContents ::= SEQUENCE OF SafeBag
-		 * 
-		 * SafeBag ::= SEQUENCE {
-		 *	bagId BAG-TYPE.&id ({PKCS12BagSet}),
-		 *	bagValue [0] EXPLICIT BAG-TYPE.&Type({PKCS12BagSet}{@bagId}),
-		 *	bagAttributes SET OF PKCS12Attribute OPTIONAL
-		 * }
-		 */
-		public byte[] GetBytes () 
-		{
-			// TODO (incomplete)
-			ASN1 safeBagSequence = new ASN1 (0x30);
-
-			// Sync Safe Bag list since X509CertificateCollection may be updated
-			ArrayList scs = new ArrayList ();
-			foreach (SafeBag sb in _safeBags) {
-				if (sb.BagOID.Equals (certBag)) {
-					ASN1 safeBag = sb.ASN1;
-					ASN1 bagValue = safeBag [1];
-					PKCS7.ContentInfo cert = new PKCS7.ContentInfo (bagValue.Value);
-					scs.Add (new X509Certificate (cert.Content [0].Value));
-				}
-			}
-
-			ArrayList addcerts = new ArrayList ();
-			ArrayList removecerts = new ArrayList ();
-
-			foreach (X509Certificate c in Certificates) {
-				bool found = false;
-
-				foreach (X509Certificate lc in scs) {
-					if (Compare (c.RawData, lc.RawData)) {
-						found = true;
-					}
-				}
-
-				if (!found) {
-					addcerts.Add (c);
-				}
-			}
-			foreach (X509Certificate c in scs) {
-				bool found = false;
-
-				foreach (X509Certificate lc in Certificates) {
-					if (Compare (c.RawData, lc.RawData)) {
-						found = true;
-					}
-				}
-
-				if (!found) {
-					removecerts.Add (c);
-				}
-			}
-
-			foreach (X509Certificate c in removecerts) {
-				RemoveCertificate (c);
-			}
-
-			foreach (X509Certificate c in addcerts) {
-				AddCertificate (c);
-			}
-			// Sync done
-
-			if (_safeBags.Count > 0) {
-				ASN1 certsSafeBag = new ASN1 (0x30);
-				foreach (SafeBag sb in _safeBags) {
-					if (sb.BagOID.Equals (certBag)) {
-						certsSafeBag.Add (sb.ASN1);
-					}
-				}
-
-				if (certsSafeBag.Count > 0) {
-					PKCS7.ContentInfo contentInfo = EncryptedContentInfo (certsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
-					safeBagSequence.Add (contentInfo.ASN1);
-				}
-			}
-
-			if (_safeBags.Count > 0) {
-				ASN1 safeContents = new ASN1 (0x30);
-				foreach (SafeBag sb in _safeBags) {
-					if (sb.BagOID.Equals (keyBag) ||
-					    sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-						safeContents.Add (sb.ASN1);
-					}
-				}
-				if (safeContents.Count > 0) {
-					ASN1 content = new ASN1 (0xA0);
-					content.Add (new ASN1 (0x04, safeContents.GetBytes ()));
-				
-					PKCS7.ContentInfo keyBag = new PKCS7.ContentInfo (PKCS7.Oid.data);
-					keyBag.Content = content;
-					safeBagSequence.Add (keyBag.ASN1);
-				}
-			}
-
-			// Doing SecretBags separately in case we want to change their encryption independently.
-			if (_safeBags.Count > 0) {
-				ASN1 secretsSafeBag = new ASN1 (0x30);
-				foreach (SafeBag sb in _safeBags) {
-					if (sb.BagOID.Equals (secretBag)) {
-						secretsSafeBag.Add (sb.ASN1);
-					}
-				}
-
-				if (secretsSafeBag.Count > 0) {
-					PKCS7.ContentInfo contentInfo = EncryptedContentInfo (secretsSafeBag, pbeWithSHAAnd3KeyTripleDESCBC);
-					safeBagSequence.Add (contentInfo.ASN1);
-				}
-			}
-
-
-			ASN1 encapsulates = new ASN1 (0x04, safeBagSequence.GetBytes ());
-			ASN1 ci = new ASN1 (0xA0);
-			ci.Add (encapsulates);
-			PKCS7.ContentInfo authSafe = new PKCS7.ContentInfo (PKCS7.Oid.data);
-			authSafe.Content = ci;
-			
-			ASN1 macData = new ASN1 (0x30);
-			if (_password != null) {
-				// only for password based encryption
-				byte[] salt = new byte [20];
-				RNG.GetBytes (salt);
-				byte[] macValue = MAC (_password, salt, _iterations, authSafe.Content [0].Value);
-				ASN1 oidSeq = new ASN1 (0x30);
-				oidSeq.Add (ASN1Convert.FromOid ("1.3.14.3.2.26"));	// SHA1
-				oidSeq.Add (new ASN1 (0x05));
-
-				ASN1 mac = new ASN1 (0x30);
-				mac.Add (oidSeq);
-				mac.Add (new ASN1 (0x04, macValue));
-
-				macData.Add (mac);
-				macData.Add (new ASN1 (0x04, salt));
-				macData.Add (ASN1Convert.FromInt32 (_iterations));
-			}
-			
-			ASN1 version = new ASN1 (0x02, new byte [1] { 0x03 });
-			
-			ASN1 pfx = new ASN1 (0x30);
-			pfx.Add (version);
-			pfx.Add (authSafe.ASN1);
-			if (macData.Count > 0) {
-				// only for password based encryption
-				pfx.Add (macData);
-			}
-
-			return pfx.GetBytes ();
-		}
-
-		// Creates an encrypted PKCS#7 ContentInfo with safeBags as its SafeContents.  Used in GetBytes(), above.
-		private PKCS7.ContentInfo EncryptedContentInfo(ASN1 safeBags, string algorithmOid)
-		{
-			byte[] salt = new byte [8];
-			RNG.GetBytes (salt);
-
-			ASN1 seqParams = new ASN1 (0x30);
-			seqParams.Add (new ASN1 (0x04, salt));
-			seqParams.Add (ASN1Convert.FromInt32 (_iterations));
-
-			ASN1 seqPbe = new ASN1 (0x30);
-			seqPbe.Add (ASN1Convert.FromOid (algorithmOid));
-			seqPbe.Add (seqParams);
-
-			byte[] encrypted = Encrypt (algorithmOid, salt, _iterations, safeBags.GetBytes ());
-			ASN1 encryptedContent = new ASN1 (0x80, encrypted);
-
-			ASN1 seq = new ASN1 (0x30);
-			seq.Add (ASN1Convert.FromOid (PKCS7.Oid.data));
-			seq.Add (seqPbe);
-			seq.Add (encryptedContent);
-
-			ASN1 version = new ASN1 (0x02, new byte [1] { 0x00 });
-			ASN1 encData = new ASN1 (0x30);
-			encData.Add (version);
-			encData.Add (seq);
-
-			ASN1 finalContent = new ASN1 (0xA0);
-			finalContent.Add (encData);
-
-			PKCS7.ContentInfo bag = new PKCS7.ContentInfo (PKCS7.Oid.encryptedData);
-			bag.Content = finalContent;
-			return bag;
-		}
-
-		public void AddCertificate (X509Certificate cert)
-		{
-			AddCertificate (cert, null);
-		}
-
-		public void AddCertificate (X509Certificate cert, IDictionary attributes)
-		{
-			bool found = false;
-
-			for (int i = 0; !found && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (certBag)) {
-					ASN1 safeBag = sb.ASN1;
-					ASN1 bagValue = safeBag [1];
-					PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
-					X509Certificate c = new X509Certificate (crt.Content [0].Value);
-					if (Compare (cert.RawData, c.RawData)) {
-						found = true;
-					}
-				}
-			}
-
-			if (!found) {
-				_safeBags.Add (new SafeBag (certBag, CertificateSafeBag (cert, attributes)));
-				_certsChanged = true;
-			}
-		}
-
-		public void RemoveCertificate (X509Certificate cert)
-		{
-			RemoveCertificate (cert, null);
-		}
-
-		public void RemoveCertificate (X509Certificate cert, IDictionary attrs)
-		{
-			int certIndex = -1;
-
-			for (int i = 0; certIndex == -1 && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (certBag)) {
-					ASN1 safeBag = sb.ASN1;
-					ASN1 bagValue = safeBag [1];
-					PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
-					X509Certificate c = new X509Certificate (crt.Content [0].Value);
-					if (Compare (cert.RawData, c.RawData)) {
-						if (attrs != null) {
-							if (safeBag.Count == 3) {
-								ASN1 bagAttributes = safeBag [2];
-								int bagAttributesFound = 0;
-								for (int j = 0; j < bagAttributes.Count; j++) {
-									ASN1 pkcs12Attribute = bagAttributes [j];
-									ASN1 attrId = pkcs12Attribute [0];
-									string ao = ASN1Convert.ToOid (attrId);
-									ArrayList dattrValues = (ArrayList)attrs [ao];
-
-									if (dattrValues != null) {
-										ASN1 attrValues = pkcs12Attribute [1];
-
-										if (dattrValues.Count == attrValues.Count) {
-											int attrValuesFound = 0;
-											for (int k = 0; k < attrValues.Count; k++) {
-												ASN1 attrValue = attrValues [k];
-												byte[] value = (byte[])dattrValues [k];
-									
-												if (Compare (value, attrValue.Value)) {
-													attrValuesFound += 1;
-												}
-											}
-											if (attrValuesFound == attrValues.Count) {
-												bagAttributesFound += 1;
-											}
-										}
-									}
-								}
-								if (bagAttributesFound == bagAttributes.Count) {
-									certIndex = i;
-								}
-							}
-						} else {
-							certIndex = i;
-						}
-					}
-				}
-			}
-
-			if (certIndex != -1) {
-				_safeBags.RemoveAt (certIndex);
-				_certsChanged = true;
-			}
-		}
-
-		private bool CompareAsymmetricAlgorithm (AsymmetricAlgorithm a1, AsymmetricAlgorithm a2)
-		{
-			// fast path
-			if (a1.KeySize != a2.KeySize)
-				return false;
-			// compare public keys - if they match we can assume the private match too
-			return (a1.ToXmlString (false) == a2.ToXmlString (false));
-		}
-
-		public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
-		{
-			AddPkcs8ShroudedKeyBag (aa, null);
-		}
-
-		public void AddPkcs8ShroudedKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
-		{
-			bool found = false;
-
-			for (int i = 0; !found && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-					ASN1 bagValue = sb.ASN1 [1];
-					PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
-					byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
-					PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
-					byte[] privateKey = pki.PrivateKey;
-
-					AsymmetricAlgorithm saa = null;
-					switch (privateKey [0]) {
-					case 0x02:
-						DSAParameters p = new DSAParameters (); // FIXME
-						saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-						break;
-					case 0x30:
-						saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-						break;
-					default:
-						Array.Clear (decrypted, 0, decrypted.Length);
-						Array.Clear (privateKey, 0, privateKey.Length);
-						throw new CryptographicException ("Unknown private key format");
-					}
-
-					Array.Clear (decrypted, 0, decrypted.Length);
-					Array.Clear (privateKey, 0, privateKey.Length);
-
-					if (CompareAsymmetricAlgorithm (aa , saa)) {
-						found = true;
-					}
-				}
-			}
-
-			if (!found) {
-				_safeBags.Add (new SafeBag (pkcs8ShroudedKeyBag, Pkcs8ShroudedKeyBagSafeBag (aa, attributes)));
-				_keyBagsChanged = true;
-			}
-		}
-
-		public void RemovePkcs8ShroudedKeyBag (AsymmetricAlgorithm aa)
-		{
-			int aaIndex = -1;
-
-			for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-					ASN1 bagValue = sb.ASN1 [1];
-					PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
-					byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
-					PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
-					byte[] privateKey = pki.PrivateKey;
-
-					AsymmetricAlgorithm saa = null;
-					switch (privateKey [0]) {
-					case 0x02:
-						DSAParameters p = new DSAParameters (); // FIXME
-						saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-						break;
-					case 0x30:
-						saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-						break;
-					default:
-						Array.Clear (decrypted, 0, decrypted.Length);
-						Array.Clear (privateKey, 0, privateKey.Length);
-						throw new CryptographicException ("Unknown private key format");
-					}
-
-					Array.Clear (decrypted, 0, decrypted.Length);
-					Array.Clear (privateKey, 0, privateKey.Length);
-
-					if (CompareAsymmetricAlgorithm (aa, saa)) {
-						aaIndex = i;
-					}
-				}
-			}
-
-			if (aaIndex != -1) {
-				_safeBags.RemoveAt (aaIndex);
-				_keyBagsChanged = true;
-			}
-		}
-
-		public void AddKeyBag (AsymmetricAlgorithm aa)
-		{
-			AddKeyBag (aa, null);
-		}
-
-		public void AddKeyBag (AsymmetricAlgorithm aa, IDictionary attributes)
-		{
-			bool found = false;
-
-			for (int i = 0; !found && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (keyBag)) {
-					ASN1 bagValue = sb.ASN1 [1];
-					PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
-					byte[] privateKey = pki.PrivateKey;
-
-					AsymmetricAlgorithm saa = null;
-					switch (privateKey [0]) {
-					case 0x02:
-						DSAParameters p = new DSAParameters (); // FIXME
-						saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-						break;
-					case 0x30:
-						saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-						break;
-					default:
-						Array.Clear (privateKey, 0, privateKey.Length);
-						throw new CryptographicException ("Unknown private key format");
-					}
-
-					Array.Clear (privateKey, 0, privateKey.Length);
-
-					if (CompareAsymmetricAlgorithm (aa, saa)) {
-						found = true;
-					}
-				}
-			}
-
-			if (!found) {
-				_safeBags.Add (new SafeBag (keyBag, KeyBagSafeBag (aa, attributes)));
-				_keyBagsChanged = true;
-			}
-		}
-
-		public void RemoveKeyBag (AsymmetricAlgorithm aa)
-		{
-			int aaIndex = -1;
-
-			for (int i = 0; aaIndex == -1 && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (keyBag)) {
-					ASN1 bagValue = sb.ASN1 [1];
-					PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
-					byte[] privateKey = pki.PrivateKey;
-
-					AsymmetricAlgorithm saa = null;
-					switch (privateKey [0]) {
-					case 0x02:
-						DSAParameters p = new DSAParameters (); // FIXME
-						saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-						break;
-					case 0x30:
-						saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-						break;
-					default:
-						Array.Clear (privateKey, 0, privateKey.Length);
-						throw new CryptographicException ("Unknown private key format");
-					}
-
-					Array.Clear (privateKey, 0, privateKey.Length);
-
-					if (CompareAsymmetricAlgorithm (aa, saa)) {
-						aaIndex = i;
-					}
-				}
-			}
-
-			if (aaIndex != -1) {
-				_safeBags.RemoveAt (aaIndex);
-				_keyBagsChanged = true;
-			}
-		}
-
-		public void AddSecretBag (byte[] secret)
-		{
-			AddSecretBag (secret, null);
-		}
-
-		public void AddSecretBag (byte[] secret, IDictionary attributes)
-		{
-			bool found = false;
-
-			for (int i = 0; !found && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (secretBag)) {
-					ASN1 bagValue = sb.ASN1 [1];
-					byte[] ssecret = bagValue.Value;
-
-					if (Compare (secret, ssecret)) {
-						found = true;
-					}
-				}
-			}
-
-			if (!found) {
-				_safeBags.Add (new SafeBag (secretBag, SecretBagSafeBag (secret, attributes)));
-				_secretBagsChanged = true;
-			}
-		}
-
-		public void RemoveSecretBag (byte[] secret)
-		{
-			int sIndex = -1;
-
-			for (int i = 0; sIndex == -1 && i < _safeBags.Count; i++) {
-				SafeBag sb = (SafeBag)_safeBags [i];
-
-				if (sb.BagOID.Equals (secretBag)) {
-					ASN1 bagValue = sb.ASN1 [1];
-					byte[] ssecret = bagValue.Value;
-
-					if (Compare (secret, ssecret)) {
-						sIndex = i;
-					}
-				}
-			}
-
-			if (sIndex != -1) {
-				_safeBags.RemoveAt (sIndex);
-				_secretBagsChanged = true;
-			}
-		}
-
-		public AsymmetricAlgorithm GetAsymmetricAlgorithm (IDictionary attrs)
-		{
-			foreach (SafeBag sb in _safeBags) {
-				if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-					ASN1 safeBag = sb.ASN1;
-
-					if (safeBag.Count == 3) {
-						ASN1 bagAttributes = safeBag [2];
-
-						int bagAttributesFound = 0;
-						for (int i = 0; i < bagAttributes.Count; i++) {
-							ASN1 pkcs12Attribute = bagAttributes [i];
-							ASN1 attrId = pkcs12Attribute [0];
-							string ao = ASN1Convert.ToOid (attrId);
-							ArrayList dattrValues = (ArrayList)attrs [ao];
-
-							if (dattrValues != null) {
-								ASN1 attrValues = pkcs12Attribute [1];
-
-								if (dattrValues.Count == attrValues.Count) {
-									int attrValuesFound = 0;
-									for (int j = 0; j < attrValues.Count; j++) {
-										ASN1 attrValue = attrValues [j];
-										byte[] value = (byte[])dattrValues [j];
-									
-										if (Compare (value, attrValue.Value)) {
-											attrValuesFound += 1;
-										}
-									}
-									if (attrValuesFound == attrValues.Count) {
-										bagAttributesFound += 1;
-									}
-								}
-							}
-						}
-						if (bagAttributesFound == bagAttributes.Count) {
-							ASN1 bagValue = safeBag [1];
-							AsymmetricAlgorithm aa = null;
-							if (sb.BagOID.Equals (keyBag)) {
-								PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
-								byte[] privateKey = pki.PrivateKey;
-								switch (privateKey [0]) {
-								case 0x02:
-									DSAParameters p = new DSAParameters (); // FIXME
-									aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-									break;
-								case 0x30:
-									aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-									break;
-								default:
-									break;
-								}
-								Array.Clear (privateKey, 0, privateKey.Length);
-							} else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-								PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
-								byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
-								PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
-								byte[] privateKey = pki.PrivateKey;
-								switch (privateKey [0]) {
-								case 0x02:
-									DSAParameters p = new DSAParameters (); // FIXME
-									aa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-									break;
-								case 0x30:
-									aa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-									break;
-								default:
-									break;
-								}
-								Array.Clear (privateKey, 0, privateKey.Length);
-								Array.Clear (decrypted, 0, decrypted.Length);
-							}
-							return aa;
-						}
-					}
-				}
-			}
-
-			return null;
-		}
-
-		public byte[] GetSecret (IDictionary attrs)
-		{
-			foreach (SafeBag sb in _safeBags) {
-				if (sb.BagOID.Equals (secretBag)) {
-					ASN1 safeBag = sb.ASN1;
-
-					if (safeBag.Count == 3) {
-						ASN1 bagAttributes = safeBag [2];
-
-						int bagAttributesFound = 0;
-						for (int i = 0; i < bagAttributes.Count; i++) {
-							ASN1 pkcs12Attribute = bagAttributes [i];
-							ASN1 attrId = pkcs12Attribute [0];
-							string ao = ASN1Convert.ToOid (attrId);
-							ArrayList dattrValues = (ArrayList)attrs [ao];
-
-							if (dattrValues != null) {
-								ASN1 attrValues = pkcs12Attribute [1];
-
-								if (dattrValues.Count == attrValues.Count) {
-									int attrValuesFound = 0;
-									for (int j = 0; j < attrValues.Count; j++) {
-										ASN1 attrValue = attrValues [j];
-										byte[] value = (byte[])dattrValues [j];
-									
-										if (Compare (value, attrValue.Value)) {
-											attrValuesFound += 1;
-										}
-									}
-									if (attrValuesFound == attrValues.Count) {
-										bagAttributesFound += 1;
-									}
-								}
-							}
-						}
-						if (bagAttributesFound == bagAttributes.Count) {
-							ASN1 bagValue = safeBag [1];
-							return bagValue.Value;
-						}
-					}
-				}
-			}
-
-			return null;
-		}
-
-		public X509Certificate GetCertificate (IDictionary attrs)
-		{
-			foreach (SafeBag sb in _safeBags) {
-				if (sb.BagOID.Equals (certBag)) {
-					ASN1 safeBag = sb.ASN1;
-
-					if (safeBag.Count == 3) {
-						ASN1 bagAttributes = safeBag [2];
-
-						int bagAttributesFound = 0;
-						for (int i = 0; i < bagAttributes.Count; i++) {
-							ASN1 pkcs12Attribute = bagAttributes [i];
-							ASN1 attrId = pkcs12Attribute [0];
-							string ao = ASN1Convert.ToOid (attrId);
-							ArrayList dattrValues = (ArrayList)attrs [ao];
-
-							if (dattrValues != null) {
-								ASN1 attrValues = pkcs12Attribute [1];
-								
-								if (dattrValues.Count == attrValues.Count) {
-									int attrValuesFound = 0;
-									for (int j = 0; j < attrValues.Count; j++) {
-										ASN1 attrValue = attrValues [j];
-										byte[] value = (byte[])dattrValues [j];
-									
-										if (Compare (value, attrValue.Value)) {
-											attrValuesFound += 1;
-										}
-									}
-									if (attrValuesFound == attrValues.Count) {
-										bagAttributesFound += 1;
-									}
-								}
-							}
-						}
-						if (bagAttributesFound == bagAttributes.Count) {
-							ASN1 bagValue = safeBag [1];
-							PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
-							return new X509Certificate (crt.Content [0].Value);
-						}
-					}
-				}
-			}
-
-			return null;
-		}
-
-		public IDictionary GetAttributes (AsymmetricAlgorithm aa)
-		{
-			IDictionary result = new Hashtable ();
-
-			foreach (SafeBag sb in _safeBags) {
-				if (sb.BagOID.Equals (keyBag) || sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-					ASN1 safeBag = sb.ASN1;
-
-					ASN1 bagValue = safeBag [1];
-					AsymmetricAlgorithm saa = null;
-					if (sb.BagOID.Equals (keyBag)) {
-						PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (bagValue.Value);
-						byte[] privateKey = pki.PrivateKey;
-						switch (privateKey [0]) {
-						case 0x02:
-							DSAParameters p = new DSAParameters (); // FIXME
-							saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-							break;
-						case 0x30:
-							saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-							break;
-						default:
-							break;
-						}
-						Array.Clear (privateKey, 0, privateKey.Length);
-					} else if (sb.BagOID.Equals (pkcs8ShroudedKeyBag)) {
-						PKCS8.EncryptedPrivateKeyInfo epki = new PKCS8.EncryptedPrivateKeyInfo (bagValue.Value);
-						byte[] decrypted = Decrypt (epki.Algorithm, epki.Salt, epki.IterationCount, epki.EncryptedData);
-						PKCS8.PrivateKeyInfo pki = new PKCS8.PrivateKeyInfo (decrypted);
-						byte[] privateKey = pki.PrivateKey;
-						switch (privateKey [0]) {
-						case 0x02:
-							DSAParameters p = new DSAParameters (); // FIXME
-							saa = PKCS8.PrivateKeyInfo.DecodeDSA (privateKey, p);
-							break;
-						case 0x30:
-							saa = PKCS8.PrivateKeyInfo.DecodeRSA (privateKey);
-							break;
-						default:
-							break;
-						}
-						Array.Clear (privateKey, 0, privateKey.Length);
-						Array.Clear (decrypted, 0, decrypted.Length);
-					}
-
-					if (saa != null && CompareAsymmetricAlgorithm (saa, aa)) {
-						if (safeBag.Count == 3) {
-							ASN1 bagAttributes = safeBag [2];
-							
-							for (int i = 0; i < bagAttributes.Count; i++) {
-								ASN1 pkcs12Attribute = bagAttributes [i];
-								ASN1 attrId = pkcs12Attribute [0];
-								string aOid = ASN1Convert.ToOid (attrId);
-								ArrayList aValues = new ArrayList ();
-
-								ASN1 attrValues = pkcs12Attribute [1];
-									
-								for (int j = 0; j < attrValues.Count; j++) {
-									ASN1 attrValue = attrValues [j];
-									aValues.Add (attrValue.Value);
-								}
-								result.Add (aOid, aValues);
-							}
-						}
-					}
-				}
-			}
-
-			return result;
-		}
-
-		public IDictionary GetAttributes (X509Certificate cert)
-		{
-			IDictionary result = new Hashtable ();
-
-			foreach (SafeBag sb in _safeBags) {
-				if (sb.BagOID.Equals (certBag)) {
-					ASN1 safeBag = sb.ASN1;
-					ASN1 bagValue = safeBag [1];
-					PKCS7.ContentInfo crt = new PKCS7.ContentInfo (bagValue.Value);
-					X509Certificate xc = new X509Certificate (crt.Content [0].Value);
-
-					if (Compare (cert.RawData, xc.RawData)) {
-						if (safeBag.Count == 3) {
-							ASN1 bagAttributes = safeBag [2];
-
-							for (int i = 0; i < bagAttributes.Count; i++) {
-								ASN1 pkcs12Attribute = bagAttributes [i];
-								ASN1 attrId = pkcs12Attribute [0];
-
-								string aOid = ASN1Convert.ToOid (attrId);
-								ArrayList aValues = new ArrayList ();
-
-								ASN1 attrValues = pkcs12Attribute [1];
-									
-								for (int j = 0; j < attrValues.Count; j++) {
-									ASN1 attrValue = attrValues [j];
-									aValues.Add (attrValue.Value);
-								}
-								result.Add (aOid, aValues);
-							}
-						}
-					}
-				}
-			}
-
-			return result;
-		}
-
-		public void SaveToFile (string filename)
-		{
-			if (filename == null)
-				throw new ArgumentNullException ("filename");
-
-			using (FileStream fs = File.Create (filename)) {
-				byte[] data = GetBytes ();
-				fs.Write (data, 0, data.Length);
-			}
-		}
-
-		public object Clone ()
-		{
-			PKCS12 clone = null;
-			if (_password != null) {
-				clone = new PKCS12 (GetBytes (), Encoding.BigEndianUnicode.GetString (_password));
-			} else {
-				clone = new PKCS12 (GetBytes ());
-			}
-			clone.IterationCount = this.IterationCount;
-
-			return clone;
-		}
-
-		// static
-
-		public const int CryptoApiPasswordLimit = 32;
-		
-		static private int password_max_length = Int32.MaxValue;
-
-		// static properties
-		
-		// MS CryptoAPI limits the password to a maximum of 31 characters
-		// other implementations, like OpenSSL, have no such limitation.
-		// Setting a maximum value will truncate the password length to 
-		// ensure compatibility with MS's PFXImportCertStore API.
-		static public int MaximumPasswordLength {
-			get { return password_max_length; }
-			set {
-				if (value < CryptoApiPasswordLimit) {
-					string msg = string.Format ("Maximum password length cannot be less than {0}.", CryptoApiPasswordLimit);
-					throw new ArgumentOutOfRangeException (msg);
-				}
-				password_max_length = value;
-			}
-		}
-	}
-}

+ 0 - 1012
Emby.Server.Implementations/Cryptography/PKCS7.cs

@@ -1,1012 +0,0 @@
-//
-// PKCS7.cs: PKCS #7 - Cryptographic Message Syntax Standard 
-//	http://www.rsasecurity.com/rsalabs/pkcs/pkcs-7/index.html
-//
-// Authors:
-//	Sebastien Pouliot <sebastien@ximian.com>
-//	Daniel Granath <dgranath#gmail.com>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections;
-using System.Security.Cryptography;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    public sealed class PKCS7 {
-
-		public class Oid {
-			// pkcs 1
-			public const string rsaEncryption = "1.2.840.113549.1.1.1";
-			// pkcs 7
-			public const string data = "1.2.840.113549.1.7.1";
-			public const string signedData = "1.2.840.113549.1.7.2";
-			public const string envelopedData = "1.2.840.113549.1.7.3";
-			public const string signedAndEnvelopedData = "1.2.840.113549.1.7.4";
-			public const string digestedData = "1.2.840.113549.1.7.5";
-			public const string encryptedData = "1.2.840.113549.1.7.6";
-			// pkcs 9
-			public const string contentType = "1.2.840.113549.1.9.3";
-			public const string messageDigest  = "1.2.840.113549.1.9.4";
-			public const string signingTime = "1.2.840.113549.1.9.5";
-			public const string countersignature = "1.2.840.113549.1.9.6";
-
-			public Oid () 
-			{
-			}
-		}
-
-		private PKCS7 ()
-		{
-		}
-
-		static public ASN1 Attribute (string oid, ASN1 value) 
-		{
-			ASN1 attr = new ASN1 (0x30);
-			attr.Add (ASN1Convert.FromOid (oid));
-			ASN1 aset = attr.Add (new ASN1 (0x31));
-			aset.Add (value);
-			return attr;
-		}
-
-		static public ASN1 AlgorithmIdentifier (string oid)
-		{
-			ASN1 ai = new ASN1 (0x30);
-			ai.Add (ASN1Convert.FromOid (oid));
-			ai.Add (new ASN1 (0x05));	// NULL
-			return ai;
-		}
-
-		static public ASN1 AlgorithmIdentifier (string oid, ASN1 parameters) 
-		{
-			ASN1 ai = new ASN1 (0x30);
-			ai.Add (ASN1Convert.FromOid (oid));
-			ai.Add (parameters);
-			return ai;
-		}
-
-		/*
-		 * IssuerAndSerialNumber ::= SEQUENCE {
-		 *	issuer Name,
-		 *	serialNumber CertificateSerialNumber 
-		 * }
-		 */
-		static public ASN1 IssuerAndSerialNumber (X509Certificate x509) 
-		{
-			ASN1 issuer = null;
-			ASN1 serial = null;
-			ASN1 cert = new ASN1 (x509.RawData);
-			int tbs = 0;
-			bool flag = false;
-			while (tbs < cert[0].Count) {
-				ASN1 e = cert[0][tbs++];
-				if (e.Tag == 0x02)
-					serial = e;
-				else if (e.Tag == 0x30) {
-					if (flag) {
-						issuer = e;
-						break;
-					}
-					flag = true;
-				}
-			}
-			ASN1 iasn = new ASN1 (0x30);
-			iasn.Add (issuer);
-			iasn.Add (serial);
-			return iasn;
-		}
-
-		/*
-		 * ContentInfo ::= SEQUENCE {
-		 *	contentType ContentType,
-		 *	content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
-		 * }
-		 * ContentType ::= OBJECT IDENTIFIER
-		 */
-		public class ContentInfo {
-
-			private string contentType;
-			private ASN1 content;
-
-			public ContentInfo () 
-			{
-				content = new ASN1 (0xA0);
-			}
-
-			public ContentInfo (string oid) : this ()
-			{
-				contentType = oid;
-			}
-
-			public ContentInfo (byte[] data) 
-				: this (new ASN1 (data)) {}
-
-			public ContentInfo (ASN1 asn1) 
-			{
-				// SEQUENCE with 1 or 2 elements
-				if ((asn1.Tag != 0x30) || ((asn1.Count < 1) && (asn1.Count > 2)))
-					throw new ArgumentException ("Invalid ASN1");
-				if (asn1[0].Tag != 0x06)
-					throw new ArgumentException ("Invalid contentType");
-				contentType = ASN1Convert.ToOid (asn1[0]);
-				if (asn1.Count > 1) {
-					if (asn1[1].Tag != 0xA0)
-						throw new ArgumentException ("Invalid content");
-					content = asn1[1];
-				}
-			}
-
-			public ASN1 ASN1 {
-				get { return GetASN1(); }
-			}
-
-			public ASN1 Content {
-				get { return content; }
-				set { content = value; }
-			}
-
-			public string ContentType {
-				get { return contentType; }
-				set { contentType = value; }
-			}
-
-			internal ASN1 GetASN1 () 
-			{
-				// ContentInfo ::= SEQUENCE {
-				ASN1 contentInfo = new ASN1 (0x30);
-				// contentType ContentType, -> ContentType ::= OBJECT IDENTIFIER
-				contentInfo.Add (ASN1Convert.FromOid (contentType));
-				// content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL 
-				if ((content != null) && (content.Count > 0))
-					contentInfo.Add (content);
-				return contentInfo;
-			}
-
-			public byte[] GetBytes () 
-			{
-				return GetASN1 ().GetBytes ();
-			}
-		}
-
-		/*
-		 * EncryptedData ::= SEQUENCE {
-		 *	version		INTEGER {edVer0(0)} (edVer0),
-		 *	 encryptedContentInfo  EncryptedContentInfo
-		 * }
-		 */
-		public class EncryptedData {
-			private byte _version;
-			private ContentInfo _content;
-			private ContentInfo _encryptionAlgorithm;
-			private byte[] _encrypted;
-
-			public EncryptedData () 
-			{
-				_version = 0;
-			}
-
-			public EncryptedData (byte[] data) 
-				: this (new ASN1 (data))
-			{
-			}
-
-			public EncryptedData (ASN1 asn1) : this () 
-			{
-				if ((asn1.Tag != 0x30) || (asn1.Count < 2))
-					throw new ArgumentException ("Invalid EncryptedData");
-
-				if (asn1 [0].Tag != 0x02)
-					throw new ArgumentException ("Invalid version");
-				_version = asn1 [0].Value [0];
-
-				ASN1 encryptedContentInfo = asn1 [1];
-				if (encryptedContentInfo.Tag != 0x30)
-					throw new ArgumentException ("missing EncryptedContentInfo");
-
-				ASN1 contentType = encryptedContentInfo [0];
-				if (contentType.Tag != 0x06)
-					throw new ArgumentException ("missing EncryptedContentInfo.ContentType");
-				_content = new ContentInfo (ASN1Convert.ToOid (contentType));
-
-				ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1];
-				if (contentEncryptionAlgorithm.Tag != 0x30)
-					throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
-				_encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0]));
-				_encryptionAlgorithm.Content = contentEncryptionAlgorithm [1];
-				
-				ASN1 encryptedContent = encryptedContentInfo [2];
-				if (encryptedContent.Tag != 0x80)
-					throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent");
-				_encrypted = encryptedContent.Value;
-			}
-
-			public ASN1 ASN1 {
-				get { return GetASN1(); }
-			}
-
-			public ContentInfo ContentInfo {
-				get { return _content; }
-			}
-
-			public ContentInfo EncryptionAlgorithm {
-				get { return _encryptionAlgorithm; }
-			}
-
-			public byte[] EncryptedContent {
-				get {
-					if (_encrypted == null)
-						return null;
-					return (byte[]) _encrypted.Clone ();
-				}
-			}
-
-			public byte Version {
-				get { return _version; }
-				set { _version = value; }
-			}
-
-			// methods
-
-			internal ASN1 GetASN1 () 
-			{
-				return null;
-			}
-
-			public byte[] GetBytes () 
-			{
-				return GetASN1 ().GetBytes ();
-			}
-		}
-
-		/*
-		 * EnvelopedData ::= SEQUENCE {
-		 *	version Version,
-		 *	recipientInfos RecipientInfos,
-		 *	encryptedContentInfo EncryptedContentInfo 
-		 * }
-		 * 
-		 * RecipientInfos ::= SET OF RecipientInfo
-		 * 
-		 * EncryptedContentInfo ::= SEQUENCE {
-		 *	contentType ContentType,
-		 *	contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
-		 *	encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
-		 * }
-		 * 
-		 * EncryptedContent ::= OCTET STRING
-		 * 
-		 */
-		public class EnvelopedData {
-			private byte _version;
-			private ContentInfo _content;
-			private ContentInfo _encryptionAlgorithm;
-			private ArrayList _recipientInfos;
-			private byte[] _encrypted;
-
-			public EnvelopedData () 
-			{
-				_version = 0;
-				_content = new ContentInfo ();
-				_encryptionAlgorithm = new ContentInfo ();
-				_recipientInfos = new ArrayList ();
-			}
-
-			public EnvelopedData (byte[] data) 
-				: this (new ASN1 (data))
-			{
-			}
-
-			public EnvelopedData (ASN1 asn1) : this ()
-			{
-				if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 3))
-					throw new ArgumentException ("Invalid EnvelopedData");
-
-				if (asn1[0][0].Tag != 0x02)
-					throw new ArgumentException ("Invalid version");
-				_version = asn1[0][0].Value[0];
-
-				// recipientInfos
-
-				ASN1 recipientInfos = asn1 [0][1];
-				if (recipientInfos.Tag != 0x31)
-					throw new ArgumentException ("missing RecipientInfos");
-				for (int i=0; i < recipientInfos.Count; i++) {
-					ASN1 recipientInfo = recipientInfos [i];
-					_recipientInfos.Add (new RecipientInfo (recipientInfo));
-				}
-
-				ASN1 encryptedContentInfo = asn1[0][2];
-				if (encryptedContentInfo.Tag != 0x30)
-					throw new ArgumentException ("missing EncryptedContentInfo");
-
-				ASN1 contentType = encryptedContentInfo [0];
-				if (contentType.Tag != 0x06)
-					throw new ArgumentException ("missing EncryptedContentInfo.ContentType");
-				_content = new ContentInfo (ASN1Convert.ToOid (contentType));
-
-				ASN1 contentEncryptionAlgorithm = encryptedContentInfo [1];
-				if (contentEncryptionAlgorithm.Tag != 0x30)
-					throw new ArgumentException ("missing EncryptedContentInfo.ContentEncryptionAlgorithmIdentifier");
-				_encryptionAlgorithm = new ContentInfo (ASN1Convert.ToOid (contentEncryptionAlgorithm [0]));
-				_encryptionAlgorithm.Content = contentEncryptionAlgorithm [1];
-				
-				ASN1 encryptedContent = encryptedContentInfo [2];
-				if (encryptedContent.Tag != 0x80)
-					throw new ArgumentException ("missing EncryptedContentInfo.EncryptedContent");
-				_encrypted = encryptedContent.Value;
-			}
-
-			public ArrayList RecipientInfos {
-				  get { return _recipientInfos; }
-			}
-
-			public ASN1 ASN1 {
-				get { return GetASN1(); }
-			}
-
-			public ContentInfo ContentInfo {
-				get { return _content; }
-			}
-
-			public ContentInfo EncryptionAlgorithm {
-				get { return _encryptionAlgorithm; }
-			}
-
-			public byte[] EncryptedContent {
-				get { 
-					if (_encrypted == null)
-						return null;
-					return (byte[]) _encrypted.Clone ();
-				}
-			}
-
-			public byte Version {
-				get { return _version; }
-				set { _version = value; }
-			}
-
-			internal ASN1 GetASN1 () 
-			{
-				// SignedData ::= SEQUENCE {
-				ASN1 signedData = new ASN1 (0x30);
-				// version Version -> Version ::= INTEGER
-/*				byte[] ver = { _version };
-				signedData.Add (new ASN1 (0x02, ver));
-				// digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
-				ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31));
-				if (hashAlgorithm != null) {
-					string hashOid = CryptoConfig.MapNameToOid (hashAlgorithm);
-					digestAlgorithms.Add (AlgorithmIdentifier (hashOid));
-				}
-
-				// contentInfo ContentInfo,
-				ASN1 ci = contentInfo.ASN1;
-				signedData.Add (ci);
-				if ((mda == null) && (hashAlgorithm != null)) {
-					// automatically add the messageDigest authenticated attribute
-					HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
-					byte[] idcHash = ha.ComputeHash (ci[1][0].Value);
-					ASN1 md = new ASN1 (0x30);
-					mda = Attribute (messageDigest, md.Add (new ASN1 (0x04, idcHash)));
-					signerInfo.AuthenticatedAttributes.Add (mda);
-				}
-
-				// certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
-				if (certs.Count > 0) {
-					ASN1 a0 = signedData.Add (new ASN1 (0xA0));
-					foreach (X509Certificate x in certs)
-						a0.Add (new ASN1 (x.RawData));
-				}
-				// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
-				if (crls.Count > 0) {
-					ASN1 a1 = signedData.Add (new ASN1 (0xA1));
-					foreach (byte[] crl in crls)
-						a1.Add (new ASN1 (crl));
-				}
-				// signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
-				ASN1 signerInfos = signedData.Add (new ASN1 (0x31));
-				if (signerInfo.Key != null)
-					signerInfos.Add (signerInfo.ASN1);*/
-				return signedData;
-			}
-
-			public byte[] GetBytes () {
-				return GetASN1 ().GetBytes ();
-			}
-		}
-
-		/* RecipientInfo ::= SEQUENCE {
-		 *	version Version,
-		 *	issuerAndSerialNumber IssuerAndSerialNumber,
-		 *	keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-		 *	encryptedKey EncryptedKey 
-		 * }
-		 * 
-		 * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
-		 * 
-		 * EncryptedKey ::= OCTET STRING
-		 */
-		public class RecipientInfo {
-
-			private int _version;
-			private string _oid;
-			private byte[] _key;
-			private byte[] _ski;
-			private string _issuer;
-			private byte[] _serial;
-
-			public RecipientInfo () {}
-
-			public RecipientInfo (ASN1 data) 
-			{
-				if (data.Tag != 0x30)
-					throw new ArgumentException ("Invalid RecipientInfo");
-				
-				ASN1 version = data [0];
-				if (version.Tag != 0x02)
-					throw new ArgumentException ("missing Version");
-				_version = version.Value [0];
-
-				// issuerAndSerialNumber IssuerAndSerialNumber
-				ASN1 subjectIdentifierType = data [1];
-				if ((subjectIdentifierType.Tag == 0x80) && (_version == 3)) {
-					_ski = subjectIdentifierType.Value;
-				}
-				else {
-					_issuer = X501.ToString (subjectIdentifierType [0]);
-					_serial = subjectIdentifierType [1].Value;
-				}
-
-				ASN1 keyEncryptionAlgorithm = data [2];
-				_oid = ASN1Convert.ToOid (keyEncryptionAlgorithm [0]);
-
-				ASN1 encryptedKey = data [3];
-				_key = encryptedKey.Value;
-			}
-
-			public string Oid {
-				get { return _oid; }
-			}
-
-			public byte[] Key {
-				get { 
-					if (_key == null)
-						return null;
-                                        return (byte[]) _key.Clone ();
-				}
-			}
-
-			public byte[] SubjectKeyIdentifier {
-				get { 
-					if (_ski == null)
-						return null;
-					return (byte[]) _ski.Clone ();
-				}
-			}
-
-			public string Issuer {
-				get { return _issuer; }
-			}
-
-			public byte[] Serial {
-				get { 
-					if (_serial == null)
-						return null;
-					return (byte[]) _serial.Clone ();
-				}
-			}
-
-			public int Version {
-				get { return _version; }
-			}
-		}
-
-		/*
-		 * SignedData ::= SEQUENCE {
-		 *	version Version,
-		 *	digestAlgorithms DigestAlgorithmIdentifiers,
-		 *	contentInfo ContentInfo,
-		 *	certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
-		 *	crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
-		 *	signerInfos SignerInfos 
-		 * }
-		 */
-		public class SignedData {
-			private byte version;
-			private string hashAlgorithm;
-			private ContentInfo contentInfo;
-			private X509CertificateCollection certs;
-			private ArrayList crls;
-			private SignerInfo signerInfo;
-			private bool mda;
-			private bool signed;
-
-			public SignedData () 
-			{
-				version = 1;
-				contentInfo = new ContentInfo ();
-				certs = new X509CertificateCollection ();
-				crls = new ArrayList ();
-				signerInfo = new SignerInfo ();
-				mda = true;
-				signed = false;
-			}
-
-			public SignedData (byte[] data) 
-				: this (new ASN1 (data)) 
-			{
-			}
-
-			public SignedData (ASN1 asn1) 
-			{
-				if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 4))
-					throw new ArgumentException ("Invalid SignedData");
-
-				if (asn1[0][0].Tag != 0x02)
-					throw new ArgumentException ("Invalid version");
-				version = asn1[0][0].Value[0];
-
-				contentInfo = new ContentInfo (asn1[0][2]);
-
-				int n = 3;
-				certs = new X509CertificateCollection ();
-				if (asn1[0][n].Tag == 0xA0) {
-					for (int i=0; i < asn1[0][n].Count; i++)
-						certs.Add (new X509Certificate (asn1[0][n][i].GetBytes ()));
-					n++;
-				}
-
-				crls = new ArrayList ();
-				if (asn1[0][n].Tag == 0xA1) {
-					for (int i=0; i < asn1[0][n].Count; i++)
-						crls.Add (asn1[0][n][i].GetBytes ());
-					n++;
-				}
-
-				if (asn1[0][n].Count > 0)
-					signerInfo = new SignerInfo (asn1[0][n]);
-				else
-					signerInfo = new SignerInfo ();
-
-				// Exchange hash algorithm Oid from SignerInfo
-				if (signerInfo.HashName != null) {
-					HashName = OidToName(signerInfo.HashName);
-				}
-				
-				// Check if SignerInfo has authenticated attributes
-				mda = (signerInfo.AuthenticatedAttributes.Count > 0);
-			}
-
-			public ASN1 ASN1 {
-				get { return GetASN1(); }
-			}
-
-			public X509CertificateCollection Certificates {
-				get { return certs; }
-			}
-
-			public ContentInfo ContentInfo {
-				get { return contentInfo; }
-			}
-
-			public ArrayList Crls {
-				get { return crls; }
-			}
-
-			public string HashName {
-				get { return hashAlgorithm; }
-				// todo add validation
-				set { 
-					hashAlgorithm = value; 
-					signerInfo.HashName = value;
-				}
-			}
-
-			public SignerInfo SignerInfo {
-				get { return signerInfo; }
-			}
-
-			public byte Version {
-				get { return version; }
-				set { version = value; }
-			}
-
-			public bool UseAuthenticatedAttributes {
-				get { return mda; }
-				set { mda = value; }
-			}
-
-			public bool VerifySignature (AsymmetricAlgorithm aa)
-			{
-				if (aa == null) {
-					return false;
-				}
-
-				RSAPKCS1SignatureDeformatter r = new RSAPKCS1SignatureDeformatter (aa);
-				r.SetHashAlgorithm (hashAlgorithm);
-				HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
-
-				byte[] signature = signerInfo.Signature;
-				byte[] hash = null;
-
-				if (mda) {
-					ASN1 asn = new ASN1 (0x31);
-					foreach (ASN1 attr in signerInfo.AuthenticatedAttributes)
-						asn.Add (attr);
-
-					hash = ha.ComputeHash (asn.GetBytes ());
-				} else {
-					hash = ha.ComputeHash (contentInfo.Content[0].Value);
-				}
-
-				if (hash != null && signature != null) {
-					return r.VerifySignature (hash, signature);
-				}
-				return false;
-			}
-
-			internal string OidToName (string oid)
-			{
-				switch (oid) {
-				case "1.3.14.3.2.26" :
-					return "SHA1";
-				case "1.2.840.113549.2.2" :
-					return "MD2";
-				case "1.2.840.113549.2.5" :
-					return "MD5";
-				case "2.16.840.1.101.3.4.1" :
-					return "SHA256";
-				case "2.16.840.1.101.3.4.2" :
-					return "SHA384";
-				case "2.16.840.1.101.3.4.3" :
-					return "SHA512";
-				default :
-					break;
-				}
-				// Unknown Oid
-				return oid;
-			}
-
-			internal ASN1 GetASN1 () 
-			{
-				// SignedData ::= SEQUENCE {
-				ASN1 signedData = new ASN1 (0x30);
-				// version Version -> Version ::= INTEGER
-				byte[] ver = { version };
-				signedData.Add (new ASN1 (0x02, ver));
-				// digestAlgorithms DigestAlgorithmIdentifiers -> DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
-				ASN1 digestAlgorithms = signedData.Add (new ASN1 (0x31));
-				if (hashAlgorithm != null) {
-					string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
-					digestAlgorithms.Add (AlgorithmIdentifier (hashOid));
-				}
-
-				// contentInfo ContentInfo,
-				ASN1 ci = contentInfo.ASN1;
-				signedData.Add (ci);
-				if (!signed && (hashAlgorithm != null)) {
-					if (mda) {
-						// Use authenticated attributes for signature
-						
-						// Automatically add the contentType authenticated attribute
-						ASN1 ctattr = Attribute (Oid.contentType, ci[0]);
-						signerInfo.AuthenticatedAttributes.Add (ctattr);
-						
-						// Automatically add the messageDigest authenticated attribute
-						HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
-						byte[] idcHash = ha.ComputeHash (ci[1][0].Value);
-						ASN1 md = new ASN1 (0x30);
-						ASN1 mdattr = Attribute (Oid.messageDigest, md.Add (new ASN1 (0x04, idcHash)));
-						signerInfo.AuthenticatedAttributes.Add (mdattr);
-					} else {
-						// Don't use authenticated attributes for signature -- signature is content
-						RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (signerInfo.Key);
-						r.SetHashAlgorithm (hashAlgorithm);
-						HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
-						byte[] sig = ha.ComputeHash (ci[1][0].Value);
-						signerInfo.Signature = r.CreateSignature (sig);
-					}
-					signed = true;
-				}
-
-				// certificates [0] IMPLICIT ExtendedCertificatesAndCertificates OPTIONAL,
-				if (certs.Count > 0) {
-					ASN1 a0 = signedData.Add (new ASN1 (0xA0));
-					foreach (X509Certificate x in certs)
-						a0.Add (new ASN1 (x.RawData));
-				}
-				// crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
-				if (crls.Count > 0) {
-					ASN1 a1 = signedData.Add (new ASN1 (0xA1));
-					foreach (byte[] crl in crls)
-						a1.Add (new ASN1 (crl));
-				}
-				// signerInfos SignerInfos -> SignerInfos ::= SET OF SignerInfo
-				ASN1 signerInfos = signedData.Add (new ASN1 (0x31));
-				if (signerInfo.Key != null)
-					signerInfos.Add (signerInfo.ASN1);
-				return signedData;
-			}
-
-			public byte[] GetBytes () 
-			{
-				return GetASN1 ().GetBytes ();
-			}
-		}
-
-		/*
-		 * SignerInfo ::= SEQUENCE {
-		 *	version Version,
-		 * 	issuerAndSerialNumber IssuerAndSerialNumber,
-		 * 	digestAlgorithm DigestAlgorithmIdentifier,
-		 * 	authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
-		 * 	digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
-		 * 	encryptedDigest EncryptedDigest,
-		 * 	unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL 
-		 * }
-		 * 
-		 * For version == 3 issuerAndSerialNumber may be replaced by ...
-		 */
-		public class SignerInfo {
-
-			private byte version;
-			private X509Certificate x509;
-			private string hashAlgorithm;
-			private AsymmetricAlgorithm key;
-			private ArrayList authenticatedAttributes;
-			private ArrayList unauthenticatedAttributes;
-			private byte[] signature;
-			private string issuer;
-			private byte[] serial;
-			private byte[] ski;
-
-			public SignerInfo () 
-			{
-				version = 1;
-				authenticatedAttributes = new ArrayList ();
-				unauthenticatedAttributes = new ArrayList ();
-			}
-
-			public SignerInfo (byte[] data) 
-				: this (new ASN1 (data)) {}
-
-			// TODO: INCOMPLETE
-			public SignerInfo (ASN1 asn1) : this () 
-			{
-				if ((asn1[0].Tag != 0x30) || (asn1[0].Count < 5))
-					throw new ArgumentException ("Invalid SignedData");
-
-				// version Version
-				if (asn1[0][0].Tag != 0x02)
-					throw new ArgumentException ("Invalid version");
-				version = asn1[0][0].Value[0];
-
-				// issuerAndSerialNumber IssuerAndSerialNumber
-				ASN1 subjectIdentifierType = asn1 [0][1];
-				if ((subjectIdentifierType.Tag == 0x80) && (version == 3)) {
-					ski = subjectIdentifierType.Value;
-				}
-				else {
-					issuer = X501.ToString (subjectIdentifierType [0]);
-					serial = subjectIdentifierType [1].Value;
-				}
-
-				// digestAlgorithm DigestAlgorithmIdentifier
-				ASN1 digestAlgorithm = asn1 [0][2];
-				hashAlgorithm = ASN1Convert.ToOid (digestAlgorithm [0]);
-
-				// authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL
-				int n = 3;
-				ASN1 authAttributes = asn1 [0][n];
-				if (authAttributes.Tag == 0xA0) {
-					n++;
-					for (int i=0; i < authAttributes.Count; i++)
-						authenticatedAttributes.Add (authAttributes [i]);
-				}
-
-				// digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier
-				n++;
-				// ASN1 digestEncryptionAlgorithm = asn1 [0][n++];
-				// string digestEncryptionAlgorithmOid = ASN1Convert.ToOid (digestEncryptionAlgorithm [0]);
-
-				// encryptedDigest EncryptedDigest
-				ASN1 encryptedDigest = asn1 [0][n++];
-				if (encryptedDigest.Tag == 0x04)
-					signature = encryptedDigest.Value;
-
-				// unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
-				ASN1 unauthAttributes = asn1 [0][n];
-				if ((unauthAttributes != null) && (unauthAttributes.Tag == 0xA1)) {
-					for (int i=0; i < unauthAttributes.Count; i++)
-						unauthenticatedAttributes.Add (unauthAttributes [i]);
-				}
-			}
-
-			public string IssuerName {
-				get { return issuer; }
-			}
-
-			public byte[] SerialNumber {
-				get { 
-					if (serial == null)
-						return null;
-					return (byte[]) serial.Clone (); 
-				}
-			}
-
-			public byte[] SubjectKeyIdentifier {
-				get { 
-					if (ski == null)
-						return null;
-					return (byte[]) ski.Clone (); 
-				}
-			}
-
-			public ASN1 ASN1 {
-				get { return GetASN1(); }
-			}
-
-			public ArrayList AuthenticatedAttributes {
-				get { return authenticatedAttributes; }
-			}
-
-			public X509Certificate Certificate {
-				get { return x509; }
-				set { x509 = value; }
-			}
-
-			public string HashName {
-				get { return hashAlgorithm; }
-				set { hashAlgorithm = value; }
-			}
-
-			public AsymmetricAlgorithm Key {
-				get { return key; }
-				set { key = value; }
-			}
-
-			public byte[] Signature {
-				get { 
-					if (signature == null)
-						return null;
-					return (byte[]) signature.Clone (); 
-				}
-
-				set {
-					if (value != null) {
-						signature = (byte[]) value.Clone ();
-					}
-				}
-			}
-
-			public ArrayList UnauthenticatedAttributes {
-				get { return unauthenticatedAttributes; }
-			}
-
-			public byte Version {
-				get { return version; }
-				set { version = value; }
-			}
-
-			internal ASN1 GetASN1 () 
-			{
-				if ((key == null) || (hashAlgorithm == null))
-					return null;
-				byte[] ver = { version };
-				ASN1 signerInfo = new ASN1 (0x30);
-				// version Version -> Version ::= INTEGER
-				signerInfo.Add (new ASN1 (0x02, ver));
-				// issuerAndSerialNumber IssuerAndSerialNumber,
-				signerInfo.Add (PKCS7.IssuerAndSerialNumber (x509));
-				// digestAlgorithm DigestAlgorithmIdentifier,
-				string hashOid = CryptoConfig.MapNameToOID (hashAlgorithm);
-				signerInfo.Add (AlgorithmIdentifier (hashOid));
-				// authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
-				ASN1 aa = null;
-				if (authenticatedAttributes.Count > 0) {
-					aa = signerInfo.Add (new ASN1 (0xA0));
-					authenticatedAttributes.Sort(new SortedSet ());
-					foreach (ASN1 attr in authenticatedAttributes)
-						aa.Add (attr);
-				}
-				// digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
-				if (key is RSA) {
-					signerInfo.Add (AlgorithmIdentifier (PKCS7.Oid.rsaEncryption));
-
-					if (aa != null) {
-						// Calculate the signature here; otherwise it must be set from SignedData
-						RSAPKCS1SignatureFormatter r = new RSAPKCS1SignatureFormatter (key);
-						r.SetHashAlgorithm (hashAlgorithm);
-						byte[] tbs = aa.GetBytes ();
-						tbs [0] = 0x31; // not 0xA0 for signature
-						HashAlgorithm ha = HashAlgorithm.Create (hashAlgorithm);
-						byte[] tbsHash = ha.ComputeHash (tbs);
-						signature = r.CreateSignature (tbsHash);
-					}
-				}
-				else if (key is DSA) {
-					throw new NotImplementedException ("not yet");
-				}
-				else
-					throw new CryptographicException ("Unknown assymetric algorithm");
-				// encryptedDigest EncryptedDigest,
-				signerInfo.Add (new ASN1 (0x04, signature));
-				// unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL 
-				if (unauthenticatedAttributes.Count > 0) {
-					ASN1 ua = signerInfo.Add (new ASN1 (0xA1));
-					unauthenticatedAttributes.Sort(new SortedSet ());
-					foreach (ASN1 attr in unauthenticatedAttributes)
-						ua.Add (attr);
-				}
-				return signerInfo;
-			}
-
-			public byte[] GetBytes () 
-			{
-				return GetASN1 ().GetBytes ();
-			}
-		}
-
-		internal class SortedSet : IComparer {
-
-			public int Compare (object x, object y)
-			{
-				if (x == null)
-					return (y == null) ? 0 : -1;
-				else if (y == null)
-					return 1;
-
-				ASN1 xx = x as ASN1;
-				ASN1 yy = y as ASN1;
-				
-				if ((xx == null) || (yy == null)) {
-					throw new ArgumentException (("Invalid objects."));
-				}
-
-				byte[] xb = xx.GetBytes ();
-				byte[] yb = yy.GetBytes ();
-
-				for (int i = 0; i < xb.Length; i++) {
-					if (i == yb.Length)
-						break;
-
-					if (xb[i] == yb[i]) 
-						continue;
-						
-					return (xb[i] < yb[i]) ? -1 : 1; 
-				}
-
-				// The arrays are equal up to the shortest of them.
-				if (xb.Length > yb.Length)
-					return 1;
-				else if (xb.Length < yb.Length)
-					return -1;
-
-				return 0;
-			}
-		}
-	}
-}

+ 0 - 495
Emby.Server.Implementations/Cryptography/PKCS8.cs

@@ -1,495 +0,0 @@
-//
-// PKCS8.cs: PKCS #8 - Private-Key Information Syntax Standard
-//	ftp://ftp.rsasecurity.com/pub/pkcs/doc/pkcs-8.doc
-//
-// Author:
-//	Sebastien Pouliot <sebastien@xamarin.com>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2006 Novell Inc. (http://www.novell.com)
-// Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections;
-using System.Security.Cryptography;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    public sealed class PKCS8 {
-
-		public enum KeyInfo {
-			PrivateKey,
-			EncryptedPrivateKey,
-			Unknown
-		}
-
-		private PKCS8 () 
-		{
-		}
-
-		static public KeyInfo GetType (byte[] data) 
-		{
-			if (data == null)
-				throw new ArgumentNullException ("data");
-
-			KeyInfo ki = KeyInfo.Unknown;
-			try {
-				ASN1 top = new ASN1 (data);
-				if ((top.Tag == 0x30) && (top.Count > 0)) {
-					ASN1 firstLevel = top [0];
-					switch (firstLevel.Tag) {
-						case 0x02:
-							ki = KeyInfo.PrivateKey;
-							break;
-						case 0x30:
-							ki = KeyInfo.EncryptedPrivateKey;
-							break;
-					}
-				}
-			}
-			catch {
-				throw new CryptographicException ("invalid ASN.1 data");
-			}
-			return ki;
-		}
-
-		/*
-		 * PrivateKeyInfo ::= SEQUENCE {
-		 *	version Version,
-		 *	privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
-		 *	privateKey PrivateKey,
-		 *	attributes [0] IMPLICIT Attributes OPTIONAL 
-		 * }
-		 * 
-		 * Version ::= INTEGER
-		 * 
-		 * PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
-		 * 
-		 * PrivateKey ::= OCTET STRING
-		 * 
-		 * Attributes ::= SET OF Attribute
-		 */
-		public class PrivateKeyInfo {
-
-			private int _version;
-			private string _algorithm;
-			private byte[] _key;
-			private ArrayList _list;
-
-			public PrivateKeyInfo () 
-			{
-				_version = 0;
-				_list = new ArrayList ();
-			}
-
-			public PrivateKeyInfo (byte[] data) : this () 
-			{
-				Decode (data);
-			}
-
-			// properties
-
-			public string Algorithm {
-				get { return _algorithm; }
-				set { _algorithm = value; }
-			}
-
-			public ArrayList Attributes {
-				get { return _list; }
-			}
-
-			public byte[] PrivateKey {
-				get {
-					if (_key == null)
-						return null;
-					return (byte[]) _key.Clone (); 
-				}
-				set { 
-					if (value == null)
-						throw new ArgumentNullException ("PrivateKey");
-					_key = (byte[]) value.Clone (); 
-				}
-			}
-
-			public int Version {
-				get { return _version; }
-				set { 
-					if (value < 0)
-						throw new ArgumentOutOfRangeException ("negative version");
-					_version = value; 
-				}
-			}
-
-			// methods
-
-			private void Decode (byte[] data) 
-			{
-				ASN1 privateKeyInfo = new ASN1 (data);
-				if (privateKeyInfo.Tag != 0x30)
-					throw new CryptographicException ("invalid PrivateKeyInfo");
-
-				ASN1 version = privateKeyInfo [0];
-				if (version.Tag != 0x02)
-					throw new CryptographicException ("invalid version");
-				_version = version.Value [0];
-
-				ASN1 privateKeyAlgorithm = privateKeyInfo [1];
-				if (privateKeyAlgorithm.Tag != 0x30)
-					throw new CryptographicException ("invalid algorithm");
-				
-				ASN1 algorithm = privateKeyAlgorithm [0];
-				if (algorithm.Tag != 0x06)
-					throw new CryptographicException ("missing algorithm OID");
-				_algorithm = ASN1Convert.ToOid (algorithm);
-
-				ASN1 privateKey = privateKeyInfo [2];
-				_key = privateKey.Value;
-
-				// attributes [0] IMPLICIT Attributes OPTIONAL
-				if (privateKeyInfo.Count > 3) {
-					ASN1 attributes = privateKeyInfo [3];
-					for (int i=0; i < attributes.Count; i++) {
-						_list.Add (attributes [i]);
-					}
-				}
-			}
-
-			public byte[] GetBytes () 
-			{
-				ASN1 privateKeyAlgorithm = new ASN1 (0x30);
-				privateKeyAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
-				privateKeyAlgorithm.Add (new ASN1 (0x05)); // ASN.1 NULL
-
-				ASN1 pki = new ASN1 (0x30);
-				pki.Add (new ASN1 (0x02, new byte [1] { (byte) _version }));
-				pki.Add (privateKeyAlgorithm);
-				pki.Add (new ASN1 (0x04, _key));
-
-				if (_list.Count > 0) {
-					ASN1 attributes = new ASN1 (0xA0);
-					foreach (ASN1 attribute in _list) {
-						attributes.Add (attribute);
-					}
-					pki.Add (attributes);
-				}
-
-				return pki.GetBytes ();
-			}
-
-			// static methods
-
-			static private byte[] RemoveLeadingZero (byte[] bigInt) 
-			{
-				int start = 0;
-				int length = bigInt.Length;
-				if (bigInt [0] == 0x00) {
-					start = 1;
-					length--;
-				}
-				byte[] bi = new byte [length];
-				Buffer.BlockCopy (bigInt, start, bi, 0, length);
-				return bi;
-			}
-
-			static private byte[] Normalize (byte[] bigInt, int length) 
-			{
-				if (bigInt.Length == length)
-					return bigInt;
-				else if (bigInt.Length > length)
-					return RemoveLeadingZero (bigInt);
-				else {
-					// pad with 0
-					byte[] bi = new byte [length];
-					Buffer.BlockCopy (bigInt, 0, bi, (length - bigInt.Length), bigInt.Length);
-					return bi;
-				}
-			}
-			
-			/*
-			 * RSAPrivateKey ::= SEQUENCE {
-			 *	version           Version, 
-			 *	modulus           INTEGER,  -- n
-			 *	publicExponent    INTEGER,  -- e
-			 *	privateExponent   INTEGER,  -- d
-			 *	prime1            INTEGER,  -- p
-			 *	prime2            INTEGER,  -- q
-			 *	exponent1         INTEGER,  -- d mod (p-1)
-			 *	exponent2         INTEGER,  -- d mod (q-1) 
-			 *	coefficient       INTEGER,  -- (inverse of q) mod p
-			 *	otherPrimeInfos   OtherPrimeInfos OPTIONAL 
-			 * }
-			 */
-			static public RSA DecodeRSA (byte[] keypair) 
-			{
-				ASN1 privateKey = new ASN1 (keypair);
-				if (privateKey.Tag != 0x30)
-					throw new CryptographicException ("invalid private key format");
-
-				ASN1 version = privateKey [0];
-				if (version.Tag != 0x02)
-					throw new CryptographicException ("missing version");
-
-				if (privateKey.Count < 9)
-					throw new CryptographicException ("not enough key parameters");
-
-				RSAParameters param = new RSAParameters ();
-				// note: MUST remove leading 0 - else MS wont import the key
-				param.Modulus = RemoveLeadingZero (privateKey [1].Value);
-				int keysize = param.Modulus.Length;
-				int keysize2 = (keysize >> 1); // half-size
-				// size must be normalized - else MS wont import the key
-				param.D = Normalize (privateKey [3].Value, keysize);
-				param.DP = Normalize (privateKey [6].Value, keysize2);
-				param.DQ = Normalize (privateKey [7].Value, keysize2);
-				param.Exponent = RemoveLeadingZero (privateKey [2].Value);
-				param.InverseQ = Normalize (privateKey [8].Value, keysize2);
-				param.P = Normalize (privateKey [4].Value, keysize2);
-				param.Q = Normalize (privateKey [5].Value, keysize2);
-
-				RSA rsa = null;
-				try {
-					rsa = RSA.Create ();
-					rsa.ImportParameters (param);
-				}
-				catch (CryptographicException) {
-                    // this may cause problem when this code is run under
-                    // the SYSTEM identity on Windows (e.g. ASP.NET). See
-                    // http://bugzilla.ximian.com/show_bug.cgi?id=77559
-                    CspParameters csp = new CspParameters();
-                    csp.Flags = CspProviderFlags.UseMachineKeyStore;
-                    rsa = new RSACryptoServiceProvider(csp);
-                    rsa.ImportParameters(param);
-                }
-                return rsa;
-			}
-
-			/*
-			 * RSAPrivateKey ::= SEQUENCE {
-			 *	version           Version, 
-			 *	modulus           INTEGER,  -- n
-			 *	publicExponent    INTEGER,  -- e
-			 *	privateExponent   INTEGER,  -- d
-			 *	prime1            INTEGER,  -- p
-			 *	prime2            INTEGER,  -- q
-			 *	exponent1         INTEGER,  -- d mod (p-1)
-			 *	exponent2         INTEGER,  -- d mod (q-1) 
-			 *	coefficient       INTEGER,  -- (inverse of q) mod p
-			 *	otherPrimeInfos   OtherPrimeInfos OPTIONAL 
-			 * }
-			 */
-			static public byte[] Encode (RSA rsa) 
-			{
-				RSAParameters param = rsa.ExportParameters (true);
-
-				ASN1 rsaPrivateKey = new ASN1 (0x30);
-				rsaPrivateKey.Add (new ASN1 (0x02, new byte [1] { 0x00 }));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Modulus));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Exponent));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.D));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.P));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.Q));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DP));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.DQ));
-				rsaPrivateKey.Add (ASN1Convert.FromUnsignedBigInteger (param.InverseQ));
-
-				return rsaPrivateKey.GetBytes ();
-			}
-
-			// DSA only encode it's X private key inside an ASN.1 INTEGER (Hint: Tag == 0x02)
-			// which isn't enough for rebuilding the keypair. The other parameters
-			// can be found (98% of the time) in the X.509 certificate associated
-			// with the private key or (2% of the time) the parameters are in it's
-			// issuer X.509 certificate (not supported in the .NET framework).
-			static public DSA DecodeDSA (byte[] privateKey, DSAParameters dsaParameters) 
-			{
-				ASN1 pvk = new ASN1 (privateKey);
-				if (pvk.Tag != 0x02)
-					throw new CryptographicException ("invalid private key format");
-
-				// X is ALWAYS 20 bytes (no matter if the key length is 512 or 1024 bits)
-				dsaParameters.X = Normalize (pvk.Value, 20);
-				DSA dsa = DSA.Create ();
-				dsa.ImportParameters (dsaParameters);
-				return dsa;
-			}
-
-			static public byte[] Encode (DSA dsa) 
-			{
-				DSAParameters param = dsa.ExportParameters (true);
-				return ASN1Convert.FromUnsignedBigInteger (param.X).GetBytes ();
-			}
-
-			static public byte[] Encode (AsymmetricAlgorithm aa) 
-			{
-				if (aa is RSA)
-					return Encode ((RSA)aa);
-				else if (aa is DSA)
-					return Encode ((DSA)aa);
-				else
-					throw new CryptographicException ("Unknown asymmetric algorithm {0}", aa.ToString ());
-			}
-		}
-
-		/*
-		 * EncryptedPrivateKeyInfo ::= SEQUENCE {
-		 *	encryptionAlgorithm EncryptionAlgorithmIdentifier,
-		 *	encryptedData EncryptedData 
-		 * }
-		 * 
-		 * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
-		 * 
-		 * EncryptedData ::= OCTET STRING
-		 * 
-		 * --
-		 *  AlgorithmIdentifier  ::= SEQUENCE {
-		 *	algorithm  OBJECT IDENTIFIER,
-		 *	parameters ANY DEFINED BY algorithm OPTIONAL
-		 * }
-		 * 
-		 * -- from PKCS#5
-		 * PBEParameter ::= SEQUENCE {
-		 *	salt OCTET STRING SIZE(8),
-		 *	iterationCount INTEGER 
-		 * }
-		 */
-		public class EncryptedPrivateKeyInfo {
-
-			private string _algorithm;
-			private byte[] _salt;
-			private int _iterations;
-			private byte[] _data;
-
-			public EncryptedPrivateKeyInfo () {}
-
-			public EncryptedPrivateKeyInfo (byte[] data) : this () 
-			{
-				Decode (data);
-			}
-
-			// properties
-
-			public string Algorithm {
-				get { return _algorithm; }
-				set { _algorithm = value; }
-			}
-
-			public byte[] EncryptedData {
-				get { return (_data == null) ? null : (byte[]) _data.Clone (); }
-				set { _data = (value == null) ? null : (byte[]) value.Clone (); }
-			}
-
-			public byte[] Salt {
-				get { 
-					if (_salt == null) {
-						RandomNumberGenerator rng = RandomNumberGenerator.Create ();
-						_salt = new byte [8];
-						rng.GetBytes (_salt);
-					}
-					return (byte[]) _salt.Clone (); 
-				}
-				set { _salt = (byte[]) value.Clone (); }
-			}
-
-			public int IterationCount {
-				get { return _iterations; }
-				set { 
-					if (value < 0)
-						throw new ArgumentOutOfRangeException ("IterationCount", "Negative");
-					_iterations = value; 
-				}
-			}
-
-			// methods
-
-			private void Decode (byte[] data) 
-			{
-				ASN1 encryptedPrivateKeyInfo = new ASN1 (data);
-				if (encryptedPrivateKeyInfo.Tag != 0x30)
-					throw new CryptographicException ("invalid EncryptedPrivateKeyInfo");
-
-				ASN1 encryptionAlgorithm = encryptedPrivateKeyInfo [0];
-				if (encryptionAlgorithm.Tag != 0x30)
-					throw new CryptographicException ("invalid encryptionAlgorithm");
-				ASN1 algorithm = encryptionAlgorithm [0];
-				if (algorithm.Tag != 0x06)
-					throw new CryptographicException ("invalid algorithm");
-				_algorithm = ASN1Convert.ToOid (algorithm);
-				// parameters ANY DEFINED BY algorithm OPTIONAL
-				if (encryptionAlgorithm.Count > 1) {
-					ASN1 parameters = encryptionAlgorithm [1];
-					if (parameters.Tag != 0x30)
-						throw new CryptographicException ("invalid parameters");
-
-					ASN1 salt = parameters [0];
-					if (salt.Tag != 0x04)
-						throw new CryptographicException ("invalid salt");
-					_salt = salt.Value;
-
-					ASN1 iterationCount = parameters [1];
-					if (iterationCount.Tag != 0x02)
-						throw new CryptographicException ("invalid iterationCount");
-					_iterations = ASN1Convert.ToInt32 (iterationCount);
-				}
-
-				ASN1 encryptedData = encryptedPrivateKeyInfo [1];
-				if (encryptedData.Tag != 0x04)
-					throw new CryptographicException ("invalid EncryptedData");
-				_data = encryptedData.Value;
-			}
-
-			// Note: PKCS#8 doesn't define how to generate the key required for encryption
-			// so you're on your own. Just don't try to copy the big guys too much ;)
-			// Netscape:	http://www.cs.auckland.ac.nz/~pgut001/pubs/netscape.txt
-			// Microsoft:	http://www.cs.auckland.ac.nz/~pgut001/pubs/breakms.txt
-			public byte[] GetBytes ()
-			{
-				if (_algorithm == null)
-					throw new CryptographicException ("No algorithm OID specified");
-
-				ASN1 encryptionAlgorithm = new ASN1 (0x30);
-				encryptionAlgorithm.Add (ASN1Convert.FromOid (_algorithm));
-
-				// parameters ANY DEFINED BY algorithm OPTIONAL
-				if ((_iterations > 0) || (_salt != null)) {
-					ASN1 salt = new ASN1 (0x04, _salt);
-					ASN1 iterations = ASN1Convert.FromInt32 (_iterations);
-
-					ASN1 parameters = new ASN1 (0x30);
-					parameters.Add (salt);
-					parameters.Add (iterations);
-					encryptionAlgorithm.Add (parameters);
-				}
-
-				// encapsulates EncryptedData into an OCTET STRING
-				ASN1 encryptedData = new ASN1 (0x04, _data);
-
-				ASN1 encryptedPrivateKeyInfo = new ASN1 (0x30);
-				encryptedPrivateKeyInfo.Add (encryptionAlgorithm);
-				encryptedPrivateKeyInfo.Add (encryptedData);
-
-				return encryptedPrivateKeyInfo.GetBytes ();
-			}
-		}
-	}
-}

+ 0 - 75
Emby.Server.Implementations/Cryptography/PfxGenerator.cs

@@ -1,75 +0,0 @@
-using System;
-using System.Collections;
-using System.Security.Cryptography;
-
-namespace Emby.Server.Core.Cryptography
-{
-    public class PFXGenerator
-    {
-        // http://www.freekpaans.nl/2015/04/creating-self-signed-x-509-certificates-using-mono-security/
-        public static byte[] GeneratePfx(string certificateName, string password)
-        {
-            byte[] sn = GenerateSerialNumber();
-            string subject = string.Format("CN={0}", certificateName);
-
-            DateTime notBefore = DateTime.Now;
-            DateTime notAfter = DateTime.Now.AddYears(20);
-
-            RSA subjectKey = new RSACryptoServiceProvider(2048);
-
-
-            string hashName = "SHA256";
-
-            X509CertificateBuilder cb = new X509CertificateBuilder(3);
-            cb.SerialNumber = sn;
-            cb.IssuerName = subject;
-            cb.NotBefore = notBefore;
-            cb.NotAfter = notAfter;
-            cb.SubjectName = subject;
-            cb.SubjectPublicKey = subjectKey;
-            cb.Hash = hashName;
-
-            byte[] rawcert = cb.Sign(subjectKey);
-
-
-            PKCS12 p12 = new PKCS12();
-            p12.Password = password;
-
-            Hashtable attributes = GetAttributes();
-
-            p12.AddCertificate(new X509Certificate(rawcert), attributes);
-            p12.AddPkcs8ShroudedKeyBag(subjectKey, attributes);
-
-            return p12.GetBytes();
-        }
-
-        private static Hashtable GetAttributes()
-        {
-            ArrayList list = new ArrayList();
-            // we use a fixed array to avoid endianess issues 
-            // (in case some tools requires the ID to be 1).
-            list.Add(new byte[4] { 1, 0, 0, 0 });
-            Hashtable attributes = new Hashtable(1);
-            attributes.Add(PKCS9.localKeyId, list);
-            return attributes;
-        }
-
-        private static byte[] GenerateSerialNumber()
-        {
-            byte[] sn = Guid.NewGuid().ToByteArray();
-
-            //must be positive
-            if ((sn[0] & 0x80) == 0x80)
-                sn[0] -= 0x80;
-            return sn;
-        }
-
-        public static byte[] GetCertificateForBytes(byte[] pfx, string password)
-        {
-            var pkcs = new PKCS12(pfx, password);
-            var cert = pkcs.GetCertificate(GetAttributes());
-
-            return cert.RawData;
-        }
-    }
-}

+ 0 - 393
Emby.Server.Implementations/Cryptography/X501Name.cs

@@ -1,393 +0,0 @@
-//
-// X501Name.cs: X.501 Distinguished Names stuff 
-//
-// Author:
-//	Sebastien Pouliot <sebastien@ximian.com>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Globalization;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    // References:
-    // 1.	Information technology - Open Systems Interconnection - The Directory: Models
-    //	http://www.itu.int/rec/recommendation.asp?type=items&lang=e&parent=T-REC-X.501-200102-I
-    // 2.	RFC2253: Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names
-    //	http://www.ietf.org/rfc/rfc2253.txt
-
-    /*
-	 * Name ::= CHOICE { RDNSequence }
-	 * 
-	 * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
-	 * 
-	 * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
-	 */
-    public sealed class X501 {
-
-		static byte[] countryName = { 0x55, 0x04, 0x06 };
-		static byte[] organizationName = { 0x55, 0x04, 0x0A };
-		static byte[] organizationalUnitName = { 0x55, 0x04, 0x0B };
-		static byte[] commonName = { 0x55, 0x04, 0x03 };
-		static byte[] localityName = { 0x55, 0x04, 0x07 };
-		static byte[] stateOrProvinceName = { 0x55, 0x04, 0x08 };
-		static byte[] streetAddress = { 0x55, 0x04, 0x09 };
-		//static byte[] serialNumber = { 0x55, 0x04, 0x05 };
-		static byte[] domainComponent = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19 };
-		static byte[] userid = { 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x01 };
-		static byte[] email = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01 };
-		static byte[] dnQualifier = { 0x55, 0x04, 0x2E };
-		static byte[] title = { 0x55, 0x04, 0x0C };
-		static byte[] surname = { 0x55, 0x04, 0x04 };
-		static byte[] givenName = { 0x55, 0x04, 0x2A };
-		static byte[] initial = { 0x55, 0x04, 0x2B };
-
-		private X501 () 
-		{
-		}
-
-		static public string ToString (ASN1 seq) 
-		{
-			StringBuilder sb = new StringBuilder ();
-			for (int i = 0; i < seq.Count; i++) {
-				ASN1 entry = seq [i];
-				AppendEntry (sb, entry, true);
-
-				// separator (not on last iteration)
-				if (i < seq.Count - 1)
-					sb.Append (", ");
-			}
-			return sb.ToString ();
-		}
-
-		static public string ToString (ASN1 seq, bool reversed, string separator, bool quotes)
-		{
-			StringBuilder sb = new StringBuilder ();
-
-			if (reversed) {
-				for (int i = seq.Count - 1; i >= 0; i--) {
-					ASN1 entry = seq [i];
-					AppendEntry (sb, entry, quotes);
-
-					// separator (not on last iteration)
-					if (i > 0)
-						sb.Append (separator);
-				}
-			} else {
-				for (int i = 0; i < seq.Count; i++) {
-					ASN1 entry = seq [i];
-					AppendEntry (sb, entry, quotes);
-
-					// separator (not on last iteration)
-					if (i < seq.Count - 1)
-						sb.Append (separator);
-				}
-			}
-			return sb.ToString ();
-		}
-
-		static private void AppendEntry (StringBuilder sb, ASN1 entry, bool quotes)
-		{
-			// multiple entries are valid
-			for (int k = 0; k < entry.Count; k++) {
-				ASN1 pair = entry [k];
-				ASN1 s = pair [1];
-				if (s == null)
-					continue;
-
-				ASN1 poid = pair [0];
-				if (poid == null)
-					continue;
-
-				if (poid.CompareValue (countryName))
-					sb.Append ("C=");
-				else if (poid.CompareValue (organizationName))
-					sb.Append ("O=");
-				else if (poid.CompareValue (organizationalUnitName))
-					sb.Append ("OU=");
-				else if (poid.CompareValue (commonName))
-					sb.Append ("CN=");
-				else if (poid.CompareValue (localityName))
-					sb.Append ("L=");
-				else if (poid.CompareValue (stateOrProvinceName))
-					sb.Append ("S=");	// NOTE: RFC2253 uses ST=
-				else if (poid.CompareValue (streetAddress))
-					sb.Append ("STREET=");
-				else if (poid.CompareValue (domainComponent))
-					sb.Append ("DC=");
-				else if (poid.CompareValue (userid))
-					sb.Append ("UID=");
-				else if (poid.CompareValue (email))
-					sb.Append ("E=");	// NOTE: Not part of RFC2253
-				else if (poid.CompareValue (dnQualifier))
-					sb.Append ("dnQualifier=");
-				else if (poid.CompareValue (title))
-					sb.Append ("T=");
-				else if (poid.CompareValue (surname))
-					sb.Append ("SN=");
-				else if (poid.CompareValue (givenName))
-					sb.Append ("G=");
-				else if (poid.CompareValue (initial))
-					sb.Append ("I=");
-				else {
-					// unknown OID
-					sb.Append ("OID.");	// NOTE: Not present as RFC2253
-					sb.Append (ASN1Convert.ToOid (poid));
-					sb.Append ("=");
-				}
-
-				string sValue = null;
-				// 16bits or 8bits string ? TODO not complete (+special chars!)
-				if (s.Tag == 0x1E) {
-					// BMPSTRING
-					StringBuilder sb2 = new StringBuilder ();
-					for (int j = 1; j < s.Value.Length; j += 2)
-						sb2.Append ((char)s.Value[j]);
-					sValue = sb2.ToString ();
-				} else {
-					if (s.Tag == 0x14)
-						sValue = Encoding.UTF7.GetString (s.Value);
-					else
-						sValue = Encoding.UTF8.GetString (s.Value);
-					// in some cases we must quote (") the value
-					// Note: this doesn't seems to conform to RFC2253
-					char[] specials = { ',', '+', '"', '\\', '<', '>', ';' };
-					if (quotes) {
-						if ((sValue.IndexOfAny (specials, 0, sValue.Length) > 0) ||
-						    sValue.StartsWith (" ") || (sValue.EndsWith (" ")))
-							sValue = "\"" + sValue + "\"";
-					}
-				}
-
-				sb.Append (sValue);
-
-				// separator (not on last iteration)
-				if (k < entry.Count - 1)
-					sb.Append (", ");
-			}
-		}
-
-		static private X520.AttributeTypeAndValue GetAttributeFromOid (string attributeType) 
-		{
-			string s = attributeType.ToUpper (CultureInfo.InvariantCulture).Trim ();
-			switch (s) {
-				case "C":
-					return new X520.CountryName ();
-				case "O":
-					return new X520.OrganizationName ();
-				case "OU":
-					return new X520.OrganizationalUnitName ();
-				case "CN":
-					return new X520.CommonName ();
-				case "L":
-					return new X520.LocalityName ();
-				case "S":	// Microsoft
-				case "ST":	// RFC2253
-					return new X520.StateOrProvinceName ();
-				case "E":	// NOTE: Not part of RFC2253
-					return new X520.EmailAddress ();
-				case "DC":	// RFC2247
-					return new X520.DomainComponent ();
-				case "UID":	// RFC1274
-					return new X520.UserId ();
-				case "DNQUALIFIER":
-					return new X520.DnQualifier ();
-				case "T":
-					return new X520.Title ();
-				case "SN":
-					return new X520.Surname ();
-				case "G":
-					return new X520.GivenName ();
-				case "I":
-					return new X520.Initial ();
-				default:
-					if (s.StartsWith ("OID.")) {
-						// MUST support it but it OID may be without it
-						return new X520.Oid (s.Substring (4));
-					} else {
-						if (IsOid (s))
-							return new X520.Oid (s);
-						else
-							return null;
-					}
-			}
-		}
-
-		static private bool IsOid (string oid)
-		{
-			try {
-				ASN1 asn = ASN1Convert.FromOid (oid);
-				return (asn.Tag == 0x06);
-			}
-			catch {
-				return false;
-			}
-		}
-
-		// no quote processing
-		static private X520.AttributeTypeAndValue ReadAttribute (string value, ref int pos)
-		{
-			while ((value[pos] == ' ') && (pos < value.Length))
-				pos++;
-
-			// get '=' position in substring
-			int equal = value.IndexOf ('=', pos);
-			if (equal == -1) {
-				string msg =  ("No attribute found.");
-				throw new FormatException (msg);
-			}
-
-			string s = value.Substring (pos, equal - pos);
-			X520.AttributeTypeAndValue atv = GetAttributeFromOid (s);
-			if (atv == null) {
-				string msg =  ("Unknown attribute '{0}'.");
-				throw new FormatException (String.Format (msg, s));
-			}
-			pos = equal + 1; // skip the '='
-			return atv;
-		}
-
-		static private bool IsHex (char c)
-		{
-			if (Char.IsDigit (c))
-				return true;
-			char up = Char.ToUpper (c, CultureInfo.InvariantCulture);
-			return ((up >= 'A') && (up <= 'F'));
-		}
-
-		static string ReadHex (string value, ref int pos)
-		{
-			StringBuilder sb = new StringBuilder ();
-			// it is (at least an) 8 bits char
-			sb.Append (value[pos++]);
-			sb.Append (value[pos]);
-			// look ahead for a 16 bits char
-			if ((pos < value.Length - 4) && (value[pos+1] == '\\') && IsHex (value[pos+2])) {
-				pos += 2; // pass last char and skip \
-				sb.Append (value[pos++]);
-				sb.Append (value[pos]);
-			}
-			byte[] data = CryptoConvert.FromHex (sb.ToString ());
-			return Encoding.UTF8.GetString (data);
-		}
-
-		static private int ReadEscaped (StringBuilder sb, string value, int pos)
-		{
-			switch (value[pos]) {
-			case '\\':
-			case '"':
-			case '=':
-			case ';':
-			case '<':
-			case '>':
-			case '+':
-			case '#':
-			case ',':
-				sb.Append (value[pos]);
-				return pos;
-			default:
-				if (pos >= value.Length - 2) {
-					string msg = ("Malformed escaped value '{0}'.");
-					throw new FormatException (string.Format (msg, value.Substring (pos)));
-				}
-				// it's either a 8 bits or 16 bits char
-				sb.Append (ReadHex (value, ref pos));
-				return pos;
-			}
-		}
-
-		static private int ReadQuoted (StringBuilder sb, string value, int pos)
-		{
-			int original = pos;
-			while (pos <= value.Length) {
-				switch (value[pos]) {
-				case '"':
-					return pos;
-				case '\\':
-					return ReadEscaped (sb, value, pos);
-				default:
-					sb.Append (value[pos]);
-					pos++;
-					break;
-				}
-			}
-			string msg = ("Malformed quoted value '{0}'.");
-			throw new FormatException (string.Format (msg, value.Substring (original)));
-		}
-
-		static private string ReadValue (string value, ref int pos)
-		{
-			int original = pos;
-			StringBuilder sb = new StringBuilder ();
-			while (pos < value.Length) {
-				switch (value [pos]) {
-				case '\\':
-					pos = ReadEscaped (sb, value, ++pos);
-					break;
-				case '"':
-					pos = ReadQuoted (sb, value, ++pos);
-					break;
-				case '=':
-				case ';':
-				case '<':
-				case '>':
-					string msg =("Malformed value '{0}' contains '{1}' outside quotes.");
-					throw new FormatException (string.Format (msg, value.Substring (original), value[pos]));
-				case '+':
-				case '#':
-					throw new NotImplementedException ();
-				case ',':
-					pos++;
-					return sb.ToString ();
-				default:
-					sb.Append (value[pos]);
-					break;
-				}
-				pos++;
-			}
-			return sb.ToString ();
-		}
-
-		static public ASN1 FromString (string rdn) 
-		{
-			if (rdn == null)
-				throw new ArgumentNullException ("rdn");
-
-			int pos = 0;
-			ASN1 asn1 = new ASN1 (0x30);
-			while (pos < rdn.Length) {
-				X520.AttributeTypeAndValue atv = ReadAttribute (rdn, ref pos);
-				atv.Value = ReadValue (rdn, ref pos);
-
-				ASN1 sequence = new ASN1 (0x31);
-				sequence.Add (atv.GetASN1 ());
-				asn1.Add (sequence); 
-			}
-			return asn1;
-		}
-	}
-}

+ 0 - 153
Emby.Server.Implementations/Cryptography/X509Builder.cs

@@ -1,153 +0,0 @@
-//
-// X509Builder.cs: Abstract builder class for X509 objects
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@ximian.com>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com) 
-//
-
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Globalization;
-using System.Security.Cryptography;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    public abstract class X509Builder {
-
-		private const string defaultHash = "SHA1";
-		private string hashName;
-
-		protected X509Builder ()
-		{
-			hashName = defaultHash;
-		}
-
-		protected abstract ASN1 ToBeSigned (string hashName);
-
-		// move to PKCS1
-		protected string GetOid (string hashName) 
-		{
-			switch (hashName.ToLower (CultureInfo.InvariantCulture)) {
-				case "md2":
-					// md2withRSAEncryption (1 2 840 113549 1 1 2)
-					return "1.2.840.113549.1.1.2";
-				case "md4":
-					// md4withRSAEncryption (1 2 840 113549 1 1 3)
-					return "1.2.840.113549.1.1.3";
-				case "md5":
-					// md5withRSAEncryption (1 2 840 113549 1 1 4)
-					return "1.2.840.113549.1.1.4";
-				case "sha1":
-					// sha1withRSAEncryption (1 2 840 113549 1 1 5)
-					return "1.2.840.113549.1.1.5";
-				case "sha256":
-					// sha256WithRSAEncryption 	OBJECT IDENTIFIER ::= { pkcs-1 11 }
-					return "1.2.840.113549.1.1.11";
-				case "sha384":
-					// sha384WithRSAEncryption 	OBJECT IDENTIFIER ::= { pkcs-1 12 }
-					return "1.2.840.113549.1.1.12";
-				case "sha512":
-					// sha512WithRSAEncryption 	OBJECT IDENTIFIER ::= { pkcs-1 13 }
-					return "1.2.840.113549.1.1.13";
-				default:
-					throw new NotSupportedException ("Unknown hash algorithm " + hashName);
-			}
-		}
-
-		public string Hash {
-			get { return hashName; }
-			set { 
-				if (hashName == null)
-					hashName = defaultHash;
-				else
-					hashName = value;
-			}
-		}
-
-		public virtual byte[] Sign (AsymmetricAlgorithm aa) 
-		{
-			if (aa is RSA)
-				return Sign (aa as RSA);
-			else if (aa is DSA)
-				return Sign (aa as DSA);
-			else
-				throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString());
-		}
-
-		private byte[] Build (ASN1 tbs, string hashoid, byte[] signature) 
-		{
-			ASN1 builder = new ASN1 (0x30);
-			builder.Add (tbs);
-			builder.Add (PKCS7.AlgorithmIdentifier (hashoid));
-			// first byte of BITSTRING is the number of unused bits in the first byte
-			byte[] bitstring = new byte [signature.Length + 1];
-			Buffer.BlockCopy (signature, 0, bitstring, 1, signature.Length);
-			builder.Add (new ASN1 (0x03, bitstring));
-			return builder.GetBytes ();
-		}
-
-		public virtual byte[] Sign (RSA key)
-		{
-			string oid = GetOid (hashName);
-			ASN1 tbs = ToBeSigned (oid);
-			HashAlgorithm ha = HashAlgorithm.Create (hashName);
-			byte[] hash = ha.ComputeHash (tbs.GetBytes ());
-
-			RSAPKCS1SignatureFormatter pkcs1 = new RSAPKCS1SignatureFormatter (key);
-			pkcs1.SetHashAlgorithm (hashName);
-			byte[] signature = pkcs1.CreateSignature (hash);
-
-			return Build (tbs, oid, signature);
-		}
-
-		public virtual byte[] Sign (DSA key) 
-		{
-			string oid = "1.2.840.10040.4.3";
-			ASN1 tbs = ToBeSigned (oid);
-			HashAlgorithm ha = HashAlgorithm.Create (hashName);
-			if (!(ha is SHA1))
-				throw new NotSupportedException ("Only SHA-1 is supported for DSA");
-			byte[] hash = ha.ComputeHash (tbs.GetBytes ());
-
-			DSASignatureFormatter dsa = new DSASignatureFormatter (key);
-			dsa.SetHashAlgorithm (hashName);
-			byte[] rs = dsa.CreateSignature (hash);
-
-			// split R and S
-			byte[] r = new byte [20];
-			Buffer.BlockCopy (rs, 0, r, 0, 20);
-			byte[] s = new byte [20];
-			Buffer.BlockCopy (rs, 20, s, 0, 20);
-			ASN1 signature = new ASN1 (0x30);
-			signature.Add (new ASN1 (0x02, r));
-			signature.Add (new ASN1 (0x02, s));
-
-			// dsaWithSha1 (1 2 840 10040 4 3)
-			return Build (tbs, oid, signature.GetBytes ());
-		}
-	}
-}

+ 0 - 563
Emby.Server.Implementations/Cryptography/X509Certificate.cs

@@ -1,563 +0,0 @@
-//
-// X509Certificates.cs: Handles X.509 certificates.
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@xamarin.com>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2006 Novell, Inc (http://www.novell.com)
-// Copyright 2013 Xamarin Inc. (http://www.xamarin.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Runtime.Serialization;
-using System.Security.Cryptography;
-using System.Security.Permissions;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    // References:
-    // a.	Internet X.509 Public Key Infrastructure Certificate and CRL Profile
-    //	http://www.ietf.org/rfc/rfc3280.txt
-    // b.	ITU ASN.1 standards (free download)
-    //	http://www.itu.int/ITU-T/studygroups/com17/languages/
-
-    public class X509Certificate : ISerializable
-    {
-
-        private ASN1 decoder;
-
-		private byte[] m_encodedcert;
-		private DateTime m_from;
-		private DateTime m_until;
-		private ASN1 issuer;
-		private string m_issuername;
-		private string m_keyalgo;
-		private byte[] m_keyalgoparams;
-		private ASN1 subject;
-		private string m_subject;
-		private byte[] m_publickey;
-		private byte[] signature;
-		private string m_signaturealgo;
-		private byte[] m_signaturealgoparams;
-		private byte[] certhash;
-		private RSA _rsa;
-		private DSA _dsa;
-
-		// from http://msdn.microsoft.com/en-gb/library/ff635835.aspx
-		private const string OID_DSA = "1.2.840.10040.4.1";
-		private const string OID_RSA = "1.2.840.113549.1.1.1";
-		
-		// from http://www.ietf.org/rfc/rfc2459.txt
-		//
-		//Certificate  ::=  SEQUENCE  {
-		//     tbsCertificate       TBSCertificate,
-		//     signatureAlgorithm   AlgorithmIdentifier,
-		//     signature            BIT STRING  }
-		//
-		//TBSCertificate  ::=  SEQUENCE  {
-		//     version         [0]  Version DEFAULT v1,
-		//     serialNumber         CertificateSerialNumber,
-		//     signature            AlgorithmIdentifier,
-		//     issuer               Name,
-		//     validity             Validity,
-		//     subject              Name,
-		//     subjectPublicKeyInfo SubjectPublicKeyInfo,
-		//     issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
-		//                          -- If present, version shall be v2 or v3
-		//     subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
-		//                          -- If present, version shall be v2 or v3
-		//     extensions      [3]  Extensions OPTIONAL
-		//                          -- If present, version shall be v3 --  }
-		private int version;
-		private byte[] serialnumber;
-
-		private byte[] issuerUniqueID;
-		private byte[] subjectUniqueID;
-		private X509ExtensionCollection extensions;
-
-		private static string encoding_error = ("Input data cannot be coded as a valid certificate.");
-
-
-		// that's were the real job is!
-		private void Parse (byte[] data) 
-		{
-			try {
-				decoder = new ASN1 (data);
-				// Certificate 
-				if (decoder.Tag != 0x30)
-					throw new CryptographicException (encoding_error);
-				// Certificate / TBSCertificate
-				if (decoder [0].Tag != 0x30)
-					throw new CryptographicException (encoding_error);
-
-				ASN1 tbsCertificate = decoder [0];
-
-				int tbs = 0;
-				// Certificate / TBSCertificate / Version
-				ASN1 v = decoder [0][tbs];
-				version = 1;			// DEFAULT v1
-				if ((v.Tag == 0xA0) && (v.Count > 0)) {
-					// version (optional) is present only in v2+ certs
-					version += v [0].Value [0];	// zero based
-					tbs++;
-				}
-
-				// Certificate / TBSCertificate / CertificateSerialNumber
-				ASN1 sn = decoder [0][tbs++];
-				if (sn.Tag != 0x02) 
-					throw new CryptographicException (encoding_error);
-				serialnumber = sn.Value;
-				Array.Reverse (serialnumber, 0, serialnumber.Length);
-		
-				// Certificate / TBSCertificate / AlgorithmIdentifier
-				tbs++;
-				// ASN1 signatureAlgo = tbsCertificate.Element (tbs++, 0x30); 
-		
-				issuer = tbsCertificate.Element (tbs++, 0x30); 
-				m_issuername = X501.ToString (issuer);
-		
-				ASN1 validity = tbsCertificate.Element (tbs++, 0x30);
-				ASN1 notBefore = validity [0];
-				m_from = ASN1Convert.ToDateTime (notBefore);
-				ASN1 notAfter = validity [1];
-				m_until = ASN1Convert.ToDateTime (notAfter);
-		
-				subject = tbsCertificate.Element (tbs++, 0x30);
-				m_subject = X501.ToString (subject);
-		
-				ASN1 subjectPublicKeyInfo = tbsCertificate.Element (tbs++, 0x30);
-		
-				ASN1 algorithm = subjectPublicKeyInfo.Element (0, 0x30);
-				ASN1 algo = algorithm.Element (0, 0x06);
-				m_keyalgo = ASN1Convert.ToOid (algo);
-				// parameters ANY DEFINED BY algorithm OPTIONAL
-				// so we dont ask for a specific (Element) type and return DER
-				ASN1 parameters = algorithm [1];
-				m_keyalgoparams = ((algorithm.Count > 1) ? parameters.GetBytes () : null);
-		
-				ASN1 subjectPublicKey = subjectPublicKeyInfo.Element (1, 0x03); 
-				// we must drop th first byte (which is the number of unused bits
-				// in the BITSTRING)
-				int n = subjectPublicKey.Length - 1;
-				m_publickey = new byte [n];
-				Buffer.BlockCopy (subjectPublicKey.Value, 1, m_publickey, 0, n);
-
-				// signature processing
-				byte[] bitstring = decoder [2].Value;
-				// first byte contains unused bits in first byte
-				signature = new byte [bitstring.Length - 1];
-				Buffer.BlockCopy (bitstring, 1, signature, 0, signature.Length);
-
-				algorithm = decoder [1];
-				algo = algorithm.Element (0, 0x06);
-				m_signaturealgo = ASN1Convert.ToOid (algo);
-				parameters = algorithm [1];
-				if (parameters != null)
-					m_signaturealgoparams = parameters.GetBytes ();
-				else
-					m_signaturealgoparams = null;
-
-				// Certificate / TBSCertificate / issuerUniqueID
-				ASN1 issuerUID = tbsCertificate.Element (tbs, 0x81);
-				if (issuerUID != null) {
-					tbs++;
-					issuerUniqueID = issuerUID.Value;
-				}
-
-				// Certificate / TBSCertificate / subjectUniqueID
-				ASN1 subjectUID = tbsCertificate.Element (tbs, 0x82);
-				if (subjectUID != null) {
-					tbs++;
-					subjectUniqueID = subjectUID.Value;
-				}
-
-				// Certificate / TBSCertificate / Extensions
-				ASN1 extns = tbsCertificate.Element (tbs, 0xA3);
-				if ((extns != null) && (extns.Count == 1))
-					extensions = new X509ExtensionCollection (extns [0]);
-				else
-					extensions = new X509ExtensionCollection (null);
-
-				// keep a copy of the original data
-				m_encodedcert = (byte[]) data.Clone ();
-			}
-			catch (Exception ex) {
-				throw new CryptographicException (encoding_error, ex);
-			}
-		}
-
-		// constructors
-
-		public X509Certificate (byte[] data) 
-		{
-			if (data != null) {
-				// does it looks like PEM ?
-				if ((data.Length > 0) && (data [0] != 0x30)) {
-					try {
-						data = PEM ("CERTIFICATE", data);
-					}
-					catch (Exception ex) {
-						throw new CryptographicException (encoding_error, ex);
-					}
-				}
-				Parse (data);
-			}
-		}
-
-		private byte[] GetUnsignedBigInteger (byte[] integer) 
-		{
-			if (integer [0] == 0x00) {
-				// this first byte is added so we're sure it's an unsigned integer
-				// however we can't feed it into RSAParameters or DSAParameters
-				int length = integer.Length - 1;
-				byte[] uinteger = new byte [length];
-				Buffer.BlockCopy (integer, 1, uinteger, 0, length);
-				return uinteger;
-			}
-			else
-				return integer;
-		}
-
-		// public methods
-
-		public DSA DSA {
-			get {
-				if (m_keyalgoparams == null)
-					throw new CryptographicException ("Missing key algorithm parameters.");
-
-				if (_dsa == null && m_keyalgo == OID_DSA) {
-					DSAParameters dsaParams = new DSAParameters ();
-					// for DSA m_publickey contains 1 ASN.1 integer - Y
-					ASN1 pubkey = new ASN1 (m_publickey);
-					if ((pubkey == null) || (pubkey.Tag != 0x02))
-						return null;
-					dsaParams.Y = GetUnsignedBigInteger (pubkey.Value);
-
-					ASN1 param = new ASN1 (m_keyalgoparams);
-					if ((param == null) || (param.Tag != 0x30) || (param.Count < 3))
-						return null;
-					if ((param [0].Tag != 0x02) || (param [1].Tag != 0x02) || (param [2].Tag != 0x02))
-						return null;
-					dsaParams.P = GetUnsignedBigInteger (param [0].Value);
-					dsaParams.Q = GetUnsignedBigInteger (param [1].Value);
-					dsaParams.G = GetUnsignedBigInteger (param [2].Value);
-
-					// BUG: MS BCL 1.0 can't import a key which 
-					// isn't the same size as the one present in
-					// the container.
-					_dsa = (DSA) new DSACryptoServiceProvider (dsaParams.Y.Length << 3);
-					_dsa.ImportParameters (dsaParams);
-				}
-				return _dsa; 
-			}
-
-			set {
-				_dsa = value;
-				if (value != null)
-					_rsa = null;
-			}
-		}
-
-		public X509ExtensionCollection Extensions {
-			get { return extensions; }
-		}
-
-		public byte[] Hash {
-			get {
-				if (certhash == null) {
-					if ((decoder == null) || (decoder.Count < 1))
-						return null;
-					string algo = PKCS1.HashNameFromOid (m_signaturealgo, false);
-					if (algo == null)
-						return null;
-					byte[] toBeSigned = decoder [0].GetBytes ();
-					using (var hash = PKCS1.CreateFromName (algo))
-						certhash = hash.ComputeHash (toBeSigned, 0, toBeSigned.Length);
-				}
-				return (byte[]) certhash.Clone ();
-			}
-		}
-
-		public virtual string IssuerName {
-			get { return m_issuername; }
-		}
-
-		public virtual string KeyAlgorithm {
-			get { return m_keyalgo; }
-		}
-
-		public virtual byte[] KeyAlgorithmParameters {
-			get {
-				if (m_keyalgoparams == null)
-					return null;
-				return (byte[]) m_keyalgoparams.Clone (); 
-			}
-			set { m_keyalgoparams = value; }
-		}
-
-		public virtual byte[] PublicKey	{
-			get { 
-				if (m_publickey == null)
-					return null;
-				return (byte[]) m_publickey.Clone ();
-			}
-		}
-
-		public virtual RSA RSA {
-			get {
-				if (_rsa == null && m_keyalgo == OID_RSA) {
-					RSAParameters rsaParams = new RSAParameters ();
-					// for RSA m_publickey contains 2 ASN.1 integers
-					// the modulus and the public exponent
-					ASN1 pubkey = new ASN1 (m_publickey);
-					ASN1 modulus = pubkey [0];
-					if ((modulus == null) || (modulus.Tag != 0x02))
-						return null;
-					ASN1 exponent = pubkey [1];
-					if (exponent.Tag != 0x02)
-						return null;
-
-					rsaParams.Modulus = GetUnsignedBigInteger (modulus.Value);
-					rsaParams.Exponent = exponent.Value;
-
-					// BUG: MS BCL 1.0 can't import a key which 
-					// isn't the same size as the one present in
-					// the container.
-					int keySize = (rsaParams.Modulus.Length << 3);
-					_rsa = (RSA) new RSACryptoServiceProvider (keySize);
-					_rsa.ImportParameters (rsaParams);
-				}
-				return _rsa; 
-			}
-
-			set {
-				if (value != null)
-					_dsa = null;
-				_rsa = value;
-			}
-		}
-	        
-		public virtual byte[] RawData {
-			get {
-				if (m_encodedcert == null)
-					return null;
-				return (byte[]) m_encodedcert.Clone ();
-			}
-		}
-
-		public virtual byte[] SerialNumber {
-			get { 
-				if (serialnumber == null)
-					return null;
-				return (byte[]) serialnumber.Clone (); 
-			}
-		}
-
-		public virtual byte[] Signature {
-			get { 
-				if (signature == null)
-					return null;
-
-				switch (m_signaturealgo) {
-					case "1.2.840.113549.1.1.2":	// MD2 with RSA encryption 
-					case "1.2.840.113549.1.1.3":	// MD4 with RSA encryption 
-					case "1.2.840.113549.1.1.4":	// MD5 with RSA encryption 
-					case "1.2.840.113549.1.1.5":	// SHA-1 with RSA Encryption 
-					case "1.3.14.3.2.29":		// SHA1 with RSA signature
-					case "1.2.840.113549.1.1.11":	// SHA-256 with RSA Encryption
-					case "1.2.840.113549.1.1.12":	// SHA-384 with RSA Encryption
-					case "1.2.840.113549.1.1.13":	// SHA-512 with RSA Encryption
-					case "1.3.36.3.3.1.2":			// RIPEMD160 with RSA Encryption
-						return (byte[]) signature.Clone ();
-
-					case "1.2.840.10040.4.3":	// SHA-1 with DSA
-						ASN1 sign = new ASN1 (signature);
-						if ((sign == null) || (sign.Count != 2))
-							return null;
-						byte[] part1 = sign [0].Value;
-						byte[] part2 = sign [1].Value;
-						byte[] sig = new byte [40];
-						// parts may be less than 20 bytes (i.e. first bytes were 0x00)
-						// parts may be more than 20 bytes (i.e. first byte > 0x80, negative)
-						int s1 = System.Math.Max (0, part1.Length - 20);
-						int e1 = System.Math.Max (0, 20 - part1.Length);
-						Buffer.BlockCopy (part1, s1, sig, e1, part1.Length - s1);
-						int s2 = System.Math.Max (0, part2.Length - 20);
-						int e2 = System.Math.Max (20, 40 - part2.Length);
-						Buffer.BlockCopy (part2, s2, sig, e2, part2.Length - s2);
-						return sig;
-
-					default:
-						throw new CryptographicException ("Unsupported hash algorithm: " + m_signaturealgo);
-				}
-			}
-		}
-
-		public virtual string SignatureAlgorithm {
-			get { return m_signaturealgo; }
-		}
-
-		public virtual byte[] SignatureAlgorithmParameters {
-			get { 
-				if (m_signaturealgoparams == null)
-					return m_signaturealgoparams;
-				return (byte[]) m_signaturealgoparams.Clone ();
-			}
-		}
-
-		public virtual string SubjectName {
-			get { return m_subject; }
-		}
-
-		public virtual DateTime ValidFrom {
-			get { return m_from; }
-		}
-
-		public virtual DateTime ValidUntil {
-			get { return m_until; }
-		}
-
-		public int Version {
-			get { return version; }
-		}
-
-		public bool IsCurrent {
-			get { return WasCurrent (DateTime.UtcNow); }
-		}
-
-		public bool WasCurrent (DateTime instant) 
-		{
-			return ((instant > ValidFrom) && (instant <= ValidUntil));
-		}
-
-		// uncommon v2 "extension"
-		public byte[] IssuerUniqueIdentifier {
-			get {
-				if (issuerUniqueID == null)
-					return null;
-				return (byte[]) issuerUniqueID.Clone ();
-			}
-		}
-
-		// uncommon v2 "extension"
-		public byte[] SubjectUniqueIdentifier {
-			get {
-				if (subjectUniqueID == null)
-					return null;
-				return (byte[]) subjectUniqueID.Clone ();
-			}
-		}
-
-		internal bool VerifySignature (DSA dsa) 
-		{
-			// signatureOID is check by both this.Hash and this.Signature
-			DSASignatureDeformatter v = new DSASignatureDeformatter (dsa);
-			// only SHA-1 is supported
-			v.SetHashAlgorithm ("SHA1");
-			return v.VerifySignature (this.Hash, this.Signature);
-		}
-
-		internal bool VerifySignature (RSA rsa) 
-		{
-			// SHA1-1 with DSA
-			if (m_signaturealgo == "1.2.840.10040.4.3")
-				return false;
-			RSAPKCS1SignatureDeformatter v = new RSAPKCS1SignatureDeformatter (rsa);
-			v.SetHashAlgorithm (PKCS1.HashNameFromOid (m_signaturealgo));
-			return v.VerifySignature (this.Hash, this.Signature);
-		}
-
-		public bool VerifySignature (AsymmetricAlgorithm aa) 
-		{
-			if (aa == null)
-				throw new ArgumentNullException ("aa");
-
-			if (aa is RSA)
-				return VerifySignature (aa as RSA);
-			else if (aa is DSA)
-				return VerifySignature (aa as DSA);
-			else 
-				throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
-		}
-
-		public bool CheckSignature (byte[] hash, string hashAlgorithm, byte[] signature) 
-		{
-			RSACryptoServiceProvider r = (RSACryptoServiceProvider) RSA;
-			return r.VerifyHash (hash, hashAlgorithm, signature);
-		}
-
-		public bool IsSelfSigned {
-			get { 
-				if (m_issuername != m_subject)
-					return false;
-
-				try {
-					if (RSA != null)
-						return VerifySignature (RSA);
-					else if (DSA != null)
-						return VerifySignature (DSA);
-					else
-						return false; // e.g. a certificate with only DSA parameters
-				}
-				catch (CryptographicException) {
-					return false;
-				}
-			}
-		}
-
-		public ASN1 GetIssuerName ()
-		{
-			return issuer;
-		}
-
-		public ASN1 GetSubjectName ()
-		{
-			return subject;
-		}
-
-		protected X509Certificate (SerializationInfo info, StreamingContext context)
-		{
-			Parse ((byte[]) info.GetValue ("raw", typeof (byte[])));
-		}
-
-		[SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
-		public virtual void GetObjectData (SerializationInfo info, StreamingContext context)
-		{
-			info.AddValue ("raw", m_encodedcert);
-			// note: we NEVER serialize the private key
-		}
-
-		static byte[] PEM (string type, byte[] data) 
-		{
-			string pem = Encoding.ASCII.GetString (data);
-			string header = String.Format ("-----BEGIN {0}-----", type);
-			string footer = String.Format ("-----END {0}-----", type);
-			int start = pem.IndexOf (header) + header.Length;
-			int end = pem.IndexOf (footer, start);
-			string base64 = pem.Substring (start, (end - start));
-			return Convert.FromBase64String (base64);
-		}
-	}
-}

+ 0 - 245
Emby.Server.Implementations/Cryptography/X509CertificateBuilder.cs

@@ -1,245 +0,0 @@
-//
-// X509CertificateBuilder.cs: Handles building of X.509 certificates.
-//
-// Author:
-//	Sebastien Pouliot <sebastien@ximian.com>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com)
-//
-
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Security.Cryptography;
-
-namespace Emby.Server.Core.Cryptography
-{
-    // From RFC3280
-    /*
-	 * Certificate  ::=  SEQUENCE  {
-	 *      tbsCertificate       TBSCertificate,
-	 *      signatureAlgorithm   AlgorithmIdentifier,
-	 *      signature            BIT STRING  
-	 * }
-	 * TBSCertificate  ::=  SEQUENCE  {
-	 *      version         [0]  Version DEFAULT v1,
-	 *      serialNumber         CertificateSerialNumber,
-	 *      signature            AlgorithmIdentifier,
-	 *      issuer               Name,
-	 *      validity             Validity,
-	 *      subject              Name,
-	 *      subjectPublicKeyInfo SubjectPublicKeyInfo,
-	 *      issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL,
-	 *                           -- If present, version MUST be v2 or v3
-	 *      subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL,
-	 *                           -- If present, version MUST be v2 or v3
-	 *      extensions      [3]  Extensions OPTIONAL
-	 *                           -- If present, version MUST be v3 --  
-	 * }
-	 * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
-	 * CertificateSerialNumber  ::=  INTEGER
-	 * Validity ::= SEQUENCE {
-	 *      notBefore      Time,
-	 *      notAfter       Time  
-	 * }
-	 * Time ::= CHOICE {
-	 *      utcTime        UTCTime,
-	 *      generalTime    GeneralizedTime 
-	 * }
-	 */
-    public class X509CertificateBuilder : X509Builder {
- 
-		private byte version;
-		private byte[] sn;
-		private string issuer;
-		private DateTime notBefore;
-		private DateTime notAfter;
-		private string subject;
-		private AsymmetricAlgorithm aa;
-		private byte[] issuerUniqueID;
-		private byte[] subjectUniqueID;
-		private X509ExtensionCollection extensions;
-
-		public X509CertificateBuilder () : this (3) {}
-	
-		public X509CertificateBuilder (byte version) 
-		{
-			if (version > 3)
-				throw new ArgumentException ("Invalid certificate version");
-			this.version = version;
-			extensions = new X509ExtensionCollection ();
-		}
-
-		public byte Version {
-			get { return version; }
-			set { version = value; }
-		}
-
-		public byte[] SerialNumber {
-			get { return sn; }
-			set { sn = value; }
-		}
-
-		public string IssuerName {
-			get { return issuer; }
-			set { issuer = value; }
-		}
-
-		public DateTime NotBefore {
-			get { return notBefore; }
-			set { notBefore = value; }
-		}
-
-		public DateTime NotAfter {
-			get { return notAfter; }
-			set { notAfter = value; }
-		}
-
-		public string SubjectName {
-			get { return subject; }
-			set { subject = value; }
-		}
-
-		public AsymmetricAlgorithm SubjectPublicKey {
-			get { return aa; }
-			set { aa = value; }
-		}
-
-		public byte[] IssuerUniqueId {
-			get { return issuerUniqueID; }
-			set { issuerUniqueID = value; }
-		}
-
-		public byte[] SubjectUniqueId {
-			get { return subjectUniqueID; }
-			set { subjectUniqueID = value; }
-		}
-
-		public X509ExtensionCollection Extensions {
-			get { return extensions; }
-		}
-
-
-		/* SubjectPublicKeyInfo  ::=  SEQUENCE  {
-		 *      algorithm            AlgorithmIdentifier,
-		 *      subjectPublicKey     BIT STRING  }
-		 */
-		private ASN1 SubjectPublicKeyInfo () 
-		{
-			ASN1 keyInfo = new ASN1 (0x30);
-			if (aa is RSA) {
-				keyInfo.Add (PKCS7.AlgorithmIdentifier ("1.2.840.113549.1.1.1"));
-				RSAParameters p = (aa as RSA).ExportParameters (false);
-				/* RSAPublicKey ::= SEQUENCE {
-				 *       modulus            INTEGER,    -- n
-				 *       publicExponent     INTEGER  }  -- e
-				 */
-				ASN1 key = new ASN1 (0x30);
-				key.Add (ASN1Convert.FromUnsignedBigInteger (p.Modulus));
-				key.Add (ASN1Convert.FromUnsignedBigInteger (p.Exponent));
-				keyInfo.Add (new ASN1 (UniqueIdentifier (key.GetBytes ())));
-			}
-			else if (aa is DSA) {
-				DSAParameters p = (aa as DSA).ExportParameters (false);
-				/* Dss-Parms  ::=  SEQUENCE  {
-				 *       p             INTEGER,
-				 *       q             INTEGER,
-				 *       g             INTEGER  }
-				 */
-				ASN1 param = new ASN1 (0x30);
-				param.Add (ASN1Convert.FromUnsignedBigInteger (p.P));
-				param.Add (ASN1Convert.FromUnsignedBigInteger (p.Q));
-				param.Add (ASN1Convert.FromUnsignedBigInteger (p.G));
-				keyInfo.Add (PKCS7.AlgorithmIdentifier ("1.2.840.10040.4.1", param));
-				ASN1 key = keyInfo.Add (new ASN1 (0x03));
-				// DSAPublicKey ::= INTEGER  -- public key, y
-				key.Add (ASN1Convert.FromUnsignedBigInteger (p.Y));
-			}
-			else
-				throw new NotSupportedException ("Unknown Asymmetric Algorithm " + aa.ToString ());
-			return keyInfo;
-		}
-
-		private byte[] UniqueIdentifier (byte[] id) 
-		{
-			// UniqueIdentifier  ::=  BIT STRING
-			ASN1 uid = new ASN1 (0x03);
-			// first byte in a BITSTRING is the number of unused bits in the first byte
-			byte[] v = new byte [id.Length + 1];
-			Buffer.BlockCopy (id, 0, v, 1, id.Length);
-			uid.Value = v;
-			return uid.GetBytes ();
-		}
-
-		protected override ASN1 ToBeSigned (string oid) 
-		{
-			// TBSCertificate
-			ASN1 tbsCert = new ASN1 (0x30);
-
-			if (version > 1) {
-				// TBSCertificate / [0] Version DEFAULT v1,
-				byte[] ver = { (byte)(version - 1) };
-				ASN1 v = tbsCert.Add (new ASN1 (0xA0));
-				v.Add (new ASN1 (0x02, ver));
-			}
-
-			// TBSCertificate / CertificateSerialNumber,
-			tbsCert.Add (new ASN1 (0x02, sn));
-
-			// TBSCertificate / AlgorithmIdentifier,
-                        tbsCert.Add (PKCS7.AlgorithmIdentifier (oid));
-
-			// TBSCertificate / Name
-			tbsCert.Add (X501.FromString (issuer));
-
-			// TBSCertificate / Validity
-			ASN1 validity = tbsCert.Add (new ASN1 (0x30));
-			// TBSCertificate / Validity / Time
-			validity.Add (ASN1Convert.FromDateTime (notBefore));
-			// TBSCertificate / Validity / Time
-			validity.Add (ASN1Convert.FromDateTime (notAfter));
-
-			// TBSCertificate / Name
-			tbsCert.Add (X501.FromString (subject));
-
-			// TBSCertificate / SubjectPublicKeyInfo
-			tbsCert.Add (SubjectPublicKeyInfo ());
-                        
-			if (version > 1) {
-				// TBSCertificate / [1]  IMPLICIT UniqueIdentifier OPTIONAL
-				if (issuerUniqueID != null)
-					tbsCert.Add (new ASN1 (0xA1, UniqueIdentifier (issuerUniqueID)));
-
-				// TBSCertificate / [2]  IMPLICIT UniqueIdentifier OPTIONAL
-				if (subjectUniqueID != null)
-					tbsCert.Add (new ASN1 (0xA1, UniqueIdentifier (subjectUniqueID)));
-
-				// TBSCertificate / [3]  Extensions OPTIONAL
-				if ((version > 2) &&  (extensions.Count > 0))
-					tbsCert.Add (new ASN1 (0xA3, extensions.GetBytes ()));
-			}
-
-			return tbsCert;
-		}
-	}
-}

+ 0 - 201
Emby.Server.Implementations/Cryptography/X509CertificateCollection.cs

@@ -1,201 +0,0 @@
-//
-// Based on System.Security.Cryptography.X509Certificates.X509CertificateCollection
-//	in System assembly
-//
-// Authors:
-//	Lawrence Pit (loz@cable.a2000.nl)
-//	Sebastien Pouliot <sebastien@ximian.com>
-//
-
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    [Serializable]
-    public class X509CertificateCollection : CollectionBase, IEnumerable {
-		
-		public X509CertificateCollection () 
-		{
-		}
-		
-		public X509CertificateCollection (X509Certificate [] value) 
-		{
-			AddRange (value);
-		}
-		
-		public X509CertificateCollection (X509CertificateCollection value)
-		{
-			AddRange (value);
-		}
-		
-		// Properties
-		
-		public X509Certificate this [int index] {
-			get { return (X509Certificate) InnerList [index]; }
-			set { InnerList [index] = value; }
-		}
-		
-		// Methods
-
-		public int Add (X509Certificate value)
-		{
-			if (value == null)
-				throw new ArgumentNullException ("value");
-			
-			return InnerList.Add (value);
-		}
-		
-		public void AddRange (X509Certificate [] value) 
-		{
-			if (value == null)
-				throw new ArgumentNullException ("value");
-
-			for (int i = 0; i < value.Length; i++) 
-				InnerList.Add (value [i]);
-		}
-		
-		public void AddRange (X509CertificateCollection value)
-		{
-			if (value == null)
-				throw new ArgumentNullException ("value");
-
-			for (int i = 0; i < value.InnerList.Count; i++) 
-				InnerList.Add (value [i]);
-		}
-		
-		public bool Contains (X509Certificate value) 
-		{
-			return (IndexOf (value) != -1);
-		}
-
-		public void CopyTo (X509Certificate[] array, int index)
-		{
-			InnerList.CopyTo (array, index);
-		}
-		
-		public new X509CertificateEnumerator GetEnumerator ()
-		{
-			return new X509CertificateEnumerator (this);
-		}
-		
-		IEnumerator IEnumerable.GetEnumerator ()
-		{
-			return InnerList.GetEnumerator ();
-		}
-		
-		public override int GetHashCode () 
-		{
-			return InnerList.GetHashCode ();
-		}
-		
-		public int IndexOf (X509Certificate value)
-		{
-			if (value == null)
-				throw new ArgumentNullException ("value");
-
-			byte[] hash = value.Hash;
-			for (int i=0; i < InnerList.Count; i++) {
-				X509Certificate x509 = (X509Certificate) InnerList [i];
-				if (Compare (x509.Hash, hash))
-					return i;
-			}
-			return -1;
-		}
-		
-		public void Insert (int index, X509Certificate value)
-		{
-			InnerList.Insert (index, value);
-		}
-		
-		public void Remove (X509Certificate value)
-		{
-			InnerList.Remove (value);
-		}
-
-		// private stuff
-
-		private bool Compare (byte[] array1, byte[] array2) 
-		{
-			if ((array1 == null) && (array2 == null))
-				return true;
-			if ((array1 == null) || (array2 == null))
-				return false;
-			if (array1.Length != array2.Length)
-				return false;
-			for (int i=0; i < array1.Length; i++) {
-				if (array1 [i] != array2 [i])
-					return false;
-			}
-			return true;
-		}
-
-		// Inner Class
-		
-		public class X509CertificateEnumerator : IEnumerator {
-
-			private IEnumerator enumerator;
-
-			// Constructors
-			
-			public X509CertificateEnumerator (X509CertificateCollection mappings)
-			{
-				enumerator = ((IEnumerable) mappings).GetEnumerator ();
-			}
-
-			// Properties
-			
-			public X509Certificate Current {
-				get { return (X509Certificate) enumerator.Current; }
-			}
-			
-			object IEnumerator.Current {
-				get { return enumerator.Current; }
-			}
-
-			// Methods
-			
-			bool IEnumerator.MoveNext ()
-			{
-				return enumerator.MoveNext ();
-			}
-			
-			void IEnumerator.Reset () 
-			{
-				enumerator.Reset ();
-			}
-			
-			public bool MoveNext () 
-			{
-				return enumerator.MoveNext ();
-			}
-			
-			public void Reset ()
-			{
-				enumerator.Reset ();
-			}
-		}		
-	}
-}

+ 0 - 208
Emby.Server.Implementations/Cryptography/X509Extension.cs

@@ -1,208 +0,0 @@
-//
-// X509Extension.cs: Base class for all X.509 extensions.
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@ximian.com>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Globalization;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-    /*
-	 * Extension  ::=  SEQUENCE  {
-	 *	extnID      OBJECT IDENTIFIER,
-	 *	critical    BOOLEAN DEFAULT FALSE,
-	 *	extnValue   OCTET STRING  
-	 * }
-	 */
-    public class X509Extension {
-
-		protected string extnOid;
-		protected bool extnCritical;
-		protected ASN1 extnValue;
-
-		protected X509Extension () 
-		{
-			extnCritical = false;
-		}
-
-		public X509Extension (ASN1 asn1) 
-		{
-			if ((asn1.Tag != 0x30) || (asn1.Count < 2))
-				throw new ArgumentException (("Invalid X.509 extension."));
-			if (asn1[0].Tag != 0x06)
-				throw new ArgumentException (("Invalid X.509 extension."));
-
-			extnOid = ASN1Convert.ToOid (asn1[0]);
-			extnCritical = ((asn1[1].Tag == 0x01) && (asn1[1].Value[0] == 0xFF));
-			// last element is an octet string which may need to be decoded
-			extnValue = asn1 [asn1.Count - 1];
-			if ((extnValue.Tag == 0x04) && (extnValue.Length > 0) && (extnValue.Count == 0)) {
-				try {
-					ASN1 encapsulated = new ASN1 (extnValue.Value);
-					extnValue.Value = null;
-					extnValue.Add (encapsulated);
-				}
-				catch {
-					// data isn't ASN.1
-				}
-			}
-			Decode ();
-		}
-
-		public X509Extension (X509Extension extension)
-		{
-			if (extension == null)
-				throw new ArgumentNullException ("extension");
-			if ((extension.Value == null) || (extension.Value.Tag != 0x04) || (extension.Value.Count != 1))
-				throw new ArgumentException (("Invalid X.509 extension."));
-
-			extnOid = extension.Oid;
-			extnCritical = extension.Critical;
-			extnValue = extension.Value;
-			Decode ();
-		}
-
-		// encode the extension *into* an OCTET STRING
-		protected virtual void Decode () 
-		{
-		}
-
-		// decode the extension from *inside* an OCTET STRING
-		protected virtual void Encode ()
-		{
-		}
-
-		public ASN1 ASN1 {
-			get {
-				ASN1 extension = new ASN1 (0x30);
-				extension.Add (ASN1Convert.FromOid (extnOid));
-				if (extnCritical)
-					extension.Add (new ASN1 (0x01, new byte [1] { 0xFF }));
-				Encode ();
-				extension.Add (extnValue);
-				return extension;
-			}
-		}
-
-		public string Oid {
-			get { return extnOid; }
-		}
-
-		public bool Critical {
-			get { return extnCritical; }
-			set { extnCritical = value; }
-		}
-
-		// this gets overrided with more meaningful names
-		public virtual string Name {
-			get { return extnOid; }
-		}
-
-		public ASN1 Value {
-			get {
-				if (extnValue == null) {
-					Encode ();
-				}
-				return extnValue;
-			}
-		}
-
-		public override bool Equals (object obj) 
-		{
-			if (obj == null)
-				return false;
-			
-			X509Extension ex = (obj as X509Extension);
-			if (ex == null)
-				return false;
-
-			if (extnCritical != ex.extnCritical)
-				return false;
-			if (extnOid != ex.extnOid)
-				return false;
-			if (extnValue.Length != ex.extnValue.Length)
-				return false;
-			
-                        for (int i=0; i < extnValue.Length; i++) {
-				if (extnValue [i] != ex.extnValue [i])
-					return false;
-			}
-			return true;
-		}
-
-		public byte[] GetBytes () 
-		{
-			return ASN1.GetBytes ();
-		}
-
-		public override int GetHashCode () 
-		{
-			// OID should be unique in a collection of extensions
-			return extnOid.GetHashCode ();
-		}
-
-		private void WriteLine (StringBuilder sb, int n, int pos) 
-		{
-			byte[] value = extnValue.Value;
-			int p = pos;
-			for (int j=0; j < 8; j++) {
-				if (j < n) {
-					sb.Append (value [p++].ToString ("X2", CultureInfo.InvariantCulture));
-					sb.Append (" ");
-				}
-				else
-					sb.Append ("   ");
-			}
-			sb.Append ("  ");
-			p = pos;
-			for (int j=0; j < n; j++) {
-				byte b = value [p++];
-				if (b < 0x20)
-					sb.Append (".");
-				else
-					sb.Append (Convert.ToChar (b));
-			}
-			sb.Append (Environment.NewLine);
-		}
-
-		public override string ToString () 
-		{
-			StringBuilder sb = new StringBuilder ();
-			int div = (extnValue.Length >> 3);
-			int rem = (extnValue.Length - (div << 3));
-			int x = 0;
-			for (int i=0; i < div; i++) {
-				WriteLine (sb, 8, x);
-				x += 8;
-			}
-			WriteLine (sb, rem, x);
-			return sb.ToString ();
-		}
-	}
-}

+ 0 - 195
Emby.Server.Implementations/Cryptography/X509Extensions.cs

@@ -1,195 +0,0 @@
-//
-// X509Extensions.cs: Handles X.509 extensions.
-//
-// Author:
-//	Sebastien Pouliot  <sebastien@ximian.com>
-//
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com)
-//
-
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Collections;
-
-namespace Emby.Server.Core.Cryptography
-{
-    /*
-	 * Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension
-	 * 
-	 * Note: 1..MAX -> There shouldn't be 0 Extensions in the ASN1 structure
-	 */
-    public sealed class X509ExtensionCollection : CollectionBase, IEnumerable {
-
-		private bool readOnly;
-
-		public X509ExtensionCollection () : base ()
-		{
-		}
-
-		public X509ExtensionCollection (ASN1 asn1) : this ()
-		{
-			readOnly = true;
-			if (asn1 == null)
-				return;
-			if (asn1.Tag != 0x30)
-				throw new Exception ("Invalid extensions format");
-			for (int i=0; i < asn1.Count; i++) {
-				X509Extension extension = new X509Extension (asn1 [i]);
-				InnerList.Add (extension);
-			}
-		}
-
-		public int Add (X509Extension extension) 
-		{
-			if (extension == null)
-				throw new ArgumentNullException ("extension");
-			if (readOnly)
-				throw new NotSupportedException ("Extensions are read only");
-		
-			return InnerList.Add (extension);
-		}
-
-		public void AddRange (X509Extension[] extension) 
-		{
-			if (extension == null)
-				throw new ArgumentNullException ("extension");
-			if (readOnly)
-				throw new NotSupportedException ("Extensions are read only");
-
-			for (int i = 0; i < extension.Length; i++) 
-				InnerList.Add (extension [i]);
-		}
-	
-		public void AddRange (X509ExtensionCollection collection) 
-		{
-			if (collection == null)
-				throw new ArgumentNullException ("collection");
-			if (readOnly)
-				throw new NotSupportedException ("Extensions are read only");
-
-			for (int i = 0; i < collection.InnerList.Count; i++) 
-				InnerList.Add (collection [i]);
-		}
-
-		public bool Contains (X509Extension extension) 
-		{
-			return (IndexOf (extension) != -1);
-		}
-
-		public bool Contains (string oid) 
-		{
-			return (IndexOf (oid) != -1);
-		}
-
-		public void CopyTo (X509Extension[] extensions, int index) 
-		{
-			if (extensions == null)
-				throw new ArgumentNullException ("extensions");
-
-			InnerList.CopyTo (extensions, index);
-		}
-
-		public int IndexOf (X509Extension extension) 
-		{
-			if (extension == null)
-				throw new ArgumentNullException ("extension");
-
-			for (int i=0; i < InnerList.Count; i++) {
-				X509Extension ex = (X509Extension) InnerList [i];
-				if (ex.Equals (extension))
-					return i;
-			}
-			return -1;
-		}
-
-		public int IndexOf (string oid) 
-		{
-			if (oid == null)
-				throw new ArgumentNullException ("oid");
-
-			for (int i=0; i < InnerList.Count; i++) {
-				X509Extension ex = (X509Extension) InnerList [i];
-				if (ex.Oid == oid)
-					return i;
-			}
-			return -1;
-		}
-
-		public void Insert (int index, X509Extension extension) 
-		{
-			if (extension == null)
-				throw new ArgumentNullException ("extension");
-
-			InnerList.Insert (index, extension);
-		}
-
-		public void Remove (X509Extension extension) 
-		{
-			if (extension == null)
-				throw new ArgumentNullException ("extension");
-
-			InnerList.Remove (extension);
-		}
-
-		public void Remove (string oid) 
-		{
-			if (oid == null)
-				throw new ArgumentNullException ("oid");
-
-			int index = IndexOf (oid);
-			if (index != -1)
-				InnerList.RemoveAt (index);
-		}
-
-		IEnumerator IEnumerable.GetEnumerator () 
-		{
-			return InnerList.GetEnumerator ();
-		}
-
-		public X509Extension this [int index] {
-			get { return (X509Extension) InnerList [index]; }
-		}
-
-		public X509Extension this [string oid] {
-			get {
-				int index = IndexOf (oid);
-				if (index == -1)
-					return null;
-				return (X509Extension) InnerList [index];
-			}
-		}
-
-		public byte[] GetBytes () 
-		{
-			if (InnerList.Count < 1)
-				return null;
-			ASN1 sequence = new ASN1 (0x30);
-			for (int i=0; i < InnerList.Count; i++) {
-				X509Extension x = (X509Extension) InnerList [i];
-				sequence.Add (x.ASN1);
-			}
-			return sequence.GetBytes ();
-		}
-	}
-}

+ 0 - 346
Emby.Server.Implementations/Cryptography/X520Attributes.cs

@@ -1,346 +0,0 @@
-//
-// X520.cs: X.520 related stuff (attributes, RDN)
-//
-// Author:
-//	Sebastien Pouliot <sebastien@ximian.com>
-//
-// (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-
-using System;
-using System.Text;
-
-namespace Emby.Server.Core.Cryptography
-{
-
-    // References:
-    // 1.	Information technology - Open Systems Interconnection - The Directory: Selected attribute types 
-    //	http://www.itu.int/rec/recommendation.asp?type=folders&lang=e&parent=T-REC-X.520 
-    // 2.	Internet X.509 Public Key Infrastructure Certificate and CRL Profile
-    //	http://www.ietf.org/rfc/rfc3280.txt
-    // 3.	A Summary of the X.500(96) User Schema for use with LDAPv3
-    //	http://www.faqs.org/rfcs/rfc2256.html
-    // 4.	RFC 2247 - Using Domains in LDAP/X.500 Distinguished Names
-    //	http://www.faqs.org/rfcs/rfc2247.html
-
-    /* 
-	 * AttributeTypeAndValue ::= SEQUENCE {
-	 * 	type     AttributeType,
-	 * 	value    AttributeValue 
-	 * }
-	 * 
-	 * AttributeType ::= OBJECT IDENTIFIER
-	 * 
-	 * AttributeValue ::= ANY DEFINED BY AttributeType
-	 */
-    public class X520 {
-
-		public abstract class AttributeTypeAndValue {
-			private string oid;
-			private string attrValue;
-			private int upperBound;
-			private byte encoding;
-
-			protected AttributeTypeAndValue (string oid, int upperBound)
-			{
-				this.oid = oid;
-				this.upperBound = upperBound;
-				this.encoding = 0xFF;
-			}
-
-			protected AttributeTypeAndValue (string oid, int upperBound, byte encoding) 
-			{
-				this.oid = oid;
-				this.upperBound = upperBound;
-				this.encoding = encoding;
-			}
-
-			public string Value {
-				get { return attrValue; }
-				set { 
-					if ((attrValue != null) && (attrValue.Length > upperBound)) {
-						string msg = ("Value length bigger than upperbound ({0}).");
-						throw new FormatException (String.Format (msg, upperBound));
-					}
-					attrValue = value; 
-				}
-			}
-
-			public ASN1 ASN1 {
-				get { return GetASN1 (); }
-			}
-
-			internal ASN1 GetASN1 (byte encoding) 
-			{
-				byte encode = encoding;
-				if (encode == 0xFF)
-					encode = SelectBestEncoding ();
-					
-				ASN1 asn1 = new ASN1 (0x30);
-				asn1.Add (ASN1Convert.FromOid (oid));
-				switch (encode) {
-					case 0x13:
-						// PRINTABLESTRING
-						asn1.Add (new ASN1 (0x13, Encoding.ASCII.GetBytes (attrValue)));
-						break;
-					case 0x16:
-						// IA5STRING
-						asn1.Add (new ASN1 (0x16, Encoding.ASCII.GetBytes (attrValue)));
-						break;
-					case 0x1E:
-						// BMPSTRING
-						asn1.Add (new ASN1 (0x1E, Encoding.BigEndianUnicode.GetBytes (attrValue)));
-						break;
-				}
-				return asn1;
-			}
-
-			internal ASN1 GetASN1 () 
-			{
-				return GetASN1 (encoding);
-			}
-
-			public byte[] GetBytes (byte encoding) 
-			{
-				return GetASN1 (encoding) .GetBytes ();
-			}
-
-			public byte[] GetBytes () 
-			{
-				return GetASN1 () .GetBytes ();
-			}
-
-			private byte SelectBestEncoding ()
-			{
-				foreach (char c in attrValue) {
-					switch (c) {
-					case '@':
-					case '_':
-						return 0x1E; // BMPSTRING
-					default:
-						if (c > 127)
-							return 0x1E; // BMPSTRING
-						break;
-					}
-				}
-				return 0x13; // PRINTABLESTRING
-			}
-		}
-
-		public class Name : AttributeTypeAndValue {
-
-			public Name () : base ("2.5.4.41", 32768) 
-			{
-			}
-		}
-
-		public class CommonName : AttributeTypeAndValue {
-
-			public CommonName () : base ("2.5.4.3", 64) 
-			{
-			}
-		}
-
-		// RFC2256, Section 5.6
-		public class SerialNumber : AttributeTypeAndValue {
-
-			// max length 64 bytes, Printable String only
-			public SerialNumber ()
-				: base ("2.5.4.5", 64, 0x13)
-			{
-			}
-		}
-
-		public class LocalityName : AttributeTypeAndValue {
-
-			public LocalityName () : base ("2.5.4.7", 128)
-			{
-			}
-		}
-
-		public class StateOrProvinceName : AttributeTypeAndValue {
-
-			public StateOrProvinceName () : base ("2.5.4.8", 128) 
-			{
-			}
-		}
-		 
-		public class OrganizationName : AttributeTypeAndValue {
-
-			public OrganizationName () : base ("2.5.4.10", 64)
-			{
-			}
-		}
-		 
-		public class OrganizationalUnitName : AttributeTypeAndValue {
-
-			public OrganizationalUnitName () : base ("2.5.4.11", 64)
-			{
-			}
-		}
-
-		// NOTE: Not part of RFC2253
-		public class EmailAddress : AttributeTypeAndValue {
-
-			public EmailAddress () : base ("1.2.840.113549.1.9.1", 128, 0x16)
-			{
-			}
-		}
-
-		// RFC2247, Section 4
-		public class DomainComponent : AttributeTypeAndValue {
-
-			// no maximum length defined
-			public DomainComponent ()
-				: base ("0.9.2342.19200300.100.1.25", Int32.MaxValue, 0x16)
-			{
-			}
-		}
-
-		// RFC1274, Section 9.3.1
-		public class UserId : AttributeTypeAndValue {
-
-			public UserId ()
-				: base ("0.9.2342.19200300.100.1.1", 256)
-			{
-			}
-		}
-
-		public class Oid : AttributeTypeAndValue {
-
-			public Oid (string oid)
-				: base (oid, Int32.MaxValue)
-			{
-			}
-		}
-
-		/* -- Naming attributes of type X520Title
-		 * id-at-title             AttributeType ::= { id-at 12 }
-		 * 
-		 * X520Title ::= CHOICE {
-		 *       teletexString     TeletexString   (SIZE (1..ub-title)),
-		 *       printableString   PrintableString (SIZE (1..ub-title)),
-		 *       universalString   UniversalString (SIZE (1..ub-title)),
-		 *       utf8String        UTF8String      (SIZE (1..ub-title)),
-		 *       bmpString         BMPString       (SIZE (1..ub-title)) 
-		 * }
-		 */
-		public class Title : AttributeTypeAndValue {
-
-			public Title () : base ("2.5.4.12", 64)
-			{
-			}
-		}
-
-		public class CountryName : AttributeTypeAndValue {
-
-			// (0x13) PRINTABLESTRING
-			public CountryName () : base ("2.5.4.6", 2, 0x13) 
-			{
-			}
-		}
-
-		public class DnQualifier : AttributeTypeAndValue {
-
-			// (0x13) PRINTABLESTRING
-			public DnQualifier () : base ("2.5.4.46", 2, 0x13) 
-			{
-			}
-		}
-
-		public class Surname : AttributeTypeAndValue {
-
-			public Surname () : base ("2.5.4.4", 32768) 
-			{
-			}
-		}
-
-		public class GivenName : AttributeTypeAndValue {
-
-			public GivenName () : base ("2.5.4.42", 16) 
-			{
-			}
-		}
-
-		public class Initial : AttributeTypeAndValue {
-
-			public Initial () : base ("2.5.4.43", 5) 
-			{
-			}
-		}
-
-	}
-        
-	/* From RFC3280
-	 * --  specifications of Upper Bounds MUST be regarded as mandatory
-	 * --  from Annex B of ITU-T X.411 Reference Definition of MTS Parameter
-	 * 
-	 * --  Upper Bounds
-	 * 
-	 * ub-name INTEGER ::= 32768
-	 * ub-common-name INTEGER ::= 64
-	 * ub-locality-name INTEGER ::= 128
-	 * ub-state-name INTEGER ::= 128
-	 * ub-organization-name INTEGER ::= 64
-	 * ub-organizational-unit-name INTEGER ::= 64
-	 * ub-title INTEGER ::= 64
-	 * ub-serial-number INTEGER ::= 64
-	 * ub-match INTEGER ::= 128
-	 * ub-emailaddress-length INTEGER ::= 128
-	 * ub-common-name-length INTEGER ::= 64
-	 * ub-country-name-alpha-length INTEGER ::= 2
-	 * ub-country-name-numeric-length INTEGER ::= 3
-	 * ub-domain-defined-attributes INTEGER ::= 4
-	 * ub-domain-defined-attribute-type-length INTEGER ::= 8
-	 * ub-domain-defined-attribute-value-length INTEGER ::= 128
-	 * ub-domain-name-length INTEGER ::= 16
-	 * ub-extension-attributes INTEGER ::= 256
-	 * ub-e163-4-number-length INTEGER ::= 15
-	 * ub-e163-4-sub-address-length INTEGER ::= 40
-	 * ub-generation-qualifier-length INTEGER ::= 3
-	 * ub-given-name-length INTEGER ::= 16
-	 * ub-initials-length INTEGER ::= 5
-	 * ub-integer-options INTEGER ::= 256
-	 * ub-numeric-user-id-length INTEGER ::= 32
-	 * ub-organization-name-length INTEGER ::= 64
-	 * ub-organizational-unit-name-length INTEGER ::= 32
-	 * ub-organizational-units INTEGER ::= 4
-	 * ub-pds-name-length INTEGER ::= 16
-	 * ub-pds-parameter-length INTEGER ::= 30
-	 * ub-pds-physical-address-lines INTEGER ::= 6
-	 * ub-postal-code-length INTEGER ::= 16
-	 * ub-pseudonym INTEGER ::= 128
-	 * ub-surname-length INTEGER ::= 40
-	 * ub-terminal-id-length INTEGER ::= 24
-	 * ub-unformatted-address-length INTEGER ::= 180
-	 * ub-x121-address-length INTEGER ::= 16
-	 * 
-	 * -- Note - upper bounds on string types, such as TeletexString, are
-	 * -- measured in characters.  Excepting PrintableString or IA5String, a
-	 * -- significantly greater number of octets will be required to hold
-	 * -- such a value.  As a minimum, 16 octets, or twice the specified
-	 * -- upper bound, whichever is the larger, should be allowed for
-	 * -- TeletexString.  For UTF8String or UniversalString at least four
-	 * -- times the upper bound should be allowed.
-	 */
-}

+ 55 - 38
Emby.Server.Implementations/Data/BaseSqliteRepository.cs

@@ -108,37 +108,49 @@ namespace Emby.Server.Implementations.Data
 
 
                 var db = SQLite3.Open(DbFilePath, connectionFlags, null);
                 var db = SQLite3.Open(DbFilePath, connectionFlags, null);
 
 
-                if (string.IsNullOrWhiteSpace(_defaultWal))
+                try
                 {
                 {
-                    _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
+                    if (string.IsNullOrWhiteSpace(_defaultWal))
+                    {
+                        _defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
 
 
-                    Logger.Info("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
-                }
+                        Logger.Info("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
+                    }
 
 
-                var queries = new List<string>
-                {
-                    //"PRAGMA cache size=-10000"
-                    //"PRAGMA read_uncommitted = true",
-                    "PRAGMA synchronous=Normal"
-                };
+                    var queries = new List<string>
+                    {
+                        //"PRAGMA cache size=-10000"
+                        //"PRAGMA read_uncommitted = true",
+                        "PRAGMA synchronous=Normal"
+                    };
 
 
-                if (CacheSize.HasValue)
-                {
-                    queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture));
-                }
+                    if (CacheSize.HasValue)
+                    {
+                        queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture));
+                    }
 
 
-                if (EnableTempStoreMemory)
-                {
-                    queries.Add("PRAGMA temp_store = memory");
+                    if (EnableTempStoreMemory)
+                    {
+                        queries.Add("PRAGMA temp_store = memory");
+                    }
+                    else
+                    {
+                        queries.Add("PRAGMA temp_store = file");
+                    }
+
+                    foreach (var query in queries)
+                    {
+                        db.Execute(query);
+                    }
                 }
                 }
-                else
+                catch
                 {
                 {
-                    queries.Add("PRAGMA temp_store = file");
-                }
+                    using (db)
+                    {
 
 
-                foreach (var query in queries)
-                {
-                    db.Execute(query);
+                    }
+
+                    throw;
                 }
                 }
 
 
                 _connection = new ManagedConnection(db, false);
                 _connection = new ManagedConnection(db, false);
@@ -265,29 +277,34 @@ namespace Emby.Server.Implementations.Data
         {
         {
             if (dispose)
             if (dispose)
             {
             {
-                try
+                DisposeConnection();
+            }
+        }
+
+        private void DisposeConnection()
+        {
+            try
+            {
+                lock (_disposeLock)
                 {
                 {
-                    lock (_disposeLock)
+                    using (WriteLock.Write())
                     {
                     {
-                        using (WriteLock.Write())
+                        if (_connection != null)
                         {
                         {
-                            if (_connection != null)
+                            using (_connection)
                             {
                             {
-                                using (_connection)
-                                {
-                                    
-                                }
-                                _connection = null;
+                                _connection.Close();
                             }
                             }
-
-                            CloseConnection();
+                            _connection = null;
                         }
                         }
+
+                        CloseConnection();
                     }
                     }
                 }
                 }
-                catch (Exception ex)
-                {
-                    Logger.ErrorException("Error disposing database", ex);
-                }
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error disposing database", ex);
             }
             }
         }
         }
 
 

+ 20 - 2
Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs

@@ -19,12 +19,14 @@ namespace Emby.Server.Implementations.Data
     public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
     public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
     {
     {
         private readonly IMemoryStreamFactory _memoryStreamProvider;
         private readonly IMemoryStreamFactory _memoryStreamProvider;
+        protected IFileSystem FileSystem { get; private set; }
 
 
-        public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider)
+        public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider, IFileSystem fileSystem)
             : base(logger)
             : base(logger)
         {
         {
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
             _memoryStreamProvider = memoryStreamProvider;
             _memoryStreamProvider = memoryStreamProvider;
+            FileSystem = fileSystem;
             DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
             DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
         }
         }
 
 
@@ -45,11 +47,27 @@ namespace Emby.Server.Implementations.Data
         /// </summary>
         /// </summary>
         private readonly IJsonSerializer _jsonSerializer;
         private readonly IJsonSerializer _jsonSerializer;
 
 
+        public void Initialize()
+        {
+            try
+            {
+                InitializeInternal();
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
+
+                FileSystem.DeleteFile(DbFilePath);
+
+                InitializeInternal();
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Opens the connection to the database
         /// Opens the connection to the database
         /// </summary>
         /// </summary>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        public void Initialize()
+        private void InitializeInternal()
         {
         {
             using (var connection = CreateConnection())
             using (var connection = CreateConnection())
             {
             {

+ 4 - 4
Emby.Server.Implementations/Data/SqliteItemRepository.cs

@@ -120,13 +120,13 @@ namespace Emby.Server.Implementations.Data
 
 
         protected override void CloseConnection()
         protected override void CloseConnection()
         {
         {
-            base.CloseConnection();
-
             if (_shrinkMemoryTimer != null)
             if (_shrinkMemoryTimer != null)
             {
             {
                 _shrinkMemoryTimer.Dispose();
                 _shrinkMemoryTimer.Dispose();
                 _shrinkMemoryTimer = null;
                 _shrinkMemoryTimer = null;
             }
             }
+
+            base.CloseConnection();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -3750,7 +3750,7 @@ namespace Emby.Server.Implementations.Data
 
 
             if (query.MinDateLastSaved.HasValue)
             if (query.MinDateLastSaved.HasValue)
             {
             {
-                whereClauses.Add("DateLastSaved>=@MinDateLastSaved");
+                whereClauses.Add("(DateLastSaved not null and DateLastSaved>=@MinDateLastSavedForUser)");
                 if (statement != null)
                 if (statement != null)
                 {
                 {
                     statement.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value);
                     statement.TryBind("@MinDateLastSaved", query.MinDateLastSaved.Value);
@@ -3759,7 +3759,7 @@ namespace Emby.Server.Implementations.Data
 
 
             if (query.MinDateLastSavedForUser.HasValue)
             if (query.MinDateLastSavedForUser.HasValue)
             {
             {
-                whereClauses.Add("DateLastSaved>=@MinDateLastSavedForUser");
+                whereClauses.Add("(DateLastSaved not null and DateLastSaved>=@MinDateLastSavedForUser)");
                 if (statement != null)
                 if (statement != null)
                 {
                 {
                     statement.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value);
                     statement.TryBind("@MinDateLastSavedForUser", query.MinDateLastSavedForUser.Value);

+ 4 - 17
Emby.Server.Implementations/Dto/DtoService.cs

@@ -571,7 +571,7 @@ namespace Emby.Server.Implementations.Dto
                 }
                 }
             }
             }
 
 
-            if (!(item is LiveTvProgram) || fields.Contains(ItemFields.PlayAccess))
+            if (/*!(item is LiveTvProgram) ||*/ fields.Contains(ItemFields.PlayAccess))
             {
             {
                 dto.PlayAccess = item.GetPlayAccess(user);
                 dto.PlayAccess = item.GetPlayAccess(user);
             }
             }
@@ -1642,6 +1642,9 @@ namespace Emby.Server.Implementations.Dto
                     return null;
                     return null;
                 }
                 }
 
 
+                return null;
+                _logger.Info("Getting image size for item type {0}", item.GetType().Name);
+
                 try
                 try
                 {
                 {
                     size = _imageProcessor.GetImageSize(imageInfo);
                     size = _imageProcessor.GetImageSize(imageInfo);
@@ -1673,22 +1676,6 @@ namespace Emby.Server.Implementations.Dto
                 return null;
                 return null;
             }
             }
 
 
-            var photo = item as Photo;
-            if (photo != null && photo.Orientation.HasValue)
-            {
-                switch (photo.Orientation.Value)
-                {
-                    case ImageOrientation.LeftBottom:
-                    case ImageOrientation.LeftTop:
-                    case ImageOrientation.RightBottom:
-                    case ImageOrientation.RightTop:
-                        var temp = height;
-                        height = width;
-                        width = temp;
-                        break;
-                }
-            }
-
             return width / height;
             return width / height;
         }
         }
     }
     }

+ 3 - 22
Emby.Server.Implementations/Emby.Server.Implementations.csproj

@@ -56,25 +56,7 @@
     <Compile Include="Collections\CollectionManager.cs" />
     <Compile Include="Collections\CollectionManager.cs" />
     <Compile Include="Collections\CollectionsDynamicFolder.cs" />
     <Compile Include="Collections\CollectionsDynamicFolder.cs" />
     <Compile Include="Configuration\ServerConfigurationManager.cs" />
     <Compile Include="Configuration\ServerConfigurationManager.cs" />
-    <Compile Include="Cryptography\ASN1.cs" />
-    <Compile Include="Cryptography\ASN1Convert.cs" />
-    <Compile Include="Cryptography\BitConverterLE.cs" />
-    <Compile Include="Cryptography\CertificateGenerator.cs" />
-    <Compile Include="Cryptography\CryptoConvert.cs" />
     <Compile Include="Cryptography\CryptographyProvider.cs" />
     <Compile Include="Cryptography\CryptographyProvider.cs" />
-    <Compile Include="Cryptography\PfxGenerator.cs" />
-    <Compile Include="Cryptography\PKCS1.cs" />
-    <Compile Include="Cryptography\PKCS12.cs" />
-    <Compile Include="Cryptography\PKCS7.cs" />
-    <Compile Include="Cryptography\PKCS8.cs" />
-    <Compile Include="Cryptography\X501Name.cs" />
-    <Compile Include="Cryptography\X509Builder.cs" />
-    <Compile Include="Cryptography\X509Certificate.cs" />
-    <Compile Include="Cryptography\X509CertificateBuilder.cs" />
-    <Compile Include="Cryptography\X509CertificateCollection.cs" />
-    <Compile Include="Cryptography\X509Extension.cs" />
-    <Compile Include="Cryptography\X509Extensions.cs" />
-    <Compile Include="Cryptography\X520Attributes.cs" />
     <Compile Include="Data\ManagedConnection.cs" />
     <Compile Include="Data\ManagedConnection.cs" />
     <Compile Include="Data\SqliteDisplayPreferencesRepository.cs" />
     <Compile Include="Data\SqliteDisplayPreferencesRepository.cs" />
     <Compile Include="Data\SqliteItemRepository.cs" />
     <Compile Include="Data\SqliteItemRepository.cs" />
@@ -425,10 +407,9 @@
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHttpStream.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHttpStream.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunUdpStream.cs" />
     <Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunUdpStream.cs" />
+    <Compile Include="LiveTv\TunerHosts\LiveStream.cs" />
     <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
     <Compile Include="LiveTv\TunerHosts\M3uParser.cs" />
     <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
     <Compile Include="LiveTv\TunerHosts\M3UTunerHost.cs" />
-    <Compile Include="LiveTv\TunerHosts\MulticastStream.cs" />
-    <Compile Include="LiveTv\TunerHosts\QueueStream.cs" />
     <Compile Include="Localization\LocalizationManager.cs" />
     <Compile Include="Localization\LocalizationManager.cs" />
     <Compile Include="Localization\TextLocalizer.cs" />
     <Compile Include="Localization\TextLocalizer.cs" />
     <Compile Include="Logging\ConsoleLogger.cs" />
     <Compile Include="Logging\ConsoleLogger.cs" />
@@ -667,8 +648,8 @@
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
-    <Reference Include="SharpCompress, Version=0.14.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
+    <Reference Include="SharpCompress, Version=0.18.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
+      <HintPath>..\packages\SharpCompress.0.18.2\lib\net45\SharpCompress.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
     <Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
       <HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>
       <HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>

+ 1 - 1
Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs

@@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.EntryPoints
             values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
             values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
             values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
             values.Add(_appHost.HttpPort.ToString(CultureInfo.InvariantCulture));
             values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
             values.Add(_appHost.HttpsPort.ToString(CultureInfo.InvariantCulture));
-            values.Add(config.EnableHttps.ToString());
+            values.Add((config.EnableHttps || config.RequireHttps).ToString());
             values.Add(_appHost.EnableHttps.ToString());
             values.Add(_appHost.EnableHttps.ToString());
 
 
             return string.Join("|", values.ToArray(values.Count));
             return string.Join("|", values.ToArray(values.Count));

+ 2 - 2
Emby.Server.Implementations/EntryPoints/UsageEntryPoint.cs

@@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.EntryPoints
 
 
             try
             try
             {
             {
-                await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger)
+                await new UsageReporter(_applicationHost, _httpClient, _logger)
                     .ReportAppUsage(client, CancellationToken.None)
                     .ReportAppUsage(client, CancellationToken.None)
                     .ConfigureAwait(false);
                     .ConfigureAwait(false);
             }
             }
@@ -117,7 +117,7 @@ namespace Emby.Server.Implementations.EntryPoints
 
 
             try
             try
             {
             {
-                await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger)
+                await new UsageReporter(_applicationHost, _httpClient, _logger)
                     .ReportServerUsage(CancellationToken.None)
                     .ReportServerUsage(CancellationToken.None)
                     .ConfigureAwait(false);
                     .ConfigureAwait(false);
             }
             }

+ 1 - 9
Emby.Server.Implementations/EntryPoints/UsageReporter.cs

@@ -17,15 +17,13 @@ namespace Emby.Server.Implementations.EntryPoints
     {
     {
         private readonly IServerApplicationHost _applicationHost;
         private readonly IServerApplicationHost _applicationHost;
         private readonly IHttpClient _httpClient;
         private readonly IHttpClient _httpClient;
-        private readonly IUserManager _userManager;
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
         private const string MbAdminUrl = "https://www.mb3admin.com/admin/";
 
 
-        public UsageReporter(IServerApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger)
+        public UsageReporter(IServerApplicationHost applicationHost, IHttpClient httpClient, ILogger logger)
         {
         {
             _applicationHost = applicationHost;
             _applicationHost = applicationHost;
             _httpClient = httpClient;
             _httpClient = httpClient;
-            _userManager = userManager;
             _logger = logger;
             _logger = logger;
         }
         }
 
 
@@ -43,12 +41,6 @@ namespace Emby.Server.Implementations.EntryPoints
                 { "platform", _applicationHost.OperatingSystemDisplayName }
                 { "platform", _applicationHost.OperatingSystemDisplayName }
             };
             };
 
 
-            var users = _userManager.Users.ToList();
-
-            data["localusers"] = users.Count(i => !i.ConnectLinkType.HasValue).ToString(CultureInfo.InvariantCulture);
-            data["guests"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.Guest).ToString(CultureInfo.InvariantCulture);
-            data["linkedusers"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.LinkedUser).ToString(CultureInfo.InvariantCulture);
-
             data["plugins"] = string.Join(",", _applicationHost.Plugins.Select(i => i.Id).ToArray());
             data["plugins"] = string.Join(",", _applicationHost.Plugins.Select(i => i.Id).ToArray());
 
 
             var logErrors = false;
             var logErrors = false;

+ 41 - 2
Emby.Server.Implementations/HttpServer/HttpListenerHost.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
@@ -423,6 +424,22 @@ namespace Emby.Server.Implementations.HttpServer
             return true;
             return true;
         }
         }
 
 
+        private bool ValidateSsl(string remoteIp, string urlString)
+        {
+            if (_config.Configuration.RequireHttps && _appHost.EnableHttps)
+            {
+                if (urlString.IndexOf("https://", StringComparison.OrdinalIgnoreCase) == -1)
+                {
+                    if (!_networkManager.IsInLocalNetwork(remoteIp))
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            return true;
+        }
+
         /// <summary>
         /// <summary>
         /// Overridable method that can be used to implement a custom hnandler
         /// Overridable method that can be used to implement a custom hnandler
         /// </summary>
         /// </summary>
@@ -453,6 +470,16 @@ namespace Emby.Server.Implementations.HttpServer
                     return;
                     return;
                 }
                 }
 
 
+                if (!ValidateSsl(httpReq.RemoteIp, urlString))
+                {
+                    var httpsUrl = urlString
+                        .Replace("http://", "https://", StringComparison.OrdinalIgnoreCase)
+                        .Replace(":" + _config.Configuration.PublicPort.ToString(CultureInfo.InvariantCulture), ":" + _config.Configuration.PublicHttpsPort.ToString(CultureInfo.InvariantCulture), StringComparison.OrdinalIgnoreCase);
+
+                    RedirectToUrl(httpRes, httpsUrl);
+                    return;
+                }
+
                 if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
                 if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     httpRes.StatusCode = 200;
                     httpRes.StatusCode = 200;
@@ -468,7 +495,7 @@ namespace Emby.Server.Implementations.HttpServer
 
 
                 enableLog = EnableLogging(urlString, localPath);
                 enableLog = EnableLogging(urlString, localPath);
                 urlToLog = urlString;
                 urlToLog = urlString;
-                 logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
+                logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
 
 
                 if (enableLog)
                 if (enableLog)
                 {
                 {
@@ -579,7 +606,13 @@ namespace Emby.Server.Implementations.HttpServer
 
 
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                ErrorHandler(ex, httpReq, !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase));
+                var logException = !string.Equals(ex.GetType().Name, "SocketException", StringComparison.OrdinalIgnoreCase);
+
+#if DEBUG
+                logException = true;
+#endif
+
+                ErrorHandler(ex, httpReq, logException);
             }
             }
             finally
             finally
             {
             {
@@ -725,6 +758,12 @@ namespace Emby.Server.Implementations.HttpServer
 
 
         public object DeserializeJson(Type type, Stream stream)
         public object DeserializeJson(Type type, Stream stream)
         {
         {
+            //using (var reader = new StreamReader(stream))
+            //{
+            //    var json = reader.ReadToEnd();
+            //    Logger.Info(json);
+            //    return _jsonSerializer.DeserializeFromString(json, type);
+            //}
             return _jsonSerializer.DeserializeFromStream(stream, type);
             return _jsonSerializer.DeserializeFromStream(stream, type);
         }
         }
 
 

+ 29 - 43
Emby.Server.Implementations/HttpServer/HttpResultFactory.cs

@@ -142,8 +142,6 @@ namespace Emby.Server.Implementations.HttpServer
                 throw new ArgumentNullException("result");
                 throw new ArgumentNullException("result");
             }
             }
 
 
-            var optimizedResult = ToOptimizedResult(requestContext, result);
-
             if (responseHeaders == null)
             if (responseHeaders == null)
             {
             {
                 responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                 responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -154,15 +152,7 @@ namespace Emby.Server.Implementations.HttpServer
                 responseHeaders["Expires"] = "-1";
                 responseHeaders["Expires"] = "-1";
             }
             }
 
 
-            // Apply headers
-            var hasHeaders = optimizedResult as IHasHeaders;
-
-            if (hasHeaders != null)
-            {
-                AddResponseHeaders(hasHeaders, responseHeaders);
-            }
-
-            return optimizedResult;
+            return ToOptimizedResultInternal(requestContext, result, responseHeaders);
         }
         }
 
 
         public static string GetCompressionType(IRequest request)
         public static string GetCompressionType(IRequest request)
@@ -189,6 +179,11 @@ namespace Emby.Server.Implementations.HttpServer
         /// <param name="dto"></param>
         /// <param name="dto"></param>
         /// <returns></returns>
         /// <returns></returns>
         public object ToOptimizedResult<T>(IRequest request, T dto)
         public object ToOptimizedResult<T>(IRequest request, T dto)
+        {
+            return ToOptimizedResultInternal(request, dto, null);
+        }
+
+        private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
         {
         {
             var contentType = request.ResponseContentType;
             var contentType = request.ResponseContentType;
 
 
@@ -197,27 +192,27 @@ namespace Emby.Server.Implementations.HttpServer
                 case "application/xml":
                 case "application/xml":
                 case "text/xml":
                 case "text/xml":
                 case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
                 case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
-                    return SerializeToXmlString(dto);
+                    return GetHttpResult(SerializeToXmlString(dto), contentType, false, responseHeaders);
 
 
                 case "application/json":
                 case "application/json":
                 case "text/json":
                 case "text/json":
-                    return _jsonSerializer.SerializeToString(dto);
+                    return GetHttpResult(_jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
                 default:
                 default:
-                    {
-                        var ms = new MemoryStream();
-                        var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
+                {
+                    var ms = new MemoryStream();
+                    var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
 
 
-                        writerFn(dto, ms);
-                        
-                        ms.Position = 0;
+                    writerFn(dto, ms);
 
 
-                        if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
-                        {
-                            return GetHttpResult(new byte[] { }, contentType, true);
-                        }
+                    ms.Position = 0;
 
 
-                        return GetHttpResult(ms, contentType, true);
+                    if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
                     }
                     }
+
+                    return GetHttpResult(ms, contentType, true, responseHeaders);
+                }
             }
             }
         }
         }
 
 
@@ -360,7 +355,7 @@ namespace Emby.Server.Implementations.HttpServer
                 if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
                 if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
                 {
                 {
                     AddAgeHeader(responseHeaders, lastDateModified);
                     AddAgeHeader(responseHeaders, lastDateModified);
-                    AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration, noCache);
+                    AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
 
 
                     var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
                     var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
 
 
@@ -370,7 +365,7 @@ namespace Emby.Server.Implementations.HttpServer
                 }
                 }
             }
             }
 
 
-            AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration, noCache);
+            AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration);
 
 
             return null;
             return null;
         }
         }
@@ -424,16 +419,6 @@ namespace Emby.Server.Implementations.HttpServer
 
 
             options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
 
 
-            if (!options.ResponseHeaders.ContainsKey("Content-Disposition"))
-            {
-                // Quotes are valid in linux. They'll possibly cause issues here
-                var filename = (Path.GetFileName(path) ?? string.Empty).Replace("\"", string.Empty);
-                if (!string.IsNullOrWhiteSpace(filename))
-                {
-                    options.ResponseHeaders["Content-Disposition"] = "inline; filename=\"" + filename + "\"";
-                }
-            }
-
             return GetStaticResult(requestContext, options);
             return GetStaticResult(requestContext, options);
         }
         }
 
 
@@ -490,7 +475,8 @@ namespace Emby.Server.Implementations.HttpServer
                 return result;
                 return result;
             }
             }
 
 
-            var isHeadRequest = options.IsHeadRequest;
+            // TODO: We don't really need the option value
+            var isHeadRequest = options.IsHeadRequest || string.Equals(requestContext.Verb, "HEAD", StringComparison.OrdinalIgnoreCase);
             var factoryFn = options.ContentFactory;
             var factoryFn = options.ContentFactory;
             var responseHeaders = options.ResponseHeaders;
             var responseHeaders = options.ResponseHeaders;
 
 
@@ -555,7 +541,7 @@ namespace Emby.Server.Implementations.HttpServer
         /// <summary>
         /// <summary>
         /// Adds the caching responseHeaders.
         /// Adds the caching responseHeaders.
         /// </summary>
         /// </summary>
-        private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, bool noCache)
+        private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
         {
         {
             // Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
             // Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
             // https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
             // https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
@@ -565,11 +551,11 @@ namespace Emby.Server.Implementations.HttpServer
                 responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
                 responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
             }
             }
 
 
-            if (!noCache && cacheDuration.HasValue)
+            if (cacheDuration.HasValue)
             {
             {
                 responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
                 responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
             }
             }
-            else if (!noCache && !string.IsNullOrEmpty(cacheKey))
+            else if (!string.IsNullOrEmpty(cacheKey))
             {
             {
                 responseHeaders["Cache-Control"] = "public";
                 responseHeaders["Cache-Control"] = "public";
             }
             }
@@ -579,15 +565,15 @@ namespace Emby.Server.Implementations.HttpServer
                 responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
                 responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
             }
             }
 
 
-            AddExpiresHeader(responseHeaders, cacheKey, cacheDuration, noCache);
+            AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
         }
         }
 
 
         /// <summary>
         /// <summary>
         /// Adds the expires header.
         /// Adds the expires header.
         /// </summary>
         /// </summary>
-        private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration, bool noCache)
+        private void AddExpiresHeader(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
         {
         {
-            if (!noCache && cacheDuration.HasValue)
+            if (cacheDuration.HasValue)
             {
             {
                 responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
                 responseHeaders["Expires"] = DateTime.UtcNow.Add(cacheDuration.Value).ToString("r");
             }
             }

+ 1 - 7
Emby.Server.Implementations/IO/LibraryMonitor.cs

@@ -333,13 +333,7 @@ namespace Emby.Server.Implementations.IO
                         NotifyFilters.Attributes;
                         NotifyFilters.Attributes;
 
 
                     newWatcher.Created += watcher_Changed;
                     newWatcher.Created += watcher_Changed;
-
-                    // Seeing mono crashes on background threads we can't catch, testing if this might help
-                    if (_environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
-                    {
-                        newWatcher.Deleted += watcher_Changed;
-                    }
-
+                    newWatcher.Deleted += watcher_Changed;
                     newWatcher.Renamed += watcher_Changed;
                     newWatcher.Renamed += watcher_Changed;
                     newWatcher.Changed += watcher_Changed;
                     newWatcher.Changed += watcher_Changed;
 
 

+ 9 - 52
Emby.Server.Implementations/Library/LibraryManager.cs

@@ -247,11 +247,6 @@ namespace Emby.Server.Implementations.Library
             }
             }
         }
         }
 
 
-        /// <summary>
-        /// The _season zero display name
-        /// </summary>
-        private string _seasonZeroDisplayName;
-
         private bool _wizardCompleted;
         private bool _wizardCompleted;
         /// <summary>
         /// <summary>
         /// Records the configuration values.
         /// Records the configuration values.
@@ -259,7 +254,6 @@ namespace Emby.Server.Implementations.Library
         /// <param name="configuration">The configuration.</param>
         /// <param name="configuration">The configuration.</param>
         private void RecordConfigurationValues(ServerConfiguration configuration)
         private void RecordConfigurationValues(ServerConfiguration configuration)
         {
         {
-            _seasonZeroDisplayName = configuration.SeasonZeroDisplayName;
             _wizardCompleted = configuration.IsStartupWizardCompleted;
             _wizardCompleted = configuration.IsStartupWizardCompleted;
         }
         }
 
 
@@ -272,59 +266,14 @@ namespace Emby.Server.Implementations.Library
         {
         {
             var config = ConfigurationManager.Configuration;
             var config = ConfigurationManager.Configuration;
 
 
-            var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
-            var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal);
             var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
             var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
 
 
             RecordConfigurationValues(config);
             RecordConfigurationValues(config);
 
 
-            if (seasonZeroNameChanged || wizardChanged)
+            if (wizardChanged)
             {
             {
                 _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
                 _taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
             }
             }
-
-            if (seasonZeroNameChanged)
-            {
-                Task.Run(async () =>
-                {
-                    await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false);
-
-                });
-            }
-        }
-
-        /// <summary>
-        /// Updates the season zero names.
-        /// </summary>
-        /// <param name="newName">The new name.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        private async Task UpdateSeasonZeroNames(string newName, CancellationToken cancellationToken)
-        {
-            var seasons = GetItemList(new InternalItemsQuery
-            {
-                IncludeItemTypes = new[] { typeof(Season).Name },
-                Recursive = true,
-                IndexNumber = 0,
-                DtoOptions = new DtoOptions(true)
-
-            }).Cast<Season>()
-                .Where(i => !string.Equals(i.Name, newName, StringComparison.Ordinal))
-                .ToList();
-
-            foreach (var season in seasons)
-            {
-                season.Name = newName;
-
-                try
-                {
-                    await UpdateItem(season, ItemUpdateType.MetadataDownload, cancellationToken).ConfigureAwait(false);
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error saving {0}", ex, season.Path);
-                }
-            }
         }
         }
 
 
         public void RegisterItem(BaseItem item)
         public void RegisterItem(BaseItem item)
@@ -433,6 +382,14 @@ namespace Emby.Server.Implementations.Library
                             _fileSystem.DeleteFile(fileSystemInfo.FullName);
                             _fileSystem.DeleteFile(fileSystemInfo.FullName);
                         }
                         }
                     }
                     }
+                    catch (FileNotFoundException)
+                    {
+                        // may have already been deleted manually by user
+                    }
+                    catch (DirectoryNotFoundException)
+                    {
+                        // may have already been deleted manually by user
+                    }
                     catch (IOException)
                     catch (IOException)
                     {
                     {
                         if (isRequiredForDelete)
                         if (isRequiredForDelete)

+ 2 - 2
Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs

@@ -55,9 +55,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
                 if (season.IndexNumber.HasValue)
                 if (season.IndexNumber.HasValue)
                 {
                 {
                     var seasonNumber = season.IndexNumber.Value;
                     var seasonNumber = season.IndexNumber.Value;
-
+                    
                     season.Name = seasonNumber == 0 ?
                     season.Name = seasonNumber == 0 ?
-                        _config.Configuration.SeasonZeroDisplayName :
+                        args.LibraryOptions.SeasonZeroDisplayName :
                         string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture));
                         string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.ToString(UsCulture));
                 }
                 }
 
 

+ 6 - 6
Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs

@@ -1006,7 +1006,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         }
         }
 
 
         private readonly SemaphoreSlim _liveStreamsSemaphore = new SemaphoreSlim(1, 1);
         private readonly SemaphoreSlim _liveStreamsSemaphore = new SemaphoreSlim(1, 1);
-        private readonly List<LiveStream> _liveStreams = new List<LiveStream>();
+        private readonly List<ILiveStream> _liveStreams = new List<ILiveStream>();
 
 
         public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
         public async Task<MediaSourceInfo> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
         {
         {
@@ -1039,7 +1039,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return mediaSource;
             return mediaSource;
         }
         }
 
 
-        public async Task<LiveStream> GetLiveStream(string uniqueId)
+        public async Task<ILiveStream> GetLiveStream(string uniqueId)
         {
         {
             await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false);
             await _liveStreamsSemaphore.WaitAsync().ConfigureAwait(false);
 
 
@@ -1055,7 +1055,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
         }
         }
 
 
-        private async Task<Tuple<LiveStream, MediaSourceInfo, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
+        private async Task<Tuple<ILiveStream, MediaSourceInfo, ITunerHost>> GetChannelStreamInternal(string channelId, string streamId, CancellationToken cancellationToken)
         {
         {
             _logger.Info("Streaming Channel " + channelId);
             _logger.Info("Streaming Channel " + channelId);
 
 
@@ -1072,7 +1072,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
                     _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
                     _logger.Info("Live stream {0} consumer count is now {1}", streamId, result.ConsumerCount);
 
 
-                    return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
+                    return new Tuple<ILiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, result.TunerHost);
                 }
                 }
 
 
                 foreach (var hostInstance in _liveTvManager.TunerHosts)
                 foreach (var hostInstance in _liveTvManager.TunerHosts)
@@ -1092,7 +1092,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                         _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
                         _logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
                             streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
                             streamId, openedMediaSource.Id, openedMediaSource.LiveStreamId);
 
 
-                        return new Tuple<LiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance);
+                        return new Tuple<ILiveStream, MediaSourceInfo, ITunerHost>(result, openedMediaSource, hostInstance);
                     }
                     }
                     catch (FileNotFoundException)
                     catch (FileNotFoundException)
                     {
                     {
@@ -1718,7 +1718,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             {
             {
                 var parent = _fileSystem.GetDirectoryName(originalPath);
                 var parent = _fileSystem.GetDirectoryName(originalPath);
                 var name = Path.GetFileNameWithoutExtension(originalPath);
                 var name = Path.GetFileNameWithoutExtension(originalPath);
-                name += "-" + index.ToString(CultureInfo.InvariantCulture);
+                name += " - " + index.ToString(CultureInfo.InvariantCulture);
 
 
                 path = Path.ChangeExtension(Path.Combine(parent, name), Path.GetExtension(originalPath));
                 path = Path.ChangeExtension(Path.Combine(parent, name), Path.GetExtension(originalPath));
                 index++;
                 index++;

+ 19 - 35
Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs

@@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
             if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
             if (!path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
             {
             {
-                return path;
+                return UnzipIfNeeded(path, path);
             }
             }
 
 
             var cacheFilename = DateTime.UtcNow.DayOfYear.ToString(CultureInfo.InvariantCulture) + "-" + DateTime.UtcNow.Hour.ToString(CultureInfo.InvariantCulture) + ".xml";
             var cacheFilename = DateTime.UtcNow.DayOfYear.ToString(CultureInfo.InvariantCulture) + "-" + DateTime.UtcNow.Hour.ToString(CultureInfo.InvariantCulture) + ".xml";
@@ -94,46 +94,30 @@ namespace Emby.Server.Implementations.LiveTv.Listings
 
 
             _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFile));
             _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(cacheFile));
 
 
-            using (var stream = _fileSystem.OpenRead(tempFile))
-            {
-                using (var reader = new StreamReader(stream, Encoding.UTF8))
-                {
-                    using (var fileStream = _fileSystem.GetFileStream(cacheFile, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
-                    {
-                        using (var writer = new StreamWriter(fileStream))
-                        {
-                            while (!reader.EndOfStream)
-                            {
-                                writer.WriteLine(reader.ReadLine());
-                            }
-                        }
-                    }
-                }
-            }
+            _fileSystem.CopyFile(tempFile, cacheFile, true);
 
 
-            _logger.Debug("Returning xmltv path {0}", cacheFile);
             return UnzipIfNeeded(path, cacheFile);
             return UnzipIfNeeded(path, cacheFile);
         }
         }
 
 
         private string UnzipIfNeeded(string originalUrl, string file)
         private string UnzipIfNeeded(string originalUrl, string file)
         {
         {
-            //var ext = Path.GetExtension(originalUrl);
-
-            //if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase))
-            //{
-            //    using (var stream = _fileSystem.OpenRead(file))
-            //    {
-            //        var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
-            //        _fileSystem.CreateDirectory(tempFolder);
-
-            //        _zipClient.ExtractAllFromZip(stream, tempFolder, true);
-
-            //        return _fileSystem.GetFiles(tempFolder, true)
-            //            .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
-            //            .Select(i => i.FullName)
-            //            .FirstOrDefault();
-            //    }
-            //}
+            var ext = Path.GetExtension(originalUrl.Split('?')[0]);
+
+            if (string.Equals(ext, ".gz", StringComparison.OrdinalIgnoreCase))
+            {
+                using (var stream = _fileSystem.OpenRead(file))
+                {
+                    var tempFolder = Path.Combine(_config.ApplicationPaths.TempDirectory, Guid.NewGuid().ToString());
+                    _fileSystem.CreateDirectory(tempFolder);
+
+                    _zipClient.ExtractAllFromGz(stream, tempFolder, true);
+
+                    return _fileSystem.GetFiles(tempFolder, true)
+                        .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
+                        .Select(i => i.FullName)
+                        .FirstOrDefault();
+                }
+            }
 
 
             return file;
             return file;
         }
         }

+ 1 - 1
Emby.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -78,7 +78,7 @@ namespace Emby.Server.Implementations.LiveTv
             return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
             return EmbyTV.EmbyTV.Current.GetActiveRecordingPath(id);
         }
         }
 
 
-        public Task<LiveStream> GetEmbyTvLiveStream(string id)
+        public Task<ILiveStream> GetEmbyTvLiveStream(string id)
         {
         {
             return EmbyTV.EmbyTV.Current.GetLiveStream(id);
             return EmbyTV.EmbyTV.Current.GetLiveStream(id);
         }
         }

+ 5 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs

@@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return new List<MediaSourceInfo>();
             return new List<MediaSourceInfo>();
         }
         }
 
 
-        protected abstract Task<LiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
+        protected abstract Task<ILiveStream> GetChannelStream(TunerHostInfo tuner, string channelId, string streamId, CancellationToken cancellationToken);
 
 
-        public async Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
+        public async Task<ILiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken)
         {
         {
             if (string.IsNullOrWhiteSpace(channelId))
             if (string.IsNullOrWhiteSpace(channelId))
             {
             {
@@ -247,7 +247,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
                 try
                 try
                 {
                 {
                     var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
                     var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
+                    var startTime = DateTime.UtcNow;
                     await liveStream.Open(cancellationToken).ConfigureAwait(false);
                     await liveStream.Open(cancellationToken).ConfigureAwait(false);
+                    var endTime = DateTime.UtcNow;
+                    Logger.Info("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds);
                     return liveStream;
                     return liveStream;
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)

+ 10 - 1
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs

@@ -347,6 +347,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                 videoCodec = "h264";
                 videoCodec = "h264";
                 videoBitrate = 1000000;
                 videoBitrate = 1000000;
             }
             }
+            else
+            {
+                // This is for android tv's 1200 condition. Remove once not needed anymore so that we can avoid possible side effects of dummying up this data
+                if ((channelInfo.IsHD ?? true))
+                {
+                    width = 1920;
+                    height = 1080;
+                }
+            }
 
 
             if (channelInfo != null)
             if (channelInfo != null)
             {
             {
@@ -491,7 +500,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return list;
             return list;
         }
         }
 
 
-        protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+        protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
         {
         {
             var profile = streamId.Split('_')[0];
             var profile = streamId.Split('_')[0];
 
 

+ 153 - 0
Emby.Server.Implementations/LiveTv/TunerHosts/LiveStream.cs

@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.System;
+
+namespace Emby.Server.Implementations.LiveTv.TunerHosts
+{
+    public class LiveStream : ILiveStream
+    {
+        public MediaSourceInfo OriginalMediaSource { get; set; }
+        public MediaSourceInfo OpenedMediaSource { get; set; }
+        public int ConsumerCount
+        {
+            get { return SharedStreamIds.Count; }
+        }
+        public ITunerHost TunerHost { get; set; }
+        public string OriginalStreamId { get; set; }
+        public bool EnableStreamSharing { get; set; }
+        public string UniqueId { get; private set; }
+
+        public List<string> SharedStreamIds { get; private set; }
+        protected readonly IEnvironmentInfo Environment;
+        protected readonly IFileSystem FileSystem;
+
+        protected readonly string TempFilePath;
+        protected readonly ILogger Logger;
+
+        public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem, ILogger logger, IServerApplicationPaths appPaths)
+        {
+            OriginalMediaSource = mediaSource;
+            Environment = environment;
+            FileSystem = fileSystem;
+            OpenedMediaSource = mediaSource;
+            Logger = logger;
+            EnableStreamSharing = true;
+            SharedStreamIds = new List<string>();
+            UniqueId = Guid.NewGuid().ToString("N");
+            TempFilePath = Path.Combine(appPaths.TranscodingTempPath, UniqueId + ".ts");
+        }
+
+        public Task Open(CancellationToken cancellationToken)
+        {
+            return OpenInternal(cancellationToken);
+        }
+
+        protected virtual Task OpenInternal(CancellationToken cancellationToken)
+        {
+            return Task.FromResult(true);
+        }
+
+        public virtual Task Close()
+        {
+            return Task.FromResult(true);
+        }
+
+        protected Stream GetInputStream(string path, bool allowAsyncFileRead)
+        {
+            var fileOpenOptions = FileOpenOptions.SequentialScan;
+
+            if (allowAsyncFileRead)
+            {
+                fileOpenOptions |= FileOpenOptions.Asynchronous;
+            }
+
+            return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
+        }
+
+        protected async Task DeleteTempFile(string path, int retryCount = 0)
+        {
+            try
+            {
+                FileSystem.DeleteFile(path);
+                return;
+            }
+            catch
+            {
+
+            }
+
+            if (retryCount > 20)
+            {
+                return;
+            }
+
+            await Task.Delay(500).ConfigureAwait(false);
+            await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false);
+        }
+
+        public async Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
+        {
+            var allowAsync = false;//Environment.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows;
+            // use non-async filestream along with read due to https://github.com/dotnet/corefx/issues/6039
+
+            using (var inputStream = (FileStream)GetInputStream(TempFilePath, allowAsync))
+            {
+                TrySeek(inputStream, -20000);
+
+                await CopyTo(inputStream, stream, 81920, null, cancellationToken).ConfigureAwait(false);
+            }
+        }
+
+        private static async Task CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
+        {
+            byte[] buffer = new byte[bufferSize];
+            while (true)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                var read = source.Read(buffer, 0, buffer.Length);
+
+                if (read > 0)
+                {
+                    //await destination.WriteAsync(buffer, 0, read).ConfigureAwait(false);
+                    destination.Write(buffer, 0, read);
+
+                    if (onStarted != null)
+                    {
+                        onStarted();
+                        onStarted = null;
+                    }
+                }
+                else
+                {
+                    await Task.Delay(10).ConfigureAwait(false);
+                }
+            }
+        }
+
+        private void TrySeek(FileStream stream, long offset)
+        {
+            try
+            {
+                stream.Seek(offset, SeekOrigin.End);
+            }
+            catch (ArgumentException)
+            {
+
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error seeking stream", ex);
+            }
+        }
+    }
+}

+ 2 - 2
Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs

@@ -75,11 +75,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
             return Task.FromResult(list);
             return Task.FromResult(list);
         }
         }
 
 
-        protected override async Task<LiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
+        protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, string channelId, string streamId, CancellationToken cancellationToken)
         {
         {
             var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
             var sources = await GetChannelStreamMediaSources(info, channelId, cancellationToken).ConfigureAwait(false);
 
 
-            var liveStream = new LiveStream(sources.First(), _environment, FileSystem);
+            var liveStream = new LiveStream(sources.First(), _environment, FileSystem, Logger, Config.ApplicationPaths);
             return liveStream;
             return liveStream;
         }
         }
 
 

+ 0 - 81
Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs

@@ -1,81 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
-
-namespace Emby.Server.Implementations.LiveTv.TunerHosts
-{
-    public class MulticastStream
-    {
-        private readonly ConcurrentDictionary<Guid, QueueStream> _outputStreams = new ConcurrentDictionary<Guid, QueueStream>();
-        private const int BufferSize = 81920;
-        private readonly ILogger _logger;
-
-        public MulticastStream(ILogger logger)
-        {
-            _logger = logger;
-        }
-
-        public async Task CopyUntilCancelled(Stream source, Action onStarted, CancellationToken cancellationToken)
-        {
-            if (source == null)
-            {
-                throw new ArgumentNullException("source");
-            }
-
-            while (true)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-
-                byte[] buffer = new byte[BufferSize];
-
-                var bytesRead = source.Read(buffer, 0, buffer.Length);
-
-                if (bytesRead > 0)
-                {
-                    foreach (var stream in _outputStreams)
-                    {
-                        stream.Value.Queue(buffer, 0, bytesRead);
-                    }
-
-                    if (onStarted != null)
-                    {
-                        var onStartedCopy = onStarted;
-                        onStarted = null;
-                        Task.Run(onStartedCopy);
-                    }
-                }
-
-                else
-                {
-                    await Task.Delay(100).ConfigureAwait(false);
-                }
-            }
-        }
-
-        public Task CopyToAsync(Stream stream, CancellationToken cancellationToken)
-        {
-            var queueStream = new QueueStream(stream, _logger);
-
-            _outputStreams.TryAdd(queueStream.Id, queueStream);
-
-            try
-            {
-                queueStream.Start(cancellationToken);
-            }
-            finally
-            {
-                _outputStreams.TryRemove(queueStream.Id, out queueStream);
-                GC.Collect();
-            }
-
-            return Task.FromResult(true);
-        }
-    }
-}

+ 0 - 45
Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs

@@ -1,45 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Logging;
-
-namespace Emby.Server.Implementations.LiveTv.TunerHosts
-{
-    public class QueueStream
-    {
-        private readonly Stream _outputStream;
-        private readonly BlockingCollection<Tuple<byte[], int, int>> _queue = new BlockingCollection<Tuple<byte[], int, int>>();
-
-        private readonly ILogger _logger;
-        public Guid Id = Guid.NewGuid();
-
-        public QueueStream(Stream outputStream, ILogger logger)
-        {
-            _outputStream = outputStream;
-            _logger = logger;
-        }
-
-        public void Queue(byte[] bytes, int offset, int count)
-        {
-            _queue.Add(new Tuple<byte[], int, int>(bytes, offset, count));
-        }
-
-        public void Start(CancellationToken cancellationToken)
-        {
-            while (true)
-            {
-                foreach (var result in _queue.GetConsumingEnumerable())
-                {
-                    cancellationToken.ThrowIfCancellationRequested();
-
-                    _outputStream.Write(result.Item1, result.Item2, result.Item3);
-                }
-            }
-        }
-    }
-}

+ 1 - 1
Emby.Server.Implementations/Notifications/SqliteNotificationsRepository.cs

@@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Notifications
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
-                Logger.ErrorException("Error loading notifications database file. Will reset and retry.", ex);
+                Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
 
 
                 FileSystem.DeleteFile(DbFilePath);
                 FileSystem.DeleteFile(DbFilePath);
 
 

+ 11 - 3
Emby.Server.Implementations/Session/SessionManager.cs

@@ -1406,11 +1406,19 @@ namespace Emby.Server.Implementations.Session
                     .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase));
                     .FirstOrDefault(i => string.Equals(request.Username, i.Name, StringComparison.OrdinalIgnoreCase));
             }
             }
 
 
-            if (user != null && !string.IsNullOrWhiteSpace(request.DeviceId))
+            if (user != null)
             {
             {
-                if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId))
+                if (!user.IsParentalScheduleAllowed())
+                {
+                    throw new SecurityException("User is not allowed access at this time.");
+                }
+
+                if (!string.IsNullOrWhiteSpace(request.DeviceId))
                 {
                 {
-                    throw new SecurityException("User is not allowed access from this device.");
+                    if (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId))
+                    {
+                        throw new SecurityException("User is not allowed access from this device.");
+                    }
                 }
                 }
             }
             }
 
 

+ 22 - 2
Emby.Server.Implementations/Social/SharingRepository.cs

@@ -7,22 +7,42 @@ using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Social;
 using MediaBrowser.Model.Social;
 using SQLitePCL.pretty;
 using SQLitePCL.pretty;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
 
 
 namespace Emby.Server.Implementations.Social
 namespace Emby.Server.Implementations.Social
 {
 {
     public class SharingRepository : BaseSqliteRepository, ISharingRepository
     public class SharingRepository : BaseSqliteRepository, ISharingRepository
     {
     {
-        public SharingRepository(ILogger logger, IApplicationPaths appPaths)
+        protected IFileSystem FileSystem { get; private set; }
+
+        public SharingRepository(ILogger logger, IApplicationPaths appPaths, IFileSystem fileSystem)
             : base(logger)
             : base(logger)
         {
         {
+            FileSystem = fileSystem;
             DbFilePath = Path.Combine(appPaths.DataPath, "shares.db");
             DbFilePath = Path.Combine(appPaths.DataPath, "shares.db");
         }
         }
 
 
+        public void Initialize()
+        {
+            try
+            {
+                InitializeInternal();
+            }
+            catch (Exception ex)
+            {
+                Logger.ErrorException("Error loading database file. Will reset and retry.", ex);
+
+                FileSystem.DeleteFile(DbFilePath);
+
+                InitializeInternal();
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Opens the connection to the database
         /// Opens the connection to the database
         /// </summary>
         /// </summary>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        public void Initialize()
+        private void InitializeInternal()
         {
         {
             using (var connection = CreateConnection())
             using (var connection = CreateConnection())
             {
             {

+ 7 - 29
Emby.Server.Implementations/TV/TVSeriesManager.cs

@@ -56,37 +56,15 @@ namespace Emby.Server.Implementations.TV
                 return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
                 return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
             }
             }
 
 
-            if (limit.HasValue)
-            {
-                limit = limit.Value + 10;
-            }
-
-            var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
-            {
-                IncludeItemTypes = new[] { typeof(Episode).Name },
-                OrderBy = new[] { new Tuple<string, SortOrder>(ItemSortBy.DatePlayed, SortOrder.Descending) },
-                SeriesPresentationUniqueKey = presentationUniqueKey,
-                Limit = limit,
-                ParentId = parentIdGuid,
-                Recursive = true,
-                DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions
-                {
-                    Fields = new ItemFields[]
-                    {
-                        ItemFields.SeriesPresentationUniqueKey
-                    }
-                },
-                GroupBySeriesPresentationUniqueKey = true
-
-            }).Cast<Episode>().Select(GetUniqueSeriesKey);
-
-            // Avoid implicitly captured closure
-            var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
+            var parents = user.RootFolder.GetChildren(user, true)
+                .Where(i => i is Folder)
+                .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
+                .ToList();
 
 
-            return GetResult(episodes, request);
+            return GetNextUp(request, parents, dtoOptions);
         }
         }
 
 
-        public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders, DtoOptions dtoOptions)
+        public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<BaseItem> parentsFolders, DtoOptions dtoOptions)
         {
         {
             var user = _userManager.GetUserById(request.UserId);
             var user = _userManager.GetUserById(request.UserId);
 
 
@@ -134,7 +112,7 @@ namespace Emby.Server.Implementations.TV
                 },
                 },
                 GroupBySeriesPresentationUniqueKey = true
                 GroupBySeriesPresentationUniqueKey = true
 
 
-            }, parentsFolders.Cast<BaseItem>().ToList()).Cast<Episode>().Select(GetUniqueSeriesKey);
+            }, parentsFolders).Cast<Episode>().Select(GetUniqueSeriesKey);
 
 
             // Avoid implicitly captured closure
             // Avoid implicitly captured closure
             var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);
             var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);

+ 1 - 1
Emby.Server.Implementations/packages.config

@@ -2,7 +2,7 @@
 <packages>
 <packages>
   <package id="Emby.XmlTv" version="1.0.10" targetFramework="net46" />
   <package id="Emby.XmlTv" version="1.0.10" targetFramework="net46" />
   <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
   <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
-  <package id="SharpCompress" version="0.14.0" targetFramework="net46" />
+  <package id="SharpCompress" version="0.18.2" targetFramework="net46" />
   <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
   <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
   <package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
   <package id="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
   <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />
   <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />

+ 52 - 0
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -26,6 +26,11 @@ namespace MediaBrowser.Api.UserLibrary
     {
     {
     }
     }
 
 
+    [Route("/Users/{UserId}/Items/Resume", "GET", Summary = "Gets items based on a query.")]
+    public class GetResumeItems : BaseItemsRequest, IReturn<QueryResult<BaseItemDto>>
+    {
+    }
+
     /// <summary>
     /// <summary>
     /// Class ItemsService
     /// Class ItemsService
     /// </summary>
     /// </summary>
@@ -79,6 +84,53 @@ namespace MediaBrowser.Api.UserLibrary
             _authContext = authContext;
             _authContext = authContext;
         }
         }
 
 
+        public object Get(GetResumeItems request)
+        {
+            var user = _userManager.GetUserById(request.UserId);
+
+            var parentIdGuid = string.IsNullOrWhiteSpace(request.ParentId) ? (Guid?)null : new Guid(request.ParentId);
+
+            var options = GetDtoOptions(_authContext, request);
+
+            var ancestorIds = new List<string>();
+
+            var excludeFolderIds = user.Configuration.LatestItemsExcludes;
+            if (!parentIdGuid.HasValue && excludeFolderIds.Length > 0)
+            {
+                ancestorIds = user.RootFolder.GetChildren(user, true)
+                    .Where(i => i is Folder)
+                    .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N")))
+                    .Select(i => i.Id.ToString("N"))
+                    .ToList();
+            }
+
+            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
+            {
+                OrderBy = new[] { ItemSortBy.DatePlayed }.Select(i => new Tuple<string, SortOrder>(i, SortOrder.Descending)).ToArray(),
+                IsResumable = true,
+                StartIndex = request.StartIndex,
+                Limit = request.Limit,
+                ParentId = parentIdGuid,
+                Recursive = true,
+                DtoOptions = options,
+                MediaTypes = request.GetMediaTypes(),
+                IsVirtualItem = false,
+                CollapseBoxSetItems = false,
+                EnableTotalRecordCount = request.EnableTotalRecordCount,
+                AncestorIds = ancestorIds.ToArray()
+            });
+
+            var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user);
+
+            var result = new QueryResult<BaseItemDto>
+            {
+                TotalRecordCount = itemsResult.TotalRecordCount,
+                Items = returnItems
+            };
+
+            return ToOptimizedSerializedResultUsingCache(result);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the specified request.
         /// Gets the specified request.
         /// </summary>
         /// </summary>

+ 19 - 5
MediaBrowser.Common/Updates/GithubUpdater.cs

@@ -114,7 +114,7 @@ namespace MediaBrowser.Common.Updates
             {
             {
                 var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
                 var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
 
 
-                obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename))).ToArray();
+                obj = obj.Where(i => (i.assets ?? new List<Asset>()).Any(a => IsAsset(a, assetFilename, i.tag_name))).ToArray();
 
 
                 list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1));
                 list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Release)).OrderByDescending(GetVersion).Take(1));
                 list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1));
                 list.AddRange(obj.Where(i => MatchesUpdateLevel(i, PackageVersionClass.Beta)).OrderByDescending(GetVersion).Take(1));
@@ -138,7 +138,8 @@ namespace MediaBrowser.Common.Updates
         private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
         private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
         {
         {
             Version version;
             Version version;
-            if (!Version.TryParse(obj.tag_name, out version))
+            var versionString = obj.tag_name;
+            if (!Version.TryParse(versionString, out version))
             {
             {
                 return null;
                 return null;
             }
             }
@@ -148,7 +149,7 @@ namespace MediaBrowser.Common.Updates
                 return null;
                 return null;
             }
             }
 
 
-            var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename));
+            var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => IsAsset(i, assetFilename, versionString));
 
 
             if (asset == null)
             if (asset == null)
             {
             {
@@ -175,9 +176,22 @@ namespace MediaBrowser.Common.Updates
             };
             };
         }
         }
 
 
-        private bool IsAsset(Asset asset, string assetFilename)
+        private bool IsAsset(Asset asset, string assetFilename, string version)
         {
         {
-            var downloadFilename = Path.GetFileName(asset.browser_download_url) ?? string.Empty;
+            var downloadFilename = Path.GetFileNameWithoutExtension(asset.browser_download_url) ?? string.Empty;
+            var assetExtension = Path.GetExtension(assetFilename);
+
+            assetFilename = assetFilename.Replace("{version}", version);
+            assetFilename = Path.GetFileNameWithoutExtension(assetFilename);
+
+            var zipExtensions = new[] { ".zip", ".7z" };
+            var extensionMatch = zipExtensions.Contains(Path.GetExtension(asset.browser_download_url) ?? string.Empty, StringComparer.OrdinalIgnoreCase) &&
+                                 zipExtensions.Contains(assetExtension ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+
+            if (!extensionMatch)
+            {
+                return false;
+            }
 
 
             if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
             if (downloadFilename.IndexOf(assetFilename, StringComparison.OrdinalIgnoreCase) != -1)
             {
             {

+ 8 - 0
MediaBrowser.Controller/Channels/Channel.cs

@@ -32,6 +32,14 @@ namespace MediaBrowser.Controller.Channels
             return base.IsVisible(user);
             return base.IsVisible(user);
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            double value = 16;
+            value /= 9;
+
+            return value;
+        }
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsInheritedParentImages
         public override bool SupportsInheritedParentImages
         {
         {

+ 0 - 2
MediaBrowser.Controller/Drawing/IImageProcessor.cs

@@ -117,8 +117,6 @@ namespace MediaBrowser.Controller.Drawing
 
 
         IImageEncoder ImageEncoder { get; set; }
         IImageEncoder ImageEncoder { get; set; }
 
 
-        void SaveImageSize(string path, DateTime imageDateModified, ImageSize size);
-
         bool SupportsTransparency(string path);
         bool SupportsTransparency(string path);
     }
     }
 }
 }

+ 0 - 5
MediaBrowser.Controller/Drawing/ImageHelper.cs

@@ -21,11 +21,6 @@ namespace MediaBrowser.Controller.Drawing
 
 
         public static IImageProcessor ImageProcessor { get; set; }
         public static IImageProcessor ImageProcessor { get; set; }
 
 
-        public static void SaveImageSize(string path, DateTime dateModified, ImageSize size)
-        {
-            ImageProcessor.SaveImageSize(path, dateModified, size);
-        }
-
         private static ImageSize GetSizeEstimate(ImageProcessingOptions options)
         private static ImageSize GetSizeEstimate(ImageProcessingOptions options)
         {
         {
             if (options.Width.HasValue && options.Height.HasValue)
             if (options.Width.HasValue && options.Height.HasValue)

+ 1 - 1
MediaBrowser.Controller/Entities/Audio/AudioPodcast.cs

@@ -15,7 +15,7 @@ namespace MediaBrowser.Controller.Entities.Audio
 
 
         public override double? GetDefaultPrimaryImageAspectRatio()
         public override double? GetDefaultPrimaryImageAspectRatio()
         {
         {
-            return null;
+            return 1;
         }
         }
     }
     }
 }
 }

+ 8 - 0
MediaBrowser.Controller/Entities/BasePluginFolder.cs

@@ -42,5 +42,13 @@ namespace MediaBrowser.Controller.Entities
                 return false;
                 return false;
             }
             }
         }
         }
+
+        //public override double? GetDefaultPrimaryImageAspectRatio()
+        //{
+        //    double value = 16;
+        //    value /= 9;
+
+        //    return value;
+        //}
     }
     }
 }
 }

+ 8 - 0
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -31,6 +31,14 @@ namespace MediaBrowser.Controller.Entities
             PhysicalFolderIds = EmptyGuidArray;
             PhysicalFolderIds = EmptyGuidArray;
         }
         }
 
 
+        //public override double? GetDefaultPrimaryImageAspectRatio()
+        //{
+        //    double value = 16;
+        //    value /= 9;
+
+        //    return value;
+        //}
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsPlayedStatus
         public override bool SupportsPlayedStatus
         {
         {

+ 5 - 0
MediaBrowser.Controller/Entities/GameGenre.cs

@@ -22,6 +22,11 @@ namespace MediaBrowser.Controller.Entities
             return GetUserDataKeys()[0];
             return GetUserDataKeys()[0];
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            return 1;
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the folder containing the item.
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself
         /// If the item is a folder, it returns the folder itself

+ 8 - 0
MediaBrowser.Controller/Entities/GameSystem.cs

@@ -44,6 +44,14 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            double value = 16;
+            value /= 9;
+
+            return value;
+        }
+
         /// <summary>
         /// <summary>
         /// Gets or sets the game system.
         /// Gets or sets the game system.
         /// </summary>
         /// </summary>

+ 5 - 0
MediaBrowser.Controller/Entities/Genre.cs

@@ -25,6 +25,11 @@ namespace MediaBrowser.Controller.Entities
             return GetUserDataKeys()[0];
             return GetUserDataKeys()[0];
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            return 1;
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the folder containing the item.
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself
         /// If the item is a folder, it returns the folder itself

+ 29 - 0
MediaBrowser.Controller/Entities/Photo.cs

@@ -62,6 +62,35 @@ namespace MediaBrowser.Controller.Entities
             return true;
             return true;
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            if (Width.HasValue && Height.HasValue)
+            {
+                double width = Width.Value;
+                double height = Height.Value;
+
+                if (Orientation.HasValue)
+                {
+                    switch (Orientation.Value)
+                    {
+                        case ImageOrientation.LeftBottom:
+                        case ImageOrientation.LeftTop:
+                        case ImageOrientation.RightBottom:
+                        case ImageOrientation.RightTop:
+                            var temp = height;
+                            height = width;
+                            width = temp;
+                            break;
+                    }
+                }
+
+                width /= Height.Value;
+                return width;
+            }
+
+            return base.GetDefaultPrimaryImageAspectRatio();
+        }
+
         public int? Width { get; set; }
         public int? Width { get; set; }
         public int? Height { get; set; }
         public int? Height { get; set; }
         public string CameraMake { get; set; }
         public string CameraMake { get; set; }

+ 5 - 0
MediaBrowser.Controller/Entities/PhotoAlbum.cs

@@ -30,5 +30,10 @@ namespace MediaBrowser.Controller.Entities
                 return false;
                 return false;
             }
             }
         }
         }
+
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            return 1;
+        }
     }
     }
 }
 }

+ 5 - 0
MediaBrowser.Controller/Entities/User.cs

@@ -254,6 +254,11 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            return 1;
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the configuration directory path.
         /// Gets the configuration directory path.
         /// </summary>
         /// </summary>

+ 8 - 0
MediaBrowser.Controller/Entities/UserView.cs

@@ -58,6 +58,14 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        //public override double? GetDefaultPrimaryImageAspectRatio()
+        //{
+        //    double value = 16;
+        //    value /= 9;
+
+        //    return value;
+        //}
+
         public override int GetChildCount(User user)
         public override int GetChildCount(User user)
         {
         {
             return GetChildren(user, true).Count;
             return GetChildren(user, true).Count;

+ 6 - 9
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -238,12 +238,9 @@ namespace MediaBrowser.Controller.Entities
                     {
                     {
                         if (queryParent is UserView)
                         if (queryParent is UserView)
                         {
                         {
-                            return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
-                        }
-                        else
-                        {
-                            return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+                            return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), queryParent, query);
                         }
                         }
+                        return GetResult(queryParent.GetChildren(user, true), queryParent, query);
                     }
                     }
             }
             }
         }
         }
@@ -1681,7 +1678,7 @@ namespace MediaBrowser.Controller.Entities
             return true;
             return true;
         }
         }
 
 
-        private IEnumerable<Folder> GetMediaFolders(User user)
+        private IEnumerable<BaseItem> GetMediaFolders(User user)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -1696,7 +1693,7 @@ namespace MediaBrowser.Controller.Entities
                 .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
                 .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
         }
         }
 
 
-        private List<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes)
+        private List<BaseItem> GetMediaFolders(User user, IEnumerable<string> viewTypes)
         {
         {
             if (user == null)
             if (user == null)
             {
             {
@@ -1717,14 +1714,14 @@ namespace MediaBrowser.Controller.Entities
                 }).ToList();
                 }).ToList();
         }
         }
 
 
-        private List<Folder> GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
+        private List<BaseItem> GetMediaFolders(Folder parent, User user, IEnumerable<string> viewTypes)
         {
         {
             if (parent == null || parent is UserView)
             if (parent == null || parent is UserView)
             {
             {
                 return GetMediaFolders(user, viewTypes);
                 return GetMediaFolders(user, viewTypes);
             }
             }
 
 
-            return new List<Folder> { parent };
+            return new List<BaseItem> { parent };
         }
         }
 
 
         private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query)
         private async Task<QueryResult<BaseItem>> GetLiveTvView(Folder queryParent, User user, InternalItemsQuery query)

+ 8 - 0
MediaBrowser.Controller/Entities/Video.cs

@@ -78,6 +78,14 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            double value = 16;
+            value /= 9;
+
+            return value;
+        }
+
         public override string CreatePresentationUniqueKey()
         public override string CreatePresentationUniqueKey()
         {
         {
             if (!string.IsNullOrWhiteSpace(PrimaryVersionId))
             if (!string.IsNullOrWhiteSpace(PrimaryVersionId))

+ 8 - 0
MediaBrowser.Controller/Entities/Year.cs

@@ -32,6 +32,14 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
+        public override double? GetDefaultPrimaryImageAspectRatio()
+        {
+            double value = 2;
+            value /= 3;
+
+            return value;
+        }
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsAncestors
         public override bool SupportsAncestors
         {
         {

+ 0 - 5
MediaBrowser.Controller/IO/StreamHelper.cs

@@ -6,11 +6,6 @@ namespace MediaBrowser.Controller.IO
 {
 {
     public static class StreamHelper
     public static class StreamHelper
     {
     {
-        public static void CopyTo(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken)
-        {
-            CopyTo(source, destination, bufferSize, null, cancellationToken);
-        }
-
         public static void CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
         public static void CopyTo(Stream source, Stream destination, int bufferSize, Action onStarted, CancellationToken cancellationToken)
         {
         {
             byte[] buffer = new byte[bufferSize];
             byte[] buffer = new byte[bufferSize];

+ 1 - 1
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -383,7 +383,7 @@ namespace MediaBrowser.Controller.LiveTv
         event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
         event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
 
 
         string GetEmbyTvActiveRecordingPath(string id);
         string GetEmbyTvActiveRecordingPath(string id);
-        Task<LiveStream> GetEmbyTvLiveStream(string id);
+        Task<ILiveStream> GetEmbyTvLiveStream(string id);
 
 
         ActiveRecordingInfo GetActiveRecordingInfo(string path);
         ActiveRecordingInfo GetActiveRecordingInfo(string path);
 
 

+ 14 - 1
MediaBrowser.Controller/LiveTv/ITunerHost.cs

@@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="streamId">The stream identifier.</param>
         /// <param name="streamId">The stream identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
         /// <returns>Task&lt;MediaSourceInfo&gt;.</returns>
-        Task<LiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
+        Task<ILiveStream> GetChannelStream(string channelId, string streamId, CancellationToken cancellationToken);
         /// <summary>
         /// <summary>
         /// Gets the channel stream media sources.
         /// Gets the channel stream media sources.
         /// </summary>
         /// </summary>
@@ -56,4 +56,17 @@ namespace MediaBrowser.Controller.LiveTv
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         Task Validate(TunerHostInfo info);
         Task Validate(TunerHostInfo info);
     }
     }
+
+    public interface ILiveStream
+    {
+        Task Open(CancellationToken cancellationToken);
+        Task Close();
+        int ConsumerCount { get; }
+        string OriginalStreamId { get; set; }
+        bool EnableStreamSharing { get; set; }
+        ITunerHost TunerHost { get; set; }
+        MediaSourceInfo OpenedMediaSource { get; set; }
+        string UniqueId { get; }
+        List<string> SharedStreamIds { get; }
+    }
 }
 }

+ 0 - 87
MediaBrowser.Controller/LiveTv/LiveStream.cs

@@ -1,87 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.System;
-
-namespace MediaBrowser.Controller.LiveTv
-{
-    public class LiveStream
-    {
-        public MediaSourceInfo OriginalMediaSource { get; set; }
-        public MediaSourceInfo OpenedMediaSource { get; set; }
-        public int ConsumerCount
-        {
-            get { return SharedStreamIds.Count; }
-        }
-        public ITunerHost TunerHost { get; set; }
-        public string OriginalStreamId { get; set; }
-        public bool EnableStreamSharing { get; set; }
-        public string UniqueId = Guid.NewGuid().ToString("N");
-
-        public List<string> SharedStreamIds = new List<string>();
-        protected readonly IEnvironmentInfo Environment;
-        protected readonly IFileSystem FileSystem;
-        const int StreamCopyToBufferSize = 81920;
-
-        public LiveStream(MediaSourceInfo mediaSource, IEnvironmentInfo environment, IFileSystem fileSystem)
-        {
-            OriginalMediaSource = mediaSource;
-            Environment = environment;
-            FileSystem = fileSystem;
-            OpenedMediaSource = mediaSource;
-            EnableStreamSharing = true;
-        }
-
-        public Task Open(CancellationToken cancellationToken)
-        {
-            return OpenInternal(cancellationToken);
-        }
-
-        protected virtual Task OpenInternal(CancellationToken cancellationToken)
-        {
-            return Task.FromResult(true);
-        }
-
-        public virtual Task Close()
-        {
-            return Task.FromResult(true);
-        }
-
-        protected Stream GetInputStream(string path, bool allowAsyncFileRead)
-        {
-            var fileOpenOptions = FileOpenOptions.SequentialScan;
-
-            if (allowAsyncFileRead)
-            {
-                fileOpenOptions |= FileOpenOptions.Asynchronous;
-            }
-
-            return FileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, fileOpenOptions);
-        }
-
-        protected async Task DeleteTempFile(string path, int retryCount = 0)
-        {
-            try
-            {
-                FileSystem.DeleteFile(path);
-                return;
-            }
-            catch
-            {
-
-            }
-
-            if (retryCount > 20)
-            {
-                return;
-            }
-
-            await Task.Delay(500).ConfigureAwait(false);
-            await DeleteTempFile(path, retryCount + 1).ConfigureAwait(false);
-        }
-    }
-}

+ 0 - 1
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -145,7 +145,6 @@
     <Compile Include="Library\UserDataSaveEventArgs.cs" />
     <Compile Include="Library\UserDataSaveEventArgs.cs" />
     <Compile Include="LiveTv\IListingsProvider.cs" />
     <Compile Include="LiveTv\IListingsProvider.cs" />
     <Compile Include="LiveTv\ITunerHost.cs" />
     <Compile Include="LiveTv\ITunerHost.cs" />
-    <Compile Include="LiveTv\LiveStream.cs" />
     <Compile Include="LiveTv\RecordingGroup.cs" />
     <Compile Include="LiveTv\RecordingGroup.cs" />
     <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
     <Compile Include="LiveTv\RecordingStatusChangedEventArgs.cs" />
     <Compile Include="LiveTv\ILiveTvRecording.cs" />
     <Compile Include="LiveTv\ILiveTvRecording.cs" />

+ 41 - 50
MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs

@@ -346,7 +346,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 "Constrained High"
                 "Constrained High"
             };
             };
 
 
-            return Array.FindIndex(list.ToArray(), t => string.Equals(t, profile, StringComparison.OrdinalIgnoreCase));
+            // strip spaces because they may be stripped out on the query string
+            return Array.FindIndex(list.ToArray(), t => string.Equals(t.Replace(" ", ""), profile.Replace(" ", ""), StringComparison.OrdinalIgnoreCase));
         }
         }
 
 
         public string GetInputPathArgument(EncodingJobInfo state)
         public string GetInputPathArgument(EncodingJobInfo state)
@@ -529,7 +530,8 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
         {
             var seconds = Math.Round(TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds);
             var seconds = Math.Round(TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds);
 
 
-            var setPtsParam = state.CopyTimestamps
+            // hls always copies timestamps
+            var setPtsParam = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive
                 ? string.Empty
                 ? string.Empty
                 : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(_usCulture));
                 : string.Format(",setpts=PTS -{0}/TB", seconds.ToString(_usCulture));
 
 
@@ -691,22 +693,26 @@ namespace MediaBrowser.Controller.MediaEncoding
                 param += string.Format(" -r {0}", framerate.Value.ToString(_usCulture));
                 param += string.Format(" -r {0}", framerate.Value.ToString(_usCulture));
             }
             }
 
 
-            var request = state.BaseRequest;
+            var targetVideoCodec = state.ActualOutputVideoCodec;
 
 
-            if (!string.IsNullOrEmpty(request.Profile))
+            var request = state.BaseRequest;
+            var profile = state.GetRequestedProfiles(targetVideoCodec).FirstOrDefault();
+            if (!string.IsNullOrEmpty(profile))
             {
             {
                 if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
                 if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) &&
                     !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
                     !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
                     !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
                     !string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
                 {
                 {
                     // not supported by h264_omx
                     // not supported by h264_omx
-                    param += " -profile:v " + request.Profile;
+                    param += " -profile:v " + profile;
                 }
                 }
             }
             }
 
 
-            if (!string.IsNullOrEmpty(request.Level))
+            var level = state.GetRequestedLevel(targetVideoCodec);
+
+            if (!string.IsNullOrEmpty(level))
             {
             {
-                var level = NormalizeTranscodingLevel(state.OutputVideoCodec, request.Level);
+                level = NormalizeTranscodingLevel(state.OutputVideoCodec, level);
 
 
                 // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
                 // h264_qsv and h264_nvenc expect levels to be expressed as a decimal. libx264 supports decimal and non-decimal format
                 // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307                
                 // also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307                
@@ -756,7 +762,6 @@ namespace MediaBrowser.Controller.MediaEncoding
                 {
                 {
                     param += " -level " + level;
                     param += " -level " + level;
                 }
                 }
-
             }
             }
 
 
             if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
@@ -796,7 +801,7 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
             if (videoStream.IsInterlaced)
             if (videoStream.IsInterlaced)
             {
             {
-                if (request.DeInterlace)
+                if (state.DeInterlace(videoStream.Codec, false))
                 {
                 {
                     return false;
                     return false;
                 }
                 }
@@ -828,23 +833,27 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             // Source and target codecs must match
             // Source and target codecs must match
-            if (string.IsNullOrEmpty(videoStream.Codec) || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase))
+            if (string.IsNullOrWhiteSpace(videoStream.Codec) || !state.SupportedVideoCodecs.Contains(videoStream.Codec, StringComparer.OrdinalIgnoreCase))
             {
             {
                 return false;
                 return false;
             }
             }
 
 
+            var requestedProfiles = state.GetRequestedProfiles(videoStream.Codec);
+
             // If client is requesting a specific video profile, it must match the source
             // If client is requesting a specific video profile, it must match the source
-            if (!string.IsNullOrEmpty(request.Profile))
+            if (requestedProfiles.Length > 0)
             {
             {
-                if (string.IsNullOrEmpty(videoStream.Profile))
+                if (string.IsNullOrWhiteSpace(videoStream.Profile))
                 {
                 {
                     //return false;
                     //return false;
                 }
                 }
 
 
-                if (!string.IsNullOrEmpty(videoStream.Profile) && !string.Equals(request.Profile, videoStream.Profile, StringComparison.OrdinalIgnoreCase))
+                var requestedProfile = requestedProfiles[0];
+                // strip spaces because they may be stripped out on the query string as well
+                if (!string.IsNullOrWhiteSpace(videoStream.Profile) && !requestedProfiles.Contains(videoStream.Profile.Replace(" ", ""), StringComparer.OrdinalIgnoreCase))
                 {
                 {
                     var currentScore = GetVideoProfileScore(videoStream.Profile);
                     var currentScore = GetVideoProfileScore(videoStream.Profile);
-                    var requestedScore = GetVideoProfileScore(request.Profile);
+                    var requestedScore = GetVideoProfileScore(requestedProfile);
 
 
                     if (currentScore == -1 || currentScore > requestedScore)
                     if (currentScore == -1 || currentScore > requestedScore)
                     {
                     {
@@ -909,11 +918,12 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
 
 
             // If a specific level was requested, the source must match or be less than
             // If a specific level was requested, the source must match or be less than
-            if (!string.IsNullOrEmpty(request.Level))
+            var level = state.GetRequestedLevel(videoStream.Codec);
+            if (!string.IsNullOrEmpty(level))
             {
             {
                 double requestLevel;
                 double requestLevel;
 
 
-                if (double.TryParse(request.Level, NumberStyles.Any, _usCulture, out requestLevel))
+                if (double.TryParse(level, NumberStyles.Any, _usCulture, out requestLevel))
                 {
                 {
                     if (!videoStream.Level.HasValue)
                     if (!videoStream.Level.HasValue)
                     {
                     {
@@ -1074,7 +1084,8 @@ namespace MediaBrowser.Controller.MediaEncoding
                 }
                 }
             }
             }
 
 
-            if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !state.CopyTimestamps)
+            var isCopyingTimestamps = state.CopyTimestamps || state.TranscodingType != TranscodingJobType.Progressive;
+            if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !isCopyingTimestamps)
             {
             {
                 var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds;
                 var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds;
 
 
@@ -1357,9 +1368,10 @@ namespace MediaBrowser.Controller.MediaEncoding
                 filters.Add("hwupload");
                 filters.Add("hwupload");
             }
             }
 
 
-            if (state.DeInterlace && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
+            if (state.DeInterlace("h264", true) && !string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
             {
             {
-                if (string.Equals(options.DeinterlaceMethod, "bobandweave", StringComparison.OrdinalIgnoreCase))
+                // If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle
+                if (string.Equals(options.DeinterlaceMethod, "bobandweave", StringComparison.OrdinalIgnoreCase) && (state.VideoStream.RealFrameRate ?? 60) <= 30)
                 {
                 {
                     filters.Add("yadif=1:-1:0");
                     filters.Add("yadif=1:-1:0");
                 }
                 }
@@ -1523,11 +1535,18 @@ namespace MediaBrowser.Controller.MediaEncoding
         /// <returns>System.Int32.</returns>
         /// <returns>System.Int32.</returns>
         public int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, bool isWebm)
         public int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, bool isWebm)
         {
         {
-            var threads = GetNumberOfThreadsInternal(state, encodingOptions, isWebm);
+            if (isWebm)
+            {
+                // Recommended per docs
+                return Math.Max(Environment.ProcessorCount - 1, 2);
+            }
+
+            var threads = state.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount;
 
 
-            if (state.BaseRequest.CpuCoreLimit.HasValue && state.BaseRequest.CpuCoreLimit.Value > 0)
+            // Automatic
+            if (threads <= 0 || threads >= Environment.ProcessorCount)
             {
             {
-                threads = Math.Min(threads, state.BaseRequest.CpuCoreLimit.Value);
+                return 0;
             }
             }
 
 
             return threads;
             return threads;
@@ -1799,11 +1818,6 @@ namespace MediaBrowser.Controller.MediaEncoding
                     state.InternalSubtitleStreamOffset = mediaStreams.Where(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal).ToList().IndexOf(state.SubtitleStream);
                     state.InternalSubtitleStreamOffset = mediaStreams.Where(i => i.Type == MediaStreamType.Subtitle && !i.IsExternal).ToList().IndexOf(state.SubtitleStream);
                 }
                 }
 
 
-                if (state.VideoStream != null && state.VideoStream.IsInterlaced)
-                {
-                    state.DeInterlace = true;
-                }
-
                 EnforceResolutionLimit(state);
                 EnforceResolutionLimit(state);
 
 
                 NormalizeSubtitleEmbed(state);
                 NormalizeSubtitleEmbed(state);
@@ -1954,29 +1968,6 @@ namespace MediaBrowser.Controller.MediaEncoding
             return null;
             return null;
         }
         }
 
 
-        /// <summary>
-        /// Gets the number of threads.
-        /// </summary>
-        /// <returns>System.Int32.</returns>
-        private int GetNumberOfThreadsInternal(EncodingJobInfo state, EncodingOptions encodingOptions, bool isWebm)
-        {
-            var threads = encodingOptions.EncodingThreadCount;
-
-            if (isWebm)
-            {
-                // Recommended per docs
-                return Math.Max(Environment.ProcessorCount - 1, 2);
-            }
-
-            // Automatic
-            if (threads == -1)
-            {
-                return 0;
-            }
-
-            return threads;
-        }
-
         public string GetSubtitleEmbedArguments(EncodingJobInfo state)
         public string GetSubtitleEmbedArguments(EncodingJobInfo state)
         {
         {
             if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)
             if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)

+ 145 - 14
MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs

@@ -160,7 +160,97 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
         public int? OutputAudioBitrate;
         public int? OutputAudioBitrate;
         public int? OutputAudioChannels;
         public int? OutputAudioChannels;
-        public bool DeInterlace { get; set; }
+
+        public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
+        {
+            var videoStream = VideoStream;
+            var isInputInterlaced = videoStream != null && videoStream.IsInterlaced;
+
+            if (!isInputInterlaced)
+            {
+                return false;
+            }
+
+            // Support general param
+            if (BaseRequest.DeInterlace)
+            {
+                return true;
+            }
+
+            if (!string.IsNullOrWhiteSpace(videoCodec))
+            {
+                if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+            }
+
+            if (forceDeinterlaceIfSourceIsInterlaced)
+            {
+                if (isInputInterlaced)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public string[] GetRequestedProfiles(string codec)
+        {
+            if (!string.IsNullOrWhiteSpace(BaseRequest.Profile))
+            {
+                return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
+            }
+
+            if (!string.IsNullOrWhiteSpace(codec))
+            {
+                var profile = BaseRequest.GetOption(codec, "profile");
+
+                if (!string.IsNullOrWhiteSpace(profile))
+                {
+                    return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
+                }
+            }
+
+            return new string[] { };
+        }
+
+        public string GetRequestedLevel(string codec)
+        {
+            if (!string.IsNullOrWhiteSpace(BaseRequest.Level))
+            {
+                return BaseRequest.Level;
+            }
+
+            if (!string.IsNullOrWhiteSpace(codec))
+            {
+                return BaseRequest.GetOption(codec, "level");
+            }
+
+            return null;
+        }
+
+        public int? GetRequestedMaxRefFrames(string codec)
+        {
+            if (!string.IsNullOrWhiteSpace(BaseRequest.Level))
+            {
+                return BaseRequest.MaxRefFrames;
+            }
+
+            if (!string.IsNullOrWhiteSpace(codec))
+            {
+                var value = BaseRequest.GetOption(codec, "maxrefframes");
+                int result;
+                if (!string.IsNullOrWhiteSpace(value) && int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
+                {
+                    return result;
+                }
+            }
+
+            return null;
+        }
+
         public bool IsVideoRequest { get; set; }
         public bool IsVideoRequest { get; set; }
         public TranscodingJobType TranscodingType { get; set; }
         public TranscodingJobType TranscodingType { get; set; }
 
 
@@ -169,7 +259,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             _logger = logger;
             _logger = logger;
             TranscodingType = jobType;
             TranscodingType = jobType;
             RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
             RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            PlayableStreamFileNames = new string[]{};
+            PlayableStreamFileNames = new string[] { };
             SupportedAudioCodecs = new List<string>();
             SupportedAudioCodecs = new List<string>();
             SupportedVideoCodecs = new List<string>();
             SupportedVideoCodecs = new List<string>();
             SupportedSubtitleCodecs = new List<string>();
             SupportedSubtitleCodecs = new List<string>();
@@ -319,12 +409,19 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
         {
             get
             get
             {
             {
-                var stream = VideoStream;
-                var request = BaseRequest;
+                if (BaseRequest.Static)
+                {
+                    return VideoStream == null ? null : VideoStream.Level;
+                }
 
 
-                return !string.IsNullOrEmpty(request.Level) && !request.Static
-                    ? double.Parse(request.Level, CultureInfo.InvariantCulture)
-                    : stream == null ? null : stream.Level;
+                var level = GetRequestedLevel(ActualOutputVideoCodec);
+                double result;
+                if (!string.IsNullOrWhiteSpace(level) && double.TryParse(level, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
+                {
+                    return result;
+                }
+
+                return null;
             }
             }
         }
         }
 
 
@@ -348,8 +445,12 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
         {
             get
             get
             {
             {
-                var stream = VideoStream;
-                return stream == null || !BaseRequest.Static ? null : stream.RefFrames;
+                if (BaseRequest.Static)
+                {
+                    return VideoStream == null ? null : VideoStream.RefFrames;
+                }
+
+                return null;
             }
             }
         }
         }
 
 
@@ -404,10 +505,18 @@ namespace MediaBrowser.Controller.MediaEncoding
         {
         {
             get
             get
             {
             {
-                var stream = VideoStream;
-                return !string.IsNullOrEmpty(BaseRequest.Profile) && !BaseRequest.Static
-                    ? BaseRequest.Profile
-                    : stream == null ? null : stream.Profile;
+                if (BaseRequest.Static)
+                {
+                    return VideoStream == null ? null : VideoStream.Profile;
+                }
+
+                var requestedProfile = GetRequestedProfiles(ActualOutputVideoCodec).FirstOrDefault();
+                if (!string.IsNullOrWhiteSpace(requestedProfile))
+                {
+                    return requestedProfile;
+                }
+
+                return null;
             }
             }
         }
         }
 
 
@@ -435,6 +544,28 @@ namespace MediaBrowser.Controller.MediaEncoding
             }
             }
         }
         }
 
 
+        public string ActualOutputVideoCodec
+        {
+            get
+            {
+                var codec = OutputVideoCodec;
+
+                if (string.Equals(codec, "copy", StringComparison.OrdinalIgnoreCase))
+                {
+                    var stream = VideoStream;
+
+                    if (stream != null)
+                    {
+                        return stream.Codec;
+                    }
+
+                    return null;
+                }
+
+                return codec;
+            }
+        }
+
         public bool? IsTargetInterlaced
         public bool? IsTargetInterlaced
         {
         {
             get
             get
@@ -444,7 +575,7 @@ namespace MediaBrowser.Controller.MediaEncoding
                     return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
                     return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
                 }
                 }
 
 
-                if (DeInterlace)
+                if (DeInterlace(ActualOutputVideoCodec, true))
                 {
                 {
                     return false;
                     return false;
                 }
                 }

+ 36 - 10
MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs

@@ -1,4 +1,7 @@
-using System.Globalization;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Dlna;
 using MediaBrowser.Model.Services;
 using MediaBrowser.Model.Services;
 
 
@@ -37,18 +40,16 @@ namespace MediaBrowser.Controller.MediaEncoding
             MaxWidth = info.MaxWidth;
             MaxWidth = info.MaxWidth;
             MaxHeight = info.MaxHeight;
             MaxHeight = info.MaxHeight;
             MaxFramerate = info.MaxFramerate;
             MaxFramerate = info.MaxFramerate;
-            Profile = info.VideoProfile;
             ItemId = info.ItemId;
             ItemId = info.ItemId;
             MediaSourceId = info.MediaSourceId;
             MediaSourceId = info.MediaSourceId;
-            AudioCodec = info.TargetAudioCodec;
+            AudioCodec = info.TargetAudioCodec.FirstOrDefault();
             MaxAudioChannels = info.MaxAudioChannels;
             MaxAudioChannels = info.MaxAudioChannels;
             AudioBitRate = info.AudioBitrate;
             AudioBitRate = info.AudioBitrate;
             AudioSampleRate = info.TargetAudioSampleRate;
             AudioSampleRate = info.TargetAudioSampleRate;
             DeviceProfile = deviceProfile;
             DeviceProfile = deviceProfile;
-            VideoCodec = info.TargetVideoCodec;
+            VideoCodec = info.TargetVideoCodec.FirstOrDefault();
             VideoBitRate = info.VideoBitrate;
             VideoBitRate = info.VideoBitrate;
             AudioStreamIndex = info.AudioStreamIndex;
             AudioStreamIndex = info.AudioStreamIndex;
-            MaxRefFrames = info.MaxRefFrames;
             MaxVideoBitDepth = info.MaxVideoBitDepth;
             MaxVideoBitDepth = info.MaxVideoBitDepth;
             SubtitleMethod = info.SubtitleDeliveryMethod;
             SubtitleMethod = info.SubtitleDeliveryMethod;
             Context = info.Context;
             Context = info.Context;
@@ -58,11 +59,7 @@ namespace MediaBrowser.Controller.MediaEncoding
             {
             {
                 SubtitleStreamIndex = info.SubtitleStreamIndex;
                 SubtitleStreamIndex = info.SubtitleStreamIndex;
             }
             }
-
-            if (info.VideoLevel.HasValue)
-            {
-                Level = info.VideoLevel.Value.ToString(_usCulture);
-            }
+            StreamOptions = info.StreamOptions;
         }
         }
     }
     }
 
 
@@ -224,12 +221,41 @@ namespace MediaBrowser.Controller.MediaEncoding
 
 
         public EncodingContext Context { get; set; }
         public EncodingContext Context { get; set; }
 
 
+        public void SetOption(string qualifier, string name, string value)
+        {
+            SetOption(qualifier + "-" + name, value);
+        }
+
+        public Dictionary<string, string> StreamOptions { get; set; }
+
+        public void SetOption(string name, string value)
+        {
+            StreamOptions[name] = value;
+        }
+
+        public string GetOption(string qualifier, string name)
+        {
+            return GetOption(qualifier + "-" + name);
+        }
+
+        public string GetOption(string name)
+        {
+            string value;
+            if (StreamOptions.TryGetValue(name, out value))
+            {
+                return value;
+            }
+
+            return null;
+        }
+
         public BaseEncodingJobOptions()
         public BaseEncodingJobOptions()
         {
         {
             EnableAutoStreamCopy = true;
             EnableAutoStreamCopy = true;
             AllowVideoStreamCopy = true;
             AllowVideoStreamCopy = true;
             AllowAudioStreamCopy = true;
             AllowAudioStreamCopy = true;
             Context = EncodingContext.Streaming;
             Context = EncodingContext.Streaming;
+            StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
         }
     }
     }
 }
 }

+ 1 - 1
MediaBrowser.Controller/TV/ITVSeriesManager.cs

@@ -15,6 +15,6 @@ namespace MediaBrowser.Controller.TV
         /// <summary>
         /// <summary>
         /// Gets the next up.
         /// Gets the next up.
         /// </summary>
         /// </summary>
-        QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders, DtoOptions options);
+        QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<BaseItem> parentsFolders, DtoOptions options);
     }
     }
 }
 }

+ 2 - 1
MediaBrowser.Model/Configuration/EncodingOptions.cs

@@ -25,7 +25,8 @@ namespace MediaBrowser.Model.Configuration
             EnableThrottling = true;
             EnableThrottling = true;
             ThrottleDelaySeconds = 180;
             ThrottleDelaySeconds = 180;
             EncodingThreadCount = -1;
             EncodingThreadCount = -1;
-            VaapiDevice = "/dev/dri/card0";
+            // This is a DRM device that is almost guaranteed to be there on every intel platform, plus it's the default one in ffmpeg if you don't specify anything
+            VaapiDevice = "/dev/dri/renderD128";
             H264Crf = 23;
             H264Crf = 23;
             EnableHardwareEncoding = true;
             EnableHardwareEncoding = true;
             EnableSubtitleExtraction = true;
             EnableSubtitleExtraction = true;

+ 3 - 0
MediaBrowser.Model/Configuration/LibraryOptions.cs

@@ -30,6 +30,8 @@
         /// <value>The metadata country code.</value>
         /// <value>The metadata country code.</value>
         public string MetadataCountryCode { get; set; }
         public string MetadataCountryCode { get; set; }
 
 
+        public string SeasonZeroDisplayName { get; set; }
+
         public LibraryOptions()
         public LibraryOptions()
         {
         {
             EnablePhotos = true;
             EnablePhotos = true;
@@ -37,6 +39,7 @@
             PathInfos = new MediaPathInfo[] { };
             PathInfos = new MediaPathInfo[] { };
             EnableInternetProviders = true;
             EnableInternetProviders = true;
             EnableAutomaticSeriesGrouping = true;
             EnableAutomaticSeriesGrouping = true;
+            SeasonZeroDisplayName = "Specials";
         }
         }
     }
     }
 
 

+ 2 - 8
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -77,12 +77,6 @@ namespace MediaBrowser.Model.Configuration
         public string MetadataPath { get; set; }
         public string MetadataPath { get; set; }
         public string MetadataNetworkPath { get; set; }
         public string MetadataNetworkPath { get; set; }
 
 
-        /// <summary>
-        /// Gets or sets the display name of the season zero.
-        /// </summary>
-        /// <value>The display name of the season zero.</value>
-        public string SeasonZeroDisplayName { get; set; }
-
         /// <summary>
         /// <summary>
         /// Gets or sets the preferred metadata language.
         /// Gets or sets the preferred metadata language.
         /// </summary>
         /// </summary>
@@ -187,6 +181,8 @@ namespace MediaBrowser.Model.Configuration
         public string[] CodecsUsed { get; set; }
         public string[] CodecsUsed { get; set; }
         public bool EnableChannelView { get; set; }
         public bool EnableChannelView { get; set; }
         public bool EnableExternalContentInSuggestions { get; set; }
         public bool EnableExternalContentInSuggestions { get; set; }
+        public bool RequireHttps { get; set; }
+        public bool IsBehindProxy { get; set; }
 
 
         public int ImageExtractionTimeoutMs { get; set; }
         public int ImageExtractionTimeoutMs { get; set; }
 
 
@@ -239,8 +235,6 @@ namespace MediaBrowser.Model.Configuration
             SortRemoveCharacters = new[] { ",", "&", "-", "{", "}", "'" };
             SortRemoveCharacters = new[] { ",", "&", "-", "{", "}", "'" };
             SortRemoveWords = new[] { "the", "a", "an" };
             SortRemoveWords = new[] { "the", "a", "an" };
 
 
-            SeasonZeroDisplayName = "Specials";
-
             UICulture = "en-us";
             UICulture = "en-us";
 
 
             MetadataOptions = new[]
             MetadataOptions = new[]

+ 19 - 2
MediaBrowser.Model/Dlna/CodecProfile.cs

@@ -36,7 +36,12 @@ namespace MediaBrowser.Model.Dlna
             return ContainerProfile.ContainsContainer(Container, container);
             return ContainerProfile.ContainsContainer(Container, container);
         }
         }
 
 
-        public bool ContainsCodec(string codec, string container)
+        public bool ContainsAnyCodec(string codec, string container)
+        {
+            return ContainsAnyCodec(ContainerProfile.SplitValue(codec), container);
+        }
+
+        public bool ContainsAnyCodec(string[] codec, string container)
         {
         {
             if (!ContainsContainer(container))
             if (!ContainsContainer(container))
             {
             {
@@ -44,8 +49,20 @@ namespace MediaBrowser.Model.Dlna
             }
             }
 
 
             var codecs = GetCodecs();
             var codecs = GetCodecs();
+            if (codecs.Length == 0)
+            {
+                return true;
+            }
+
+            foreach (var val in codec)
+            {
+                if (ListHelper.ContainsIgnoreCase(codecs, val))
+                {
+                    return true;
+                }
+            }
 
 
-            return codecs.Length == 0 || ListHelper.ContainsIgnoreCase(codecs, ContainerProfile.SplitValue(codec)[0]);
+            return false;
         }
         }
     }
     }
 }
 }

+ 136 - 57
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -283,7 +283,7 @@ namespace MediaBrowser.Model.Dlna
                     var conditions = new List<ProfileCondition>();
                     var conditions = new List<ProfileCondition>();
                     foreach (CodecProfile i in options.Profile.CodecProfiles)
                     foreach (CodecProfile i in options.Profile.CodecProfiles)
                     {
                     {
-                        if (i.Type == CodecType.Audio && i.ContainsCodec(audioCodec, item.Container))
+                        if (i.Type == CodecType.Audio && i.ContainsAnyCodec(audioCodec, item.Container))
                         {
                         {
                             bool applyConditions = true;
                             bool applyConditions = true;
                             foreach (ProfileCondition applyCondition in i.ApplyConditions)
                             foreach (ProfileCondition applyCondition in i.ApplyConditions)
@@ -375,7 +375,7 @@ namespace MediaBrowser.Model.Dlna
                 var audioCodecProfiles = new List<CodecProfile>();
                 var audioCodecProfiles = new List<CodecProfile>();
                 foreach (CodecProfile i in options.Profile.CodecProfiles)
                 foreach (CodecProfile i in options.Profile.CodecProfiles)
                 {
                 {
-                    if (i.Type == CodecType.Audio && i.ContainsCodec(transcodingProfile.AudioCodec, transcodingProfile.Container))
+                    if (i.Type == CodecType.Audio && i.ContainsAnyCodec(transcodingProfile.AudioCodec, transcodingProfile.Container))
                     {
                     {
                         audioCodecProfiles.Add(i);
                         audioCodecProfiles.Add(i);
                     }
                     }
@@ -406,7 +406,7 @@ namespace MediaBrowser.Model.Dlna
                     }
                     }
                 }
                 }
 
 
-                ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
+                ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, false);
 
 
                 // Honor requested max channels
                 // Honor requested max channels
                 if (options.MaxAudioChannels.HasValue)
                 if (options.MaxAudioChannels.HasValue)
@@ -769,24 +769,36 @@ namespace MediaBrowser.Model.Dlna
                 playlistItem.AudioStreamIndex = audioStreamIndex;
                 playlistItem.AudioStreamIndex = audioStreamIndex;
                 ConditionProcessor conditionProcessor = new ConditionProcessor();
                 ConditionProcessor conditionProcessor = new ConditionProcessor();
 
 
-                var videoTranscodingConditions = new List<ProfileCondition>();
+                var isFirstAppliedCodecProfile = true;
                 foreach (CodecProfile i in options.Profile.CodecProfiles)
                 foreach (CodecProfile i in options.Profile.CodecProfiles)
                 {
                 {
-                    if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec, transcodingProfile.Container))
+                    if (i.Type == CodecType.Video && i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container))
                     {
                     {
                         bool applyConditions = true;
                         bool applyConditions = true;
                         foreach (ProfileCondition applyCondition in i.ApplyConditions)
                         foreach (ProfileCondition applyCondition in i.ApplyConditions)
                         {
                         {
-                            bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream);
-                            int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate;
-                            int? audioChannels = audioStream == null ? null : audioStream.Channels;
-                            string audioProfile = audioStream == null ? null : audioStream.Profile;
-                            int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
-                            int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
+                            int? width = videoStream == null ? null : videoStream.Width;
+                            int? height = videoStream == null ? null : videoStream.Height;
+                            int? bitDepth = videoStream == null ? null : videoStream.BitDepth;
+                            int? videoBitrate = videoStream == null ? null : videoStream.BitRate;
+                            double? videoLevel = videoStream == null ? null : videoStream.Level;
+                            string videoProfile = videoStream == null ? null : videoStream.Profile;
+                            float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate;
+                            bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic;
+                            bool? isInterlaced = videoStream == null ? (bool?)null : videoStream.IsInterlaced;
+                            string videoCodecTag = videoStream == null ? null : videoStream.CodecTag;
+                            bool? isAvc = videoStream == null ? null : videoStream.IsAVC;
 
 
-                            if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))
+                            TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
+                            int? packetLength = videoStream == null ? null : videoStream.PacketLength;
+                            int? refFrames = videoStream == null ? null : videoStream.RefFrames;
+
+                            int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
+                            int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
+
+                            if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
                             {
                             {
-                                LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item);
+                                LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item);
                                 applyConditions = false;
                                 applyConditions = false;
                                 break;
                                 break;
                             }
                             }
@@ -794,44 +806,35 @@ namespace MediaBrowser.Model.Dlna
 
 
                         if (applyConditions)
                         if (applyConditions)
                         {
                         {
-                            foreach (ProfileCondition c in i.Conditions)
+                            var transcodingVideoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
+                            foreach (var transcodingVideoCodec in transcodingVideoCodecs)
                             {
                             {
-                                videoTranscodingConditions.Add(c);
+                                if (i.ContainsAnyCodec(transcodingVideoCodec, transcodingProfile.Container))
+                                {
+                                    ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingVideoCodec, !isFirstAppliedCodecProfile);
+                                    isFirstAppliedCodecProfile = false;
+                                }
                             }
                             }
-                            break;
                         }
                         }
                     }
                     }
                 }
                 }
-                ApplyTranscodingConditions(playlistItem, videoTranscodingConditions);
 
 
                 var audioTranscodingConditions = new List<ProfileCondition>();
                 var audioTranscodingConditions = new List<ProfileCondition>();
                 foreach (CodecProfile i in options.Profile.CodecProfiles)
                 foreach (CodecProfile i in options.Profile.CodecProfiles)
                 {
                 {
-                    if (i.Type == CodecType.VideoAudio && i.ContainsCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container))
+                    if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container))
                     {
                     {
                         bool applyConditions = true;
                         bool applyConditions = true;
                         foreach (ProfileCondition applyCondition in i.ApplyConditions)
                         foreach (ProfileCondition applyCondition in i.ApplyConditions)
                         {
                         {
-                            int? width = videoStream == null ? null : videoStream.Width;
-                            int? height = videoStream == null ? null : videoStream.Height;
-                            int? bitDepth = videoStream == null ? null : videoStream.BitDepth;
-                            int? videoBitrate = videoStream == null ? null : videoStream.BitRate;
-                            double? videoLevel = videoStream == null ? null : videoStream.Level;
-                            string videoProfile = videoStream == null ? null : videoStream.Profile;
-                            float? videoFramerate = videoStream == null ? null : videoStream.AverageFrameRate ?? videoStream.AverageFrameRate;
-                            bool? isAnamorphic = videoStream == null ? null : videoStream.IsAnamorphic;
-                            bool? isInterlaced = videoStream == null ? (bool?)null : videoStream.IsInterlaced;
-                            string videoCodecTag = videoStream == null ? null : videoStream.CodecTag;
-                            bool? isAvc = videoStream == null ? null : videoStream.IsAVC;
-
-                            TransportStreamTimestamp? timestamp = videoStream == null ? TransportStreamTimestamp.None : item.Timestamp;
-                            int? packetLength = videoStream == null ? null : videoStream.PacketLength;
-                            int? refFrames = videoStream == null ? null : videoStream.RefFrames;
-
-                            int? numAudioStreams = item.GetStreamCount(MediaStreamType.Audio);
-                            int? numVideoStreams = item.GetStreamCount(MediaStreamType.Video);
+                            bool? isSecondaryAudio = audioStream == null ? null : item.IsSecondaryAudio(audioStream);
+                            int? inputAudioBitrate = audioStream == null ? null : audioStream.BitRate;
+                            int? audioChannels = audioStream == null ? null : audioStream.Channels;
+                            string audioProfile = audioStream == null ? null : audioStream.Profile;
+                            int? inputAudioSampleRate = audioStream == null ? null : audioStream.SampleRate;
+                            int? inputAudioBitDepth = audioStream == null ? null : audioStream.BitDepth;
 
 
-                            if (!conditionProcessor.IsVideoConditionSatisfied(applyCondition, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isInterlaced, refFrames, numVideoStreams, numAudioStreams, videoCodecTag, isAvc))
+                            if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))
                             {
                             {
                                 LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item);
                                 LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item);
                                 applyConditions = false;
                                 applyConditions = false;
@@ -878,7 +881,7 @@ namespace MediaBrowser.Model.Dlna
                 }
                 }
 
 
                 // Do this after initial values are set to account for greater than/less than conditions
                 // Do this after initial values are set to account for greater than/less than conditions
-                ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
+                ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, false);
             }
             }
 
 
             playlistItem.TranscodeReasons = transcodeReasons;
             playlistItem.TranscodeReasons = transcodeReasons;
@@ -896,8 +899,10 @@ namespace MediaBrowser.Model.Dlna
             return 192000;
             return 192000;
         }
         }
 
 
-        private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream)
+        private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string[] targetAudioCodecs, MediaStream audioStream)
         {
         {
+            var targetAudioCodec = targetAudioCodecs.Length == 0 ? null : targetAudioCodecs[0];
+
             int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
             int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? GetDefaultAudioBitrateIfUnknown(audioStream);
 
 
             // Reduce the bitrate if we're downmixing
             // Reduce the bitrate if we're downmixing
@@ -1061,7 +1066,7 @@ namespace MediaBrowser.Model.Dlna
             conditions = new List<ProfileCondition>();
             conditions = new List<ProfileCondition>();
             foreach (CodecProfile i in profile.CodecProfiles)
             foreach (CodecProfile i in profile.CodecProfiles)
             {
             {
-                if (i.Type == CodecType.Video && i.ContainsCodec(videoCodec, container))
+                if (i.Type == CodecType.Video && i.ContainsAnyCodec(videoCodec, container))
                 {
                 {
                     bool applyConditions = true;
                     bool applyConditions = true;
                     foreach (ProfileCondition applyCondition in i.ApplyConditions)
                     foreach (ProfileCondition applyCondition in i.ApplyConditions)
@@ -1117,7 +1122,7 @@ namespace MediaBrowser.Model.Dlna
 
 
                 foreach (CodecProfile i in profile.CodecProfiles)
                 foreach (CodecProfile i in profile.CodecProfiles)
                 {
                 {
-                    if (i.Type == CodecType.VideoAudio && i.ContainsCodec(audioCodec, container))
+                    if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(audioCodec, container))
                     {
                     {
                         bool applyConditions = true;
                         bool applyConditions = true;
                         foreach (ProfileCondition applyCondition in i.ApplyConditions)
                         foreach (ProfileCondition applyCondition in i.ApplyConditions)
@@ -1257,13 +1262,13 @@ namespace MediaBrowser.Model.Dlna
             }
             }
 
 
             // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion
             // Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion
-            return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ?? 
-                GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ?? 
+            return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ??
+                GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ??
                 new SubtitleProfile
                 new SubtitleProfile
-            {
-                Method = SubtitleDeliveryMethod.Encode,
-                Format = subtitleStream.Codec
-            };
+                {
+                    Method = SubtitleDeliveryMethod.Encode,
+                    Format = subtitleStream.Codec
+                };
         }
         }
 
 
         private static bool IsSubtitleEmbedSupported(MediaStream subtitleStream, SubtitleProfile subtitleProfile, string transcodingSubProtocol, string transcodingContainer)
         private static bool IsSubtitleEmbedSupported(MediaStream subtitleStream, SubtitleProfile subtitleProfile, string transcodingSubProtocol, string transcodingContainer)
@@ -1407,7 +1412,7 @@ namespace MediaBrowser.Model.Dlna
             }
             }
         }
         }
 
 
-        private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions)
+        private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string qualifier, bool qualifiedOnly)
         {
         {
             foreach (ProfileCondition condition in conditions)
             foreach (ProfileCondition condition in conditions)
             {
             {
@@ -1428,6 +1433,11 @@ namespace MediaBrowser.Model.Dlna
                 {
                 {
                     case ProfileConditionValue.AudioBitrate:
                     case ProfileConditionValue.AudioBitrate:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
@@ -1448,6 +1458,11 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.AudioChannels:
                     case ProfileConditionValue.AudioChannels:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
@@ -1468,6 +1483,11 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.IsAvc:
                     case ProfileConditionValue.IsAvc:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             bool isAvc;
                             bool isAvc;
                             if (bool.TryParse(value, out isAvc))
                             if (bool.TryParse(value, out isAvc))
                             {
                             {
@@ -1484,6 +1504,11 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.IsAnamorphic:
                     case ProfileConditionValue.IsAnamorphic:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             bool isAnamorphic;
                             bool isAnamorphic;
                             if (bool.TryParse(value, out isAnamorphic))
                             if (bool.TryParse(value, out isAnamorphic))
                             {
                             {
@@ -1500,16 +1525,21 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.IsInterlaced:
                     case ProfileConditionValue.IsInterlaced:
                         {
                         {
+                            if (string.IsNullOrWhiteSpace(qualifier))
+                            {
+                                continue;
+                            }
+
                             bool isInterlaced;
                             bool isInterlaced;
                             if (bool.TryParse(value, out isInterlaced))
                             if (bool.TryParse(value, out isInterlaced))
                             {
                             {
                                 if (!isInterlaced && condition.Condition == ProfileConditionType.Equals)
                                 if (!isInterlaced && condition.Condition == ProfileConditionType.Equals)
                                 {
                                 {
-                                    item.DeInterlace = true;
+                                    item.SetOption(qualifier, "deinterlace", "true");
                                 }
                                 }
                                 else if (isInterlaced && condition.Condition == ProfileConditionType.NotEquals)
                                 else if (isInterlaced && condition.Condition == ProfileConditionType.NotEquals)
                                 {
                                 {
-                                    item.DeInterlace = true;
+                                    item.SetOption(qualifier, "deinterlace", "true");
                                 }
                                 }
                             }
                             }
                             break;
                             break;
@@ -1527,26 +1557,36 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.RefFrames:
                     case ProfileConditionValue.RefFrames:
                         {
                         {
+                            if (string.IsNullOrWhiteSpace(qualifier))
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
                                 if (condition.Condition == ProfileConditionType.Equals)
                                 if (condition.Condition == ProfileConditionType.Equals)
                                 {
                                 {
-                                    item.MaxRefFrames = num;
+                                    item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(num));
                                 }
                                 }
                                 else if (condition.Condition == ProfileConditionType.LessThanEqual)
                                 else if (condition.Condition == ProfileConditionType.LessThanEqual)
                                 {
                                 {
-                                    item.MaxRefFrames = Math.Min(num, item.MaxRefFrames ?? num);
+                                    item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(Math.Min(num, item.GetTargetRefFrames(qualifier) ?? num)));
                                 }
                                 }
                                 else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
                                 else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
                                 {
                                 {
-                                    item.MaxRefFrames = Math.Max(num, item.MaxRefFrames ?? num);
+                                    item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(Math.Max(num, item.GetTargetRefFrames(qualifier) ?? num)));
                                 }
                                 }
                             }
                             }
                             break;
                             break;
                         }
                         }
                     case ProfileConditionValue.VideoBitDepth:
                     case ProfileConditionValue.VideoBitDepth:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
@@ -1567,11 +1607,30 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.VideoProfile:
                     case ProfileConditionValue.VideoProfile:
                         {
                         {
-                            item.VideoProfile = (value ?? string.Empty).Split('|')[0];
+                            if (string.IsNullOrWhiteSpace(qualifier))
+                            {
+                                continue;
+                            }
+
+                            if (!string.IsNullOrWhiteSpace(value))
+                            {
+                                // change from split by | to comma
+
+                                // strip spaces to avoid having to encode
+                                var values = value
+                                    .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+
+                                item.SetOption(qualifier, "profile", string.Join(",", values));
+                            }
                             break;
                             break;
                         }
                         }
                     case ProfileConditionValue.Height:
                     case ProfileConditionValue.Height:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
@@ -1592,6 +1651,11 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.VideoBitrate:
                     case ProfileConditionValue.VideoBitrate:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
@@ -1612,6 +1676,11 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.VideoFramerate:
                     case ProfileConditionValue.VideoFramerate:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             float num;
                             float num;
                             if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
@@ -1632,26 +1701,36 @@ namespace MediaBrowser.Model.Dlna
                         }
                         }
                     case ProfileConditionValue.VideoLevel:
                     case ProfileConditionValue.VideoLevel:
                         {
                         {
+                            if (string.IsNullOrWhiteSpace(qualifier))
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {
                                 if (condition.Condition == ProfileConditionType.Equals)
                                 if (condition.Condition == ProfileConditionType.Equals)
                                 {
                                 {
-                                    item.VideoLevel = num;
+                                    item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(num));
                                 }
                                 }
                                 else if (condition.Condition == ProfileConditionType.LessThanEqual)
                                 else if (condition.Condition == ProfileConditionType.LessThanEqual)
                                 {
                                 {
-                                    item.VideoLevel = Math.Min(num, item.VideoLevel ?? num);
+                                    item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(Math.Min(num, item.GetTargetVideoLevel(qualifier) ?? num)));
                                 }
                                 }
                                 else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
                                 else if (condition.Condition == ProfileConditionType.GreaterThanEqual)
                                 {
                                 {
-                                    item.VideoLevel = Math.Max(num, item.VideoLevel ?? num);
+                                    item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(Math.Max(num, item.GetTargetVideoLevel(qualifier) ?? num)));
                                 }
                                 }
                             }
                             }
                             break;
                             break;
                         }
                         }
                     case ProfileConditionValue.Width:
                     case ProfileConditionValue.Width:
                         {
                         {
+                            if (qualifiedOnly)
+                            {
+                                continue;
+                            }
+
                             int num;
                             int num;
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
                             {
                             {

+ 176 - 34
MediaBrowser.Model/Dlna/StreamInfo.cs

@@ -22,6 +22,33 @@ namespace MediaBrowser.Model.Dlna
             VideoCodecs = new string[] { };
             VideoCodecs = new string[] { };
             SubtitleCodecs = new string[] { };
             SubtitleCodecs = new string[] { };
             TranscodeReasons = new List<TranscodeReason>();
             TranscodeReasons = new List<TranscodeReason>();
+            StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+        }
+
+        public void SetOption(string qualifier, string name, string value)
+        {
+            SetOption(qualifier + "-" + name, value);
+        }
+
+        public void SetOption(string name, string value)
+        {
+            StreamOptions[name] = value;
+        }
+
+        public string GetOption(string qualifier, string name)
+        {
+            return GetOption(qualifier + "-" + name);
+        }
+
+        public string GetOption(string name)
+        {
+            string value;
+            if (StreamOptions.TryGetValue(name, out value))
+            {
+                return value;
+            }
+
+            return null;
         }
         }
 
 
         public string ItemId { get; set; }
         public string ItemId { get; set; }
@@ -37,14 +64,11 @@ namespace MediaBrowser.Model.Dlna
 
 
         public long StartPositionTicks { get; set; }
         public long StartPositionTicks { get; set; }
 
 
-        public string VideoProfile { get; set; }
-
         public int? SegmentLength { get; set; }
         public int? SegmentLength { get; set; }
         public int? MinSegments { get; set; }
         public int? MinSegments { get; set; }
         public bool BreakOnNonKeyFrames { get; set; }
         public bool BreakOnNonKeyFrames { get; set; }
 
 
         public bool RequireAvc { get; set; }
         public bool RequireAvc { get; set; }
-        public bool DeInterlace { get; set; }
         public bool RequireNonAnamorphic { get; set; }
         public bool RequireNonAnamorphic { get; set; }
         public bool CopyTimestamps { get; set; }
         public bool CopyTimestamps { get; set; }
         public bool EnableSubtitlesInManifest { get; set; }
         public bool EnableSubtitlesInManifest { get; set; }
@@ -62,13 +86,10 @@ namespace MediaBrowser.Model.Dlna
 
 
         public int? VideoBitrate { get; set; }
         public int? VideoBitrate { get; set; }
 
 
-        public int? VideoLevel { get; set; }
-
         public int? MaxWidth { get; set; }
         public int? MaxWidth { get; set; }
         public int? MaxHeight { get; set; }
         public int? MaxHeight { get; set; }
 
 
         public int? MaxVideoBitDepth { get; set; }
         public int? MaxVideoBitDepth { get; set; }
-        public int? MaxRefFrames { get; set; }
 
 
         public float? MaxFramerate { get; set; }
         public float? MaxFramerate { get; set; }
 
 
@@ -92,6 +113,8 @@ namespace MediaBrowser.Model.Dlna
         public List<MediaSourceInfo> AllMediaSources { get; set; }
         public List<MediaSourceInfo> AllMediaSources { get; set; }
         public List<TranscodeReason> TranscodeReasons { get; set; }
         public List<TranscodeReason> TranscodeReasons { get; set; }
 
 
+        public Dictionary<string, string> StreamOptions { get; private set; }
+
         public string MediaSourceId
         public string MediaSourceId
         {
         {
             get
             get
@@ -146,7 +169,9 @@ namespace MediaBrowser.Model.Dlna
                     continue;
                     continue;
                 }
                 }
 
 
-                list.Add(string.Format("{0}={1}", pair.Name, pair.Value));
+                var encodedValue = pair.Value.Replace(" ", "%20");
+
+                list.Add(string.Format("{0}={1}", pair.Name, encodedValue));
             }
             }
 
 
             string queryString = string.Join("&", list.ToArray(list.Count));
             string queryString = string.Join("&", list.ToArray(list.Count));
@@ -246,11 +271,37 @@ namespace MediaBrowser.Model.Dlna
                 list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks)));
                 list.Add(new NameValuePair("StartTimeTicks", StringHelper.ToStringCultureInvariant(startPositionTicks)));
             }
             }
 
 
-            list.Add(new NameValuePair("Level", item.VideoLevel.HasValue ? StringHelper.ToStringCultureInvariant(item.VideoLevel.Value) : string.Empty));
+            if (isDlna)
+            {
+                // hack alert
+                // dlna needs to be update to support the qualified params
+                var level = item.GetTargetVideoLevel("h264");
+
+                list.Add(new NameValuePair("Level", level.HasValue ? StringHelper.ToStringCultureInvariant(level.Value) : string.Empty));
+            }
+
+            if (isDlna)
+            {
+                // hack alert
+                // dlna needs to be update to support the qualified params
+                var refframes = item.GetTargetRefFrames("h264");
+
+                list.Add(new NameValuePair("MaxRefFrames", refframes.HasValue ? StringHelper.ToStringCultureInvariant(refframes.Value) : string.Empty));
+            }
 
 
-            list.Add(new NameValuePair("MaxRefFrames", item.MaxRefFrames.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxRefFrames.Value) : string.Empty));
             list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty));
             list.Add(new NameValuePair("MaxVideoBitDepth", item.MaxVideoBitDepth.HasValue ? StringHelper.ToStringCultureInvariant(item.MaxVideoBitDepth.Value) : string.Empty));
-            list.Add(new NameValuePair("Profile", item.VideoProfile ?? string.Empty));
+
+            if (isDlna)
+            {
+                // hack alert
+                // dlna needs to be update to support the qualified params
+                var profile = item.GetOption("h264", "profile");
+
+                // Avoid having to encode
+                profile = (profile ?? string.Empty).Replace(" ", "");
+
+                list.Add(new NameValuePair("Profile", profile));
+            }
 
 
             // no longer used
             // no longer used
             list.Add(new NameValuePair("Cabac", string.Empty));
             list.Add(new NameValuePair("Cabac", string.Empty));
@@ -282,7 +333,16 @@ namespace MediaBrowser.Model.Dlna
             list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
             list.Add(new NameValuePair("SubtitleCodec", item.SubtitleStreamIndex.HasValue && item.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Embed ? subtitleCodecs : string.Empty));
 
 
             list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower()));
             list.Add(new NameValuePair("RequireNonAnamorphic", item.RequireNonAnamorphic.ToString().ToLower()));
-            list.Add(new NameValuePair("DeInterlace", item.DeInterlace.ToString().ToLower()));
+
+            if (isDlna)
+            {
+                // hack alert
+                // dlna needs to be update to support the qualified params
+                var deinterlace = string.Equals(item.GetOption("h264", "deinterlace"), "true", StringComparison.OrdinalIgnoreCase) ||
+                                  string.Equals(item.GetOption("mpeg2video", "deinterlace"), "true", StringComparison.OrdinalIgnoreCase);
+
+                list.Add(new NameValuePair("DeInterlace", deinterlace.ToString().ToLower()));
+            }
 
 
             if (!isDlna && isHls)
             if (!isDlna && isHls)
             {
             {
@@ -306,6 +366,20 @@ namespace MediaBrowser.Model.Dlna
                 list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray())));
                 list.Add(new NameValuePair("TranscodeReasons", string.Join(",", item.TranscodeReasons.Distinct().Select(i => i.ToString()).ToArray())));
             }
             }
 
 
+            if (!isDlna)
+            {
+                foreach (var pair in item.StreamOptions)
+                {
+                    if (string.IsNullOrWhiteSpace(pair.Value))
+                    {
+                        continue;
+                    }
+
+                    // strip spaces to avoid having to encode h264 profile names
+                    list.Add(new NameValuePair(pair.Key, pair.Value.Replace(" ", "")));
+                }
+            }
+
             return list;
             return list;
         }
         }
 
 
@@ -509,8 +583,19 @@ namespace MediaBrowser.Model.Dlna
         {
         {
             get
             get
             {
             {
-                MediaStream stream = TargetVideoStream;
-                return stream == null || !IsDirectStream ? null : stream.RefFrames;
+                if (IsDirectStream)
+                {
+                    return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
+                }
+
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrWhiteSpace(videoCodec))
+                {
+                    return GetTargetRefFrames(videoCodec);
+                }
+
+                return TargetVideoStream == null ? (int?)null : TargetVideoStream.RefFrames;
             }
             }
         }
         }
 
 
@@ -535,11 +620,54 @@ namespace MediaBrowser.Model.Dlna
         {
         {
             get
             get
             {
             {
-                MediaStream stream = TargetVideoStream;
-                return VideoLevel.HasValue && !IsDirectStream
-                    ? VideoLevel
-                    : stream == null ? null : stream.Level;
+                if (IsDirectStream)
+                {
+                    return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
+                }
+
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrWhiteSpace(videoCodec))
+                {
+                    return GetTargetVideoLevel(videoCodec);
+                }
+
+                return TargetVideoStream == null ? (double?)null : TargetVideoStream.Level;
+            }
+        }
+
+        public double? GetTargetVideoLevel(string codec)
+        {
+            var value = GetOption(codec, "level");
+            if (string.IsNullOrWhiteSpace(value))
+            {
+                return null;
+            }
+
+            double result;
+            if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
+            {
+                return result;
             }
             }
+
+            return null;
+        }
+
+        public int? GetTargetRefFrames(string codec)
+        {
+            var value = GetOption(codec, "maxrefframes");
+            if (string.IsNullOrWhiteSpace(value))
+            {
+                return null;
+            }
+
+            int result;
+            if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out result))
+            {
+                return result;
+            }
+
+            return null;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -563,10 +691,19 @@ namespace MediaBrowser.Model.Dlna
         {
         {
             get
             get
             {
             {
-                MediaStream stream = TargetVideoStream;
-                return !string.IsNullOrEmpty(VideoProfile) && !IsDirectStream
-                    ? VideoProfile
-                    : stream == null ? null : stream.Profile;
+                if (IsDirectStream)
+                {
+                    return TargetVideoStream == null ? null : TargetVideoStream.Profile;
+                }
+
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrWhiteSpace(videoCodec))
+                {
+                    return GetOption(videoCodec, "profile");
+                }
+
+                return TargetVideoStream == null ? null : TargetVideoStream.Profile;
             }
             }
         }
         }
 
 
@@ -626,7 +763,7 @@ namespace MediaBrowser.Model.Dlna
         /// <summary>
         /// <summary>
         /// Predicts the audio codec that will be in the output stream
         /// Predicts the audio codec that will be in the output stream
         /// </summary>
         /// </summary>
-        public string TargetAudioCodec
+        public string[] TargetAudioCodec
         {
         {
             get
             get
             {
             {
@@ -636,22 +773,22 @@ namespace MediaBrowser.Model.Dlna
 
 
                 if (IsDirectStream)
                 if (IsDirectStream)
                 {
                 {
-                    return inputCodec;
+                    return string.IsNullOrWhiteSpace(inputCodec) ? new string[] { } : new[] { inputCodec };
                 }
                 }
 
 
                 foreach (string codec in AudioCodecs)
                 foreach (string codec in AudioCodecs)
                 {
                 {
                     if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
                     if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
                     {
                     {
-                        return codec;
+                        return string.IsNullOrWhiteSpace(codec) ? new string[] { } : new[] { codec };
                     }
                     }
                 }
                 }
 
 
-                return AudioCodecs.Length == 0 ? null : AudioCodecs[0];
+                return AudioCodecs;
             }
             }
         }
         }
 
 
-        public string TargetVideoCodec
+        public string[] TargetVideoCodec
         {
         {
             get
             get
             {
             {
@@ -661,24 +798,24 @@ namespace MediaBrowser.Model.Dlna
 
 
                 if (IsDirectStream)
                 if (IsDirectStream)
                 {
                 {
-                    return inputCodec;
+                    return string.IsNullOrWhiteSpace(inputCodec) ? new string[] { } : new[] { inputCodec };
                 }
                 }
 
 
                 foreach (string codec in VideoCodecs)
                 foreach (string codec in VideoCodecs)
                 {
                 {
                     if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
                     if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
                     {
                     {
-                        return codec;
+                        return string.IsNullOrWhiteSpace(codec) ? new string[] { } : new[] { codec };
                     }
                     }
                 }
                 }
 
 
-                return VideoCodecs.Length == 0 ? null : VideoCodecs[0];
+                return VideoCodecs;
             }
             }
         }
         }
-        
+
         /// <summary>
         /// <summary>
-             /// Predicts the audio channels that will be in the output stream
-             /// </summary>
+        /// Predicts the audio channels that will be in the output stream
+        /// </summary>
         public long? TargetSize
         public long? TargetSize
         {
         {
             get
             get
@@ -763,9 +900,14 @@ namespace MediaBrowser.Model.Dlna
                     return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
                     return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
                 }
                 }
 
 
-                if (DeInterlace)
+                var targetVideoCodecs = TargetVideoCodec;
+                var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
+                if (!string.IsNullOrWhiteSpace(videoCodec))
                 {
                 {
-                    return false;
+                    if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
                 }
                 }
 
 
                 return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
                 return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;

+ 2 - 0
MediaBrowser.Model/IO/IZipClient.cs

@@ -23,6 +23,8 @@ namespace MediaBrowser.Model.IO
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles);
         void ExtractAll(Stream source, string targetPath, bool overwriteExistingFiles);
 
 
+        void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles);
+
         /// <summary>
         /// <summary>
         /// Extracts all from zip.
         /// Extracts all from zip.
         /// </summary>
         /// </summary>

+ 1 - 3
MediaBrowser.Providers/Manager/ItemImageProvider.cs

@@ -204,9 +204,7 @@ namespace MediaBrowser.Providers.Manager
 
 
         private bool HasImage(IHasMetadata item, ImageType type)
         private bool HasImage(IHasMetadata item, ImageType type)
         {
         {
-            var image = item.GetImageInfo(type, 0);
-
-            return image != null;
+            return item.HasImage(type);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 1 - 1
MediaBrowser.Providers/TV/DummySeasonProvider.cs

@@ -113,7 +113,7 @@ namespace MediaBrowser.Providers.TV
             CancellationToken cancellationToken)
             CancellationToken cancellationToken)
         {
         {
             var seasonName = seasonNumber == 0 ?
             var seasonName = seasonNumber == 0 ?
-                _config.Configuration.SeasonZeroDisplayName :
+                _libraryManager.GetLibraryOptions(series).SeasonZeroDisplayName :
                 (seasonNumber.HasValue ? string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.Value.ToString(_usCulture)) : _localization.GetLocalizedString("NameSeasonUnknown"));
                 (seasonNumber.HasValue ? string.Format(_localization.GetLocalizedString("NameSeasonNumber"), seasonNumber.Value.ToString(_usCulture)) : _localization.GetLocalizedString("NameSeasonUnknown"));
 
 
             _logger.Info("Creating Season {0} entry for {1}", seasonName, series.Name);
             _logger.Info("Creating Season {0} entry for {1}", seasonName, series.Name);

+ 4 - 4
MediaBrowser.Providers/TV/SeasonMetadataService.cs

@@ -8,9 +8,7 @@ using MediaBrowser.Providers.Manager;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
-using System.Threading.Tasks;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 
 
 namespace MediaBrowser.Providers.TV
 namespace MediaBrowser.Providers.TV
@@ -23,9 +21,11 @@ namespace MediaBrowser.Providers.TV
 
 
             if (item.IndexNumber.HasValue && item.IndexNumber.Value == 0)
             if (item.IndexNumber.HasValue && item.IndexNumber.Value == 0)
             {
             {
-                if (!string.Equals(item.Name, ServerConfigurationManager.Configuration.SeasonZeroDisplayName, StringComparison.OrdinalIgnoreCase))
+                var seasonZeroDisplayName = LibraryManager.GetLibraryOptions(item).SeasonZeroDisplayName;
+
+                if (!string.Equals(item.Name, seasonZeroDisplayName, StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    item.Name = ServerConfigurationManager.Configuration.SeasonZeroDisplayName;
+                    item.Name = seasonZeroDisplayName;
                     updateType = updateType | ItemUpdateType.MetadataEdit;
                     updateType = updateType | ItemUpdateType.MetadataEdit;
                 }
                 }
             }
             }

+ 0 - 1
MediaBrowser.Server.Mono/ImageEncoderHelper.cs

@@ -1,7 +1,6 @@
 using System;
 using System;
 using Emby.Drawing;
 using Emby.Drawing;
 using Emby.Drawing.ImageMagick;
 using Emby.Drawing.ImageMagick;
-using Emby.Server.Core;
 using Emby.Server.Implementations;
 using Emby.Server.Implementations;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;

+ 2 - 3
MediaBrowser.Server.Mono/MediaBrowser.Server.Mono.csproj

@@ -55,9 +55,8 @@
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
       <HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
-    <Reference Include="SharpCompress, Version=0.14.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\packages\SharpCompress.0.14.0\lib\net45\SharpCompress.dll</HintPath>
-      <Private>True</Private>
+    <Reference Include="SharpCompress, Version=0.18.2.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
+      <HintPath>..\packages\SharpCompress.0.18.2\lib\net45\SharpCompress.dll</HintPath>
     </Reference>
     </Reference>
     <Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
     <Reference Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
       <HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>
       <HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>

+ 1 - 2
MediaBrowser.Server.Mono/MonoAppHost.cs

@@ -3,7 +3,6 @@ using System.Collections.Generic;
 using System.Reflection;
 using System.Reflection;
 using Emby.Server.CinemaMode;
 using Emby.Server.CinemaMode;
 using Emby.Server.Connect;
 using Emby.Server.Connect;
-using Emby.Server.Core;
 using Emby.Server.Implementations;
 using Emby.Server.Implementations;
 using Emby.Server.Sync;
 using Emby.Server.Sync;
 using MediaBrowser.Controller.Connect;
 using MediaBrowser.Controller.Connect;
@@ -26,7 +25,7 @@ namespace MediaBrowser.Server.Mono
             get
             get
             {
             {
                 // A restart script must be provided
                 // A restart script must be provided
-                return StartupOptions.ContainsOption("-restartpath");
+                return StartupOptions.ContainsOption("-restartpath") && StartupOptions.ContainsOption("-ffmpeg");
             }
             }
         }
         }
 
 

+ 0 - 2
MediaBrowser.Server.Mono/Program.cs

@@ -12,8 +12,6 @@ using System.Reflection;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Emby.Drawing;
 using Emby.Drawing;
-using Emby.Server.Core.Cryptography;
-using Emby.Server.Core;
 using Emby.Server.Implementations;
 using Emby.Server.Implementations;
 using Emby.Server.Implementations.EnvironmentInfo;
 using Emby.Server.Implementations.EnvironmentInfo;
 using Emby.Server.Implementations.IO;
 using Emby.Server.Implementations.IO;

+ 1 - 1
MediaBrowser.Server.Mono/packages.config

@@ -2,7 +2,7 @@
 <packages>
 <packages>
   <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
   <package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
   <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
   <package id="ServiceStack.Text" version="4.5.8" targetFramework="net46" />
-  <package id="SharpCompress" version="0.14.0" targetFramework="net46" />
+  <package id="SharpCompress" version="0.18.2" targetFramework="net46" />
   <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
   <package id="SimpleInjector" version="4.0.8" targetFramework="net46" />
   <package id="SkiaSharp" version="1.58.1" targetFramework="net46" />
   <package id="SkiaSharp" version="1.58.1" targetFramework="net46" />
   <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />
   <package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác