Conversation.java

  1package eu.siacs.conversations.entities;
  2
  3import java.security.interfaces.DSAPublicKey;
  4import java.util.List;
  5import java.util.concurrent.CopyOnWriteArrayList;
  6
  7import eu.siacs.conversations.services.XmppConnectionService;
  8import eu.siacs.conversations.utils.UIHelper;
  9
 10import net.java.otr4j.OtrException;
 11import net.java.otr4j.crypto.OtrCryptoEngineImpl;
 12import net.java.otr4j.crypto.OtrCryptoException;
 13import net.java.otr4j.session.SessionID;
 14import net.java.otr4j.session.SessionImpl;
 15import net.java.otr4j.session.SessionStatus;
 16import android.content.ContentValues;
 17import android.content.Context;
 18import android.database.Cursor;
 19import android.graphics.Bitmap;
 20import android.os.SystemClock;
 21
 22public class Conversation extends AbstractEntity {
 23	public static final String TABLENAME = "conversations";
 24
 25	public static final int STATUS_AVAILABLE = 0;
 26	public static final int STATUS_ARCHIVED = 1;
 27	public static final int STATUS_DELETED = 2;
 28
 29	public static final int MODE_MULTI = 1;
 30	public static final int MODE_SINGLE = 0;
 31
 32	public static final String NAME = "name";
 33	public static final String ACCOUNT = "accountUuid";
 34	public static final String CONTACT = "contactUuid";
 35	public static final String CONTACTJID = "contactJid";
 36	public static final String STATUS = "status";
 37	public static final String CREATED = "created";
 38	public static final String MODE = "mode";
 39
 40	private String name;
 41	private String contactUuid;
 42	private String accountUuid;
 43	private String contactJid;
 44	private int status;
 45	private long created;
 46	private int mode;
 47
 48	private long mutedTill = 0;
 49
 50	private String nextPresence;
 51
 52	private transient CopyOnWriteArrayList<Message> messages = null;
 53	private transient Account account = null;
 54
 55	private transient SessionImpl otrSession;
 56
 57	private transient String otrFingerprint = null;
 58
 59	private int nextMessageEncryption = -1;
 60	private String nextMessage;
 61
 62	private transient MucOptions mucOptions = null;
 63
 64	private transient String latestMarkableMessageId;
 65
 66	private byte[] symmetricKey;
 67
 68	private boolean otrSessionNeedsStarting = false;
 69
 70	private Bookmark bookmark;
 71
 72	public Conversation(String name, Account account, String contactJid,
 73			int mode) {
 74		this(java.util.UUID.randomUUID().toString(), name, null, account
 75				.getUuid(), contactJid, System.currentTimeMillis(),
 76				STATUS_AVAILABLE, mode);
 77		this.account = account;
 78	}
 79
 80	public Conversation(String uuid, String name, String contactUuid,
 81			String accountUuid, String contactJid, long created, int status,
 82			int mode) {
 83		this.uuid = uuid;
 84		this.name = name;
 85		this.contactUuid = contactUuid;
 86		this.accountUuid = accountUuid;
 87		this.contactJid = contactJid;
 88		this.created = created;
 89		this.status = status;
 90		this.mode = mode;
 91	}
 92
 93	public List<Message> getMessages() {
 94		if (messages == null) {
 95			this.messages = new CopyOnWriteArrayList<Message>(); // prevent null
 96																	// pointer
 97		}
 98
 99		// populate with Conversation (this)
100
101		for (Message msg : messages) {
102			msg.setConversation(this);
103		}
104
105		return messages;
106	}
107
108	public boolean isRead() {
109		if ((this.messages == null) || (this.messages.size() == 0))
110			return true;
111		return this.messages.get(this.messages.size() - 1).isRead();
112	}
113
114	public void markRead() {
115		if (this.messages == null) {
116			return;
117		}
118		for (int i = this.messages.size() - 1; i >= 0; --i) {
119			if (messages.get(i).isRead()) {
120				break;
121			}
122			this.messages.get(i).markRead();
123		}
124	}
125
126	public String popLatestMarkableMessageId() {
127		String id = this.latestMarkableMessageId;
128		this.latestMarkableMessageId = null;
129		return id;
130	}
131
132	public Message getLatestMessage() {
133		if ((this.messages == null) || (this.messages.size() == 0)) {
134			Message message = new Message(this, "", Message.ENCRYPTION_NONE);
135			message.setTime(getCreated());
136			return message;
137		} else {
138			Message message = this.messages.get(this.messages.size() - 1);
139			message.setConversation(this);
140			return message;
141		}
142	}
143
144	public void setMessages(CopyOnWriteArrayList<Message> msgs) {
145		this.messages = msgs;
146	}
147
148	public String getName() {
149		if (getMode() == MODE_MULTI && getMucOptions().getSubject() != null) {
150			return getMucOptions().getSubject();
151		} else if (getMode() == MODE_MULTI && bookmark != null
152				&& bookmark.getName() != null) {
153			return bookmark.getName();
154		} else {
155			return this.getContact().getDisplayName();
156		}
157	}
158
159	public String getProfilePhotoString() {
160		return this.getContact().getProfilePhoto();
161	}
162
163	public String getAccountUuid() {
164		return this.accountUuid;
165	}
166
167	public Account getAccount() {
168		return this.account;
169	}
170
171	public Contact getContact() {
172		return this.account.getRoster().getContact(this.contactJid);
173	}
174
175	public void setAccount(Account account) {
176		this.account = account;
177	}
178
179	public String getContactJid() {
180		return this.contactJid;
181	}
182
183	public int getStatus() {
184		return this.status;
185	}
186
187	public long getCreated() {
188		return this.created;
189	}
190
191	public ContentValues getContentValues() {
192		ContentValues values = new ContentValues();
193		values.put(UUID, uuid);
194		values.put(NAME, name);
195		values.put(CONTACT, contactUuid);
196		values.put(ACCOUNT, accountUuid);
197		values.put(CONTACTJID, contactJid);
198		values.put(CREATED, created);
199		values.put(STATUS, status);
200		values.put(MODE, mode);
201		return values;
202	}
203
204	public static Conversation fromCursor(Cursor cursor) {
205		return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
206				cursor.getString(cursor.getColumnIndex(NAME)),
207				cursor.getString(cursor.getColumnIndex(CONTACT)),
208				cursor.getString(cursor.getColumnIndex(ACCOUNT)),
209				cursor.getString(cursor.getColumnIndex(CONTACTJID)),
210				cursor.getLong(cursor.getColumnIndex(CREATED)),
211				cursor.getInt(cursor.getColumnIndex(STATUS)),
212				cursor.getInt(cursor.getColumnIndex(MODE)));
213	}
214
215	public void setStatus(int status) {
216		this.status = status;
217	}
218
219	public int getMode() {
220		return this.mode;
221	}
222
223	public void setMode(int mode) {
224		this.mode = mode;
225	}
226
227	public SessionImpl startOtrSession(XmppConnectionService service,
228			String presence, boolean sendStart) {
229		if (this.otrSession != null) {
230			return this.otrSession;
231		} else {
232			SessionID sessionId = new SessionID(
233					this.getContactJid().split("/",2)[0], presence, "xmpp");
234			this.otrSession = new SessionImpl(sessionId, getAccount()
235					.getOtrEngine(service));
236			try {
237				if (sendStart) {
238					this.otrSession.startSession();
239					this.otrSessionNeedsStarting = false;
240					return this.otrSession;
241				} else {
242					this.otrSessionNeedsStarting = true;
243				}
244				return this.otrSession;
245			} catch (OtrException e) {
246				return null;
247			}
248		}
249
250	}
251
252	public SessionImpl getOtrSession() {
253		return this.otrSession;
254	}
255
256	public void resetOtrSession() {
257		this.otrFingerprint = null;
258		this.otrSessionNeedsStarting = false;
259		this.otrSession = null;
260	}
261
262	public void startOtrIfNeeded() {
263		if (this.otrSession != null && this.otrSessionNeedsStarting) {
264			try {
265				this.otrSession.startSession();
266			} catch (OtrException e) {
267				this.resetOtrSession();
268			}
269		}
270	}
271
272	public void endOtrIfNeeded() {
273		if (this.otrSession != null) {
274			if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
275				try {
276					this.otrSession.endSession();
277					this.resetOtrSession();
278				} catch (OtrException e) {
279					this.resetOtrSession();
280				}
281			} else {
282				this.resetOtrSession();
283			}
284		}
285	}
286
287	public boolean hasValidOtrSession() {
288		return this.otrSession != null;
289	}
290
291	public String getOtrFingerprint() {
292		if (this.otrFingerprint == null) {
293			try {
294				if (getOtrSession() == null) {
295					return "";
296				}
297				DSAPublicKey remotePubKey = (DSAPublicKey) getOtrSession()
298						.getRemotePublicKey();
299				StringBuilder builder = new StringBuilder(
300						new OtrCryptoEngineImpl().getFingerprint(remotePubKey));
301				builder.insert(8, " ");
302				builder.insert(17, " ");
303				builder.insert(26, " ");
304				builder.insert(35, " ");
305				this.otrFingerprint = builder.toString();
306			} catch (OtrCryptoException e) {
307
308			}
309		}
310		return this.otrFingerprint;
311	}
312
313	public synchronized MucOptions getMucOptions() {
314		if (this.mucOptions == null) {
315			this.mucOptions = new MucOptions(this.getAccount());
316		}
317		this.mucOptions.setConversation(this);
318		return this.mucOptions;
319	}
320
321	public void resetMucOptions() {
322		this.mucOptions = null;
323	}
324
325	public void setContactJid(String jid) {
326		this.contactJid = jid;
327	}
328
329	public void setNextPresence(String presence) {
330		this.nextPresence = presence;
331	}
332
333	public String getNextPresence() {
334		return this.nextPresence;
335	}
336
337	public int getLatestEncryption() {
338		int latestEncryption = this.getLatestMessage().getEncryption();
339		if ((latestEncryption == Message.ENCRYPTION_DECRYPTED)
340				|| (latestEncryption == Message.ENCRYPTION_DECRYPTION_FAILED)) {
341			return Message.ENCRYPTION_PGP;
342		} else {
343			return latestEncryption;
344		}
345	}
346
347	public int getNextEncryption(boolean force) {
348		if (this.nextMessageEncryption == -1) {
349			int latest = this.getLatestEncryption();
350			if (latest == Message.ENCRYPTION_NONE) {
351				if (force && getMode() == MODE_SINGLE) {
352					return Message.ENCRYPTION_OTR;
353				} else if (getContact().getPresences().size() == 1) {
354					if (getContact().getOtrFingerprints().size() >= 1) {
355						return Message.ENCRYPTION_OTR;
356					} else {
357						return latest;
358					}
359				} else {
360					return latest;
361				}
362			} else {
363				return latest;
364			}
365		}
366		if (this.nextMessageEncryption == Message.ENCRYPTION_NONE && force
367				&& getMode() == MODE_SINGLE) {
368			return Message.ENCRYPTION_OTR;
369		} else {
370			return this.nextMessageEncryption;
371		}
372	}
373
374	public void setNextEncryption(int encryption) {
375		this.nextMessageEncryption = encryption;
376	}
377
378	public String getNextMessage() {
379		if (this.nextMessage == null) {
380			return "";
381		} else {
382			return this.nextMessage;
383		}
384	}
385
386	public void setNextMessage(String message) {
387		this.nextMessage = message;
388	}
389
390	public void setLatestMarkableMessageId(String id) {
391		if (id != null) {
392			this.latestMarkableMessageId = id;
393		}
394	}
395
396	public void setSymmetricKey(byte[] key) {
397		this.symmetricKey = key;
398	}
399
400	public byte[] getSymmetricKey() {
401		return this.symmetricKey;
402	}
403
404	public void setBookmark(Bookmark bookmark) {
405		this.bookmark = bookmark;
406		this.bookmark.setConversation(this);
407	}
408
409	public void deregisterWithBookmark() {
410		if (this.bookmark != null) {
411			this.bookmark.setConversation(null);
412		}
413	}
414
415	public Bookmark getBookmark() {
416		return this.bookmark;
417	}
418
419	public Bitmap getImage(Context context, int size) {
420		if (mode == MODE_SINGLE) {
421			return getContact().getImage(size, context);
422		} else {
423			return UIHelper.getContactPicture(this, size, context, false);
424		}
425	}
426
427	public boolean hasDuplicateMessage(Message message) {
428		for (int i = this.getMessages().size() - 1; i >= 0; --i) {
429			if (this.messages.get(i).equals(message)) {
430				return true;
431			}
432		}
433		return false;
434	}
435
436	public void setMutedTill(long mutedTill) {
437		this.mutedTill = mutedTill;
438	}
439
440	public boolean isMuted() {
441		return SystemClock.elapsedRealtime() < this.mutedTill;
442	}
443}