Skip to content

Commit e02ace9

Browse files
committed
move attachment handling to handle, fix conversations growing async
1 parent bcda48b commit e02ace9

File tree

5 files changed

+154
-86
lines changed

5 files changed

+154
-86
lines changed

Signal-Windows.Lib/SignalLibHandle.cs

Lines changed: 131 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,12 @@ namespace Signal_Windows.Lib
2525
public interface ISignalFrontend
2626
{
2727
void AddOrUpdateConversation(SignalConversation conversation, SignalMessage updateMessage);
28-
Task HandleMessage(SignalMessage message, SignalConversation conversation);
29-
Task HandleIdentitykeyChange(LinkedList<SignalMessage> messages);
28+
void HandleMessage(SignalMessage message, SignalConversation conversation);
29+
void HandleIdentitykeyChange(LinkedList<SignalMessage> messages);
3030
void HandleMessageUpdate(SignalMessage updatedMessage);
3131
void ReplaceConversationList(List<SignalConversation> conversations);
3232
void HandleAuthFailure();
33-
void HandleAttachmentStatusChanged(SignalAttachment sa, SignalAttachmentStatus newStatus);
33+
void HandleAttachmentStatusChanged(SignalAttachment sa);
3434
}
3535

3636
public interface ISignalLibHandle
@@ -84,6 +84,7 @@ internal class SignalLibHandle : ISignalLibHandle
8484
private SignalServiceMessageReceiver MessageReceiver;
8585
public BlockingCollection<SignalMessage> OutgoingQueue = new BlockingCollection<SignalMessage>(new ConcurrentQueue<SignalMessage>());
8686
private EventWaitHandle GlobalResetEvent;
87+
private ISet<DownloadOperation> Downloads = new HashSet<DownloadOperation>();
8788

8889
public event EventHandler<SignalMessageEventArgs> SignalMessageEvent;
8990

@@ -166,6 +167,7 @@ public async Task Acquire(CoreDispatcher d, ISignalFrontend w) //TODO wrap tryca
166167
await Task.Run(() =>
167168
{
168169
InitNetwork();
170+
RecoverDownloads().Wait();
169171
});
170172
await failTask; // has to complete before messages are loaded
171173
Running = true;
@@ -180,6 +182,7 @@ public void BackgroundAcquire()
180182
SignalDBContext.FailAllPendingMessages();
181183
Store = LibsignalDBContext.GetSignalStore();
182184
InitNetwork();
185+
RecoverDownloads().Wait();
183186
Running = true;
184187
}
185188

@@ -206,6 +209,8 @@ await Task.Run(() =>
206209
}
207210
Task.WaitAll(tasks.ToArray());
208211
InitNetwork();
212+
Downloads.Clear();
213+
RecoverDownloads().Wait();
209214
});
210215
Running = true;
211216
Logger.LogTrace("Reacquire() releasing");
@@ -240,15 +245,22 @@ public void BackgroundRelease()
240245

