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}