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,7 +662,33 @@ namespace Emby.Dlna.PlayTo
var e = track.Element(uPnpNamespaces.items) ?? track;
return UpnpContainer.Create(e);
var elementString = (string)e;
if (!string.IsNullOrWhiteSpace(elementString))
{
return UpnpContainer.Create(e);
}
track = result.Document.Descendants("CurrentURI").FirstOrDefault();
if (track == null)
{
return null;
}
e = track.Element(uPnpNamespaces.items) ?? track;
elementString = (string)e;
if (!string.IsNullOrWhiteSpace(elementString))
{
return new uBaseObject
{
Url = elementString
};
}
return null;
}
private async Task<Tuple<bool, uBaseObject>> GetPositionInfo()
@ -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,134 +21,152 @@ 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
try
if (!_excludeExtensions.Contains(Path.GetExtension(item.Path) ?? string.Empty, StringComparer.OrdinalIgnoreCase))
{
using (var fileStream = _fileSystem.OpenRead(item.Path))
try
{
using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(item.Path), fileStream, null)))
using (var fileStream = _fileSystem.OpenRead(item.Path))
{
var image = file as TagLib.Image.File;
var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag;
if (tag != null)
using (var file = TagLib.File.Create(new StreamFileAbstraction(Path.GetFileName(item.Path), fileStream, null)))
{
var structure = tag.Structure;
var image = file as TagLib.Image.File;
if (structure != null)
var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag;
if (tag != null)
{
var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry;
var structure = tag.Structure;
if (exif != null)
if (structure != null)
{
var exifStructure = exif.Structure;
var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry;
if (exifStructure != null)
if (exif != null)
{
var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry;
var exifStructure = exif.Structure;
if (entry != null)
if (exifStructure != null)
{
double val = entry.Value.Numerator;
val /= entry.Value.Denominator;
item.Aperture = val;
}
var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry;
entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry;
if (entry != null)
{
double val = entry.Value.Numerator;
val /= entry.Value.Denominator;
item.Aperture = val;
}
if (entry != null)
{
double val = entry.Value.Numerator;
val /= entry.Value.Denominator;
item.ShutterSpeed = val;
entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry;
if (entry != null)
{
double val = entry.Value.Numerator;
val /= entry.Value.Denominator;
item.ShutterSpeed = val;
}
}
}
}
}
}
item.CameraMake = image.ImageTag.Make;
item.CameraModel = image.ImageTag.Model;
item.Width = image.Properties.PhotoWidth;
item.Height = image.Properties.PhotoHeight;
var rating = image.ImageTag.Rating;
if (rating.HasValue)
{
item.CommunityRating = rating;
}
else
{
item.CommunityRating = null;
}
item.Overview = image.ImageTag.Comment;
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
{
item.Name = image.ImageTag.Title;
}
var dateTaken = image.ImageTag.DateTime;
if (dateTaken.HasValue)
{
item.DateCreated = dateTaken.Value;
item.PremiereDate = dateTaken.Value;
item.ProductionYear = dateTaken.Value.Year;
}
item.Genres = image.ImageTag.Genres.ToList();
item.Tags = image.ImageTag.Keywords;
item.Software = image.ImageTag.Software;
if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None)
{
item.Orientation = null;
}
else
{
MediaBrowser.Model.Drawing.ImageOrientation orientation;
if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
if (image != null)
{
item.Orientation = orientation;
item.CameraMake = image.ImageTag.Make;
item.CameraModel = image.ImageTag.Model;
item.Width = image.Properties.PhotoWidth;
item.Height = image.Properties.PhotoHeight;
var rating = image.ImageTag.Rating;
if (rating.HasValue)
{
item.CommunityRating = rating;
}
else
{
item.CommunityRating = null;
}
item.Overview = image.ImageTag.Comment;
if (!string.IsNullOrWhiteSpace(image.ImageTag.Title))
{
item.Name = image.ImageTag.Title;
}
var dateTaken = image.ImageTag.DateTime;
if (dateTaken.HasValue)
{
item.DateCreated = dateTaken.Value;
item.PremiereDate = dateTaken.Value;
item.ProductionYear = dateTaken.Value.Year;
}
item.Genres = image.ImageTag.Genres.ToList();
item.Tags = image.ImageTag.Keywords;
item.Software = image.ImageTag.Software;
if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None)
{
item.Orientation = null;
}
else
{
MediaBrowser.Model.Drawing.ImageOrientation orientation;
if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation))
{
item.Orientation = orientation;
}
}
item.ExposureTime = image.ImageTag.ExposureTime;
item.FocalLength = image.ImageTag.FocalLength;
item.Latitude = image.ImageTag.Latitude;
item.Longitude = image.ImageTag.Longitude;
item.Altitude = image.ImageTag.Altitude;
if (image.ImageTag.ISOSpeedRatings.HasValue)
{
item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value);
}
else
{
item.IsoSpeedRating = null;
}
}
}
item.ExposureTime = image.ImageTag.ExposureTime;
item.FocalLength = image.ImageTag.FocalLength;
item.Latitude = image.ImageTag.Latitude;
item.Longitude = image.ImageTag.Longitude;
item.Altitude = image.ImageTag.Altitude;
if (image.ImageTag.ISOSpeedRatings.HasValue)
{
item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value);
}
else
{
item.IsoSpeedRating = null;
}
}
}
catch (Exception e)
{
_logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
}
}
catch (Exception e)
if (!item.Width.HasValue || !item.Height.HasValue)
{
_logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path);
var size = _imageProcessor.GetImageSize(item.Path);
item.Width = Convert.ToInt32(size.Width);
item.Height = Convert.ToInt32(size.Height);
}
const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport;

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,22 +2207,39 @@ namespace Emby.Server.Implementations
{
var updateLevel = SystemUpdateLevel;
var cacheLength = updateLevel == PackageVersionClass.Release ?
TimeSpan.FromHours(4) :
TimeSpan.FromHours(12) :
TimeSpan.FromMinutes(5);
var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser",
"Emby",
ApplicationVersion,
updateLevel,
ReleaseAssetFilename,
"MBServer",
UpdateTargetFileName,
cacheLength,
cancellationToken).ConfigureAwait(false);
try
{
var result = await new GithubUpdater(HttpClient, JsonSerializer).CheckForUpdateResult("MediaBrowser",
"Emby",
ApplicationVersion,
updateLevel,
ReleaseAssetFilename,
"MBServer",
UpdateTargetFileName,
cacheLength,
cancellationToken).ConfigureAwait(false);
HasUpdateAvailable = result.IsUpdateAvailable;
HasUpdateAvailable = result.IsUpdateAvailable;
return result;
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,37 +108,49 @@ namespace Emby.Server.Implementations.Data
var db = SQLite3.Open(DbFilePath, connectionFlags, null);
if (string.IsNullOrWhiteSpace(_defaultWal))
try
{
_defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
if (string.IsNullOrWhiteSpace(_defaultWal))
{
_defaultWal = db.Query("PRAGMA journal_mode").SelectScalarString().First();
Logger.Info("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
Logger.Info("Default journal_mode for {0} is {1}", DbFilePath, _defaultWal);
}
var queries = new List<string>
{
//"PRAGMA cache size=-10000"
//"PRAGMA read_uncommitted = true",
"PRAGMA synchronous=Normal"
};
if (CacheSize.HasValue)
{
queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture));
}
if (EnableTempStoreMemory)
{
queries.Add("PRAGMA temp_store = memory");
}
else
{
queries.Add("PRAGMA temp_store = file");
}
foreach (var query in queries)
{
db.Execute(query);
}
}
catch
{
using (db)
{
var queries = new List<string>
{
//"PRAGMA cache size=-10000"
//"PRAGMA read_uncommitted = true",
"PRAGMA synchronous=Normal"
};
}
if (CacheSize.HasValue)
{
queries.Add("PRAGMA cache_size=" + CacheSize.Value.ToString(CultureInfo.InvariantCulture));
}
if (EnableTempStoreMemory)
{
queries.Add("PRAGMA temp_store = memory");
}
else
{
queries.Add("PRAGMA temp_store = file");
}
foreach (var query in queries)
{
db.Execute(query);
throw;
}
_connection = new ManagedConnection(db, false);
@ -265,29 +277,34 @@ namespace Emby.Server.Implementations.Data
{
if (dispose)
{
try
{
lock (_disposeLock)
{
using (WriteLock.Write())
{
if (_connection != null)
{
using (_connection)
{
}
_connection = null;
}
DisposeConnection();
}
}
CloseConnection();
private void DisposeConnection()
{
try
{
lock (_disposeLock)
{
using (WriteLock.Write())
{
if (_connection != null)
{
using (_connection)
{
_connection.Close();
}
_connection = null;
}
CloseConnection();
}
}
catch (Exception ex)
{
Logger.ErrorException("Error disposing database", ex);
}
}
catch (Exception ex)
{
Logger.ErrorException("Error disposing database", ex);
}
}

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;
@ -468,7 +495,7 @@ namespace Emby.Server.Implementations.HttpServer
enableLog = EnableLogging(urlString, localPath);
urlToLog = urlString;
logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
logHeaders = enableLog && urlToLog.IndexOf("/videos/", StringComparison.OrdinalIgnoreCase) != -1;
if (enableLog)
{
@ -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,27 +192,27 @@ 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();
var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
writerFn(dto, ms);
ms.Position = 0;
if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
{
var ms = new MemoryStream();
var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
writerFn(dto, ms);
ms.Position = 0;
if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
{
return GetHttpResult(new byte[] { }, contentType, true);
}
return GetHttpResult(ms, contentType, true);
return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
}
return GetHttpResult(ms, contentType, true, responseHeaders);
}
}
}
@ -360,7 +355,7 @@ namespace Emby.Server.Implementations.HttpServer
if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
{
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.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

@ -55,9 +55,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
if (season.IndexNumber.HasValue)
{
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,11 +1406,19 @@ 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 (!_deviceManager.CanAccessDevice(user.Id.ToString("N"), request.DeviceId))
if (!user.IsParentalScheduleAllowed())
{
throw new SecurityException("User is not allowed access from this device.");
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.");
}
}
}

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();
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);
return GetNextUp(request, parents, dtoOptions);
}
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<Folder> parentsFolders, DtoOptions dtoOptions)
public QueryResult<BaseItem> GetNextUp(NextUpQuery request, List<BaseItem> parentsFolders, DtoOptions dtoOptions)
{
var user = _userManager.GetUserById(request.UserId);
@ -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,12 +238,9 @@ namespace MediaBrowser.Controller.Entities
{
if (queryParent is UserView)
{
return GetResult(GetMediaFolders(user).SelectMany(i => i.GetChildren(user, true)), queryParent, query);
}
else
{
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, true)), queryParent, query);
}
return GetResult(queryParent.GetChildren(user, true), queryParent, query);
}
}
}
@ -1681,7 +1678,7 @@ namespace MediaBrowser.Controller.Entities
return true;
}
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)
@ -1257,13 +1262,13 @@ namespace MediaBrowser.Model.Dlna
}
// Look for an external or hls profile that matches the stream type (text/graphical) and doesn't require conversion
return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ??
GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ??
return GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, false) ??
GetExternalSubtitleProfile(subtitleStream, subtitleProfiles, playMethod, transcoderSupport, true) ??
new SubtitleProfile
{
Method = SubtitleDeliveryMethod.Encode,
Format = subtitleStream.Codec
};
{
Method = SubtitleDeliveryMethod.Encode,
Format = subtitleStream.Codec
};
}
private static bool IsSubtitleEmbedSupported(MediaStream subtitleStream, SubtitleProfile subtitleProfile, string transcodingSubProtocol, string transcodingContainer)
@ -1407,7 +1412,7 @@ namespace MediaBrowser.Model.Dlna
}
}
private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions)
private void ApplyTranscodingConditions(StreamInfo item, IEnumerable<ProfileCondition> conditions, string qualifier, bool qualifiedOnly)
{
foreach (ProfileCondition condition in conditions)
{
@ -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,13 +620,56 @@ 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>
/// Predicts the audio sample rate that will be in the output stream
/// </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,24 +798,24 @@ 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;
}
}
/// <summary>
/// Predicts the audio channels that will be in the output stream
/// </summary>
/// Predicts the audio channels that will be in the output stream
/// </summary>
public long? TargetSize
{
get
@ -763,9 +900,14 @@ 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))
{
return false;
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