Exemple #1
0
        private void OnNewRoosterMessage(IMessageSender sender, NewValidMessageEventArgs args)
        {
            if (args.MESSAGE is RosterResultMessage msg && sender is XMPPClient xmppClient)
            {
                string type = msg.TYPE;
                if (!string.Equals(type, IQMessage.RESULT) && !string.Equals(type, IQMessage.SET))
                {
                    // No roster result or set => return
                    return;
                }

                SemaLock semaLock             = DataCache.INSTANCE.NewChatSemaLock();
                IEnumerable <ChatModel> chats = DataCache.INSTANCE.GetChats(client.dbAccount.bareJid, semaLock);

                // TYPE == SET is being send by the server when the roster changes from outside:
                if (string.Equals(type, IQMessage.RESULT))
                {
                    foreach (ChatModel chat in chats)
                    {
                        chat.inRoster = false;
                    }
                }

                List <ChatModel> addedChats   = new List <ChatModel>();
                List <ChatModel> updatedChats = new List <ChatModel>();

                foreach (RosterItem item in msg.ITEMS)
                {
                    ChatModel chat = chats.Where(c => string.Equals(c.bareJid, item.JID)).FirstOrDefault();
                    if (!(chat is null))
                    {
                        chat.inRoster = !string.Equals(item.SUBSCRIPTION, "remove");
                        updatedChats.Add(chat);
                    }
Exemple #2
0
        //--------------------------------------------------------Events:---------------------------------------------------------------------\\
        #region --Events--
        private void OnOmemoSessionBuildError(XMPPClient xmppClient, OmemoSessionBuildErrorEventArgs args)
        {
            Task.Run(() =>
            {
                ChatModel chat;
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    chat = DataCache.INSTANCE.GetChat(xmppClient.getXMPPAccount().getBareJid(), args.CHAT_JID, semaLock);
                }
                if (!(chat is null))
                {
                    // Add an error chat message:
                    ChatMessageModel msg = new ChatMessageModel()
                    {
                        chatId         = chat.id,
                        date           = DateTime.Now,
                        fromBareJid    = args.CHAT_JID,
                        isCC           = false,
                        isDummyMessage = false,
                        isEncrypted    = false,
                        isFavorite     = false,
                        isImage        = false,
                        message        = "Failed to encrypt and send " + args.MESSAGES.Count + " OMEMO message(s) with:\n" + args.ERROR,
                        stableId       = AbstractMessage.getRandomId(),
                        state          = MessageState.UNREAD,
                        type           = MessageMessage.TYPE_ERROR
                    };
                    DataCache.INSTANCE.AddChatMessage(msg, chat);

                    // Set chat messages to encrypted failed:
                    SetOmemoChatMessagesSendFailed(args.MESSAGES, chat);
                }
            });
        }
Exemple #3
0
        /// <summary>
        /// Returns a new locked <see cref="SemaLock"/>.
        /// </summary>
        public SemaLock NewSemaLock()
        {
            SemaLock l = new SemaLock(LOCK_SEMA);

            l.Wait();
            return(l);
        }
Exemple #4
0
 public void StoreDeviceListSubscription(string bareJid, Tuple <OmemoDeviceListSubscriptionState, DateTime> lastUpdate)
 {
     if (string.Equals(bareJid, dbAccount.bareJid))
     {
         OmemoDeviceListSubscriptionModel subscription = dbAccount.omemoInfo.deviceListSubscription;
         subscription.lastUpdateReceived = lastUpdate.Item2;
         subscription.state = lastUpdate.Item1;
         using (MainDbContext ctx = new MainDbContext())
         {
             ctx.Update(subscription);
         }
     }
     else
     {
         ChatModel chat;
         using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
         {
             chat = DataCache.INSTANCE.GetChat(dbAccount.bareJid, bareJid, semaLock);
         }
         if (chat is null)
         {
             throw new InvalidOperationException("Failed to store device list subscription. Chat '" + bareJid + "' does not exist.");
         }
         OmemoDeviceListSubscriptionModel subscription = chat.omemoInfo.deviceListSubscription;
         subscription.lastUpdateReceived = lastUpdate.Item2;
         subscription.state = lastUpdate.Item1;
         using (MainDbContext ctx = new MainDbContext())
         {
             ctx.Update(subscription);
         }
     }
 }
Exemple #5
0
        public async Task EnterRoomAsync()
        {
            using (SemaLock semaLock = INFO.NewSemaLock())
            {
                // Update MUC info:
                INFO.state = MucState.ENTERING;
                using (MainDbContext ctx = new MainDbContext())
                {
                    // Clear MUC members:
                    INFO.occupants.ForEach(o => o.Remove(ctx, true));
                    INFO.occupants.Clear();
                    INFO.OnOccupantsChanged();
                    ctx.Update(INFO);
                }
            }

            // Create message:
            JoinRoomRequestMessage msg = new JoinRoomRequestMessage(CLIENT.getXMPPAccount().getFullJid(), INFO.chat.bareJid, INFO.nickname, INFO.password);

            // Subscribe to events for receiving answers:
            CLIENT.NewMUCMemberPresenceMessage -= OnMucMemberPresenceMessage;
            CLIENT.NewMUCMemberPresenceMessage += OnMucMemberPresenceMessage;
            CLIENT.NewMUCPresenceErrorMessage  -= OnMucPresenceErrorMessage;
            CLIENT.NewMUCPresenceErrorMessage  += OnMucPresenceErrorMessage;

            // Send message:
            await CLIENT.SendAsync(msg);

            Logger.Info($"Entering MUC room '{INFO.chat.bareJid}' as '{INFO.nickname }'...");
        }
Exemple #6
0
        private async void OnNewPresence(XMPPClient xmppClient, NewPresenceMessageEventArgs args)
        {
            string from = Utils.getBareJidFromFullJid(args.PRESENCE_MESSAGE.getFrom());

            // If received a presence message from your own account, ignore it:
            if (string.Equals(from, client.dbAccount.bareJid))
            {
                return;
            }

            ChatModel chat;

            using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
            {
                chat = DataCache.INSTANCE.GetChat(xmppClient.getXMPPAccount().getBareJid(), from, semaLock);
            }
            if (chat is null)
            {
                Logger.Warn("Received a presence message for an unknown chat from: " + from + ", to: " + client.dbAccount.bareJid);
                return;
            }

            // Answer presence probes:
            // TODO: answer those only for chats we are subscribed to.
            if (string.Equals(args.PRESENCE_MESSAGE.TYPE, "probe"))
            {
                await AnswerPresenceProbeAsync(from, client.dbAccount.bareJid, chat, args.PRESENCE_MESSAGE);

                return;
            }

            if (chat.chatType == ChatType.CHAT)
            {
                if (string.Equals(args.PRESENCE_MESSAGE.TYPE, "subscribe"))
                {
                    chat.subscription = args.PRESENCE_MESSAGE.TYPE;
                }

                if (!string.Equals(args.PRESENCE_MESSAGE.TYPE, "unsubscribe") && !string.Equals(args.PRESENCE_MESSAGE.TYPE, "subscribe"))
                {
                    switch (chat.subscription)
                    {
                    case "from":
                    case "none":
                    case "pending":
                    case null:
                        chat.presence = Presence.Unavailable;
                        break;

                    default:
                        chat.status   = args.PRESENCE_MESSAGE.STATUS;
                        chat.presence = args.PRESENCE_MESSAGE.PRESENCE;
                        break;
                    }
                }

                chat.Update();
            }
        }
Exemple #7
0
        private async Task OnMucErrorMessageAsync(XMPPClient client, MUCErrorMessage errorMessage)
        {
            string room = Utils.getBareJidFromFullJid(errorMessage.getFrom());

            if (room is null)
            {
                return;
            }
            ChatModel chat;

            using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
            {
                chat = DataCache.INSTANCE.GetChat(client.getXMPPAccount().getBareJid(), room, semaLock);
            }
            if (chat is null || chat.muc is null)
            {
                return;
            }
            Logger.Error("Received an error message for MUC: " + chat.bareJid + "\n" + errorMessage.ERROR_MESSAGE);
            StopMucJoinHelper(chat);
            if (chat.muc.state != MucState.DISCONNECTED)
            {
                await SendMucLeaveMessageAsync(client, chat.muc);
            }
            using (SemaLock semaLock = chat.muc.NewSemaLock())
            {
                switch (errorMessage.ERROR_CODE)
                {
                // No access - user got baned:
                case 403:
                    chat.muc.state = MucState.BANED;
                    AddChatInfoMessage(chat, room, "Unable to join room!\nYou are baned from this room.");
                    return;

                default:
                    chat.muc.state = MucState.ERROR;
                    break;
                }
                chat.muc.Update();
            }

            // Add an error chat message:
            ChatMessageModel msg = new ChatMessageModel()
            {
                stableId    = errorMessage.ID ?? AbstractMessage.getRandomId(),
                chatId      = chat.id,
                date        = DateTime.Now,
                fromBareJid = Utils.getBareJidFromFullJid(errorMessage.getFrom()),
                isImage     = false,
                message     = "Code: " + errorMessage.ERROR_CODE + "\nType: " + errorMessage.ERROR_TYPE + "\nMessage:\n" + errorMessage.ERROR_MESSAGE,
                state       = MessageState.UNREAD,
                type        = MessageMessage.TYPE_ERROR
            };

            DataCache.INSTANCE.AddChatMessage(msg, chat);
        }
Exemple #8
0
        protected override async void OnBackgroundActivated(BackgroundActivatedEventArgs args)
        {
            BackgroundTaskDeferral deferral = args.TaskInstance.GetDeferral();

            switch (args.TaskInstance.Task.Name)
            {
            case BackgroundTaskHelper.TOAST_BACKGROUND_TASK_NAME:
                if (args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details)
                {
                    InitLogger();

                    string   arguments = details.Argument;
                    ValueSet userInput = details.UserInput;

                    Logger.Debug("App activated in background through toast with: " + arguments);
                    AbstractToastActivation abstractToastActivation = ToastActivationArgumentParser.ParseArguments(arguments);

                    if (abstractToastActivation is null)
                    {
                        Logger.Warn("Unable to evaluate toast activation string - unknown format");
                    }
                    else if (abstractToastActivation is MarkChatAsReadToastActivation markChatAsRead)
                    {
                        ToastHelper.RemoveToastGroup(markChatAsRead.CHAT_ID.ToString());
                        DataCache.INSTANCE.MarkAllChatMessagesAsRead(markChatAsRead.CHAT_ID);
                    }
                    else if (abstractToastActivation is MarkMessageAsReadToastActivation markMessageAsRead)
                    {
                        DataCache.INSTANCE.MarkChatMessageAsRead(markMessageAsRead.CHAT_ID, markMessageAsRead.CHAT_MESSAGE_ID);
                    }
                    else if (abstractToastActivation is SendReplyToastActivation sendReply)
                    {
                        ChatModel chat;
                        using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                        {
                            chat = DataCache.INSTANCE.GetChat(sendReply.CHAT_ID, semaLock);
                        }
                        if (chat is not null && userInput[ToastHelper.TEXT_BOX_ID] is string text)
                        {
                            string trimedText = text.Trim(UiUtils.TRIM_CHARS);
                            await SendChatMessageAsync(chat, trimedText);
                        }
                        DataCache.INSTANCE.MarkChatMessageAsRead(sendReply.CHAT_ID, sendReply.CHAT_MESSAGE_ID);
                    }

                    ToastHelper.UpdateBadgeNumber();
                }
                break;

            default:
                break;
            }

            deferral.Complete();
        }
Exemple #9
0
        private async Task AddMucAsync(Client client, string roomBareJid, string nickname, string password, bool bookmark, bool autoJoin)
        {
            ChatModel chat = new ChatModel(roomBareJid, client.dbAccount)
            {
                chatType     = ChatType.MUC,
                inRoster     = bookmark,
                subscription = "none",
                isChatActive = true
            };
            MucInfoModel muc = new MucInfoModel()
            {
                chat          = chat,
                subject       = null,
                autoEnterRoom = autoJoin,
                name          = null,
                nickname      = nickname,
                password      = string.IsNullOrEmpty(password) ? null : password,
                state         = MucState.DISCONNECTED
            };

            chat.muc = muc;
            SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock();

            semaLock.Wait();
            DataCache.INSTANCE.AddChatUnsafe(chat, client);
            semaLock.Dispose();

            if (muc.autoEnterRoom)
            {
                await MucHandler.INSTANCE.EnterMucAsync(client.xmppClient, muc);
            }

            if (bookmark)
            {
                List <ConferenceItem> conferenceItems;
                using (MainDbContext ctx = new MainDbContext())
                {
                    conferenceItems = ctx.GetXEP0048ConferenceItemsForAccount(client.dbAccount.bareJid);
                }
                MessageResponseHelperResult <IQMessage> result = await client.xmppClient.PUB_SUB_COMMAND_HELPER.setBookmars_xep_0048Async(conferenceItems);

                if (result.STATE == MessageResponseHelperResultState.SUCCESS)
                {
                    if (result.RESULT is IQErrorMessage errMsg)
                    {
                        Logger.Warn("Failed to set bookmarks: " + errMsg.ToString());
                    }
                }
                else
                {
                    Logger.Warn("Failed to set bookmarks: " + result.STATE);
                }
            }
        }
Exemple #10
0
        public void StoreDevices(List <Tuple <OmemoProtocolAddress, string> > devices, string bareJid)
        {
            IEnumerable <OmemoDeviceModel> newDevices = devices.Select(d => new OmemoDeviceModel(d.Item1)
            {
                deviceLabel = d.Item2
            });

            List <OmemoDeviceModel> oldDevices;
            AbstractModel           model;

            if (string.Equals(bareJid, dbAccount.bareJid))
            {
                oldDevices = dbAccount.omemoInfo.devices;
                model      = dbAccount.omemoInfo;
            }
            else
            {
                OmemoChatInformationModel omemoChatInfo;
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    omemoChatInfo = DataCache.INSTANCE.GetChat(dbAccount.bareJid, bareJid, semaLock)?.omemoInfo;
                }
                model      = omemoChatInfo;
                oldDevices = omemoChatInfo.devices;

                if (model is null)
                {
                    throw new InvalidOperationException("Failed to store devices. Chat '" + bareJid + "' does not exist.");
                }
            }

            using (MainDbContext ctx = new MainDbContext())
            {
                // Remove old:
                for (int i = 0; i < oldDevices.Count; i++)
                {
                    OmemoDeviceModel device = oldDevices[i];
                    if (newDevices.Where(d => d.deviceId == device.deviceId).FirstOrDefault() is null)
                    {
                        ctx.Devices.Remove(device);
                        oldDevices.RemoveAt(i);
                    }
                }

                // Add/Update existing:
                foreach (OmemoDeviceModel device in newDevices)
                {
                    OmemoDeviceModel dev = oldDevices.Where(d => d.deviceId == device.deviceId).FirstOrDefault();
                    if (!(dev is null))
                    {
                        dev.deviceLabel = device.deviceLabel;
                        ctx.Devices.Update(dev);
                    }
Exemple #11
0
        /// <summary>
        /// Adds and starts a new chat.
        /// </summary>
        /// <param name="client">Which account/client owns this chat?</param>
        /// <param name="bareJid">The bare JID of the opponent.</param>
        /// <param name="addToRoster">Should the chat get added to the users roster?</param>
        /// <param name="requestSubscription">Request a presence subscription?</param>
        private async Task AddChatAsync(Client client, string bareJid, bool addToRoster, bool requestSubscription)
        {
            if (client is null || string.IsNullOrEmpty(bareJid))
            {
                Logger.Error("Unable to add chat! client ?= " + (client is null) + " bareJid ?=" + (bareJid is null));
                return;
            }

            await Task.Run(async() =>
            {
                SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock();
                ChatModel chat    = DataCache.INSTANCE.GetChat(client.dbAccount.bareJid, bareJid, semaLock);
                bool newChat      = chat is null;
                if (newChat)
                {
                    chat = new ChatModel(bareJid, client.dbAccount);
                }
                else
                {
                    semaLock.Dispose();
                }

                // Set chat active:
                chat.isChatActive = true;
                chat.lastActive   = DateTime.Now;

                // Add to roster:
                if (addToRoster && !chat.inRoster)
                {
                    await client.xmppClient.GENERAL_COMMAND_HELPER.addToRosterAsync(bareJid);
                    chat.inRoster = true;
                }

                // Request presence subscription:
                if (requestSubscription && (string.Equals(chat.subscription, "none") || string.Equals(chat.subscription, "from")) && string.IsNullOrEmpty(chat.subscription))
                {
                    await client.xmppClient.GENERAL_COMMAND_HELPER.requestPresenceSubscriptionAsync(bareJid);
                    chat.subscription = "pending";
                }

                if (newChat)
                {
                    DataCache.INSTANCE.AddChatUnsafe(chat, client);
                    semaLock.Dispose();
                }
                else
                {
                    chat.Update();
                }
            });
        }
Exemple #12
0
        public void StoreSession(OmemoProtocolAddress address, OmemoSessionModel session)
        {
            OmemoDeviceModel device;

            if (string.Equals(address.BARE_JID, dbAccount.bareJid))
            {
                device = dbAccount.omemoInfo.devices.Where(d => address.DEVICE_ID == d.deviceId).FirstOrDefault();
            }
            else
            {
                ChatModel chat;
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    chat = DataCache.INSTANCE.GetChat(dbAccount.bareJid, address.BARE_JID, semaLock);
                }
                if (chat is null)
                {
                    throw new InvalidOperationException("Failed to store session. Chat '" + address.BARE_JID + "' does not exist.");
                }
                device = chat.omemoInfo.devices.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault();
            }

            if (device is null)
            {
                throw new InvalidOperationException("Failed to store session. Device '" + address.ToString() + "' does not exist.");
            }
            if (device.session is null)
            {
                device.session = session;
            }

            bool newSession = device.session.id != session.id;
            OmemoSessionModel oldSession = null;

            if (newSession)
            {
                oldSession     = device.session;
                device.session = session;
            }

            device.Update();

            // Remove the old session:
            if (newSession)
            {
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.Remove(oldSession);
                }
            }
        }
Exemple #13
0
 public OmemoSessionModel LoadSession(OmemoProtocolAddress address)
 {
     if (string.Equals(address.BARE_JID, dbAccount.bareJid))
     {
         return(dbAccount.omemoInfo.devices.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.session);
     }
     else
     {
         using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
         {
             return(DataCache.INSTANCE.GetChat(dbAccount.bareJid, address.BARE_JID, semaLock)?.omemoInfo?.devices?.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.session);
         }
     }
 }
Exemple #14
0
        public OmemoFingerprint LoadFingerprint(OmemoProtocolAddress address)
        {
            OmemoFingerprintModel fingerprint;

            if (string.Equals(address.BARE_JID, dbAccount.bareJid))
            {
                fingerprint = dbAccount.omemoInfo.devices.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.fingerprint;
            }
            else
            {
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    fingerprint = DataCache.INSTANCE.GetChat(dbAccount.bareJid, address.BARE_JID, semaLock)?.omemoInfo?.devices?.Where(d => d.deviceId == address.DEVICE_ID).FirstOrDefault()?.fingerprint;
                }
            }
            return(fingerprint?.ToOmemoFingerprint(address));
        }
Exemple #15
0
        /// <summary>
        /// Called once a client enters the 'Disconnected' or 'Error' state.
        /// </summary>
        private void OnDisconnectedOrError()
        {
            IEnumerable <ChatModel> chats;

            using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
            {
                chats = DataCache.INSTANCE.GetChats(client.dbAccount.bareJid, semaLock);
            }
            foreach (ChatModel chat in chats)
            {
                chat.presence = Presence.Unavailable;
            }
            using (MainDbContext ctx = new MainDbContext())
            {
                ctx.UpdateRange(chats);
            }
            MucHandler.INSTANCE.OnClientDisconnected(client.xmppClient);
        }
Exemple #16
0
        public void StoreFingerprint(OmemoFingerprint fingerprint)
        {
            OmemoDeviceModel device;

            if (string.Equals(fingerprint.ADDRESS.BARE_JID, dbAccount.bareJid))
            {
                device = dbAccount.omemoInfo.devices.Where(d => fingerprint.ADDRESS.DEVICE_ID == d.deviceId).FirstOrDefault();
            }
            else
            {
                ChatModel chat;
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    chat = DataCache.INSTANCE.GetChat(dbAccount.bareJid, fingerprint.ADDRESS.BARE_JID, semaLock);
                }
                if (chat is null)
                {
                    throw new InvalidOperationException("Failed to store fingerprint. Chat '" + fingerprint.ADDRESS.BARE_JID + "' does not exist.");
                }
                device = chat.omemoInfo.devices.Where(d => d.deviceId == fingerprint.ADDRESS.DEVICE_ID).FirstOrDefault();
            }

            if (device is null)
            {
                throw new InvalidOperationException("Failed to store fingerprint. Device '" + fingerprint.ADDRESS.ToString() + "' does not exist.");
            }
            if (device.fingerprint is null)
            {
                device.fingerprint = new OmemoFingerprintModel(fingerprint);
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.Add(device.fingerprint);
                    ctx.Update(device);
                }
            }
            else
            {
                device.fingerprint.lastSeen = fingerprint.lastSeen;
                device.fingerprint.trusted  = fingerprint.trusted;
                device.fingerprint.Update();
            }
        }
