Merge pull request #2931 from MediaBrowser/beta

Beta
This commit is contained in:
Luke 2017-10-01 23:35:45 -04:00 committed by GitHub
commit a73da532d5
105 changed files with 1462 additions and 9002 deletions

View File

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

View File

@ -209,8 +209,8 @@ namespace Emby.Dlna.Didl
var targetHeight = streamInfo.TargetHeight;
var contentFeatureList = new ContentFeatureBuilder(_profile).BuildVideoHeader(streamInfo.Container,
streamInfo.TargetVideoCodec,
streamInfo.TargetAudioCodec,
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetWidth,
targetHeight,
streamInfo.TargetVideoBitDepth,
@ -353,8 +353,8 @@ namespace Emby.Dlna.Didl
}
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container,
streamInfo.TargetAudioCodec,
streamInfo.TargetVideoCodec,
streamInfo.TargetAudioCodec.FirstOrDefault(),
streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioBitrate,
targetWidth,
targetHeight,
@ -554,7 +554,7 @@ namespace Emby.Dlna.Didl
}
var mediaProfile = _profile.GetAudioMediaProfile(streamInfo.Container,
streamInfo.TargetAudioCodec,
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetChannels,
targetAudioBitrate,
targetSampleRate,
@ -567,7 +567,7 @@ namespace Emby.Dlna.Didl
: mediaProfile.MimeType;
var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container,
streamInfo.TargetAudioCodec,
streamInfo.TargetAudioCodec.FirstOrDefault(),
targetAudioBitrate,
targetSampleRate,
targetChannels,
@ -1141,17 +1141,17 @@ namespace Emby.Dlna.Didl
int? width = 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)
.TrimStart('.')

View File

