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