Using Telegram API via HTTP - c#

I'm trying to use the Telegram API via http (documentation on their site says this is possible) to authorize, following these instructions:
https://core.telegram.org/mtproto/auth_key#dh-exchange-initiation
https://core.telegram.org/mtproto/description#unencrypted-message
However, I cannot get any response from the server except a 404 page. Here is the code I'm using:
async Task<String> SendAuthorizeRequestTEST()
{
HttpClient client = new HttpClient();
String message = "req_pq#60469778 3761821:int128 = ResPQ";
HttpContent content = new ByteArrayContent(Packetify(message));
HttpResponseMessage msg = await client.PostAsync(new Uri("http://149.154.167.40:443"), content);
byte[] bytes = await msg.Content.ReadAsByteArrayAsync();
return Encoding.UTF8.GetString(bytes);
}
public byte[] Packetify(String message)
{
var memoryStream = new MemoryStream();
var binaryWriter = new BinaryWriter(memoryStream);
byte[] messageBytes = Encoding.UTF8.GetBytes(message);
binaryWriter.Write(0); //auth_key_id
binaryWriter.Write(1234567); //message_id
binaryWriter.Write(messageBytes.Length); //message_data_length
binaryWriter.Write(messageBytes); //message_data
byte[] packet = memoryStream.ToArray();
memoryStream.Dispose();
binaryWriter.Dispose();
return packet;
}
What am I doing wrong?

You could study what webogram does. It uses the HTTP protocol to speak to telegram.
Further more here are some steps you can use to move along quickly
https://stackoverflow.com/a/34929980/44080
cheers.

Related

How to return PDF content from another HttpResponseMessage to the browser?

