Exemplo n.º 1
0
        internal static SignalConversation UpdateMessageRead(long timestamp)
        {
            SignalConversation conversation;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    var message = ctx.Messages
                                  .Where(m => m.ComposedTimestamp == timestamp)
                                  .First(); //TODO care about early reads or messages with the same timestamp sometime
                    conversation = GetSignalConversationByThreadId(ctx, message.ThreadId);
                    var currentLastSeenMessage = ctx.Messages
                                                 .Where(m => m.ThreadId == conversation.ThreadId)
                                                 .Skip((int)conversation.LastSeenMessageIndex - 1)
                                                 .Take(1)
                                                 .Single();
                    if (message.Id > currentLastSeenMessage.Id)
                    {
                        var diff = (uint)ctx.Messages
                                   .Where(m => m.ThreadId == conversation.ThreadId && m.Id <= message.Id && m.Id > currentLastSeenMessage.Id)
                                   .Count();
                        conversation.LastSeenMessageIndex += diff;
                        if (diff > conversation.UnreadCount)
                        {
                            throw new InvalidOperationException($"UpdateMessageRead encountered an inconsistent state: {diff} > {conversation.UnreadCount}");
                        }
                        conversation.UnreadCount -= diff;
                        ctx.SaveChanges();
                    }
                }
            }
            return(conversation);
        }
Exemplo n.º 2
0
 public static void UpdateExpiresInLocked(SignalConversation thread, uint exp)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             if (!thread.ThreadId.EndsWith("="))
             {
                 var contact = ctx.Contacts
                               .Where(c => c.ThreadId == thread.ThreadId)
                               .SingleOrDefault();
                 if (contact != null)
                 {
                     contact.ExpiresInSeconds = exp;
                 }
             }
             else
             {
                 var group = ctx.Groups
                             .Where(c => c.ThreadId == thread.ThreadId)
                             .SingleOrDefault();
                 if (group != null)
                 {
                     group.ExpiresInSeconds = exp;
                 }
             }
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 3
0
        public static SignalGroup GetOrCreateGroupLocked(string groupId, long timestamp)
        {
            SignalGroup dbgroup;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    dbgroup = ctx.Groups
                              .Where(g => g.ThreadId == groupId)
                              .Include(g => g.GroupMemberships)
                              .ThenInclude(gm => gm.Contact)
                              .SingleOrDefault();
                    if (dbgroup == null)
                    {
                        dbgroup = new SignalGroup()
                        {
                            ThreadId            = groupId,
                            ThreadDisplayName   = "Unknown group",
                            LastActiveTimestamp = timestamp,
                            AvatarFile          = null,
                            UnreadCount         = 0,
                            CanReceive          = false,
                            GroupMemberships    = new List <GroupMembership>()
                        };
                        ctx.Add(dbgroup);
                        ctx.SaveChanges();
                    }
                }
            }
            return(dbgroup);
        }
Exemplo n.º 4
0
        public static void UpdateMessageStatus(SignalMessage outgoingSignalMessage, MainPageViewModel mpvm)
        {
            SignalMessage m;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    m = ctx.Messages.Single(t => t.ComposedTimestamp == outgoingSignalMessage.ComposedTimestamp && t.Author == null);
                    if (m != null)
                    {
                        if (outgoingSignalMessage.Status == SignalMessageStatus.Confirmed)
                        {
                            if (m.Receipts > 0)
                            {
                                m.Status = SignalMessageStatus.Received;
                            }
                            else
                            {
                                m.Status = SignalMessageStatus.Confirmed;
                            }
                        }
                        else
                        {
                            m.Status = outgoingSignalMessage.Status;
                        }
                        ctx.SaveChanges();
                    }
                }
            }
            Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                mpvm.UIUpdateMessageBox(m);
            }).AsTask().Wait();
        }
Exemplo n.º 5
0
        public static SignalMessage IncreaseReceiptCountLocked(SignalServiceEnvelope envelope)
        {
            SignalMessage m;
            bool          set_mark = false;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    m = ctx.Messages.SingleOrDefault(t => t.ComposedTimestamp == envelope.GetTimestamp());
                    if (m != null)
                    {
                        m.Receipts++;
                        if (m.Status == SignalMessageStatus.Confirmed)
                        {
                            m.Status = SignalMessageStatus.Received;
                            set_mark = true;
                        }
                    }
                    else
                    {
                        ctx.EarlyReceipts.Add(new SignalEarlyReceipt()
                        {
                            DeviceId  = (uint)envelope.GetSourceDevice(),
                            Timestamp = envelope.GetTimestamp(),
                            Username  = envelope.GetSourceE164()
                        });
                    }
                    ctx.SaveChanges();
                }
            }
            return(set_mark ? m : null);
        }