Exemple #17
0
        public List <Tuple <OmemoProtocolAddress, string> > LoadDevices(string bareJid)
        {
            List <OmemoDeviceModel> devices;

            if (string.Equals(bareJid, dbAccount.bareJid))
            {
                devices = dbAccount.omemoInfo.devices;
            }
            else
            {
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    devices = DataCache.INSTANCE.GetChat(dbAccount.bareJid, bareJid, semaLock)?.omemoInfo?.devices;
                }
            }
            if (devices is null)
            {
                return(new List <Tuple <OmemoProtocolAddress, string> >());
            }
            return(devices.Select(d => new Tuple <OmemoProtocolAddress, string>(new OmemoProtocolAddress(bareJid, d.deviceId), d.deviceLabel)).ToList());
        }
Exemple #18
0
        //--------------------------------------------------------Set-, Get- Methods:---------------------------------------------------------\\
        #region --Set-, Get- Methods--


        #endregion
        //--------------------------------------------------------Misc Methods:---------------------------------------------------------------\\
        #region --Misc Methods (Public)--
        public Tuple <OmemoDeviceListSubscriptionState, DateTime> LoadDeviceListSubscription(string bareJid)
        {
            OmemoDeviceListSubscriptionModel subscription;

            if (string.Equals(bareJid, dbAccount.bareJid))
            {
                subscription = dbAccount.omemoInfo.deviceListSubscription;
            }
            else
            {
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    subscription = DataCache.INSTANCE.GetChat(dbAccount.bareJid, bareJid, semaLock)?.omemoInfo?.deviceListSubscription;
                }
            }
            if (subscription is null)
            {
                return(new Tuple <OmemoDeviceListSubscriptionState, DateTime>(OmemoDeviceListSubscriptionState.NONE, DateTime.MinValue));
            }
            return(new Tuple <OmemoDeviceListSubscriptionState, DateTime>(subscription.state, subscription.lastUpdateReceived));
        }
        private async Task AddIoTDevice(string deviceBareJid, Client client)
        {
            // Run it in a new Task since we want to access the DB and this is a blocking action.
            await Task.Run(async() =>
            {
                // Add to DB:
                ChatModel chat = new ChatModel(deviceBareJid, client.dbAccount)
                {
                    chatType     = ChatType.IOT_DEVICE,
                    isChatActive = true
                };
                SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock();
                DataCache.INSTANCE.AddChatUnsafe(chat, client);
                semaLock.Dispose();

                // Add to the roster:
                await client.xmppClient.GENERAL_COMMAND_HELPER.addToRosterAsync(deviceBareJid);

                // Add as a PEP node:
                // TODO
            });
        }
