Search in sources :

Example 1 with RtpSessionStatus

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

the class JingleConnectionManager method writeLogMissedOutgoing.

private void writeLogMissedOutgoing(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_SEND, 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 2 with RtpSessionStatus

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

the class JingleConnectionManager method deliverMessage.

public void deliverMessage(final Account account, final Jid to, final Jid from, final Element message, String remoteMsgId, String serverMsgId, long timestamp) {
    Preconditions.checkArgument(Namespace.JINGLE_MESSAGE.equals(message.getNamespace()));
    final String sessionId = message.getAttribute("id");
    if (sessionId == null) {
        return;
    }
    if ("accept".equals(message.getName())) {
        for (AbstractJingleConnection connection : connections.values()) {
            if (connection instanceof JingleRtpConnection) {
                final JingleRtpConnection rtpConnection = (JingleRtpConnection) connection;
                final AbstractJingleConnection.Id id = connection.getId();
                if (id.account == account && id.sessionId.equals(sessionId)) {
                    rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                    return;
                }
            }
        }
        return;
    }
    final boolean fromSelf = from.asBareJid().equals(account.getJid().asBareJid());
    final boolean addressedDirectly = to != null && to.equals(account.getJid());
    final AbstractJingleConnection.Id id;
    if (fromSelf) {
        if (to != null && to.isFullJid()) {
            id = AbstractJingleConnection.Id.of(account, to, sessionId);
        } else {
            return;
        }
    } else {
        id = AbstractJingleConnection.Id.of(account, from, sessionId);
    }
    final AbstractJingleConnection existingJingleConnection = connections.get(id);
    if (existingJingleConnection != null) {
        if (existingJingleConnection instanceof JingleRtpConnection) {
            ((JingleRtpConnection) existingJingleConnection).deliveryMessage(from, message, serverMsgId, timestamp);
        } else {
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": " + existingJingleConnection.getClass().getName() + " does not support jingle messages");
        }
        return;
    }
    if (fromSelf) {
        if ("proceed".equals(message.getName())) {
            final Conversation c = mXmppConnectionService.findOrCreateConversation(account, id.with, false, false);
            final Message previousBusy = c.findRtpSession(sessionId, Message.STATUS_RECEIVED);
            if (previousBusy != null) {
                previousBusy.setBody(new RtpSessionStatus(true, 0).toString());
                if (serverMsgId != null) {
                    previousBusy.setServerMsgId(serverMsgId);
                }
                previousBusy.setTime(timestamp);
                mXmppConnectionService.updateMessage(previousBusy, true);
                Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": updated previous busy because call got picked up by another device");
                return;
            }
        }
        // TODO handle reject for cases where we don’t have carbon copies (normally reject is to be sent to own bare jid as well)
        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": ignore jingle message from self");
        return;
    }
    if ("propose".equals(message.getName())) {
        final Propose propose = Propose.upgrade(message);
        final List<GenericDescription> descriptions = propose.getDescriptions();
        final Collection<RtpDescription> rtpDescriptions = Collections2.transform(Collections2.filter(descriptions, d -> d instanceof RtpDescription), input -> (RtpDescription) input);
        if (rtpDescriptions.size() > 0 && rtpDescriptions.size() == descriptions.size() && isUsingClearNet(account)) {
            final Collection<Media> media = Collections2.transform(rtpDescriptions, RtpDescription::getMedia);
            if (media.contains(Media.UNKNOWN)) {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": encountered unknown media in session proposal. " + propose);
                return;
            }
            final Optional<RtpSessionProposal> matchingSessionProposal = findMatchingSessionProposal(account, id.with, ImmutableSet.copyOf(media));
            if (matchingSessionProposal.isPresent()) {
                final String ourSessionId = matchingSessionProposal.get().sessionId;
                final String theirSessionId = id.sessionId;
                if (ComparisonChain.start().compare(ourSessionId, theirSessionId).compare(account.getJid().toEscapedString(), id.with.toEscapedString()).result() > 0) {
                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": our session lost tie break. automatically accepting their session. winning Session=" + theirSessionId);
                    // TODO a retract for this reason should probably include some indication of tie break
                    retractSessionProposal(matchingSessionProposal.get());
                    final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, from);
                    this.connections.put(id, rtpConnection);
                    rtpConnection.setProposedMedia(ImmutableSet.copyOf(media));
                    rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
                } else {
                    Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": our session won tie break. waiting for other party to accept. winningSession=" + ourSessionId);
                }
                return;
            }
            final boolean stranger = isWithStrangerAndStrangerNotificationsAreOff(account, id.with);
            if (isBusy() || stranger) {
                writeLogMissedIncoming(account, id.with.asBareJid(), id.sessionId, serverMsgId, timestamp);
                if (stranger) {
                    Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring call proposal from stranger " + id.with);
                    return;
                }
                final int activeDevices = account.activeDevicesWithRtpCapability();
                Log.d(Config.LOGTAG, "active devices with rtp capability: " + activeDevices);
                if (activeDevices == 0) {
                    final MessagePacket reject = mXmppConnectionService.getMessageGenerator().sessionReject(from, sessionId);
                    mXmppConnectionService.sendMessagePacket(account, reject);
                } else {
                    Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": ignoring proposal because busy on this device but there are other devices");
                }
            } else {
                final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, from);
                this.connections.put(id, rtpConnection);
                rtpConnection.setProposedMedia(ImmutableSet.copyOf(media));
                rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
            }
        } else {
            Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": unable to react to proposed session with " + rtpDescriptions.size() + " rtp descriptions of " + descriptions.size() + " total descriptions");
        }
    } else if (addressedDirectly && "proceed".equals(message.getName())) {
        synchronized (rtpSessionProposals) {
            final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
            if (proposal != null) {
                rtpSessionProposals.remove(proposal);
                final JingleRtpConnection rtpConnection = new JingleRtpConnection(this, id, account.getJid());
                rtpConnection.setProposedMedia(proposal.media);
                this.connections.put(id, rtpConnection);
                rtpConnection.transitionOrThrow(AbstractJingleConnection.State.PROPOSED);
                rtpConnection.deliveryMessage(from, message, serverMsgId, timestamp);
            } else {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver proceed");
                if (remoteMsgId == null) {
                    return;
                }
                final MessagePacket errorMessage = new MessagePacket();
                errorMessage.setTo(from);
                errorMessage.setId(remoteMsgId);
                errorMessage.setType(MessagePacket.TYPE_ERROR);
                final Element error = errorMessage.addChild("error");
                error.setAttribute("code", "404");
                error.setAttribute("type", "cancel");
                error.addChild("item-not-found", "urn:ietf:params:xml:ns:xmpp-stanzas");
                mXmppConnectionService.sendMessagePacket(account, errorMessage);
            }
        }
    } else if (addressedDirectly && "reject".equals(message.getName())) {
        final RtpSessionProposal proposal = getRtpSessionProposal(account, from.asBareJid(), sessionId);
        synchronized (rtpSessionProposals) {
            if (proposal != null && rtpSessionProposals.remove(proposal) != null) {
                writeLogMissedOutgoing(account, proposal.with, proposal.sessionId, serverMsgId, timestamp);
                toneManager.transition(RtpEndUserState.DECLINED_OR_BUSY, proposal.media);
                mXmppConnectionService.notifyJingleRtpConnectionUpdate(account, proposal.with, proposal.sessionId, RtpEndUserState.DECLINED_OR_BUSY);
            } else {
                Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": no rtp session proposal found for " + from + " to deliver reject");
            }
        }
    } else {
        Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": retrieved out of order jingle message" + message);
    }
}
Also used : Conversational(eu.siacs.conversations.entities.Conversational) Content(eu.siacs.conversations.xmpp.jingle.stanzas.Content) RtpDescription(eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription) Config(eu.siacs.conversations.Config) ScheduledFuture(java.util.concurrent.ScheduledFuture) IqPacket(eu.siacs.conversations.xmpp.stanzas.IqPacket) Reason(eu.siacs.conversations.xmpp.jingle.stanzas.Reason) HashMap(java.util.HashMap) Collections2(com.google.common.collect.Collections2) Account(eu.siacs.conversations.entities.Account) Element(eu.siacs.conversations.xml.Element) Conversation(eu.siacs.conversations.entities.Conversation) SecureRandom(java.security.SecureRandom) GenericDescription(eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription) Optional(com.google.common.base.Optional) Map(java.util.Map) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) FileTransferDescription(eu.siacs.conversations.xmpp.jingle.stanzas.FileTransferDescription) Objects(com.google.common.base.Objects) WeakReference(java.lang.ref.WeakReference) Log(android.util.Log) XmppConnection(eu.siacs.conversations.xmpp.XmppConnection) MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) ImmutableSet(com.google.common.collect.ImmutableSet) Contact(eu.siacs.conversations.entities.Contact) Collection(java.util.Collection) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Propose(eu.siacs.conversations.xmpp.jingle.stanzas.Propose) Set(java.util.Set) JinglePacket(eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket) ComparisonChain(com.google.common.collect.ComparisonChain) Executors(java.util.concurrent.Executors) TimeUnit(java.util.concurrent.TimeUnit) Transferable(eu.siacs.conversations.entities.Transferable) List(java.util.List) Message(eu.siacs.conversations.entities.Message) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus) XmppConnectionService(eu.siacs.conversations.services.XmppConnectionService) Namespace(eu.siacs.conversations.xml.Namespace) OnIqPacketReceived(eu.siacs.conversations.xmpp.OnIqPacketReceived) Base64(android.util.Base64) Preconditions(com.google.common.base.Preconditions) CacheBuilder(com.google.common.cache.CacheBuilder) Cache(com.google.common.cache.Cache) AbstractConnectionManager(eu.siacs.conversations.services.AbstractConnectionManager) Collections(java.util.Collections) Jid(eu.siacs.conversations.xmpp.Jid) Message(eu.siacs.conversations.entities.Message) Element(eu.siacs.conversations.xml.Element) GenericDescription(eu.siacs.conversations.xmpp.jingle.stanzas.GenericDescription) Conversation(eu.siacs.conversations.entities.Conversation) RtpDescription(eu.siacs.conversations.xmpp.jingle.stanzas.RtpDescription) MessagePacket(eu.siacs.conversations.xmpp.stanzas.MessagePacket) Propose(eu.siacs.conversations.xmpp.jingle.stanzas.Propose) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus)