Exemplo n.º 6
0
        public static SignalContact GetOrCreateContactLocked(string username, long timestamp)
        {
            SignalContact contact;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    contact = ctx.Contacts
                              .Where(c => c.ThreadId == username)
                              .SingleOrDefault();
                    if (contact == null)
                    {
                        contact = new SignalContact()
                        {
                            ThreadId            = username,
                            ThreadDisplayName   = username,
                            CanReceive          = true,
                            LastActiveTimestamp = timestamp,
                            Color = null //Utils.CalculateDefaultColor(username)
                        };
                        ctx.Contacts.Add(contact);
                        ctx.SaveChanges();
                    }
                }
            }
            return(contact);
        }
Exemplo n.º 7
0
        public static void DeleteMessage(SignalMessage message)
        {
            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    ctx.Remove(message);
                    SignalConversation conversation = ctx.Contacts
                                                      .Where(c => c.ThreadId == message.ThreadId)
                                                      .Single();
                    conversation.MessagesCount       -= 1;
                    conversation.LastMessage          = null;
                    conversation.LastMessageId        = null;
                    conversation.LastSeenMessage      = null;
                    conversation.LastSeenMessageIndex = ctx.Messages
                                                        .Where(m => m.ThreadId == conversation.ThreadId)
                                                        .Count() - 1;

                    // also delete fts message
                    SignalMessageContent ftsMessage = ctx.Messages_fts.Where(m => m == message.Content)
                                                      .Single();
                    ctx.Remove(ftsMessage);
                    ctx.SaveChanges();
                }
            }
        }
Exemplo n.º 8
0
        public static SignalMessage UpdateMessageStatus(SignalMessage outgoingSignalMessage)
        {
            SignalMessage m;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    m = ctx.Messages.Single(t => t.ComposedTimestamp == outgoingSignalMessage.ComposedTimestamp && t.Author == null);
                    if (m != null)
                    {
                        if (outgoingSignalMessage.Status == SignalMessageStatus.Confirmed)
                        {
                            if (m.Receipts > 0)
                            {
                                m.Status = SignalMessageStatus.Received;
                            }
                            else
                            {
                                m.Status = SignalMessageStatus.Confirmed;
                            }
                        }
                        else
                        {
                            m.Status = outgoingSignalMessage.Status;
                        }
                        ctx.SaveChanges();
                    }
                    return(m);
                }
            }
        }
        private static LinkedList <SignalMessage> InsertIdentityChangedMessages(string number)
        {
            long now = Util.CurrentTimeMillis();
            LinkedList <SignalMessage> messages = new LinkedList <SignalMessage>();

            using (var ctx = new SignalDBContext())
            {
                SignalContact contact = SignalDBContext.GetSignalContactByThreadId(ctx, number);
                if (contact != null)
                {
                    string        str = $"Your safety numbers with {contact.ThreadDisplayName} have changed.";
                    SignalMessage msg = new SignalMessage()
                    {
                        Author            = contact,
                        ComposedTimestamp = now,
                        ReceivedTimestamp = now,
                        Direction         = SignalMessageDirection.Incoming,
                        Type       = SignalMessageType.IdentityKeyChange,
                        ThreadId   = contact.ThreadId,
                        ThreadGuid = contact.ThreadGuid,
                        Content    = new SignalMessageContent()
                        {
                            Content = str
                        }
                    };
                    contact.LastMessage    = msg;
                    contact.MessagesCount += 1;
                    contact.UnreadCount   += 1;
                    ctx.Messages.Add(msg);
                    messages.AddLast(msg);
                    var groups = ctx.GroupMemberships
                                 .Where(gm => gm.ContactId == contact.Id)
                                 .Include(gm => gm.Group);
                    foreach (var gm in groups)
                    {
                        msg = new SignalMessage()
                        {
                            Author            = contact,
                            ComposedTimestamp = now,
                            ReceivedTimestamp = now,
                            Direction         = SignalMessageDirection.Incoming,
                            Type       = SignalMessageType.IdentityKeyChange,
                            ThreadId   = gm.Group.ThreadId,
                            ThreadGuid = gm.Group.ThreadGuid,
                            Content    = new SignalMessageContent()
                            {
                                Content = str
                            }
                        };
                        gm.Group.LastMessage    = msg;
                        gm.Group.MessagesCount += 1;
                        gm.Group.UnreadCount   += 1;
                        ctx.Messages.Add(msg);
                        messages.AddLast(msg);
                    }
                }
                ctx.SaveChanges();
            }
            return(messages);
        }
