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