Example 3 with RtpSessionStatus

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

the class MessageAdapter method getView.

@Override
public View getView(int position, View view, ViewGroup parent) {
    final Message message = getItem(position);
    final boolean omemoEncryption = message.getEncryption() == Message.ENCRYPTION_AXOLOTL;
    final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted());
    final Conversational conversation = message.getConversation();
    final Account account = conversation.getAccount();
    final int type = getItemViewType(position);
    ViewHolder viewHolder;
    if (view == null) {
        viewHolder = new ViewHolder();
        switch(type) {
            case DATE_SEPARATOR:
                view = activity.getLayoutInflater().inflate(R.layout.message_date_bubble, parent, false);
                viewHolder.status_message = view.findViewById(R.id.message_body);
                viewHolder.message_box = view.findViewById(R.id.message_box);
                viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
                break;
            case RTP_SESSION:
                view = activity.getLayoutInflater().inflate(R.layout.message_rtp_session, parent, false);
                viewHolder.status_message = view.findViewById(R.id.message_body);
                viewHolder.message_box = view.findViewById(R.id.message_box);
                viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
                break;
            case SENT:
                view = activity.getLayoutInflater().inflate(R.layout.message_sent, parent, false);
                viewHolder.message_box = view.findViewById(R.id.message_box);
                viewHolder.contact_picture = view.findViewById(R.id.message_photo);
                viewHolder.download_button = view.findViewById(R.id.download_button);
                viewHolder.indicator = view.findViewById(R.id.security_indicator);
                viewHolder.edit_indicator = view.findViewById(R.id.edit_indicator);
                viewHolder.image = view.findViewById(R.id.message_image);
                viewHolder.messageBody = view.findViewById(R.id.message_body);
                viewHolder.time = view.findViewById(R.id.message_time);
                viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
                viewHolder.audioPlayer = view.findViewById(R.id.audio_player);
                break;
            case RECEIVED:
                view = activity.getLayoutInflater().inflate(R.layout.message_received, parent, false);
                viewHolder.message_box = view.findViewById(R.id.message_box);
                viewHolder.contact_picture = view.findViewById(R.id.message_photo);
                viewHolder.download_button = view.findViewById(R.id.download_button);
                viewHolder.indicator = view.findViewById(R.id.security_indicator);
                viewHolder.edit_indicator = view.findViewById(R.id.edit_indicator);
                viewHolder.image = view.findViewById(R.id.message_image);
                viewHolder.messageBody = view.findViewById(R.id.message_body);
                viewHolder.time = view.findViewById(R.id.message_time);
                viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received);
                viewHolder.encryption = view.findViewById(R.id.message_encryption);
                viewHolder.audioPlayer = view.findViewById(R.id.audio_player);
                break;
            case STATUS:
                view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false);
                viewHolder.contact_picture = view.findViewById(R.id.message_photo);
                viewHolder.status_message = view.findViewById(R.id.status_message);
                viewHolder.load_more_messages = view.findViewById(R.id.load_more_messages);
                break;
            default:
                throw new AssertionError("Unknown view type");
        }
        view.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) view.getTag();
        if (viewHolder == null) {
            return view;
        }
    }
    boolean darkBackground = type == RECEIVED && (!isInValidSession || mUseGreenBackground) || activity.isDarkTheme();
    if (type == DATE_SEPARATOR) {
        if (UIHelper.today(message.getTimeSent())) {
            viewHolder.status_message.setText(R.string.today);
        } else if (UIHelper.yesterday(message.getTimeSent())) {
            viewHolder.status_message.setText(R.string.yesterday);
        } else {
            viewHolder.status_message.setText(DateUtils.formatDateTime(activity, message.getTimeSent(), DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR));
        }
        viewHolder.message_box.setBackgroundResource(activity.isDarkTheme() ? R.drawable.date_bubble_grey : R.drawable.date_bubble_white);
        return view;
    } else if (type == RTP_SESSION) {
        final boolean isDarkTheme = activity.isDarkTheme();
        final boolean received = message.getStatus() <= Message.STATUS_RECEIVED;
        final RtpSessionStatus rtpSessionStatus = RtpSessionStatus.of(message.getBody());
        final long duration = rtpSessionStatus.duration;
        if (received) {
            if (duration > 0) {
                viewHolder.status_message.setText(activity.getString(R.string.incoming_call_duration, TimeFrameUtils.resolve(activity, duration)));
            } else if (rtpSessionStatus.successful) {
                viewHolder.status_message.setText(R.string.incoming_call);
            } else {
                viewHolder.status_message.setText(activity.getString(R.string.missed_call_timestamp, UIHelper.readableTimeDifferenceFull(activity, message.getTimeSent())));
            }
        } else {
            if (duration > 0) {
                viewHolder.status_message.setText(activity.getString(R.string.outgoing_call_duration, TimeFrameUtils.resolve(activity, duration)));
            } else {
                viewHolder.status_message.setText(R.string.outgoing_call);
            }
        }
        viewHolder.indicatorReceived.setImageResource(RtpSessionStatus.getDrawable(received, rtpSessionStatus.successful, isDarkTheme));
        viewHolder.indicatorReceived.setAlpha(isDarkTheme ? 0.7f : 0.57f);
        viewHolder.message_box.setBackgroundResource(isDarkTheme ? R.drawable.date_bubble_grey : R.drawable.date_bubble_white);
        return view;
    } else if (type == STATUS) {
        if ("LOAD_MORE".equals(message.getBody())) {
            viewHolder.status_message.setVisibility(View.GONE);
            viewHolder.contact_picture.setVisibility(View.GONE);
            viewHolder.load_more_messages.setVisibility(View.VISIBLE);
            viewHolder.load_more_messages.setOnClickListener(v -> loadMoreMessages((Conversation) message.getConversation()));
        } else {
            viewHolder.status_message.setVisibility(View.VISIBLE);
            viewHolder.load_more_messages.setVisibility(View.GONE);
            viewHolder.status_message.setText(message.getBody());
            boolean showAvatar;
            if (conversation.getMode() == Conversation.MODE_SINGLE) {
                showAvatar = true;
                AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar_on_status_message);
            } else if (message.getCounterpart() != null || message.getTrueCounterpart() != null || (message.getCounterparts() != null && message.getCounterparts().size() > 0)) {
                showAvatar = true;
                AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar_on_status_message);
            } else {
                showAvatar = false;
            }
            if (showAvatar) {
                viewHolder.contact_picture.setAlpha(0.5f);
                viewHolder.contact_picture.setVisibility(View.VISIBLE);
            } else {
                viewHolder.contact_picture.setVisibility(View.GONE);
            }
        }
        return view;
    } else {
        AvatarWorkerTask.loadAvatar(message, viewHolder.contact_picture, R.dimen.avatar);
    }
    resetClickListener(viewHolder.message_box, viewHolder.messageBody);
    viewHolder.contact_picture.setOnClickListener(v -> {
        if (MessageAdapter.this.mOnContactPictureClickedListener != null) {
            MessageAdapter.this.mOnContactPictureClickedListener.onContactPictureClicked(message);
        }
    });
    viewHolder.contact_picture.setOnLongClickListener(v -> {
        if (MessageAdapter.this.mOnContactPictureLongClickedListener != null) {
            MessageAdapter.this.mOnContactPictureLongClickedListener.onContactPictureLongClicked(v, message);
            return true;
        } else {
            return false;
        }
    });
    final Transferable transferable = message.getTransferable();
    final boolean unInitiatedButKnownSize = MessageUtils.unInitiatedButKnownSize(message);
    if (unInitiatedButKnownSize || message.isDeleted() || (transferable != null && transferable.getStatus() != Transferable.STATUS_UPLOADING)) {
        if (unInitiatedButKnownSize || transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER) {
            displayDownloadableMessage(viewHolder, message, activity.getString(R.string.download_x_file, UIHelper.getFileDescriptionString(activity, message)), darkBackground);
        } else if (transferable != null && transferable.getStatus() == Transferable.STATUS_OFFER_CHECK_FILESIZE) {
            displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), darkBackground);
        } else {
            displayInfoMessage(viewHolder, UIHelper.getMessagePreview(activity, message).first, darkBackground);
        }
    } else if (message.isFileOrImage() && message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED) {
        if (message.getFileParams().width > 0 && message.getFileParams().height > 0) {
            displayMediaPreviewMessage(viewHolder, message, darkBackground);
        } else if (message.getFileParams().runtime > 0) {
            displayAudioMessage(viewHolder, message, darkBackground);
        } else {
            displayOpenableMessage(viewHolder, message, darkBackground);
        }
    } else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
        if (account.isPgpDecryptionServiceConnected()) {
            if (conversation instanceof Conversation && !account.hasPendingPgpIntent((Conversation) conversation)) {
                displayInfoMessage(viewHolder, activity.getString(R.string.message_decrypting), darkBackground);
            } else {
                displayInfoMessage(viewHolder, activity.getString(R.string.pgp_message), darkBackground);
            }
        } else {
            displayInfoMessage(viewHolder, activity.getString(R.string.install_openkeychain), darkBackground);
            viewHolder.message_box.setOnClickListener(this::promptOpenKeychainInstall);
            viewHolder.messageBody.setOnClickListener(this::promptOpenKeychainInstall);
        }
    } else if (message.getEncryption() == Message.ENCRYPTION_DECRYPTION_FAILED) {
        displayInfoMessage(viewHolder, activity.getString(R.string.decryption_failed), darkBackground);
    } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_NOT_FOR_THIS_DEVICE) {
        displayInfoMessage(viewHolder, activity.getString(R.string.not_encrypted_for_this_device), darkBackground);
    } else if (message.getEncryption() == Message.ENCRYPTION_AXOLOTL_FAILED) {
        displayInfoMessage(viewHolder, activity.getString(R.string.omemo_decryption_failed), darkBackground);
    } else {
        if (message.isGeoUri()) {
            displayLocationMessage(viewHolder, message, darkBackground);
        } else if (message.bodyIsOnlyEmojis() && message.getType() != Message.TYPE_PRIVATE) {
            displayEmojiMessage(viewHolder, message.getBody().trim(), darkBackground);
        } else if (message.treatAsDownloadable()) {
            try {
                final URI uri = new URI(message.getBody());
                displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize_on_host, UIHelper.getFileDescriptionString(activity, message), uri.getHost()), darkBackground);
            } catch (Exception e) {
                displayDownloadableMessage(viewHolder, message, activity.getString(R.string.check_x_filesize, UIHelper.getFileDescriptionString(activity, message)), darkBackground);
            }
        } else {
            displayTextMessage(viewHolder, message, darkBackground, type);
        }
    }
    if (type == RECEIVED) {
        if (isInValidSession) {
            int bubble;
            if (!mUseGreenBackground) {
                bubble = activity.getThemeResource(R.attr.message_bubble_received_monochrome, R.drawable.message_bubble_received_white);
            } else {
                bubble = activity.getThemeResource(R.attr.message_bubble_received_green, R.drawable.message_bubble_received);
            }
            viewHolder.message_box.setBackgroundResource(bubble);
            viewHolder.encryption.setVisibility(View.GONE);
        } else {
            viewHolder.message_box.setBackgroundResource(R.drawable.message_bubble_received_warning);
            viewHolder.encryption.setVisibility(View.VISIBLE);
            if (omemoEncryption && !message.isTrusted()) {
                viewHolder.encryption.setText(R.string.not_trusted);
            } else {
                viewHolder.encryption.setText(CryptoHelper.encryptionTypeToText(message.getEncryption()));
            }
        }
    }
    displayStatus(viewHolder, message, type, darkBackground);
    return view;
}
Also used : Conversational(eu.siacs.conversations.entities.Conversational) Account(eu.siacs.conversations.entities.Account) Message(eu.siacs.conversations.entities.Message) RtpSessionStatus(eu.siacs.conversations.entities.RtpSessionStatus) Transferable(eu.siacs.conversations.entities.Transferable) Conversation(eu.siacs.conversations.entities.Conversation) URI(java.net.URI)

