From b0780224b5bdd68d74ef514e64e14ce9d37d7b90 Mon Sep 17 00:00:00 2001 From: steckbrief Date: Sat, 5 May 2018 20:28:04 +0200 Subject: introduces new message state model --- .../conversationsplus/parser/MessageParser.java | 735 +++++++++------------ 1 file changed, 323 insertions(+), 412 deletions(-) (limited to 'src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java') diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java index 889adbac..a849d26b 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java @@ -3,35 +3,37 @@ package de.thedevstack.conversationsplus.parser; import android.util.Log; import android.util.Pair; -import de.thedevstack.conversationsplus.entities.FileParams; -import de.thedevstack.conversationsplus.enums.FileStatus; +import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlMessageParser; +import de.thedevstack.conversationsplus.crypto.otr.OtrMessageParser; +import de.thedevstack.conversationsplus.crypto.otr.OtrUtil; +import de.thedevstack.conversationsplus.enums.MessageDirection; +import de.thedevstack.conversationsplus.enums.MessageStatus; import de.thedevstack.conversationsplus.persistance.DatabaseBackend; import de.thedevstack.conversationsplus.services.avatar.AvatarCache; import de.thedevstack.conversationsplus.services.filetransfer.http.download.AutomaticFileDownload; import de.thedevstack.conversationsplus.services.filetransfer.http.download.HttpRetrieveHead; +import de.thedevstack.conversationsplus.utils.ConversationUtil; +import de.thedevstack.conversationsplus.utils.MessageParserUtil; import de.thedevstack.conversationsplus.utils.MessageUtil; import de.thedevstack.conversationsplus.utils.UiUpdateHelper; -import de.thedevstack.conversationsplus.utils.XmppSendUtil; +import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor; import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacket; import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser; import de.thedevstack.conversationsplus.xmpp.carbons.Carbons; +import de.thedevstack.conversationsplus.xmpp.chatmarkers.ChatMarkers; import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint; import de.thedevstack.conversationsplus.xmpp.mam.Mam; +import de.thedevstack.conversationsplus.xmpp.muc.MucPacketParser; import de.thedevstack.conversationsplus.xmpp.openpgp.OpenPgpXep; +import de.thedevstack.conversationsplus.xmpp.receipts.MessageDeliveryReceipts; import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketReceiver; -import de.tzur.conversations.Settings; -import net.java.otr4j.session.Session; -import net.java.otr4j.session.SessionStatus; - -import java.util.ArrayList; import java.util.Set; import de.thedevstack.android.logcat.Logging; import de.thedevstack.conversationsplus.ConversationsPlusPreferences; import de.thedevstack.conversationsplus.utils.AvatarUtil; import de.thedevstack.conversationsplus.Config; -import de.thedevstack.conversationsplus.crypto.OtrService; import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService; import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlServiceImpl; import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlMessage; @@ -44,7 +46,6 @@ import de.thedevstack.conversationsplus.entities.MucOptions; import de.thedevstack.conversationsplus.services.avatar.AvatarService; import de.thedevstack.conversationsplus.services.mam.MessageArchiveService; import de.thedevstack.conversationsplus.services.XmppConnectionService; -import de.thedevstack.conversationsplus.utils.CryptoHelper; import de.thedevstack.conversationsplus.xml.Element; import de.thedevstack.conversationsplus.xmpp.OnMessagePacketReceived; import de.thedevstack.conversationsplus.xmpp.chatstate.ChatState; @@ -72,117 +73,13 @@ public class MessageParser extends AbstractParser implements } return false; } else { + updateLastseen(packet, account, true); // Todo: Should the timestamp be extracted here? return conversation.setIncomingChatState(state); } } return false; } - private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) { - String presence; - if (from.isBareJid()) { - presence = ""; - } else { - presence = from.getResourcepart(); - } - if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) { - conversation.endOtrIfNeeded(); - } - if (!conversation.hasValidOtrSession()) { - conversation.startOtrSession(presence,false); - } else { - String foreignPresence = conversation.getOtrSession().getSessionID().getUserID(); - if (!foreignPresence.equals(presence)) { - conversation.endOtrIfNeeded(); - conversation.startOtrSession(presence, false); - } - } - try { - conversation.setLastReceivedOtrMessageId(id); - Session otrSession = conversation.getOtrSession(); - body = otrSession.transformReceiving(body); - SessionStatus status = otrSession.getSessionStatus(); - if (body == null && status == SessionStatus.ENCRYPTED) { - mXmppConnectionService.onOtrSessionEstablished(conversation); - return null; - } else if (body == null && status == SessionStatus.FINISHED) { - conversation.resetOtrSession(); - UiUpdateHelper.updateConversationUi(); - return null; - } else if (body == null || (body.isEmpty())) { - return null; - } - if (body.startsWith(CryptoHelper.FILETRANSFER)) { - String key = body.substring(CryptoHelper.FILETRANSFER.length()); - conversation.setSymmetricKey(CryptoHelper.hexToBytes(key)); - return null; - } - final OtrService otrService = conversation.getAccount().getOtrService(); - Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED); - finishedMessage.setFingerprint(otrService.getFingerprint(otrSession.getRemotePublicKey())); - conversation.setLastReceivedOtrMessageId(null); - - return finishedMessage; - } catch (Exception e) { - conversation.resetOtrSession(); - return null; - } - } - - private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) { - Message finishedMessage = null; - AxolotlService service = conversation.getAccount().getAxolotlService(); - XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid()); - XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage); - if(plaintextMessage != null) { - finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status); - finishedMessage.setFingerprint(plaintextMessage.getFingerprint()); - Logging.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint()); - } - - return finishedMessage; - } - - private class Invite { - Jid jid; - String password; - Invite(Jid jid, String password) { - this.jid = jid; - this.password = password; - } - - public boolean execute(Account account) { - if (jid != null) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid, true); - if (!conversation.getMucOptions().online()) { - conversation.getMucOptions().setPassword(password); - DatabaseBackend.getInstance().updateConversation(conversation); - mXmppConnectionService.joinMuc(conversation); - UiUpdateHelper.updateConversationUi(); - } - return true; - } - return false; - } - } - - private Invite extractInvite(Element message) { - Element x = message.findChild("x", "http://jabber.org/protocol/muc#user"); - if (x != null) { - Element invite = x.findChild("invite"); - if (invite != null) { - Element pw = x.findChild("password"); - return new Invite(message.getAttributeAsJid("from"), pw != null ? pw.getContent(): null); - } - } else { - x = message.findChild("x","jabber:x:conference"); - if (x != null) { - return new Invite(x.getAttributeAsJid("jid"),x.getAttribute("password")); - } - } - return null; - } - private static String extractStanzaId(Element packet, Jid by) { for(Element child : packet.getChildren()) { if (child.getName().equals("stanza-id") @@ -251,13 +148,15 @@ public class MessageParser extends AbstractParser implements } else if (error != null) { Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending message to "+ from+ " failed - " + error); } - Message message = mXmppConnectionService.markMessage(account, + Message message = XmppConnectionServiceAccessor.xmppConnectionService.getMessage(account, from.toBareJid(), - packet.getId(), - Message.STATUS_SEND_FAILED); - if (message != null && message.getEncryption() == Message.ENCRYPTION_OTR) { - message.getConversation().endOtrIfNeeded(); - } + packet.getId()); + if (null != message) { + MessageUtil.setAndSaveMessageStatus(message, MessageStatus.FAILED); + if (Message.ENCRYPTION_OTR == message.getEncryption()) { + message.getConversation().endOtrIfNeeded(); + } + } } return true; } @@ -269,326 +168,338 @@ public class MessageParser extends AbstractParser implements if (handleErrorMessage(account, original)) { return; } - final MessagePacket packet; + MessagePacket packet = null; Long timestamp = null; - final boolean isForwarded; boolean isCarbon = false; + boolean isMAMCatchup = false; String serverMsgId = null; + MessageDirection direction = null; + final Element fin = original.findChild("fin", Mam.NAMESPACE); + MessageArchiveService messageArchiveService = XmppConnectionServiceAccessor.xmppConnectionService.getMessageArchiveService(); if (fin != null) { - mXmppConnectionService.getMessageArchiveService().processFin(fin,original.getFrom()); + // Todo: Check for query.getWith() -> MAM Catchup -> Handle?? + messageArchiveService.processFin(fin,original.getFrom()); return; } final Element result = original.findChild("result", Mam.NAMESPACE); - final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); - if (query != null && query.validFrom(original.getFrom())) { - Pair f = original.getForwardedMessagePacket("result", Mam.NAMESPACE); - if (f == null) { - return; - } - timestamp = f.second; - packet = f.first; - isForwarded = true; - serverMsgId = result.getAttribute("id"); - query.incrementMessageCount(); - } else if (query != null) { - Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); - return; - } else if (original.fromServer(account)) { - Pair f; - f = original.getForwardedMessagePacket("received", Carbons.NAMESPACE); - f = f == null ? original.getForwardedMessagePacket("sent", Carbons.NAMESPACE) : f; - packet = f != null ? f.first : original; - if (handleErrorMessage(account, packet)) { - return; - } - timestamp = f != null ? f.second : null; - isCarbon = f != null; - isForwarded = isCarbon; - } else { - packet = original; - isForwarded = false; - } + boolean isMAM = result != null; + + if (isMAM) { + final MessageArchiveService.Query query = messageArchiveService.findQuery(result.getAttribute("queryid")); + if (null != query && query.validFrom(original.getFrom())) { + Pair forwardedMessagePacket = original.getForwardedMessagePacket("result", Mam.NAMESPACE); + if (forwardedMessagePacket == null) { + return; + } + isMAMCatchup = null == query.getWith(); + timestamp = forwardedMessagePacket.second; + packet = forwardedMessagePacket.first; + serverMsgId = result.getAttribute("id"); + query.incrementMessageCount(); + } else { + Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); + return; + } + } else if (original.fromServer(account)) { // Todo: Check if carbons are not always from the own bare JID + Pair forwardedMessagePacket = original.getForwardedMessagePacket("received", Carbons.NAMESPACE); + if (null != forwardedMessagePacket) { + direction = MessageDirection.IN; + } else { + forwardedMessagePacket = original.getForwardedMessagePacket("sent", Carbons.NAMESPACE); + if (null != forwardedMessagePacket) { + direction = MessageDirection.OUT; + } + } + + isCarbon = forwardedMessagePacket != null; + if (isCarbon) { + packet = forwardedMessagePacket.first; + timestamp = forwardedMessagePacket.second; + if (handleErrorMessage(account, packet)) { + return; + } + } + } + + if (null == packet) { + packet = original; + } + + if (null == timestamp) { + timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis()); + } + + final Jid from = packet.getFrom(); + + if (from == null) { + Log.d(Config.LOGTAG,"no from in: "+packet.toString()); + return; + } - if (timestamp == null) { - timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis()); - } final String body = packet.getBody(); - final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user"); - final String pgpEncrypted = packet.findChildContent(OpenPgpXep.ENCRYPTED_ELEMENT, OpenPgpXep.ENCRYPTED_NAMESPACE); - final Element oob = packet.findChild("x", "jabber:x:oob"); - final boolean isOob = oob!= null && body != null && body.equals(oob.findChildContent("url")); - final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); - int status; + final String pgpEncrypted = (Config.supportOpenPgp()) ? packet.findChildContent(OpenPgpXep.ENCRYPTED_ELEMENT, OpenPgpXep.ENCRYPTED_NAMESPACE) : null; + final Element axolotlEncrypted = (Config.supportOmemo()) ? packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX) : null; final Jid counterpart; final Jid to = packet.getTo(); - final Jid from = packet.getFrom(); - final String remoteMsgId = packet.getId(); - if (from == null) { - Log.d(Config.LOGTAG,"no from in: "+packet.toString()); - return; - } - - boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; - boolean isProperlyAddressed = (to != null ) && (!to.isBareJid() || account.countPresences() <= 1); - boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status"); if (packet.fromAccount(account)) { - status = Message.STATUS_SEND; + direction = (null == direction) ? MessageDirection.OUT : direction; counterpart = to != null ? to : account.getJid(); } else { - status = Message.STATUS_RECEIVED; + direction = (null == direction) ? MessageDirection.IN : direction; counterpart = from; } - Invite invite = extractInvite(packet); - if (invite != null && invite.execute(account)) { - return; - } - - if (extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) { - UiUpdateHelper.updateConversationUi(); - } - - if ((body != null || pgpEncrypted != null || axolotlEncrypted != null) && !isMucStatusMessage) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat, query); - if (isTypeGroupChat) { - if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { - status = Message.STATUS_SEND_RECEIVED; - isCarbon = true; //not really carbon but received from another resource - if (MessageUtil.markMessage(conversation, remoteMsgId, status)) { - return; - } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { - Message message = conversation.findSentMessageWithBody(packet.getBody()); - if (message != null) { - MessageUtil.markMessage(message, status); - return; - } - } - } else { - status = Message.STATUS_RECEIVED; - } - } - Message message; - if (body != null && body.startsWith("?OTR") && Config.supportOtr()) { - if (!isForwarded && !isTypeGroupChat && isProperlyAddressed) { - message = parseOtrChat(body, from, remoteMsgId, conversation); - if (message == null) { - return; - } - } else { - Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": ignoring OTR message from "+from+" isForwarded="+Boolean.toString(isForwarded)+", isProperlyAddressed="+Boolean.valueOf(isProperlyAddressed)); - message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); - } - } else if (pgpEncrypted != null && Config.supportOpenPgp()) { - message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status); - } else if (axolotlEncrypted != null && Config.supportOmemo()) { - Jid origin; - if (conversation.getMode() == Conversation.MODE_MULTI) { - origin = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart()); - if (origin == null) { - Log.d(Config.LOGTAG,"axolotl message in non anonymous conference received"); - return; - } - } else { - origin = from; - } - message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status); - if (message == null) { - return; - } - } else { - message = new Message(conversation, body, Message.ENCRYPTION_NONE, status); - } - - if (serverMsgId == null) { - serverMsgId = extractStanzaId(packet, isTypeGroupChat ? conversation.getJid().toBareJid() : account.getServer()); - } - message.setHttpUploaded(packet.hasChild(HttpUploadHint.ELEMENT_NAME, HttpUploadHint.NAMESPACE)); - if (message.isHttpUploaded()) { - message.setTreatAsDownloadable(Message.Decision.MUST); - } - - message.setCounterpart(counterpart); - message.setRemoteMsgId(remoteMsgId); - message.setServerMsgId(serverMsgId); - message.setCarbon(isCarbon); - message.setTime(timestamp); - message.setOob(isOob); - message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); - if (conversation.getMode() == Conversation.MODE_MULTI) { - Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart()); - message.setTrueCounterpart(trueCounterpart); - if (trueCounterpart != null) { - updateLastseen(timestamp, account, trueCounterpart, false); - } - if (!isTypeGroupChat) { - message.setType(Message.TYPE_PRIVATE); - } - } else { - updateLastseen(timestamp, account, packet.getFrom(), true); - } + if (MucPacketParser.extractAndExecuteInvite(account, packet)) { + return; + } - boolean checkForDuplicates = query != null - || (isTypeGroupChat && packet.hasChild("delay","urn:xmpp:delay")) - || message.getType() == Message.TYPE_PRIVATE; - if (checkForDuplicates && conversation.hasDuplicateMessage(message)) { - Logging.d(Config.LOGTAG, "skipping duplicate message from '" + message.getCounterpart().toString() + "' with remote id " + message.getRemoteMsgId()); - return; - } - - if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) { - conversation.prepend(message); - } else { - conversation.add(message); - } - - if (message.getEncryption() == Message.ENCRYPTION_PGP) { - conversation.getAccount().getPgpDecryptionService().add(message); - } - - if (query == null || query.getWith() == null) { //either no mam or catchup - if (status == Message.STATUS_SEND || status == Message.STATUS_SEND_RECEIVED) { - Logging.d("markRead", "MessageParser.onMessagePacketReceived1 (" + conversation.getName() + ")"); - mXmppConnectionService.markRead(conversation); - if (query == null) { - account.activateGracePeriod(); - } - } else { - // only not mam messages should be marked as unread - if (query == null) { - message.markUnread(); - } - } - } + Conversation conversation = null; - if (query == null) { - UiUpdateHelper.updateConversationUi(); - } + if ((body != null || pgpEncrypted != null || axolotlEncrypted != null)) { + boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; + conversation = XmppConnectionServiceAccessor.xmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat); - if (Settings.CONFIRM_MESSAGE_READ && remoteMsgId != null && !isForwarded && !isTypeGroupChat) { - sendMessageReceipts(account, packet); - } + final String remoteMsgId = packet.getId(); - if (message.getStatus() == Message.STATUS_RECEIVED - && conversation.getOtrSession() != null - && !conversation.getOtrSession().getSessionID().getUserID() - .equals(message.getCounterpart().getResourcepart())) { - conversation.endOtrIfNeeded(); - } + Message message = null; + if (Config.supportOtr() && OtrUtil.isOtrBody(body)) { + if (OtrUtil.isValidOtrMessagePacket(packet, account)) { + message = OtrMessageParser.parseOtrChat(body, counterpart, remoteMsgId, conversation); + } else { + Logging.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": ignoring OTR message from "+counterpart+", addressed to: " + to); + } + } else { + message = this.createMessage(conversation, counterpart, body, pgpEncrypted, axolotlEncrypted); + } - MessageUtil.extractFileParamsFromBody(message); - FileParams fileParams = message.getFileParams(); - if (message.treatAsDownloadable() != Message.Decision.NEVER && message.treatAsDownloadable() != Message.Decision.NOT_DECIDED) { - if (null != fileParams) { - fileParams.setFileStatus(FileStatus.NEEDS_DOWNLOAD); - } - } - if (message.getEncryption() == Message.ENCRYPTION_NONE || !ConversationsPlusPreferences.dontSaveEncrypted()) { - DatabaseBackend.getInstance().createMessage(message); - } - if (message.trusted() - && message.treatAsDownloadable() != Message.Decision.NEVER - && ConversationsPlusPreferences.autoAcceptFileSize() > 0 - && (message.isHttpUploaded() || MessageUtil.hasDownloadableLink(message))) { // Can this be checked by MessageUtil.needsDownload(message) ?? - HttpRetrieveHead hrh = new HttpRetrieveHead(message); - hrh.setListener(new AutomaticFileDownload(true)); - hrh.retrieveAndSetContentTypeAndLength(); - } else { - if (query == null) { - mXmppConnectionService.getNotificationService().push(message); - } else if (query.getWith() == null) { // mam catchup - /* - Like suggested in https://bugs.thedevstack.de/task/156 user should be notified - in some other way of loaded messages. - */ - // mXmppConnectionService.getNotificationService().pushFromBacklog(message); - } - } + if (null != message) { + message.setDirection(direction); + message.setMessageStatus(MessageStatus.TRANSMITTING); + message.setCarbon(isCarbon); + message.setMamReceived(isMAM); + message.setConfirmation(MessageParserUtil.extractMessageConfirmation(packet)); + + if (isTypeGroupChat + && this.fixMessageDirectionForGroupChatIfNecessary(message, counterpart)) { + return; + } + + this.handleMessagePacketWithBodyOrEncryptedContent(message, packet, account, conversation, counterpart, isTypeGroupChat, remoteMsgId, serverMsgId, timestamp); + + this.markMessagesUnreadIfNecessary(message, isMAM, isMAMCatchup); + + if (!isMAM) { + UiUpdateHelper.updateConversationUi(); + } + + if (message.trusted() + && MessageStatus.TRANSMITTING == message.getMessageStatus() // Todo: Every message should be in transmitting state here + && ConversationsPlusPreferences.autoAcceptFileSize() > 0 + && (message.isHttpUploaded() || MessageUtil.hasDownloadableLink(message))) { // Can this be checked by MessageUtil.needsDownload(message) ?? + HttpRetrieveHead hrh = new HttpRetrieveHead(message); + hrh.setListener(new AutomaticFileDownload()); + hrh.retrieveAndSetContentTypeAndLength(); + } else { + MessageUtil.setAndSaveMessageStatus(message, MessageStatus.TRANSMITTED); + } + } } else if (!packet.hasChild("body")){ //no body - if (isTypeGroupChat) { - Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); - if (packet.hasChild("subject")) { - if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { - conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0); - String subject = packet.findChildContent("subject"); - conversation.getMucOptions().setSubject(subject); - final Bookmark bookmark = conversation.getBookmark(); - if (bookmark != null && bookmark.getBookmarkName() == null) { - if (bookmark.setBookmarkName(subject)) { - mXmppConnectionService.pushBookmarks(account); - } - } - UiUpdateHelper.updateConversationUi(); - return; - } - } - - if (conversation != null && isMucStatusMessage) { - for (Element child : mucUserElement.getChildren()) { - if (child.getName().equals("status") - && MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED.equals(child.getAttribute("code"))) { - mXmppConnectionService.fetchConferenceConfiguration(conversation); - } - } - } - } + this.handleMessagePacketWithoutBody(packet, account); } - Element received = packet.findChild("received", "urn:xmpp:chat-markers:0"); - if (received == null) { - received = packet.findChild("received", "urn:xmpp:receipts"); - } - if (received != null && !packet.fromAccount(account)) { - mXmppConnectionService.markMessage(account, from.toBareJid(), received.getAttribute("id"), Message.STATUS_SEND_RECEIVED); - } - Element displayed = packet.findChild("displayed", "urn:xmpp:chat-markers:0"); - if (displayed != null) { - if (packet.fromAccount(account)) { - Conversation conversation = mXmppConnectionService.find(account,counterpart.toBareJid()); - if (conversation != null) { - Logging.d("markRead", "MessageParser.onMessagePacketReceived2 (" + conversation.getName() + ")"); - mXmppConnectionService.markRead(conversation); - } - } else { - updateLastseen(timestamp, account, packet.getFrom(), true); - final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), displayed.getAttribute("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()) { - MessageUtil.markMessage(message, Message.STATUS_SEND_DISPLAYED); - message = message.prev(); - } - } - } + this.handleChatState(conversation, account, packet, counterpart); - Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event"); - if (event != null) { - parseEvent(event, from, account); - } + this.handleMessageReceipts(packet, account, from, counterpart, timestamp); - String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick"); - if (nick != null) { - Contact contact = account.getRoster().getContact(from); - contact.setPresenceName(nick); - } - } + this.handleEvent(packet, account, from); - private void sendMessageReceipts(Account account, MessagePacket packet) { - ArrayList receiptsNamespaces = new ArrayList<>(); - if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { - receiptsNamespaces.add("urn:xmpp:chat-markers:0"); - } - if (packet.hasChild("request", "urn:xmpp:receipts")) { - receiptsNamespaces.add("urn:xmpp:receipts"); - } - if (receiptsNamespaces.size() > 0) { - MessagePacket receipt = mXmppConnectionService.getMessageGenerator().received(account, - packet, - receiptsNamespaces, - packet.getType()); - XmppSendUtil.sendMessagePacket(account, receipt); - } + this.handleNick(packet, account, from); } + + private void markMessagesUnreadIfNecessary(Message message, boolean isMAM, boolean isCatchup) { + Conversation conversation = message.getConversation(); + Account account = conversation.getAccount(); + if (!isMAM || isCatchup) { //either no mam or catchup + if (MessageUtil.isOutgoingMessage(message)) { + Logging.d("markRead", "MessageParser.onMessagePacketReceived1 (" + conversation.getName() + ")"); + XmppConnectionServiceAccessor.xmppConnectionService.markRead(conversation); + if (!isMAM) { + account.activateGracePeriod(); + } + } else { + // only not mam messages should be marked as unread + if (!isMAM) { + message.markUnread(); + } + } + } + } + + private void handleChatState(Conversation conversation, Account account, MessagePacket packet, Jid counterpart) { + if (null == conversation) { + conversation = XmppConnectionServiceAccessor.xmppConnectionService.find(account, counterpart.toBareJid()); + } + if (extractChatState(conversation, packet)) { + UiUpdateHelper.updateConversationUi(); + } + } + + private boolean fixMessageDirectionForGroupChatIfNecessary(Message message, Jid counterpart) { + Conversation conversation = message.getConversation(); + if (MessageUtil.isIncomingMessage(message) + && counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) { // Message is sent from me + message.setDirection(MessageDirection.OUT); // Not sure, but assuming + String remoteMsgId = message.getRemoteMsgId(); + if (MessageUtil.setAndSaveMessageStatus(conversation, remoteMsgId, MessageStatus.RECEIVED)) { // TODO: Check if this status is correct + return true; + } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { + Message existingMessage = conversation.findSentMessageWithBody(message.getBody()); + if (existingMessage != null) { + MessageUtil.setAndSaveMessageStatus(existingMessage, MessageStatus.RECEIVED); + return true; + } + } + } + return false; + } + + private Message createMessage(Conversation conversation, Jid counterpart, String body, + String pgpEncrypted, Element axolotlEncrypted) { + Message message; + + if (pgpEncrypted != null) { + message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP); + } else if (axolotlEncrypted != null) { + message = AxolotlMessageParser.parseAxolotlChat(axolotlEncrypted, counterpart, counterpart, conversation); + if (message == null) { + return null; + } + } else { + message = new Message(conversation, body, Message.ENCRYPTION_NONE); + } + + return message; + } + + private Message handleMessagePacketWithBodyOrEncryptedContent(Message message, MessagePacket packet, Account account, Conversation conversation, Jid counterpart, + boolean isTypeGroupChat, String remoteMsgId, String serverMsgId, long timestamp) { + if (serverMsgId == null) { + serverMsgId = extractStanzaId(packet, isTypeGroupChat ? conversation.getJid().toBareJid() : account.getServer()); + } + message.setHttpUploaded(packet.hasChild(HttpUploadHint.HTTP_UPLOAD_HINT)); + + message.setCounterpart(counterpart); + message.setRemoteMsgId(remoteMsgId); + message.setServerMsgId(serverMsgId); + message.setTime(timestamp); + + if (conversation.getMode() == Conversation.MODE_MULTI) { + Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart()); + message.setTrueCounterpart(trueCounterpart); + if (trueCounterpart != null) { + updateLastseen(timestamp, account, trueCounterpart, false); + } + if (!isTypeGroupChat) { + message.setType(Message.TYPE_PRIVATE); + } + } else { + updateLastseen(timestamp, account, packet.getFrom(), true); + } + + conversation.add(message); + + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + conversation.getAccount().getPgpDecryptionService().add(message); + } + + if (MessageUtil.isIncomingMessage(message) + && OtrUtil.isOtrSessionActive(conversation) + && !OtrUtil.isCounterpartOfActiveOtrSession(conversation, message.getCounterpart())) { + conversation.endOtrIfNeeded(); + } + + MessageParserUtil.extractFileParamsFromBody(message); + + if (message.getEncryption() == Message.ENCRYPTION_NONE || !ConversationsPlusPreferences.dontSaveEncrypted()) { + DatabaseBackend.getInstance().createMessage(message); + } + + return message; + } + + private void handleNick(MessagePacket packet, Account account, Jid from) { // TODO: Is this a nick change? + String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick"); + if (nick != null) { + Contact contact = account.getRoster().getContact(from); + contact.setPresenceName(nick); + } + } + + private void handleEvent(MessagePacket packet, Account account, Jid from) { + Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event"); + if (event != null) { + parseEvent(event, from, account); + } + } + + private void handleMessageReceipts(MessagePacket packet, Account account, Jid from, Jid counterpart, long timestamp) { + Element received = packet.findChild(ChatMarkers.RECEIVED); + if (received == null) { + received = packet.findChild(MessageDeliveryReceipts.RECEIVED); + } + if (received != null && !packet.fromAccount(account)) { + MessageUtil.markMessageAsReceived(account, from.toBareJid(), received.getAttribute("id")); + } + Element displayed = packet.findChild(ChatMarkers.DISPLAYED); + if (displayed != null) { + if (packet.fromAccount(account)) { + Conversation conversation = mXmppConnectionService.find(account, counterpart.toBareJid()); + if (conversation != null) { + Logging.d("markRead", "MessageParser.onMessagePacketReceived2 (" + conversation.getName() + ")"); + mXmppConnectionService.markRead(conversation); + } + } else { + updateLastseen(timestamp, account, packet.getFrom(), true); + Message message = XmppConnectionServiceAccessor.xmppConnectionService.getMessage(account, from.toBareJid(), displayed.getAttribute("id")); + if (null != message) { + ConversationUtil.markMessagesAsDisplayedUpToMessage(message); + } + } + } + } + + private void handleMessagePacketWithoutBody(MessagePacket packet, Account account) { + if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) { + Jid from = packet.getFrom(); + final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user"); + boolean isMucStatusMessage = from.isBareJid() && mucUserElement != null && mucUserElement.hasChild("status"); + Conversation conversation = mXmppConnectionService.find(account, from.toBareJid()); + if (packet.hasChild("subject")) { + if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) { + conversation.setHasMessagesLeftOnServer(conversation.countMessages() > 0); + String subject = packet.findChildContent("subject"); + conversation.getMucOptions().setSubject(subject); + final Bookmark bookmark = conversation.getBookmark(); + if (bookmark != null && bookmark.getBookmarkName() == null) { + if (bookmark.setBookmarkName(subject)) { + mXmppConnectionService.pushBookmarks(account); + } + } + UiUpdateHelper.updateConversationUi(); + return; + } + } + + if (conversation != null && isMucStatusMessage) { + for (Element child : mucUserElement.getChildren()) { + if (child.getName().equals("status") + && MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED.equals(child.getAttribute("code"))) { + mXmppConnectionService.fetchConferenceConfiguration(conversation); + } + } + } + } + } } -- cgit v1.2.3