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