Exemple #20
0
        public void OnMUCRoomSubjectMessage(MUCRoomSubjectMessage mucRoomSubject)
        {
            string    to   = Utils.getBareJidFromFullJid(mucRoomSubject.getTo());
            string    from = Utils.getBareJidFromFullJid(mucRoomSubject.getFrom());
            ChatModel chat;

            using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
            {
                chat = DataCache.INSTANCE.GetChat(to, from, semaLock);
            }
            if (chat is null || chat.muc is null || (chat.muc.state == MucState.DISCONNECTED && string.Equals(chat.muc.subject, mucRoomSubject.SUBJECT)))
            {
                return;
            }
            using (SemaLock semaLock = chat.muc.NewSemaLock())
            {
                chat.muc.subject = mucRoomSubject.SUBJECT;
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.Update(chat.muc);
                }
            }
        }
        private async Task AddMucAsync(Client client, string roomBareJid, string nickname, string password, bool bookmark, bool autoJoin)
        {
            ChatModel chat = new ChatModel(roomBareJid, client.dbAccount)
            {
                chatType     = ChatType.MUC,
                inRoster     = bookmark,
                subscription = "none",
                isChatActive = true
            };
            MucInfoModel muc = new MucInfoModel()
            {
                chat          = chat,
                subject       = null,
                autoEnterRoom = autoJoin,
                name          = null,
                nickname      = nickname,
                password      = string.IsNullOrEmpty(password) ? null : password,
                state         = MucState.DISCONNECTED
            };

            chat.muc = muc;
            SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock();

            semaLock.Wait();
            DataCache.INSTANCE.AddChatUnsafe(chat, client);
            semaLock.Dispose();

            if (muc.autoEnterRoom)
            {
                await MucHandler.INSTANCE.EnterMucAsync(client.xmppClient, muc);
            }

            if (bookmark)
            {
                await client.PublishBookmarksAsync();
            }
        }
