1package eu.siacs.conversations.persistance;
2
3import android.content.ContentValues;
4import android.content.Context;
5import android.database.Cursor;
6import android.database.DatabaseUtils;
7import android.database.sqlite.SQLiteCantOpenDatabaseException;
8import android.database.sqlite.SQLiteDatabase;
9import android.database.sqlite.SQLiteOpenHelper;
10import android.util.Base64;
11import android.util.Log;
12
13import org.whispersystems.libaxolotl.AxolotlAddress;
14import org.whispersystems.libaxolotl.IdentityKey;
15import org.whispersystems.libaxolotl.IdentityKeyPair;
16import org.whispersystems.libaxolotl.InvalidKeyException;
17import org.whispersystems.libaxolotl.state.PreKeyRecord;
18import org.whispersystems.libaxolotl.state.SessionRecord;
19import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
20
21import java.io.IOException;
22import java.util.ArrayList;
23import java.util.HashSet;
24import java.util.Iterator;
25import java.util.List;
26import java.util.Set;
27import java.util.concurrent.CopyOnWriteArrayList;
28
29import eu.siacs.conversations.Config;
30import eu.siacs.conversations.crypto.axolotl.AxolotlService;
31import eu.siacs.conversations.crypto.axolotl.SQLiteAxolotlStore;
32import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
33import eu.siacs.conversations.entities.Account;
34import eu.siacs.conversations.entities.Contact;
35import eu.siacs.conversations.entities.Conversation;
36import eu.siacs.conversations.entities.Message;
37import eu.siacs.conversations.entities.Roster;
38import eu.siacs.conversations.xmpp.jid.InvalidJidException;
39import eu.siacs.conversations.xmpp.jid.Jid;
40
41public class DatabaseBackend extends SQLiteOpenHelper {
42
43 private static DatabaseBackend instance = null;
44
45 private static final String DATABASE_NAME = "history";
46 private static final int DATABASE_VERSION = 21;
47
48 private static String CREATE_CONTATCS_STATEMENT = "create table "
49 + Contact.TABLENAME + "(" + Contact.ACCOUNT + " TEXT, "
50 + Contact.SERVERNAME + " TEXT, " + Contact.SYSTEMNAME + " TEXT,"
51 + Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
52 + Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
53 + Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
54 + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
55 + Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
56 + Account.TABLENAME + "(" + Account.UUID
57 + ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
58 + Contact.JID + ") ON CONFLICT REPLACE);";
59
60 private static String CREATE_PREKEYS_STATEMENT = "CREATE TABLE "
61 + SQLiteAxolotlStore.PREKEY_TABLENAME + "("
62 + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
63 + SQLiteAxolotlStore.ID + " INTEGER, "
64 + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
65 + SQLiteAxolotlStore.ACCOUNT
66 + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
67 + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
68 + SQLiteAxolotlStore.ID
69 + ") ON CONFLICT REPLACE"
70 + ");";
71
72 private static String CREATE_SIGNED_PREKEYS_STATEMENT = "CREATE TABLE "
73 + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME + "("
74 + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
75 + SQLiteAxolotlStore.ID + " INTEGER, "
76 + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
77 + SQLiteAxolotlStore.ACCOUNT
78 + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
79 + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
80 + SQLiteAxolotlStore.ID
81 + ") ON CONFLICT REPLACE" +
82 ");";
83
84 private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
85 + SQLiteAxolotlStore.SESSION_TABLENAME + "("
86 + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
87 + SQLiteAxolotlStore.NAME + " TEXT, "
88 + SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
89 + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
90 + SQLiteAxolotlStore.ACCOUNT
91 + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
92 + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
93 + SQLiteAxolotlStore.NAME + ", "
94 + SQLiteAxolotlStore.DEVICE_ID
95 + ") ON CONFLICT REPLACE"
96 + ");";
97
98 private static String CREATE_IDENTITIES_STATEMENT = "CREATE TABLE "
99 + SQLiteAxolotlStore.IDENTITIES_TABLENAME + "("
100 + SQLiteAxolotlStore.ACCOUNT + " TEXT, "
101 + SQLiteAxolotlStore.NAME + " TEXT, "
102 + SQLiteAxolotlStore.OWN + " INTEGER, "
103 + SQLiteAxolotlStore.FINGERPRINT + " TEXT, "
104 + SQLiteAxolotlStore.TRUSTED + " INTEGER, "
105 + SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
106 + SQLiteAxolotlStore.ACCOUNT
107 + ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
108 + "UNIQUE( " + SQLiteAxolotlStore.ACCOUNT + ", "
109 + SQLiteAxolotlStore.NAME + ", "
110 + SQLiteAxolotlStore.FINGERPRINT
111 + ") ON CONFLICT IGNORE"
112 + ");";
113
114 private DatabaseBackend(Context context) {
115 super(context, DATABASE_NAME, null, DATABASE_VERSION);
116 }
117
118 @Override
119 public void onCreate(SQLiteDatabase db) {
120 db.execSQL("PRAGMA foreign_keys=ON;");
121 db.execSQL("create table " + Account.TABLENAME + "(" + Account.UUID
122 + " TEXT PRIMARY KEY," + Account.USERNAME + " TEXT,"
123 + Account.SERVER + " TEXT," + Account.PASSWORD + " TEXT,"
124 + Account.DISPLAY_NAME + " TEXT, "
125 + Account.ROSTERVERSION + " TEXT," + Account.OPTIONS
126 + " NUMBER, " + Account.AVATAR + " TEXT, " + Account.KEYS
127 + " TEXT, " + Account.HOSTNAME + " TEXT, " + Account.PORT + " NUMBER DEFAULT 5222)");
128 db.execSQL("create table " + Conversation.TABLENAME + " ("
129 + Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
130 + " TEXT, " + Conversation.CONTACT + " TEXT, "
131 + Conversation.ACCOUNT + " TEXT, " + Conversation.CONTACTJID
132 + " TEXT, " + Conversation.CREATED + " NUMBER, "
133 + Conversation.STATUS + " NUMBER, " + Conversation.MODE
134 + " NUMBER, " + Conversation.ATTRIBUTES + " TEXT, FOREIGN KEY("
135 + Conversation.ACCOUNT + ") REFERENCES " + Account.TABLENAME
136 + "(" + Account.UUID + ") ON DELETE CASCADE);");
137 db.execSQL("create table " + Message.TABLENAME + "( " + Message.UUID
138 + " TEXT PRIMARY KEY, " + Message.CONVERSATION + " TEXT, "
139 + Message.TIME_SENT + " NUMBER, " + Message.COUNTERPART
140 + " TEXT, " + Message.TRUE_COUNTERPART + " TEXT,"
141 + Message.BODY + " TEXT, " + Message.ENCRYPTION + " NUMBER, "
142 + Message.STATUS + " NUMBER," + Message.TYPE + " NUMBER, "
143 + Message.RELATIVE_FILE_PATH + " TEXT, "
144 + Message.SERVER_MSG_ID + " TEXT, "
145 + Message.FINGERPRINT + " TEXT, "
146 + Message.CARBON + " INTEGER, "
147 + Message.READ + " NUMBER DEFAULT 1, "
148 + Message.REMOTE_MSG_ID + " TEXT, FOREIGN KEY("
149 + Message.CONVERSATION + ") REFERENCES "
150 + Conversation.TABLENAME + "(" + Conversation.UUID
151 + ") ON DELETE CASCADE);");
152
153 db.execSQL(CREATE_CONTATCS_STATEMENT);
154 db.execSQL(CREATE_SESSIONS_STATEMENT);
155 db.execSQL(CREATE_PREKEYS_STATEMENT);
156 db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
157 db.execSQL(CREATE_IDENTITIES_STATEMENT);
158 }
159
160 @Override
161 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
162 if (oldVersion < 2 && newVersion >= 2) {
163 db.execSQL("update " + Account.TABLENAME + " set "
164 + Account.OPTIONS + " = " + Account.OPTIONS + " | 8");
165 }
166 if (oldVersion < 3 && newVersion >= 3) {
167 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
168 + Message.TYPE + " NUMBER");
169 }
170 if (oldVersion < 5 && newVersion >= 5) {
171 db.execSQL("DROP TABLE " + Contact.TABLENAME);
172 db.execSQL(CREATE_CONTATCS_STATEMENT);
173 db.execSQL("UPDATE " + Account.TABLENAME + " SET "
174 + Account.ROSTERVERSION + " = NULL");
175 }
176 if (oldVersion < 6 && newVersion >= 6) {
177 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
178 + Message.TRUE_COUNTERPART + " TEXT");
179 }
180 if (oldVersion < 7 && newVersion >= 7) {
181 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
182 + Message.REMOTE_MSG_ID + " TEXT");
183 db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
184 + Contact.AVATAR + " TEXT");
185 db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN "
186 + Account.AVATAR + " TEXT");
187 }
188 if (oldVersion < 8 && newVersion >= 8) {
189 db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
190 + Conversation.ATTRIBUTES + " TEXT");
191 }
192 if (oldVersion < 9 && newVersion >= 9) {
193 db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
194 + Contact.LAST_TIME + " NUMBER");
195 db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
196 + Contact.LAST_PRESENCE + " TEXT");
197 }
198 if (oldVersion < 10 && newVersion >= 10) {
199 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
200 + Message.RELATIVE_FILE_PATH + " TEXT");
201 }
202 if (oldVersion < 11 && newVersion >= 11) {
203 db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
204 + Contact.GROUPS + " TEXT");
205 db.execSQL("delete from " + Contact.TABLENAME);
206 db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
207 }
208 if (oldVersion < 12 && newVersion >= 12) {
209 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
210 + Message.SERVER_MSG_ID + " TEXT");
211 }
212 if (oldVersion < 13 && newVersion >= 13) {
213 db.execSQL("delete from " + Contact.TABLENAME);
214 db.execSQL("update " + Account.TABLENAME + " set " + Account.ROSTERVERSION + " = NULL");
215 }
216 if (oldVersion < 14 && newVersion >= 14) {
217 // migrate db to new, canonicalized JID domainpart representation
218
219 // Conversation table
220 Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME, new String[0]);
221 while (cursor.moveToNext()) {
222 String newJid;
223 try {
224 newJid = Jid.fromString(
225 cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
226 ).toString();
227 } catch (InvalidJidException ignored) {
228 Log.e(Config.LOGTAG, "Failed to migrate Conversation CONTACTJID "
229 + cursor.getString(cursor.getColumnIndex(Conversation.CONTACTJID))
230 + ": " + ignored + ". Skipping...");
231 continue;
232 }
233
234 String updateArgs[] = {
235 newJid,
236 cursor.getString(cursor.getColumnIndex(Conversation.UUID)),
237 };
238 db.execSQL("update " + Conversation.TABLENAME
239 + " set " + Conversation.CONTACTJID + " = ? "
240 + " where " + Conversation.UUID + " = ?", updateArgs);
241 }
242 cursor.close();
243
244 // Contact table
245 cursor = db.rawQuery("select * from " + Contact.TABLENAME, new String[0]);
246 while (cursor.moveToNext()) {
247 String newJid;
248 try {
249 newJid = Jid.fromString(
250 cursor.getString(cursor.getColumnIndex(Contact.JID))
251 ).toString();
252 } catch (InvalidJidException ignored) {
253 Log.e(Config.LOGTAG, "Failed to migrate Contact JID "
254 + cursor.getString(cursor.getColumnIndex(Contact.JID))
255 + ": " + ignored + ". Skipping...");
256 continue;
257 }
258
259 String updateArgs[] = {
260 newJid,
261 cursor.getString(cursor.getColumnIndex(Contact.ACCOUNT)),
262 cursor.getString(cursor.getColumnIndex(Contact.JID)),
263 };
264 db.execSQL("update " + Contact.TABLENAME
265 + " set " + Contact.JID + " = ? "
266 + " where " + Contact.ACCOUNT + " = ? "
267 + " AND " + Contact.JID + " = ?", updateArgs);
268 }
269 cursor.close();
270
271 // Account table
272 cursor = db.rawQuery("select * from " + Account.TABLENAME, new String[0]);
273 while (cursor.moveToNext()) {
274 String newServer;
275 try {
276 newServer = Jid.fromParts(
277 cursor.getString(cursor.getColumnIndex(Account.USERNAME)),
278 cursor.getString(cursor.getColumnIndex(Account.SERVER)),
279 "mobile"
280 ).getDomainpart();
281 } catch (InvalidJidException ignored) {
282 Log.e(Config.LOGTAG, "Failed to migrate Account SERVER "
283 + cursor.getString(cursor.getColumnIndex(Account.SERVER))
284 + ": " + ignored + ". Skipping...");
285 continue;
286 }
287
288 String updateArgs[] = {
289 newServer,
290 cursor.getString(cursor.getColumnIndex(Account.UUID)),
291 };
292 db.execSQL("update " + Account.TABLENAME
293 + " set " + Account.SERVER + " = ? "
294 + " where " + Account.UUID + " = ?", updateArgs);
295 }
296 cursor.close();
297 }
298 if (oldVersion < 15 && newVersion >= 15) {
299 recreateAxolotlDb(db);
300 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
301 + Message.FINGERPRINT + " TEXT");
302 }
303 if (oldVersion < 16 && newVersion >= 16) {
304 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
305 + Message.CARBON + " INTEGER");
306 }
307 if (oldVersion < 19 && newVersion >= 19) {
308 db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.DISPLAY_NAME + " TEXT");
309 }
310 if (oldVersion < 20 && newVersion >= 20) {
311 db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.HOSTNAME + " TEXT");
312 db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PORT + " NUMBER DEFAULT 5222");
313 }
314 /* Any migrations that alter the Account table need to happen BEFORE this migration, as it
315 * depends on account de-serialization.
316 */
317 if (oldVersion < 17 && newVersion >= 17) {
318 List<Account> accounts = getAccounts(db);
319 for (Account account : accounts) {
320 String ownDeviceIdString = account.getKey(SQLiteAxolotlStore.JSONKEY_REGISTRATION_ID);
321 if (ownDeviceIdString == null) {
322 continue;
323 }
324 int ownDeviceId = Integer.valueOf(ownDeviceIdString);
325 AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), ownDeviceId);
326 deleteSession(db, account, ownAddress);
327 IdentityKeyPair identityKeyPair = loadOwnIdentityKeyPair(db, account);
328 if (identityKeyPair != null) {
329 setIdentityKeyTrust(db, account, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), XmppAxolotlSession.Trust.TRUSTED);
330 } else {
331 Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": could not load own identity key pair");
332 }
333 }
334 }
335 if (oldVersion < 18 && newVersion >= 18) {
336 db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN " + Message.READ + " NUMBER DEFAULT 1");
337 }
338
339 if (oldVersion < 21 && newVersion >= 21) {
340 List<Account> accounts = getAccounts(db);
341 for (Account account : accounts) {
342 account.unsetPgpSignature();
343 db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
344 + "=?", new String[]{account.getUuid()});
345 }
346 }
347 }
348
349 public static synchronized DatabaseBackend getInstance(Context context) {
350 if (instance == null) {
351 instance = new DatabaseBackend(context);
352 }
353 return instance;
354 }
355
356 public void createConversation(Conversation conversation) {
357 SQLiteDatabase db = this.getWritableDatabase();
358 db.insert(Conversation.TABLENAME, null, conversation.getContentValues());
359 }
360
361 public void createMessage(Message message) {
362 SQLiteDatabase db = this.getWritableDatabase();
363 db.insert(Message.TABLENAME, null, message.getContentValues());
364 }
365
366 public void createAccount(Account account) {
367 SQLiteDatabase db = this.getWritableDatabase();
368 db.insert(Account.TABLENAME, null, account.getContentValues());
369 }
370
371 public void createContact(Contact contact) {
372 SQLiteDatabase db = this.getWritableDatabase();
373 db.insert(Contact.TABLENAME, null, contact.getContentValues());
374 }
375
376 public int getConversationCount() {
377 SQLiteDatabase db = this.getReadableDatabase();
378 Cursor cursor = db.rawQuery("select count(uuid) as count from "
379 + Conversation.TABLENAME + " where " + Conversation.STATUS
380 + "=" + Conversation.STATUS_AVAILABLE, null);
381 cursor.moveToFirst();
382 int count = cursor.getInt(0);
383 cursor.close();
384 return count;
385 }
386
387 public CopyOnWriteArrayList<Conversation> getConversations(int status) {
388 CopyOnWriteArrayList<Conversation> list = new CopyOnWriteArrayList<>();
389 SQLiteDatabase db = this.getReadableDatabase();
390 String[] selectionArgs = {Integer.toString(status)};
391 Cursor cursor = db.rawQuery("select * from " + Conversation.TABLENAME
392 + " where " + Conversation.STATUS + " = ? order by "
393 + Conversation.CREATED + " desc", selectionArgs);
394 while (cursor.moveToNext()) {
395 list.add(Conversation.fromCursor(cursor));
396 }
397 cursor.close();
398 return list;
399 }
400
401 public ArrayList<Message> getMessages(Conversation conversations, int limit) {
402 return getMessages(conversations, limit, -1);
403 }
404
405 public ArrayList<Message> getMessages(Conversation conversation, int limit,
406 long timestamp) {
407 ArrayList<Message> list = new ArrayList<>();
408 SQLiteDatabase db = this.getReadableDatabase();
409 Cursor cursor;
410 if (timestamp == -1) {
411 String[] selectionArgs = {conversation.getUuid()};
412 cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
413 + "=?", selectionArgs, null, null, Message.TIME_SENT
414 + " DESC", String.valueOf(limit));
415 } else {
416 String[] selectionArgs = {conversation.getUuid(),
417 Long.toString(timestamp)};
418 cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
419 + "=? and " + Message.TIME_SENT + "<?", selectionArgs,
420 null, null, Message.TIME_SENT + " DESC",
421 String.valueOf(limit));
422 }
423 if (cursor.getCount() > 0) {
424 cursor.moveToLast();
425 do {
426 Message message = Message.fromCursor(cursor);
427 message.setConversation(conversation);
428 list.add(message);
429 } while (cursor.moveToPrevious());
430 }
431 cursor.close();
432 return list;
433 }
434
435 public Iterable<Message> getMessagesIterable(final Conversation conversation) {
436 return new Iterable<Message>() {
437 @Override
438 public Iterator<Message> iterator() {
439 class MessageIterator implements Iterator<Message> {
440 SQLiteDatabase db = getReadableDatabase();
441 String[] selectionArgs = {conversation.getUuid()};
442 Cursor cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
443 + "=?", selectionArgs, null, null, Message.TIME_SENT
444 + " ASC", null);
445
446 public MessageIterator() {
447 cursor.moveToFirst();
448 }
449
450 @Override
451 public boolean hasNext() {
452 return !cursor.isAfterLast();
453 }
454
455 @Override
456 public Message next() {
457 Message message = Message.fromCursor(cursor);
458 cursor.moveToNext();
459 return message;
460 }
461
462 @Override
463 public void remove() {
464 throw new UnsupportedOperationException();
465 }
466 }
467 return new MessageIterator();
468 }
469 };
470 }
471
472 public Conversation findConversation(final Account account, final Jid contactJid) {
473 SQLiteDatabase db = this.getReadableDatabase();
474 String[] selectionArgs = {account.getUuid(),
475 contactJid.toBareJid().toString() + "/%",
476 contactJid.toBareJid().toString()
477 };
478 Cursor cursor = db.query(Conversation.TABLENAME, null,
479 Conversation.ACCOUNT + "=? AND (" + Conversation.CONTACTJID
480 + " like ? OR " + Conversation.CONTACTJID + "=?)", selectionArgs, null, null, null);
481 if (cursor.getCount() == 0)
482 return null;
483 cursor.moveToFirst();
484 Conversation conversation = Conversation.fromCursor(cursor);
485 cursor.close();
486 return conversation;
487 }
488
489 public void updateConversation(final Conversation conversation) {
490 final SQLiteDatabase db = this.getWritableDatabase();
491 final String[] args = {conversation.getUuid()};
492 db.update(Conversation.TABLENAME, conversation.getContentValues(),
493 Conversation.UUID + "=?", args);
494 }
495
496 public List<Account> getAccounts() {
497 SQLiteDatabase db = this.getReadableDatabase();
498 return getAccounts(db);
499 }
500
501 private List<Account> getAccounts(SQLiteDatabase db) {
502 List<Account> list = new ArrayList<>();
503 Cursor cursor = db.query(Account.TABLENAME, null, null, null, null,
504 null, null);
505 while (cursor.moveToNext()) {
506 list.add(Account.fromCursor(cursor));
507 }
508 cursor.close();
509 return list;
510 }
511
512 public void updateAccount(Account account) {
513 SQLiteDatabase db = this.getWritableDatabase();
514 String[] args = {account.getUuid()};
515 db.update(Account.TABLENAME, account.getContentValues(), Account.UUID
516 + "=?", args);
517 }
518
519 public void deleteAccount(Account account) {
520 SQLiteDatabase db = this.getWritableDatabase();
521 String[] args = {account.getUuid()};
522 db.delete(Account.TABLENAME, Account.UUID + "=?", args);
523 }
524
525 public boolean hasEnabledAccounts() {
526 SQLiteDatabase db = this.getReadableDatabase();
527 Cursor cursor = db.rawQuery("select count(" + Account.UUID + ") from "
528 + Account.TABLENAME + " where not options & (1 <<1)", null);
529 try {
530 cursor.moveToFirst();
531 int count = cursor.getInt(0);
532 cursor.close();
533 return (count > 0);
534 } catch (SQLiteCantOpenDatabaseException e) {
535 return true; // better safe than sorry
536 } catch (RuntimeException e) {
537 return true; // better safe than sorry
538 }
539 }
540
541 @Override
542 public SQLiteDatabase getWritableDatabase() {
543 SQLiteDatabase db = super.getWritableDatabase();
544 db.execSQL("PRAGMA foreign_keys=ON;");
545 return db;
546 }
547
548 public void updateMessage(Message message) {
549 SQLiteDatabase db = this.getWritableDatabase();
550 String[] args = {message.getUuid()};
551 db.update(Message.TABLENAME, message.getContentValues(), Message.UUID
552 + "=?", args);
553 }
554
555 public void readRoster(Roster roster) {
556 SQLiteDatabase db = this.getReadableDatabase();
557 Cursor cursor;
558 String args[] = {roster.getAccount().getUuid()};
559 cursor = db.query(Contact.TABLENAME, null, Contact.ACCOUNT + "=?", args, null, null, null);
560 while (cursor.moveToNext()) {
561 roster.initContact(Contact.fromCursor(cursor));
562 }
563 cursor.close();
564 }
565
566 public void writeRoster(final Roster roster) {
567 final Account account = roster.getAccount();
568 final SQLiteDatabase db = this.getWritableDatabase();
569 for (Contact contact : roster.getContacts()) {
570 if (contact.getOption(Contact.Options.IN_ROSTER)) {
571 db.insert(Contact.TABLENAME, null, contact.getContentValues());
572 } else {
573 String where = Contact.ACCOUNT + "=? AND " + Contact.JID + "=?";
574 String[] whereArgs = {account.getUuid(), contact.getJid().toString()};
575 db.delete(Contact.TABLENAME, where, whereArgs);
576 }
577 }
578 account.setRosterVersion(roster.getVersion());
579 updateAccount(account);
580 }
581
582 public void deleteMessage(Message message) {
583 SQLiteDatabase db = this.getWritableDatabase();
584 String[] args = {message.getUuid()};
585 db.delete(Message.TABLENAME, Message.UUID + "=?", args);
586 }
587
588 public void deleteMessagesInConversation(Conversation conversation) {
589 SQLiteDatabase db = this.getWritableDatabase();
590 String[] args = {conversation.getUuid()};
591 db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args);
592 }
593
594 public Conversation findConversationByUuid(String conversationUuid) {
595 SQLiteDatabase db = this.getReadableDatabase();
596 String[] selectionArgs = {conversationUuid};
597 Cursor cursor = db.query(Conversation.TABLENAME, null,
598 Conversation.UUID + "=?", selectionArgs, null, null, null);
599 if (cursor.getCount() == 0) {
600 return null;
601 }
602 cursor.moveToFirst();
603 Conversation conversation = Conversation.fromCursor(cursor);
604 cursor.close();
605 return conversation;
606 }
607
608 public Message findMessageByUuid(String messageUuid) {
609 SQLiteDatabase db = this.getReadableDatabase();
610 String[] selectionArgs = {messageUuid};
611 Cursor cursor = db.query(Message.TABLENAME, null, Message.UUID + "=?",
612 selectionArgs, null, null, null);
613 if (cursor.getCount() == 0) {
614 return null;
615 }
616 cursor.moveToFirst();
617 Message message = Message.fromCursor(cursor);
618 cursor.close();
619 return message;
620 }
621
622 public Account findAccountByUuid(String accountUuid) {
623 SQLiteDatabase db = this.getReadableDatabase();
624 String[] selectionArgs = {accountUuid};
625 Cursor cursor = db.query(Account.TABLENAME, null, Account.UUID + "=?",
626 selectionArgs, null, null, null);
627 if (cursor.getCount() == 0) {
628 return null;
629 }
630 cursor.moveToFirst();
631 Account account = Account.fromCursor(cursor);
632 cursor.close();
633 return account;
634 }
635
636 public List<Message> getImageMessages(Conversation conversation) {
637 ArrayList<Message> list = new ArrayList<>();
638 SQLiteDatabase db = this.getReadableDatabase();
639 Cursor cursor;
640 String[] selectionArgs = {conversation.getUuid(), String.valueOf(Message.TYPE_IMAGE)};
641 cursor = db.query(Message.TABLENAME, null, Message.CONVERSATION
642 + "=? AND " + Message.TYPE + "=?", selectionArgs, null, null, null);
643 if (cursor.getCount() > 0) {
644 cursor.moveToLast();
645 do {
646 Message message = Message.fromCursor(cursor);
647 message.setConversation(conversation);
648 list.add(message);
649 } while (cursor.moveToPrevious());
650 }
651 cursor.close();
652 return list;
653 }
654
655 private Cursor getCursorForSession(Account account, AxolotlAddress contact) {
656 final SQLiteDatabase db = this.getReadableDatabase();
657 String[] columns = null;
658 String[] selectionArgs = {account.getUuid(),
659 contact.getName(),
660 Integer.toString(contact.getDeviceId())};
661 Cursor cursor = db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
662 columns,
663 SQLiteAxolotlStore.ACCOUNT + " = ? AND "
664 + SQLiteAxolotlStore.NAME + " = ? AND "
665 + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
666 selectionArgs,
667 null, null, null);
668
669 return cursor;
670 }
671
672 public SessionRecord loadSession(Account account, AxolotlAddress contact) {
673 SessionRecord session = null;
674 Cursor cursor = getCursorForSession(account, contact);
675 if (cursor.getCount() != 0) {
676 cursor.moveToFirst();
677 try {
678 session = new SessionRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
679 } catch (IOException e) {
680 cursor.close();
681 throw new AssertionError(e);
682 }
683 }
684 cursor.close();
685 return session;
686 }
687
688 public List<Integer> getSubDeviceSessions(Account account, AxolotlAddress contact) {
689 final SQLiteDatabase db = this.getReadableDatabase();
690 return getSubDeviceSessions(db, account, contact);
691 }
692
693 private List<Integer> getSubDeviceSessions(SQLiteDatabase db, Account account, AxolotlAddress contact) {
694 List<Integer> devices = new ArrayList<>();
695 String[] columns = {SQLiteAxolotlStore.DEVICE_ID};
696 String[] selectionArgs = {account.getUuid(),
697 contact.getName()};
698 Cursor cursor = db.query(SQLiteAxolotlStore.SESSION_TABLENAME,
699 columns,
700 SQLiteAxolotlStore.ACCOUNT + " = ? AND "
701 + SQLiteAxolotlStore.NAME + " = ?",
702 selectionArgs,
703 null, null, null);
704
705 while (cursor.moveToNext()) {
706 devices.add(cursor.getInt(
707 cursor.getColumnIndex(SQLiteAxolotlStore.DEVICE_ID)));
708 }
709
710 cursor.close();
711 return devices;
712 }
713
714 public boolean containsSession(Account account, AxolotlAddress contact) {
715 Cursor cursor = getCursorForSession(account, contact);
716 int count = cursor.getCount();
717 cursor.close();
718 return count != 0;
719 }
720
721 public void storeSession(Account account, AxolotlAddress contact, SessionRecord session) {
722 SQLiteDatabase db = this.getWritableDatabase();
723 ContentValues values = new ContentValues();
724 values.put(SQLiteAxolotlStore.NAME, contact.getName());
725 values.put(SQLiteAxolotlStore.DEVICE_ID, contact.getDeviceId());
726 values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(session.serialize(), Base64.DEFAULT));
727 values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
728 db.insert(SQLiteAxolotlStore.SESSION_TABLENAME, null, values);
729 }
730
731 public void deleteSession(Account account, AxolotlAddress contact) {
732 SQLiteDatabase db = this.getWritableDatabase();
733 deleteSession(db, account, contact);
734 }
735
736 private void deleteSession(SQLiteDatabase db, Account account, AxolotlAddress contact) {
737 String[] args = {account.getUuid(),
738 contact.getName(),
739 Integer.toString(contact.getDeviceId())};
740 db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
741 SQLiteAxolotlStore.ACCOUNT + " = ? AND "
742 + SQLiteAxolotlStore.NAME + " = ? AND "
743 + SQLiteAxolotlStore.DEVICE_ID + " = ? ",
744 args);
745 }
746
747 public void deleteAllSessions(Account account, AxolotlAddress contact) {
748 SQLiteDatabase db = this.getWritableDatabase();
749 String[] args = {account.getUuid(), contact.getName()};
750 db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
751 SQLiteAxolotlStore.ACCOUNT + "=? AND "
752 + SQLiteAxolotlStore.NAME + " = ?",
753 args);
754 }
755
756 private Cursor getCursorForPreKey(Account account, int preKeyId) {
757 SQLiteDatabase db = this.getReadableDatabase();
758 String[] columns = {SQLiteAxolotlStore.KEY};
759 String[] selectionArgs = {account.getUuid(), Integer.toString(preKeyId)};
760 Cursor cursor = db.query(SQLiteAxolotlStore.PREKEY_TABLENAME,
761 columns,
762 SQLiteAxolotlStore.ACCOUNT + "=? AND "
763 + SQLiteAxolotlStore.ID + "=?",
764 selectionArgs,
765 null, null, null);
766
767 return cursor;
768 }
769
770 public PreKeyRecord loadPreKey(Account account, int preKeyId) {
771 PreKeyRecord record = null;
772 Cursor cursor = getCursorForPreKey(account, preKeyId);
773 if (cursor.getCount() != 0) {
774 cursor.moveToFirst();
775 try {
776 record = new PreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
777 } catch (IOException e) {
778 throw new AssertionError(e);
779 }
780 }
781 cursor.close();
782 return record;
783 }
784
785 public boolean containsPreKey(Account account, int preKeyId) {
786 Cursor cursor = getCursorForPreKey(account, preKeyId);
787 int count = cursor.getCount();
788 cursor.close();
789 return count != 0;
790 }
791
792 public void storePreKey(Account account, PreKeyRecord record) {
793 SQLiteDatabase db = this.getWritableDatabase();
794 ContentValues values = new ContentValues();
795 values.put(SQLiteAxolotlStore.ID, record.getId());
796 values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
797 values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
798 db.insert(SQLiteAxolotlStore.PREKEY_TABLENAME, null, values);
799 }
800
801 public void deletePreKey(Account account, int preKeyId) {
802 SQLiteDatabase db = this.getWritableDatabase();
803 String[] args = {account.getUuid(), Integer.toString(preKeyId)};
804 db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
805 SQLiteAxolotlStore.ACCOUNT + "=? AND "
806 + SQLiteAxolotlStore.ID + "=?",
807 args);
808 }
809
810 private Cursor getCursorForSignedPreKey(Account account, int signedPreKeyId) {
811 SQLiteDatabase db = this.getReadableDatabase();
812 String[] columns = {SQLiteAxolotlStore.KEY};
813 String[] selectionArgs = {account.getUuid(), Integer.toString(signedPreKeyId)};
814 Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
815 columns,
816 SQLiteAxolotlStore.ACCOUNT + "=? AND " + SQLiteAxolotlStore.ID + "=?",
817 selectionArgs,
818 null, null, null);
819
820 return cursor;
821 }
822
823 public SignedPreKeyRecord loadSignedPreKey(Account account, int signedPreKeyId) {
824 SignedPreKeyRecord record = null;
825 Cursor cursor = getCursorForSignedPreKey(account, signedPreKeyId);
826 if (cursor.getCount() != 0) {
827 cursor.moveToFirst();
828 try {
829 record = new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
830 } catch (IOException e) {
831 throw new AssertionError(e);
832 }
833 }
834 cursor.close();
835 return record;
836 }
837
838 public List<SignedPreKeyRecord> loadSignedPreKeys(Account account) {
839 List<SignedPreKeyRecord> prekeys = new ArrayList<>();
840 SQLiteDatabase db = this.getReadableDatabase();
841 String[] columns = {SQLiteAxolotlStore.KEY};
842 String[] selectionArgs = {account.getUuid()};
843 Cursor cursor = db.query(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
844 columns,
845 SQLiteAxolotlStore.ACCOUNT + "=?",
846 selectionArgs,
847 null, null, null);
848
849 while (cursor.moveToNext()) {
850 try {
851 prekeys.add(new SignedPreKeyRecord(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT)));
852 } catch (IOException ignored) {
853 }
854 }
855 cursor.close();
856 return prekeys;
857 }
858
859 public boolean containsSignedPreKey(Account account, int signedPreKeyId) {
860 Cursor cursor = getCursorForPreKey(account, signedPreKeyId);
861 int count = cursor.getCount();
862 cursor.close();
863 return count != 0;
864 }
865
866 public void storeSignedPreKey(Account account, SignedPreKeyRecord record) {
867 SQLiteDatabase db = this.getWritableDatabase();
868 ContentValues values = new ContentValues();
869 values.put(SQLiteAxolotlStore.ID, record.getId());
870 values.put(SQLiteAxolotlStore.KEY, Base64.encodeToString(record.serialize(), Base64.DEFAULT));
871 values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
872 db.insert(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME, null, values);
873 }
874
875 public void deleteSignedPreKey(Account account, int signedPreKeyId) {
876 SQLiteDatabase db = this.getWritableDatabase();
877 String[] args = {account.getUuid(), Integer.toString(signedPreKeyId)};
878 db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
879 SQLiteAxolotlStore.ACCOUNT + "=? AND "
880 + SQLiteAxolotlStore.ID + "=?",
881 args);
882 }
883
884 private Cursor getIdentityKeyCursor(Account account, String name, boolean own) {
885 final SQLiteDatabase db = this.getReadableDatabase();
886 return getIdentityKeyCursor(db, account, name, own);
887 }
888
889 private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, boolean own) {
890 return getIdentityKeyCursor(db, account, name, own, null);
891 }
892
893 private Cursor getIdentityKeyCursor(Account account, String fingerprint) {
894 final SQLiteDatabase db = this.getReadableDatabase();
895 return getIdentityKeyCursor(db, account, fingerprint);
896 }
897
898 private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String fingerprint) {
899 return getIdentityKeyCursor(db, account, null, null, fingerprint);
900 }
901
902 private Cursor getIdentityKeyCursor(SQLiteDatabase db, Account account, String name, Boolean own, String fingerprint) {
903 String[] columns = {SQLiteAxolotlStore.TRUSTED,
904 SQLiteAxolotlStore.KEY};
905 ArrayList<String> selectionArgs = new ArrayList<>(4);
906 selectionArgs.add(account.getUuid());
907 String selectionString = SQLiteAxolotlStore.ACCOUNT + " = ?";
908 if (name != null) {
909 selectionArgs.add(name);
910 selectionString += " AND " + SQLiteAxolotlStore.NAME + " = ?";
911 }
912 if (fingerprint != null) {
913 selectionArgs.add(fingerprint);
914 selectionString += " AND " + SQLiteAxolotlStore.FINGERPRINT + " = ?";
915 }
916 if (own != null) {
917 selectionArgs.add(own ? "1" : "0");
918 selectionString += " AND " + SQLiteAxolotlStore.OWN + " = ?";
919 }
920 Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
921 columns,
922 selectionString,
923 selectionArgs.toArray(new String[selectionArgs.size()]),
924 null, null, null);
925
926 return cursor;
927 }
928
929 public IdentityKeyPair loadOwnIdentityKeyPair(Account account) {
930 SQLiteDatabase db = getReadableDatabase();
931 return loadOwnIdentityKeyPair(db, account);
932 }
933
934 private IdentityKeyPair loadOwnIdentityKeyPair(SQLiteDatabase db, Account account) {
935 String name = account.getJid().toBareJid().toString();
936 IdentityKeyPair identityKeyPair = null;
937 Cursor cursor = getIdentityKeyCursor(db, account, name, true);
938 if (cursor.getCount() != 0) {
939 cursor.moveToFirst();
940 try {
941 identityKeyPair = new IdentityKeyPair(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT));
942 } catch (InvalidKeyException e) {
943 Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
944 }
945 }
946 cursor.close();
947
948 return identityKeyPair;
949 }
950
951 public Set<IdentityKey> loadIdentityKeys(Account account, String name) {
952 return loadIdentityKeys(account, name, null);
953 }
954
955 public Set<IdentityKey> loadIdentityKeys(Account account, String name, XmppAxolotlSession.Trust trust) {
956 Set<IdentityKey> identityKeys = new HashSet<>();
957 Cursor cursor = getIdentityKeyCursor(account, name, false);
958
959 while (cursor.moveToNext()) {
960 if (trust != null &&
961 cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.TRUSTED))
962 != trust.getCode()) {
963 continue;
964 }
965 try {
966 identityKeys.add(new IdentityKey(Base64.decode(cursor.getString(cursor.getColumnIndex(SQLiteAxolotlStore.KEY)), Base64.DEFAULT), 0));
967 } catch (InvalidKeyException e) {
968 Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Encountered invalid IdentityKey in database for account" + account.getJid().toBareJid() + ", address: " + name);
969 }
970 }
971 cursor.close();
972
973 return identityKeys;
974 }
975
976 public long numTrustedKeys(Account account, String name) {
977 SQLiteDatabase db = getReadableDatabase();
978 String[] args = {
979 account.getUuid(),
980 name,
981 String.valueOf(XmppAxolotlSession.Trust.TRUSTED.getCode()),
982 String.valueOf(XmppAxolotlSession.Trust.TRUSTED_X509.getCode())
983 };
984 return DatabaseUtils.queryNumEntries(db, SQLiteAxolotlStore.IDENTITIES_TABLENAME,
985 SQLiteAxolotlStore.ACCOUNT + " = ?"
986 + " AND " + SQLiteAxolotlStore.NAME + " = ?"
987 + " AND (" + SQLiteAxolotlStore.TRUSTED + " = ? OR " + SQLiteAxolotlStore.TRUSTED + " = ?)",
988 args
989 );
990 }
991
992 private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized) {
993 storeIdentityKey(account, name, own, fingerprint, base64Serialized, XmppAxolotlSession.Trust.UNDECIDED);
994 }
995
996 private void storeIdentityKey(Account account, String name, boolean own, String fingerprint, String base64Serialized, XmppAxolotlSession.Trust trusted) {
997 SQLiteDatabase db = this.getWritableDatabase();
998 ContentValues values = new ContentValues();
999 values.put(SQLiteAxolotlStore.ACCOUNT, account.getUuid());
1000 values.put(SQLiteAxolotlStore.NAME, name);
1001 values.put(SQLiteAxolotlStore.OWN, own ? 1 : 0);
1002 values.put(SQLiteAxolotlStore.FINGERPRINT, fingerprint);
1003 values.put(SQLiteAxolotlStore.KEY, base64Serialized);
1004 values.put(SQLiteAxolotlStore.TRUSTED, trusted.getCode());
1005 db.insert(SQLiteAxolotlStore.IDENTITIES_TABLENAME, null, values);
1006 }
1007
1008 public XmppAxolotlSession.Trust isIdentityKeyTrusted(Account account, String fingerprint) {
1009 Cursor cursor = getIdentityKeyCursor(account, fingerprint);
1010 XmppAxolotlSession.Trust trust = null;
1011 if (cursor.getCount() > 0) {
1012 cursor.moveToFirst();
1013 int trustValue = cursor.getInt(cursor.getColumnIndex(SQLiteAxolotlStore.TRUSTED));
1014 trust = XmppAxolotlSession.Trust.fromCode(trustValue);
1015 }
1016 cursor.close();
1017 return trust;
1018 }
1019
1020 public boolean setIdentityKeyTrust(Account account, String fingerprint, XmppAxolotlSession.Trust trust) {
1021 SQLiteDatabase db = this.getWritableDatabase();
1022 return setIdentityKeyTrust(db, account, fingerprint, trust);
1023 }
1024
1025 private boolean setIdentityKeyTrust(SQLiteDatabase db, Account account, String fingerprint, XmppAxolotlSession.Trust trust) {
1026 String[] selectionArgs = {
1027 account.getUuid(),
1028 fingerprint
1029 };
1030 ContentValues values = new ContentValues();
1031 values.put(SQLiteAxolotlStore.TRUSTED, trust.getCode());
1032 int rows = db.update(SQLiteAxolotlStore.IDENTITIES_TABLENAME, values,
1033 SQLiteAxolotlStore.ACCOUNT + " = ? AND "
1034 + SQLiteAxolotlStore.FINGERPRINT + " = ? ",
1035 selectionArgs);
1036 return rows == 1;
1037 }
1038
1039 public void storeIdentityKey(Account account, String name, IdentityKey identityKey) {
1040 storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
1041 }
1042
1043 public void storeOwnIdentityKeyPair(Account account, IdentityKeyPair identityKeyPair) {
1044 storeIdentityKey(account, account.getJid().toBareJid().toString(), true, identityKeyPair.getPublicKey().getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKeyPair.serialize(), Base64.DEFAULT), XmppAxolotlSession.Trust.TRUSTED);
1045 }
1046
1047 public void recreateAxolotlDb() {
1048 recreateAxolotlDb(getWritableDatabase());
1049 }
1050
1051 public void recreateAxolotlDb(SQLiteDatabase db) {
1052 Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + ">>> (RE)CREATING AXOLOTL DATABASE <<<");
1053 db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SESSION_TABLENAME);
1054 db.execSQL(CREATE_SESSIONS_STATEMENT);
1055 db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.PREKEY_TABLENAME);
1056 db.execSQL(CREATE_PREKEYS_STATEMENT);
1057 db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME);
1058 db.execSQL(CREATE_SIGNED_PREKEYS_STATEMENT);
1059 db.execSQL("DROP TABLE IF EXISTS " + SQLiteAxolotlStore.IDENTITIES_TABLENAME);
1060 db.execSQL(CREATE_IDENTITIES_STATEMENT);
1061 }
1062
1063 public void wipeAxolotlDb(Account account) {
1064 String accountName = account.getUuid();
1065 Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + ">>> WIPING AXOLOTL DATABASE FOR ACCOUNT " + accountName + " <<<");
1066 SQLiteDatabase db = this.getWritableDatabase();
1067 String[] deleteArgs = {
1068 accountName
1069 };
1070 db.delete(SQLiteAxolotlStore.SESSION_TABLENAME,
1071 SQLiteAxolotlStore.ACCOUNT + " = ?",
1072 deleteArgs);
1073 db.delete(SQLiteAxolotlStore.PREKEY_TABLENAME,
1074 SQLiteAxolotlStore.ACCOUNT + " = ?",
1075 deleteArgs);
1076 db.delete(SQLiteAxolotlStore.SIGNED_PREKEY_TABLENAME,
1077 SQLiteAxolotlStore.ACCOUNT + " = ?",
1078 deleteArgs);
1079 db.delete(SQLiteAxolotlStore.IDENTITIES_TABLENAME,
1080 SQLiteAxolotlStore.ACCOUNT + " = ?",
1081 deleteArgs);
1082 }
1083}