1package eu.siacs.conversations.entities;
2
3import java.security.interfaces.DSAPublicKey;
4import java.util.ArrayList;
5import java.util.List;
6
7import eu.siacs.conversations.services.XmppConnectionService;
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.net.Uri;
19
20public class Conversation extends AbstractEntity {
21
22 private static final long serialVersionUID = -6727528868973996739L;
23
24 public static final String TABLENAME = "conversations";
25
26 public static final int STATUS_AVAILABLE = 0;
27 public static final int STATUS_ARCHIVED = 1;
28 public static final int STATUS_DELETED = 2;
29
30 public static final int MODE_MULTI = 1;
31 public static final int MODE_SINGLE = 0;
32
33 public static final String NAME = "name";
34 public static final String ACCOUNT = "accountUuid";
35 public static final String CONTACT = "contactUuid";
36 public static final String CONTACTJID = "contactJid";
37 public static final String STATUS = "status";
38 public static final String CREATED = "created";
39 public static final String MODE = "mode";
40
41 private String name;
42 private String contactUuid;
43 private String accountUuid;
44 private String contactJid;
45 private int status;
46 private long created;
47 private int mode;
48
49 private String nextPresence;
50
51 private transient List<Message> messages = null;
52 private transient Account account = null;
53
54 private transient SessionImpl otrSession;
55
56 private transient String otrFingerprint = null;
57
58 private int nextMessageEncryption = -1;
59 private String nextMessage;
60
61 private transient MucOptions mucOptions = null;
62
63 private transient String latestMarkableMessageId;
64
65 private byte[] symmetricKey;
66
67 private boolean otrSessionNeedsStarting = false;
68
69 public Conversation(String name, Account account, String contactJid,
70 int mode) {
71 this(java.util.UUID.randomUUID().toString(), name, null, account
72 .getUuid(), contactJid, System.currentTimeMillis(),
73 STATUS_AVAILABLE, mode);
74 this.account = account;
75 }
76
77 public Conversation(String uuid, String name, String contactUuid,
78 String accountUuid, String contactJid, long created, int status,
79 int mode) {
80 this.uuid = uuid;
81 this.name = name;
82 this.contactUuid = contactUuid;
83 this.accountUuid = accountUuid;
84 this.contactJid = contactJid;
85 this.created = created;
86 this.status = status;
87 this.mode = mode;
88 }
89
90 public List<Message> getMessages() {
91 if (messages == null)
92 this.messages = new ArrayList<Message>(); // prevent null pointer
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 void markRead(XmppConnectionService service) {
122 markRead();
123 if (service.confirmMessages() && this.latestMarkableMessageId != null) {
124 service.sendConfirmMessage(getAccount(), getContactJid(),
125 this.latestMarkableMessageId);
126 this.latestMarkableMessageId = null;
127 }
128 }
129
130 public Message getLatestMessage() {
131 if ((this.messages == null) || (this.messages.size() == 0)) {
132 Message message = new Message(this, "", Message.ENCRYPTION_NONE);
133 message.setTime(getCreated());
134 return message;
135 } else {
136 Message message = this.messages.get(this.messages.size() - 1);
137 message.setConversation(this);
138 return message;
139 }
140 }
141
142 public void setMessages(List<Message> msgs) {
143 this.messages = msgs;
144 }
145
146 public String getName(boolean useSubject) {
147 if ((getMode() == MODE_MULTI) && (getMucOptions().getSubject() != null)
148 && useSubject) {
149 return getMucOptions().getSubject();
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 Uri getProfilePhotoUri() {
180 if (this.getProfilePhotoString() != null) {
181 return Uri.parse(this.getProfilePhotoString());
182 }
183 return null;
184 }
185
186 public int getStatus() {
187 return this.status;
188 }
189
190 public long getCreated() {
191 return this.created;
192 }
193
194 public ContentValues getContentValues() {
195 ContentValues values = new ContentValues();
196 values.put(UUID, uuid);
197 values.put(NAME, name);
198 values.put(CONTACT, contactUuid);
199 values.put(ACCOUNT, accountUuid);
200 values.put(CONTACTJID, contactJid);
201 values.put(CREATED, created);
202 values.put(STATUS, status);
203 values.put(MODE, mode);
204 return values;
205 }
206
207 public static Conversation fromCursor(Cursor cursor) {
208 return new Conversation(cursor.getString(cursor.getColumnIndex(UUID)),
209 cursor.getString(cursor.getColumnIndex(NAME)),
210 cursor.getString(cursor.getColumnIndex(CONTACT)),
211 cursor.getString(cursor.getColumnIndex(ACCOUNT)),
212 cursor.getString(cursor.getColumnIndex(CONTACTJID)),
213 cursor.getLong(cursor.getColumnIndex(CREATED)),
214 cursor.getInt(cursor.getColumnIndex(STATUS)),
215 cursor.getInt(cursor.getColumnIndex(MODE)));
216 }
217
218 public void setStatus(int status) {
219 this.status = status;
220 }
221
222 public int getMode() {
223 return this.mode;
224 }
225
226 public void setMode(int mode) {
227 this.mode = mode;
228 }
229
230 public SessionImpl startOtrSession(Context context, String presence,
231 boolean sendStart) {
232 if (this.otrSession != null) {
233 return this.otrSession;
234 } else {
235 SessionID sessionId = new SessionID(this.getContactJid(), presence,
236 "xmpp");
237 this.otrSession = new SessionImpl(sessionId, getAccount()
238 .getOtrEngine(context));
239 try {
240 if (sendStart) {
241 this.otrSession.startSession();
242 this.otrSessionNeedsStarting = false;
243 return this.otrSession;
244 } else {
245 this.otrSessionNeedsStarting = true;
246 }
247 return this.otrSession;
248 } catch (OtrException e) {
249 return null;
250 }
251 }
252
253 }
254
255 public SessionImpl getOtrSession() {
256 return this.otrSession;
257 }
258
259 public void resetOtrSession() {
260 this.otrFingerprint = null;
261 this.otrSessionNeedsStarting = false;
262 this.otrSession = null;
263 }
264
265 public void startOtrIfNeeded() {
266 if (this.otrSession != null && this.otrSessionNeedsStarting) {
267 try {
268 this.otrSession.startSession();
269 } catch (OtrException e) {
270 this.resetOtrSession();
271 }
272 }
273 }
274
275 public void endOtrIfNeeded() {
276 if (this.otrSession != null) {
277 if (this.otrSession.getSessionStatus() == SessionStatus.ENCRYPTED) {
278 try {
279 this.otrSession.endSession();
280 this.resetOtrSession();
281 } catch (OtrException e) {
282 this.resetOtrSession();
283 }
284 } else {
285 this.resetOtrSession();
286 }
287 }
288 }
289
290 public boolean hasValidOtrSession() {
291 return this.otrSession != null;
292 }
293
294 public String getOtrFingerprint() {
295 if (this.otrFingerprint == null) {
296 try {
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() {
348 if (this.nextMessageEncryption == -1) {
349 return this.getLatestEncryption();
350 }
351 return this.nextMessageEncryption;
352 }
353
354 public void setNextEncryption(int encryption) {
355 this.nextMessageEncryption = encryption;
356 }
357
358 public String getNextMessage() {
359 if (this.nextMessage == null) {
360 return "";
361 } else {
362 return this.nextMessage;
363 }
364 }
365
366 public void setNextMessage(String message) {
367 this.nextMessage = message;
368 }
369
370 public void setLatestMarkableMessageId(String id) {
371 if (id != null) {
372 this.latestMarkableMessageId = id;
373 }
374 }
375
376 public void setSymmetricKey(byte[] key) {
377 this.symmetricKey = key;
378 }
379
380 public byte[] getSymmetricKey() {
381 return this.symmetricKey;
382 }
383}