Exemple #22
0
        public void StoreDevices(List <OmemoProtocolAddress> devices, string bareJid)
        {
            IEnumerable <OmemoDeviceModel> newDevices = devices.Select(d => new OmemoDeviceModel(d));

            if (string.Equals(bareJid, dbAccount.bareJid))
            {
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.RemoveRange(dbAccount.omemoInfo.devices);
                    dbAccount.omemoInfo.devices.Clear();
                    ctx.Update(dbAccount.omemoInfo);
                    dbAccount.omemoInfo.devices.AddRange(newDevices);
                    ctx.Update(dbAccount.omemoInfo);
                }
            }
            else
            {
                OmemoChatInformationModel omemoChatInfo;
                using (SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock())
                {
                    omemoChatInfo = DataCache.INSTANCE.GetChat(dbAccount.bareJid, bareJid, semaLock)?.omemoInfo;
                }
                if (omemoChatInfo is null)
                {
                    throw new InvalidOperationException("Failed to store devices. Chat '" + bareJid + "' does not exist.");
                }
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.RemoveRange(omemoChatInfo.devices);
                    omemoChatInfo.devices.Clear();
                    ctx.Update(omemoChatInfo);
                    omemoChatInfo.devices.AddRange(newDevices);
                    ctx.Update(omemoChatInfo);
                }
            }
        }