@ -662,9 +662,35 @@ namespace Emby.Dlna.PlayTo
var e = track.Element(uPnpNamespaces.items) ?? track;
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()
{
var command = AvCommands.ServiceActions.FirstOrDefault(c => c.Name == "GetPositionInfo");
@ -720,7 +746,7 @@ namespace Emby.Dlna.PlayTo
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;

View File

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

View File

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

View File

@ -35,8 +35,8 @@
<IgnoreTranscodeByteRangeRequests>false</IgnoreTranscodeByteRangeRequests>
<XmlRootAttributes />
<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="jpeg" type="Photo" />
</DirectPlayProfiles>
@ -71,7 +71,7 @@
</Conditions>
<ApplyConditions />
</CodecProfile>
<CodecProfile type="VideoAudio" codec="ac3,aac,mp3">
<CodecProfile type="VideoAudio" codec="ac3,eac3,aac,mp3">
<Conditions>
<ProfileCondition condition="LessThanEqual" property="AudioChannels" value="6" isRequired="true" />
</Conditions>

View File

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

View File

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

View File

@ -35,11 +35,6 @@ namespace Emby.Drawing
/// </summary>
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary>
/// The _cached imaged sizes
/// </summary>
private readonly ConcurrentDictionary<Guid, ImageSize> _cachedImagedSizes;
/// <summary>
/// Gets the list of currently registered image processors
/// Image processors are specialized metadata providers that run after the normal ones
@ -75,34 +70,7 @@ namespace Emby.Drawing
_appPaths = appPaths;
ImageEnhancers = new IImageEnhancer[] { };
_saveImageSizeTimer = timerFactory.Create(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
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
@ -133,7 +101,6 @@ namespace Emby.Drawing
"aiff",
"cr2",
"crw",
"dng",
// Remove until supported
//"nef",
@ -275,15 +242,15 @@ namespace Emby.Drawing
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 outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
@ -477,98 +444,30 @@ namespace Emby.Drawing
public ImageSize GetImageSize(ItemImageInfo info, bool allowSlowMethods)
{
return GetImageSize(info.Path, info.DateModified, allowSlowMethods);
return GetImageSize(info.Path, allowSlowMethods);
}
public ImageSize GetImageSize(ItemImageInfo info)
{
return GetImageSize(info.Path, info.DateModified, false);
return GetImageSize(info.Path, false);
}
public ImageSize GetImageSize(string path)
{
return GetImageSize(path, _fileSystem.GetLastWriteTimeUtc(path), false);
return GetImageSize(path, false);
}
/// <summary>
/// Gets the size of the image.
/// </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))
{
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>
@ -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>
/// Gets the image cache tag.
/// </summary>
@ -1016,7 +882,6 @@ namespace Emby.Drawing
disposable.Dispose();
}
_saveImageSizeTimer.Dispose();
GC.SuppressFinalize(this);
}

View File

@ -3,6 +3,7 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
@ -20,19 +21,25 @@ namespace Emby.Photos
{
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private IImageProcessor _imageProcessor;
public PhotoProvider(ILogger logger, IFileSystem fileSystem)
public PhotoProvider(ILogger logger, IFileSystem fileSystem, IImageProcessor imageProcessor)
{
_logger = logger;
_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)
{
item.SetImagePath(ImageType.Primary, item.Path);
// Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs
if (!_excludeExtensions.Contains(Path.GetExtension(item.Path) ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
try
{
using (var fileStream = _fileSystem.OpenRead(item.Path))
@ -79,6 +86,8 @@ namespace Emby.Photos
}
}
if (image != null)
{
item.CameraMake = image.ImageTag.Make;
item.CameraModel = image.ImageTag.Model;
@ -145,10 +154,20 @@ namespace Emby.Photos
}
}
}
}
catch (Exception e)
{
_logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
}
}
if (!item.Width.HasValue || !item.Height.HasValue)
{
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;
return Task.FromResult(result);

View File

@ -10,20 +10,39 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Querying;
using SQLitePCL.pretty;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Activity
{
public class ActivityRepository : BaseSqliteRepository, IActivityRepository
{
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)
{
DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
FileSystem = fileSystem;
}
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())
{

View File

@ -7,7 +7,6 @@ using Emby.Dlna.MediaReceiverRegistrar;
using Emby.Dlna.Ssdp;
using Emby.Drawing;
using Emby.Photos;
using Emby.Server.Core.Cryptography;
using Emby.Server.Implementations.Activity;
using Emby.Server.Implementations.Archiving;
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
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;
RegisterSingleInstance(DisplayPreferencesRepository);
@ -997,7 +996,7 @@ namespace Emby.Server.Implementations
EncodingManager = new EncodingManager(FileSystemManager, Logger, MediaEncoder, ChapterManager, LibraryManager);
RegisterSingleInstance(EncodingManager);
var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths);
var sharingRepo = new SharingRepository(LogManager.GetLogger("SharingRepository"), ApplicationPaths, FileSystemManager);
sharingRepo.Initialize();
// This is only needed for disposal purposes. If removing this, make sure to have the manager handle disposing it
RegisterSingleInstance<ISharingRepository>(sharingRepo);
@ -1351,7 +1350,7 @@ namespace Emby.Server.Implementations
private IActivityRepository GetActivityLogRepository()
{
var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths);
var repo = new ActivityRepository(LogManager.GetLogger("ActivityRepository"), ServerConfigurationManager.ApplicationPaths, FileSystemManager);
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 password = "embycert";
if (generateCertificate)
{
if (!FileSystemManager.FileExists(certPath))
{
FileSystemManager.CreateDirectory(FileSystemManager.GetDirectoryName(certPath));
//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;
}
}
}
// try
// {
// CertificateGenerator.CreateSelfSignCertificatePfx(certPath, certHost, password, Logger);
// }
// catch (Exception ex)
// {
// Logger.ErrorException("Error creating ssl cert", ex);
// return null;
// }
// }
//}
return new CertificateInfo
{
@ -1932,13 +1931,13 @@ namespace Emby.Server.Implementations
{
get
{
return SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps;
return SupportsHttps && (ServerConfigurationManager.Configuration.EnableHttps || ServerConfigurationManager.Configuration.RequireHttps);
}
}
public bool SupportsHttps
{
get { return Certificate != null; }
get { return Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy; }
}
public async Task<string> GetLocalApiUrl()
@ -2208,9 +2207,11 @@ namespace Emby.Server.Implementations
{
var updateLevel = SystemUpdateLevel;
var cacheLength = updateLevel == PackageVersionClass.Release ?
TimeSpan.FromHours(4) :
TimeSpan.FromHours(12) :
TimeSpan.FromMinutes(5);
try
{
var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser",
"Emby",
ApplicationVersion,
@ -2225,6 +2226,21 @@ namespace Emby.Server.Implementations
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
};
}
throw;
}
}
protected virtual string UpdateTargetFileName
{

View File

@ -4,6 +4,7 @@ using SharpCompress.Archives.Rar;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
using SharpCompress.Readers.Zip;
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>
/// Extracts all from7z.
/// </summary>

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 ();
}
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 ());
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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 ();
}
}
}
}

View File

@ -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 ();
}
}
}

View File

@ -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 ();
}
}
}

View File

@ -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.
*/
}

View File

@ -108,6 +108,8 @@ namespace Emby.Server.Implementations.Data
var db = SQLite3.Open(DbFilePath, connectionFlags, null);
try
{
if (string.IsNullOrWhiteSpace(_defaultWal))
{
_defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
@ -140,6 +142,16 @@ namespace Emby.Server.Implementations.Data
{
db.Execute(query);
}
}
catch
{
using (db)
{
}
throw;
}
_connection = new ManagedConnection(db, false);
@ -264,6 +276,12 @@ namespace Emby.Server.Implementations.Data
protected virtual void Dispose(bool dispose)
{
if (dispose)
{
DisposeConnection();
}
}
private void DisposeConnection()
{
try
{
@ -275,7 +293,7 @@ namespace Emby.Server.Implementations.Data
{
using (_connection)
{
_connection.Close();
}
_connection = null;
}
@ -289,7 +307,6 @@ namespace Emby.Server.Implementations.Data
Logger.ErrorException("Error disposing database", ex);
}
}
}
protected virtual void CloseConnection()
{

View File

@ -19,12 +19,14 @@ namespace Emby.Server.Implementations.Data
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
{
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)
{
_jsonSerializer = jsonSerializer;
_memoryStreamProvider = memoryStreamProvider;
FileSystem = fileSystem;
DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
}
@ -45,11 +47,27 @@ namespace Emby.Server.Implementations.Data
/// </summary>
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>
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
public void Initialize()
private void InitializeInternal()
{
using (var connection = CreateConnection())
{

View File

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

View File

@ -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);
}
@ -1642,6 +1642,9 @@ namespace Emby.Server.Implementations.Dto
return null;
}
return null;
_logger.Info("Getting image size for item type {0}", item.GetType().Name);
try
{
size = _imageProcessor.GetImageSize(imageInfo);
@ -1673,22 +1676,6 @@ namespace Emby.Server.Implementations.Dto
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;
}
}

