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