Exemple #23
0
        public async Task HandleNewChatMessageAsync(MessageMessage msg)
        {
            // Handel MUC room subject messages:
            if (msg is MUCRoomSubjectMessage)
            {
                MucHandler.INSTANCE.OnMUCRoomSubjectMessage(msg as MUCRoomSubjectMessage);
                return;
            }

            string from        = Utils.getBareJidFromFullJid(msg.getFrom());
            string to          = Utils.getBareJidFromFullJid(msg.getTo());
            string chatBareJid = string.Equals(from, client.dbAccount.bareJid) ? to : from;

            SemaLock  semaLock    = DataCache.INSTANCE.NewChatSemaLock();
            ChatModel chat        = DataCache.INSTANCE.GetChat(client.dbAccount.bareJid, chatBareJid, semaLock);
            bool      newChat     = chat is null;
            bool      chatChanged = false;

            // Spam detection:
            if (Settings.GetSettingBoolean(SettingsConsts.SPAM_DETECTION_ENABLED))
            {
                if (Settings.GetSettingBoolean(SettingsConsts.SPAM_DETECTION_FOR_ALL_CHAT_MESSAGES) || newChat)
                {
                    if (SpamHelper.INSTANCE.IsSpam(msg.MESSAGE))
                    {
                        Logger.Warn("Received spam message from " + chatBareJid);
                        return;
                    }
                }
            }

            // Detect invalid chat messages:
            if (!string.Equals(msg.TYPE, MessageMessage.TYPE_CHAT) && !string.Equals(msg.TYPE, MessageMessage.TYPE_ERROR) && !string.Equals(msg.TYPE, MessageMessage.TYPE_GROUPCHAT))
            {
                Logger.Warn($"Received an unknown message type ('{msg.TYPE}') form '{chatBareJid}'. Dropping it.");
                return;
            }

            // Add the new chat to the DB since it's expected to be there by for example our OMEMO encryption:
            if (newChat)
            {
                chat = new ChatModel(chatBareJid, client.dbAccount)
                {
                    lastActive   = msg.delay,
                    chatType     = string.Equals(msg.TYPE, MessageMessage.TYPE_GROUPCHAT) ? ChatType.MUC : ChatType.CHAT,
                    isChatActive = false // Mark chat as inactive until we can be sure, it's a valid message
                };
                DataCache.INSTANCE.AddChatUnsafe(chat, client);
            }
            else
            {
                // Mark chat as active:
                chat.isChatActive = true;
                chatChanged       = true;
            }
            semaLock.Dispose();

            // Check if device id is valid and if, decrypt the OMEMO messages:
            if (msg is OmemoEncryptedMessage omemoMessage)
            {
                OmemoHelper helper = client.xmppClient.getOmemoHelper();
                if (helper is null)
                {
                    OnOmemoSessionBuildError(client.xmppClient, new OmemoSessionBuildErrorEventArgs(chatBareJid, OmemoSessionBuildError.KEY_ERROR, new List <OmemoEncryptedMessage> {
                        omemoMessage
                    }));
                    Logger.Error("Failed to decrypt OMEMO message - OmemoHelper is null");
                    return;
                }
                else if (!await DecryptOmemoEncryptedMessageAsync(omemoMessage, !newChat && chat.omemoInfo.trustedKeysOnly))
                {
                    if (newChat)
                    {
                        // We failed to decrypt, so this chat could be spam. Delete it again...
                        DataCache.INSTANCE.DeleteChat(chat, false, false);
                        Logger.Debug($"Deleting chat '{chat.bareJid}' again, since decrypting the initial OMEMO message failed.");
                    }
                    return;
                }
                else if (omemoMessage.IS_PURE_KEY_EXCHANGE_MESSAGE)
                {
                    return;
                }
            }

            // Valid new chat, so we can change it to active now:
            chat.isChatActive = true;
            chatChanged       = true;

            ChatMessageModel message = null;

            if (!newChat)
            {
                message = DataCache.INSTANCE.GetChatMessage(chat.id, msg.ID);
            }

            // Filter messages that already exist:
            // ToDo: Allow MUC messages being edited and detect it
            if (!(message is null))
            {
                Logger.Debug("Duplicate message received from '" + chatBareJid + "'. Dropping it...");
                return;
            }
            message = new ChatMessageModel(msg, chat);
            // Set the image path and file name:
            if (message.isImage)
            {
                await DataCache.PrepareImageModelPathAndNameAsync(message.image);
            }
            DataCache.INSTANCE.AddChatMessage(message, chat);

            // Handle MUC invite messages:
            if (msg is DirectMUCInvitationMessage inviteMessage)
            {
                if (!newChat)
                {
                    Logger.Info("Ignoring received MUC direct invitation form '" + chatBareJid + "' since we already joined this MUC (" + inviteMessage.ROOM_JID + ").");
                    return;
                }
                // Ensure we add the message to the DB before we add the invite since the invite has the message as a foreign key:
                using (MainDbContext ctx = new MainDbContext())
                {
                    ctx.Add(new MucDirectInvitationModel(inviteMessage, message));
                }
            }

            bool isMUCMessage = string.Equals(MessageMessage.TYPE_GROUPCHAT, message.type);

            if (chat.lastActive.CompareTo(msg.delay) < 0)
            {
                chatChanged     = true;
                chat.lastActive = msg.delay;
            }

            // Send XEP-0184 (Message Delivery Receipts) reply:
            if (msg.RECIPT_REQUESTED && !Settings.GetSettingBoolean(SettingsConsts.DONT_SEND_CHAT_MESSAGE_RECEIVED_MARKERS))
            {
                await Task.Run(async() =>
                {
                    DeliveryReceiptMessage receiptMessage = new DeliveryReceiptMessage(client.dbAccount.fullJid.FullJid(), from, msg.ID);
                    await client.xmppClient.SendAsync(receiptMessage);
                });
            }

            if (chatChanged)
            {
                chat.Update();
                chatChanged = false;
            }

            // Show toast:
            if (!chat.muted)
            {
                await Task.Run(() =>
                {
                    try
                    {
                        switch (msg.TYPE)
                        {
                        case MessageMessage.TYPE_GROUPCHAT:
                        case MessageMessage.TYPE_CHAT:
                            if (!message.isCC)
                            {
                                // Create toast:
                                if (message.isImage)
                                {
                                    ToastHelper.ShowChatTextImageToast(message, chat);
                                }
                                else
                                {
                                    ToastHelper.ShowChatTextToast(message, chat);
                                }

                                // Update badge notification count:
                                ToastHelper.UpdateBadgeNumber();
                            }
                            break;

                        default:
                            break;
                        }
                    }
                    catch (Exception e)
                    {
                        Logger.Error("Failed to send toast notification!", e);
                    }
                });
            }
        }