View File

@ -56,25 +56,7 @@
<Compile Include="Collections\CollectionManager.cs" />
<Compile Include="Collections\CollectionsDynamicFolder.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\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\SqliteDisplayPreferencesRepository.cs" />
<Compile Include="Data\SqliteItemRepository.cs" />
@ -425,10 +407,9 @@
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHost.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunHttpStream.cs" />
<Compile Include="LiveTv\TunerHosts\HdHomerun\HdHomerunUdpStream.cs" />
<Compile Include="LiveTv\TunerHosts\LiveStream.cs" />
<Compile Include="LiveTv\TunerHosts\M3uParser.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\TextLocalizer.cs" />
<Compile Include="Logging\ConsoleLogger.cs" />
@ -667,8 +648,8 @@
<HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
<Private>True</Private>
</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 Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>

View File

@ -48,7 +48,7 @@ namespace Emby.Server.Implementations.EntryPoints
values.Add(config.PublicPort.ToString(CultureInfo.InvariantCulture));
values.Add(_appHost.HttpPort.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());
return string.Join("|", values.ToArray(values.Count));

View File

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

View File

@ -17,15 +17,13 @@ namespace Emby.Server.Implementations.EntryPoints
{
private readonly IServerApplicationHost _applicationHost;
private readonly IHttpClient _httpClient;
private readonly IUserManager _userManager;
private readonly ILogger _logger;
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;
_httpClient = httpClient;
_userManager = userManager;
_logger = logger;
}
@ -43,12 +41,6 @@ namespace Emby.Server.Implementations.EntryPoints
{ "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());
var logErrors = false;

View File

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
@ -423,6 +424,22 @@ namespace Emby.Server.Implementations.HttpServer
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>
/// Overridable method that can be used to implement a custom hnandler
/// </summary>
@ -453,6 +470,16 @@ namespace Emby.Server.Implementations.HttpServer
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))
{
httpRes.StatusCode = 200;
@ -579,7 +606,13 @@ namespace Emby.Server.Implementations.HttpServer
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
{
@ -725,6 +758,12 @@ namespace Emby.Server.Implementations.HttpServer
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);
}

View File

@ -142,8 +142,6 @@ namespace Emby.Server.Implementations.HttpServer
throw new ArgumentNullException("result");
}
var optimizedResult = ToOptimizedResult(requestContext, result);
if (responseHeaders == null)
{
responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@ -154,15 +152,7 @@ namespace Emby.Server.Implementations.HttpServer
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)
@ -189,6 +179,11 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="dto"></param>
/// <returns></returns>
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;
@ -197,11 +192,11 @@ namespace Emby.Server.Implementations.HttpServer
case "application/xml":
case "text/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 "text/json":
return _jsonSerializer.SerializeToString(dto);
return GetHttpResult(_jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
default:
{
var ms = new MemoryStream();
@ -213,10 +208,10 @@ namespace Emby.Server.Implementations.HttpServer
if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
{
return GetHttpResult(new byte[] { }, contentType, true);
return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
}
return GetHttpResult(ms, contentType, true);
return GetHttpResult(ms, contentType, true, responseHeaders);
}
}
}
@ -360,7 +355,7 @@ namespace Emby.Server.Implementations.HttpServer
if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
{
AddAgeHeader(responseHeaders, lastDateModified);
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration, noCache);
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
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;
}
@ -424,16 +419,6 @@ namespace Emby.Server.Implementations.HttpServer
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);
}
@ -490,7 +475,8 @@ namespace Emby.Server.Implementations.HttpServer
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 responseHeaders = options.ResponseHeaders;
@ -555,7 +541,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Adds the caching responseHeaders.
/// </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
// 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");
}
if (!noCache && cacheDuration.HasValue)
if (cacheDuration.HasValue)
{
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";
}
@ -579,15 +565,15 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders["pragma"] = "no-cache, no-store, must-revalidate";
}
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration, noCache);
AddExpiresHeader(responseHeaders, cacheKey, cacheDuration);
}
/// <summary>
/// Adds the expires header.
/// </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");
}

View File

