Search in sources :

Example 81 with Conversation

use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.

the class JingleFileTransferConnection method init.

private void init(JinglePacket packet) {
    // should move to deliverPacket
    // TODO if not 'OFFERED' reply with out-of-order
    this.mJingleStatus = JINGLE_STATUS_INITIATED;
    final Conversation conversation = this.xmppConnectionService.findOrCreateConversation(id.account, id.with.asBareJid(), false, false);
    this.message = new Message(conversation, "", Message.ENCRYPTION_NONE);
    this.message.setStatus(Message.STATUS_RECEIVED);
    this.mStatus = Transferable.STATUS_OFFER;
    this.message.setTransferable(this);
    this.message.setCounterpart(this.id.with);
    this.responder = this.id.account.getJid();
    final Content content = packet.getJingleContent();
    final GenericTransportInfo transportInfo = content.getTransport();
    this.contentCreator = content.getCreator();
    Content.Senders senders;
    try {
        senders = content.getSenders();
    } catch (final Exception e) {
        senders = Content.Senders.INITIATOR;
    }
    this.contentSenders = senders;
    this.contentName = content.getAttribute("name");
    if (transportInfo instanceof S5BTransportInfo) {
        final S5BTransportInfo s5BTransportInfo = (S5BTransportInfo) transportInfo;
        this.transportId = s5BTransportInfo.getTransportId();
        this.initialTransport = s5BTransportInfo.getClass();
        this.mergeCandidates(s5BTransportInfo.getCandidates());
    } else if (transportInfo instanceof IbbTransportInfo) {
        final IbbTransportInfo ibbTransportInfo = (IbbTransportInfo) transportInfo;
        this.initialTransport = ibbTransportInfo.getClass();
        this.transportId = ibbTransportInfo.getTransportId();
        final int remoteBlockSize = ibbTransportInfo.getBlockSize();
        if (remoteBlockSize <= 0) {
            Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote party requested invalid ibb block size");
            respondToIq(packet, false);
            this.fail();
        }
        this.ibbBlockSize = Math.min(MAX_IBB_BLOCK_SIZE, ibbTransportInfo.getBlockSize());
    } else {
        Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": remote tried to use unknown transport " + transportInfo.getNamespace());
        respondToIq(packet, false);
        this.fail();
        return;
    }
    this.description = (FileTransferDescription) content.getDescription();
    final Element fileOffer = this.description.getFileOffer();
    if (fileOffer != null) {
        boolean remoteIsUsingJet = false;
        Element encrypted = fileOffer.findChild("encrypted", AxolotlService.PEP_PREFIX);
        if (encrypted == null) {
            final Element security = content.findChild("security", Namespace.JINGLE_ENCRYPTED_TRANSPORT);
            if (security != null && AxolotlService.PEP_PREFIX.equals(security.getAttribute("type"))) {
                Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received jingle file offer with JET");
                encrypted = security.findChild("encrypted", AxolotlService.PEP_PREFIX);
                remoteIsUsingJet = true;
            }
        }
        if (encrypted != null) {
            this.mXmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, packet.getFrom().asBareJid());
        }
        Element fileSize = fileOffer.findChild("size");
        final String path = fileOffer.findChildContent("name");
        if (path != null) {
            AbstractConnectionManager.Extension extension = AbstractConnectionManager.Extension.of(path);
            if (VALID_IMAGE_EXTENSIONS.contains(extension.main)) {
                message.setType(Message.TYPE_IMAGE);
                message.setRelativeFilePath(message.getUuid() + "." + extension.main);
            } else if (VALID_CRYPTO_EXTENSIONS.contains(extension.main)) {
                if (VALID_IMAGE_EXTENSIONS.contains(extension.secondary)) {
                    message.setType(Message.TYPE_IMAGE);
                    message.setRelativeFilePath(message.getUuid() + "." + extension.secondary);
                } else {
                    message.setType(Message.TYPE_FILE);
                    message.setRelativeFilePath(message.getUuid() + (extension.secondary != null ? ("." + extension.secondary) : ""));
                }
                message.setEncryption(Message.ENCRYPTION_PGP);
            } else {
                message.setType(Message.TYPE_FILE);
                message.setRelativeFilePath(message.getUuid() + (extension.main != null ? ("." + extension.main) : ""));
            }
            long size = parseLong(fileSize, 0);
            message.setBody(Long.toString(size));
            conversation.add(message);
            jingleConnectionManager.updateConversationUi(true);
            this.file = this.xmppConnectionService.getFileBackend().getFile(message, false);
            if (mXmppAxolotlMessage != null) {
                XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = id.account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage, false);
                if (transportMessage != null) {
                    message.setEncryption(Message.ENCRYPTION_AXOLOTL);
                    this.file.setKey(transportMessage.getKey());
                    this.file.setIv(transportMessage.getIv());
                    message.setFingerprint(transportMessage.getFingerprint());
                } else {
                    Log.d(Config.LOGTAG, "could not process KeyTransportMessage");
                }
            }
            message.resetFileParams();
            // legacy OMEMO encrypted file transfers reported the file size after encryption
            // JET reports the plain text size. however lower levels of our receiving code still
            // expect the cipher text size. so we just + 16 bytes (auth tag size) here
            this.file.setExpectedSize(size + (remoteIsUsingJet ? 16 : 0));
            respondToIq(packet, true);
            if (id.account.getRoster().getContact(id.with).showInContactList() && jingleConnectionManager.hasStoragePermission() && size < this.jingleConnectionManager.getAutoAcceptFileSize() && xmppConnectionService.isDataSaverDisabled()) {
                Log.d(Config.LOGTAG, "auto accepting file from " + id.with);
                this.acceptedAutomatically = true;
                this.sendAccept();
            } else {
                message.markUnread();
                Log.d(Config.LOGTAG, "not auto accepting new file offer with size: " + size + " allowed size:" + this.jingleConnectionManager.getAutoAcceptFileSize());
                this.xmppConnectionService.getNotificationService().push(message);
            }
            Log.d(Config.LOGTAG, "receiving file: expecting size of " + this.file.getExpectedSize());
            return;
        }
        respondToIq(packet, false);
    }
}
Also used : XmppAxolotlMessage(eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage) Message(eu.siacs.conversations.entities.Message) Element(eu.siacs.conversations.xml.Element) Conversation(eu.siacs.conversations.entities.Conversation) AbstractConnectionManager(eu.siacs.conversations.services.AbstractConnectionManager) XmppAxolotlMessage(eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage) IOException(java.io.IOException) FileNotFoundException(java.io.FileNotFoundException) S5BTransportInfo(eu.siacs.conversations.xmpp.jingle.stanzas.S5BTransportInfo) Content(eu.siacs.conversations.xmpp.jingle.stanzas.Content) GenericTransportInfo(eu.siacs.conversations.xmpp.jingle.stanzas.GenericTransportInfo) IbbTransportInfo(eu.siacs.conversations.xmpp.jingle.stanzas.IbbTransportInfo)

