Exemplo n.º 1
0
        /// <summary>
        /// Create a folder on the filesytem
        /// </summary>
        /// <param name="name">Folder name</param>
        /// <param name="parent">Parent node to attach created folder</param>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        /// <exception cref="ArgumentNullException">name or parent is null</exception>
        /// <exception cref="ArgumentException">parent is not valid (all types are allowed expect <see cref="NodeType.File" />)</exception>
        public INode CreateFolder(string name, INode parent)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

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

            this.EnsureLoggedIn();

            byte[] key          = Crypto.CreateAesKey();
            byte[] attributes   = Crypto.EncryptAttributes(new Attributes(name), key);
            byte[] encryptedKey = Crypto.EncryptAes(key, this.masterKey);

            CreateNodeRequest request  = CreateNodeRequest.CreateFolderNodeRequest(parent, attributes.ToBase64(), encryptedKey.ToBase64(), key);
            GetNodesResponse  response = this.Request <GetNodesResponse>(request, this.masterKey);

            return(response.Nodes[0]);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Upload a stream on Mega.co.nz and attach created node to selected parent
        /// </summary>
        /// <param name="stream">Data to upload</param>
        /// <param name="name">Created node name</param>
        /// <param name="parent">Node to attach the uploaded file (all types except <see cref="NodeType.File" /> are supported)</param>
        /// <returns>Created node</returns>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        /// <exception cref="ArgumentNullException">stream or name or parent is null</exception>
        /// <exception cref="ArgumentException">parent is not valid (all types except <see cref="NodeType.File" /> are supported)</exception>
        public INode Upload(Stream stream, string name, INode parent)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

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

            this.EnsureLoggedIn();

            // Retrieve upload URL
            UploadUrlRequest  uploadRequest  = new UploadUrlRequest(stream.Length);
            UploadUrlResponse uploadResponse = this.Request <UploadUrlResponse>(uploadRequest);

            using (MegaAesCtrStreamCrypter encryptedStream = new MegaAesCtrStreamCrypter(stream))
            {
                string completionHandle = this._webClient.PostRequestRaw(new Uri(uploadResponse.Url), encryptedStream);

                // Encrypt attributes
                byte[] cryptedAttributes = Crypto.EncryptAttributes(new Attributes(name), encryptedStream.FileKey);

                // Compute the file key
                byte[] fileKey = new byte[32];
                for (int i = 0; i < 8; i++)
                {
                    fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.Iv[i]);
                    fileKey[i + 16] = encryptedStream.Iv[i];
                }

                for (int i = 8; i < 16; i++)
                {
                    fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.MetaMac[i - 8]);
                    fileKey[i + 16] = encryptedStream.MetaMac[i - 8];
                }

                byte[] encryptedKey = Crypto.EncryptKey(fileKey, this._masterKey);

                CreateNodeRequest createNodeRequest  = CreateNodeRequest.CreateFileNodeRequest(parent, cryptedAttributes.ToBase64(), encryptedKey.ToBase64(), fileKey, completionHandle);
                GetNodesResponse  createNodeResponse = this.Request <GetNodesResponse>(createNodeRequest, this._masterKey);
                return(createNodeResponse.Nodes[0]);
            }
        }