Exemplo n.º 10
0
        public static SignalGroup InsertOrUpdateGroupLocked(string groupId, string displayname, string avatarfile, bool canReceive, long timestamp, MainPageViewModel mpvm)
        {
            SignalGroup dbgroup;
            bool        is_new = false;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    dbgroup = ctx.Groups
                              .Where(g => g.ThreadId == groupId)
                              .Include(g => g.GroupMemberships)
                              .ThenInclude(gm => gm.Contact)
                              .SingleOrDefault();
                    if (dbgroup == null)
                    {
                        is_new  = true;
                        dbgroup = new SignalGroup()
                        {
                            ThreadId            = groupId,
                            ThreadDisplayName   = displayname,
                            LastActiveTimestamp = timestamp,
                            AvatarFile          = avatarfile,
                            UnreadCount         = 0,
                            CanReceive          = canReceive,
                            GroupMemberships    = new List <GroupMembership>()
                        };
                        ctx.Add(dbgroup);
                    }
                    else
                    {
                        dbgroup.ThreadDisplayName   = displayname;
                        dbgroup.LastActiveTimestamp = timestamp;
                        dbgroup.AvatarFile          = avatarfile;
                        dbgroup.CanReceive          = true;
                    }
                    ctx.SaveChanges();
                }
                if (is_new)
                {
                    Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        mpvm.AddThread(dbgroup);
                    }).AsTask().Wait();
                }
                else
                {
                    Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        mpvm.UIUpdateThread(dbgroup);
                    }).AsTask().Wait();
                }
            }
            return(dbgroup);
        }
Exemplo n.º 11
0
 public static void UpdateAttachmentLocked(SignalAttachment sa)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             ctx.Attachments.Update(sa);
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 12
0
 public static void DeleteAttachment(SignalAttachment attachment)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             ctx.Remove(attachment);
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 13
0
 public static void SaveMessageLocked(SignalMessage message)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             SaveMessage(ctx, message);
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 14
0
 public static void UpdateMessageExpiresAt(SignalMessage message)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             var m = ctx.Messages.Single(t => t.Id == message.Id);
             m.ExpiresAt = message.ExpiresAt;
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 15
0
 public static void FailAllPendingMessages()
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             var messages = ctx.Messages
                            .Where(m => m.Direction == SignalMessageDirection.Outgoing && m.Status == SignalMessageStatus.Pending).ToList();
             messages.ForEach(m => m.Status = SignalMessageStatus.Failed_Unknown);
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 16
0
 internal static void UpdateAttachmentStatus(SignalAttachment attachment)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             var savedAttachment = ctx.Attachments
                                   .Where(a => a.Id == attachment.Id)
                                   .First();
             savedAttachment.Status = attachment.Status;
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 17
0
 public static void UpdateExpiresInLocked(SignalConversation thread)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             var dbConversation = GetSignalConversationByThreadId(ctx, thread.ThreadId);
             if (dbConversation != null)
             {
                 dbConversation.ExpiresInSeconds = thread.ExpiresInSeconds;
                 ctx.SaveChanges();
             }
         }
     }
 }
Exemplo n.º 18
0
 public static void InsertOrUpdateConversationLocked(SignalConversation conversation)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             if (conversation is SignalContact contact)
             {
                 var c = ctx.Contacts.SingleOrDefault(b => b.ThreadId == conversation.ThreadId);
                 if (c == null)
                 {
                     ctx.Contacts.Add(contact);
                 }
                 else
                 {
                     c.Color             = contact.Color;
                     c.ThreadId          = conversation.ThreadId;
                     c.ThreadDisplayName = conversation.ThreadDisplayName;
                     c.CanReceive        = conversation.CanReceive;
                     c.AvatarFile        = conversation.AvatarFile;
                     c.Draft             = conversation.Draft;
                     c.UnreadCount       = conversation.UnreadCount;
                 }
             }
             else if (conversation is SignalGroup group)
             {
                 var c = ctx.Groups.SingleOrDefault(b => b.ThreadId == conversation.ThreadId);
                 if (c == null)
                 {
                     ctx.Groups.Add(group);
                 }
                 else
                 {
                     c.ThreadId          = conversation.ThreadId;
                     c.ThreadDisplayName = conversation.ThreadDisplayName;
                     c.CanReceive        = conversation.CanReceive;
                     c.AvatarFile        = conversation.AvatarFile;
                     c.Draft             = conversation.Draft;
                     c.UnreadCount       = conversation.UnreadCount;
                 }
             }
             ctx.SaveChanges();
         }
     }
 }