I am in the process of creating a proxy server that makes a request to a PDF Blob link then takes the request to setup its HttpResponse Header which we sent to the client. This diagram should explain
As of now, I am successful at making the request to get the pdf content however I am not sure how to send that back to the user. I have followed other Stackoverflow post such as this one : https://stackoverflow.com/a/43232581/10541061
I turn the response message in step 3 of the diagram to a stream and sent it back in the new HttpResponseMessage content.But instead of PDF content , I get a json file
What I want to return to the client
What I am actually returning to the client
Here is the code I am using to create this proxy endpoint
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<HttpResponseMessage> OpenPDF([FromQuery] string url)
{
var _httpClient = _httpClientFactory.CreateClient();
var response = await _httpClient.GetAsync(url);
var stream = await response.Content.ReadAsStreamAsync();
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(stream);
message.Content.Headers.ContentLength = stream.Length;
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
EDIT
Ok so this actually sends back the PDF when I write the proxy like this
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<FileStreamResult> OpenPDF([FromQuery] string url)
{
var fileStream = new MemoryStream();
var _httpClient = _httpClientFactory.CreateClient();
var file = await _httpClient.GetStreamAsync(url).ConfigureAwait(false);
await file.CopyToAsync(fileStream);
fileStream.Position = 0;
return File(fileStream, "application/pdf", "filename.pdf");
}
The problem is I want to update the content-disposition to inline so I can force this to open in the browser instead of downloading.So I decided to take the filestream and injecting that in the httpResponseMessage.content instead but that still didn't work. It would continue to send me a json file
[AllowAnonymous]
[HttpGet("openPDF")]
public async Task<HttpResponseMessage> OpenPDF([FromQuery] string url)
{
var fileStream = new MemoryStream();
var _httpClient = _httpClientFactory.CreateClient();
var file = await _httpClient.GetStreamAsync(url).ConfigureAwait(false);
await file.CopyToAsync(fileStream);
fileStream.Position = 0;
HttpResponseMessage message = new HttpResponseMessage(HttpStatusCode.OK);
message.Content = new StreamContent(fileStream);
message.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
return message;
}
To be honest, I thought defining the content-type should suffice but guess not
This is pretty straight forward for .NET 6... suspect it should be roughly the same for .NET 4x... This uses the NuGet package Azure.Storage.Blobs
https://github.com/Azure/azure-sdk-for-net/blob/Azure.Storage.Blobs_12.13.1/sdk/storage/Azure.Storage.Blobs/README.md
[HttpGet("stream")]
public async Task GetBlobAsync()
{
var url = new Uri("https://path.to.blob.content/xxx");
var blobClient = new BlobClient(url);
Response.Headers.Add("Content-Type", "application/pdf");
Response.Headers.Add("Content-Disposition", #"attachment;filename=""intended file name.pdf""");
await blobClient.DownloadToAsync(Response.Body);
}
for .NET 4x.
try to add:
result.Content.Headers.ContentDisposition =
new ContentDispositionHeaderValue("inline")
{
FileName = "filename.pdf"
};

Docusign HMAC hash not matching

I`m trying to authenticate the DocuSign request in my WebHook using HMAC but not matter what I do the generated does not match any of the incoming values.
I`m using the same key and the same code as the one in the DocuSign documentation so I guess the only thing that is different is the extraction of the request body. Did anyone manage to get this work in C# ? How did extract the request body from the request ?
My code:
GenerateHash(key, GetRequestBodyByteArray())
private static byte[] GetRequestBodyByteArray()
{
using (var buffer = new MemoryStream())
{
// Copy the request stream to the memory stream.
var stream = HttpContext.Current.Request.InputStream;
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(buffer);
// Rewind the memory stream.
buffer.Position = 0L;
return buffer.ToArray();
}
}
private string GenerateHash(string connectKey, byte[] requestBody)
{
var keyAsBytes = Encoding.UTF8.GetBytes(connectKey);
var hmac = new HMACSHA256(keyAsBytes);
var hashAsBytes = hmac.ComputeHash(requestBody);
return Convert.ToBase64String(hashAsBytes);
}
See this guide Step2 C# example. Also you can simplify your StreamReader. Very simplified version will look something like this, signature is your X-DocuSign-Signature-1 from the header
Task<string> body;
var signature = Request.Headers["x-docusign-signature-1"];
string secret = "your secret";
using (var reader = new StreamReader(Request.Body))
{
body = reader.ReadToEndAsync();
}
var result = Hmac.ComputeHash(secret,body.Result.ToString()).Equals(signature);

InMemoryRandomAccessStream incorrect(?) behavior

I have 2 methods with identical functionality:
private static async Task<IRandomAccessStream> _DownloadImage(string url)
{
HttpClient http = new HttpClient();
var httpStream = await http.GetStreamAsync(url);
var memStream = new MemoryStream();
await httpStream.CopyToAsync(memStream);
memStream.Seek(0, SeekOrigin.Begin);
return memStream.AsRandomAccessStream();
}
and
private static async Task<IRandomAccessStream> _DownloadImage2(string url)
{
HttpClient http = new HttpClient();
var httpStream = await http.GetStreamAsync(url);
var stream = new InMemoryRandomAccessStream();
await httpStream.CopyToAsync(stream.AsStreamForWrite());
stream.Seek(0);
return stream;
}
The problem is that for some URLs the second version gives a zero-length stream. Is it a bug or am I missing something?
The url for which the first version gives a normal stream with picture while the second one is empty: http://icdn.lenta.ru/images/2014/09/17/10/20140917102331786/pic_ffa73ea27023b3e69b6cef4d068c0499.jpg
UPD:
I've found that files which have size below 16384 are at risk. I found this 16384 value at the AsStreamForWrite documentation.
#Kiewic, also proposed a workaround. Still I'd like to know why it doesn't work as is.
UPD2:
Created a bug report in Connect.
It works when you keep the Stream in a variable, try:
// using System.Net.Http;
private static async Task<IRandomAccessStream> _DownloadImage2(string url)
{
HttpClient http = new HttpClient();
var httpStream = await http.GetStreamAsync(url);
var stream = new InMemoryRandomAccessStream();
Stream streamForWrite = stream.AsStreamForWrite();
await httpStream.CopyToAsync(streamForWrite);
stream.Seek(0);
Debug.WriteLine("Length: " + streamForWrite.Length);
Debug.WriteLine("Size: " + stream.Size);
return stream;
}

send byte array by HTTP POST in store app

I'm trying to send some images + some meta data to a server by HTTP post from a windows store app but get stuck when trying to actually include the data in the post. It cannot be done the way you would accomplish this in a windows forms app or similar due to the changes to the store app API.
I get the error.
cannot convert source type byte[] to target type System.Net.Http.httpContent
now this is obviously because it's 2 different types that can't be implicitly casted, but it's basically what I'm looking to be able to do. How do I make get my byte array data into the httpContent type so I can include it in the following call
httpClient.PostAsync(Uri uri,HttpContent content);
here's my full upload method:
async private Task UploadPhotos(List<Photo> photoCollection, string recipient, string format)
{
PhotoDataGroupDTO photoGroupDTO = PhotoSessionMapper.Map(photoCollection);
try
{
var client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
client.DefaultRequestHeaders.Add("Upload", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
// POST action_begin
const string actionBeginUri = "http://localhost:51139/PhotoService.axd?action=Begin";
HttpResponseMessage response = await client.GetAsync(actionBeginUri);
response.EnsureSuccessStatusCode();
string responseBodyAsText = await response.Content.ReadAsStringAsync();
string id = responseBodyAsText;
////
// POST action_upload
Uri actionUploadUri = new Uri("http://localhost:51139/PhotoService.axd?action=Upload&brand={0}&id={1}&name={2}.jpg");
var metaData = new Dictionary<string, string>()
{
{"Id", id},
{"Brand", "M3rror"}, //TODO: Denne tekst skal komme fra en konfigurationsfil.
{"Format", format},
{"Recipient", recipient}
};
string stringData = "";
foreach (string key in metaData.Keys)
{
string value;
metaData.TryGetValue(key, out value);
stringData += key + "=" + value + ",";
}
UTF8Encoding encoding = new UTF8Encoding();
byte[] byteData = encoding.GetBytes(stringData);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, actionUploadUri);
// send meta data
// TODO get byte data in as content
HttpContent metaDataContent = byteData;
HttpResponseMessage actionUploadResponse = await client.PostAsync(actionUploadUri, metaDataContent);
actionUploadResponse.EnsureSuccessStatusCode();
responseBodyAsText = await actionUploadResponse.Content.ReadAsStringAsync();
// send photos
// TODO get byte data in as content
foreach (byte[] imageData in photoGroupDTO.PhotosData)
{
HttpContent imageContent = imageData;
actionUploadResponse = await client.PostAsync(actionUploadUri, imageContent);
actionUploadResponse.EnsureSuccessStatusCode();
responseBodyAsText = await actionUploadResponse.Content.ReadAsStringAsync();
}
////
// POST action_complete
const string actionCompleteUri = "http://localhost:51139/PhotoService.axd?action=Complete";
HttpResponseMessage actionCompleteResponse = await client.GetAsync(actionCompleteUri);
actionCompleteResponse.EnsureSuccessStatusCode();
responseBodyAsText = await actionCompleteResponse.Content.ReadAsStringAsync();
////
}
catch (HttpRequestException e)
{
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
}
It will be more straightforward to use System.Net.Http.ByteArrayContent. E.g:
// Converting byte[] into System.Net.Http.HttpContent.
byte[] data = new byte[] { 1, 2, 3, 4, 5};
ByteArrayContent byteContent = new ByteArrayContent(data);
HttpResponseMessage reponse = await client.PostAsync(uri, byteContent);
For text only with an specific text encoding use:
// Convert string into System.Net.Http.HttpContent using UTF-8 encoding.
StringContent stringContent = new StringContent(
"blah blah",
System.Text.Encoding.UTF8);
HttpResponseMessage reponse = await client.PostAsync(uri, stringContent);
Or as you mentioned above, for text and images using multipart/form-data:
// Send binary data and string data in a single request.
MultipartFormDataContent multipartContent = new MultipartFormDataContent();
multipartContent.Add(byteContent);
multipartContent.Add(stringContent);
HttpResponseMessage reponse = await client.PostAsync(uri, multipartContent);
You need to wrap the byte array in an HttpContent type.
If you are using System,Net.Http.HttpClient:
HttpContent metaDataContent = new ByteArrayContent(byteData);
If you are using the preferred Windows.Web.Http.HttpClient:
Stream stream = new MemoryStream(byteData);
HttpContent metaDataContent = new HttpStreamContent(stream.AsInputStream());
The concept you are looking for is called Serialization. Serialization means preparing your data (which could be heterogeneous and without a predefined strucutre) for storage or transmission. Then, when you need to use the data again, you do the opposite operation, deserialization, and get back the original data structure. The link above shows a few methods on how this could be done in C#.

C# RestSharp PUT method and send raw bytes (protobuf)

How to send a byte array using HTTP PUT method?
Method AddFile of the class RestRequest sends extra headers.
Method AddParameter takes an Object type.
How I do it:
byte[] data;
using (var ms = new MemoryStream())
{
Serializer.Serialize(ms, query);
data = ms.ToArray();
ms.Close();
}
var client = new RestClient(ServerPath);
var request = new RestRequest(RequestPath, Method.PUT);
request.AddFile("stream", x => new MemoryStream(data), string.Empty);
client.ExecuteAsync(request, responce => Debug.WriteLine(responce.Content));
But on server side I see extra headers
-------------------------------28947758029299
Content-Disposition: form-data; name="stream"; filename=""
Content-Type: application/octet-stream
[RAW DATA HERE]
-------------------------------28947758029299--
Extra headers make query unreadable. What I do wrong?
Thanks to Marc Gravell.
Solution:
var client = new HttpClient();
var httpContent = new ByteArrayContent(data);
client.PutAsync(Path, httpContent);
I struggle on this for sometimes and finally found the solution
the magic is to give as request body without parameter name
// file to send
IFormFile file
byte[] buffer;
using (var ms = new MemoryStream())
using (var stream = file.OpenReadStream())
{
stream.CopyTo(ms);
buffer = ms.ToArray();
}
var uploadDoc = new RestRequest("ressource", Method.POST);
uploadDoc.RequestFormat = DataFormat.None;
uploadDoc.AddParameter("", buffer, ParameterType.RequestBody);
var response = new RestClient("baseUrl").Execute(uploadDoc);
hope it can help others

Categories

Resources