Exemple #24
0
        //--------------------------------------------------------Events:---------------------------------------------------------------------\\
        #region --Events--
        private void OnNewMucMemberPresenceMessage(XMPPClient client, NewMUCMemberPresenceMessageEventArgs args)
        {
            Task.Run(async() =>
            {
                MUCMemberPresenceMessage msg = args.mucMemberPresenceMessage;
                string roomJid = Utils.getBareJidFromFullJid(msg.getFrom());
                if (roomJid is null)
                {
                    return;
                }
                SemaLock semaLock = DataCache.INSTANCE.NewChatSemaLock();
                ChatModel chat    = DataCache.INSTANCE.GetChat(client.getXMPPAccount().getBareJid(), roomJid, semaLock);
                semaLock.Dispose();
                if (chat is null || chat.muc is null)
                {
                    return;
                }

                // Prevent multiple accesses to the same occupant at the same time:
                await OCCUPANT_CREATION_SEMA.WaitAsync();
                MucOccupantModel occupant = chat.muc.occupants.Where(o => string.Equals(o.nickname, msg.FROM_NICKNAME)).FirstOrDefault();
                bool newOccupant          = occupant is null;
                if (newOccupant)
                {
                    occupant = new MucOccupantModel()
                    {
                        nickname = msg.FROM_NICKNAME
                    };
                }
                occupant.affiliation = msg.AFFILIATION;
                occupant.role        = msg.ROLE;
                occupant.fullJid     = msg.JID;

                bool isUnavailable   = Equals(msg.TYPE, "unavailable");
                bool nicknameChanged = false;
                if (isUnavailable)
                {
                    if (msg.STATUS_CODES.Contains(MUCPresenceStatusCode.PRESENCE_SELFE_REFERENCE))
                    {
                        // Nickname got changed by user or room:
                        if (msg.STATUS_CODES.Contains(MUCPresenceStatusCode.MEMBER_NICK_CHANGED) || msg.STATUS_CODES.Contains(MUCPresenceStatusCode.ROOM_NICK_CHANGED))
                        {
                            nicknameChanged = true;

                            // Update MUC info:
                            chat.muc.nickname = msg.NICKNAME;

                            // Update the user nickname:
                            occupant.nickname = msg.NICKNAME;
                        }
                        // Occupant got kicked:
                        else if (msg.STATUS_CODES.Contains(MUCPresenceStatusCode.MEMBER_GOT_KICKED))
                        {
                            chat.muc.state = MucState.KICKED;
                        }
                        else if (msg.STATUS_CODES.Contains(MUCPresenceStatusCode.MEMBER_GOT_BANED))
                        {
                            chat.muc.state = MucState.BANED;
                        }
                        else
                        {
                            chat.muc.state = MucState.DISCONNECTED;
                        }
                    }


                    if (msg.STATUS_CODES.Contains(MUCPresenceStatusCode.MEMBER_GOT_KICKED))
                    {
                        // Add kicked chat message:
                        AddOccupantKickedMessage(chat, roomJid, occupant.nickname);
                    }

                    if (msg.STATUS_CODES.Contains(MUCPresenceStatusCode.MEMBER_GOT_BANED))
                    {
                        // Add baned chat message:
                        AddOccupantBanedMessage(chat, roomJid, occupant.nickname);
                    }
                }

                using (SemaLock mucSemaLock = chat.muc.NewSemaLock())
                {
                    using (SemaLock occupantSemaLock = occupant.NewSemaLock())
                    {
                        // If the type equals 'unavailable', a user left the room:
                        if (isUnavailable && !nicknameChanged)
                        {
                            if (!newOccupant)
                            {
                                using (MainDbContext ctx = new MainDbContext())
                                {
                                    chat.muc.occupants.Remove(occupant);
                                    chat.muc.OnOccupantsChanged();
                                    ctx.Update(chat.muc);
                                }

                                using (MainDbContext ctx = new MainDbContext())
                                {
                                    ctx.Remove(occupant);
                                }
                            }
                        }
                        else
                        {
                            if (newOccupant)
                            {
                                occupant.Add();
                                chat.muc.occupants.Add(occupant);
                                chat.muc.OnOccupantsChanged();
                                using (MainDbContext ctx = new MainDbContext())
                                {
                                    ctx.Update(chat.muc);
                                }
                            }
                            else
                            {
                                using (MainDbContext ctx = new MainDbContext())
                                {
                                    ctx.Update(occupant);
                                }
                            }
                        }
                    }
                }
                OCCUPANT_CREATION_SEMA.Release();
            });
        }