This stream can be used like a CryptoStream, but does not closes/finilizes the given HashAlgorithm on dispose. Also any other operation than READ/WRITE are directly passed to the given stream.
Inheritance: StreamWrapper
        public void ConstructorTest() {
            var mock = new Mock<Stream>();
            var hashAlg = new Mock<HashAlgorithm>();
            using (var stream = new NonClosingHashStream(mock.Object, hashAlg.Object, CryptoStreamMode.Read)) {
                Assert.AreEqual(CryptoStreamMode.Read, stream.CipherMode);
            }

            using (var stream = new NonClosingHashStream(mock.Object, hashAlg.Object, CryptoStreamMode.Write)) {
                Assert.AreEqual(CryptoStreamMode.Write, stream.CipherMode);
            }
        }
        public void WriteTest() {
            byte[] content = new byte[1024];
            using (var stream = new MemoryStream(content))
            using (var hashAlg = new SHA1Managed())
            using (var outputstream = new MemoryStream()) {
                using (var hashstream = new NonClosingHashStream(outputstream, hashAlg, CryptoStreamMode.Write)) {
                    stream.CopyTo(hashstream);
                }

                Assert.AreEqual(content, outputstream.ToArray());
                hashAlg.TransformFinalBlock(new byte[0], 0, 0);
                Assert.AreEqual(SHA1.Create().ComputeHash(content), hashAlg.Hash);
            }
        }
        /// <summary>
        ///  Uploads the localFileStream to remoteDocument.
        /// </summary>
        /// <returns>
        ///  The new CMIS document.
        /// </returns>
        /// <param name='remoteDocument'>
        ///  Remote document where the local content should be uploaded to.
        /// </param>
        /// <param name='localFileStream'>
        ///  Local file stream.
        /// </param>
        /// <param name='transmission'>
        ///  Transmission status where the uploader should report its uploading status.
        /// </param>
        /// <param name='hashAlg'>
        ///  Hash alg which should be used to calculate a checksum over the uploaded content.
        /// </param>
        /// <param name='overwrite'>
        ///  If true, the local content will overwrite the existing content.
        /// </param>
        /// <exception cref="CmisSync.Lib.Tasks.UploadFailedException">If upload fails</exception>
        public virtual IDocument UploadFile(
            IDocument remoteDocument,
            Stream localFileStream,
            Transmission transmission,
            HashAlgorithm hashAlg,
            bool overwrite = true,
            UpdateChecksum update = null)
        {
            if (remoteDocument == null) {
                throw new ArgumentException("remoteDocument can not be null");
            }

            if (localFileStream == null) {
                throw new ArgumentException("localFileStream can not be null");
            }

            if (transmission == null) {
                throw new ArgumentException("status can not be null");
            }

            if (hashAlg == null) {
                throw new ArgumentException("hashAlg can not be null");
            }

            using(NonClosingHashStream hashstream = new NonClosingHashStream(localFileStream, hashAlg, CryptoStreamMode.Read))
            using(var transmissionStream = transmission.CreateStream(hashstream))
            {
                ContentStream contentStream = new ContentStream();
                contentStream.FileName = remoteDocument.Name;
                contentStream.MimeType = Cmis.MimeType.GetMIMEType(contentStream.FileName);
                contentStream.Stream = transmissionStream;
                try {
                    remoteDocument.SetContentStream(contentStream, overwrite, true);
                } catch(Exception e) {
                    throw new UploadFailedException(e, remoteDocument);
                }
            }

            hashAlg.TransformFinalBlock(new byte[0], 0, 0);
            return remoteDocument;
        }
        /// <summary>
        ///  Uploads the file.
        ///  Resumes an upload if the given localFileStream.Position is larger than zero.
        /// </summary>
        /// <returns>
        ///  The new CMIS document.
        /// </returns>
        /// <param name='remoteDocument'>
        ///  Remote document where the local content should be uploaded to.
        /// </param>
        /// <param name='localFileStream'>
        ///  Local file stream.
        /// </param>
        /// <param name='transmission'>
        ///  Transmission status where the uploader should report its uploading status.
        /// </param>
        /// <param name='hashAlg'>
        ///  Hash alg which should be used to calculate a checksum over the uploaded content.
        /// </param>
        /// <param name='overwrite'>
        ///  If true, the local content will overwrite the existing content.
        /// </param>
        /// <param name="update">Is called on every new chunk, if not <c>null</c>.</param>
        /// <exception cref="CmisSync.Lib.Tasks.UploadFailedException">
        /// Contains the last successful remote document state. This is needed for continue a failed upload.
        /// </exception>
        public override IDocument UploadFile(
            IDocument remoteDocument,
            Stream localFileStream,
            Transmission transmission,
            HashAlgorithm hashAlg,
            bool overwrite = true,
            UpdateChecksum update = null)
        {
            IDocument result = remoteDocument;
            for (long offset = localFileStream.Position; offset < localFileStream.Length; offset += this.ChunkSize) {
                bool isFirstChunk = offset == 0;
                bool isLastChunk = (offset + this.ChunkSize) >= localFileStream.Length;
                using (var hashstream = new NonClosingHashStream(localFileStream, hashAlg, CryptoStreamMode.Read))
                using (var chunkstream = new ChunkedStream(hashstream, this.ChunkSize))
                using (var offsetstream = new OffsetStream(chunkstream, offset))
                using (var transmissionStream = transmission.CreateStream(offsetstream)) {
                    transmission.Length = localFileStream.Length;
                    transmission.Position = offset;
                    chunkstream.ChunkPosition = offset;

                    ContentStream contentStream = new ContentStream();
                    contentStream.FileName = remoteDocument.Name;
                    contentStream.MimeType = Cmis.MimeType.GetMIMEType(remoteDocument.Name);
                    if (isLastChunk) {
                        contentStream.Length = localFileStream.Length - offset;
                    } else {
                        contentStream.Length = this.ChunkSize;
                    }

                    contentStream.Stream = transmissionStream;
                    try {
                        if (isFirstChunk && result.ContentStreamId != null && overwrite) {
                            result.DeleteContentStream(true);
                        }

                        result.AppendContentStream(contentStream, isLastChunk, true);
                        HashAlgorithmReuse reuse = hashAlg as HashAlgorithmReuse;
                        if (reuse != null && update != null) {
                            using (HashAlgorithm hash = (HashAlgorithm)reuse.Clone()) {
                                hash.TransformFinalBlock(new byte[0], 0, 0);
                                update(hash.Hash, result.ContentStreamLength.GetValueOrDefault());
                            }
                        }
                    } catch (Exception e) {
                        if (e is FileTransmission.AbortException) {
                            throw;
                        }

                        if (e.InnerException is FileTransmission.AbortException) {
                            throw e.InnerException;
                        }

                        throw new UploadFailedException(e, result);
                    }
                }
            }

            hashAlg.TransformFinalBlock(new byte[0], 0, 0);
            return result;
        }
 public void ConstructorFailsOnStreamIsNull() {
     Assert.Throws<ArgumentNullException>(() => { using (var stream = new NonClosingHashStream(null, new Mock<HashAlgorithm>().Object, CryptoStreamMode.Write)); });
 }
        /// <summary>
        /// Uploads the file content to the remote document.
        /// </summary>
        /// <returns>The SHA-1 hash of the uploaded file content.</returns>
        /// <param name="localFile">Local file.</param>
        /// <param name="doc">Remote document.</param>
        /// <param name="transmissionManager">Transmission manager.</param>
        /// <param name="transmissionEvent">File Transmission event.</param>
        /// <param name="mappedObject">Mapped object saved in <c>Storage</c></param>
        protected byte[] UploadFileWithPWC(IFileInfo localFile, ref IDocument doc, Transmission transmission, IMappedObject mappedObject = null) {
            byte[] checksum = null;
            var docPWC = this.LoadRemotePWCDocument(doc, ref checksum);

            using (var file = localFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete)) {
                if (checksum != null) {
                    // check PWC checksum for integration
                    using (var hashAlg = new SHA1Managed()) {
                        int bufsize = 8 * 1024;
                        byte[] buffer = new byte[bufsize];
                        for (long offset = 0; offset < docPWC.ContentStreamLength.GetValueOrDefault();) {
                            int readsize = bufsize;
                            if (readsize + offset > docPWC.ContentStreamLength.GetValueOrDefault()) {
                                readsize = (int)(docPWC.ContentStreamLength.GetValueOrDefault() - offset);
                            }

                            readsize = file.Read(buffer, 0, readsize);
                            hashAlg.TransformBlock(buffer, 0, readsize, buffer, 0);
                            offset += readsize;
                            if (readsize == 0) {
                                break;
                            }
                        }

                        hashAlg.TransformFinalBlock(new byte[0], 0, 0);
                        if (!hashAlg.Hash.SequenceEqual(checksum)) {
                            docPWC.DeleteContentStream();
                        }

                        file.Seek(0, SeekOrigin.Begin);
                    }
                }

                byte[] hash = null;
                var uploader = FileTransmission.ContentTaskUtils.CreateUploader(this.TransmissionStorage.ChunkSize);
                using (var hashAlg = new SHA1Reuse()) {
                    try {
                        using (var hashstream = new NonClosingHashStream(file, hashAlg, CryptoStreamMode.Read)) {
                            int bufsize = 8 * 1024;
                            byte[] buffer = new byte[bufsize];
                            for (long offset = 0; offset < docPWC.ContentStreamLength.GetValueOrDefault();) {
                                int readsize = bufsize;
                                if (readsize + offset > docPWC.ContentStreamLength.GetValueOrDefault()) {
                                    readsize = (int)(docPWC.ContentStreamLength.GetValueOrDefault() - offset);
                                }

                                readsize = hashstream.Read(buffer, 0, readsize);
                                offset += readsize;
                                if (readsize == 0) {
                                    break;
                                }
                            }
                        }

                        var document = doc;
                        uploader.UploadFile(docPWC, file, transmission, hashAlg, false, (byte[] checksumUpdate, long length) => this.SaveRemotePWCDocument(localFile, document, docPWC, checksumUpdate, transmission));
                        hash = hashAlg.Hash;
                    } catch (Exception ex) {
                        transmission.FailedException = ex;
                        throw;
                    }
                }

                this.TransmissionStorage.RemoveObjectByRemoteObjectId(doc.Id);

                var properties = new Dictionary<string, object>();
                properties.Add(PropertyIds.LastModificationDate, localFile.LastWriteTimeUtc);
                doc = this.Session.GetObject(docPWC.CheckIn(true, properties, null, string.Empty)) as IDocument;

                // Refresh is required, or DotCMIS will use cached one only
                doc.Refresh();

                transmission.Status = TransmissionStatus.FINISHED;
                return hash;
            }
        }