rewrote parser code. mam id and possible other stuff still missing. also massivly untested

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/parser/AbstractParser.java       |  41 
src/main/java/eu/siacs/conversations/parser/MessageParser.java        | 665 
src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java |   4 
src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java  |  24 
4 files changed, 230 insertions(+), 504 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/parser/AbstractParser.java 🔗

@@ -11,6 +11,7 @@ import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.services.XmppConnectionService;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.jid.Jid;
+import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 
 public abstract class AbstractParser {
 
@@ -20,22 +21,23 @@ public abstract class AbstractParser {
 		this.mXmppConnectionService = service;
 	}
 
-	protected long getTimestamp(Element packet) {
-		long now = System.currentTimeMillis();
-		Element delay = packet.findChild("delay");
-		if (delay == null) {
-			return now;
-		}
-		String stamp = delay.getAttribute("stamp");
-		if (stamp == null) {
-			return now;
-		}
-		try {
-			long time = parseTimestamp(stamp).getTime();
-			return now < time ? now : time;
-		} catch (ParseException e) {
-			return now;
+	public static Long getTimestamp(Element element, Long defaultValue) {
+		Element delay = element.findChild("delay");
+		if (delay != null) {
+			String stamp = delay.getAttribute("stamp");
+			if (stamp != null) {
+				try {
+					return AbstractParser.parseTimestamp(delay.getAttribute("stamp")).getTime();
+				} catch (ParseException e) {
+					return defaultValue;
+				}
+			}
 		}
+		return defaultValue;
+	}
+
+	protected long getTimestamp(Element packet) {
+		return getTimestamp(packet,System.currentTimeMillis());
 	}
 
 	public static Date parseTimestamp(String timestamp) throws ParseException {
@@ -46,14 +48,11 @@ public abstract class AbstractParser {
 		return dateFormat.parse(timestamp);
 	}
 
-	protected void updateLastseen(final Element packet, final Account account,
-			final boolean presenceOverwrite) {
-		final Jid from = packet.getAttributeAsJid("from");
-		updateLastseen(packet, account, from, presenceOverwrite);
+	protected void updateLastseen(final Element packet, final Account account, final boolean presenceOverwrite) {
+		updateLastseen(packet, account, packet.getAttributeAsJid("from"), presenceOverwrite);
 	}
 
-	protected void updateLastseen(final Element packet, final Account account, final Jid from,
-								  final boolean presenceOverwrite) {
+	protected void updateLastseen(final Element packet, final Account account, final Jid from, final boolean presenceOverwrite) {
 		final String presence = from == null || from.isBareJid() ? "" : from.getResourcepart();
 		final Contact contact = account.getRoster().getContact(from);
 		final long timestamp = getTimestamp(packet);

src/main/java/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -1,8 +1,12 @@
 package eu.siacs.conversations.parser;
 
+import android.util.Log;
+import android.util.Pair;
+
 import net.java.otr4j.session.Session;
 import net.java.otr4j.session.SessionStatus;
 
+import eu.siacs.conversations.Config;
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
@@ -25,12 +29,12 @@ public class MessageParser extends AbstractParser implements
 		super(service);
 	}
 
-	private boolean extractChatState(Conversation conversation, final Element element) {
-		ChatState state = ChatState.parse(element);
+	private boolean extractChatState(Conversation conversation, final MessagePacket packet) {
+		ChatState state = ChatState.parse(packet);
 		if (state != null && conversation != null) {
 			final Account account = conversation.getAccount();
-			Jid from = element.getAttributeAsJid("from");
-			if (from != null && from.toBareJid().equals(account.getJid().toBareJid())) {
+			Jid from = packet.getFrom();
+			if (from.toBareJid().equals(account.getJid().toBareJid())) {
 				conversation.setOutgoingChatState(state);
 				return false;
 			} else {
@@ -40,86 +44,27 @@ public class MessageParser extends AbstractParser implements
 		return false;
 	}
 
-	private Message parseChat(MessagePacket packet, Account account) {
-		final Jid jid = packet.getFrom();
-		if (jid == null) {
-			return null;
-		}
-		Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid.toBareJid(), false);
-		String pgpBody = getPgpBody(packet);
-		Message finishedMessage;
-		if (pgpBody != null) {
-			finishedMessage = new Message(conversation,
-					pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECEIVED);
-		} else {
-			finishedMessage = new Message(conversation,
-					packet.getBody(), Message.ENCRYPTION_NONE,
-					Message.STATUS_RECEIVED);
-		}
-		finishedMessage.setRemoteMsgId(packet.getId());
-		finishedMessage.markable = isMarkable(packet);
-		if (conversation.getMode() == Conversation.MODE_MULTI
-				&& !jid.isBareJid()) {
-			final Jid trueCounterpart = conversation.getMucOptions()
-					.getTrueCounterpart(jid.getResourcepart());
-			if (trueCounterpart != null) {
-				updateLastseen(packet, account, trueCounterpart, false);
-			}
-			finishedMessage.setType(Message.TYPE_PRIVATE);
-			finishedMessage.setTrueCounterpart(trueCounterpart);
-			if (conversation.hasDuplicateMessage(finishedMessage)) {
-				return null;
-			}
-		} else {
-			updateLastseen(packet, account, true);
-		}
-		finishedMessage.setCounterpart(jid);
-		finishedMessage.setTime(getTimestamp(packet));
-		extractChatState(conversation,packet);
-		return finishedMessage;
-	}
-
-	private Message parseOtrChat(MessagePacket packet, Account account) {
-		final Jid to = packet.getTo();
-		final Jid from = packet.getFrom();
-		if (to == null || from == null) {
-			return null;
-		}
-		boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1;
-		Conversation conversation = mXmppConnectionService
-				.findOrCreateConversation(account, from.toBareJid(), false);
+	private Message parseOtrChat(String body, Jid from, String id, Conversation conversation) {
 		String presence;
 		if (from.isBareJid()) {
 			presence = "";
 		} else {
 			presence = from.getResourcepart();
 		}
-		extractChatState(conversation, packet);
-		updateLastseen(packet, account, true);
-		String body = packet.getBody();
 		if (body.matches("^\\?OTRv\\d{1,2}\\?.*")) {
 			conversation.endOtrIfNeeded();
 		}
 		if (!conversation.hasValidOtrSession()) {
-			if (properlyAddressed) {
-				conversation.startOtrSession(presence,false);
-			} else {
-				return null;
-			}
+			conversation.startOtrSession(presence,false);
 		} else {
-			String foreignPresence = conversation.getOtrSession()
-					.getSessionID().getUserID();
+			String foreignPresence = conversation.getOtrSession().getSessionID().getUserID();
 			if (!foreignPresence.equals(presence)) {
 				conversation.endOtrIfNeeded();
-				if (properlyAddressed) {
-					conversation.startOtrSession(presence, false);
-				} else {
-					return null;
-				}
+				conversation.startOtrSession(presence, false);
 			}
 		}
 		try {
-			conversation.setLastReceivedOtrMessageId(packet.getId());
+			conversation.setLastReceivedOtrMessageId(id);
 			Session otrSession = conversation.getOtrSession();
 			SessionStatus before = otrSession.getSessionStatus();
 			body = otrSession.transformReceiving(body);
@@ -140,12 +85,7 @@ public class MessageParser extends AbstractParser implements
 				conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
 				return null;
 			}
-			Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR,
-					Message.STATUS_RECEIVED);
-			finishedMessage.setTime(getTimestamp(packet));
-			finishedMessage.setRemoteMsgId(packet.getId());
-			finishedMessage.markable = isMarkable(packet);
-			finishedMessage.setCounterpart(from);
+			Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR, Message.STATUS_RECEIVED);
 			conversation.setLastReceivedOtrMessageId(null);
 			return finishedMessage;
 		} catch (Exception e) {
@@ -154,293 +94,6 @@ public class MessageParser extends AbstractParser implements
 		}
 	}
 
-	private Message parseGroupchat(MessagePacket packet, Account account) {
-		int status;
-		final Jid from = packet.getFrom();
-		if (from == null) {
-			return null;
-		}
-		if (mXmppConnectionService.find(account.pendingConferenceLeaves,
-				account, from.toBareJid()) != null) {
-			return null;
-		}
-		Conversation conversation = mXmppConnectionService
-				.findOrCreateConversation(account, from.toBareJid(), true);
-		final Jid trueCounterpart = conversation.getMucOptions().getTrueCounterpart(from.getResourcepart());
-		if (trueCounterpart != null) {
-			updateLastseen(packet, account, trueCounterpart, false);
-		}
-		if (packet.hasChild("subject")) {
-			conversation.setHasMessagesLeftOnServer(true);
-			conversation.getMucOptions().setSubject(packet.findChild("subject").getContent());
-			mXmppConnectionService.updateConversationUi();
-			return null;
-		}
-
-		final Element x = packet.findChild("x", "http://jabber.org/protocol/muc#user");
-		if (from.isBareJid() && (x == null || !x.hasChild("status"))) {
-			return null;
-		} else if (from.isBareJid() && x.hasChild("status")) {
-			for(Element child : x.getChildren()) {
-				if (child.getName().equals("status")) {
-					String code = child.getAttribute("code");
-					if (code.contains(MucOptions.STATUS_CODE_ROOM_CONFIG_CHANGED)) {
-						mXmppConnectionService.fetchConferenceConfiguration(conversation);
-					}
-				}
-			}
-			return null;
-		}
-
-		if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
-			if (mXmppConnectionService.markMessage(conversation,
-					packet.getId(), Message.STATUS_SEND_RECEIVED)) {
-				return null;
-			} else if (packet.getId() == null) {
-				Message message = conversation.findSentMessageWithBody(packet.getBody());
-				if (message != null) {
-					mXmppConnectionService.markMessage(message,Message.STATUS_SEND_RECEIVED);
-					return null;
-				} else {
-					status = Message.STATUS_SEND;
-				}
-			} else {
-				status = Message.STATUS_SEND;
-			}
-		} else {
-			status = Message.STATUS_RECEIVED;
-		}
-		String pgpBody = getPgpBody(packet);
-		Message finishedMessage;
-		if (pgpBody == null) {
-			finishedMessage = new Message(conversation,
-					packet.getBody(), Message.ENCRYPTION_NONE, status);
-		} else {
-			finishedMessage = new Message(conversation, pgpBody,
-					Message.ENCRYPTION_PGP, status);
-		}
-		finishedMessage.setRemoteMsgId(packet.getId());
-		finishedMessage.markable = isMarkable(packet);
-		finishedMessage.setCounterpart(from);
-		if (status == Message.STATUS_RECEIVED) {
-			finishedMessage.setTrueCounterpart(conversation.getMucOptions()
-					.getTrueCounterpart(from.getResourcepart()));
-		}
-		if (packet.hasChild("delay")
-				&& conversation.hasDuplicateMessage(finishedMessage)) {
-			return null;
-		}
-		finishedMessage.setTime(getTimestamp(packet));
-		return finishedMessage;
-	}
-
-	private Message parseCarbonMessage(final MessagePacket packet, final Account account) {
-		int status;
-		final Jid fullJid;
-		Element forwarded;
-		if (packet.hasChild("received", "urn:xmpp:carbons:2")) {
-			forwarded = packet.findChild("received", "urn:xmpp:carbons:2")
-					.findChild("forwarded", "urn:xmpp:forward:0");
-			status = Message.STATUS_RECEIVED;
-		} else if (packet.hasChild("sent", "urn:xmpp:carbons:2")) {
-			forwarded = packet.findChild("sent", "urn:xmpp:carbons:2")
-					.findChild("forwarded", "urn:xmpp:forward:0");
-			status = Message.STATUS_SEND;
-		} else {
-			return null;
-		}
-		if (forwarded == null) {
-			return null;
-		}
-		Element message = forwarded.findChild("message");
-		if (message == null) {
-			return null;
-		}
-		if (!message.hasChild("body")) {
-			if (status == Message.STATUS_RECEIVED
-					&& message.getAttribute("from") != null) {
-				parseNonMessage(message, account);
-			} else if (status == Message.STATUS_SEND
-					&& message.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
-				final Jid to = message.getAttributeAsJid("to");
-				if (to != null) {
-					final Conversation conversation = mXmppConnectionService.find(
-							mXmppConnectionService.getConversations(), account,
-							to.toBareJid());
-					if (conversation != null) {
-						mXmppConnectionService.markRead(conversation);
-					}
-				}
-			}
-			return null;
-		}
-		if (status == Message.STATUS_RECEIVED) {
-			fullJid = message.getAttributeAsJid("from");
-			if (fullJid == null) {
-				return null;
-			} else {
-				updateLastseen(message, account, true);
-			}
-		} else {
-			fullJid = message.getAttributeAsJid("to");
-			if (fullJid == null) {
-				return null;
-			}
-		}
-		if (message.hasChild("x","http://jabber.org/protocol/muc#user")
-				&& "chat".equals(message.getAttribute("type"))) {
-			return null;
-		}
-		Conversation conversation = mXmppConnectionService
-				.findOrCreateConversation(account, fullJid.toBareJid(), false);
-		String pgpBody = getPgpBody(message);
-		Message finishedMessage;
-		if (pgpBody != null) {
-			finishedMessage = new Message(conversation, pgpBody,
-					Message.ENCRYPTION_PGP, status);
-		} else {
-			String body = message.findChild("body").getContent();
-			finishedMessage = new Message(conversation, body,
-					Message.ENCRYPTION_NONE, status);
-		}
-		extractChatState(conversation,message);
-		finishedMessage.setTime(getTimestamp(message));
-		finishedMessage.setRemoteMsgId(message.getAttribute("id"));
-		finishedMessage.markable = isMarkable(message);
-		finishedMessage.setCounterpart(fullJid);
-		if (conversation.getMode() == Conversation.MODE_MULTI
-				&& !fullJid.isBareJid()) {
-			finishedMessage.setType(Message.TYPE_PRIVATE);
-			finishedMessage.setTrueCounterpart(conversation.getMucOptions()
-					.getTrueCounterpart(fullJid.getResourcepart()));
-			if (conversation.hasDuplicateMessage(finishedMessage)) {
-				return null;
-			}
-		}
-		return finishedMessage;
-	}
-
-	private Message parseMamMessage(MessagePacket packet, final Account account) {
-		final Element result = packet.findChild("result","urn:xmpp:mam:0");
-		if (result == null ) {
-			return null;
-		}
-		final MessageArchiveService.Query query = this.mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid"));
-		if (query!=null) {
-			query.incrementTotalCount();
-		}
-		final Element forwarded = result.findChild("forwarded","urn:xmpp:forward:0");
-		if (forwarded == null) {
-			return null;
-		}
-		final Element message = forwarded.findChild("message");
-		if (message == null) {
-			return null;
-		}
-		final Element body = message.findChild("body");
-		if (body == null || message.hasChild("private","urn:xmpp:carbons:2") || message.hasChild("no-copy","urn:xmpp:hints")) {
-			return null;
-		}
-		int encryption;
-		String content = getPgpBody(message);
-		if (content != null) {
-			encryption = Message.ENCRYPTION_PGP;
-		} else {
-			encryption = Message.ENCRYPTION_NONE;
-			content = body.getContent();
-		}
-		if (content == null) {
-			return null;
-		}
-		final long timestamp = getTimestamp(forwarded);
-		final Jid to = message.getAttributeAsJid("to");
-		final Jid from = message.getAttributeAsJid("from");
-		Jid counterpart;
-		int status;
-		Conversation conversation;
-		if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) {
-			status = Message.STATUS_SEND;
-			conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false,query);
-			counterpart = to;
-		} else if (from !=null && to != null) {
-			status = Message.STATUS_RECEIVED;
-			conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false,query);
-			counterpart = from;
-		} else {
-			return null;
-		}
-		Message finishedMessage = new Message(conversation,content,encryption,status);
-		finishedMessage.setTime(timestamp);
-		finishedMessage.setCounterpart(counterpart);
-		finishedMessage.setRemoteMsgId(message.getAttribute("id"));
-		finishedMessage.setServerMsgId(result.getAttribute("id"));
-		if (conversation.hasDuplicateMessage(finishedMessage)) {
-			return null;
-		}
-		if (query!=null) {
-			query.incrementMessageCount();
-		}
-		return finishedMessage;
-	}
-
-	private void parseError(final MessagePacket packet, final Account account) {
-		final Jid from = packet.getFrom();
-		mXmppConnectionService.markMessage(account, from.toBareJid(),
-				packet.getId(), Message.STATUS_SEND_FAILED);
-	}
-
-	private void parseNonMessage(Element packet, Account account) {
-		final Jid from = packet.getAttributeAsJid("from");
-		if (account.getJid().equals(from)) {
-			return;
-		}
-		if (extractChatState(from == null ? null : mXmppConnectionService.find(account,from), packet)) {
-			mXmppConnectionService.updateConversationUi();
-		}
-		Invite invite = extractInvite(packet);
-		if (invite != null && invite.jid != null) {
-			Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true);
-			if (!conversation.getMucOptions().online()) {
-				conversation.getMucOptions().setPassword(invite.password);
-				mXmppConnectionService.databaseBackend.updateConversation(conversation);
-				mXmppConnectionService.joinMuc(conversation);
-				mXmppConnectionService.updateConversationUi();
-			}
-		}
-		if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
-			Element event = packet.findChild("event",
-					"http://jabber.org/protocol/pubsub#event");
-			parseEvent(event, from, account);
-		} else if (from != null && packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
-			String id = packet
-					.findChild("displayed", "urn:xmpp:chat-markers:0")
-					.getAttribute("id");
-			updateLastseen(packet, account, true);
-			final Message displayedMessage = mXmppConnectionService.markMessage(account, from.toBareJid(), 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();
-			}
-		} else if (from != null
-				&& packet.hasChild("received", "urn:xmpp:chat-markers:0")) {
-			String id = packet.findChild("received", "urn:xmpp:chat-markers:0")
-					.getAttribute("id");
-			updateLastseen(packet, account, false);
-			mXmppConnectionService.markMessage(account, from.toBareJid(),
-					id, Message.STATUS_SEND_RECEIVED);
-		} else if (from != null
-				&& packet.hasChild("received", "urn:xmpp:receipts")) {
-			String id = packet.findChild("received", "urn:xmpp:receipts")
-					.getAttribute("id");
-			updateLastseen(packet, account, false);
-			mXmppConnectionService.markMessage(account, from.toBareJid(),
-					id, Message.STATUS_SEND_RECEIVED);
-		}
-	}
-
 	private class Invite {
 		Jid jid;
 		String password;
@@ -451,7 +104,7 @@ public class MessageParser extends AbstractParser implements
 	}
 
 	private Invite extractInvite(Element message) {
-		Element x = message.findChild("x","http://jabber.org/protocol/muc#user");
+		Element x = message.findChild("x", "http://jabber.org/protocol/muc#user");
 		if (x != null) {
 			Element invite = x.findChild("invite");
 			if (invite != null) {
@@ -523,142 +176,208 @@ public class MessageParser extends AbstractParser implements
 		}
 	}
 
-	private String getPgpBody(Element message) {
-		Element child = message.findChild("x", "jabber:x:encrypted");
-		if (child == null) {
-			return null;
+	@Override
+	public void onMessagePacketReceived(Account account, MessagePacket original) {
+		final MessagePacket packet;
+		Long timestamp = null;
+		final boolean isForwarded;
+		boolean carbon = false; //live carbons or mam-sub
+		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;
+			f = f == null ? original.getForwardedMessagePacket("result", "urn:xmpp:mam:0") : f;
+			packet = f != null ? f.first : original;
+			timestamp = f != null ? f.second : null;
+			isForwarded = f != null;
+			carbon = original.hasChild("received", "urn:xmpp:carbons:2") || original.hasChild("received", "urn:xmpp:carbons:2");
+
+			Element fin = packet.findChild("fin", "urn:xmpp:mam:0");
+			if (fin != null) {
+				mXmppConnectionService.getMessageArchiveService().processFin(fin);
+				return;
+			}
+
 		} else {
-			return child.getContent();
+			packet = original;
+			isForwarded = false;
+		}
+		if (timestamp == null) {
+			timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis());
+		}
+		final String body = packet.getBody();
+		final String encrypted = packet.findChildContent("x", "jabber:x:encrypted");
+		int status;
+		final Jid to = packet.getTo();
+		final Jid from = packet.getFrom();
+		final Jid counterpart;
+		final String id = packet.getId();
+		boolean isTypeGroupChat = packet.getType() == MessagePacket.TYPE_GROUPCHAT;
+		boolean properlyAddressed = !to.isBareJid() || account.countPresences() == 1;
+		if (packet.fromAccount(account)) {
+			status = Message.STATUS_SEND;
+			counterpart = to;
+		} else {
+			status = Message.STATUS_RECEIVED;
+			counterpart = from;
 		}
-	}
 
-	private boolean isMarkable(Element message) {
-		return message.hasChild("markable", "urn:xmpp:chat-markers:0");
-	}
+		if (from == null || to == null) {
+			Log.d(Config.LOGTAG,"no to or from in: "+packet.toString());
+			return;
+		}
 
-	@Override
-	public void onMessagePacketReceived(Account account, MessagePacket packet) {
-		Message message = null;
-		this.parseNick(packet, account);
-		if ((packet.getType() == MessagePacket.TYPE_CHAT || packet.getType() == MessagePacket.TYPE_NORMAL)) {
-			if ((packet.getBody() != null)
-					&& (packet.getBody().startsWith("?OTR"))) {
-				message = this.parseOtrChat(packet, account);
-				if (message != null) {
-					message.markUnread();
-				}
-			} else if (packet.hasChild("body") && extractInvite(packet) == null) {
-				message = this.parseChat(packet, account);
-				if (message != null) {
-					message.markUnread();
-				}
-			} else if (packet.hasChild("received", "urn:xmpp:carbons:2")
-					|| (packet.hasChild("sent", "urn:xmpp:carbons:2"))) {
-				message = this.parseCarbonMessage(packet, account);
-				if (message != null) {
-					if (message.getStatus() == Message.STATUS_SEND) {
-						account.activateGracePeriod();
-						mXmppConnectionService.markRead(message.getConversation());
-					} else {
-						message.markUnread();
+		Invite invite = extractInvite(packet);
+		if (invite != null && invite.jid != null) {
+			Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, invite.jid, true);
+			if (!conversation.getMucOptions().online()) {
+				conversation.getMucOptions().setPassword(invite.password);
+				mXmppConnectionService.databaseBackend.updateConversation(conversation);
+				mXmppConnectionService.joinMuc(conversation);
+				mXmppConnectionService.updateConversationUi();
+			}
+			return;
+		}
+
+		if (extractChatState(mXmppConnectionService.find(account,from), packet)) {
+			mXmppConnectionService.updateConversationUi();
+		}
+
+		if (body != null || encrypted != null) {
+			Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat);
+			if (isTypeGroupChat) {
+				if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
+					status = Message.STATUS_SEND;
+					if (mXmppConnectionService.markMessage(conversation, id, Message.STATUS_SEND_RECEIVED)) {
+						return;
+					} else if (id == null) {
+						Message message = conversation.findSentMessageWithBody(packet.getBody());
+						if (message != null) {
+							mXmppConnectionService.markMessage(message, Message.STATUS_SEND_RECEIVED);
+							return;
+						}
 					}
+				} else {
+					status = Message.STATUS_RECEIVED;
 				}
-			} else if (packet.hasChild("result","urn:xmpp:mam:0")) {
-				message = parseMamMessage(packet, account);
-				if (message != null) {
-					Conversation conversation = message.getConversation();
-					conversation.add(message);
-					mXmppConnectionService.databaseBackend.createMessage(message);
+			}
+			Message message;
+			if (body != null && body.startsWith("?OTR")) {
+				if (!isForwarded && !isTypeGroupChat && properlyAddressed) {
+					message = parseOtrChat(body, from, id, conversation);
+					if (message == null) {
+						return;
+					}
+				} else {
+					message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
 				}
-				return;
-			} else if (packet.hasChild("fin","urn:xmpp:mam:0")) {
-				Element fin = packet.findChild("fin","urn:xmpp:mam:0");
-				mXmppConnectionService.getMessageArchiveService().processFin(fin);
+			} else if (encrypted != null) {
+				message = new Message(conversation, encrypted, Message.ENCRYPTION_PGP, status);
 			} else {
-				parseNonMessage(packet, account);
+				message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
 			}
-		} else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
-			message = this.parseGroupchat(packet, account);
-			if (message != null) {
-				if (message.getStatus() == Message.STATUS_RECEIVED) {
-					message.markUnread();
-				} else {
-					mXmppConnectionService.markRead(message.getConversation());
-					account.activateGracePeriod();
+			message.setCounterpart(counterpart);
+			message.setRemoteMsgId(id);
+			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()));
+				if (!isTypeGroupChat) {
+					message.setType(Message.TYPE_PRIVATE);
 				}
 			}
-		} else if (packet.getType() == MessagePacket.TYPE_ERROR) {
-			this.parseError(packet, account);
-			return;
-		} else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
-			this.parseHeadline(packet, account);
-			return;
-		}
-		if ((message == null) || (message.getBody() == null)) {
-			return;
-		}
-		if ((mXmppConnectionService.confirmMessages())
-				&& ((packet.getId() != null))) {
-			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);
+			updateLastseen(packet,account,true);
+			conversation.add(message);
+			if (carbon || status == Message.STATUS_RECEIVED) {
+				mXmppConnectionService.markRead(conversation);
+				account.activateGracePeriod();
+			} else if (!isForwarded) {
+				message.markUnread();
 			}
-			if (packet.hasChild("request", "urn:xmpp:receipts")) {
-				MessagePacket receipt = mXmppConnectionService
-						.getMessageGenerator().received(account, packet,
-								"urn:xmpp:receipts");
-				mXmppConnectionService.sendMessagePacket(account, receipt);
+
+
+			if (mXmppConnectionService.confirmMessages() && id != null && !packet.fromAccount(account)) {
+				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);
+				}
 			}
-		}
-		Conversation conversation = message.getConversation();
-		conversation.add(message);
-		if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) {
-			if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
-				mXmppConnectionService.updateConversation(conversation);
+			if (account.getXmppConnection() != null && account.getXmppConnection().getFeatures().advancedStreamFeaturesLoaded()) {
+				if (conversation.setLastMessageTransmitted(System.currentTimeMillis())) {
+					mXmppConnectionService.updateConversation(conversation);
+				}
 			}
-		}
 