Example 4 with RtpSessionStatus

use of eu.siacs.conversations.entities.RtpSessionStatus 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 5 with RtpSessionStatus

use of eu.siacs.conversations.entities.RtpSessionStatus 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)5 Message (eu.siacs.conversations.entities.Message)5 RtpSessionStatus (eu.siacs.conversations.entities.RtpSessionStatus)5 Account (eu.siacs.conversations.entities.Account)2 Contact (eu.siacs.conversations.entities.Contact)2 Conversational (eu.siacs.conversations.entities.Conversational)2 Transferable (eu.siacs.conversations.entities.Transferable)2 Element (eu.siacs.conversations.xml.Element)2 Jid (eu.siacs.conversations.xmpp.Jid)2 MessagePacket (eu.siacs.conversations.xmpp.stanzas.MessagePacket)2 Base64 (android.util.Base64)1 Log (android.util.Log)1 Objects (com.google.common.base.Objects)1 Optional (com.google.common.base.Optional)1 Preconditions (com.google.common.base.Preconditions)1 Cache (com.google.common.cache.Cache)1 CacheBuilder (com.google.common.cache.CacheBuilder)1 Collections2 (com.google.common.collect.Collections2)1 ComparisonChain (com.google.common.collect.ComparisonChain)1 ImmutableSet (com.google.common.collect.ImmutableSet)1