Exemple #1
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 #2
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);
                }
            }
        }
        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
            });
        }
        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 #5
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();
            });
        }
Exemple #6
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);
                    }
                });
            }
        }