/// <summary> /// Creates a new object based on a result from CAN API including validation checks. /// </summary> /// <param name="ApiResult">CAN API result object to copy values from.</param> /// <returns>Structure describing result of CAN IPNS refresh operation.</returns> public static CanRefreshIpnsResult FromApiResult(CanApiResult ApiResult) { log.Trace("()"); CanRefreshIpnsResult res = new CanRefreshIpnsResult(ApiResult); if (res.Success) { bool error = false; try { CanRefreshIpnsResponse response = JsonConvert.DeserializeObject <CanRefreshIpnsResponse>(res.DataStr); res.Details = response; } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); error = true; } if (error) { res.Success = false; res.Message = "Invalid CAN response."; res.IsCanError = false; } } log.Trace("(-)"); return(res); }
/// <summary> /// Creates an instance of the object as a copy of another existing instance. /// </summary> /// <param name="ApiResult">Existing instance to copy.</param> public CanApiResult(CanApiResult ApiResult) { Success = ApiResult.Success; Data = ApiResult.Data; DataStr = ApiResult.DataStr; Message = ApiResult.Message; IsCanError = ApiResult.IsCanError; }
/// <summary> /// Deletes CAN object from CAN server. /// </summary> /// <param name="ObjectPath">CAN path to the object.</param> /// <returns>Structure describing whether the function succeeded and response provided by CAN server.</returns> public async Task <CanDeleteResult> CanDeleteObject(string ObjectPath) { log.Trace("(ObjectPath:'{0}')", ObjectPath); NameValueCollection args = new NameValueCollection(); args.Add("arg", ObjectPath); CanApiResult apiResult = await SendRequest("pin/rm", args); CanDeleteResult res = CanDeleteResult.FromApiResult(apiResult); log.Trace("(-):{0}", res); return(res); }
/// <summary> /// Uploads CAN object to CAN server. /// </summary> /// <param name="ObjectData">CAN object to upload.</param> /// <returns>Structure describing whether the function succeeded and response provided by CAN server.</returns> public async Task <CanUploadResult> CanUploadObject(byte[] ObjectData) { log.Trace("(ObjectData.Length:{0})", ObjectData.Length); CanApiResult apiResult = await SendRequest("add", new NameValueCollection(), "file", "object", ObjectData); CanUploadResult res = CanUploadResult.FromApiResult(apiResult); if (res.Success) { log.Trace("(-):*.Success={0},*.Hash='{1}'", res.Success, res.Hash.ToBase58()); } else { log.Trace("(-):*.Success={0},*.Message='{1}'", res.Success, res.Message); } return(res); }
/// <summary> /// Creates a new object based on a result from CAN API including validation checks. /// </summary> /// <param name="ApiResult">CAN API result object to copy values from.</param> /// <returns>Structure describing result of CAN upload operation.</returns> public static CanUploadResult FromApiResult(CanApiResult ApiResult) { log.Trace("()"); CanUploadResult res = new CanUploadResult(ApiResult); if (res.Success) { bool error = false; try { CanUploadObjectResponse response = JsonConvert.DeserializeObject <CanUploadObjectResponse>(res.DataStr); if (!string.IsNullOrEmpty(response.Hash)) { res.Hash = Base58Encoding.Encoder.DecodeRaw(response.Hash); if (res.Hash == null) { log.Error("Unable to decode hash '{0}'.", response.Hash); error = true; } } else { log.Error("Empty hash in CAN response."); error = true; } } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); error = true; } if (error) { res.Success = false; res.Message = "Invalid CAN response."; res.IsCanError = false; } } log.Trace("(-)"); return(res); }
/// <summary> /// Creates a new object based on a result from CAN API including validation checks. /// </summary> /// <param name="ApiResult">CAN API result object to copy values from.</param> /// <returns>Structure describing result of CAN upload operation.</returns> public static CanDeleteResult FromApiResult(CanApiResult ApiResult) { log.Trace("()"); CanDeleteResult res = new CanDeleteResult(ApiResult); if (res.Success) { bool error = false; try { CanDeleteObjectResponse response = JsonConvert.DeserializeObject <CanDeleteObjectResponse>(res.DataStr); res.Pins = response.Pins; // If the object was deleted previously, we might have empty Pins in response. // We are thus OK if we receive success response and no more validation is done. } catch (Exception e) { log.Error("Exception occurred: {0}", e.ToString()); error = true; } if (error) { res.Success = false; res.Message = "Invalid CAN response."; res.IsCanError = false; } } else if (res.Message.ToLowerInvariant() == "not pinned") { res.Success = true; res.Pins = null; } log.Trace("(-)"); return(res); }
/// <summary> /// Creates delete result from generic API result. /// </summary> /// <param name="ApiResult">Existing instance to copy.</param> public CanRefreshIpnsResult(CanApiResult ApiResult) : base(ApiResult) { }
/// <summary> /// Sends HTTP POST request to CAN server. /// </summary> /// <param name="Action">Specifies the API function to call.</param> /// <param name="Params">List of parameters and their values.</param> /// <param name="FileToUploadParamName">Name of the file parameter, or null if no file is being uploaded.</param> /// <param name="FileToUploadName">Name of the file being uploaded, or null if no file is being uploaded.</param> /// <param name="FileToUploadData">Binary data of the file being uploaded, or null if no file is being uploaded.</param> /// <returns>Structure describing whether the function succeeded and response provided by CAN server.</returns> private async Task <CanApiResult> SendRequest(string Action, NameValueCollection Params, string FileToUploadParamName = null, string FileToUploadName = null, byte[] FileToUploadData = null) { log.Trace("(Action:'{0}',FileToUploadParamName:'{1}')", Action, FileToUploadParamName); CanApiResult res = new CanApiResult(); string query = ""; foreach (string key in Params) { query += string.Format("{0}{1}={2}", query.Length > 0 ? "&" : "", WebUtility.HtmlEncode(key), WebUtility.HtmlEncode(Params[key])); } string url = string.Format("{0}{1}{2}{3}", apiUrl, Action, query.Length > 0 ? "?" : "", query); log.Debug("CAN API URL is '{0}'.", url); try { using (HttpClient client = new HttpClient()) { client.Timeout = TimeSpan.FromSeconds(20); byte[] boundaryBytes = new byte[16]; Crypto.Rng.GetBytes(boundaryBytes); string boundary = string.Format("------------------------{0}", boundaryBytes.ToHex().ToLowerInvariant()); using (MultipartFormDataContent content = new MultipartFormDataContent(boundary)) { if (FileToUploadParamName != null) { ByteArrayContent fileContent = new ByteArrayContent(FileToUploadData); fileContent.Headers.Add("Content-Type", "application/octet-stream"); fileContent.Headers.Add("Content-Disposition", string.Format("form-data; name=\"{0}\"; filename = \"{1}\"", FileToUploadParamName, FileToUploadName)); content.Add(fileContent, FileToUploadParamName, FileToUploadName); } using (HttpResponseMessage message = await client.PostAsync(url, content, shutdownSignaling.ShutdownCancellationTokenSource.Token)) { res.Success = message.IsSuccessStatusCode; byte[] data = await message.Content.ReadAsByteArrayAsync(); string dataStr = null; try { dataStr = Encoding.UTF8.GetString(data); } catch { } if (res.Success) { res.Data = data; res.DataStr = dataStr; } else { try { dataStr = Encoding.UTF8.GetString(data); CanErrorResponse cer = JsonConvert.DeserializeObject <CanErrorResponse>(dataStr); res.Message = cer.Message; } catch { res.Message = dataStr != null ? dataStr : "Invalid response."; } res.IsCanError = true; } } } } } catch (Exception e) { if (e is OperationCanceledException) { log.Debug("Shutdown detected."); res.IsCanError = false; res.Message = "Shutdown"; } else { log.Warn("Exception occurred: {0}", e.Message); } } if (res.Success) { log.Trace("(-):*.Success={0},*.Data:\n{1}", res.Success, res.DataStr != null ? res.DataStr.SubstrMax() : "n/a"); } else { log.Trace("(-):*.Success={0},*.IsCanError={1},*.Message:\n{2}", res.Success, res.IsCanError, res.Message != null ? res.Message.SubstrMax(512) : ""); } return(res); }
/// <summary> /// Creates delete result from generic API result. /// </summary> /// <param name="ApiResult">Existing instance to copy.</param> public CanDeleteResult(CanApiResult ApiResult) : base(ApiResult) { }
/// <summary> /// Creates upload result from generic API result. /// </summary> /// <param name="ApiResult">Existing instance to copy.</param> public CanUploadResult(CanApiResult ApiResult) : base(ApiResult) { }
/// <summary> /// Refreshes server's IPNS record in CAN. /// </summary> /// <param name="IpnsRecord">IPNS record to refresh.</param> /// <param name="PublicKey">Public key of the IPNS record owner.</param> /// <returns>Structure describing whether the function succeeded and response provided by CAN server.</returns> public async Task <CanRefreshIpnsResult> RefreshIpnsRecord(CanIpnsEntry IpnsRecord, byte[] PublicKey) { log.Trace("(PublicKey:'{0}')", PublicKey.ToHex()); string ipnsRecordEncoded = IpnsRecord.ToByteArray().ToBase64UrlPad(true); CanCryptoKey cryptoKey = new CanCryptoKey() { Type = CanCryptoKey.Types.KeyType.Ed25519, Data = ProtocolHelper.ByteArrayToByteString(PublicKey) }; string keyEncoded = Base58Encoding.Encoder.Encode(cryptoKey.ToByteArray()); log.Debug("Encoding public key: {0}", keyEncoded); NameValueCollection args = new NameValueCollection(); args.Add("arg", ipnsRecordEncoded); args.Add("key", keyEncoded); CanApiResult apiResult = await SendRequest("name/upload", args); CanRefreshIpnsResult res = CanRefreshIpnsResult.FromApiResult(apiResult); if (res.Success) { res.IsCanError = false; // Check that the ID, path and sequence number match what we expect. string canId = CanApi.PublicKeyToId(PublicKey).ToBase58(); if (res.Details.Peer == canId) { string path = Encoding.UTF8.GetString(IpnsRecord.Value.ToByteArray()); if (res.Details.NewPath == path) { if (res.Details.NewSeq == IpnsRecord.Sequence) { // All OK. } else { log.Warn("CAN sequence is {0}, received {1}.", IpnsRecord.Sequence, res.Details.NewSeq); res.Success = false; res.Message = "CAN path in response does not match expected value."; } } else { log.Warn("CAN path is '{0}', received '{1}'.", path, res.Details.NewPath); res.Success = false; res.Message = "CAN path in response does not match expected value."; } } else { log.Warn("CAN ID is '{0}', received '{1}'.", canId, res.Details.Peer); res.Success = false; res.Message = "CAN ID in response does not match expected value."; } } if (res.Success) { log.Trace("(-):*.Success={0}", res.Success); } else { log.Trace("(-):*.Success={0},*.IsCanError={1},*.Message='{2}'", res.Success, res.IsCanError, res.Message); } return(res); }