Exemplo n.º 3
0
        public void OnDeserialized(StreamingContext ctx)
        {
            byte[]           masterKey     = (byte[])((object[])ctx.Context)[0];
            GetNodesResponse nodesResponse = (GetNodesResponse)((object[])ctx.Context)[1];

            this.LastModificationDate = OriginalDateTime.AddSeconds(this.SerializedLastModificationDate).ToLocalTime();

            if (this.Type == NodeType.File || this.Type == NodeType.Directory)
            {
                int    splitPosition = this.SerializedKey.IndexOf(":", StringComparison.InvariantCulture);
                byte[] encryptedKey  = this.SerializedKey.Substring(splitPosition + 1).FromBase64();

                this.DecryptedKey = Crypto.DecryptKey(encryptedKey, masterKey);
                this.Key          = this.DecryptedKey;

                // If node is shared, we need to retrieve shared masterkey
                if (nodesResponse.SharedKeys != null)
                {
                    string owner = this.SerializedKey.Substring(0, splitPosition);
                    GetNodesResponse.SharedKey sharedKey = nodesResponse.SharedKeys.FirstOrDefault(x => x.Id == owner);
                    if (sharedKey != null)
                    {
                        masterKey = Crypto.DecryptKey(sharedKey.Key.FromBase64(), masterKey);

                        if (this.Type == NodeType.Directory)
                        {
                            this.DecryptedKey = masterKey;
                        }

                        this.Key = Crypto.DecryptKey(encryptedKey, masterKey);
                    }
                }

                if (this.Type == NodeType.File)
                {
                    // Extract Iv and MetaMac
                    byte[] iv      = new byte[8];
                    byte[] metaMac = new byte[8];
                    Array.Copy(this.DecryptedKey, 16, iv, 0, 8);
                    Array.Copy(this.DecryptedKey, 24, metaMac, 0, 8);
                    this.Iv      = iv;
                    this.MetaMac = metaMac;

                    // For files, key is 256 bits long. Compute the key to retrieve 128 AES key
                    this.Key = new byte[16];
                    for (int idx = 0; idx < 16; idx++)
                    {
                        this.Key[idx] = (byte)(this.DecryptedKey[idx] ^ this.DecryptedKey[idx + 16]);
                    }
                }

                Attributes attributes = Crypto.DecryptAttributes(this.SerializedAttributes.FromBase64(), this.Key);
                this.Name = attributes.Name;
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Retrieve all filesystem nodes
        /// </summary>
        /// <returns>Flat representation of all the filesystem nodes</returns>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        public IEnumerable <INode> GetNodes()
        {
            this.EnsureLoggedIn();

            GetNodesRequest  request  = new GetNodesRequest();
            GetNodesResponse response = this.Request <GetNodesResponse>(request, this.masterKey);

            Node[] nodes = response.Nodes;
            if (this.trashNode == null)
            {
                this.trashNode = nodes.First(n => n.Type == NodeType.Trash);
            }

            return(nodes.Distinct().OfType <INode>());
        }
Exemplo n.º 5
0
        /// <summary>
        /// Retrieve list of nodes from a specified Uri
        /// </summary>
        /// <param name="uri">Uri</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>
        public IEnumerable <INode> GetNodesFromLink(Uri uri)
        {
            if (uri == null)
            {
                throw new ArgumentNullException("uri");
            }

            this.EnsureLoggedIn();

            string shareId;

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

            // Retrieve attributes
            GetNodesRequest  getNodesRequest  = new GetNodesRequest(shareId);
            GetNodesResponse getNodesResponse = this.Request <GetNodesResponse>(getNodesRequest, key);

            return(getNodesResponse.Nodes.Select(x => new PublicNode(x, shareId)).OfType <INode>());
        }
Exemplo n.º 6
0
        public void OnDeserialized(StreamingContext ctx)
        {
            object[]         context       = (object[])ctx.Context;
            GetNodesResponse nodesResponse = (GetNodesResponse)context[0];

            if (context.Length == 1)
            {
                // Add key from incoming sharing.
                if (this.SharingKey != null)
                {
                    nodesResponse.SharedKeys.Add(new GetNodesResponse.SharedKey(this.Id, this.SharingKey));
                }
                return;
            }
            else
            {
                byte[] masterKey = (byte[])context[1];

                this.LastModificationDate = OriginalDateTime.AddSeconds(this.SerializedLastModificationDate).ToLocalTime();

                if (this.Type == NodeType.File || this.Type == NodeType.Directory)
                {
                    // There are cases where the SerializedKey property contains multiple keys separated with /
                    // This can occur when a folder is shared and the parent is shared too.
                    // Both keys are working so we use the first one
                    string serializedKey = this.SerializedKey.Split('/')[0];
                    int    splitPosition = serializedKey.IndexOf(":", StringComparison.InvariantCulture);
                    byte[] encryptedKey  = serializedKey.Substring(splitPosition + 1).FromBase64();

                    // If node is shared, we need to retrieve shared masterkey
                    if (nodesResponse.SharedKeys != null)
                    {
                        string handle = serializedKey.Substring(0, splitPosition);
                        GetNodesResponse.SharedKey sharedKey = nodesResponse.SharedKeys.FirstOrDefault(x => x.Id == handle);
                        if (sharedKey != null)
                        {
                            masterKey = Crypto.DecryptKey(sharedKey.Key.FromBase64(), masterKey);
                            if (this.Type == NodeType.Directory)
                            {
                                this.SharedKey = masterKey;
                            }
                            else
                            {
                                this.SharedKey = Crypto.DecryptKey(encryptedKey, masterKey);
                            }
                        }
                    }

                    this.Key = Crypto.DecryptKey(encryptedKey, masterKey);

                    if (this.Type == NodeType.File)
                    {
                        byte[] iv, metaMac, fileKey;
                        Crypto.GetPartsFromDecryptedKey(this.Key, out iv, out metaMac, out fileKey);

                        this.Iv      = iv;
                        this.MetaMac = metaMac;
                        this.Key     = fileKey;
                    }

                    Attributes attributes = Crypto.DecryptAttributes(this.SerializedAttributes.FromBase64(), this.Key);
                    this.Name = attributes.Name;
                }
            }
        }
Exemplo n.º 7
0
        public INode Upload(Stream stream, string name, INode parent, DateTime?modificationDate = null, CancellationToken?cancellationToken = null)
#endif
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

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

            this.EnsureLoggedIn();

#if !NET35
            if (cancellationToken.HasValue)
            {
                stream = new CancellableStream(stream, cancellationToken.Value);
            }
#endif

            string completionHandle = string.Empty;
            int    requestDelay     = this.options.ApiRequestDelay;
            int    remainingRetry   = this.options.ApiRequestAttempts;
            while (remainingRetry-- > 0)
            {
                // Retrieve upload URL
                UploadUrlRequest  uploadRequest  = new UploadUrlRequest(stream.Length);
                UploadUrlResponse uploadResponse = this.Request <UploadUrlResponse>(uploadRequest);

                ApiResultCode apiResult = ApiResultCode.Ok;
                using (MegaAesCtrStreamCrypter encryptedStream = new MegaAesCtrStreamCrypter(stream))
                {
                    var chunkStartPosition  = 0;
                    var chunksSizesToUpload = this.ComputeChunksSizesToUpload(encryptedStream.ChunksPositions, encryptedStream.Length).ToArray();
                    Uri uri = null;
                    for (int i = 0; i < chunksSizesToUpload.Length; i++)
                    {
                        completionHandle = string.Empty;

                        int    chunkSize   = chunksSizesToUpload[i];
                        byte[] chunkBuffer = new byte[chunkSize];
                        encryptedStream.Read(chunkBuffer, 0, chunkSize);

                        using (MemoryStream chunkStream = new MemoryStream(chunkBuffer))
                        {
                            uri = new Uri(uploadResponse.Url + "/" + chunkStartPosition);
                            chunkStartPosition += chunkSize;
                            try
                            {
                                completionHandle = this.webClient.PostRequestRaw(uri, chunkStream);
                                if (string.IsNullOrEmpty(completionHandle))
                                {
                                    apiResult = ApiResultCode.Ok;
                                    continue;
                                }

                                long retCode;
                                if (completionHandle.FromBase64().Length != 27 && long.TryParse(completionHandle, out retCode))
                                {
                                    apiResult = (ApiResultCode)retCode;
                                    break;
                                }
                            }
                            catch (Exception ex)
                            {
                                apiResult = ApiResultCode.RequestFailedRetry;
                                this.ApiRequestFailed?.Invoke(this, new ApiRequestFailedEventArgs(uri, remainingRetry, requestDelay, apiResult, ex));

                                break;
                            }
                        }
                    }

                    if (apiResult != ApiResultCode.Ok)
                    {
                        this.ApiRequestFailed?.Invoke(this, new ApiRequestFailedEventArgs(uri, remainingRetry, requestDelay, apiResult, completionHandle));

                        if (apiResult == ApiResultCode.RequestFailedRetry || apiResult == ApiResultCode.RequestFailedPermanetly || apiResult == ApiResultCode.TooManyRequests)
                        {
                            // Restart upload from the beginning
                            requestDelay = this.Wait(requestDelay);

                            // Reset steam position
                            stream.Seek(0, SeekOrigin.Begin);

                            continue;
                        }

                        throw new ApiException(apiResult);
                    }

                    // Encrypt attributes
                    byte[] cryptedAttributes = Crypto.EncryptAttributes(new Attributes(name, stream, modificationDate), encryptedStream.FileKey);

                    // Compute the file key
                    byte[] fileKey = new byte[32];
                    for (int i = 0; i < 8; i++)
                    {
                        fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.Iv[i]);
                        fileKey[i + 16] = encryptedStream.Iv[i];
                    }

                    for (int i = 8; i < 16; i++)
                    {
                        fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.MetaMac[i - 8]);
                        fileKey[i + 16] = encryptedStream.MetaMac[i - 8];
                    }

                    byte[] encryptedKey = Crypto.EncryptKey(fileKey, this.masterKey);

                    CreateNodeRequest createNodeRequest  = CreateNodeRequest.CreateFileNodeRequest(parent, cryptedAttributes.ToBase64(), encryptedKey.ToBase64(), fileKey, completionHandle);
                    GetNodesResponse  createNodeResponse = this.Request <GetNodesResponse>(createNodeRequest, this.masterKey);
                    return(createNodeResponse.Nodes[0]);
                }
            }

            throw new UploadException(completionHandle);
        }