Example 82 with Conversation

use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.

the class JingleConnectionManager method writeLogMissedIncoming.

private void writeLogMissedIncoming(final Account account, Jid with, final String sessionId, String serverMsgId, long timestamp) {
    final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, with.asBareJid(), false, false);
    final Message message = new Message(conversation, Message.STATUS_RECEIVED, Message.TYPE_RTP_SESSION, sessionId);
    message.setBody(new RtpSessionStatus(false, 0).toString());
    message.setServerMsgId(serverMsgId);
    message.setTime(timestamp);
    writeMessage(message);
}
Also used : Message(eu.siacs.conversations.entities.Message) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus) Conversation(eu.siacs.conversations.entities.Conversation)

Example 83 with Conversation

use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.

the class JingleConnectionManager method writeMessage.

private void writeMessage(final Message message) {
    final Conversational conversational = message.getConversation();
    if (conversational instanceof Conversation) {
        ((Conversation) conversational).add(message);
        mXmppConnectionService.databaseBackend.createMessage(message);
        mXmppConnectionService.updateConversationUi();
    } else {
        throw new IllegalStateException("Somehow the conversation in a message was a stub");
    }
}
Also used : Conversational(eu.siacs.conversations.entities.Conversational) Conversation(eu.siacs.conversations.entities.Conversation)

Example 84 with Conversation

use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.

the class JingleRtpConnection method writeMessage.

private void writeMessage() {
    final Conversational conversational = message.getConversation();
    if (conversational instanceof Conversation) {
        ((Conversation) conversational).add(this.message);
        xmppConnectionService.createMessageAsync(message);
        xmppConnectionService.updateConversationUi();
    } else {
        throw new IllegalStateException("Somehow the conversation in a message was a stub");
    }
}
Also used : Conversational(eu.siacs.conversations.entities.Conversational) Conversation(eu.siacs.conversations.entities.Conversation)

Example 85 with Conversation

use of eu.siacs.conversations.entities.Conversation in project Conversations by siacs.

the class MessageParser method onMessagePacketReceived.

