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