@ -333,13 +333,7 @@ namespace Emby.Server.Implementations.IO
NotifyFilters.Attributes;
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.Renamed += watcher_Changed;
newWatcher.Changed += watcher_Changed;

View File

@ -247,11 +247,6 @@ namespace Emby.Server.Implementations.Library
}
}
/// <summary>
/// The _season zero display name
/// </summary>
private string _seasonZeroDisplayName;
private bool _wizardCompleted;
/// <summary>
/// Records the configuration values.
@ -259,7 +254,6 @@ namespace Emby.Server.Implementations.Library
/// <param name="configuration">The configuration.</param>
private void RecordConfigurationValues(ServerConfiguration configuration)
{
_seasonZeroDisplayName = configuration.SeasonZeroDisplayName;
_wizardCompleted = configuration.IsStartupWizardCompleted;
}
@ -272,59 +266,14 @@ namespace Emby.Server.Implementations.Library
{
var config = ConfigurationManager.Configuration;
var newSeasonZeroName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
var seasonZeroNameChanged = !string.Equals(_seasonZeroDisplayName, newSeasonZeroName, StringComparison.Ordinal);
var wizardChanged = config.IsStartupWizardCompleted != _wizardCompleted;
RecordConfigurationValues(config);
if (seasonZeroNameChanged || wizardChanged)
if (wizardChanged)
{
_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)
@ -433,6 +382,14 @@ namespace Emby.Server.Implementations.Library
_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)
{
if (isRequiredForDelete)

View File

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

View File

@ -1006,7 +1006,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
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)
{
@ -1039,7 +1039,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
return mediaSource;
}
public async Task<LiveStream> GetLiveStream(string uniqueId)
public async Task<ILiveStream> GetLiveStream(string uniqueId)
{
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);
@ -1072,7 +1072,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_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)
@ -1092,7 +1092,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
_logger.Info("Returning mediasource streamId {0}, mediaSource.Id {1}, mediaSource.LiveStreamId {2}",
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)
{
@ -1718,7 +1718,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
var parent = _fileSystem.GetDirectoryName(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));
index++;

View File

@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
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";
@ -94,46 +94,30 @@ namespace Emby.Server.Implementations.LiveTv.Listings
_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);
}
private string UnzipIfNeeded(string originalUrl, string file)
{
//var ext = Path.GetExtension(originalUrl);
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);
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);
_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 _fileSystem.GetFiles(tempFolder, true)
.Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
.Select(i => i.FullName)
.FirstOrDefault();
}
}
return file;
}

View File

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

View File

@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
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))
{
@ -247,7 +247,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
try
{
var liveStream = await GetChannelStream(host, channelId, streamId, cancellationToken).ConfigureAwait(false);
var startTime = DateTime.UtcNow;
await liveStream.Open(cancellationToken).ConfigureAwait(false);
var endTime = DateTime.UtcNow;
Logger.Info("Live stream opened after {0}ms", (endTime - startTime).TotalMilliseconds);
return liveStream;
}
catch (Exception ex)

View File

@ -347,6 +347,15 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
videoCodec = "h264";
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)
{
@ -491,7 +500,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
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];

View File

@ -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);
}
}
}
}

View File

@ -75,11 +75,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
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 liveStream = new LiveStream(sources.First(), _environment, FileSystem);
var liveStream = new LiveStream(sources.First(), _environment, FileSystem, Logger, Config.ApplicationPaths);
return liveStream;
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Notifications
}
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);

View File

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

View File

@ -7,22 +7,42 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Social;
using SQLitePCL.pretty;
using MediaBrowser.Model.Extensions;
using MediaBrowser.Model.IO;
namespace Emby.Server.Implementations.Social
{
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)
{
FileSystem = fileSystem;
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>
/// Opens the connection to the database
/// </summary>
/// <returns>Task.</returns>
public void Initialize()
private void InitializeInternal()
{
using (var connection = CreateConnection())
{

View File

@ -56,37 +56,15 @@ namespace Emby.Server.Implementations.TV
return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request);
}
if (limit.HasValue)
{
limit = limit.Value + 10;
var parents = user.RootFolder.GetChildren(user, true)
.Where(i => i is Folder)
.Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
.ToList();
return GetNextUp(request, parents, dtoOptions);
}
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);
return GetResult(episodes, request);
}
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);
@ -134,7 +112,7 @@ namespace Emby.Server.Implementations.TV
},
GroupBySeriesPresentationUniqueKey = true
}, parentsFolders.Cast<BaseItem>().ToList()).Cast<Episode>().Select(GetUniqueSeriesKey);
}, parentsFolders).Cast<Episode>().Select(GetUniqueSeriesKey);
// Avoid implicitly captured closure
var episodes = GetNextUpEpisodes(request, user, items, dtoOptions);

View File

@ -2,7 +2,7 @@
<packages>
<package id="Emby.XmlTv" version="1.0.10" 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="SQLitePCL.pretty" version="1.1.0" targetFramework="portable45-net45+win8" />
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />

View File

