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("/")[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 if (latestEncryption == Message.ENCRYPTION_NONE) {
343			if (getContact().getPresences().size() == 1) {
344				if (getContact().getOtrFingerprints().size() >= 1) {
345					return Message.ENCRYPTION_OTR;
346				} else {
347					return latestEncryption;
348				}
349			} else {
350				return latestEncryption;
351			}
352		} else {
353			return latestEncryption;
354		}
355	}
356
357	public int getNextEncryption() {
358		if (this.nextMessageEncryption == -1) {
359			return this.getLatestEncryption();
360		}
361		return this.nextMessageEncryption;
362	}
363
364	public void setNextEncryption(int encryption) {
365		this.nextMessageEncryption = encryption;
366	}
367
368	public String getNextMessage() {
369		if (this.nextMessage == null) {
370			return "";
371		} else {
372			return this.nextMessage;
373		}
374	}
375
376	public void setNextMessage(String message) {
377		this.nextMessage = message;
378	}
379
380	public void setLatestMarkableMessageId(String id) {
381		if (id != null) {
382			this.latestMarkableMessageId = id;
383		}
384	}
385
386	public void setSymmetricKey(byte[] key) {
387		this.symmetricKey = key;
388	}
389
390	public byte[] getSymmetricKey() {
391		return this.symmetricKey;
392	}
393
394	public void setBookmark(Bookmark bookmark) {
395		this.bookmark = bookmark;
396		this.bookmark.setConversation(this);
397	}
398
399	public void deregisterWithBookmark() {
400		if (this.bookmark != null) {
401			this.bookmark.setConversation(null);
402		}
403	}
404
405	public Bookmark getBookmark() {
406		return this.bookmark;
407	}
408
409	public Bitmap getImage(Context context, int size) {
410		if (mode == MODE_SINGLE) {
411			return getContact().getImage(size, context);
412		} else {
413			return UIHelper.getContactPicture(this, size, context, false);
414		}
415	}
416
417	public boolean hasDuplicateMessage(Message message) {
418		for (int i = this.getMessages().size() - 1; i >= 0; --i) {
419			if (this.messages.get(i).equals(message)) {
420				return true;
421			}
422		}
423		return false;
424	}
425
426	public void setMutedTill(long mutedTill) {
427		this.mutedTill = mutedTill;
428	}
429
430	public boolean isMuted() {
431		return SystemClock.elapsedRealtime() < this.mutedTill;
432	}
433}