/// <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]); }
/// <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]); } }
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; } }
/// <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>()); }
/// <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>()); }
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; } } }
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); }
/// <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]); } }
/// <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); }