-		if (message.getStatus() == Message.STATUS_RECEIVED
-				&& conversation.getOtrSession() != null
-				&& !conversation.getOtrSession().getSessionID().getUserID()
-				.equals(message.getCounterpart().getResourcepart())) {
-			conversation.endOtrIfNeeded();
-		}
+			if (message.getStatus() == Message.STATUS_RECEIVED
+					&& conversation.getOtrSession() != null
+					&& !conversation.getOtrSession().getSessionID().getUserID()
+					.equals(message.getCounterpart().getResourcepart())) {
+				conversation.endOtrIfNeeded();
+			}
 
-		if (packet.getType() != MessagePacket.TYPE_ERROR) {
 			if (message.getEncryption() == Message.ENCRYPTION_NONE
 					|| mXmppConnectionService.saveEncryptedMessages()) {
 				mXmppConnectionService.databaseBackend.createMessage(message);
 			}
+			final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
+			if (message.trusted() && message.bodyContainsDownloadable() && manager.getAutoAcceptFileSize() > 0) {
+				manager.createNewConnection(message);
+			} else if (!message.isRead()) {
+				mXmppConnectionService.getNotificationService().push(message);
+			}
+			mXmppConnectionService.updateConversationUi();
+		} else {
+			if (packet.hasChild("subject") && isTypeGroupChat) {
+				Conversation conversation = mXmppConnectionService.find(account, from.toBareJid());
+				if (conversation != null && conversation.getMode() == Conversation.MODE_MULTI) {
+					conversation.setHasMessagesLeftOnServer(true);
+					conversation.getMucOptions().setSubject(packet.findChildContent("subject"));
+					mXmppConnectionService.updateConversationUi();
+					return;
+				}
+			}
 		}