241246
public async Task SendMessage(SignalMessage message, SignalConversation conversation)
242247
{
243-
await Task.Run(async () =>
248+
await Task.Run(() =>
244249
{
245250
Logger.LogTrace("SendMessage() locking");
246-
await SemaphoreSlim.WaitAsync(CancelSource.Token);
247-
Logger.LogDebug("SendMessage saving message " + message.ComposedTimestamp);
248-
SaveAndDispatchSignalMessage(message, conversation);
249-
OutgoingQueue.Add(message);
250-
SemaphoreSlim.Release();
251-
Logger.LogTrace("SendMessage() released");
251+
SemaphoreSlim.Wait(CancelSource.Token);
252+
Logger.LogTrace("SendMessage() locked");
253+
try
254+
{
255+
Logger.LogDebug("SendMessage saving message " + message.ComposedTimestamp);
256+
SaveAndDispatchSignalMessage(message, conversation);
257+
OutgoingQueue.Add(message);
258+
}
259+
finally
260+
{
261+
SemaphoreSlim.Release();
262+
Logger.LogTrace("SendMessage() released");
263+
}
252264
});
253265
}
254266

@@ -286,26 +298,6 @@ public void DecryptAttachment(SignalServiceAttachmentPointer pointer, Stream tem
286298
{
287299
MessageReceiver.DecryptAttachment(pointer, tempStream, downloadStream);
288300
}
289-
290-
public async Task HandleDownload(DownloadOperation download, bool start, SignalAttachment attachment)
291-
{
292-
if (start)
293-
{
294-
await download.StartAsync();
295-
}
296-
else
297-
{
298-
await download.AttachAsync();
299-
}
300-
IStorageFile tempFile = download.ResultFile;
301-
StorageFile downloadedFile = await DownloadsFolder.CreateFileAsync(attachment.SentFileName, CreationCollisionOption.GenerateUniqueName);
302-
using (var tempFileStream = (await tempFile.OpenAsync(FileAccessMode.ReadWrite)).AsStream())
303-
using (var downloadedFileStream = (await downloadedFile.OpenAsync(FileAccessMode.ReadWrite)).AsStream())
304-
{
305-
DecryptAttachment(attachment.ToAttachmentPointer(), tempFileStream, downloadedFileStream);
306-
}
307-
await tempFile.DeleteAsync();
308-
}
309301
#endregion
310302

311303
#region attachment api
@@ -327,21 +319,23 @@ internal void SaveAndDispatchSignalMessage(SignalMessage message, SignalConversa
327319
{
328320
conversation.UnreadCount = 0;
329321
conversation.LastSeenMessageIndex = conversation.MessagesCount;
322+
330323
}
331324
SignalDBContext.SaveMessageLocked(message);
332325
conversation.LastMessage = message;
333326
conversation.LastActiveTimestamp = message.ComposedTimestamp;
327+
StartAttachmentDownloads(message);
334328
DispatchHandleMessage(message, conversation);
335329
}
336330

337331
internal void DispatchHandleIdentityKeyChange(LinkedList<SignalMessage> messages)
338332
{
339-
List<Task> operations = new List<Task>(); ;
333+
List<Task> operations = new List<Task>();
340334
foreach (var dispatcher in Frames.Keys)
341335
{
342-
operations.Add(dispatcher.RunTaskAsync(async () =>
336+
operations.Add(dispatcher.RunTaskAsync(() =>
343337
{
344-
await Frames[dispatcher].HandleIdentitykeyChange(messages);
338+
Frames[dispatcher].HandleIdentitykeyChange(messages);
345339
}));
346340
}
347341
Task.WaitAll(operations.ToArray());
@@ -365,9 +359,9 @@ internal void DispatchHandleMessage(SignalMessage message, SignalConversation co
365359
List<Task> operations = new List<Task>();
366360
foreach (var dispatcher in Frames.Keys)
367361
{
368-
operations.Add(dispatcher.RunTaskAsync(async () =>
362+
operations.Add(dispatcher.RunTaskAsync(() =>
369363
{
370-
await Frames[dispatcher].HandleMessage(message, conversation);
364+
Frames[dispatcher].HandleMessage(message, conversation);
371365
}));
372366
}
373367
SignalMessageEvent?.Invoke(this, new SignalMessageEventArgs(message, Events.SignalMessageType.NormalMessage));
@@ -470,6 +464,108 @@ private void InitNetwork()
470464
OutgoingMessages = new OutgoingMessages(CancelSource.Token, MessageSender, this);
471465
OutgoingMessagesTask = Task.Factory.StartNew(() => OutgoingMessages.HandleOutgoingMessages(), TaskCreationOptions.LongRunning);
472466
}
467+
468+
private void StartAttachmentDownloads(SignalMessage message)
469+
{
470+
if (message.Attachments != null)
471+
{
472+
foreach (var attachment in message.Attachments)
473+
{
474+
if (Downloads.Count < 100)
475+
{
476+
SignalServiceAttachmentPointer attachmentPointer = attachment.ToAttachmentPointer();
477+
IStorageFolder localFolder = ApplicationData.Current.LocalFolder;
478+
IStorageFile tmpDownload = Task.Run(async () =>
479+
{
480+
return await ApplicationData.Current.LocalCacheFolder.CreateFileAsync(@"Attachments\" + attachment.Id + ".cipher");
481+
}).Result;
482+
BackgroundDownloader downloader = new BackgroundDownloader();
483+
downloader.SetRequestHeader("Content-Type", "application/octet-stream");
484+
downloader.SuccessToastNotification = LibUtils.CreateToastNotification($"{attachment.SentFileName} has finished downloading.");
485+
downloader.FailureToastNotification = LibUtils.CreateToastNotification($"{attachment.SentFileName} has failed to download.");
486+
// this is the recommended way to call CreateDownload
487+
// see https://docs.microsoft.com/en-us/uwp/api/windows.networking.backgroundtransfer.backgrounddownloader#Methods
488+
DownloadOperation download = downloader.CreateDownload(new Uri(RetrieveAttachmentUrl(attachmentPointer)), tmpDownload);
489+
SignalDBContext.UpdateAttachmentFileName(attachment);
490+
Downloads.Add(download);
491+
Task.Run(async () =>
492+
{
493+
Logger.LogInformation("Waiting for download {0}({1})", attachment.SentFileName, attachment.Id);
494+
await download.StartAsync();
495+
await HandleSuccessfullDownload(attachment, tmpDownload, download);
496+
});
497+
}
498+
}
499+
}
500+
}
501+
502+
private async Task HandleSuccessfullDownload(SignalAttachment attachment, IStorageFile tmpDownload, DownloadOperation download)
503+
{
504+
StorageFile plaintextFile = await ApplicationData.Current.LocalCacheFolder.CreateFileAsync(@"Attachments\" + attachment.Id + ".plain", CreationCollisionOption.ReplaceExisting);
505+
using (var tmpFileStream = (await tmpDownload.OpenAsync(FileAccessMode.ReadWrite)).AsStream())
506+
using (var plaintextFileStream = (await plaintextFile.OpenAsync(FileAccessMode.ReadWrite)).AsStream())
507+
{
508+
Logger.LogInformation("Decrypting to {0}\\{1}", plaintextFile.Path, plaintextFile.Name);
509+
DecryptAttachment(attachment.ToAttachmentPointer(), tmpFileStream, plaintextFileStream);
510+
}
511+
Logger.LogInformation("Deleting tmpFile {0}", tmpDownload.Name);
512+
await tmpDownload.DeleteAsync();
513+
attachment.Status = SignalAttachmentStatus.Finished;
514+
DispatchAttachmentStatusChanged(download, attachment);
515+
}
516+
517+
private async Task RecoverDownloads()
518+
{
519+
var downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
520+
foreach (DownloadOperation download in downloads)
521+
{
522+
try
523+
{
524+
SignalAttachment attachment = SignalDBContext.GetAttachmentByFileNameLocked(download.Guid.ToString());
525+
if (attachment != null)
526+
{
527+
Downloads.Add(download);
528+
var t = Task.Run(async () =>
529+
{
530+
Logger.LogInformation("Attaching to download {0} ({1})", attachment.Id, download.Guid);
531+
await download.AttachAsync();
532+
await HandleSuccessfullDownload(attachment, download.ResultFile, download);
533+
});
534+
}
535+
else
536+
{
537+
Logger.LogInformation("Aborting unrecognized download {0}", download.Guid);
538+
download.AttachAsync().Cancel();
539+
}
540+
}
541+
catch(Exception e)
542+
{
543+
Logger.LogError("TriageDownloads encountered an error: {0}\n{1}", e.Message, e.StackTrace);
544+
}
545+
}
546+
}
547+
548+
private void DispatchAttachmentStatusChanged(DownloadOperation op, SignalAttachment attachment)
549+
{
550+
try
551+
{
552+
SemaphoreSlim.Wait(CancelSource.Token);
553+
Downloads.Remove(op);
554+
List<Task> operations = new List<Task>();
555+
foreach (var dispatcher in Frames.Keys)
556+
{
557+
operations.Add(dispatcher.RunTaskAsync(() =>
558+
{
559+
Frames[dispatcher].HandleAttachmentStatusChanged(attachment);
560+
}));
561+
}
562+
Task.WaitAll(operations.ToArray());
563+
}
564+
finally
565+
{
566+
SemaphoreSlim.Release();
567+
}
568+
}
473569
#endregion
474570
}
475571
}

Signal-Windows.Lib/Storage/DB.cs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -907,27 +907,30 @@ public static SignalMessage IncreaseReceiptCountLocked(SignalServiceEnvelope env
907907

908908
#region Attachments
909909

910-
public static void UpdateAttachmentLocked(SignalAttachment sa)
910+
public static SignalAttachment GetAttachmentByFileNameLocked(string fileName)
911911
{
912912
lock (DBLock)
913913
{
914914
using (var ctx = new SignalDBContext())
915915
{
916-
ctx.Attachments.Update(sa);
917-
ctx.SaveChanges();
916+
return ctx.Attachments
917+
.Where(a => a.FileName == fileName)
918+
.FirstOrDefault();
918919
}
919920
}
920921
}
921922

922-
public static SignalAttachment GetAttachmentByFileNameLocked(string fileName)
923+
internal static void UpdateAttachmentFileName(SignalAttachment attachment)
923924
{
924925
lock (DBLock)
925926
{
926927
using (var ctx = new SignalDBContext())
927928
{
928-
return ctx.Attachments
929-
.Where(a => a.FileName == fileName)
930-
.FirstOrDefault();
929+
var savedAttachment = ctx.Attachments
930+
.Where(a => a.Id == attachment.Id)
931+
.First();
932+
savedAttachment.FileName = attachment.FileName;
933+
ctx.SaveChanges();
931934
}
932935
}
933936
}

Signal-Windows/App.xaml.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,20 +178,6 @@ protected override async void OnLaunched(LaunchActivatedEventArgs e)
178178
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(currentId);
179179
}
180180
}
181-
else
182-
{
183-
await DiscoverDownloads();
184-
}
185-
}
186-
187-
private async Task DiscoverDownloads()
188-
{
189-
var downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
190-
foreach (var download in downloads)
191-
{
192-
SignalAttachment attachment = SignalDBContext.GetAttachmentByFileNameLocked(download.Guid.ToString());
193-
Task downloadTask = SignalLibHandle.Instance.HandleDownload(download, false, attachment);
194-
}
195181
}
196182

197183
private async Task CreateSecondaryWindow(ActivationViewSwitcher switcher, string conversationId)

Signal-Windows/SignalWindowsFrontend.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ public void AddOrUpdateConversation(SignalConversation conversation, SignalMessa
2424
Locator.MainPageInstance.AddOrUpdateConversation(conversation, updateMessage);
2525
}
2626

27-
public async Task HandleIdentitykeyChange(LinkedList<SignalMessage> messages)
27+
public void HandleIdentitykeyChange(LinkedList<SignalMessage> messages)
2828
{
29-
await Locator.MainPageInstance.HandleIdentitykeyChange(messages);
29+
Locator.MainPageInstance.HandleIdentitykeyChange(messages);
3030
}
3131

32-
public async Task HandleMessage(SignalMessage message, SignalConversation conversation)
32+
public void HandleMessage(SignalMessage message, SignalConversation conversation)
3333
{
3434
Locator.MainPageInstance.HandleMessage(message, conversation);
3535
}
@@ -49,9 +49,9 @@ public void HandleAuthFailure()
4949
// TODO
5050
}
5151

52-
public void HandleAttachmentStatusChanged(SignalAttachment sa, SignalAttachmentStatus status)
52+
public void HandleAttachmentStatusChanged(SignalAttachment sa)
5353
{
54-
//TODO
54+
Locator.MainPageInstance.HandleAttachmentStatusChanged(sa);
5555
}
5656
}
5757
}

0 commit comments

Comments
 (0)