Exemplo n.º 19
0
 public static void InsertOrUpdateGroupMembershipLocked(long groupid, long memberid)
 {
     lock (DBLock)
     {
         using (var ctx = new SignalDBContext())
         {
             var old = ctx.GroupMemberships.Where(g => g.GroupId == groupid && g.ContactId == memberid).SingleOrDefault();
             if (old == null)
             {
                 ctx.GroupMemberships.Add(new GroupMembership()
                 {
                     ContactId = memberid,
                     GroupId   = groupid
                 });
                 ctx.SaveChanges();
             }
         }
     }
 }
Exemplo n.º 20
0
        public static void InsertOrUpdateContactLocked(SignalContact contact, MainPageViewModel mpvm)
        {
            bool is_new = false;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    var c = ctx.Contacts.SingleOrDefault(b => b.ThreadId == contact.ThreadId);
                    if (c == null)
                    {
                        is_new = true;
                        ctx.Contacts.Add(contact);
                    }
                    else
                    {
                        c.Color             = contact.Color;
                        c.ThreadId          = contact.ThreadId;
                        c.ThreadDisplayName = contact.ThreadDisplayName;
                        c.CanReceive        = contact.CanReceive;
                        c.AvatarFile        = contact.AvatarFile;
                        c.Draft             = contact.Draft;
                        c.UnreadCount       = contact.UnreadCount;
                    }
                    ctx.SaveChanges();
                }
                if (is_new)
                {
                    Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        mpvm.AddThread(contact);
                    }).AsTask().Wait();
                }
                else
                {
                    Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        mpvm.UIUpdateThread(contact);
                    }).AsTask().Wait();
                }
            }
        }
Exemplo n.º 21
0
        public static SignalGroup InsertOrUpdateGroupLocked(string groupId, string displayname, string avatarfile, bool canReceive, long timestamp)
        {
            SignalGroup dbgroup;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    dbgroup = ctx.Groups
                              .Where(g => g.ThreadId == groupId)
                              .Include(g => g.GroupMemberships)
                              .ThenInclude(gm => gm.Contact)
                              .SingleOrDefault();
                    if (dbgroup == null)
                    {
                        dbgroup = new SignalGroup()
                        {
                            ThreadId            = groupId,
                            ThreadDisplayName   = displayname,
                            LastActiveTimestamp = timestamp,
                            AvatarFile          = avatarfile,
                            UnreadCount         = 0,
                            CanReceive          = canReceive,
                            GroupMemberships    = new List <GroupMembership>()
                        };
                        ctx.Add(dbgroup);
                    }
                    else
                    {
                        dbgroup.ThreadDisplayName   = displayname;
                        dbgroup.LastActiveTimestamp = timestamp;
                        dbgroup.AvatarFile          = avatarfile;
                        dbgroup.CanReceive          = true;
                    }
                    ctx.SaveChanges();
                }
            }
            return(dbgroup);
        }
Exemplo n.º 22
0
        public static void IncreaseReceiptCountLocked(SignalServiceEnvelope envelope, MainPageViewModel mpvm)
        {
            SignalMessage m;
            bool          set_mark = false;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    m = ctx.Messages.SingleOrDefault(t => t.ComposedTimestamp == envelope.getTimestamp());
                    if (m != null)
                    {
                        m.Receipts++;
                        if (m.Status == SignalMessageStatus.Confirmed)
                        {
                            m.Status = SignalMessageStatus.Received;
                            set_mark = true;
                        }
                    }
                    else
                    {
                        ctx.EarlyReceipts.Add(new SignalEarlyReceipt()
                        {
                            DeviceId  = (uint)envelope.getSourceDevice(),
                            Timestamp = envelope.getTimestamp(),
                            Username  = envelope.getSource()
                        });
                    }
                    ctx.SaveChanges();
                }
            }
            if (set_mark)
            {
                Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    mpvm.UIUpdateMessageBox(m);
                }).AsTask().Wait();
            }
        }
Exemplo n.º 23
0
        public static SignalContact GetOrCreateContactLocked(string username, long timestamp, MainPageViewModel mpvm)
        {
            SignalContact contact;
            bool          is_new = false;

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    contact = ctx.Contacts
                              .Where(c => c.ThreadId == username)
                              .SingleOrDefault();
                    if (contact == null)
                    {
                        is_new  = true;
                        contact = new SignalContact()
                        {
                            ThreadId            = username,
                            ThreadDisplayName   = username,
                            CanReceive          = true,
                            LastActiveTimestamp = timestamp,
                            Color = Utils.Colors[Utils.CalculateDefaultColorIndex(username)]
                        };
                        ctx.Contacts.Add(contact);
                        ctx.SaveChanges();
                    }
                }
                if (is_new)
                {
                    Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                    {
                        mpvm.AddThread(contact);
                    }).AsTask().Wait();
                }
            }
            return(contact);
        }
