1package eu.siacs.conversations.parser;
2
3import android.util.Log;
4
5import net.java.otr4j.session.Session;
6import net.java.otr4j.session.SessionStatus;
7
8import eu.siacs.conversations.Config;
9import eu.siacs.conversations.entities.Account;
10import eu.siacs.conversations.entities.Contact;
11import eu.siacs.conversations.entities.Conversation;
12import eu.siacs.conversations.entities.Message;
13import eu.siacs.conversations.services.XmppConnectionService;
14import eu.siacs.conversations.utils.CryptoHelper;
15import eu.siacs.conversations.xml.Element;
16import eu.siacs.conversations.xmpp.OnMessagePacketReceived;
17import eu.siacs.conversations.xmpp.jid.Jid;
18import eu.siacs.conversations.xmpp.pep.Avatar;
19import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
20
21public class MessageParser extends AbstractParser implements
22 OnMessagePacketReceived {
23 public MessageParser(XmppConnectionService service) {
24 super(service);
25 }
26
27 private Message parseChat(MessagePacket packet, Account account) {
28 final Jid jid = packet.getFrom();
29 if (jid == null) {
30 return null;
31 }
32 Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, jid.toBareJid(), false);
33 updateLastseen(packet, account, true);
34 String pgpBody = getPgpBody(packet);
35 Message finishedMessage;
36 if (pgpBody != null) {
37 finishedMessage = new Message(conversation,
38 pgpBody, Message.ENCRYPTION_PGP, Message.STATUS_RECEIVED);
39 } else {
40 finishedMessage = new Message(conversation,
41 packet.getBody(), Message.ENCRYPTION_NONE,
42 Message.STATUS_RECEIVED);
43 }
44 finishedMessage.setRemoteMsgId(packet.getId());
45 finishedMessage.markable = isMarkable(packet);
46 if (conversation.getMode() == Conversation.MODE_MULTI
47 && !jid.isBareJid()) {
48 finishedMessage.setType(Message.TYPE_PRIVATE);
49 finishedMessage.setTrueCounterpart(conversation.getMucOptions()
50 .getTrueCounterpart(jid.getResourcepart()));
51 if (conversation.hasDuplicateMessage(finishedMessage)) {
52 return null;
53 }
54
55 }
56 finishedMessage.setCounterpart(jid);
57 finishedMessage.setTime(getTimestamp(packet));
58 return finishedMessage;
59 }
60
61 private Message parseOtrChat(MessagePacket packet, Account account) {
62 boolean properlyAddressed = (!packet.getTo().isBareJid())
63 || (account.countPresences() == 1);
64 final Jid from = packet.getFrom();
65 if (from == null) {
66 return null;
67 }
68 Conversation conversation = mXmppConnectionService
69 .findOrCreateConversation(account, from.toBareJid(), false);
70 String presence;
71 if (from.isBareJid()) {
72 presence = "";
73 } else {
74 presence = from.getResourcepart();
75 }
76 updateLastseen(packet, account, true);
77 String body = packet.getBody();
78 if (body.matches("^\\?OTRv\\d*\\?")) {
79 conversation.endOtrIfNeeded();
80 }
81 if (!conversation.hasValidOtrSession()) {
82 if (properlyAddressed) {
83 conversation.startOtrSession(presence,false);
84 } else {
85 return null;
86 }
87 } else {
88 String foreignPresence = conversation.getOtrSession()
89 .getSessionID().getUserID();
90 if (!foreignPresence.equals(presence)) {
91 conversation.endOtrIfNeeded();
92 if (properlyAddressed) {
93 conversation.startOtrSession(presence, false);
94 } else {
95 return null;
96 }
97 }
98 }
99 try {
100 Session otrSession = conversation.getOtrSession();
101 SessionStatus before = otrSession.getSessionStatus();
102 body = otrSession.transformReceiving(body);
103 SessionStatus after = otrSession.getSessionStatus();
104 if ((before != after) && (after == SessionStatus.ENCRYPTED)) {
105 mXmppConnectionService.onOtrSessionEstablished(conversation);
106 } else if ((before != after) && (after == SessionStatus.FINISHED)) {
107 conversation.resetOtrSession();
108 mXmppConnectionService.updateConversationUi();
109 }
110 if ((body == null) || (body.isEmpty())) {
111 return null;
112 }
113 if (body.startsWith(CryptoHelper.FILETRANSFER)) {
114 String key = body.substring(CryptoHelper.FILETRANSFER.length());
115 conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
116 return null;
117 }
118 Message finishedMessage = new Message(conversation, body, Message.ENCRYPTION_OTR,
119 Message.STATUS_RECEIVED);
120 finishedMessage.setTime(getTimestamp(packet));
121 finishedMessage.setRemoteMsgId(packet.getId());
122 finishedMessage.markable = isMarkable(packet);
123 finishedMessage.setCounterpart(from);
124 return finishedMessage;
125 } catch (Exception e) {
126 conversation.resetOtrSession();
127 return null;
128 }
129 }
130
131 private Message parseGroupchat(MessagePacket packet, Account account) {
132 int status;
133 final Jid from = packet.getFrom();
134 if (from == null) {
135 return null;
136 }
137 if (mXmppConnectionService.find(account.pendingConferenceLeaves,
138 account, from.toBareJid()) != null) {
139 return null;
140 }
141 Conversation conversation = mXmppConnectionService
142 .findOrCreateConversation(account, from.toBareJid(), true);
143 if (packet.hasChild("subject")) {
144 conversation.getMucOptions().setSubject(
145 packet.findChild("subject").getContent());
146 mXmppConnectionService.updateConversationUi();
147 return null;
148 }
149 if (from.isBareJid()) {
150 return null;
151 }
152 if (from.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
153 if (mXmppConnectionService.markMessage(conversation,
154 packet.getId(), Message.STATUS_SEND)) {
155 return null;
156 } else if (packet.getId() == null) {
157 Message message = conversation.findSentMessageWithBody(packet.getBody());
158 if (message != null) {
159 mXmppConnectionService.markMessage(message,Message.STATUS_SEND_RECEIVED);
160 return null;
161 } else {
162 status = Message.STATUS_SEND;
163 }
164 } else {
165 status = Message.STATUS_SEND;
166 }
167 } else {
168 status = Message.STATUS_RECEIVED;
169 }
170 String pgpBody = getPgpBody(packet);
171 Message finishedMessage;
172 if (pgpBody == null) {
173 finishedMessage = new Message(conversation,
174 packet.getBody(), Message.ENCRYPTION_NONE, status);
175 } else {
176 finishedMessage = new Message(conversation, pgpBody,
177 Message.ENCRYPTION_PGP, status);
178 }
179 finishedMessage.setRemoteMsgId(packet.getId());
180 finishedMessage.markable = isMarkable(packet);
181 finishedMessage.setCounterpart(from);
182 if (status == Message.STATUS_RECEIVED) {
183 finishedMessage.setTrueCounterpart(conversation.getMucOptions()
184 .getTrueCounterpart(from.getResourcepart()));
185 }
186 if (packet.hasChild("delay")
187 && conversation.hasDuplicateMessage(finishedMessage)) {
188 return null;
189 }
190 finishedMessage.setTime(getTimestamp(packet));
191 return finishedMessage;
192 }
193
194 private Message parseCarbonMessage(final MessagePacket packet, final Account account) {
195 int status;
196 final Jid fullJid;
197 Element forwarded;
198 if (packet.hasChild("received", "urn:xmpp:carbons:2")) {
199 forwarded = packet.findChild("received", "urn:xmpp:carbons:2")
200 .findChild("forwarded", "urn:xmpp:forward:0");
201 status = Message.STATUS_RECEIVED;
202 } else if (packet.hasChild("sent", "urn:xmpp:carbons:2")) {
203 forwarded = packet.findChild("sent", "urn:xmpp:carbons:2")
204 .findChild("forwarded", "urn:xmpp:forward:0");
205 status = Message.STATUS_SEND;
206 } else {
207 return null;
208 }
209 if (forwarded == null) {
210 return null;
211 }
212 Element message = forwarded.findChild("message");
213 if (message == null) {
214 return null;
215 }
216 if (!message.hasChild("body")) {
217 if (status == Message.STATUS_RECEIVED
218 && message.getAttribute("from") != null) {
219 parseNonMessage(message, account);
220 } else if (status == Message.STATUS_SEND
221 && message.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
222 final Jid to = message.getAttributeAsJid("to");
223 if (to != null) {
224 final Conversation conversation = mXmppConnectionService.find(
225 mXmppConnectionService.getConversations(), account,
226 to.toBareJid());
227 if (conversation != null) {
228 mXmppConnectionService.markRead(conversation, false);
229 }
230 }
231 }
232 return null;
233 }
234 if (status == Message.STATUS_RECEIVED) {
235 fullJid = message.getAttributeAsJid("from");
236 if (fullJid == null) {
237 return null;
238 } else {
239 updateLastseen(message, account, true);
240 }
241 } else {
242 fullJid = message.getAttributeAsJid("to");
243 if (fullJid == null) {
244 return null;
245 }
246 }
247 Conversation conversation = mXmppConnectionService
248 .findOrCreateConversation(account, fullJid.toBareJid(), false);
249 String pgpBody = getPgpBody(message);
250 Message finishedMessage;
251 if (pgpBody != null) {
252 finishedMessage = new Message(conversation, pgpBody,
253 Message.ENCRYPTION_PGP, status);
254 } else {
255 String body = message.findChild("body").getContent();
256 finishedMessage = new Message(conversation, body,
257 Message.ENCRYPTION_NONE, status);
258 }
259 finishedMessage.setTime(getTimestamp(message));
260 finishedMessage.setRemoteMsgId(message.getAttribute("id"));
261 finishedMessage.markable = isMarkable(message);
262 finishedMessage.setCounterpart(fullJid);
263 if (conversation.getMode() == Conversation.MODE_MULTI
264 && !fullJid.isBareJid()) {
265 finishedMessage.setType(Message.TYPE_PRIVATE);
266 finishedMessage.setTrueCounterpart(conversation.getMucOptions()
267 .getTrueCounterpart(fullJid.getResourcepart()));
268 if (conversation.hasDuplicateMessage(finishedMessage)) {
269 return null;
270 }
271 }
272 return finishedMessage;
273 }
274
275 private void parseError(final MessagePacket packet, final Account account) {
276 final Jid from = packet.getFrom();
277 mXmppConnectionService.markMessage(account, from.toBareJid(),
278 packet.getId(), Message.STATUS_SEND_FAILED);
279 }
280
281 private void parseNonMessage(Element packet, Account account) {
282 final Jid from = packet.getAttributeAsJid("from");
283 if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
284 Element event = packet.findChild("event",
285 "http://jabber.org/protocol/pubsub#event");
286 parseEvent(event, from, account);
287 } else if (from != null
288 && packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
289 String id = packet
290 .findChild("displayed", "urn:xmpp:chat-markers:0")
291 .getAttribute("id");
292 updateLastseen(packet, account, true);
293 mXmppConnectionService.markMessage(account, from.toBareJid(),
294 id, Message.STATUS_SEND_DISPLAYED);
295 } else if (from != null
296 && packet.hasChild("received", "urn:xmpp:chat-markers:0")) {
297 String id = packet.findChild("received", "urn:xmpp:chat-markers:0")
298 .getAttribute("id");
299 updateLastseen(packet, account, false);
300 mXmppConnectionService.markMessage(account, from.toBareJid(),
301 id, Message.STATUS_SEND_RECEIVED);
302 } else if (from != null
303 && packet.hasChild("received", "urn:xmpp:receipts")) {
304 String id = packet.findChild("received", "urn:xmpp:receipts")
305 .getAttribute("id");
306 updateLastseen(packet, account, false);
307 mXmppConnectionService.markMessage(account, from.toBareJid(),
308 id, Message.STATUS_SEND_RECEIVED);
309 } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
310 Element x = packet.findChild("x",
311 "http://jabber.org/protocol/muc#user");
312 if (x.hasChild("invite")) {
313 Conversation conversation = mXmppConnectionService
314 .findOrCreateConversation(account,
315 packet.getAttributeAsJid("from"), true);
316 if (!conversation.getMucOptions().online()) {
317 if (x.hasChild("password")) {
318 Element password = x.findChild("password");
319 conversation.getMucOptions().setPassword(
320 password.getContent());
321 mXmppConnectionService.databaseBackend
322 .updateConversation(conversation);
323 }
324 mXmppConnectionService.joinMuc(conversation);
325 mXmppConnectionService.updateConversationUi();
326 }
327 }
328 } else if (packet.hasChild("x", "jabber:x:conference")) {
329 Element x = packet.findChild("x", "jabber:x:conference");
330 Jid jid = x.getAttributeAsJid("jid");
331 String password = x.getAttribute("password");
332 if (jid != null) {
333 Conversation conversation = mXmppConnectionService
334 .findOrCreateConversation(account, jid, true);
335 if (!conversation.getMucOptions().online()) {
336 if (password != null) {
337 conversation.getMucOptions().setPassword(password);
338 mXmppConnectionService.databaseBackend
339 .updateConversation(conversation);
340 }
341 mXmppConnectionService.joinMuc(conversation);
342 mXmppConnectionService.updateConversationUi();
343 }
344 }
345 }
346 }
347
348 private void parseEvent(final Element event, final Jid from, final Account account) {
349 Element items = event.findChild("items");
350 if (items == null) {
351 return;
352 }
353 String node = items.getAttribute("node");
354 if (node == null) {
355 return;
356 }
357 if (node.equals("urn:xmpp:avatar:metadata")) {
358 Avatar avatar = Avatar.parseMetadata(items);
359 if (avatar != null) {
360 avatar.owner = from;
361 if (mXmppConnectionService.getFileBackend().isAvatarCached(
362 avatar)) {
363 if (account.getJid().toBareJid().equals(from)) {
364 if (account.setAvatar(avatar.getFilename())) {
365 mXmppConnectionService.databaseBackend
366 .updateAccount(account);
367 }
368 mXmppConnectionService.getAvatarService().clear(
369 account);
370 mXmppConnectionService.updateConversationUi();
371 mXmppConnectionService.updateAccountUi();
372 } else {
373 Contact contact = account.getRoster().getContact(
374 from);
375 contact.setAvatar(avatar.getFilename());
376 mXmppConnectionService.getAvatarService().clear(
377 contact);
378 mXmppConnectionService.updateConversationUi();
379 mXmppConnectionService.updateRosterUi();
380 }
381 } else {
382 mXmppConnectionService.fetchAvatar(account, avatar);
383 }
384 }
385 } else if (node.equals("http://jabber.org/protocol/nick")) {
386 Element item = items.findChild("item");
387 if (item != null) {
388 Element nick = item.findChild("nick",
389 "http://jabber.org/protocol/nick");
390 if (nick != null) {
391 if (from != null) {
392 Contact contact = account.getRoster().getContact(
393 from);
394 contact.setPresenceName(nick.getContent());
395 mXmppConnectionService.getAvatarService().clear(account);
396 mXmppConnectionService.updateConversationUi();
397 mXmppConnectionService.updateAccountUi();
398 }
399 }
400 }
401 }
402 }
403
404 private String getPgpBody(Element message) {
405 Element child = message.findChild("x", "jabber:x:encrypted");
406 if (child == null) {
407 return null;
408 } else {
409 return child.getContent();
410 }
411 }
412
413 private boolean isMarkable(Element message) {
414 return message.hasChild("markable", "urn:xmpp:chat-markers:0");
415 }
416
417 @Override
418 public void onMessagePacketReceived(Account account, MessagePacket packet) {
419 Message message = null;
420 this.parseNick(packet, account);
421
422 if ((packet.getType() == MessagePacket.TYPE_CHAT || packet.getType() == MessagePacket.TYPE_NORMAL)) {
423 if ((packet.getBody() != null)
424 && (packet.getBody().startsWith("?OTR"))) {
425 message = this.parseOtrChat(packet, account);
426 if (message != null) {
427 message.markUnread();
428 }
429 } else if (packet.hasChild("body")
430 && !(packet.hasChild("x",
431 "http://jabber.org/protocol/muc#user"))) {
432 message = this.parseChat(packet, account);
433 if (message != null) {
434 message.markUnread();
435 }
436 } else if (packet.hasChild("received", "urn:xmpp:carbons:2")
437 || (packet.hasChild("sent", "urn:xmpp:carbons:2"))) {
438 message = this.parseCarbonMessage(packet, account);
439 if (message != null) {
440 if (message.getStatus() == Message.STATUS_SEND) {
441 account.activateGracePeriod();
442 mXmppConnectionService.markRead(
443 message.getConversation(), false);
444 } else {
445 message.markUnread();
446 }
447 }
448 } else {
449 parseNonMessage(packet, account);
450 }
451 } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
452 message = this.parseGroupchat(packet, account);
453 if (message != null) {
454 if (message.getStatus() == Message.STATUS_RECEIVED) {
455 message.markUnread();
456 } else {
457 mXmppConnectionService.markRead(message.getConversation(),
458 false);
459 account.activateGracePeriod();
460 }
461 }
462 } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
463 this.parseError(packet, account);
464 return;
465 } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
466 this.parseHeadline(packet, account);
467 return;
468 }
469 if ((message == null) || (message.getBody() == null)) {
470 return;
471 }
472 if ((mXmppConnectionService.confirmMessages())
473 && ((packet.getId() != null))) {
474 if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
475 MessagePacket receipt = mXmppConnectionService
476 .getMessageGenerator().received(account, packet,
477 "urn:xmpp:chat-markers:0");
478 mXmppConnectionService.sendMessagePacket(account, receipt);
479 }
480 if (packet.hasChild("request", "urn:xmpp:receipts")) {
481 MessagePacket receipt = mXmppConnectionService
482 .getMessageGenerator().received(account, packet,
483 "urn:xmpp:receipts");
484 mXmppConnectionService.sendMessagePacket(account, receipt);
485 }
486 }
487 Conversation conversation = message.getConversation();
488 conversation.add(message);
489 conversation.setLastMessageReceived(System.currentTimeMillis());
490 mXmppConnectionService.updateConversation(conversation);
491
492 if (message.getStatus() == Message.STATUS_RECEIVED
493 && conversation.getOtrSession() != null
494 && !conversation.getOtrSession().getSessionID().getUserID()
495 .equals(message.getCounterpart().getResourcepart())) {
496 Log.d(Config.LOGTAG, "ending because of reasons");
497 conversation.endOtrIfNeeded();
498 }
499
500 if (packet.getType() != MessagePacket.TYPE_ERROR) {
501 if (message.getEncryption() == Message.ENCRYPTION_NONE
502 || mXmppConnectionService.saveEncryptedMessages()) {
503 mXmppConnectionService.databaseBackend.createMessage(message);
504 }
505 }
506 if (message.trusted() && message.bodyContainsDownloadable()) {
507 this.mXmppConnectionService.getHttpConnectionManager()
508 .createNewConnection(message);
509 } else {
510 mXmppConnectionService.getNotificationService().push(message);
511 }
512 mXmppConnectionService.updateConversationUi();
513 }
514
515 private void parseHeadline(MessagePacket packet, Account account) {
516 if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
517 Element event = packet.findChild("event",
518 "http://jabber.org/protocol/pubsub#event");
519 parseEvent(event, packet.getFrom(), account);
520 }
521 }
522
523 private void parseNick(MessagePacket packet, Account account) {
524 Element nick = packet.findChild("nick",
525 "http://jabber.org/protocol/nick");
526 if (nick != null) {
527 if (packet.getFrom() != null) {
528 Contact contact = account.getRoster().getContact(
529 packet.getFrom());
530 contact.setPresenceName(nick.getContent());
531 }
532 }
533 }
534}