/// <summary>
        /// Retrieve a Stream to download and decrypt the specified Uri
        /// </summary>
        /// <param name="uri">Uri to download</param>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        /// <exception cref="ArgumentNullException">uri is null</exception>
        /// <exception cref="ArgumentException">Uri is not valid (id and key are required)</exception>
        /// <exception cref="DownloadException">Checksum is invalid. Downloaded data are corrupted</exception>
        public Stream Download(Uri uri, CancellationToken?cancellationToken = null)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            this.EnsureLoggedIn();

            string id;

            byte[] iv, metaMac, key;
            this.GetPartsFromUri(uri, out id, out iv, out metaMac, out key);

            // Retrieve download URL
            DownloadUrlRequestFromId downloadRequest  = new DownloadUrlRequestFromId(id);
            DownloadUrlResponse      downloadResponse = this.Request <DownloadUrlResponse>(downloadRequest);

            Uri    downloadUrl = new Uri(downloadResponse.Url);
            Stream dataStream  = new SeekableReadStream(this.webClient.GetLength(downloadUrl), (buffer, bufferOffset, offset, count)
                                                        => this.webClient.GetRequestRawWithRange(
                                                            downloadUrl, offset, offset + count).Read(buffer, bufferOffset, count));

            Stream resultStream = new MegaAesCtrStreamDecrypter(dataStream, downloadResponse.Size, key, iv, metaMac);

            if (cancellationToken.HasValue)
            {
                resultStream = new CancellableStream(resultStream, cancellationToken.Value);
            }

            return(resultStream);
        }
        /// <summary>
        /// Retrieve a Stream to download and decrypt the specified node
        /// </summary>
        /// <param name="node">Node to download (only <see cref="NodeType.File" /> can be downloaded)</param>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        /// <exception cref="ArgumentNullException">node or outputFile is null</exception>
        /// <exception cref="ArgumentException">node is not valid (only <see cref="NodeType.File" /> can be downloaded)</exception>
        /// <exception cref="DownloadException">Checksum is invalid. Downloaded data are corrupted</exception>
        public Stream Download(INode node, CancellationToken?cancellationToken = null)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }

            if (node.Type != NodeType.File)
            {
                throw new ArgumentException("Invalid node");
            }

            INodeCrypto nodeCrypto = node as INodeCrypto;

            if (nodeCrypto == null)
            {
                throw new ArgumentException("node must implement INodeCrypto");
            }

            this.EnsureLoggedIn();

            // Retrieve download URL
            DownloadUrlRequest  downloadRequest  = new DownloadUrlRequest(node);
            DownloadUrlResponse downloadResponse = this.Request <DownloadUrlResponse>(downloadRequest);

            Uri    downloadUrl = new Uri(downloadResponse.Url);
            Stream dataStream  = new SeekableReadStream(this.webClient.GetLength(downloadUrl), (buffer, bufferOffset, offset, count)
                                                        => this.webClient.GetRequestRawWithRange(
                                                            downloadUrl, offset, offset + count).Read(buffer, bufferOffset, count));

            Stream resultStream = new MegaAesCtrStreamDecrypter(dataStream, downloadResponse.Size, nodeCrypto.Key, nodeCrypto.Iv, nodeCrypto.MetaMac);

            if (cancellationToken.HasValue)
            {
                resultStream = new CancellableStream(resultStream, cancellationToken.Value);
            }

            return(resultStream);
        }