Message.java

  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 mergeable(final Message message) {
432		return message != null &&
433				(message.getType() == Message.TYPE_TEXT &&
434						this.getTransferable() == null &&
435						message.getTransferable() == null &&
436						message.getEncryption() != Message.ENCRYPTION_PGP &&
437						this.getType() == message.getType() &&
438						//this.getStatus() == message.getStatus() &&
439						isStatusMergeable(this.getStatus(), message.getStatus()) &&
440						this.getEncryption() == message.getEncryption() &&
441						this.getCounterpart() != null &&
442						this.getCounterpart().equals(message.getCounterpart()) &&
443						this.edited() == message.edited() &&
444						(message.getTimeSent() - this.getTimeSent()) <= (Config.MESSAGE_MERGE_WINDOW * 1000) &&
445						!GeoHelper.isGeoUri(message.getBody()) &&
446						!GeoHelper.isGeoUri(this.body) &&
447						message.treatAsDownloadable() == Decision.NEVER &&
448						this.treatAsDownloadable() == Decision.NEVER &&
449						!message.getBody().startsWith(ME_COMMAND) &&
450						!this.getBody().startsWith(ME_COMMAND) &&
451						!this.bodyIsHeart() &&
452						!message.bodyIsHeart() &&
453						this.isTrusted() == message.isTrusted()
454				);
455	}
456
457	private static boolean isStatusMergeable(int a, int b) {
458		return a == b || (
459				(a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_UNSEND)
460						|| (a == Message.STATUS_SEND_RECEIVED && b == Message.STATUS_SEND)
461						|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND)
462						|| (a == Message.STATUS_UNSEND && b == Message.STATUS_SEND_RECEIVED)
463						|| (a == Message.STATUS_SEND && b == Message.STATUS_UNSEND)
464						|| (a == Message.STATUS_SEND && b == Message.STATUS_SEND_RECEIVED)
465		);
466	}
467
468	public String getMergedBody() {
469		StringBuilder body = new StringBuilder(this.body.trim());
470		Message current = this;
471		while(current.mergeable(current.next())) {
472			current = current.next();
473			body.append(MERGE_SEPARATOR);
474			body.append(current.getBody().trim());
475		}
476		return body.toString();
477	}
478
479	public boolean hasMeCommand() {
480		return getMergedBody().startsWith(ME_COMMAND);
481	}
482
483	public int getMergedStatus() {
484		int status = this.status;
485		Message current = this;
486		while(current.mergeable(current.next())) {
487			current = current.next();
488			status = current.status;
489		}
490		return status;
491	}
492
493	public long getMergedTimeSent() {
494		long time = this.timeSent;
495		Message current = this;
496		while(current.mergeable(current.next())) {
497			current = current.next();
498			time = current.timeSent;
499		}
500		return time;
501	}
502
503	public boolean wasMergedIntoPrevious() {
504		Message prev = this.prev();
505		return prev != null && prev.mergeable(this);
506	}
507
508	public boolean trusted() {
509		Contact contact = this.getContact();
510		return (status > STATUS_RECEIVED || (contact != null && contact.trusted()));
511	}
512
513	public boolean fixCounterpart() {
514		Presences presences = conversation.getContact().getPresences();
515		if (counterpart != null && presences.has(counterpart.getResourcepart())) {
516			return true;
517		} else if (presences.size() >= 1) {
518			try {
519				counterpart = Jid.fromParts(conversation.getJid().getLocalpart(),
520						conversation.getJid().getDomainpart(),
521						presences.asStringArray()[0]);
522				return true;
523			} catch (InvalidJidException e) {
524				counterpart = null;
525				return false;
526			}
527		} else {
528			counterpart = null;
529			return false;
530		}
531	}
532
533	public void setUuid(String uuid) {
534		this.uuid = uuid;
535	}
536
537	public String getEditedId() {
538		return edited;
539	}
540
541	public enum Decision {
542		MUST,
543		SHOULD,
544		NEVER,
545	}
546
547	private static String extractRelevantExtension(URL url) {
548		String path = url.getPath();
549		return extractRelevantExtension(path);
550	}
551
552	private static String extractRelevantExtension(String path) {
553		if (path == null || path.isEmpty()) {
554			return null;
555		}
556		
557		String filename = path.substring(path.lastIndexOf('/') + 1).toLowerCase();
558		int dotPosition = filename.lastIndexOf(".");
559
560		if (dotPosition != -1) {
561			String extension = filename.substring(dotPosition + 1);
562			// we want the real file extension, not the crypto one
563			if (Arrays.asList(Transferable.VALID_CRYPTO_EXTENSIONS).contains(extension)) {
564				return extractRelevantExtension(filename.substring(0,dotPosition));
565			} else {
566				return extension;
567			}
568		}
569		return null;
570	}
571
572	public String getMimeType() {
573		if (relativeFilePath != null) {
574			int start = relativeFilePath.lastIndexOf('.') + 1;
575			if (start < relativeFilePath.length()) {
576				return MimeUtils.guessMimeTypeFromExtension(relativeFilePath.substring(start));
577			} else {
578				return null;
579			}
580		} else {
581			try {
582				return MimeUtils.guessMimeTypeFromExtension(extractRelevantExtension(new URL(body.trim())));
583			} catch (MalformedURLException e) {
584				return null;
585			}
586		}
587	}
588
589	public Decision treatAsDownloadable() {
590		if (body.trim().contains(" ")) {
591			return Decision.NEVER;
592		}
593		try {
594			URL url = new URL(body);
595			if (!url.getProtocol().equalsIgnoreCase("http") && !url.getProtocol().equalsIgnoreCase("https")) {
596				return Decision.NEVER;
597			}
598			String extension = extractRelevantExtension(url);
599			if (extension == null) {
600				return Decision.NEVER;
601			}
602			String ref = url.getRef();
603			boolean encrypted = ref != null && ref.matches("([A-Fa-f0-9]{2}){48}");
604
605			if (encrypted) {
606				if (MimeUtils.guessMimeTypeFromExtension(extension) != null) {
607					return Decision.MUST;
608				} else {
609					return Decision.NEVER;
610				}
611			} else if (Arrays.asList(Transferable.VALID_IMAGE_EXTENSIONS).contains(extension)
612					|| Arrays.asList(Transferable.WELL_KNOWN_EXTENSIONS).contains(extension)) {
613				return Decision.SHOULD;
614			} else {
615				return Decision.NEVER;
616			}
617
618		} catch (MalformedURLException e) {
619			return Decision.NEVER;
620		}
621	}
622
623	public boolean bodyIsHeart() {
624		return body != null && UIHelper.HEARTS.contains(body.trim());
625	}
626
627	public FileParams getFileParams() {
628		FileParams params = getLegacyFileParams();
629		if (params != null) {
630			return params;
631		}
632		params = new FileParams();
633		if (this.transferable != null) {
634			params.size = this.transferable.getFileSize();
635		}
636		if (body == null) {
637			return params;
638		}
639		String parts[] = body.split("\\|");
640		switch (parts.length) {
641			case 1:
642				try {
643					params.size = Long.parseLong(parts[0]);
644				} catch (NumberFormatException e) {
645					try {
646						params.url = new URL(parts[0]);
647					} catch (MalformedURLException e1) {
648						params.url = null;
649					}
650				}
651				break;
652			case 2:
653			case 4:
654				try {
655					params.url = new URL(parts[0]);
656				} catch (MalformedURLException e1) {
657					params.url = null;
658				}
659				try {
660					params.size = Long.parseLong(parts[1]);
661				} catch (NumberFormatException e) {
662					params.size = 0;
663				}
664				try {
665					params.width = Integer.parseInt(parts[2]);
666				} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
667					params.width = 0;
668				}
669				try {
670					params.height = Integer.parseInt(parts[3]);
671				} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
672					params.height = 0;
673				}
674				break;
675			case 3:
676				try {
677					params.size = Long.parseLong(parts[0]);
678				} catch (NumberFormatException e) {
679					params.size = 0;
680				}
681				try {
682					params.width = Integer.parseInt(parts[1]);
683				} catch (NumberFormatException e) {
684					params.width = 0;
685				}
686				try {
687					params.height = Integer.parseInt(parts[2]);
688				} catch (NumberFormatException e) {
689					params.height = 0;
690				}
691				break;
692		}
693		return params;
694	}
695
696	public FileParams getLegacyFileParams() {
697		FileParams params = new FileParams();
698		if (body == null) {
699			return params;
700		}
701		String parts[] = body.split(",");
702		if (parts.length == 3) {
703			try {
704				params.size = Long.parseLong(parts[0]);
705			} catch (NumberFormatException e) {
706				return null;
707			}
708			try {
709				params.width = Integer.parseInt(parts[1]);
710			} catch (NumberFormatException e) {
711				return null;
712			}
713			try {
714				params.height = Integer.parseInt(parts[2]);
715			} catch (NumberFormatException e) {
716				return null;
717			}
718			return params;
719		} else {
720			return null;
721		}
722	}
723
724	public void untie() {
725		this.mNextMessage = null;
726		this.mPreviousMessage = null;
727	}
728
729	public boolean isFileOrImage() {
730		return type == TYPE_FILE || type == TYPE_IMAGE;
731	}
732
733	public boolean hasFileOnRemoteHost() {
734		return isFileOrImage() && getFileParams().url != null;
735	}
736
737	public boolean needsUploading() {
738		return isFileOrImage() && getFileParams().url == null;
739	}
740
741	public class FileParams {
742		public URL url;
743		public long size = 0;
744		public int width = 0;
745		public int height = 0;
746	}
747
748	public void setAxolotlFingerprint(String fingerprint) {
749		this.axolotlFingerprint = fingerprint;
750	}
751
752	public String getAxolotlFingerprint() {
753		return axolotlFingerprint;
754	}
755
756	public boolean isTrusted() {
757		XmppAxolotlSession.Trust t = conversation.getAccount().getAxolotlService().getFingerprintTrust(axolotlFingerprint);
758		return t != null && t.trusted();
759	}
760
761	private  int getPreviousEncryption() {
762		for (Message iterator = this.prev(); iterator != null; iterator = iterator.prev()){
763			if( iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED ) {
764				continue;
765			}
766			return iterator.getEncryption();
767		}
768		return ENCRYPTION_NONE;
769	}
770
771	private int getNextEncryption() {
772		for (Message iterator = this.next(); iterator != null; iterator = iterator.next()){
773			if( iterator.isCarbon() || iterator.getStatus() == STATUS_RECEIVED ) {
774				continue;
775			}
776			return iterator.getEncryption();
777		}
778		return conversation.getNextEncryption();
779	}
780
781	public boolean isValidInSession() {
782		int pastEncryption = getCleanedEncryption(this.getPreviousEncryption());
783		int futureEncryption = getCleanedEncryption(this.getNextEncryption());
784
785		boolean inUnencryptedSession = pastEncryption == ENCRYPTION_NONE
786				|| futureEncryption == ENCRYPTION_NONE
787				|| pastEncryption != futureEncryption;
788
789		return inUnencryptedSession || getCleanedEncryption(this.getEncryption()) == pastEncryption;
790	}
791
792	private static int getCleanedEncryption(int encryption) {
793		if (encryption == ENCRYPTION_DECRYPTED || encryption == ENCRYPTION_DECRYPTION_FAILED) {
794			return ENCRYPTION_PGP;
795		}
796		return encryption;
797	}
798}