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