Exemplo n.º 24
0
        public static void SaveMessageLocked(SignalMessage message)
        {
            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    long timestamp;
                    if (message.Direction == SignalMessageDirection.Synced)
                    {
                        var receipts = ctx.EarlyReceipts
                                       .Where(er => er.Timestamp == message.ComposedTimestamp)
                                       .ToList();

                        message.Receipts = (uint)receipts.Count;
                        ctx.EarlyReceipts.RemoveRange(receipts);
                        if (message.Receipts > 0)
                        {
                            message.Status = SignalMessageStatus.Received;
                        }
                    }
                    if (message.Author != null)
                    {
                        timestamp      = message.ReceivedTimestamp;
                        message.Author = ctx.Contacts.Where(a => a.Id == message.Author.Id).Single();
                    }
                    else
                    {
                        timestamp = message.ComposedTimestamp;
                    }
                    if (!message.ThreadId.EndsWith("="))
                    {
                        var contact = ctx.Contacts
                                      .Where(c => c.ThreadId == message.ThreadId)
                                      .Single();
                        contact.LastActiveTimestamp = timestamp;
                        contact.LastMessage         = message;
                        contact.MessagesCount      += 1;
                        if (message.Author == null)
                        {
                            contact.UnreadCount          = 0;
                            contact.LastSeenMessageIndex = contact.MessagesCount;
                        }
                        else
                        {
                            contact.UnreadCount += 1;
                        }
                    }
                    else
                    {
                        var group = ctx.Groups
                                    .Where(c => c.ThreadId == message.ThreadId)
                                    .Single();
                        message.ExpiresAt         = group.ExpiresInSeconds;
                        group.LastActiveTimestamp = timestamp;
                        group.LastMessage         = message;
                        group.MessagesCount      += 1;
                        if (message.Author == null)
                        {
                            group.UnreadCount          = 0;
                            group.LastSeenMessageIndex = group.MessagesCount;
                        }
                        else
                        {
                            group.UnreadCount += 1;
                        }
                    }
                    ctx.Messages.Add(message);
                    ctx.SaveChanges();
                }
            }
        }
Exemplo n.º 25
0
        public static LinkedList <SignalMessage> InsertIdentityChangedMessages(string number)
        {
            long now = Util.CurrentTimeMillis();
            LinkedList <SignalMessage> messages = new LinkedList <SignalMessage>();

            lock (DBLock)
            {
                using (var ctx = new SignalDBContext())
                {
                    SignalContact contact = ctx.Contacts
                                            .Where(c => c.ThreadId == number)
                                            .SingleOrDefault();
                    if (contact != null)
                    {
                        string        str = $"Your safety numbers with {contact.ThreadDisplayName} have changed.";
                        SignalMessage msg = new SignalMessage()
                        {
                            Author            = contact,
                            ComposedTimestamp = now,
                            ReceivedTimestamp = now,
                            Direction         = SignalMessageDirection.Incoming,
                            Type     = SignalMessageType.IdentityKeyChange,
                            ThreadId = contact.ThreadId,
                            Content  = new SignalMessageContent()
                            {
                                Content = str
                            }
                        };
                        contact.LastMessage    = msg;
                        contact.MessagesCount += 1;
                        ctx.Messages.Add(msg);
                        messages.AddLast(msg);
                        var groups = ctx.GroupMemberships
                                     .Where(gm => gm.ContactId == contact.Id)
                                     .Include(gm => gm.Group);
                        foreach (var gm in groups)
                        {
                            msg = new SignalMessage()
                            {
                                Author            = contact,
                                ComposedTimestamp = now,
                                ReceivedTimestamp = now,
                                Direction         = SignalMessageDirection.Incoming,
                                Type     = SignalMessageType.IdentityKeyChange,
                                ThreadId = gm.Group.ThreadId,
                                Content  = new SignalMessageContent()
                                {
                                    Content = str
                                }
                            };
                            gm.Group.LastMessage    = msg;
                            gm.Group.MessagesCount += 1;
                            ctx.Messages.Add(msg);
                            messages.AddLast(msg);
                        }
                    }
                    else
                    {
                        Debug.WriteLine("InsertIdentityChangedMessages for non-existing contact!");
                    }
                    ctx.SaveChanges();
                }
            }
            return(messages);
        }