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 }
84 if ((body == null) || (body.isEmpty())) {
85 return null;
86 }
87 if (body.startsWith(CryptoHelper.FILETRANSFER)) {
88 String key = body.substring(CryptoHelper.FILETRANSFER.length());
89 conversation.setSymmetricKey(CryptoHelper.hexToBytes(key));
90 return null;
91 }
92 conversation
93 .setLatestMarkableMessageId(getMarkableMessageId(packet));
94 Message finishedMessage = new Message(conversation,
95 packet.getFrom(), body, Message.ENCRYPTION_OTR,
96 Message.STATUS_RECIEVED);
97 finishedMessage.setTime(getTimestamp(packet));
98 return finishedMessage;
99 } catch (Exception e) {
100 String receivedId = packet.getId();
101 if (receivedId != null) {
102 mXmppConnectionService.replyWithNotAcceptable(account, packet);
103 }
104 conversation.endOtrIfNeeded();
105 return null;
106 }
107 }
108
109 private Message parseGroupchat(MessagePacket packet, Account account) {
110 int status;
111 String[] fromParts = packet.getFrom().split("/");
112 if (mXmppConnectionService.find(account.pendingConferenceLeaves,account,fromParts[0]) != null) {
113 return null;
114 }
115 Conversation conversation = mXmppConnectionService
116 .findOrCreateConversation(account, fromParts[0], true);
117 if (packet.hasChild("subject")) {
118 conversation.getMucOptions().setSubject(
119 packet.findChild("subject").getContent());
120 mXmppConnectionService.updateConversationUi();
121 return null;
122 }
123 if ((fromParts.length == 1)) {
124 return null;
125 }
126 String counterPart = fromParts[1];
127 if (counterPart.equals(conversation.getMucOptions().getActualNick())) {
128 if (mXmppConnectionService.markMessage(conversation,
129 packet.getId(), Message.STATUS_SEND)) {
130 return null;
131 } else {
132 status = Message.STATUS_SEND;
133 }
134 } else {
135 status = Message.STATUS_RECIEVED;
136 }
137 String pgpBody = getPgpBody(packet);
138 conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
139 Message finishedMessage;
140 if (pgpBody == null) {
141 finishedMessage = new Message(conversation, counterPart,
142 packet.getBody(), Message.ENCRYPTION_NONE, status);
143 } else {
144 finishedMessage = new Message(conversation, counterPart, pgpBody,
145 Message.ENCRYPTION_PGP, status);
146 }
147 finishedMessage.setTime(getTimestamp(packet));
148 return finishedMessage;
149 }
150
151 private Message parseCarbonMessage(MessagePacket packet, Account account) {
152 int status;
153 String fullJid;
154 Element forwarded;
155 if (packet.hasChild("received")) {
156 forwarded = packet.findChild("received").findChild("forwarded");
157 status = Message.STATUS_RECIEVED;
158 } else if (packet.hasChild("sent")) {
159 forwarded = packet.findChild("sent").findChild("forwarded");
160 status = Message.STATUS_SEND;
161 } else {
162 return null;
163 }
164 if (forwarded == null) {
165 return null;
166 }
167 Element message = forwarded.findChild("message");
168 if ((message == null) || (!message.hasChild("body"))) {
169 if (status == Message.STATUS_RECIEVED) {
170 parseNormal(message, account);
171 }
172 return null;
173 }
174 if (status == Message.STATUS_RECIEVED) {
175 fullJid = message.getAttribute("from");
176 updateLastseen(message, account, true);
177 } else {
178 fullJid = message.getAttribute("to");
179 }
180 if (fullJid==null) {
181 return null;
182 }
183 String[] parts = fullJid.split("/");
184 Conversation conversation = mXmppConnectionService
185 .findOrCreateConversation(account, parts[0], false);
186 conversation.setLatestMarkableMessageId(getMarkableMessageId(packet));
187 String pgpBody = getPgpBody(message);
188 Message finishedMessage;
189 if (pgpBody != null) {
190 finishedMessage = new Message(conversation, fullJid, pgpBody,
191 Message.ENCRYPTION_PGP, status);
192 } else {
193 String body = message.findChild("body").getContent();
194 finishedMessage = new Message(conversation, fullJid, body,
195 Message.ENCRYPTION_NONE, status);
196 }
197 finishedMessage.setTime(getTimestamp(message));
198 return finishedMessage;
199 }
200
201 private void parseError(MessagePacket packet, Account account) {
202 String[] fromParts = packet.getFrom().split("/");
203 mXmppConnectionService.markMessage(account, fromParts[0],
204 packet.getId(), Message.STATUS_SEND_FAILED);
205 }
206
207 private void parseNormal(Element packet, Account account) {
208 if (packet.hasChild("displayed", "urn:xmpp:chat-markers:0")) {
209 String id = packet
210 .findChild("displayed", "urn:xmpp:chat-markers:0")
211 .getAttribute("id");
212 String[] fromParts = packet.getAttribute("from").split("/");
213 updateLastseen(packet, account, true);
214 mXmppConnectionService.markMessage(account, fromParts[0], id,
215 Message.STATUS_SEND_DISPLAYED);
216 } else if (packet.hasChild("received", "urn:xmpp:chat-markers:0")) {
217 String id = packet.findChild("received", "urn:xmpp:chat-markers:0")
218 .getAttribute("id");
219 String[] fromParts = packet.getAttribute("from").split("/");
220 updateLastseen(packet, account, false);
221 mXmppConnectionService.markMessage(account, fromParts[0], id,
222 Message.STATUS_SEND_RECEIVED);
223 } else if (packet.hasChild("x")) {
224 Element x = packet.findChild("x");
225 if (x.hasChild("invite")) {
226 Conversation conversation = mXmppConnectionService
227 .findOrCreateConversation(account,
228 packet.getAttribute("from"), true);
229 if (!conversation.getMucOptions().online()) {
230 mXmppConnectionService.joinMuc(conversation);
231 mXmppConnectionService.updateConversationUi();
232 }
233 }
234
235 }
236 }
237
238 private String getPgpBody(Element message) {
239 Element child = message.findChild("x", "jabber:x:encrypted");
240 if (child == null) {
241 return null;
242 } else {
243 return child.getContent();
244 }
245 }
246
247 private String getMarkableMessageId(Element message) {
248 if (message.hasChild("markable", "urn:xmpp:chat-markers:0")) {
249 return message.getAttribute("id");
250 } else {
251 return null;
252 }
253 }
254
255 @Override
256 public void onMessagePacketReceived(Account account, MessagePacket packet) {
257 Message message = null;
258 boolean notify = true;
259 if (mXmppConnectionService.getPreferences().getBoolean(
260 "notification_grace_period_after_carbon_received", true)) {
261 notify = (SystemClock.elapsedRealtime() - lastCarbonMessageReceived) > XmppConnectionService.CARBON_GRACE_PERIOD;
262 }
263
264 if ((packet.getType() == MessagePacket.TYPE_CHAT)) {
265 if ((packet.getBody() != null)
266 && (packet.getBody().startsWith("?OTR"))) {
267 message = this.parseOtrChat(packet, account);
268 if (message != null) {
269 message.markUnread();
270 }
271 } else if (packet.hasChild("body")) {
272 message = this.parseChat(packet, account);
273 message.markUnread();
274 } else if (packet.hasChild("received") || (packet.hasChild("sent"))) {
275 message = this.parseCarbonMessage(packet, account);
276 if (message != null) {
277 if (message.getStatus() == Message.STATUS_SEND) {
278 lastCarbonMessageReceived = SystemClock
279 .elapsedRealtime();
280 notify = false;
281 message.getConversation().markRead();
282 } else {
283 message.markUnread();
284 }
285 }
286 }
287
288 } else if (packet.getType() == MessagePacket.TYPE_GROUPCHAT) {
289 message = this.parseGroupchat(packet, account);
290 if (message != null) {
291 if (message.getStatus() == Message.STATUS_RECIEVED) {
292 message.markUnread();
293 } else {
294 message.getConversation().markRead();
295 lastCarbonMessageReceived = SystemClock
296 .elapsedRealtime();
297 notify = false;
298 }
299 }
300 } else if (packet.getType() == MessagePacket.TYPE_ERROR) {
301 this.parseError(packet, account);
302 return;
303 } else if (packet.getType() == MessagePacket.TYPE_NORMAL) {
304 this.parseNormal(packet, account);
305 }
306 if ((message == null) || (message.getBody() == null)) {
307 return;
308 }
309 if ((mXmppConnectionService.confirmMessages())
310 && ((packet.getId() != null))) {
311 MessagePacket receivedPacket = new MessagePacket();
312 receivedPacket.setType(MessagePacket.TYPE_NORMAL);
313 receivedPacket.setTo(message.getCounterpart());
314 receivedPacket.setFrom(account.getFullJid());
315 if (packet.hasChild("markable", "urn:xmpp:chat-markers:0")) {
316 Element received = receivedPacket.addChild("received",
317 "urn:xmpp:chat-markers:0");
318 received.setAttribute("id", packet.getId());
319 account.getXmppConnection().sendMessagePacket(receivedPacket);
320 } else if (packet.hasChild("request", "urn:xmpp:receipts")) {
321 Element received = receivedPacket.addChild("received",
322 "urn:xmpp:receipts");
323 received.setAttribute("id", packet.getId());
324 account.getXmppConnection().sendMessagePacket(receivedPacket);
325 }
326 }
327 Conversation conversation = message.getConversation();
328 conversation.getMessages().add(message);
329 if (packet.getType() != MessagePacket.TYPE_ERROR) {
330 mXmppConnectionService.databaseBackend.createMessage(message);
331 }
332 mXmppConnectionService.notifyUi(conversation, notify);
333 }
334}