MessageParser.java

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