-		final HttpConnectionManager manager = this.mXmppConnectionService.getHttpConnectionManager();
-		if (message.trusted() && message.bodyContainsDownloadable() && manager.getAutoAcceptFileSize() > 0) {
-			manager.createNewConnection(message);
-		} else if (!message.isRead()) {
-			mXmppConnectionService.getNotificationService().push(message);
+
+		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());
+				mXmppConnectionService.markRead(conversation);
+			} else {
+				updateLastseen(packet, account, 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()) {
+					mXmppConnectionService.markMessage(message, Message.STATUS_SEND_DISPLAYED);
+					message = message.prev();
+				}
+			}
 		}
-		mXmppConnectionService.updateConversationUi();
-	}
 
-	private void parseHeadline(MessagePacket packet, Account account) {
-		if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
-			Element event = packet.findChild("event",
-					"http://jabber.org/protocol/pubsub#event");
-			parseEvent(event, packet.getFrom(), account);
+		Element event = packet.findChild("event", "http://jabber.org/protocol/pubsub#event");
+		if (event != null) {
+			parseEvent(event, from, account);
 		}
-	}
 
-	private void parseNick(MessagePacket packet, Account account) {
-		Element nick = packet.findChild("nick",
-				"http://jabber.org/protocol/nick");
+		String nick = packet.findChildContent("nick", "http://jabber.org/protocol/nick");
 		if (nick != null) {
-			if (packet.getFrom() != null) {
-				Contact contact = account.getRoster().getContact(
-						packet.getFrom());
-				contact.setPresenceName(nick.getContent());
-			}
+			Contact contact = account.getRoster().getContact(from);
+			contact.setPresenceName(nick);
 		}
 	}