@ -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>
/// Class ItemsService
/// </summary>
@ -79,6 +84,53 @@ namespace MediaBrowser.Api.UserLibrary
_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>
/// Gets the specified request.
/// </summary>

View File

@ -114,7 +114,7 @@ namespace MediaBrowser.Common.Updates
{
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.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)
{
Version version;
if (!Version.TryParse(obj.tag_name, out version))
var versionString = obj.tag_name;
if (!Version.TryParse(versionString, out version))
{
return null;
}
@ -148,7 +149,7 @@ namespace MediaBrowser.Common.Updates
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)
{
@ -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)
{

View File

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

View File

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

View File

@ -21,11 +21,6 @@ namespace MediaBrowser.Controller.Drawing
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)
{
if (options.Width.HasValue && options.Height.HasValue)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -62,6 +62,35 @@ namespace MediaBrowser.Controller.Entities
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? Height { get; set; }
public string CameraMake { get; set; }

View File

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

View File

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

View File

@ -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)
{
return GetChildren(user, true).Count;

View File

@ -238,15 +238,12 @@ namespace MediaBrowser.Controller.Entities
{
if (queryParent is UserView)
{
return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), queryParent, query);
}
else
{
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
}
}
}
}
private async Task<QueryResult<BaseItem>> GetMusicFolders(Folder parent, User user, InternalItemsQuery query)
{
@ -1681,7 +1678,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
private IEnumerable<Folder> GetMediaFolders(User user)
private IEnumerable<BaseItem> GetMediaFolders(User user)
{
if (user == null)
{
@ -1696,7 +1693,7 @@ namespace MediaBrowser.Controller.Entities
.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)
{
@ -1717,14 +1714,14 @@ namespace MediaBrowser.Controller.Entities
}).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)
{
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)

View File

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

View File

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

View File

@ -6,11 +6,6 @@ namespace MediaBrowser.Controller.IO
{
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)
{
byte[] buffer = new byte[bufferSize];

View File

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

View File

@ -36,7 +36,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="streamId">The stream identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <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>
/// Gets the channel stream media sources.
/// </summary>
@ -56,4 +56,17 @@ namespace MediaBrowser.Controller.LiveTv
/// <returns>Task.</returns>
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; }
}
}

View File

@ -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);
}
}
}

View File

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

View File

@ -346,7 +346,8 @@ namespace MediaBrowser.Controller.MediaEncoding
"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)
@ -529,7 +530,8 @@ namespace MediaBrowser.Controller.MediaEncoding
{
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.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));
}
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) &&
!string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(videoEncoder, "h264_v4l2m2m", StringComparison.OrdinalIgnoreCase))
{
// 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
// also needed for libx264 due to https://trac.ffmpeg.org/ticket/3307
@ -756,7 +762,6 @@ namespace MediaBrowser.Controller.MediaEncoding
{
param += " -level " + level;
}
}
if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase))
@ -796,7 +801,7 @@ namespace MediaBrowser.Controller.MediaEncoding
if (videoStream.IsInterlaced)
{
if (request.DeInterlace)
if (state.DeInterlace(videoStream.Codec, false))
{
return false;
}
@ -828,23 +833,27 @@ namespace MediaBrowser.Controller.MediaEncoding
}
// 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;
}
var requestedProfiles = state.GetRequestedProfiles(videoStream.Codec);
// 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;
}
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 requestedScore = GetVideoProfileScore(request.Profile);
var requestedScore = GetVideoProfileScore(requestedProfile);
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 (!string.IsNullOrEmpty(request.Level))
var level = state.GetRequestedLevel(videoStream.Codec);
if (!string.IsNullOrEmpty(level))
{
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)
{
@ -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;
@ -1357,9 +1368,10 @@ namespace MediaBrowser.Controller.MediaEncoding
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");
}
@ -1523,11 +1535,18 @@ namespace MediaBrowser.Controller.MediaEncoding
/// <returns>System.Int32.</returns>
public int GetNumberOfThreads(EncodingJobInfo state, EncodingOptions encodingOptions, bool isWebm)
{
var threads = GetNumberOfThreadsInternal(state, encodingOptions, isWebm);
if (state.BaseRequest.CpuCoreLimit.HasValue && state.BaseRequest.CpuCoreLimit.Value > 0)
if (isWebm)
{
threads = Math.Min(threads, state.BaseRequest.CpuCoreLimit.Value);
// Recommended per docs
return Math.Max(Environment.ProcessorCount - 1, 2);
}
var threads = state.BaseRequest.CpuCoreLimit ?? encodingOptions.EncodingThreadCount;
// Automatic
if (threads <= 0 || threads >= Environment.ProcessorCount)
{
return 0;
}
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);
}
if (state.VideoStream != null && state.VideoStream.IsInterlaced)
{
state.DeInterlace = true;
}
EnforceResolutionLimit(state);
NormalizeSubtitleEmbed(state);
@ -1954,29 +1968,6 @@ namespace MediaBrowser.Controller.MediaEncoding
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)
{
if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed)

