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