-}
+}

src/main/java/eu/siacs/conversations/xmpp/stanzas/AbstractStanza.java 🔗

@@ -51,4 +51,8 @@ public class AbstractStanza extends Element {
 			|| getTo().equals(account.getJid().toBareJid())
 			|| getTo().equals(account.getJid());
 	}
+
+	public boolean fromAccount(final Account account) {
+		return getFrom() != null && getFrom().toBareJid().equals(account.getJid().toBareJid());
+	}
 }

src/main/java/eu/siacs/conversations/xmpp/stanzas/MessagePacket.java 🔗

@@ -1,5 +1,10 @@
 package eu.siacs.conversations.xmpp.stanzas;
 
+import android.util.Pair;
+
+import java.text.ParseException;
+
+import eu.siacs.conversations.parser.AbstractParser;
 import eu.siacs.conversations.xml.Element;
 
 public class MessagePacket extends AbstractStanza {
@@ -14,12 +19,7 @@ public class MessagePacket extends AbstractStanza {
 	}
 
 	public String getBody() {
-		Element body = this.findChild("body");
-		if (body != null) {
-			return body.getContent();
-		} else {
-			return null;
-		}
+		return findChildContent("body");
 	}
 
 	public void setBody(String text) {
@@ -67,19 +67,23 @@ public class MessagePacket extends AbstractStanza {
 		}
 	}
 
-	public MessagePacket getForwardedMessagePacket(String name, String namespace) {
+	public Pair<MessagePacket,Long> getForwardedMessagePacket(String name, String namespace) {
 		Element wrapper = findChild(name, namespace);
 		if (wrapper == null) {
 			return null;
 		}
-		Element forwarded = wrapper.findChild("forwarded","urn:xmpp:forward:0");
+		Element forwarded = wrapper.findChild("forwarded", "urn:xmpp:forward:0");
 		if (forwarded == null) {
 			return null;
 		}
-		return MessagePacket.create(forwarded.findChild("message"));
+		MessagePacket packet = create(forwarded.findChild("message"));
+		if (packet == null) {
+			return null;
+		}
+		Long timestamp = AbstractParser.getTimestamp(forwarded,null);
+		return new Pair(packet,timestamp);
 	}
 
-
 	public static MessagePacket create(Element element) {
 		if (element == null) {
 			return null;