MessageParser.java

  1package eu.siacs.conversations.parser;
  2
  3import android.os.SystemClock;
  4import net.java.otr4j.session.Session;
  5import net.java.otr4j.session.SessionStatus;
  6import eu.siacs.conversations.entities.Account;
  7import eu.siacs.conversations.entities.Conversation;
  8import eu.siacs.conversations.entities.Message;
  9import eu.siacs.conversations.services.XmppConnectionService;
 10import eu.siacs.conversations.utils.CryptoHelper;
 11import eu.siacs.conversations.xml.Element;
 12import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
 13import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
 14
 15public class MessageParser extends AbstractParser implements
 16		OnMessagePacketReceived {
 17
 18	private long lastCarbonMessageReceived = -XmppConnectionService.CARBON_GRACE_PERIOD;
 19
 20	public MessageParser(XmppConnectionService service) {
 21		super(service);
 22	}
 23
 24	private Message parseChat(MessagePacket packet, Account account) {
 25		String[] fromParts = packet.getFrom().split("/");
 26		Conversation conversation = mXmppConnectionService
 27				.findOrCreateConversation(account, fromParts[0], false);
 28		conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
 29		updateLastseen(packet, account, true);
 30		String pgpBody = getPgpBody(packet);
 31		Message finishedMessage;
 32		if (pgpBody != null) {
 33			finishedMessage = new Message(conversation, packet.getFrom(),
 34					pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECIEVED);
 35		} else {
 36			finishedMessage = new Message(conversation, packet.getFrom(),
 37					packet.getBody(), Message.ENCRYPTION_NONE,
 38					Message.STATUS_RECIEVED);
 39		}
 40		finishedMessage.setTime(getTimestamp(packet));
 41		return finishedMessage;
 42	}
 43
 44	private Message parseOtrChat(MessagePacket packet, Account account) {
 45		boolean properlyAddressed = (packet.getTo().split("/").length == 2)
 46				|| (account.countPresences() == 1);
 47		String[] fromParts = packet.getFrom().split("/");
 48		Conversation conversation = mXmppConnectionService
 49				.findOrCreateConversation(account, fromParts[0], false);
 50		updateLastseen(packet, account, true);
 51		String body = packet.getBody();
 52		if (!conversation.hasValidOtrSession()) {
 53			if (properlyAddressed) {
 54				conversation.startOtrSession(
 55						mXmppConnectionService.getApplicationContext(),
 56						fromParts[1], false);
 57			} else {
 58				return null;
 59			}
 60		} else {
 61			String foreignPresence = conversation.getOtrSession()
 62					.getSessionID().getUserID();
 63			if (!foreignPresence.equals(fromParts[1])) {
 64				conversation.endOtrIfNeeded();
 65				if (properlyAddressed) {
 66					conversation.startOtrSession(
 67							mXmppConnectionService.getApplicationContext(),
 68							fromParts[1], false);
 69				} else {
 70					return null;
 71				}
 72			}
 73		}
 74		try {
 75			Session otrSession = conversation.getOtrSession();
 76			SessionStatus before = otrSession.getSessionStatus();
 77			body = otrSession.transformReceiving(body);
 78			SessionStatus after = otrSession.getSessionStatus();
 79			if ((before != after) && (after == SessionStatus.ENCRYPTED)) {
 80				mXmppConnectionService.onOtrSessionEstablished(conversation);
 81			} else if ((before != after) && (after == SessionStatus.FINISHED)) {
 82				conversation.resetOtrSession();
 83			}
 84			if ((body == null) || (body.isEmpty())) {
 85				return null;
 86			}
 87			if (body.startsWith(CryptoHelper.FILETRANSFER)) {
 88				String key = body.substring(CryptoHelper.FILETRANSFER.length());
 89				conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
 90				return null;
 91			}
 92			conversation
 93					.setLatestMarkableMessageId(getMarkableMessageId(packet));
 94			Message finishedMessage = new Message(conversation,
 95					packet.getFrom(), body, Message.ENCRYPTION_OTR,
 96					Message.STATUS_RECIEVED);
 97			finishedMessage.setTime(getTimestamp(packet));
 98			return finishedMessage;
 99		} catch (Exception e) {
100			String receivedId = packet.getId();
101			if (receivedId != null) {
102				mXmppConnectionService.replyWithNotAcceptable(account, packet);
103			}
104			conversation.endOtrIfNeeded();
105			return null;
106		}
107	}
108
109	private Message parseGroupchat(MessagePacket packet, Account account) {
110		int status;
111		String[] fromParts = packet.getFrom().split("/");
112		if (mXmppConnectionService.find(account.pendingConferenceLeaves,account,fromParts[0]) != null) {
113			return null;
114		}
115		Conversation conversation = mXmppConnectionService
116				.findOrCreateConversation(account, fromParts[0], true);
117		if (packet.hasChild("subject")) {
118			conversation.getMucOptions().setSubject(
119					packet.findChild("subject").getContent());
120			mXmppConnectionService.updateConversationUi();
121			return null;
122		}
123		if ((fromParts.length == 1)) {
124			return null;
125		}
126		String counterPart = fromParts[1];
127		if (counterPart.equals(conversation.getMucOptions().getActualNick())) {
128			if (mXmppConnectionService.markMessage(conversation,
129					packet.getId(), Message.STATUS_SEND)) {
130				return null;
131			} else {
132				status = Message.STATUS_SEND;
133			}
134		} else {
135			status = Message.STATUS_RECIEVED;
136		}
137		String pgpBody = getPgpBody(packet);
138		conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
139		Message finishedMessage;
140		if (pgpBody == null) {
141			finishedMessage = new Message(conversation, counterPart,
142					packet.getBody(), Message.ENCRYPTION_NONE, status);
143		} else {
144			finishedMessage = new Message(conversation, counterPart, pgpBody,
145					Message.ENCRYPTION_PGP, status);
146		}
147		finishedMessage.setTime(getTimestamp(packet));
148		return finishedMessage;
149	}
150
151	private Message parseCarbonMessage(MessagePacket packet, Account account) {
152		int status;
153		String fullJid;
154		Element forwarded;
155		if (packet.hasChild("received")) {
156			forwarded = packet.findChild("received").findChild("forwarded");
157			status = Message.STATUS_RECIEVED;
158		} else if (packet.hasChild("sent")) {
159			forwarded = packet.findChild("sent").findChild("forwarded");
160			status = Message.STATUS_SEND;
161		} else {
162			return null;
163		}
164		if (forwarded == null) {
165			return null;
166		}
167		Element message = forwarded.findChild("message");
168		if ((message == null) || (!message.hasChild("body"))) {
169			if (status == Message.STATUS_RECIEVED) {
170				parseNormal(message, account);
171			}
172			return null;
173		}
174		if (status == Message.STATUS_RECIEVED) {
175			fullJid = message.getAttribute("from");
176			updateLastseen(message, account, true);
177		} else {
178			fullJid = message.getAttribute("to");
179		}
180		if (fullJid==null) {
181			return null;
182		}
183		String[] parts = fullJid.split("/");
184		Conversation conversation = mXmppConnectionService
185				.findOrCreateConversation(account, parts[0], false);
186		conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
187		String pgpBody = getPgpBody(message);
188		Message finishedMessage;
189		if (pgpBody != null) {
190			finishedMessage = new Message(conversation, fullJid, pgpBody,
191					Message.ENCRYPTION_PGP, status);
192		} else {
193			String body = message.findChild("body").getContent();
194			finishedMessage = new Message(conversation, fullJid, body,
195					Message.ENCRYPTION_NONE, status);
196		}
197		finishedMessage.setTime(getTimestamp(message));
198		return finishedMessage;
199	}
200
201	private void parseError(MessagePacket packet, Account account) {
202		String[] fromParts = packet.getFrom().split("/");
203		mXmppConnectionService.markMessage(account, fromParts[0],
204				packet.getId(), Message.STATUS_SEND_FAILED);
205	}
206
207	private void parseNormal(Element packet, Account account) {
208		if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
209			String id = packet
210					.findChild("displayed", "urn:xmpp:chat-markers:0")
211					.getAttribute("id");
212			String[] fromParts = packet.getAttribute("from").split("/");
213			updateLastseen(packet, account, true);
214			mXmppConnectionService.markMessage(account, fromParts[0], id,
215					Message.STATUS_SEND_DISPLAYED);
216		} else if (packet.hasChild("received", "urn:xmpp:chat-markers:0")) {
217			String id = packet.findChild("received", "urn:xmpp:chat-markers:0")
218					.getAttribute("id");
219			String[] fromParts = packet.getAttribute("from").split("/");
220			updateLastseen(packet, account, false);
221			mXmppConnectionService.markMessage(account, fromParts[0], id,
222					Message.STATUS_SEND_RECEIVED);
223		} else if (packet.hasChild("x")) {
224			Element x = packet.findChild("x");
225			if (x.hasChild("invite")) {
226				Conversation conversation = mXmppConnectionService
227						.findOrCreateConversation(account,
228								packet.getAttribute("from"), true);
229				if (!conversation.getMucOptions().online()) {
230					mXmppConnectionService.joinMuc(conversation);
231					mXmppConnectionService.updateConversationUi();
232				}	
233			}
234
235		}
236	}
237
238	private String getPgpBody(Element message) {
239		Element child = message.findChild("x", "jabber:x:encrypted");
240		if (child == null) {
241			return null;
242		} else {
243			return child.getContent();
244		}
245	}
246
247	private String getMarkableMessageId(Element message) {
248		if (message.hasChild("markable", "urn:xmpp:chat-markers:0")) {
249			return message.getAttribute("id");
250		} else {
251			return null;
252		}
253	}
254
255	@Override
256	public void onMessagePacketReceived(Account account, MessagePacket packet) {
257		Message message = null;
258		boolean notify = true;
259		if (mXmppConnectionService.getPreferences().getBoolean(
260				"notification_grace_period_after_carbon_received", true)) {
261			notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > XmppConnectionService.CARBON_GRACE_PERIOD;
262		}
263
264		if ((packet.getType() == MessagePacket.TYPE_CHAT)) {
265			if ((packet.getBody() != null)
266					&& (packet.getBody().startsWith("?OTR"))) {
267				message = this.parseOtrChat(packet, account);
268				if (message != null) {
269					message.markUnread();
270				}
271			} else if (packet.hasChild("body")) {
272				message = this.parseChat(packet, account);
273				message.markUnread();
274			} else if (packet.hasChild("received") || (packet.hasChild("sent"))) {
275				message = this.parseCarbonMessage(packet, account);
276				if (message != null) {
277					if (message.getStatus() == Message.STATUS_SEND) {
278						lastCarbonMessageReceived = SystemClock
279								.elapsedRealtime();
280						notify = false;
281						message.getConversation().markRead();
282					} else {
283						message.markUnread();
284					}
285				}
286			}
287
288		} else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
289			message = this.parseGroupchat(packet, account);
290			if (message != null) {
291				if (message.getStatus() == Message.STATUS_RECIEVED) {
292					message.markUnread();
293				} else {
294					message.getConversation().markRead();
295					lastCarbonMessageReceived = SystemClock
296							.elapsedRealtime();
297					notify = false;
298				}
299			}
300		} else if (packet.getType() == MessagePacket.TYPE_ERROR) {
301			this.parseError(packet, account);
302			return;
303		} else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
304			this.parseNormal(packet, account);
305		}
306		if ((message == null) || (message.getBody() == null)) {
307			return;
308		}
309		if ((mXmppConnectionService.confirmMessages())
310				&& ((packet.getId() != null))) {
311			MessagePacket receivedPacket = new MessagePacket();
312			receivedPacket.setType(MessagePacket.TYPE_NORMAL);
313			receivedPacket.setTo(message.getCounterpart());
314			receivedPacket.setFrom(account.getFullJid());
315			if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
316				Element received = receivedPacket.addChild("received",
317						"urn:xmpp:chat-markers:0");
318				received.setAttribute("id", packet.getId());
319				account.getXmppConnection().sendMessagePacket(receivedPacket);
320			} else if (packet.hasChild("request", "urn:xmpp:receipts")) {
321				Element received = receivedPacket.addChild("received",
322						"urn:xmpp:receipts");
323				received.setAttribute("id", packet.getId());
324				account.getXmppConnection().sendMessagePacket(receivedPacket);
325			}
326		}
327		Conversation conversation = message.getConversation();
328		conversation.getMessages().add(message);
329		if (packet.getType() != MessagePacket.TYPE_ERROR) {
330			mXmppConnectionService.databaseBackend.createMessage(message);
331		}
332		mXmppConnectionService.notifyUi(conversation, notify);
333	}
334}