private TResponse RequestCore <TResponse>(RequestBase request, object context = null) where TResponse : class { string dataRequest = JsonConvert.SerializeObject(new object[] { request }); Uri uri = GenerateUrl(request.QueryArguments); object jsonData = null; int requestDelay = options.ApiRequestDelay; int remainingRetry = options.ApiRequestAttempts; while (remainingRetry-- > 0) { string dataResult = webClient.PostRequestJson(uri, dataRequest); if (string.IsNullOrEmpty(dataResult) || (jsonData = JsonConvert.DeserializeObject(dataResult)) == null || jsonData is long || (jsonData is JArray && ((JArray)jsonData)[0].Type == JTokenType.Integer)) { ApiResultCode apiCode = jsonData == null ? ApiResultCode.RequestFailedRetry : jsonData is long ?(ApiResultCode)Enum.ToObject(typeof(ApiResultCode), jsonData) : (ApiResultCode)((JArray)jsonData)[0].Value <int>(); if (apiCode != ApiResultCode.Ok) { ApiRequestFailed?.Invoke(this, new ApiRequestFailedEventArgs(uri, options.ApiRequestAttempts - remainingRetry, requestDelay, apiCode, dataResult)); } if (apiCode == ApiResultCode.RequestFailedRetry) { Thread.Sleep(requestDelay = (int)Math.Round(requestDelay * options.ApiRequestDelayExponentialFactor)); continue; } if (apiCode != ApiResultCode.Ok) { throw new ApiException(apiCode); } } break; } JsonSerializerSettings settings = new JsonSerializerSettings(); settings.Context = new StreamingContext(StreamingContextStates.All, context); string data = ((JArray)jsonData)[0].ToString(); return((typeof(TResponse) == typeof(string)) ? data as TResponse : JsonConvert.DeserializeObject <TResponse>(data, settings)); }
private void OnApiRequestFailed(object sender, ApiRequestFailedEventArgs e) { ApiRequestFailed?.Invoke(sender, e); }
/// <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, DateTime?modificationDate = null, CancellationToken?cancellationToken = null) { 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"); } EnsureLoggedIn(); if (cancellationToken.HasValue) { stream = new CancellableStream(stream, cancellationToken.Value); } string completionHandle = string.Empty; int requestDelay = options.ApiRequestDelay; int remainingRetry = options.ApiRequestAttempts; while (remainingRetry-- > 0) { // Retrieve upload URL UploadUrlRequest uploadRequest = new UploadUrlRequest(stream.Length); UploadUrlResponse uploadResponse = Request <UploadUrlResponse>(uploadRequest); ApiResultCode apiResult = ApiResultCode.Ok; using (MegaAesCtrStreamCrypter encryptedStream = new MegaAesCtrStreamCrypter(stream)) { var chunkStartPosition = 0; var chunksSizesToUpload = 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 = 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; ApiRequestFailed?.Invoke(this, new ApiRequestFailedEventArgs(uri, remainingRetry, requestDelay, apiResult, ex)); break; } } } if (apiResult != ApiResultCode.Ok) { 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 Thread.Sleep(requestDelay = (int)Math.Round(requestDelay * options.ApiRequestDelayExponentialFactor)); // 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, masterKey); CreateNodeRequest createNodeRequest = CreateNodeRequest.CreateFileNodeRequest(parent, cryptedAttributes.ToBase64(), encryptedKey.ToBase64(), fileKey, completionHandle); GetNodesResponse createNodeResponse = Request <GetNodesResponse>(createNodeRequest, masterKey); return(createNodeResponse.Nodes[0]); } } throw new UploadException(completionHandle); }