diff options
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java')
-rw-r--r-- | src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java | 200 |
1 files changed, 149 insertions, 51 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java index 8c7d8efd..fd89ad00 100644 --- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java +++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java @@ -1,14 +1,25 @@ package de.thedevstack.conversationsplus.parser; +import android.util.Log; import android.util.Pair; +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.axolotl.AxolotlService; +import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlServiceImpl; +import de.thedevstack.conversationsplus.crypto.axolotl.XmppAxolotlMessage; import de.thedevstack.conversationsplus.entities.Account; +import de.thedevstack.conversationsplus.entities.Bookmark; import de.thedevstack.conversationsplus.entities.Contact; import de.thedevstack.conversationsplus.entities.Conversation; import de.thedevstack.conversationsplus.entities.Message; @@ -17,7 +28,6 @@ import de.thedevstack.conversationsplus.http.HttpConnectionManager; import de.thedevstack.conversationsplus.services.AvatarService; import de.thedevstack.conversationsplus.services.MessageArchiveService; import de.thedevstack.conversationsplus.services.XmppConnectionService; -import de.thedevstack.conversationsplus.utils.AvatarUtil; import de.thedevstack.conversationsplus.utils.CryptoHelper; import de.thedevstack.conversationsplus.xml.Element; import de.thedevstack.conversationsplus.xmpp.OnMessagePacketReceived; @@ -39,6 +49,10 @@ public class MessageParser extends AbstractParser implements Jid from = packet.getFrom(); if (from.toBareJid().equals(account.getJid().toBareJid())) { conversation.setOutgoingChatState(state); + if (state == ChatState.ACTIVE || state == ChatState.COMPOSING) { + mXmppConnectionService.markRead(conversation); + account.activateGracePeriod(); + } return false; } else { return conversation.setIncomingChatState(state); @@ -72,11 +86,9 @@ public class MessageParser extends AbstractParser implements body = otrSession.transformReceiving(body); SessionStatus status = otrSession.getSessionStatus(); if (body == null && status == SessionStatus.ENCRYPTED) { - conversation.setNextEncryption(Message.ENCRYPTION_OTR); mXmppConnectionService.onOtrSessionEstablished(conversation); return null; } else if (body == null && status == SessionStatus.FINISHED) { - conversation.setNextEncryption(Message.ENCRYPTION_NONE); conversation.resetOtrSession(); mXmppConnectionService.updateConversationUi(); return null; @@ -97,6 +109,20 @@ public class MessageParser extends AbstractParser implements } } + private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, 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.setAxolotlFingerprint(plaintextMessage.getFingerprint()); + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint()); + } + + return finishedMessage; + } + private class Invite { Jid jid; String password; @@ -137,13 +163,24 @@ public class MessageParser extends AbstractParser implements return null; } + private static String extractStanzaId(Element packet, Jid by) { + for(Element child : packet.getChildren()) { + if (child.getName().equals("stanza-id") + && "urn:xmpp:sid:0".equals(child.getNamespace()) + && by.equals(child.getAttributeAsJid("by"))) { + return child.getAttribute("id"); + } + } + return null; + } + private void parseEvent(final Element event, final Jid from, final Account account) { Element items = event.findChild("items"); String node = items == null ? null : items.getAttribute("node"); if ("urn:xmpp:avatar:metadata".equals(node)) { Avatar avatar = Avatar.parseMetadata(items); if (avatar != null) { - avatar.owner = from; + avatar.owner = from.toBareJid(); if (AvatarUtil.isAvatarCached(avatar)) { if (account.getJid().toBareJid().equals(from)) { if (account.setAvatar(avatar.getFilename())) { @@ -166,13 +203,20 @@ public class MessageParser extends AbstractParser implements } else if ("http://jabber.org/protocol/nick".equals(node)) { Element i = items.findChild("item"); Element nick = i == null ? null : i.findChild("nick", "http://jabber.org/protocol/nick"); - if (nick != null) { + if (nick != null && nick.getContent() != null) { Contact contact = account.getRoster().getContact(from); contact.setPresenceName(nick.getContent()); AvatarService.getInstance().clear(account); mXmppConnectionService.updateConversationUi(); mXmppConnectionService.updateAccountUi(); } + } else if (ConversationsPlusPreferences.omemoEnabled() && AxolotlService.PEP_DEVICE_LIST.equals(node)) { + Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing..."); + Element item = items.findChild("item"); + Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item); + AxolotlService axolotlService = account.getAxolotlService(); + axolotlService.registerDevices(from, deviceIds); + mXmppConnectionService.updateAccountUi(); } } @@ -180,6 +224,13 @@ public class MessageParser extends AbstractParser implements if (packet.getType() == MessagePacket.TYPE_ERROR) { Jid from = packet.getFrom(); if (from != null) { + Element error = packet.findChild("error"); + String text = error == null ? null : error.findChildContent("text"); + if (text != null) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending message to "+ from+ " failed - " + text); + } else if (error != null) { + Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": sending message to "+ from+ " failed - " + error); + } Message message = mXmppConnectionService.markMessage(account, from.toBareJid(), packet.getId(), @@ -201,6 +252,7 @@ public class MessageParser extends AbstractParser implements final MessagePacket packet; Long timestamp = null; final boolean isForwarded; + boolean isCarbon = false; String serverMsgId = null; final Element fin = original.findChild("fin", "urn:xmpp:mam:0"); if (fin != null) { @@ -218,7 +270,7 @@ public class MessageParser extends AbstractParser implements packet = f.first; isForwarded = true; serverMsgId = result.getAttribute("id"); - query.incrementTotalCount(); + query.incrementMessageCount(); } else if (query != null) { Logging.d(Config.LOGTAG,account.getJid().toBareJid()+": received mam result from invalid sender"); return; @@ -231,7 +283,8 @@ public class MessageParser extends AbstractParser implements return; } timestamp = f != null ? f.second : null; - isForwarded = f != null; + isCarbon = f != null; + isForwarded = isCarbon; } else { packet = original; isForwarded = false; @@ -241,25 +294,26 @@ public class MessageParser extends AbstractParser implements timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis()); } final String body = packet.getBody(); - final String encrypted = packet.findChildContent("x", "jabber:x:encrypted"); - final Element mucUserElement = packet.findChild("x","http://jabber.org/protocol/muc#user"); + final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user"); + final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted"); + final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX); int status; final Jid counterpart; final Jid to = packet.getTo(); final Jid from = packet.getFrom(); final String remoteMsgId = packet.getId(); - if (from == null || to == null) { - Logging.d(Config.LOGTAG,"no to or from in: "+packet.toString()); + if (from == null) { + Log.d(Config.LOGTAG,"no from in: "+packet.toString()); return; } boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT; - boolean isProperlyAddressed = !to.isBareJid() || account.countPresences() == 1; + 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; - counterpart = to; + counterpart = to != null ? to : account.getJid(); } else { status = Message.STATUS_RECEIVED; counterpart = from; @@ -270,21 +324,21 @@ public class MessageParser extends AbstractParser implements return; } - if (extractChatState(mXmppConnectionService.find(account,from), packet)) { + if (extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), packet)) { mXmppConnectionService.updateConversationUi(); } - if ((body != null || encrypted != null) && !isMucStatusMessage) { - Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat); + 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 (mXmppConnectionService.markMessage(conversation, remoteMsgId, status)) { return; - } else { - Message message = conversation.findSentMessageWithBody(body); + } else if (remoteMsgId == null || Config.IGNORE_ID_REWRITE_IN_MUC) { + Message message = conversation.findSentMessageWithBody(packet.getBody()); if (message != null) { - message.setRemoteMsgId(remoteMsgId); mXmppConnectionService.markMessage(message, status); return; } @@ -294,69 +348,85 @@ public class MessageParser extends AbstractParser implements } } Message message; - if (body != null && body.startsWith("?OTR")) { + 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 (encrypted != null) { - message = new Message(conversation, encrypted, Message.ENCRYPTION_PGP, status); + } else if (pgpEncrypted != null && Config.supportOpenPgp()) { + message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status); + } else if (axolotlEncrypted != null && Config.supportOmemo()) { + message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, 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.setCounterpart(counterpart); message.setRemoteMsgId(remoteMsgId); message.setServerMsgId(serverMsgId); + message.setCarbon(isCarbon); message.setTime(timestamp); message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); if (conversation.getMode() == Conversation.MODE_MULTI) { - message.setTrueCounterpart(conversation.getMucOptions().getTrueCounterpart(counterpart.getResourcepart())); + 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); } - updateLastseen(packet,account,true); - boolean checkForDuplicates = serverMsgId != null + + 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()+" "+message.getBody()); + Log.d(Config.LOGTAG,"skipping duplicate message from "+message.getCounterpart().toString()+" "+message.getBody()); return; } - if (query != null) { - query.incrementMessageCount(); + + if (query != null && query.getPagingOrder() == MessageArchiveService.PagingOrder.REVERSE) { + conversation.prepend(message); + } else { + conversation.add(message); } - conversation.add(message); - if (serverMsgId == null) { + + 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) { mXmppConnectionService.markRead(conversation); - account.activateGracePeriod(); + if (query == null) { + account.activateGracePeriod(); + } } else { message.markUnread(); } - mXmppConnectionService.updateConversationUi(); } - if (ConversationsPlusPreferences.confirmMessages() && remoteMsgId != null && !isForwarded) { - if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) { - MessagePacket receipt = mXmppConnectionService - .getMessageGenerator().received(account, packet, "urn:xmpp:chat-markers:0"); - mXmppConnectionService.sendMessagePacket(account, receipt); - } - if (packet.hasChild("request", "urn:xmpp:receipts")) { - MessagePacket receipt = mXmppConnectionService - .getMessageGenerator().received(account, packet, "urn:xmpp:receipts"); - mXmppConnectionService.sendMessagePacket(account, receipt); - } + if (query == null) { + mXmppConnectionService.updateConversationUi(); } - if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) { - if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) { - mXmppConnectionService.updateConversation(conversation); - } + + if (Settings.CONFIRM_MESSAGE_READ && remoteMsgId != null && !isForwarded && !isTypeGroupChat) { + sendMessageReceipts(account, packet); } if (message.getStatus() == Message.STATUS_RECEIVED @@ -377,15 +447,26 @@ public class MessageParser extends AbstractParser implements && mXmppConnectionService.isDownloadAllowedInConnection()) { manager.createNewDownloadConnection(message); } else { - mXmppConnectionService.getNotificationService().push(message); + if (query == null) { + mXmppConnectionService.getNotificationService().push(message); + } else if (query.getWith() == null) { // mam catchup + mXmppConnectionService.getNotificationService().pushFromBacklog(message); + } } - } else { //no body + } 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); - conversation.getMucOptions().setSubject(packet.findChildContent("subject")); + 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); + } + } mXmppConnectionService.updateConversationUi(); return; } @@ -417,7 +498,7 @@ public class MessageParser extends AbstractParser implements mXmppConnectionService.markRead(conversation); } } else { - updateLastseen(packet, account, true); + 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 @@ -440,4 +521,21 @@ public class MessageParser extends AbstractParser implements contact.setPresenceName(nick); } } + + private void sendMessageReceipts(Account account, MessagePacket packet) { + ArrayList<String> 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()); + mXmppConnectionService.sendMessagePacket(account, receipt); + } + } } |