Skip to content

Commit 09914f0

Browse files
committed
Fix attachment uploading
1 parent e6d2511 commit 09914f0

File tree

5 files changed

+147
-122
lines changed

5 files changed

+147
-122
lines changed

libsignal-service-dotnet/crypto/AttachmentCipherOutputStream.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,34 +37,45 @@ internal static long GetCiphertextLength(long plaintextLength)
3737

3838
public override void Write(byte[] buffer, int offset, int count)
3939
{
40-
Cipher.Write(buffer, offset, count);
40+
WriteToCipherStream(buffer, offset, count);
4141
byte[] cipherBuffer = new byte[Aes.BlockSize];
4242
int read = TmpStream.Read(cipherBuffer, 0, cipherBuffer.Length);
4343
while (read > 0)
4444
{
45-
Mac.AppendData(buffer);
45+
Mac.AppendData(cipherBuffer, 0, read);
4646
base.Write(cipherBuffer, 0, read);
47+
read = TmpStream.Read(cipherBuffer, 0, cipherBuffer.Length);
4748
}
4849
}
4950

5051
public override void Flush()
5152
{
52-
Cipher.FlushFinalBlock();
53+
FlushFinalBlock();
5354
byte[] cipherBuffer = new byte[Aes.BlockSize];
5455
int read = TmpStream.Read(cipherBuffer, 0, cipherBuffer.Length);
5556
while (read > 0)
5657
{
5758
Mac.AppendData(cipherBuffer, 0, read);
5859
base.Write(cipherBuffer, 0, read);
60+
read = TmpStream.Read(cipherBuffer, 0, cipherBuffer.Length);
5961
}
6062
byte[] auth = Mac.GetHashAndReset();
6163
base.Write(auth, 0, auth.Length);
6264
base.Flush();
6365
}
6466

65-
public static void ReadIntoBuffer(Stream s)
67+
private void WriteToCipherStream(byte[] buffer, int offset, int count)
6668
{
69+
var oldPos = TmpStream.Position;
70+
Cipher.Write(buffer, offset, count);
71+
TmpStream.Position = oldPos;
72+
}
6773

74+
private void FlushFinalBlock()
75+
{
76+
var oldPos = TmpStream.Position;
77+
Cipher.FlushFinalBlock();
78+
TmpStream.Position = oldPos;
6879
}
6980
}
7081
}