@Override
public void onMessagePacketReceived(Account account, MessagePacket original) {
    if (handleErrorMessage(account, original)) {
        return;
    }
    final MessagePacket packet;
    Long timestamp = null;
    boolean isCarbon = false;
    String serverMsgId = null;
    final Element fin = original.findChild("fin", MessageArchiveService.Version.MAM_0.namespace);
    if (fin != null) {
        mXmppConnectionService.getMessageArchiveService().processFinLegacy(fin, original.getFrom());
        return;
    }
    final Element result = MessageArchiveService.Version.findResult(original);
    final String queryId = result == null ? null : result.getAttribute("queryid");
    final MessageArchiveService.Query query = queryId == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(queryId);
    if (query != null && query.validFrom(original.getFrom())) {
        final Pair<MessagePacket, Long> f = original.getForwardedMessagePacket("result", query.version.namespace);
        if (f == null) {
            return;
        }
        timestamp = f.second;
        packet = f.first;
        serverMsgId = result.getAttribute("id");
        query.incrementMessageCount();
        if (handleErrorMessage(account, packet)) {
            return;
        }
    } else if (query != null) {
        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received mam result with invalid from (" + original.getFrom() + ") or queryId (" + queryId + ")");
        return;
    } else if (original.fromServer(account)) {
        Pair<MessagePacket, Long> f;
        f = original.getForwardedMessagePacket("received", "urn:xmpp:carbons:2");
        f = f == null ? original.getForwardedMessagePacket("sent", "urn:xmpp:carbons:2") : f;
        packet = f != null ? f.first : original;
        if (handleErrorMessage(account, packet)) {
            return;
        }
        timestamp = f != null ? f.second : null;
        isCarbon = f != null;
    } else {
        packet = original;
    }
    if (timestamp == null) {
        timestamp = AbstractParser.parseTimestamp(original, AbstractParser.parseTimestamp(packet));
    }
    final LocalizedContent body = packet.getBody();
    final Element mucUserElement = packet.findChild("x", Namespace.MUC_USER);
    final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
    final Element replaceElement = packet.findChild("replace", "urn:xmpp:message-correct:0");
    final Element oob = packet.findChild("x", Namespace.OOB);
    final String oobUrl = oob != null ? oob.findChildContent("url") : null;
    final String replacementId = replaceElement == null ? null : replaceElement.getAttribute("id");
    final Element axolotlEncrypted = packet.findChildEnsureSingle(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
    int status;
    final Jid counterpart;
    final Jid to = packet.getTo();
    final Jid from = packet.getFrom();
    final Element originId = packet.findChild("origin-id", Namespace.STANZA_IDS);
    final String remoteMsgId;
    if (originId != null && originId.getAttribute("id") != null) {
        remoteMsgId = originId.getAttribute("id");
    } else {
        remoteMsgId = packet.getId();
    }
    boolean notify = false;
    if (from == null || !InvalidJid.isValid(from) || !InvalidJid.isValid(to)) {
        Log.e(Config.LOGTAG, "encountered invalid message from='" + from + "' to='" + to + "'");
        return;
    }
    boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
    if (query != null && !query.muc() && isTypeGroupChat) {
        Log.e(Config.LOGTAG, account.getJid().asBareJid() + ": received groupchat (" + from + ") message on regular MAM request. skipping");
        return;
    }
    boolean isMucStatusMessage = InvalidJid.hasValidFrom(packet) && from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status");
    boolean selfAddressed;
    if (packet.fromAccount(account)) {
        status = Message.STATUS_SEND;
        selfAddressed = to == null || account.getJid().asBareJid().equals(to.asBareJid());
        if (selfAddressed) {
            counterpart = from;
        } else {
            counterpart = to != null ? to : account.getJid();
        }
    } else {
        status = Message.STATUS_RECEIVED;
        counterpart = from;
        selfAddressed = false;
    }
    final Invite invite = extractInvite(packet);
    if (invite != null) {
        if (isTypeGroupChat) {
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring invite to " + invite.jid + " because type=groupchat");
        } else if (invite.direct && (mucUserElement != null || invite.inviter == null || mXmppConnectionService.isMuc(account, invite.inviter))) {
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignoring direct invite to " + invite.jid + " because it was received in MUC");
        } else {
            invite.execute(account);
            return;
        }
    }
    if ((body != null || pgpEncrypted != null || (axolotlEncrypted != null && axolotlEncrypted.hasChild("payload")) || oobUrl != null) && !isMucStatusMessage) {
        final boolean conversationIsProbablyMuc = isTypeGroupChat || mucUserElement != null || account.getXmppConnection().getMucServersWithholdAccount().contains(counterpart.getDomain().toEscapedString());
        final Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), conversationIsProbablyMuc, false, query, false);
        final boolean conversationMultiMode = conversation.getMode() == Conversation.MODE_MULTI;
        if (serverMsgId == null) {
            serverMsgId = extractStanzaId(packet, isTypeGroupChat, conversation);
        }
        if (selfAddressed) {
            if (mXmppConnectionService.markMessage(conversation, remoteMsgId, Message.STATUS_SEND_RECEIVED, serverMsgId)) {
                return;
            }
            status = Message.STATUS_RECEIVED;
            if (remoteMsgId != null && conversation.findMessageWithRemoteId(remoteMsgId, counterpart) != null) {
                return;
            }
        }
        if (isTypeGroupChat) {
            if (conversation.getMucOptions().isSelf(counterpart)) {
                status = Message.STATUS_SEND_RECEIVED;
                // not really carbon but received from another resource
                isCarbon = true;
                if (mXmppConnectionService.markMessage(conversation, remoteMsgId, status, serverMsgId, body)) {
                    return;
                } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) {
                    if (body != null) {
                        Message message = conversation.findSentMessageWithBody(body.content);
                        if (message != null) {
                            mXmppConnectionService.markMessage(message, status);
                            return;
                        }
                    }
                }
            } else {
                status = Message.STATUS_RECEIVED;
            }
        }
        final Message message;
        if (pgpEncrypted != null && Config.supportOpenPgp()) {
            message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
        } else if (axolotlEncrypted != null && Config.supportOmemo()) {
            Jid origin;
            Set<Jid> fallbacksBySourceId = Collections.emptySet();
            if (conversationMultiMode) {
                final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
                origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
                if (origin == null) {
                    try {
                        fallbacksBySourceId = account.getAxolotlService().findCounterpartsBySourceId(XmppAxolotlMessage.parseSourceId(axolotlEncrypted));
                    } catch (IllegalArgumentException e) {
                    // ignoring
                    }
                }
                if (origin == null && fallbacksBySourceId.size() == 0) {
                    Log.d(Config.LOGTAG, "axolotl message in anonymous conference received and no possible fallbacks");
                    return;
                }
            } else {
                fallbacksBySourceId = Collections.emptySet();
                origin = from;
            }
            final boolean liveMessage = query == null && !isTypeGroupChat && mucUserElement == null;
            final boolean checkedForDuplicates = liveMessage || (serverMsgId != null && remoteMsgId != null && !conversation.possibleDuplicate(serverMsgId, remoteMsgId));
            if (origin != null) {
                message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status, checkedForDuplicates, query != null);
            } else {
                Message trial = null;
                for (Jid fallback : fallbacksBySourceId) {
                    trial = parseAxolotlChat(axolotlEncrypted, fallback, conversation, status, checkedForDuplicates && fallbacksBySourceId.size() == 1, query != null);
                    if (trial != null) {
                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": decoded muc message using fallback");
                        origin = fallback;
                        break;
                    }
                }
                message = trial;
            }
            if (message == null) {
                if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) {
                    mXmppConnectionService.updateConversationUi();
                }
                if (query != null && status == Message.STATUS_SEND && remoteMsgId != null) {
                    Message previouslySent = conversation.findSentMessageWithUuid(remoteMsgId);
                    if (previouslySent != null && previouslySent.getServerMsgId() == null && serverMsgId != null) {
                        previouslySent.setServerMsgId(serverMsgId);
                        mXmppConnectionService.databaseBackend.updateMessage(previouslySent, false);
                        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": encountered previously sent OMEMO message without serverId. updating...");
                    }
                }
                return;
            }
            if (conversationMultiMode) {
                message.setTrueCounterpart(origin);
            }
        } else if (body == null && oobUrl != null) {
            message = new Message(conversation, oobUrl, Message.ENCRYPTION_NONE, status);
            message.setOob(true);
            if (CryptoHelper.isPgpEncryptedUrl(oobUrl)) {
                message.setEncryption(Message.ENCRYPTION_DECRYPTED);
            }
        } else {
            message = new Message(conversation, body.content, Message.ENCRYPTION_NONE, status);
            if (body.count > 1) {
                message.setBodyLanguage(body.language);
            }
        }
        message.setCounterpart(counterpart);
        message.setRemoteMsgId(remoteMsgId);
        message.setServerMsgId(serverMsgId);
        message.setCarbon(isCarbon);
        message.setTime(timestamp);
        if (body != null && body.content != null && body.content.equals(oobUrl)) {
            message.setOob(true);
            if (CryptoHelper.isPgpEncryptedUrl(oobUrl)) {
                message.setEncryption(Message.ENCRYPTION_DECRYPTED);
            }
        }
        message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0");
        if (conversationMultiMode) {
            message.setMucUser(conversation.getMucOptions().findUserByFullJid(counterpart));
            final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
            Jid trueCounterpart;
            if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL) {
                trueCounterpart = message.getTrueCounterpart();
            } else if (query != null && query.safeToExtractTrueCounterpart()) {
                trueCounterpart = getTrueCounterpart(mucUserElement, fallback);
            } else {
                trueCounterpart = fallback;
            }
            if (trueCounterpart != null && isTypeGroupChat) {
                if (trueCounterpart.asBareJid().equals(account.getJid().asBareJid())) {
                    status = isTypeGroupChat ? Message.STATUS_SEND_RECEIVED : Message.STATUS_SEND;
                } else {
                    status = Message.STATUS_RECEIVED;
                    message.setCarbon(false);
                }
            }
            message.setStatus(status);
            message.setTrueCounterpart(trueCounterpart);
            if (!isTypeGroupChat) {
                message.setType(Message.TYPE_PRIVATE);
            }
        } else {
            updateLastseen(account, from);
        }
        if (replacementId != null && mXmppConnectionService.allowMessageCorrection()) {
            final Message replacedMessage = conversation.findMessageWithRemoteIdAndCounterpart(replacementId, counterpart, message.getStatus() == Message.STATUS_RECEIVED, message.isCarbon());
            if (replacedMessage != null) {
                final boolean fingerprintsMatch = replacedMessage.getFingerprint() == null || replacedMessage.getFingerprint().equals(message.getFingerprint());
                final boolean trueCountersMatch = replacedMessage.getTrueCounterpart() != null && message.getTrueCounterpart() != null && replacedMessage.getTrueCounterpart().asBareJid().equals(message.getTrueCounterpart().asBareJid());
                // can not be checked when using mam
                final boolean mucUserMatches = query == null && replacedMessage.sameMucUser(message);
                final boolean duplicate = conversation.hasDuplicateMessage(message);
                if (fingerprintsMatch && (trueCountersMatch || !conversationMultiMode || mucUserMatches) && !duplicate) {
                    Log.d(Config.LOGTAG, "replaced message '" + replacedMessage.getBody() + "' with '" + message.getBody() + "'");
                    synchronized (replacedMessage) {
                        final String uuid = replacedMessage.getUuid();
                        replacedMessage.setUuid(UUID.randomUUID().toString());
                        replacedMessage.setBody(message.getBody());
                        replacedMessage.putEdited(replacedMessage.getRemoteMsgId(), replacedMessage.getServerMsgId());
                        replacedMessage.setRemoteMsgId(remoteMsgId);
                        if (replacedMessage.getServerMsgId() == null || message.getServerMsgId() != null) {
                            replacedMessage.setServerMsgId(message.getServerMsgId());
                        }
                        replacedMessage.setEncryption(message.getEncryption());
                        if (replacedMessage.getStatus() == Message.STATUS_RECEIVED) {
                            replacedMessage.markUnread();
                        }
                        extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet);
                        mXmppConnectionService.updateMessage(replacedMessage, uuid);
                        if (mXmppConnectionService.confirmMessages() && replacedMessage.getStatus() == Message.STATUS_RECEIVED && // TODO do we really want to send receipts for all PMs?
                        (replacedMessage.trusted() || replacedMessage.isPrivateMessage()) && remoteMsgId != null && !selfAddressed && !isTypeGroupChat) {
                            processMessageReceipts(account, packet, remoteMsgId, query);
                        }
                        if (replacedMessage.getEncryption() == Message.ENCRYPTION_PGP) {
                            conversation.getAccount().getPgpDecryptionService().discard(replacedMessage);
                            conversation.getAccount().getPgpDecryptionService().decrypt(replacedMessage, false);
                        }
                    }
                    mXmppConnectionService.getNotificationService().updateNotification();
                    return;
                } else {
                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": received message correction but verification didn't check out");
                }
            }
        }
        long deletionDate = mXmppConnectionService.getAutomaticMessageDeletionDate();
        if (deletionDate != 0 && message.getTimeSent() < deletionDate) {
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": skipping message from " + message.getCounterpart().toString() + " because it was sent prior to our deletion date");
            return;
        }
        boolean checkForDuplicates = (isTypeGroupChat && packet.hasChild("delay", "urn:xmpp:delay")) || message.isPrivateMessage() || message.getServerMsgId() != null || (query == null && mXmppConnectionService.getMessageArchiveService().isCatchupInProgress(conversation));
        if (checkForDuplicates) {
            final Message duplicate = conversation.findDuplicateMessage(message);
            if (duplicate != null) {
                final boolean serverMsgIdUpdated;
                if (duplicate.getStatus() != Message.STATUS_RECEIVED && duplicate.getUuid().equals(message.getRemoteMsgId()) && duplicate.getServerMsgId() == null && message.getServerMsgId() != null) {
                    duplicate.setServerMsgId(message.getServerMsgId());
                    if (mXmppConnectionService.databaseBackend.updateMessage(duplicate, false)) {
                        serverMsgIdUpdated = true;
                    } else {
                        serverMsgIdUpdated = false;
                        Log.e(Config.LOGTAG, "failed to update message");
                    }
                } else {
                    serverMsgIdUpdated = false;
                }
                Log.d(Config.LOGTAG, "skipping duplicate message with " + message.getCounterpart() + ". serverMsgIdUpdated=" + serverMsgIdUpdated);
                return;
            }
        }
        if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
            conversation.prepend(query.getActualInThisQuery(), message);
        } else {
            conversation.add(message);
        }
        if (query != null) {
            query.incrementActualMessageCount();
        }
        if (query == null || query.isCatchup()) {
            // either no mam or catchup
            if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) {
                mXmppConnectionService.markRead(conversation);
                if (query == null) {
                    activateGracePeriod(account);
                }
            } else {
                message.markUnread();
                notify = true;
            }
        }
        if (message.getEncryption() == Message.ENCRYPTION_PGP) {
            notify = conversation.getAccount().getPgpDecryptionService().decrypt(message, notify);
        } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE || message.getEncryption() == Message.ENCRYPTION_AXOLOTL_FAILED) {
            notify = false;
        }
        if (query == null) {
            extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet);
            mXmppConnectionService.updateConversationUi();
        }
        if (mXmppConnectionService.confirmMessages() && message.getStatus() == Message.STATUS_RECEIVED && (message.trusted() || message.isPrivateMessage()) && remoteMsgId != null && !selfAddressed && !isTypeGroupChat) {
            processMessageReceipts(account, packet, remoteMsgId, query);
        }
        mXmppConnectionService.databaseBackend.createMessage(message);
        final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
        if (message.trusted() && message.treatAsDownloadable() && manager.getAutoAcceptFileSize() > 0) {
            manager.createNewDownloadConnection(message);
        } else if (notify) {
            if (query != null && query.isCatchup()) {
                mXmppConnectionService.getNotificationService().pushFromBacklog(message);
            } else {
                mXmppConnectionService.getNotificationService().push(message);
            }
        }
    } else if (!packet.hasChild("body")) {
        // no body
        final Conversation conversation = mXmppConnectionService.find(account, from.asBareJid());
        if (axolotlEncrypted != null) {
            Jid origin;
            if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
                final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
                origin = getTrueCounterpart(query != null ? mucUserElement : null, fallback);
                if (origin == null) {
                    Log.d(Config.LOGTAG, "omemo key transport message in anonymous conference received");
                    return;
                }
            } else if (isTypeGroupChat) {
                return;
            } else {
                origin = from;
            }
            try {
                final XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlEncrypted, origin.asBareJid());
                account.getAxolotlService().processReceivingKeyTransportMessage(xmppAxolotlMessage, query != null);
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": omemo key transport message received from " + origin);
            } catch (Exception e) {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": invalid omemo key transport message received " + e.getMessage());
                return;
            }
        }
        if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.asBareJid()), isTypeGroupChat, packet)) {
            mXmppConnectionService.updateConversationUi();
        }
        if (isTypeGroupChat) {
            if (packet.hasChild("subject")) {
                // TODO usually we would want to check for lack of body; however some servers do set a body :(
                if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
                    conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0);
                    final LocalizedContent subject = packet.findInternationalizedChildContentInDefaultNamespace("subject");
                    if (subject != null && conversation.getMucOptions().setSubject(subject.content)) {
                        mXmppConnectionService.updateConversation(conversation);
                    }
                    mXmppConnectionService.updateConversationUi();
                    return;
                }
            }
        }
        if (conversation != null && mucUserElement != null && InvalidJid.hasValidFrom(packet) && from.isBareJid()) {
            for (Element child : mucUserElement.getChildren()) {
                if ("status".equals(child.getName())) {
                    try {
                        int code = Integer.parseInt(child.getAttribute("code"));
                        if ((code >= 170 && code <= 174) || (code >= 102 && code <= 104)) {
                            mXmppConnectionService.fetchConferenceConfiguration(conversation);
                            break;
                        }
                    } catch (Exception e) {
                    // ignored
                    }
                } else if ("item".equals(child.getName())) {
                    MucOptions.User user = AbstractParser.parseItem(conversation, child);
                    Log.d(Config.LOGTAG, account.getJid() + ": changing affiliation for " + user.getRealJid() + " to " + user.getAffiliation() + " in " + conversation.getJid().asBareJid());
                    if (!user.realJidMatchesAccount()) {
                        boolean isNew = conversation.getMucOptions().updateUser(user);
                        mXmppConnectionService.getAvatarService().clear(conversation);
                        mXmppConnectionService.updateMucRosterUi();
                        mXmppConnectionService.updateConversationUi();
                        Contact contact = user.getContact();
                        if (!user.getAffiliation().ranks(MucOptions.Affiliation.MEMBER)) {
                            Jid jid = user.getRealJid();
                            List<Jid> cryptoTargets = conversation.getAcceptedCryptoTargets();
                            if (cryptoTargets.remove(user.getRealJid())) {
                                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": removed " + jid + " from crypto targets of " + conversation.getName());
                                conversation.setAcceptedCryptoTargets(cryptoTargets);
                                mXmppConnectionService.updateConversation(conversation);
                            }
                        } else if (isNew && user.getRealJid() != null && conversation.getMucOptions().isPrivateAndNonAnonymous() && (contact == null || !contact.mutualPresenceSubscription()) && account.getAxolotlService().hasEmptyDeviceList(user.getRealJid())) {
                            account.getAxolotlService().fetchDeviceIds(user.getRealJid());
                        }
                    }
                }
            }
        }
        if (!isTypeGroupChat) {
            for (Element child : packet.getChildren()) {
                if (Namespace.JINGLE_MESSAGE.equals(child.getNamespace()) && JINGLE_MESSAGE_ELEMENT_NAMES.contains(child.getName())) {
                    final String action = child.getName();
                    final String sessionId = child.getAttribute("id");
                    if (sessionId == null) {
                        break;
                    }
                    if (query == null) {
                        if (serverMsgId == null) {
                            serverMsgId = extractStanzaId(account, packet);
                        }
                        mXmppConnectionService.getJingleConnectionManager().deliverMessage(account, packet.getTo(), packet.getFrom(), child, remoteMsgId, serverMsgId, timestamp);
                        if (!account.getJid().asBareJid().equals(from.asBareJid()) && remoteMsgId != null) {
                            processMessageReceipts(account, packet, remoteMsgId, query);
                        }
                    } else if (query.isCatchup()) {
                        if ("propose".equals(action)) {
                            final Element description = child.findChild("description");
                            final String namespace = description == null ? null : description.getNamespace();
                            if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
                                final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false);
                                final Message preExistingMessage = c.findRtpSession(sessionId, status);
                                if (preExistingMessage != null) {
                                    preExistingMessage.setServerMsgId(serverMsgId);
                                    mXmppConnectionService.updateMessage(preExistingMessage);
                                    break;
                                }
                                final Message message = new Message(c, status, Message.TYPE_RTP_SESSION, sessionId);
                                message.setServerMsgId(serverMsgId);
                                message.setTime(timestamp);
                                message.setBody(new RtpSessionStatus(false, 0).toString());
                                c.add(message);
                                mXmppConnectionService.databaseBackend.createMessage(message);
                            }
                        } else if ("proceed".equals(action)) {
                            // status needs to be flipped to find the original propose
                            final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false);
                            final int s = packet.fromAccount(account) ? Message.STATUS_RECEIVED : Message.STATUS_SEND;
                            final Message message = c.findRtpSession(sessionId, s);
                            if (message != null) {
                                message.setBody(new RtpSessionStatus(true, 0).toString());
                                if (serverMsgId != null) {
                                    message.setServerMsgId(serverMsgId);
                                }
                                message.setTime(timestamp);
                                mXmppConnectionService.updateMessage(message, true);
                            } else {
                                Log.d(Config.LOGTAG, "unable to find original rtp session message for received propose");
                            }
                        }
                    } else {
                        // MAM reloads (non catchups
                        if ("propose".equals(action)) {
                            final Element description = child.findChild("description");
                            final String namespace = description == null ? null : description.getNamespace();
                            if (Namespace.JINGLE_APPS_RTP.equals(namespace)) {
                                final Conversation c = mXmppConnectionService.findOrCreateConversation(account, counterpart.asBareJid(), false, false);
                                final Message preExistingMessage = c.findRtpSession(sessionId, status);
                                if (preExistingMessage != null) {
                                    preExistingMessage.setServerMsgId(serverMsgId);
                                    mXmppConnectionService.updateMessage(preExistingMessage);
                                    break;
                                }
                                final Message message = new Message(c, status, Message.TYPE_RTP_SESSION, sessionId);
                                message.setServerMsgId(serverMsgId);
                                message.setTime(timestamp);
                                message.setBody(new RtpSessionStatus(true, 0).toString());
                                if (query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) {
                                    c.prepend(query.getActualInThisQuery(), message);
                                } else {
                                    c.add(message);
                                }
                                query.incrementActualMessageCount();
                                mXmppConnectionService.databaseBackend.createMessage(message);
                            }
                        }
                    }
                    break;
                }
            }
        }
    }
    Element received = packet.findChild("received", "urn:xmpp:chat-markers:0");
    if (received == null) {
        received = packet.findChild("received", "urn:xmpp:receipts");
    }
    if (received != null) {
        String id = received.getAttribute("id");
        if (packet.fromAccount(account)) {
            if (query != null && id != null && packet.getTo() != null) {
                query.removePendingReceiptRequest(new ReceiptRequest(packet.getTo(), id));
            }
        } else if (id != null) {
            if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) {
                final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX.length());
                mXmppConnectionService.getJingleConnectionManager().updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.DISCOVERED);
            } else {
                mXmppConnectionService.markMessage(account, from.asBareJid(), id, Message.STATUS_SEND_RECEIVED);
            }
        }
    }
    Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0");
    if (displayed != null) {
        final String id = displayed.getAttribute("id");
        final Jid sender = InvalidJid.getNullForInvalid(displayed.getAttributeAsJid("sender"));
        if (packet.fromAccount(account) && !selfAddressed) {
            dismissNotification(account, counterpart, query, id);
            if (query == null) {
                activateGracePeriod(account);
            }
        } else if (isTypeGroupChat) {
            final Conversation conversation = mXmppConnectionService.find(account, counterpart.asBareJid());
            final Message message;
            if (conversation != null && id != null) {
                if (sender != null) {
                    message = conversation.findMessageWithRemoteId(id, sender);
                } else {
                    message = conversation.findMessageWithServerMsgId(id);
                }
            } else {
                message = null;
            }
            if (message != null) {
                final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart);
                final Jid trueJid = getTrueCounterpart((query != null && query.safeToExtractTrueCounterpart()) ? mucUserElement : null, fallback);
                final boolean trueJidMatchesAccount = account.getJid().asBareJid().equals(trueJid == null ? null : trueJid.asBareJid());
                if (trueJidMatchesAccount || conversation.getMucOptions().isSelf(counterpart)) {
                    if (!message.isRead() && (query == null || query.isCatchup())) {
                        // checking if message is unread fixes race conditions with reflections
                        mXmppConnectionService.markRead(conversation);
                    }
                } else if (!counterpart.isBareJid() && trueJid != null) {
                    final ReadByMarker readByMarker = ReadByMarker.from(counterpart, trueJid);
                    if (message.addReadByMarker(readByMarker)) {
                        mXmppConnectionService.updateMessage(message, false);
                    }
                }
            }
        } else {
            final Message displayedMessage = mXmppConnectionService.markMessage(account, from.asBareJid(), id, Message.STATUS_SEND_DISPLAYED);
            Message message = displayedMessage == null ? null : displayedMessage.prev();
            while (message != null && message.getStatus() == Message.STATUS_SEND_RECEIVED && message.getTimeSent() < displayedMessage.getTimeSent()) {
                mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
                message = message.prev();
            }
            if (displayedMessage != null && selfAddressed) {
                dismissNotification(account, counterpart, query, id);
            }
        }
    }
    final Element event = original.findChild("event", "http://jabber.org/protocol/pubsub#event");
    if (event != null && InvalidJid.hasValidFrom(original)) {
        if (event.hasChild("items")) {
            parseEvent(event, original.getFrom(), account);
        } else if (event.hasChild("delete")) {
            parseDeleteEvent(event, original.getFrom(), account);
        } else if (event.hasChild("purge")) {
            parsePurgeEvent(event, original.getFrom(), account);
        }
    }
    final String nick = packet.findChildContent("nick", Namespace.NICK);
    if (nick != null && InvalidJid.hasValidFrom(original)) {
        final Contact contact = account.getRoster().getContact(from);
        if (contact.setPresenceName(nick)) {
            mXmppConnectionService.syncRoster(account);
            mXmppConnectionService.getAvatarService().clear(contact);
        }
    }
}
Also used : Set(java.util.Set) XmppAxolotlMessage(eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage) Message(eu.siacs.conversations.entities.Message) ReadByMarker(eu.siacs.conversations.entities.ReadByMarker) Element(eu.siacs.conversations.xml.Element) Conversation(eu.siacs.conversations.entities.Conversation) MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) MessageArchiveService(eu.siacs.conversations.services.MessageArchiveService) HttpConnectionManager(eu.siacs.conversations.http.HttpConnectionManager) ReceiptRequest(eu.siacs.conversations.entities.ReceiptRequest) LocalizedContent(eu.siacs.conversations.xml.LocalizedContent) InvalidJid(eu.siacs.conversations.xmpp.InvalidJid) Jid(eu.siacs.conversations.xmpp.Jid) XmppAxolotlMessage(eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage) BrokenSessionException(eu.siacs.conversations.crypto.axolotl.BrokenSessionException) NotEncryptedForThisDeviceException(eu.siacs.conversations.crypto.axolotl.NotEncryptedForThisDeviceException) OutdatedSenderException(eu.siacs.conversations.crypto.axolotl.OutdatedSenderException) Contact(eu.siacs.conversations.entities.Contact) MucOptions(eu.siacs.conversations.entities.MucOptions) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus)

Aggregations

Conversation (eu.siacs.conversations.entities.Conversation)110 Account (eu.siacs.conversations.entities.Account)27 Message (eu.siacs.conversations.entities.Message)24 Jid (eu.siacs.conversations.xmpp.Jid)22 Contact (eu.siacs.conversations.entities.Contact)17 MucOptions (eu.siacs.conversations.entities.MucOptions)10 Intent (android.content.Intent)9 Element (eu.siacs.conversations.xml.Element)9 PendingIntent (android.app.PendingIntent)8 XmppAxolotlMessage (eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage)8 MessagePacket (eu.siacs.conversations.xmpp.stanzas.MessagePacket)8 Uri (android.net.Uri)7 Conversational (eu.siacs.conversations.entities.Conversational)7 InvalidJidException (eu.siacs.conversations.xmpp.jid.InvalidJidException)7 SuppressLint (android.annotation.SuppressLint)6 SpannableString (android.text.SpannableString)6 XmppConnection (eu.siacs.conversations.xmpp.XmppConnection)6 Jid (eu.siacs.conversations.xmpp.jid.Jid)6 ArrayList (java.util.ArrayList)6 Fragment (android.app.Fragment)4