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 Message parseMamMessage(MessagePacket packet, final Account account) {
276 final Element result = packet.findChild("result","urn:xmpp:mam:0");
277 if (result == null ) {
278 return null;
279 }
280 final Element forwarded = result.findChild("forwarded","urn:xmpp:forward:0");
281 if (forwarded == null) {
282 return null;
283 }
284 final Element message = forwarded.findChild("message");
285 if (message == null) {
286 return null;
287 }
288 final Element body = message.findChild("body");
289 if (body == null || message.hasChild("private","urn:xmpp:carbons:2") || message.hasChild("no-copy","urn:xmpp:hints")) {
290 return null;
291 }
292 int encryption;
293 String content = getPgpBody(message);
294 if (content != null) {
295 encryption = Message.ENCRYPTION_PGP;
296 } else {
297 encryption = Message.ENCRYPTION_NONE;
298 content = body.getContent();
299 }
300 if (content == null) {
301 return null;
302 }
303 final long timestamp = getTimestamp(forwarded);
304 final Jid to = message.getAttributeAsJid("to");
305 final Jid from = message.getAttributeAsJid("from");
306 Jid counterpart;
307 int status;
308 Conversation conversation;
309 if (from!=null && to != null && from.toBareJid().equals(account.getJid().toBareJid())) {
310 status = Message.STATUS_SEND;
311 conversation = this.mXmppConnectionService.findOrCreateConversation(account,to.toBareJid(),false);
312 counterpart = to;
313 } else if (from !=null && to != null) {
314 status = Message.STATUS_RECEIVED;
315 conversation = this.mXmppConnectionService.findOrCreateConversation(account,from.toBareJid(),false);
316 counterpart = from;
317 } else {
318 return null;
319 }
320 Message finishedMessage = new Message(conversation,content,encryption,status);
321 finishedMessage.setTime(timestamp);
322 finishedMessage.setCounterpart(counterpart);
323 Log.d(Config.LOGTAG,"received mam message "+content);
324 return finishedMessage;
325 }
326
327 private void parseError(final MessagePacket packet, final Account account) {
328 final Jid from = packet.getFrom();
329 mXmppConnectionService.markMessage(account, from.toBareJid(),
330 packet.getId(), Message.STATUS_SEND_FAILED);
331 }
332
333 private void parseNonMessage(Element packet, Account account) {
334 final Jid from = packet.getAttributeAsJid("from");
335 if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
336 Element event = packet.findChild("event",
337 "http://jabber.org/protocol/pubsub#event");
338 parseEvent(event, from, account);
339 } else if (from != null
340 && packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
341 String id = packet
342 .findChild("displayed", "urn:xmpp:chat-markers:0")
343 .getAttribute("id");
344 updateLastseen(packet, account, true);
345 mXmppConnectionService.markMessage(account, from.toBareJid(),
346 id, Message.STATUS_SEND_DISPLAYED);
347 } else if (from != null
348 && packet.hasChild("received", "urn:xmpp:chat-markers:0")) {
349 String id = packet.findChild("received", "urn:xmpp:chat-markers:0")
350 .getAttribute("id");
351 updateLastseen(packet, account, false);
352 mXmppConnectionService.markMessage(account, from.toBareJid(),
353 id, Message.STATUS_SEND_RECEIVED);
354 } else if (from != null
355 && packet.hasChild("received", "urn:xmpp:receipts")) {
356 String id = packet.findChild("received", "urn:xmpp:receipts")
357 .getAttribute("id");
358 updateLastseen(packet, account, false);
359 mXmppConnectionService.markMessage(account, from.toBareJid(),
360 id, Message.STATUS_SEND_RECEIVED);
361 } else if (packet.hasChild("x", "http://jabber.org/protocol/muc#user")) {
362 Element x = packet.findChild("x",
363 "http://jabber.org/protocol/muc#user");
364 if (x.hasChild("invite")) {
365 Conversation conversation = mXmppConnectionService
366 .findOrCreateConversation(account,
367 packet.getAttributeAsJid("from"), true);
368 if (!conversation.getMucOptions().online()) {
369 if (x.hasChild("password")) {
370 Element password = x.findChild("password");
371 conversation.getMucOptions().setPassword(
372 password.getContent());
373 mXmppConnectionService.databaseBackend
374 .updateConversation(conversation);
375 }
376 mXmppConnectionService.joinMuc(conversation);
377 mXmppConnectionService.updateConversationUi();
378 }
379 }
380 } else if (packet.hasChild("x", "jabber:x:conference")) {
381 Element x = packet.findChild("x", "jabber:x:conference");
382 Jid jid = x.getAttributeAsJid("jid");
383 String password = x.getAttribute("password");
384 if (jid != null) {
385 Conversation conversation = mXmppConnectionService
386 .findOrCreateConversation(account, jid, true);
387 if (!conversation.getMucOptions().online()) {
388 if (password != null) {
389 conversation.getMucOptions().setPassword(password);
390 mXmppConnectionService.databaseBackend
391 .updateConversation(conversation);
392 }
393 mXmppConnectionService.joinMuc(conversation);
394 mXmppConnectionService.updateConversationUi();
395 }
396 }
397 }
398 }
399
400 private void parseEvent(final Element event, final Jid from, final Account account) {
401 Element items = event.findChild("items");
402 if (items == null) {
403 return;
404 }
405 String node = items.getAttribute("node");
406 if (node == null) {
407 return;
408 }
409 if (node.equals("urn:xmpp:avatar:metadata")) {
410 Avatar avatar = Avatar.parseMetadata(items);
411 if (avatar != null) {
412 avatar.owner = from;
413 if (mXmppConnectionService.getFileBackend().isAvatarCached(
414 avatar)) {
415 if (account.getJid().toBareJid().equals(from)) {
416 if (account.setAvatar(avatar.getFilename())) {
417 mXmppConnectionService.databaseBackend
418 .updateAccount(account);
419 }
420 mXmppConnectionService.getAvatarService().clear(
421 account);
422 mXmppConnectionService.updateConversationUi();
423 mXmppConnectionService.updateAccountUi();
424 } else {
425 Contact contact = account.getRoster().getContact(
426 from);
427 contact.setAvatar(avatar.getFilename());
428 mXmppConnectionService.getAvatarService().clear(
429 contact);
430 mXmppConnectionService.updateConversationUi();
431 mXmppConnectionService.updateRosterUi();
432 }
433 } else {
434 mXmppConnectionService.fetchAvatar(account, avatar);
435 }
436 }
437 } else if (node.equals("http://jabber.org/protocol/nick")) {
438 Element item = items.findChild("item");
439 if (item != null) {
440 Element nick = item.findChild("nick",
441 "http://jabber.org/protocol/nick");
442 if (nick != null) {
443 if (from != null) {
444 Contact contact = account.getRoster().getContact(
445 from);
446 contact.setPresenceName(nick.getContent());
447 mXmppConnectionService.getAvatarService().clear(account);
448 mXmppConnectionService.updateConversationUi();
449 mXmppConnectionService.updateAccountUi();
450 }
451 }
452 }
453 }
454 }
455
456 private String getPgpBody(Element message) {
457 Element child = message.findChild("x", "jabber:x:encrypted");
458 if (child == null) {
459 return null;
460 } else {
461 return child.getContent();
462 }
463 }
464
465 private boolean isMarkable(Element message) {
466 return message.hasChild("markable", "urn:xmpp:chat-markers:0");
467 }
468
469 @Override
470 public void onMessagePacketReceived(Account account, MessagePacket packet) {
471 Message message = null;
472 this.parseNick(packet, account);
473
474 if ((packet.getType() == MessagePacket.TYPE_CHAT || packet.getType() == MessagePacket.TYPE_NORMAL)) {
475 if ((packet.getBody() != null)
476 && (packet.getBody().startsWith("?OTR"))) {
477 message = this.parseOtrChat(packet, account);
478 if (message != null) {
479 message.markUnread();
480 }
481 } else if (packet.hasChild("body")
482 && !(packet.hasChild("x",
483 "http://jabber.org/protocol/muc#user"))) {
484 message = this.parseChat(packet, account);
485 if (message != null) {
486 message.markUnread();
487 }
488 } else if (packet.hasChild("received", "urn:xmpp:carbons:2")
489 || (packet.hasChild("sent", "urn:xmpp:carbons:2"))) {
490 message = this.parseCarbonMessage(packet, account);
491 if (message != null) {
492 if (message.getStatus() == Message.STATUS_SEND) {
493 account.activateGracePeriod();
494 mXmppConnectionService.markRead(
495 message.getConversation(), false);
496 } else {
497 message.markUnread();
498 }
499 }
500 } else if (packet.hasChild("result","urn:xmpp:mam:0")) {
501 message = parseMamMessage(packet, account);
502 if (message != null) {
503 Conversation conversation = message.getConversation();
504 conversation.add(message);
505 mXmppConnectionService.databaseBackend.createMessage(message);
506 }
507 return;
508 } else if (packet.hasChild("fin","urn:xmpp:mam:0")) {
509 Element fin = packet.findChild("fin","urn:xmpp:mam:0");
510 mXmppConnectionService.getMessageArchiveService().processFin(fin);
511 } else {
512 parseNonMessage(packet, account);
513 }
514 } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
515 message = this.parseGroupchat(packet, account);
516 if (message != null) {
517 if (message.getStatus() == Message.STATUS_RECEIVED) {
518 message.markUnread();
519 } else {
520 mXmppConnectionService.markRead(message.getConversation(),
521 false);
522 account.activateGracePeriod();
523 }
524 }
525 } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
526 this.parseError(packet, account);
527 return;
528 } else if (packet.getType() == MessagePacket.TYPE_HEADLINE) {
529 this.parseHeadline(packet, account);
530 return;
531 }
532 if ((message == null) || (message.getBody() == null)) {
533 return;
534 }
535 if ((mXmppConnectionService.confirmMessages())
536 && ((packet.getId() != null))) {
537 if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
538 MessagePacket receipt = mXmppConnectionService
539 .getMessageGenerator().received(account, packet,
540 "urn:xmpp:chat-markers:0");
541 mXmppConnectionService.sendMessagePacket(account, receipt);
542 }
543 if (packet.hasChild("request", "urn:xmpp:receipts")) {
544 MessagePacket receipt = mXmppConnectionService
545 .getMessageGenerator().received(account, packet,
546 "urn:xmpp:receipts");
547 mXmppConnectionService.sendMessagePacket(account, receipt);
548 }
549 }
550 Conversation conversation = message.getConversation();
551 conversation.add(message);
552 conversation.setLastMessageReceived(System.currentTimeMillis());
553 mXmppConnectionService.updateConversation(conversation);
554
555 if (message.getStatus() == Message.STATUS_RECEIVED
556 && conversation.getOtrSession() != null
557 && !conversation.getOtrSession().getSessionID().getUserID()
558 .equals(message.getCounterpart().getResourcepart())) {
559 conversation.endOtrIfNeeded();
560 }
561
562 if (packet.getType() != MessagePacket.TYPE_ERROR) {
563 if (message.getEncryption() == Message.ENCRYPTION_NONE
564 || mXmppConnectionService.saveEncryptedMessages()) {
565 mXmppConnectionService.databaseBackend.createMessage(message);
566 }
567 }
568 if (message.trusted() && message.bodyContainsDownloadable()) {
569 this.mXmppConnectionService.getHttpConnectionManager()
570 .createNewConnection(message);
571 } else if (!message.isRead()) {
572 mXmppConnectionService.getNotificationService().push(message);
573 }
574 mXmppConnectionService.updateConversationUi();
575 }
576
577 private void parseHeadline(MessagePacket packet, Account account) {
578 if (packet.hasChild("event", "http://jabber.org/protocol/pubsub#event")) {
579 Element event = packet.findChild("event",
580 "http://jabber.org/protocol/pubsub#event");
581 parseEvent(event, packet.getFrom(), account);
582 }
583 }
584
585 private void parseNick(MessagePacket packet, Account account) {
586 Element nick = packet.findChild("nick",
587 "http://jabber.org/protocol/nick");
588 if (nick != null) {
589 if (packet.getFrom() != null) {
590 Contact contact = account.getRoster().getContact(
591 packet.getFrom());
592 contact.setPresenceName(nick.getContent());
593 }
594 }
595 }
596}