libsignal-service-dotnet/crypto/DigestingOutputStream.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class DigestingOutputStream : Stream
1111
{
1212
private IncrementalHash RunningDigest = IncrementalHash.CreateHash(HashAlgorithmName.SHA256);
1313
private Stream OutputStream { get; }
14-
public override bool CanRead => throw new NotImplementedException();
14+
public override bool CanRead => false;
1515
public override bool CanSeek => false;
1616
public override bool CanWrite => true;
1717
public override long Length => throw new NotImplementedException();

libsignal-service-dotnet/crypto/PaddingInputStream.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ internal class PaddingInputStream : Stream
1111
private readonly Stream InputStream;
1212
private long PaddingRemaining;
1313

14-
public override bool CanRead => throw new NotImplementedException();
15-
public override bool CanSeek => throw new NotImplementedException();
16-
public override bool CanWrite => throw new NotImplementedException();
14+
public override bool CanRead => true;
15+
public override bool CanSeek => false;
16+
public override bool CanWrite => false;
1717
public override long Length { get => InputStream.Length + Util.ToIntExact(PaddingRemaining); }
1818
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
1919

Lines changed: 110 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,86 @@
1-
using System;
2-
using System.IO;
3-
4-
namespace libsignalservice.messages
1+
using System;
2+
using System.IO;
3+
4+
namespace libsignalservice.messages
55
{
66
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
77
public abstract class SignalServiceAttachment
8-
{
9-
public String ContentType { get; }
10-
11-
internal SignalServiceAttachment(String contentType)
12-
{
13-
this.ContentType = contentType;
14-
}
15-
16-
public abstract bool IsStream();
17-
18-
public abstract bool IsPointer();
19-
20-
public SignalServiceAttachmentStream AsStream()
21-
{
22-
return (SignalServiceAttachmentStream)this;
23-
}
24-
25-
public SignalServiceAttachmentPointer AsPointer()
26-
{
27-
return (SignalServiceAttachmentPointer)this;
28-
}
29-
30-
public class Builder
31-
{
32-
private Stream InputStream;
33-
private string ContentType;
34-
private string FileName;
35-
private long Length;
36-
private IProgressListener Listener;
8+
{
9+
public String ContentType { get; }
10+
11+
internal SignalServiceAttachment(String contentType)
12+
{
13+
this.ContentType = contentType;
14+
}
15+
16+
public abstract bool IsStream();
17+
18+
public abstract bool IsPointer();
19+
20+
public SignalServiceAttachmentStream AsStream()
21+
{
22+
return (SignalServiceAttachmentStream)this;
23+
}
24+
25+
public SignalServiceAttachmentPointer AsPointer()
26+
{
27+
return (SignalServiceAttachmentPointer)this;
28+
}
29+
30+
public static Builder NewStreamBuilder()
31+
{
32+
return new Builder();
33+
}
34+
35+
public class Builder
36+
{
37+
private Stream InputStream;
38+
private string ContentType;
39+
private string FileName;
40+
private long Length;
41+
private IProgressListener Listener;
3742
private bool VoiceNote;
3843
private int Width;
39-
private int Height;
40-
41-
Builder()
42-
{
43-
}
44-
45-
public Builder WithStream(Stream inputStream)
46-
{
47-
InputStream = inputStream;
48-
return this;
49-
}
50-
51-
public Builder WithContentType(string contentType)
52-
{
53-
ContentType = contentType;
54-
return this;
55-
}
56-
57-
public Builder WithLength(long length)
58-
{
59-
Length = length;
60-
return this;
61-
}
62-
63-
public Builder WithFileName(string fileName)
64-
{
65-
FileName = fileName;
66-
return this;
67-
}
68-
69-
public Builder WithListener(IProgressListener listener)
70-
{
71-
Listener = listener;
72-
return this;
73-
}
74-
75-
public Builder WithVoiceNote(bool voiceNote)
76-
{
77-
VoiceNote = voiceNote;
78-
return this;
44+
private int Height;
45+
46+
internal Builder()
47+
{
48+
}
49+
50+
public Builder WithStream(Stream inputStream)
51+
{
52+
InputStream = inputStream;
53+
return this;
54+
}
55+
56+
public Builder WithContentType(string contentType)
57+
{
58+
ContentType = contentType;
59+
return this;
60+
}
61+
62+
public Builder WithLength(long length)
63+
{
64+
Length = length;
65+
return this;
66+
}
67+
68+
public Builder WithFileName(string fileName)
69+
{
70+
FileName = fileName;
71+
return this;
72+
}
73+
74+
public Builder WithListener(IProgressListener listener)
75+
{
76+
Listener = listener;
77+
return this;
78+
}
79+
80+
public Builder WithVoiceNote(bool voiceNote)
81+
{
82+
VoiceNote = voiceNote;
83+
return this;
7984
}
8085

8186
public Builder WithWidth(int width)
@@ -88,36 +93,36 @@ public Builder WithHeight(int height)
8893
{
8994
Height = height;
9095
return this;
91-
}
92-
93-
public SignalServiceAttachmentStream Build()
94-
{
95-
if (InputStream == null)
96-
{
97-
throw new ArgumentException("Must specify stream!");
98-
}
99-
if (ContentType == null)
100-
{
101-
throw new ArgumentException("No content type specified!");
102-
}
103-
if (Length == 0)
104-
{
105-
throw new ArgumentException("No length specified!");
106-
}
107-
108-
return new SignalServiceAttachmentStream(InputStream, ContentType, (uint)Length, FileName, VoiceNote, null, Width, Height, Listener);
109-
}
110-
}
111-
112-
public interface IProgressListener
113-
{
114-
/// <summary>
115-
/// Called on a progress change event.
116-
/// </summary>
117-
/// <param name="total">The total amount of transmit/receive in bytes.</param>
118-
/// <param name="progress">The amount that has been transmitted/received in bytes thus far</param>
119-
void OnAttachmentProgress(long total, long progress);
120-
}
96+
}
97+
98+
public SignalServiceAttachmentStream Build()
99+
{
100+
if (InputStream == null)
101+
{
102+
throw new ArgumentException("Must specify stream!");
103+
}
104+
if (ContentType == null)
105+
{
106+
throw new ArgumentException("No content type specified!");
107+
}
108+
if (Length == 0)
109+
{
110+
throw new ArgumentException("No length specified!");
111+
}
112+
113+
return new SignalServiceAttachmentStream(InputStream, ContentType, (uint)Length, FileName, VoiceNote, null, Width, Height, Listener);
114+
}
115+
}
116+
117+
public interface IProgressListener
118+
{
119+
/// <summary>
120+
/// Called on a progress change event.
121+
/// </summary>
122+
/// <param name="total">The total amount of transmit/receive in bytes.</param>
123+
/// <param name="progress">The amount that has been transmitted/received in bytes thus far</param>
124+
void OnAttachmentProgress(long total, long progress);
125+
}
121126
}
122-
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
123-
}
127+
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
128+
}

libsignal-service-dotnet/push/PushServiceSocket.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using libsignalservice.profiles;
1111
using libsignalservice.push.exceptions;
1212
using libsignalservice.util;
13+
using Microsoft.Extensions.Logging;
1314
using Newtonsoft.Json;
1415

1516
using System;
@@ -58,6 +59,7 @@ internal class PushServiceSocket
5859

5960
private static readonly string PROFILE_PATH = "/v1/profile/%s";
6061

62+
private readonly ILogger Logger = LibsignalLogging.CreateLogger<PushServiceSocket>();
6163
private readonly SignalServiceConfiguration SignalConnectionInformation;
6264
private readonly ICredentialsProvider CredentialsProvider;
6365
private readonly string UserAgent;
@@ -337,7 +339,7 @@ public async Task<bool> SetCurrentSignedPreKey(CancellationToken token, SignedPr
337339
{
338340
var (id, location) = await RetrieveAttachmentUploadUrl(token);
339341

340-
byte[] digest = await UploadAttachment("PUT", location, attachment.Data,
342+
byte[] digest = await UploadAttachment(token, "PUT", location, attachment.Data,
341343
attachment.DataSize, attachment.OutputFactory, attachment.Listener);
342344

343345
return (id, digest);
@@ -364,7 +366,7 @@ public async Task<bool> SetCurrentSignedPreKey(CancellationToken token, SignedPr
364366
public async Task RetrieveAttachment(CancellationToken token, string relay, ulong attachmentId, Stream tmpDestination, int maxSizeBytes)
365367
{
366368
string attachmentUrlLocation = await RetrieveAttachmentDownloadUrl(token, relay, attachmentId);
367-
await DownloadAttachment(attachmentUrlLocation, tmpDestination);
369+
await DownloadAttachment(token, attachmentUrlLocation, tmpDestination);
368370
}
369371

370372
/// <summary>
@@ -490,7 +492,7 @@ public void CancelInFlightRequests()
490492
throw new NotImplementedException();
491493
}
492494

493-
private async Task DownloadAttachment(string url, Stream localDestination)
495+
private async Task DownloadAttachment(CancellationToken token, string url, Stream localDestination)
494496
{
495497
try
496498
{
@@ -500,7 +502,7 @@ private async Task DownloadAttachment(string url, Stream localDestination)
500502
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);
501503
req.Content = new StringContent("");
502504
req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
503-
using (var resp = await connection.SendAsync(req))
505+
using (var resp = await connection.SendAsync(req, token))
504506
{
505507
Stream input = await resp.Content.ReadAsStreamAsync();
506508
byte[] buffer = new byte[4096];
@@ -526,19 +528,26 @@ private async Task DownloadAttachment(string url, Stream localDestination)
526528
}
527529
}
528530

529-
private async Task<byte[]> UploadAttachment(string method, string url, Stream data, long dataSize,
531+
private async Task<byte[]> UploadAttachment(CancellationToken token, string method, string url, Stream data, long dataSize,
530532
OutputStreamFactory outputStreamFactory, IProgressListener listener)
531533
{
534+
// buffer payload in memory...
532535
MemoryStream tmpStream = new MemoryStream();
533536
DigestingOutputStream outputStream = outputStreamFactory.CreateFor(tmpStream);
534537
StreamContent streamContent = new StreamContent(tmpStream);
535-
var request = new HttpRequestMessage(HttpMethod.Put, url);
536-
request.Content = streamContent;
538+
data.CopyTo(outputStream);
539+
outputStream.Flush();
540+
tmpStream.Position = 0;
541+
542+
// ... and upload it!
543+
var request = new HttpRequestMessage(HttpMethod.Put, url)
544+
{
545+
Content = new StreamContent(tmpStream)
546+
};
537547
request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
538548
request.Headers.ConnectionClose = true;
539-
540549
HttpClient client = new HttpClient();
541-
HttpResponseMessage response = await client.SendAsync(request);
550+
HttpResponseMessage response = await client.SendAsync(request, token);
542551
if (response.StatusCode != HttpStatusCode.OK)
543552
{
544553
throw new IOException($"Bad response: {response.StatusCode} {await response.Content.ReadAsStringAsync()}");

0 commit comments

Comments
 (0)