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