Exemplo n.º 8
0
        /// <summary>
        /// Upload a stream on Mega.co.nz and attach created node to selected parent
        /// </summary>
        /// <param name="stream">Data to upload</param>
        /// <param name="name">Created node name</param>
        /// <param name="parent">Node to attach the uploaded file (all types except <see cref="NodeType.File" /> are supported)</param>
        /// <returns>Created node</returns>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        /// <exception cref="ArgumentNullException">stream or name or parent is null</exception>
        /// <exception cref="ArgumentException">parent is not valid (all types except <see cref="NodeType.File" /> are supported)</exception>
        public INode Upload(Stream stream, string name, INode parent)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

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

            this.EnsureLoggedIn();

            // Retrieve upload URL
            UploadUrlRequest  uploadRequest  = new UploadUrlRequest(stream.Length);
            UploadUrlResponse uploadResponse = this.Request <UploadUrlResponse>(uploadRequest);

            using (MegaAesCtrStreamCrypter encryptedStream = new MegaAesCtrStreamCrypter(stream))
            {
                string completionHandle = null;
                for (int i = 0; i < encryptedStream.ChunksPositions.Length; i++)
                {
                    long currentChunkPosition = encryptedStream.ChunksPositions[i];
                    long nextChunkPosition    = i == encryptedStream.ChunksPositions.Length - 1
            ? encryptedStream.Length
            : encryptedStream.ChunksPositions[i + 1];

                    int    chunkSize   = (int)(nextChunkPosition - currentChunkPosition);
                    byte[] chunkBuffer = new byte[chunkSize];
                    encryptedStream.Read(chunkBuffer, 0, chunkSize);
                    using (MemoryStream chunkStream = new MemoryStream(chunkBuffer))
                    {
                        int             remainingRetry = ApiRequestAttempts;
                        string          result         = null;
                        UploadException lastException  = null;
                        while (remainingRetry-- > 0)
                        {
                            Uri uri = new Uri(uploadResponse.Url + "/" + encryptedStream.ChunksPositions[i]);
                            result = this.webClient.PostRequestRaw(uri, chunkStream);
                            if (result.StartsWith("-"))
                            {
                                lastException = new UploadException(result);
                                Thread.Sleep(ApiRequestDelay);
                                continue;
                            }

                            lastException = null;
                            break;
                        }

                        if (lastException != null)
                        {
                            throw lastException;
                        }

                        completionHandle = result;
                    }
                }

                // Encrypt attributes
                byte[] cryptedAttributes = Crypto.EncryptAttributes(new Attributes(name), encryptedStream.FileKey);

                // Compute the file key
                byte[] fileKey = new byte[32];
                for (int i = 0; i < 8; i++)
                {
                    fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.Iv[i]);
                    fileKey[i + 16] = encryptedStream.Iv[i];
                }

                for (int i = 8; i < 16; i++)
                {
                    fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.MetaMac[i - 8]);
                    fileKey[i + 16] = encryptedStream.MetaMac[i - 8];
                }

                byte[] encryptedKey = Crypto.EncryptKey(fileKey, this.masterKey);

                CreateNodeRequest createNodeRequest  = CreateNodeRequest.CreateFileNodeRequest(parent, cryptedAttributes.ToBase64(), encryptedKey.ToBase64(), fileKey, completionHandle);
                GetNodesResponse  createNodeResponse = this.Request <GetNodesResponse>(createNodeRequest, this.masterKey);
                return(createNodeResponse.Nodes[0]);
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Upload a stream on Mega.co.nz and attach created node to selected parent
        /// </summary>
        /// <param name="stream">Data to upload</param>
        /// <param name="name">Created node name</param>
        /// <param name="parent">Node to attach the uploaded file (all types except <see cref="NodeType.File" /> are supported)</param>
        /// <returns>Created node</returns>
        /// <exception cref="NotSupportedException">Not logged in</exception>
        /// <exception cref="ApiException">Mega.co.nz service reports an error</exception>
        /// <exception cref="ArgumentNullException">stream or name or parent is null</exception>
        /// <exception cref="ArgumentException">parent is not valid (all types except <see cref="NodeType.File" /> are supported)</exception>
        public INode Upload(Stream stream, string name, INode parent)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("name");
            }

            if (parent == null)
            {
                throw new ArgumentNullException("parent");
            }

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

            this.EnsureLoggedIn();

            string completionHandle = "-";
            int    remainingRetry   = ApiRequestAttempts;

            while (remainingRetry-- > 0)
            {
                // Retrieve upload URL
                UploadUrlRequest  uploadRequest  = new UploadUrlRequest(stream.Length);
                UploadUrlResponse uploadResponse = this.Request <UploadUrlResponse>(uploadRequest);

                using (MegaAesCtrStreamCrypter encryptedStream = new MegaAesCtrStreamCrypter(stream))
                {
                    var chunkStartPosition  = 0;
                    var chunksSizesToUpload = this.ComputeChunksSizesToUpload(encryptedStream.ChunksPositions, encryptedStream.Length).ToArray();
                    for (int i = 0; i < chunksSizesToUpload.Length; i++)
                    {
                        int    chunkSize   = chunksSizesToUpload[i];
                        byte[] chunkBuffer = new byte[chunkSize];
                        encryptedStream.Read(chunkBuffer, 0, chunkSize);

                        using (MemoryStream chunkStream = new MemoryStream(chunkBuffer))
                        {
                            Uri uri = new Uri(uploadResponse.Url + "/" + chunkStartPosition);
                            chunkStartPosition += chunkSize;
                            try
                            {
                                completionHandle = this.webClient.PostRequestRaw(uri, chunkStream);

                                if (completionHandle.StartsWith("-"))
                                {
                                    break;
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex);
                                break;
                            }
                        }
                    }

                    if (completionHandle.StartsWith("-"))
                    {
                        // Restart upload from the beginning
                        Thread.Sleep(ApiRequestDelay);

                        // Reset steam position
                        stream.Position = 0;

                        continue;
                    }

                    // Encrypt attributes
                    byte[] cryptedAttributes = Crypto.EncryptAttributes(new Attributes(name), encryptedStream.FileKey);

                    // Compute the file key
                    byte[] fileKey = new byte[32];
                    for (int i = 0; i < 8; i++)
                    {
                        fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.Iv[i]);
                        fileKey[i + 16] = encryptedStream.Iv[i];
                    }

                    for (int i = 8; i < 16; i++)
                    {
                        fileKey[i]      = (byte)(encryptedStream.FileKey[i] ^ encryptedStream.MetaMac[i - 8]);
                        fileKey[i + 16] = encryptedStream.MetaMac[i - 8];
                    }

                    byte[] encryptedKey = Crypto.EncryptKey(fileKey, this.masterKey);

                    CreateNodeRequest createNodeRequest  = CreateNodeRequest.CreateFileNodeRequest(parent, cryptedAttributes.ToBase64(), encryptedKey.ToBase64(), fileKey, completionHandle);
                    GetNodesResponse  createNodeResponse = this.Request <GetNodesResponse>(createNodeRequest, this.masterKey);
                    return(createNodeResponse.Nodes[0]);
                }
            }

            throw new UploadException(completionHandle);
        }