File: Signing\Authoring\SigningOptions.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging)
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Globalization;
using System.IO;
using NuGet.Common;

namespace NuGet.Packaging.Signing
{
    public sealed class SigningOptions : IDisposable
    {
        private readonly Lazy<Stream> _inputPackageStream;
        private readonly Lazy<Stream> _outputPackageStream;
        private bool _isDisposed;

        /// <summary>
        /// Readable stream for the package that will be used as an input for any signing operation.
        /// </summary>
        public Stream InputPackageStream
        {
            get
            {
                if (_isDisposed)
                {
                    throw new ObjectDisposedException(nameof(SigningOptions));
                }

                return _inputPackageStream.Value;
            }
        }

        /// <summary>
        /// Readable and writeable stream for the output package for any signing operation.
        /// </summary>
        public Stream OutputPackageStream
        {
            get
            {
                if (_isDisposed)
                {
                    throw new ObjectDisposedException(nameof(SigningOptions));
                }

                return _outputPackageStream.Value;
            }
        }

        /// <summary>
        /// Switch used to indicate if an existing signature should be overwritten.
        /// </summary>
        public bool Overwrite { get; }

        /// <summary>
        /// Provider to create a Signature that can be added to the package.
        /// </summary>
        public ISignatureProvider SignatureProvider { get; }

        /// <summary>
        /// Logger to be used to display the logs during the execution of signing actions.
        /// </summary>
        public ILogger Logger { get; }

        /// <summary>Instantiates a new <see cref="SigningOptions" /> object.</summary>
        /// <param name="inputPackageStream">A readable stream for the package that will be used as input for any
        /// signing operation.</param>
        /// <param name="outputPackageStream">A readable and writeable stream for the output package for any signing
        /// operation.</param>
        /// <param name="overwrite">A flag indicating if an existing signature should be overwritten.</param>
        /// <param name="signatureProvider">A provider to create a Signature that can be added to the package.</param>
        /// <param name="logger">A logger.</param>
        /// <remarks>Signing operations cannot be done in place; therefore, <paramref name="inputPackageStream"/>
        /// and <paramref name="outputPackageStream" /> should be different streams.</remarks>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="inputPackageStream" /> is <see langword="null" />.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="outputPackageStream" /> is <see langword="null" />.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="signatureProvider" /> is <see langword="null" />.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> is <see langword="null" />.</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="inputPackageStream" /> and
        /// <paramref name="outputPackageStream"/> are the same object.</exception>
        public SigningOptions(
            Lazy<Stream> inputPackageStream,
            Lazy<Stream> outputPackageStream,
            bool overwrite,
            ISignatureProvider signatureProvider,
            ILogger logger)
        {
            _inputPackageStream = inputPackageStream ?? throw new ArgumentNullException(nameof(inputPackageStream));
            _outputPackageStream = outputPackageStream ?? throw new ArgumentNullException(nameof(outputPackageStream));
            SignatureProvider = signatureProvider ?? throw new ArgumentNullException(nameof(signatureProvider));
            Logger = logger ?? throw new ArgumentNullException(nameof(logger));
            Overwrite = overwrite;
        }

        /// <summary>Creates a new <see cref="SigningOptions" /> object from file paths.</summary>
        /// <param name="inputPackageFilePath">The file path of the package that will be used as input for any
        /// signing operation.</param>
        /// <param name="outputPackageFilePath">The file path of the package that will be the output for any signing
        /// operation.</param>
        /// <param name="overwrite">A flag indicating if an existing signature should be overwritten.</param>
        /// <param name="signatureProvider">A provider to create a Signature that can be added to the package.</param>
        /// <param name="logger">A logger.</param>
        /// <remarks>Signing operations cannot be done in place; therefore, <paramref name="inputPackageFilePath"/>
        /// and <paramref name="outputPackageFilePath" /> should be different file paths.</remarks>
        /// <exception cref="ArgumentException">Thrown if <paramref name="inputPackageFilePath" /> is <see langword="null" />,
        /// an empty string, or equivalent to <paramref name="outputPackageFilePath" />.</exception>
        /// <exception cref="ArgumentException">Thrown if <paramref name="inputPackageFilePath" /> is <see langword="null" />,
        /// an empty string, or equivalent to <paramref name="outputPackageFilePath" />.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="signatureProvider" /> is <see langword="null" />.</exception>
        /// <exception cref="ArgumentNullException">Thrown if <paramref name="logger" /> is <see langword="null" />.</exception>
        public static SigningOptions CreateFromFilePaths(
            string inputPackageFilePath,
            string outputPackageFilePath,
            bool overwrite,
            ISignatureProvider signatureProvider,
            ILogger logger)
        {
            if (string.IsNullOrEmpty(inputPackageFilePath))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(inputPackageFilePath));
            }

            if (string.IsNullOrEmpty(outputPackageFilePath))
            {
                throw new ArgumentException(Strings.ArgumentCannotBeNullOrEmpty, nameof(outputPackageFilePath));
            }

            if (StringComparer.OrdinalIgnoreCase.Equals(NormalizeFilePath(inputPackageFilePath), NormalizeFilePath(outputPackageFilePath)))
            {
                throw new ArgumentException(
                    string.Format(
                        CultureInfo.CurrentCulture,
                        Strings.SigningCannotBeDoneInPlace,
                        nameof(inputPackageFilePath),
                        nameof(outputPackageFilePath)));
            }

            if (signatureProvider == null)
            {
                throw new ArgumentNullException(nameof(signatureProvider));
            }

            if (logger == null)
            {
                throw new ArgumentNullException(nameof(logger));
            }

            return new SigningOptions(
                new Lazy<Stream>(() => File.OpenRead(inputPackageFilePath)),
                new Lazy<Stream>(() => File.Open(outputPackageFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite)),
                overwrite,
                signatureProvider,
                logger);
        }

        public void Dispose()
        {
            if (!_isDisposed)
            {
                if (_inputPackageStream.IsValueCreated)
                {
                    _inputPackageStream.Value.Dispose();
                }

                if (_outputPackageStream.IsValueCreated)
                {
                    _outputPackageStream.Value.Dispose();
                }

                GC.SuppressFinalize(this);

                _isDisposed = true;
            }
        }

        private static string NormalizeFilePath(string filePath)
        {
            return Path.GetFullPath(filePath).TrimEnd('\\');
        }
    }
}