1package eu.siacs.conversations.entities;
2
3import android.content.ContentValues;
4import android.database.Cursor;
5
6import java.net.MalformedURLException;
7import java.net.URL;
8import java.util.Arrays;
9
10import eu.siacs.conversations.Config;
11import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
12import eu.siacs.conversations.utils.GeoHelper;
13import eu.siacs.conversations.utils.MimeUtils;
14import eu.siacs.conversations.utils.UIHelper;
15import eu.siacs.conversations.xmpp.jid.InvalidJidException;
16import eu.siacs.conversations.xmpp.jid.Jid;
17
18public class Message extends AbstractEntity {
19
20 public static final String TABLENAME = "messages";
21
22 public static final String MERGE_SEPARATOR = " \u200B\n\n";
23
24 public static final int STATUS_RECEIVED = 0;
25 public static final int STATUS_UNSEND = 1;
26 public static final int STATUS_SEND = 2;
27 public static final int STATUS_SEND_FAILED = 3;
28 public static final int STATUS_WAITING = 5;
29 public static final int STATUS_OFFERED = 6;
30 public static final int STATUS_SEND_RECEIVED = 7;
31 public static final int STATUS_SEND_DISPLAYED = 8;
32
33 public static final int ENCRYPTION_NONE = 0;
34 public static final int ENCRYPTION_PGP = 1;
35 public static final int ENCRYPTION_OTR = 2;
36 public static final int ENCRYPTION_DECRYPTED = 3;
37 public static final int ENCRYPTION_DECRYPTION_FAILED = 4;
38 public static final int ENCRYPTION_AXOLOTL = 5;
39
40 public static final int TYPE_TEXT = 0;
41 public static final int TYPE_IMAGE = 1;
42 public static final int TYPE_FILE = 2;
43 public static final int TYPE_STATUS = 3;
44 public static final int TYPE_PRIVATE = 4;
45
46 public static final String CONVERSATION = "conversationUuid";
47 public static final String COUNTERPART = "counterpart";
48 public static final String TRUE_COUNTERPART = "trueCounterpart";
49 public static final String BODY = "body";
50 public static final String TIME_SENT = "timeSent";
51 public static final String ENCRYPTION = "encryption";
52 public static final String STATUS = "status";
53 public static final String TYPE = "type";
54 public static final String CARBON = "carbon";
55 public static final String EDITED = "edited";
56 public static final String REMOTE_MSG_ID = "remoteMsgId";
57 public static final String SERVER_MSG_ID = "serverMsgId";
58 public static final String RELATIVE_FILE_PATH = "relativeFilePath";
59 public static final String FINGERPRINT = "axolotl_fingerprint";
60 public static final String READ = "read";
61 public static final String ME_COMMAND = "/me ";
62
63
64 public boolean markable = false;
65 protected String conversationUuid;
66 protected Jid counterpart;
67 protected Jid trueCounterpart;
68 protected String body;
69 protected String encryptedBody;
70 protected long timeSent;
71 protected int encryption;
72 protected int status;
73 protected int type;
74 protected boolean carbon = false;
75 protected String edited = null;
76 protected String relativeFilePath;
77 protected boolean read = true;
78 protected String remoteMsgId = null;
79 protected String serverMsgId = null;
80 protected Conversation conversation = null;
81 protected Transferable transferable = null;
82 private Message mNextMessage = null;
83 private Message mPreviousMessage = null;
84 private String axolotlFingerprint = null;
85
86 private Message() {
87
88 }
89
90 public Message(Conversation conversation, String body, int encryption) {
91 this(conversation, body, encryption, STATUS_UNSEND);
92 }
93
94 public Message(Conversation conversation, String body, int encryption, int status) {
95 this(java.util.UUID.randomUUID().toString(),
96 conversation.getUuid(),
97 conversation.getJid() == null ? null : conversation.getJid().toBareJid(),
98 null,
99 body,
100 System.currentTimeMillis(),
101 encryption,
102 status,
103 TYPE_TEXT,
104 false,
105 null,
106 null,
107 null,
108 null,
109 true,
110 null);
111 this.conversation = conversation;
112 }
113
114 private Message(final String uuid, final String conversationUUid, final Jid counterpart,
115 final Jid trueCounterpart, final String body, final long timeSent,
116 final int encryption, final int status, final int type, final boolean carbon,
117 final String remoteMsgId, final String relativeFilePath,
118 final String serverMsgId, final String fingerprint, final boolean read,
119 final String edited) {
120 this.uuid = uuid;
121 this.conversationUuid = conversationUUid;
122 this.counterpart = counterpart;
123 this.trueCounterpart = trueCounterpart;
124 this.body = body;
125 this.timeSent = timeSent;
126 this.encryption = encryption;
127 this.status = status;
128 this.type = type;
129 this.carbon = carbon;
130 this.remoteMsgId = remoteMsgId;
131 this.relativeFilePath = relativeFilePath;
132 this.serverMsgId = serverMsgId;
133 this.axolotlFingerprint = fingerprint;
134 this.read = read;
135 this.edited = edited;
136 }
137
138 public static Message fromCursor(Cursor cursor) {
139 Jid jid;
140 try {
141 String value = cursor.getString(cursor.getColumnIndex(COUNTERPART));
142 if (value != null) {
143 jid = Jid.fromString(value, true);
144 } else {
145 jid = null;
146 }
147 } catch (InvalidJidException e) {
148 jid = null;
149 }
150 Jid trueCounterpart;
151 try {
152 String value = cursor.getString(cursor.getColumnIndex(TRUE_COUNTERPART));
153 if (value != null) {
154 trueCounterpart = Jid.fromString(value, true);
155 } else {
156 trueCounterpart = null;
157 }
158 } catch (InvalidJidException e) {
159 trueCounterpart = null;
160 }
161 return new Message(cursor.getString(cursor.getColumnIndex(UUID)),
162 cursor.getString(cursor.getColumnIndex(CONVERSATION)),
163 jid,
164 trueCounterpart,
165 cursor.getString(cursor.getColumnIndex(BODY)),
166 cursor.getLong(cursor.getColumnIndex(TIME_SENT)),
167 cursor.getInt(cursor.getColumnIndex(ENCRYPTION)),
168 cursor.getInt(cursor.getColumnIndex(STATUS)),
169 cursor.getInt(cursor.getColumnIndex(TYPE)),
170 cursor.getInt(cursor.getColumnIndex(CARBON)) > 0,
171 cursor.getString(cursor.getColumnIndex(REMOTE_MSG_ID)),
172 cursor.getString(cursor.getColumnIndex(RELATIVE_FILE_PATH)),
173 cursor.getString(cursor.getColumnIndex(SERVER_MSG_ID)),
174 cursor.getString(cursor.getColumnIndex(FINGERPRINT)),
175 cursor.getInt(cursor.getColumnIndex(READ)) > 0,
176 cursor.getString(cursor.getColumnIndex(EDITED)));
177 }
178
179 public static Message createStatusMessage(Conversation conversation, String body) {
180 final Message message = new Message();
181 message.setType(Message.TYPE_STATUS);
182 message.setConversation(conversation);
183 message.setBody(body);
184 return message;
185 }
186
187 public static Message createLoadMoreMessage(Conversation conversation) {
188 final Message message = new Message();
189 message.setType(Message.TYPE_STATUS);
190 message.setConversation(conversation);
191 message.setBody("LOAD_MORE");
192 return message;
193 }
194
195 @Override
196 public ContentValues getContentValues() {
197 ContentValues values = new ContentValues();
198 values.put(UUID, uuid);
199 values.put(CONVERSATION, conversationUuid);
200 if (counterpart == null) {
201 values.putNull(COUNTERPART);
202 } else {
203 values.put(COUNTERPART, counterpart.toString());
204 }
205 if (trueCounterpart == null) {
206 values.putNull(TRUE_COUNTERPART);
207 } else {
208 values.put(TRUE_COUNTERPART, trueCounterpart.toString());
209 }
210 values.put(BODY, body);
211 values.put(TIME_SENT, timeSent);
212 values.put(ENCRYPTION, encryption);
213 values.put(STATUS, status);
214 values.put(TYPE, type);
215 values.put(CARBON, carbon ? 1 : 0);
216 values.put(REMOTE_MSG_ID, remoteMsgId);
217 values.put(RELATIVE_FILE_PATH, relativeFilePath);
218 values.put(SERVER_MSG_ID, serverMsgId);
219 values.put(FINGERPRINT, axolotlFingerprint);
220 values.put(READ,read ? 1 : 0);
221 values.put(EDITED, edited);
222 return values;
223 }
224
225 public String getConversationUuid() {
226 return conversationUuid;
227 }
228
229 public Conversation getConversation() {
230 return this.conversation;
231 }
232
233 public void setConversation(Conversation conv) {
234 this.conversation = conv;
235 }
236
237 public Jid getCounterpart() {
238 return counterpart;
239 }
240
241 public void setCounterpart(final Jid counterpart) {
242 this.counterpart = counterpart;
243 }
244
245 public Contact getContact() {
246 if (this.conversation.getMode() == Conversation.MODE_SINGLE) {
247 return this.conversation.getContact();
248 } else {
249 if (this.trueCounterpart == null) {
250 return null;
251 } else {
252 return this.conversation.getAccount().getRoster()
253 .getContactFromRoster(this.trueCounterpart);
254 }
255 }
256 }
257
258 public String getBody() {
259 return body;
260 }
261
262 public void setBody(String body) {
263 this.body = body;
264 }
265
266 public long getTimeSent() {
267 return timeSent;
268 }
269
270 public int getEncryption() {
271 return encryption;
272 }
273
274 public void setEncryption(int encryption) {
275 this.encryption = encryption;
276 }
277
278 public int getStatus() {
279 return status;
280 }
281
282 public void setStatus(int status) {
283 this.status = status;
284 }
285
286 public String getRelativeFilePath() {
287 return this.relativeFilePath;
288 }
289
290 public void setRelativeFilePath(String path) {
291 this.relativeFilePath = path;
292 }
293
294 public String getRemoteMsgId() {
295 return this.remoteMsgId;
296 }
297
298 public void setRemoteMsgId(String id) {
299 this.remoteMsgId = id;
300 }
301
302 public String getServerMsgId() {
303 return this.serverMsgId;
304 }
305
306 public void setServerMsgId(String id) {
307 this.serverMsgId = id;
308 }
309
310 public boolean isRead() {
311 return this.read;
312 }
313
314 public void markRead() {
315 this.read = true;
316 }
317
318 public void markUnread() {
319 this.read = false;
320 }
321
322 public void setTime(long time) {
323 this.timeSent = time;
324 }
325
326 public String getEncryptedBody() {
327 return this.encryptedBody;
328 }
329
330 public void setEncryptedBody(String body) {
331 this.encryptedBody = body;
332 }
333
334 public int getType() {
335 return this.type;
336 }
337
338 public void setType(int type) {
339 this.type = type;
340 }
341
342 public boolean isCarbon() {
343 return carbon;
344 }
345
346 public void setCarbon(boolean carbon) {
347 this.carbon = carbon;
348 }
349
350 public void setEdited(String edited) {
351 this.edited = edited;
352 }
353
354 public boolean edited() {
355 return this.edited != null;
356 }
357
358 public void setTrueCounterpart(Jid trueCounterpart) {
359 this.trueCounterpart = trueCounterpart;
360 }
361
362 public Jid getTrueCounterpart() {
363 return this.trueCounterpart;
364 }
365
366 public Transferable getTransferable() {
367 return this.transferable;
368 }
369
370 public void setTransferable(Transferable transferable) {
371 this.transferable = transferable;
372 }
373
374 public boolean equals(Message message) {
375 if (this.serverMsgId != null && message.getServerMsgId() != null) {
376 return this.serverMsgId.equals(message.getServerMsgId());
377 } else if (this.body == null || this.counterpart == null) {
378 return false;
379 } else {
380 String body, otherBody;
381 if (this.hasFileOnRemoteHost()) {
382 body = getFileParams().url.toString();
383 otherBody = message.body == null ? null : message.body.trim();
384 } else {
385 body = this.body;
386 otherBody = message.body;
387 }
388 if (message.getRemoteMsgId() != null) {
389 return (message.getRemoteMsgId().equals(this.remoteMsgId) || message.getRemoteMsgId().equals(this.uuid))
390 && this.counterpart.equals(message.getCounterpart())
391 && (body.equals(otherBody)
392 ||(message.getEncryption() == Message.ENCRYPTION_PGP
393 && message.getRemoteMsgId().matches("[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"))) ;
394 } else {
395 return this.remoteMsgId == null
396 && this.counterpart.equals(message.getCounterpart())
397 && body.equals(otherBody)
398 && Math.abs(this.getTimeSent() - message.getTimeSent()) < Config.MESSAGE_MERGE_WINDOW * 1000;
399 }
400 }
401 }
402
403 public Message next() {
404 synchronized (this.conversation.messages) {
405 if (this.mNextMessage == null) {
406 int index = this.conversation.messages.indexOf(this);
407 if (index < 0 || index >= this.conversation.messages.size() - 1) {
408 this.mNextMessage = null;
409 } else {
410 this.mNextMessage = this.conversation.messages.get(index + 1);
411 }
412 }
413 return this.mNextMessage;
414 }
415 }
416
417 public Message prev() {
418 synchronized (this.conversation.messages) {
419 if (this.mPreviousMessage == null) {
420 int index = this.conversation.messages.indexOf(this);
421 if (index <= 0 || index > this.conversation.messages.size()) {
422 this.mPreviousMessage = null;
423 } else {
424 this.mPreviousMessage = this.conversation.messages.get(index - 1);
425 }
426 }
427 return this.mPreviousMessage;
428 }
429 }
430
431 public boolean isLastCorrectableMessage() {
432 Message next = next();
433 while(next != null) {
434 if (next.isCorrectable()) {
435 return false;
436 }
437 next = next.next();
438 }
439 return isCorrectable();
440 }
441
442 private boolean isCorrectable() {
443 return getStatus() != STATUS_RECEIVED && !isCarbon();
444 }
445
446 public boolean mergeable(final Message message) {
447 return message != null &&
448 (message.getType() == Message.TYPE_TEXT &&
449 this.getTransferable() == null &&
450 message.getTransferable() == null &&
451 message.getEncryption() != Message.ENCRYPTION_PGP &&
452 message.getEncryption() != Message.ENCRYPTION_DECRYPTION_FAILED &&
453 this.getType() == message.getType() &&
454 //this.getStatus() == message.getStatus() &&
455 isStatusMergeable(this.getStatus(), message.getStatus()) &&
456 this.getEncryption() == message.getEncryption() &&
457 this.getCounterpart() != null &&
458 this.getCounterpart().equals(message.getCounterpart()) &&
459 this.edited() == message.edited() &&
460 (message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
461 !GeoHelper.isGeoUri(message.getBody()) &&
462 !GeoHelper.isGeoUri(this.body) &&
463 message.treatAsDownloadable() == Decision.NEVER &&
464 this.treatAsDownloadable() == Decision.NEVER &&
465 !message.getBody().startsWith(ME_COMMAND) &&
466 !this.getBody().startsWith(ME_COMMAND) &&
467 !this.bodyIsHeart() &&
468 !message.bodyIsHeart() &&
469 this.isTrusted() == message.isTrusted()
470 );
471 }
472
473 private static boolean isStatusMergeable(int a, int b) {
474 return a == b || (
475 (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
476 || (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
477 || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
478 || (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
479 || (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
480 || (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
481 );
482 }
483
484 public String getMergedBody() {
485 StringBuilder body = new StringBuilder(this.body.trim());
486 Message current = this;
487 while(current.mergeable(current.next())) {
488 current = current.next();
489 body.append(MERGE_SEPARATOR);
490 body.append(current.getBody().trim());
491 }
492 return body.toString();
493 }
494
495 public boolean hasMeCommand() {
496 return getMergedBody().startsWith(ME_COMMAND);
497 }
498
499 public int getMergedStatus() {
500 int status = this.status;
501 Message current = this;
502 while(current.mergeable(current.next())) {
503 current = current.next();
504 status = current.status;
505 }
506 return status;
507 }
508
509 public long getMergedTimeSent() {
510 long time = this.timeSent;
511 Message current = this;
512 while(current.mergeable(current.next())) {
513 current = current.next();
514 time = current.timeSent;
515 }
516 return time;
517 }
518
519 public boolean wasMergedIntoPrevious() {
520 Message prev = this.prev();
521 return prev != null && prev.mergeable(this);
522 }
523
524 public boolean trusted() {
525 Contact contact = this.getContact();
526 return (status > STATUS_RECEIVED || (contact != null && contact.trusted()));
527 }
528
529 public boolean fixCounterpart() {
530 Presences presences = conversation.getContact().getPresences();
531 if (counterpart != null && presences.has(counterpart.getResourcepart())) {
532 return true;
533 } else if (presences.size() >= 1) {
534 try {
535 counterpart = Jid.fromParts(conversation.getJid().getLocalpart(),
536 conversation.getJid().getDomainpart(),
537 presences.asStringArray()[0]);
538 return true;
539 } catch (InvalidJidException e) {
540 counterpart = null;
541 return false;
542 }
543 } else {
544 counterpart = null;
545 return false;
546 }
547 }
548
549 public void setUuid(String uuid) {
550 this.uuid = uuid;
551 }
552
553 public String getEditedId() {
554 return edited;
555 }
556
557 public enum Decision {
558 MUST,
559 SHOULD,
560 NEVER,
561 }
562
563 private static String extractRelevantExtension(URL url) {
564 String path = url.getPath();
565 return extractRelevantExtension(path);
566 }
567
568 private static String extractRelevantExtension(String path) {
569 if (path == null || path.isEmpty()) {
570 return null;
571 }
572
573 String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase();
574 int dotPosition = filename.lastIndexOf(".");
575
576 if (dotPosition != -1) {
577 String extension = filename.substring(dotPosition + 1);
578 // we want the real file extension, not the crypto one
579 if (Transferable.VALID_CRYPTO_EXTENSIONS.contains(extension)) {
580 return extractRelevantExtension(filename.substring(0,dotPosition));
581 } else {
582 return extension;
583 }
584 }
585 return null;
586 }
587
588 public String getMimeType() {
589 if (relativeFilePath != null) {
590 int start = relativeFilePath.lastIndexOf('.') + 1;
591 if (start < relativeFilePath.length()) {
592 return MimeUtils.guessMimeTypeFromExtension(relativeFilePath.substring(start));
593 } else {
594 return null;
595 }
596 } else {
597 try {
598 return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(body.trim())));
599 } catch (MalformedURLException e) {
600 return null;
601 }
602 }
603 }
604
605 public Decision treatAsDownloadable() {
606 if (body.trim().contains(" ")) {
607 return Decision.NEVER;
608 }
609 try {
610 URL url = new URL(body);
611 if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
612 return Decision.NEVER;
613 }
614 String extension = extractRelevantExtension(url);
615 if (extension == null) {
616 return Decision.NEVER;
617 }
618 String ref = url.getRef();
619 boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}");
620
621 if (encrypted) {
622 if (MimeUtils.guessMimeTypeFromExtension(extension) != null) {
623 return Decision.MUST;
624 } else {
625 return Decision.NEVER;
626 }
627 } else if (Transferable.VALID_IMAGE_EXTENSIONS.contains(extension)
628 || Transferable.WELL_KNOWN_EXTENSIONS.contains(extension)) {
629 return Decision.SHOULD;
630 } else {
631 return Decision.NEVER;
632 }
633
634 } catch (MalformedURLException e) {
635 return Decision.NEVER;
636 }
637 }
638
639 public boolean bodyIsHeart() {
640 return body != null && UIHelper.HEARTS.contains(body.trim());
641 }
642
643 public FileParams getFileParams() {
644 FileParams params = getLegacyFileParams();
645 if (params != null) {
646 return params;
647 }
648 params = new FileParams();
649 if (this.transferable != null) {
650 params.size = this.transferable.getFileSize();
651 }
652 if (body == null) {
653 return params;
654 }
655 String parts[] = body.split("\\|");
656 switch (parts.length) {
657 case 1:
658 try {
659 params.size = Long.parseLong(parts[0]);
660 } catch (NumberFormatException e) {
661 try {
662 params.url = new URL(parts[0]);
663 } catch (MalformedURLException e1) {
664 params.url = null;
665 }
666 }
667 break;
668 case 2:
669 case 4:
670 try {
671 params.url = new URL(parts[0]);
672 } catch (MalformedURLException e1) {
673 params.url = null;
674 }
675 try {
676 params.size = Long.parseLong(parts[1]);
677 } catch (NumberFormatException e) {
678 params.size = 0;
679 }
680 try {
681 params.width = Integer.parseInt(parts[2]);
682 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
683 params.width = 0;
684 }
685 try {
686 params.height = Integer.parseInt(parts[3]);
687 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
688 params.height = 0;
689 }
690 break;
691 case 3:
692 try {
693 params.size = Long.parseLong(parts[0]);
694 } catch (NumberFormatException e) {
695 params.size = 0;
696 }
697 try {
698 params.width = Integer.parseInt(parts[1]);
699 } catch (NumberFormatException e) {
700 params.width = 0;
701 }
702 try {
703 params.height = Integer.parseInt(parts[2]);
704 } catch (NumberFormatException e) {
705 params.height = 0;
706 }
707 break;
708 }
709 return params;
710 }
711
712 public FileParams getLegacyFileParams() {
713 FileParams params = new FileParams();
714 if (body == null) {
715 return params;
716 }
717 String parts[] = body.split(",");
718 if (parts.length == 3) {
719 try {
720 params.size = Long.parseLong(parts[0]);
721 } catch (NumberFormatException e) {
722 return null;
723 }
724 try {
725 params.width = Integer.parseInt(parts[1]);
726 } catch (NumberFormatException e) {
727 return null;
728 }
729 try {
730 params.height = Integer.parseInt(parts[2]);
731 } catch (NumberFormatException e) {
732 return null;
733 }
734 return params;
735 } else {
736 return null;
737 }
738 }
739
740 public void untie() {
741 this.mNextMessage = null;
742 this.mPreviousMessage = null;
743 }
744
745 public boolean isFileOrImage() {
746 return type == TYPE_FILE || type == TYPE_IMAGE;
747 }
748
749 public boolean hasFileOnRemoteHost() {
750 return isFileOrImage() && getFileParams().url != null;
751 }
752
753 public boolean needsUploading() {
754 return isFileOrImage() && getFileParams().url == null;
755 }
756
757 public class FileParams {
758 public URL url;
759 public long size = 0;
760 public int width = 0;
761 public int height = 0;
762 }
763
764 public void setAxolotlFingerprint(String fingerprint) {
765 this.axolotlFingerprint = fingerprint;
766 }
767
768 public String getAxolotlFingerprint() {
769 return axolotlFingerprint;
770 }
771
772 public boolean isTrusted() {
773 XmppAxolotlSession.Trust t = conversation.getAccount().getAxolotlService().getFingerprintTrust(axolotlFingerprint);
774 return t != null && t.trusted();
775 }
776
777 private int getPreviousEncryption() {
778 for (Message iterator = this.prev(); iterator != null; iterator = iterator.prev()){
779 if( iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED ) {
780 continue;
781 }
782 return iterator.getEncryption();
783 }
784 return ENCRYPTION_NONE;
785 }
786
787 private int getNextEncryption() {
788 for (Message iterator = this.next(); iterator != null; iterator = iterator.next()){
789 if( iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED ) {
790 continue;
791 }
792 return iterator.getEncryption();
793 }
794 return conversation.getNextEncryption();
795 }
796
797 public boolean isValidInSession() {
798 int pastEncryption = getCleanedEncryption(this.getPreviousEncryption());
799 int futureEncryption = getCleanedEncryption(this.getNextEncryption());
800
801 boolean inUnencryptedSession = pastEncryption == ENCRYPTION_NONE
802 || futureEncryption == ENCRYPTION_NONE
803 || pastEncryption != futureEncryption;
804
805 return inUnencryptedSession || getCleanedEncryption(this.getEncryption()) == pastEncryption;
806 }
807
808 private static int getCleanedEncryption(int encryption) {
809 if (encryption == ENCRYPTION_DECRYPTED || encryption == ENCRYPTION_DECRYPTION_FAILED) {
810 return ENCRYPTION_PGP;
811 }
812 return encryption;
813 }
814}