View File

@ -160,7 +160,97 @@ namespace MediaBrowser.Controller.MediaEncoding
public int? OutputAudioBitrate;
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 TranscodingJobType TranscodingType { get; set; }
@ -169,7 +259,7 @@ namespace MediaBrowser.Controller.MediaEncoding
_logger = logger;
TranscodingType = jobType;
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
PlayableStreamFileNames = new string[]{};
PlayableStreamFileNames = new string[] { };
SupportedAudioCodecs = new List<string>();
SupportedVideoCodecs = new List<string>();
SupportedSubtitleCodecs = new List<string>();
@ -319,12 +409,19 @@ namespace MediaBrowser.Controller.MediaEncoding
{
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
{
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
{
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
{
get
@ -444,7 +575,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return VideoStream == null ? (bool?)null : VideoStream.IsInterlaced;
}
if (DeInterlace)
if (DeInterlace(ActualOutputVideoCodec, true))
{
return false;
}

View File

@ -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.Services;
@ -37,18 +40,16 @@ namespace MediaBrowser.Controller.MediaEncoding
MaxWidth = info.MaxWidth;
MaxHeight = info.MaxHeight;
MaxFramerate = info.MaxFramerate;
Profile = info.VideoProfile;
ItemId = info.ItemId;
MediaSourceId = info.MediaSourceId;
AudioCodec = info.TargetAudioCodec;
AudioCodec = info.TargetAudioCodec.FirstOrDefault();
MaxAudioChannels = info.MaxAudioChannels;
AudioBitRate = info.AudioBitrate;
AudioSampleRate = info.TargetAudioSampleRate;
DeviceProfile = deviceProfile;
VideoCodec = info.TargetVideoCodec;
VideoCodec = info.TargetVideoCodec.FirstOrDefault();
VideoBitRate = info.VideoBitrate;
AudioStreamIndex = info.AudioStreamIndex;
MaxRefFrames = info.MaxRefFrames;
MaxVideoBitDepth = info.MaxVideoBitDepth;
SubtitleMethod = info.SubtitleDeliveryMethod;
Context = info.Context;
@ -58,11 +59,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{
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 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()
{
EnableAutoStreamCopy = true;
AllowVideoStreamCopy = true;
AllowAudioStreamCopy = true;
Context = EncodingContext.Streaming;
StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
}
}

View File

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

View File

@ -25,7 +25,8 @@ namespace MediaBrowser.Model.Configuration
EnableThrottling = true;
ThrottleDelaySeconds = 180;
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;
EnableHardwareEncoding = true;
EnableSubtitleExtraction = true;

View File

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

View File

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

View File

@ -36,7 +36,12 @@ namespace MediaBrowser.Model.Dlna
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))
{
@ -44,8 +49,20 @@ namespace MediaBrowser.Model.Dlna
}
var codecs = GetCodecs();
if (codecs.Length == 0)
{
return true;
}
return codecs.Length == 0 || ListHelper.ContainsIgnoreCase(codecs, ContainerProfile.SplitValue(codec)[0]);
foreach (var val in codec)
{
if (ListHelper.ContainsIgnoreCase(codecs, val))
{
return true;
}
}
return false;
}
}
}

View File

