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