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