@ -283,7 +283,7 @@ namespace MediaBrowser.Model.Dlna
var conditions = new List<ProfileCondition>();
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;
foreach (ProfileCondition applyCondition in i.ApplyConditions)
@ -375,7 +375,7 @@ namespace MediaBrowser.Model.Dlna
var audioCodecProfiles = new List<CodecProfile>();
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);
}
@ -406,7 +406,7 @@ namespace MediaBrowser.Model.Dlna
}
}
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions);
ApplyTranscodingConditions(playlistItem, audioTranscodingConditions, null, false);
// Honor requested max channels
if (options.MaxAudioChannels.HasValue)
@ -769,45 +769,10 @@ namespace MediaBrowser.Model.Dlna
playlistItem.AudioStreamIndex = audioStreamIndex;
ConditionProcessor conditionProcessor = new ConditionProcessor();
var videoTranscodingConditions = new List<ProfileCondition>();
var isFirstAppliedCodecProfile = true;
foreach (CodecProfile i in options.Profile.CodecProfiles)
{
if (i.Type == CodecType.Video && i.ContainsCodec(transcodingProfile.VideoCodec, transcodingProfile.Container))
{
bool applyConditions = true;
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;
if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))
{
LogConditionFailure(options.Profile, "AudioCodecProfile", applyCondition, item);
applyConditions = false;
break;
}
}
if (applyConditions)
{
foreach (ProfileCondition c in i.Conditions)
{
videoTranscodingConditions.Add(c);
}
break;
}
}
}
ApplyTranscodingConditions(playlistItem, videoTranscodingConditions);
var audioTranscodingConditions = new List<ProfileCondition>();
foreach (CodecProfile i in options.Profile.CodecProfiles)
{
if (i.Type == CodecType.VideoAudio && i.ContainsCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container))
if (i.Type == CodecType.Video && i.ContainsAnyCodec(transcodingProfile.VideoCodec, transcodingProfile.Container))
{
bool applyConditions = true;
foreach (ProfileCondition applyCondition in i.ApplyConditions)
@ -839,6 +804,44 @@ namespace MediaBrowser.Model.Dlna
}
}
if (applyConditions)
{
var transcodingVideoCodecs = ContainerProfile.SplitValue(transcodingProfile.VideoCodec);
foreach (var transcodingVideoCodec in transcodingVideoCodecs)
{
if (i.ContainsAnyCodec(transcodingVideoCodec, transcodingProfile.Container))
{
ApplyTranscodingConditions(playlistItem, i.Conditions, transcodingVideoCodec, !isFirstAppliedCodecProfile);
isFirstAppliedCodecProfile = false;
}
}
}
}
}
var audioTranscodingConditions = new List<ProfileCondition>();
foreach (CodecProfile i in options.Profile.CodecProfiles)
{
if (i.Type == CodecType.VideoAudio && i.ContainsAnyCodec(playlistItem.TargetAudioCodec, transcodingProfile.Container))
{
bool applyConditions = true;
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;
if (!conditionProcessor.IsVideoAudioConditionSatisfied(applyCondition, audioChannels, inputAudioBitrate, inputAudioSampleRate, inputAudioBitDepth, audioProfile, isSecondaryAudio))
{
LogConditionFailure(options.Profile, "VideoCodecProfile", applyCondition, item);
applyConditions = false;
break;
}
}
if (applyConditions)
{
foreach (ProfileCondition c in i.Conditions)
@ -878,7 +881,7 @@ namespace MediaBrowser.Model.Dlna
}
// 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;
@ -896,8 +899,10 @@ namespace MediaBrowser.Model.Dlna
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);
// Reduce the bitrate if we're downmixing
@ -1061,7 +1066,7 @@ namespace MediaBrowser.Model.Dlna
conditions = new List<ProfileCondition>();
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;
foreach (ProfileCondition applyCondition in i.ApplyConditions)
@ -1117,7 +1122,7 @@ namespace MediaBrowser.Model.Dlna
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;
foreach (ProfileCondition applyCondition in i.ApplyConditions)
@ -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)
{
@ -1428,6 +1433,11 @@ namespace MediaBrowser.Model.Dlna
{
case ProfileConditionValue.AudioBitrate:
{
if (qualifiedOnly)
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
@ -1448,6 +1458,11 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.AudioChannels:
{
if (qualifiedOnly)
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
@ -1468,6 +1483,11 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.IsAvc:
{
if (qualifiedOnly)
{
continue;
}
bool isAvc;
if (bool.TryParse(value, out isAvc))
{
@ -1484,6 +1504,11 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.IsAnamorphic:
{
if (qualifiedOnly)
{
continue;
}
bool isAnamorphic;
if (bool.TryParse(value, out isAnamorphic))
{
@ -1500,16 +1525,21 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.IsInterlaced:
{
if (string.IsNullOrWhiteSpace(qualifier))
{
continue;
}
bool isInterlaced;
if (bool.TryParse(value, out isInterlaced))
{
if (!isInterlaced && condition.Condition == ProfileConditionType.Equals)
{
item.DeInterlace = true;
item.SetOption(qualifier, "deinterlace", "true");
}
else if (isInterlaced && condition.Condition == ProfileConditionType.NotEquals)
{
item.DeInterlace = true;
item.SetOption(qualifier, "deinterlace", "true");
}
}
break;
@ -1527,26 +1557,36 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.RefFrames:
{
if (string.IsNullOrWhiteSpace(qualifier))
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
item.MaxRefFrames = num;
item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(num));
}
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)
{
item.MaxRefFrames = Math.Max(num, item.MaxRefFrames ?? num);
item.SetOption(qualifier, "maxrefframes", StringHelper.ToStringCultureInvariant(Math.Max(num, item.GetTargetRefFrames(qualifier) ?? num)));
}
}
break;
}
case ProfileConditionValue.VideoBitDepth:
{
if (qualifiedOnly)
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
@ -1567,11 +1607,30 @@ namespace MediaBrowser.Model.Dlna
}
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;
}
case ProfileConditionValue.Height:
{
if (qualifiedOnly)
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
@ -1592,6 +1651,11 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.VideoBitrate:
{
if (qualifiedOnly)
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
@ -1612,6 +1676,11 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.VideoFramerate:
{
if (qualifiedOnly)
{
continue;
}
float num;
if (float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
@ -1632,26 +1701,36 @@ namespace MediaBrowser.Model.Dlna
}
case ProfileConditionValue.VideoLevel:
{
if (string.IsNullOrWhiteSpace(qualifier))
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{
if (condition.Condition == ProfileConditionType.Equals)
{
item.VideoLevel = num;
item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(num));
}
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)
{
item.VideoLevel = Math.Max(num, item.VideoLevel ?? num);
item.SetOption(qualifier, "level", StringHelper.ToStringCultureInvariant(Math.Max(num, item.GetTargetVideoLevel(qualifier) ?? num)));
}
}
break;
}
case ProfileConditionValue.Width:
{
if (qualifiedOnly)
{
continue;
}
int num;
if (int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out num))
{

View File

@ -22,6 +22,33 @@ namespace MediaBrowser.Model.Dlna
VideoCodecs = new string[] { };
SubtitleCodecs = new string[] { };
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; }
@ -37,14 +64,11 @@ namespace MediaBrowser.Model.Dlna
public long StartPositionTicks { get; set; }
public string VideoProfile { get; set; }
public int? SegmentLength { get; set; }
public int? MinSegments { get; set; }
public bool BreakOnNonKeyFrames { get; set; }
public bool RequireAvc { get; set; }
public bool DeInterlace { get; set; }
public bool RequireNonAnamorphic { get; set; }
public bool CopyTimestamps { get; set; }
public bool EnableSubtitlesInManifest { get; set; }
@ -62,13 +86,10 @@ namespace MediaBrowser.Model.Dlna
public int? VideoBitrate { get; set; }
public int? VideoLevel { get; set; }
public int? MaxWidth { get; set; }
public int? MaxHeight { get; set; }
public int? MaxVideoBitDepth { get; set; }
public int? MaxRefFrames { get; set; }
public float? MaxFramerate { get; set; }
@ -92,6 +113,8 @@ namespace MediaBrowser.Model.Dlna
public List<MediaSourceInfo> AllMediaSources { get; set; }
public List<TranscodeReason> TranscodeReasons { get; set; }
public Dictionary<string, string> StreamOptions { get; private set; }
public string MediaSourceId
{
get
@ -146,7 +169,9 @@ namespace MediaBrowser.Model.Dlna
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));
@ -246,11 +271,37 @@ namespace MediaBrowser.Model.Dlna
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("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
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("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)
{
@ -306,6 +366,20 @@ namespace MediaBrowser.Model.Dlna
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;
}
@ -509,8 +583,19 @@ namespace MediaBrowser.Model.Dlna
{
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
{
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>
@ -563,10 +691,19 @@ namespace MediaBrowser.Model.Dlna
{
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>
/// Predicts the audio codec that will be in the output stream
/// </summary>
public string TargetAudioCodec
public string[] TargetAudioCodec
{
get
{
@ -636,22 +773,22 @@ namespace MediaBrowser.Model.Dlna
if (IsDirectStream)
{
return inputCodec;
return string.IsNullOrWhiteSpace(inputCodec) ? new string[] { } : new[] { inputCodec };
}
foreach (string codec in AudioCodecs)
{
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
{
@ -661,18 +798,18 @@ namespace MediaBrowser.Model.Dlna
if (IsDirectStream)
{
return inputCodec;
return string.IsNullOrWhiteSpace(inputCodec) ? new string[] { } : new[] { inputCodec };
}
foreach (string codec in VideoCodecs)
{
if (StringHelper.EqualsIgnoreCase(codec, inputCodec))
{
return codec;
return string.IsNullOrWhiteSpace(codec) ? new string[] { } : new[] { codec };
}
}
return VideoCodecs.Length == 0 ? null : VideoCodecs[0];
return VideoCodecs;
}
}
@ -763,10 +900,15 @@ namespace MediaBrowser.Model.Dlna
return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
}
if (DeInterlace)
var targetVideoCodecs = TargetVideoCodec;
var videoCodec = targetVideoCodecs.Length == 0 ? null : targetVideoCodecs[0];
if (!string.IsNullOrWhiteSpace(videoCodec))
{
if (string.Equals(GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
return TargetVideoStream == null ? (bool?)null : TargetVideoStream.IsInterlaced;
}

View File

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

View File

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

View File

@ -113,7 +113,7 @@ namespace MediaBrowser.Providers.TV
CancellationToken cancellationToken)
{
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"));
_logger.Info("Creating Season {0} entry for {1}", seasonName, series.Name);

View File

@ -8,9 +8,7 @@ using MediaBrowser.Providers.Manager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.IO;
using MediaBrowser.Model.IO;
namespace MediaBrowser.Providers.TV
@ -23,9 +21,11 @@ namespace MediaBrowser.Providers.TV
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;
}
}

View File

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

View File

@ -55,9 +55,8 @@
<HintPath>..\packages\ServiceStack.Text.4.5.8\lib\net45\ServiceStack.Text.dll</HintPath>
<Private>True</Private>
</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 Include="SimpleInjector, Version=4.0.8.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.4.0.8\lib\net45\SimpleInjector.dll</HintPath>

View File

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

View File

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

View File

@ -2,7 +2,7 @@
<packages>
<package id="Mono.Posix" version="4.0.0.0" targetFramework="net45" />
<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="SkiaSharp" version="1.58.1" targetFramework="net46" />
<package id="SQLitePCLRaw.core" version="1.1.8" targetFramework="net46" />

Some files were not shown because too many files have changed in this diff Show More