Reformat code to use tabs

Andreas Straub created

This really sucks to do it like this. Sorry. :(

Change summary

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java     | 1650 
src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java |  320 
src/main/java/eu/siacs/conversations/entities/Contact.java                  |   20 
src/main/java/eu/siacs/conversations/generator/IqGenerator.java             |   36 
src/main/java/eu/siacs/conversations/generator/MessageGenerator.java        |   22 
src/main/java/eu/siacs/conversations/parser/IqParser.java                   |   76 
src/main/java/eu/siacs/conversations/parser/MessageParser.java              |   56 
src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java       |   34 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java    |    6 
src/main/java/eu/siacs/conversations/ui/ConversationActivity.java           |   10 
10 files changed, 1,115 insertions(+), 1,115 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -51,832 +51,832 @@ import eu.siacs.conversations.xmpp.stanzas.IqPacket;
 
 public class AxolotlService {
 
-    public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
-    public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
-    public static final String PEP_PREKEYS = PEP_PREFIX + ".prekeys";
-    public static final String PEP_BUNDLE = PEP_PREFIX + ".bundle";
-
-    private final Account account;
-    private final XmppConnectionService mXmppConnectionService;
-    private final SQLiteAxolotlStore axolotlStore;
-    private final SessionMap sessions;
-    private final BundleMap bundleCache;
-    private int ownDeviceId;
-
-    public static class SQLiteAxolotlStore implements AxolotlStore {
-
-        public static final String PREKEY_TABLENAME = "prekeys";
-        public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
-        public static final String SESSION_TABLENAME = "sessions";
-        public static final String ACCOUNT = "account";
-        public static final String DEVICE_ID = "device_id";
-        public static final String ID = "id";
-        public static final String KEY = "key";
-        public static final String NAME = "name";
-        public static final String TRUSTED = "trusted";
-
-        public static final String JSONKEY_IDENTITY_KEY_PAIR = "axolotl_key";
-        public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
-        public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
-
-        private final Account account;
-        private final XmppConnectionService mXmppConnectionService;
-
-        private final IdentityKeyPair identityKeyPair;
-        private final int localRegistrationId;
-        private int currentPreKeyId = 0;
-
-
-        private static IdentityKeyPair generateIdentityKeyPair() {
-            Log.d(Config.LOGTAG, "Generating axolotl IdentityKeyPair...");
-            ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
-            IdentityKeyPair ownKey = new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
-                    identityKeyPairKeys.getPrivateKey());
-            return ownKey;
-        }
-
-        private static int generateRegistrationId() {
-            Log.d(Config.LOGTAG, "Generating axolotl registration ID...");
-            int reg_id = KeyHelper.generateRegistrationId(false);
-            return reg_id;
-        }
-
-        public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
-            this.account = account;
-            this.mXmppConnectionService = service;
-            this.identityKeyPair = loadIdentityKeyPair();
-            this.localRegistrationId = loadRegistrationId();
-            this.currentPreKeyId = loadCurrentPreKeyId();
-            for( SignedPreKeyRecord record:loadSignedPreKeys()) {
-                Log.d(Config.LOGTAG, "Got Axolotl signed prekey record:" + record.getId());
-            }
-        }
-
-        public int getCurrentPreKeyId() {
-            return currentPreKeyId;
-        }
-
-        // --------------------------------------
-        // IdentityKeyStore
-        // --------------------------------------
-
-        private IdentityKeyPair loadIdentityKeyPair() {
-            String serializedKey = this.account.getKey(JSONKEY_IDENTITY_KEY_PAIR);
-            IdentityKeyPair ownKey;
-            if( serializedKey != null ) {
-                try {
-                    ownKey = new IdentityKeyPair(Base64.decode(serializedKey,Base64.DEFAULT));
-                    return ownKey;
-                } catch (InvalidKeyException e) {
-                    Log.d(Config.LOGTAG, "Invalid key stored for account " + account.getJid() + ": " + e.getMessage());
+	public static final String PEP_PREFIX = "eu.siacs.conversations.axolotl";
+	public static final String PEP_DEVICE_LIST = PEP_PREFIX + ".devicelist";
+	public static final String PEP_PREKEYS = PEP_PREFIX + ".prekeys";
+	public static final String PEP_BUNDLE = PEP_PREFIX + ".bundle";
+
+	private final Account account;
+	private final XmppConnectionService mXmppConnectionService;
+	private final SQLiteAxolotlStore axolotlStore;
+	private final SessionMap sessions;
+	private final BundleMap bundleCache;
+	private int ownDeviceId;
+
+	public static class SQLiteAxolotlStore implements AxolotlStore {
+
+		public static final String PREKEY_TABLENAME = "prekeys";
+		public static final String SIGNED_PREKEY_TABLENAME = "signed_prekeys";
+		public static final String SESSION_TABLENAME = "sessions";
+		public static final String ACCOUNT = "account";
+		public static final String DEVICE_ID = "device_id";
+		public static final String ID = "id";
+		public static final String KEY = "key";
+		public static final String NAME = "name";
+		public static final String TRUSTED = "trusted";
+
+		public static final String JSONKEY_IDENTITY_KEY_PAIR = "axolotl_key";
+		public static final String JSONKEY_REGISTRATION_ID = "axolotl_reg_id";
+		public static final String JSONKEY_CURRENT_PREKEY_ID = "axolotl_cur_prekey_id";
+
+		private final Account account;
+		private final XmppConnectionService mXmppConnectionService;
+
+		private final IdentityKeyPair identityKeyPair;
+		private final int localRegistrationId;
+		private int currentPreKeyId = 0;
+
+
+		private static IdentityKeyPair generateIdentityKeyPair() {
+			Log.d(Config.LOGTAG, "Generating axolotl IdentityKeyPair...");
+			ECKeyPair identityKeyPairKeys = Curve.generateKeyPair();
+			IdentityKeyPair ownKey = new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()),
+					identityKeyPairKeys.getPrivateKey());
+			return ownKey;
+		}
+
+		private static int generateRegistrationId() {
+			Log.d(Config.LOGTAG, "Generating axolotl registration ID...");
+			int reg_id = KeyHelper.generateRegistrationId(false);
+			return reg_id;
+		}
+
+		public SQLiteAxolotlStore(Account account, XmppConnectionService service) {
+			this.account = account;
+			this.mXmppConnectionService = service;
+			this.identityKeyPair = loadIdentityKeyPair();
+			this.localRegistrationId = loadRegistrationId();
+			this.currentPreKeyId = loadCurrentPreKeyId();
+			for( SignedPreKeyRecord record:loadSignedPreKeys()) {
+				Log.d(Config.LOGTAG, "Got Axolotl signed prekey record:" + record.getId());
+			}
+		}
+
+		public int getCurrentPreKeyId() {
+			return currentPreKeyId;
+		}
+
+		// --------------------------------------
+		// IdentityKeyStore
+		// --------------------------------------
+
+		private IdentityKeyPair loadIdentityKeyPair() {
+			String serializedKey = this.account.getKey(JSONKEY_IDENTITY_KEY_PAIR);
+			IdentityKeyPair ownKey;
+			if( serializedKey != null ) {
+				try {
+					ownKey = new IdentityKeyPair(Base64.decode(serializedKey,Base64.DEFAULT));
+					return ownKey;
+				} catch (InvalidKeyException e) {
+					Log.d(Config.LOGTAG, "Invalid key stored for account " + account.getJid() + ": " + e.getMessage());
 //                    return null;
-                }
-            } //else {
-                Log.d(Config.LOGTAG, "Could not retrieve axolotl key for account " + account.getJid());
-                ownKey = generateIdentityKeyPair();
-                boolean success = this.account.setKey(JSONKEY_IDENTITY_KEY_PAIR, Base64.encodeToString(ownKey.serialize(), Base64.DEFAULT));
-                if(success) {
-                    mXmppConnectionService.databaseBackend.updateAccount(account);
-                } else {
-                    Log.e(Config.LOGTAG, "Failed to write new key to the database!");
-                }
-            //}
-            return ownKey;
-        }
-
-        private int loadRegistrationId() {
-            String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
-            int reg_id;
-            if (regIdString != null) {
-                reg_id = Integer.valueOf(regIdString);
-            } else {
-                Log.d(Config.LOGTAG, "Could not retrieve axolotl registration id for account " + account.getJid());
-                reg_id = generateRegistrationId();
-                boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID,""+reg_id);
-                if(success) {
-                    mXmppConnectionService.databaseBackend.updateAccount(account);
-                } else {
-                    Log.e(Config.LOGTAG, "Failed to write new key to the database!");
-                }
-            }
-            return reg_id;
-        }
-
-        private int loadCurrentPreKeyId() {
-            String regIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
-            int reg_id;
-            if (regIdString != null) {
-                reg_id = Integer.valueOf(regIdString);
-            } else {
-                Log.d(Config.LOGTAG, "Could not retrieve current prekey id for account " + account.getJid());
-                reg_id = 0;
-            }
-            return reg_id;
-        }
-
-
-        /**
-         * Get the local client's identity key pair.
-         *
-         * @return The local client's persistent identity key pair.
-         */
-        @Override
-        public IdentityKeyPair getIdentityKeyPair() {
-            return identityKeyPair;
-        }
-
-        /**
-         * Return the local client's registration ID.
-         * <p/>
-         * Clients should maintain a registration ID, a random number
-         * between 1 and 16380 that's generated once at install time.
-         *
-         * @return the local client's registration ID.
-         */
-        @Override
-        public int getLocalRegistrationId() {
-            return localRegistrationId;
-        }
-
-        /**
-         * Save a remote client's identity key
-         * <p/>
-         * Store a remote client's identity key as trusted.
-         *
-         * @param name        The name of the remote client.
-         * @param identityKey The remote client's identity key.
-         */
-        @Override
-        public void saveIdentity(String name, IdentityKey identityKey) {
-            try {
-                Jid contactJid = Jid.fromString(name);
-                Conversation conversation = this.mXmppConnectionService.find(this.account, contactJid);
-                if (conversation != null) {
-                    conversation.getContact().addAxolotlIdentityKey(identityKey);
-                    mXmppConnectionService.updateConversationUi();
-                    mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
-                }
-            } catch (final InvalidJidException e) {
-                Log.e(Config.LOGTAG, "Failed to save identityKey for contact name " + name + ": " + e.toString());
-            }
-        }
-
-        /**
-         * Verify a remote client's identity key.
-         * <p/>
-         * Determine whether a remote client's identity is trusted.  Convention is
-         * that the TextSecure protocol is 'trust on first use.'  This means that
-         * an identity key is considered 'trusted' if there is no entry for the recipient
-         * in the local store, or if it matches the saved key for a recipient in the local
-         * store.  Only if it mismatches an entry in the local store is it considered
-         * 'untrusted.'
-         *
-         * @param name        The name of the remote client.
-         * @param identityKey The identity key to verify.
-         * @return true if trusted, false if untrusted.
-         */
-        @Override
-        public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
-            try {
-                Jid contactJid = Jid.fromString(name);
-                Conversation conversation = this.mXmppConnectionService.find(this.account, contactJid);
-                if (conversation != null) {
-                    List<IdentityKey> trustedKeys = conversation.getContact().getAxolotlIdentityKeys();
-                    return trustedKeys.isEmpty() || trustedKeys.contains(identityKey);
-                } else {
-                    return false;
-                }
-            } catch (final InvalidJidException e) {
-                Log.e(Config.LOGTAG, "Failed to save identityKey for contact name" + name + ": " + e.toString());
-                return false;
-            }
-        }
-
-        // --------------------------------------
-        // SessionStore
-        // --------------------------------------
-
-        /**
-         * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
-         * or a new SessionRecord if one does not currently exist.
-         * <p/>
-         * It is important that implementations return a copy of the current durable information.  The
-         * returned SessionRecord may be modified, but those changes should not have an effect on the
-         * durable session state (what is returned by subsequent calls to this method) without the
-         * store method being called here first.
-         *
-         * @param address The name and device ID of the remote client.
-         * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
-         * a new SessionRecord if one does not currently exist.
-         */
-        @Override
-        public SessionRecord loadSession(AxolotlAddress address) {
-            SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
-            return (session!=null)?session:new SessionRecord();
-        }
-
-        /**
-         * Returns all known devices with active sessions for a recipient
-         *
-         * @param name the name of the client.
-         * @return all known sub-devices with active sessions.
-         */
-        @Override
-        public List<Integer> getSubDeviceSessions(String name) {
-            return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
-                    new AxolotlAddress(name,0));
-        }
-
-        /**
-         * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
-         *
-         * @param address the address of the remote client.
-         * @param record  the current SessionRecord for the remote client.
-         */
-        @Override
-        public void storeSession(AxolotlAddress address, SessionRecord record) {
-            mXmppConnectionService.databaseBackend.storeSession(account, address, record);
-        }
-
-        /**
-         * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
-         *
-         * @param address the address of the remote client.
-         * @return true if a {@link SessionRecord} exists, false otherwise.
-         */
-        @Override
-        public boolean containsSession(AxolotlAddress address) {
-            return mXmppConnectionService.databaseBackend.containsSession(account, address);
-        }
-
-        /**
-         * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
-         *
-         * @param address the address of the remote client.
-         */
-        @Override
-        public void deleteSession(AxolotlAddress address) {
-            mXmppConnectionService.databaseBackend.deleteSession(account, address);
-        }
-
-        /**
-         * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
-         *
-         * @param name the name of the remote client.
-         */
-        @Override
-        public void deleteAllSessions(String name) {
-            mXmppConnectionService.databaseBackend.deleteAllSessions(account,
-                    new AxolotlAddress(name, 0));
-        }
-
-        public boolean isTrustedSession(AxolotlAddress address) {
-            return mXmppConnectionService.databaseBackend.isTrustedSession(this.account, address);
-        }
-
-        public void setTrustedSession(AxolotlAddress address, boolean trusted) {
-            mXmppConnectionService.databaseBackend.setTrustedSession(this.account, address,trusted);
-        }
-
-        // --------------------------------------
-        // PreKeyStore
-        // --------------------------------------
-
-        /**
-         * Load a local PreKeyRecord.
-         *
-         * @param preKeyId the ID of the local PreKeyRecord.
-         * @return the corresponding PreKeyRecord.
-         * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
-         */
-        @Override
-        public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
-            PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
-            if(record == null) {
-                throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
-            }
-            return record;
-        }
-
-        /**
-         * Store a local PreKeyRecord.
-         *
-         * @param preKeyId the ID of the PreKeyRecord to store.
-         * @param record   the PreKeyRecord.
-         */
-        @Override
-        public void storePreKey(int preKeyId, PreKeyRecord record) {
-            mXmppConnectionService.databaseBackend.storePreKey(account, record);
-            currentPreKeyId = preKeyId;
-            boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID,Integer.toString(preKeyId));
-            if(success) {
-                mXmppConnectionService.databaseBackend.updateAccount(account);
-            } else {
-                Log.e(Config.LOGTAG, "Failed to write new prekey id to the database!");
-            }
-        }
-
-        /**
-         * @param preKeyId A PreKeyRecord ID.
-         * @return true if the store has a record for the preKeyId, otherwise false.
-         */
-        @Override
-        public boolean containsPreKey(int preKeyId) {
-            return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
-        }
-
-        /**
-         * Delete a PreKeyRecord from local storage.
-         *
-         * @param preKeyId The ID of the PreKeyRecord to remove.
-         */
-        @Override
-        public void removePreKey(int preKeyId) {
-            mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
-        }
-
-        // --------------------------------------
-        // SignedPreKeyStore
-        // --------------------------------------
-
-        /**
-         * Load a local SignedPreKeyRecord.
-         *
-         * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
-         * @return the corresponding SignedPreKeyRecord.
-         * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
-         */
-        @Override
-        public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
-            SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
-            if(record == null) {
-                throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
-            }
-            return record;
-        }
-
-        /**
-         * Load all local SignedPreKeyRecords.
-         *
-         * @return All stored SignedPreKeyRecords.
-         */
-        @Override
-        public List<SignedPreKeyRecord> loadSignedPreKeys() {
-            return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
-        }
-
-        /**
-         * Store a local SignedPreKeyRecord.
-         *
-         * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
-         * @param record         the SignedPreKeyRecord.
-         */
-        @Override
-        public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
-            mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
-        }
-
-        /**
-         * @param signedPreKeyId A SignedPreKeyRecord ID.
-         * @return true if the store has a record for the signedPreKeyId, otherwise false.
-         */
-        @Override
-        public boolean containsSignedPreKey(int signedPreKeyId) {
-            return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
-        }
-
-        /**
-         * Delete a SignedPreKeyRecord from local storage.
-         *
-         * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
-         */
-        @Override
-        public void removeSignedPreKey(int signedPreKeyId) {
-            mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
-        }
-    }
-
-    public static class XmppAxolotlSession {
-        private SessionCipher cipher;
-        private boolean isTrusted = false;
-        private SQLiteAxolotlStore sqLiteAxolotlStore;
-        private AxolotlAddress remoteAddress;
-
-        public XmppAxolotlSession(SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
-            this.cipher = new SessionCipher(store, remoteAddress);
-            this.remoteAddress = remoteAddress;
-            this.sqLiteAxolotlStore = store;
-            this.isTrusted = sqLiteAxolotlStore.isTrustedSession(remoteAddress);
-        }
-
-        public void trust() {
-            sqLiteAxolotlStore.setTrustedSession(remoteAddress, true);
-            this.isTrusted = true;
-        }
-
-        public boolean isTrusted() {
-            return this.isTrusted;
-        }
-
-        public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
-            byte[] plaintext = null;
-            try {
-                try {
-                    PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents());
-                    Log.d(Config.LOGTAG,"PreKeyWhisperMessage ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
-                    plaintext = cipher.decrypt(message);
-                } catch (InvalidMessageException|InvalidVersionException e) {
-                    WhisperMessage message = new WhisperMessage(incomingHeader.getContents());
-                    plaintext = cipher.decrypt(message);
-                } catch (InvalidKeyException|InvalidKeyIdException| UntrustedIdentityException e) {
-                    Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
-                }
-            } catch (LegacyMessageException|InvalidMessageException e) {
-                Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
-            } catch (DuplicateMessageException|NoSessionException e) {
-                Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
-            }
-            return plaintext;
-        }
-
-        public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(byte[] outgoingMessage) {
-            CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
-            XmppAxolotlMessage.XmppAxolotlMessageHeader header =
-                    new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(),
-                            ciphertextMessage.serialize());
-            return header;
-        }
-    }
-
-    private static class AxolotlAddressMap<T> {
-        protected Map<String, Map<Integer,T>> map;
-        protected final Object MAP_LOCK = new Object();
-
-        public AxolotlAddressMap() {
-            this.map = new HashMap<>();
-        }
-
-        public void put(AxolotlAddress address, T value) {
-            synchronized (MAP_LOCK) {
-                Map<Integer, T> devices = map.get(address.getName());
-                if (devices == null) {
-                    devices = new HashMap<>();
-                    map.put(address.getName(), devices);
-                }
-                devices.put(address.getDeviceId(), value);
-            }
-        }
-
-        public T get(AxolotlAddress address) {
-            synchronized (MAP_LOCK) {
-                Map<Integer, T> devices = map.get(address.getName());
-                if(devices == null) {
-                    return null;
-                }
-                return devices.get(address.getDeviceId());
-            }
-        }
-
-        public Map<Integer, T> getAll(AxolotlAddress address) {
-            synchronized (MAP_LOCK) {
-                Map<Integer, T> devices = map.get(address.getName());
-                if(devices == null) {
-                    return new HashMap<>();
-                }
-                return devices;
-            }
-        }
-
-        public boolean hasAny(AxolotlAddress address) {
-            synchronized (MAP_LOCK) {
-                Map<Integer, T> devices = map.get(address.getName());
-                return devices != null && !devices.isEmpty();
-            }
-        }
-
-
-    }
-
-    private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
-
-        public SessionMap(SQLiteAxolotlStore store, Account account) {
-            super();
-            this.fillMap(store, account);
-        }
-
-        private void fillMap(SQLiteAxolotlStore store, Account account) {
-            for(Contact contact:account.getRoster().getContacts()){
-                Jid bareJid = contact.getJid().toBareJid();
-                if(bareJid == null) {
-                    continue; // FIXME: handle this?
-                }
-                String address = bareJid.toString();
-                List<Integer> deviceIDs = store.getSubDeviceSessions(address);
-                for(Integer deviceId:deviceIDs) {
-                    AxolotlAddress axolotlAddress = new AxolotlAddress(address, deviceId);
-                    this.put(axolotlAddress, new XmppAxolotlSession(store, axolotlAddress));
-                }
-            }
-        }
-
-    }
-
-    private static class BundleMap extends AxolotlAddressMap<PreKeyBundle> {
-
-    }
-
-    public AxolotlService(Account account, XmppConnectionService connectionService) {
-        this.mXmppConnectionService = connectionService;
-        this.account = account;
-        this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
-        this.sessions = new SessionMap(axolotlStore, account);
-        this.bundleCache = new BundleMap();
-        this.ownDeviceId = axolotlStore.getLocalRegistrationId();
-    }
-
-    public void trustSession(AxolotlAddress counterpart) {
-        XmppAxolotlSession session = sessions.get(counterpart);
-        if(session != null) {
-            session.trust();
-        }
-    }
-
-    public boolean isTrustedSession(AxolotlAddress counterpart) {
-        XmppAxolotlSession session = sessions.get(counterpart);
-        return session != null && session.isTrusted();
-    }
-
-    private AxolotlAddress getAddressForJid(Jid jid) {
-        return new AxolotlAddress(jid.toString(), 0);
-    }
-
-    private Set<XmppAxolotlSession> findOwnSessions() {
-        AxolotlAddress ownAddress = getAddressForJid(account.getJid());
-        Set<XmppAxolotlSession> ownDeviceSessions = new HashSet<>(this.sessions.getAll(ownAddress).values());
-        return ownDeviceSessions;
-    }
-
-    private Set<XmppAxolotlSession> findSessionsforContact(Contact contact) {
-        AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
-        Set<XmppAxolotlSession> sessions = new HashSet<>(this.sessions.getAll(contactAddress).values());
-        return sessions;
-    }
-
-    private boolean hasAny(Contact contact) {
-        AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
-        return sessions.hasAny(contactAddress);
-    }
-
-    public int getOwnDeviceId() {
-        return ownDeviceId;
-    }
-
-    public void fetchBundleIfNeeded(final Contact contact, final Integer deviceId) {
-        final AxolotlAddress address = new AxolotlAddress(contact.getJid().toString(), deviceId);
-        if (sessions.get(address) != null) {
-            return;
-        }
-
-        synchronized (bundleCache) {
-            PreKeyBundle bundle = bundleCache.get(address);
-            if (bundle == null) {
-                bundle = new PreKeyBundle(0, deviceId, 0, null, 0, null, null, null);
-                bundleCache.put(address, bundle);
-            }
-
-            if(bundle.getPreKey() == null) {
-                Log.d(Config.LOGTAG, "No preKey in cache, fetching...");
-                IqPacket prekeysPacket = mXmppConnectionService.getIqGenerator().retrievePreKeysForDevice(contact.getJid(), deviceId);
-                mXmppConnectionService.sendIqPacket(account, prekeysPacket, new OnIqPacketReceived() {
-                    @Override
-                    public void onIqPacketReceived(Account account, IqPacket packet) {
-                        synchronized (bundleCache) {
-                            Log.d(Config.LOGTAG, "Received preKey IQ packet, processing...");
-                            final IqParser parser = mXmppConnectionService.getIqParser();
-                            final PreKeyBundle bundle = bundleCache.get(address);
-                            final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
-                            if (preKeyBundleList.isEmpty()) {
-                                Log.d(Config.LOGTAG, "preKey IQ packet invalid: " + packet);
-                                return;
-                            }
-                            Random random = new Random();
-                            final PreKeyBundle newBundle = preKeyBundleList.get(random.nextInt(preKeyBundleList.size()));
-                            if (bundle == null || newBundle == null) {
-                                //should never happen
-                                return;
-                            }
-
-                            final PreKeyBundle mergedBundle = new PreKeyBundle(bundle.getRegistrationId(),
-                                    bundle.getDeviceId(), newBundle.getPreKeyId(), newBundle.getPreKey(),
-                                    bundle.getSignedPreKeyId(), bundle.getSignedPreKey(),
-                                    bundle.getSignedPreKeySignature(), bundle.getIdentityKey());
-
-                            bundleCache.put(address, mergedBundle);
-                        }
-                    }
-                });
-            }
-            if(bundle.getIdentityKey() == null) {
-                Log.d(Config.LOGTAG, "No bundle in cache, fetching...");
-                IqPacket bundlePacket = mXmppConnectionService.getIqGenerator().retrieveBundleForDevice(contact.getJid(), deviceId);
-                mXmppConnectionService.sendIqPacket(account, bundlePacket, new OnIqPacketReceived() {
-                    @Override
-                    public void onIqPacketReceived(Account account, IqPacket packet) {
-                        synchronized (bundleCache) {
-                            Log.d(Config.LOGTAG, "Received bundle IQ packet, processing...");
-                            final IqParser parser = mXmppConnectionService.getIqParser();
-                            final PreKeyBundle bundle = bundleCache.get(address);
-                            final PreKeyBundle newBundle = parser.bundle(packet);
-                            if( bundle == null || newBundle == null ) {
-                                Log.d(Config.LOGTAG, "bundle IQ packet invalid: " + packet);
-                                //should never happen
-                                return;
-                            }
-
-                            final PreKeyBundle mergedBundle = new PreKeyBundle(bundle.getRegistrationId(),
-                                    bundle.getDeviceId(), bundle.getPreKeyId(), bundle.getPreKey(),
-                                    newBundle.getSignedPreKeyId(), newBundle.getSignedPreKey(),
-                                    newBundle.getSignedPreKeySignature(), newBundle.getIdentityKey());
-
-                            axolotlStore.saveIdentity(contact.getJid().toBareJid().toString(), newBundle.getIdentityKey());
-                            bundleCache.put(address, mergedBundle);
-                        }
-                    }
-                });
-            }
-        }
-    }
-
-    public void publishOwnDeviceIdIfNeeded() {
-        IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid());
-        mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
-            @Override
-            public void onIqPacketReceived(Account account, IqPacket packet) {
-                Element item = mXmppConnectionService.getIqParser().getItem(packet);
-                List<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
-                if(deviceIds == null) {
-                    deviceIds = new ArrayList<>();
-                }
-                if(!deviceIds.contains(getOwnDeviceId())) {
-                    Log.d(Config.LOGTAG, "Own device " + getOwnDeviceId() + " not in PEP devicelist. Publishing...");
-                    deviceIds.add(getOwnDeviceId());
-                    IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds);
-                    mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
-                        @Override
-                        public void onIqPacketReceived(Account account, IqPacket packet) {
-                            // TODO: implement this!
-                        }
-                    });
-                }
-            }
-        });
-    }
-
-    public void publishBundleIfNeeded() {
-        IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundleForDevice(account.getJid().toBareJid(), ownDeviceId);
-        mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
-            @Override
-            public void onIqPacketReceived(Account account, IqPacket packet) {
-                PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet);
-                if(bundle == null) {
-                    Log.d(Config.LOGTAG, "Bundle " + getOwnDeviceId() + " not in PEP. Publishing...");
-                    int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size();
-                    try {
-                        SignedPreKeyRecord signedPreKeyRecord = KeyHelper.generateSignedPreKey(
-                                axolotlStore.getIdentityKeyPair(), numSignedPreKeys + 1);
-                        axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
-                        IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundle(
-                                signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
-                                ownDeviceId);
-                        mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
-                            @Override
-                            public void onIqPacketReceived(Account account, IqPacket packet) {
-                                // TODO: implement this!
-                                Log.d(Config.LOGTAG, "Published bundle, got: " + packet);
-                            }
-                        });
-                    } catch (InvalidKeyException e) {
-                        Log.e(Config.LOGTAG, "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage());
-                    }
-                }
-            }
-        });
-    }
-
-    public void publishPreKeysIfNeeded() {
-        IqPacket packet = mXmppConnectionService.getIqGenerator().retrievePreKeysForDevice(account.getJid().toBareJid(), ownDeviceId);
-        mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
-            @Override
-            public void onIqPacketReceived(Account account, IqPacket packet) {
-                Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet);
-                if(keys == null || keys.isEmpty()) {
-                    Log.d(Config.LOGTAG, "Prekeys " + getOwnDeviceId() + " not in PEP. Publishing...");
-                    List<PreKeyRecord> preKeyRecords = KeyHelper.generatePreKeys(
-                            axolotlStore.getCurrentPreKeyId(), 100);
-                    for(PreKeyRecord record : preKeyRecords) {
-                        axolotlStore.storePreKey(record.getId(), record);
-                    }
-                    IqPacket publish = mXmppConnectionService.getIqGenerator().publishPreKeys(
-                            preKeyRecords, ownDeviceId);
-
-                    mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
-                        @Override
-                        public void onIqPacketReceived(Account account, IqPacket packet) {
-                            Log.d(Config.LOGTAG, "Published prekeys, got: " + packet);
-                            // TODO: implement this!
-                        }
-                    });
-                }
-            }
-        });
-    }
-
-
-    public boolean isContactAxolotlCapable(Contact contact) {
-        AxolotlAddress address = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
-        return sessions.hasAny(address) || bundleCache.hasAny(address);
-    }
-
-    public void initiateSynchronousSession(Contact contact) {
-
-    }
-
-    private void createSessionsIfNeeded(Contact contact) throws NoSessionsCreatedException {
-        Log.d(Config.LOGTAG, "Creating axolotl sessions if needed...");
-        AxolotlAddress address = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
-        for(Integer deviceId: bundleCache.getAll(address).keySet()) {
-            Log.d(Config.LOGTAG, "Processing device ID: " + deviceId);
-            AxolotlAddress remoteAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), deviceId);
-            if(sessions.get(remoteAddress) == null) {
-                Log.d(Config.LOGTAG, "Building new sesstion for " + deviceId);
-                SessionBuilder builder = new SessionBuilder(this.axolotlStore, remoteAddress);
-                try {
-                    builder.process(bundleCache.get(remoteAddress));
-                    XmppAxolotlSession session = new XmppAxolotlSession(this.axolotlStore, remoteAddress);
-                    sessions.put(remoteAddress, session);
-                } catch (InvalidKeyException e) {
-                    Log.d(Config.LOGTAG, "Error building session for " + deviceId+ ": InvalidKeyException, " +e.getMessage());
-                } catch (UntrustedIdentityException e) {
-                    Log.d(Config.LOGTAG, "Error building session for " + deviceId+ ": UntrustedIdentityException, " +e.getMessage());
-                }
-            } else {
-                Log.d(Config.LOGTAG, "Already have session for " + deviceId);
-            }
-        }
-        if(!this.hasAny(contact)) {
-            Log.e(Config.LOGTAG, "No Axolotl sessions available!");
-            throw new NoSessionsCreatedException(); // FIXME: proper error handling
-        }
-    }
-
-    public XmppAxolotlMessage processSending(Contact contact, String outgoingMessage) throws NoSessionsCreatedException {
-        XmppAxolotlMessage message = new XmppAxolotlMessage(contact, ownDeviceId, outgoingMessage);
-        createSessionsIfNeeded(contact);
-        Log.d(Config.LOGTAG, "Building axolotl foreign headers...");
-
-        for(XmppAxolotlSession session : findSessionsforContact(contact)) {
+				}
+			} //else {
+				Log.d(Config.LOGTAG, "Could not retrieve axolotl key for account " + account.getJid());
+				ownKey = generateIdentityKeyPair();
+				boolean success = this.account.setKey(JSONKEY_IDENTITY_KEY_PAIR, Base64.encodeToString(ownKey.serialize(), Base64.DEFAULT));
+				if(success) {
+					mXmppConnectionService.databaseBackend.updateAccount(account);
+				} else {
+					Log.e(Config.LOGTAG, "Failed to write new key to the database!");
+				}
+			//}
+			return ownKey;
+		}
+
+		private int loadRegistrationId() {
+			String regIdString = this.account.getKey(JSONKEY_REGISTRATION_ID);
+			int reg_id;
+			if (regIdString != null) {
+				reg_id = Integer.valueOf(regIdString);
+			} else {
+				Log.d(Config.LOGTAG, "Could not retrieve axolotl registration id for account " + account.getJid());
+				reg_id = generateRegistrationId();
+				boolean success = this.account.setKey(JSONKEY_REGISTRATION_ID,""+reg_id);
+				if(success) {
+					mXmppConnectionService.databaseBackend.updateAccount(account);
+				} else {
+					Log.e(Config.LOGTAG, "Failed to write new key to the database!");
+				}
+			}
+			return reg_id;
+		}
+
+		private int loadCurrentPreKeyId() {
+			String regIdString = this.account.getKey(JSONKEY_CURRENT_PREKEY_ID);
+			int reg_id;
+			if (regIdString != null) {
+				reg_id = Integer.valueOf(regIdString);
+			} else {
+				Log.d(Config.LOGTAG, "Could not retrieve current prekey id for account " + account.getJid());
+				reg_id = 0;
+			}
+			return reg_id;
+		}
+
+
+		/**
+		 * Get the local client's identity key pair.
+		 *
+		 * @return The local client's persistent identity key pair.
+		 */
+		@Override
+		public IdentityKeyPair getIdentityKeyPair() {
+			return identityKeyPair;
+		}
+
+		/**
+		 * Return the local client's registration ID.
+		 * <p/>
+		 * Clients should maintain a registration ID, a random number
+		 * between 1 and 16380 that's generated once at install time.
+		 *
+		 * @return the local client's registration ID.
+		 */
+		@Override
+		public int getLocalRegistrationId() {
+			return localRegistrationId;
+		}
+
+		/**
+		 * Save a remote client's identity key
+		 * <p/>
+		 * Store a remote client's identity key as trusted.
+		 *
+		 * @param name        The name of the remote client.
+		 * @param identityKey The remote client's identity key.
+		 */
+		@Override
+		public void saveIdentity(String name, IdentityKey identityKey) {
+			try {
+				Jid contactJid = Jid.fromString(name);
+				Conversation conversation = this.mXmppConnectionService.find(this.account, contactJid);
+				if (conversation != null) {
+					conversation.getContact().addAxolotlIdentityKey(identityKey);
+					mXmppConnectionService.updateConversationUi();
+					mXmppConnectionService.syncRosterToDisk(conversation.getAccount());
+				}
+			} catch (final InvalidJidException e) {
+				Log.e(Config.LOGTAG, "Failed to save identityKey for contact name " + name + ": " + e.toString());
+			}
+		}
+
+		/**
+		 * Verify a remote client's identity key.
+		 * <p/>
+		 * Determine whether a remote client's identity is trusted.  Convention is
+		 * that the TextSecure protocol is 'trust on first use.'  This means that
+		 * an identity key is considered 'trusted' if there is no entry for the recipient
+		 * in the local store, or if it matches the saved key for a recipient in the local
+		 * store.  Only if it mismatches an entry in the local store is it considered
+		 * 'untrusted.'
+		 *
+		 * @param name        The name of the remote client.
+		 * @param identityKey The identity key to verify.
+		 * @return true if trusted, false if untrusted.
+		 */
+		@Override
+		public boolean isTrustedIdentity(String name, IdentityKey identityKey) {
+			try {
+				Jid contactJid = Jid.fromString(name);
+				Conversation conversation = this.mXmppConnectionService.find(this.account, contactJid);
+				if (conversation != null) {
+					List<IdentityKey> trustedKeys = conversation.getContact().getAxolotlIdentityKeys();
+					return trustedKeys.isEmpty() || trustedKeys.contains(identityKey);
+				} else {
+					return false;
+				}
+			} catch (final InvalidJidException e) {
+				Log.e(Config.LOGTAG, "Failed to save identityKey for contact name" + name + ": " + e.toString());
+				return false;
+			}
+		}
+
+		// --------------------------------------
+		// SessionStore
+		// --------------------------------------
+
+		/**
+		 * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
+		 * or a new SessionRecord if one does not currently exist.
+		 * <p/>
+		 * It is important that implementations return a copy of the current durable information.  The
+		 * returned SessionRecord may be modified, but those changes should not have an effect on the
+		 * durable session state (what is returned by subsequent calls to this method) without the
+		 * store method being called here first.
+		 *
+		 * @param address The name and device ID of the remote client.
+		 * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
+		 * a new SessionRecord if one does not currently exist.
+		 */
+		@Override
+		public SessionRecord loadSession(AxolotlAddress address) {
+			SessionRecord session = mXmppConnectionService.databaseBackend.loadSession(this.account, address);
+			return (session!=null)?session:new SessionRecord();
+		}
+
+		/**
+		 * Returns all known devices with active sessions for a recipient
+		 *
+		 * @param name the name of the client.
+		 * @return all known sub-devices with active sessions.
+		 */
+		@Override
+		public List<Integer> getSubDeviceSessions(String name) {
+			return mXmppConnectionService.databaseBackend.getSubDeviceSessions(account,
+					new AxolotlAddress(name,0));
+		}
+
+		/**
+		 * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
+		 *
+		 * @param address the address of the remote client.
+		 * @param record  the current SessionRecord for the remote client.
+		 */
+		@Override
+		public void storeSession(AxolotlAddress address, SessionRecord record) {
+			mXmppConnectionService.databaseBackend.storeSession(account, address, record);
+		}
+
+		/**
+		 * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
+		 *
+		 * @param address the address of the remote client.
+		 * @return true if a {@link SessionRecord} exists, false otherwise.
+		 */
+		@Override
+		public boolean containsSession(AxolotlAddress address) {
+			return mXmppConnectionService.databaseBackend.containsSession(account, address);
+		}
+
+		/**
+		 * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
+		 *
+		 * @param address the address of the remote client.
+		 */
+		@Override
+		public void deleteSession(AxolotlAddress address) {
+			mXmppConnectionService.databaseBackend.deleteSession(account, address);
+		}
+
+		/**
+		 * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
+		 *
+		 * @param name the name of the remote client.
+		 */
+		@Override
+		public void deleteAllSessions(String name) {
+			mXmppConnectionService.databaseBackend.deleteAllSessions(account,
+					new AxolotlAddress(name, 0));
+		}
+
+		public boolean isTrustedSession(AxolotlAddress address) {
+			return mXmppConnectionService.databaseBackend.isTrustedSession(this.account, address);
+		}
+
+		public void setTrustedSession(AxolotlAddress address, boolean trusted) {
+			mXmppConnectionService.databaseBackend.setTrustedSession(this.account, address,trusted);
+		}
+
+		// --------------------------------------
+		// PreKeyStore
+		// --------------------------------------
+
+		/**
+		 * Load a local PreKeyRecord.
+		 *
+		 * @param preKeyId the ID of the local PreKeyRecord.
+		 * @return the corresponding PreKeyRecord.
+		 * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
+		 */
+		@Override
+		public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
+			PreKeyRecord record = mXmppConnectionService.databaseBackend.loadPreKey(account, preKeyId);
+			if(record == null) {
+				throw new InvalidKeyIdException("No such PreKeyRecord: " + preKeyId);
+			}
+			return record;
+		}
+
+		/**
+		 * Store a local PreKeyRecord.
+		 *
+		 * @param preKeyId the ID of the PreKeyRecord to store.
+		 * @param record   the PreKeyRecord.
+		 */
+		@Override
+		public void storePreKey(int preKeyId, PreKeyRecord record) {
+			mXmppConnectionService.databaseBackend.storePreKey(account, record);
+			currentPreKeyId = preKeyId;
+			boolean success = this.account.setKey(JSONKEY_CURRENT_PREKEY_ID,Integer.toString(preKeyId));
+			if(success) {
+				mXmppConnectionService.databaseBackend.updateAccount(account);
+			} else {
+				Log.e(Config.LOGTAG, "Failed to write new prekey id to the database!");
+			}
+		}
+
+		/**
+		 * @param preKeyId A PreKeyRecord ID.
+		 * @return true if the store has a record for the preKeyId, otherwise false.
+		 */
+		@Override
+		public boolean containsPreKey(int preKeyId) {
+			return mXmppConnectionService.databaseBackend.containsPreKey(account, preKeyId);
+		}
+
+		/**
+		 * Delete a PreKeyRecord from local storage.
+		 *
+		 * @param preKeyId The ID of the PreKeyRecord to remove.
+		 */
+		@Override
+		public void removePreKey(int preKeyId) {
+			mXmppConnectionService.databaseBackend.deletePreKey(account, preKeyId);
+		}
+
+		// --------------------------------------
+		// SignedPreKeyStore
+		// --------------------------------------
+
+		/**
+		 * Load a local SignedPreKeyRecord.
+		 *
+		 * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
+		 * @return the corresponding SignedPreKeyRecord.
+		 * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
+		 */
+		@Override
+		public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
+			SignedPreKeyRecord record = mXmppConnectionService.databaseBackend.loadSignedPreKey(account, signedPreKeyId);
+			if(record == null) {
+				throw new InvalidKeyIdException("No such SignedPreKeyRecord: " + signedPreKeyId);
+			}
+			return record;
+		}
+
+		/**
+		 * Load all local SignedPreKeyRecords.
+		 *
+		 * @return All stored SignedPreKeyRecords.
+		 */
+		@Override
+		public List<SignedPreKeyRecord> loadSignedPreKeys() {
+			return mXmppConnectionService.databaseBackend.loadSignedPreKeys(account);
+		}
+
+		/**
+		 * Store a local SignedPreKeyRecord.
+		 *
+		 * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
+		 * @param record         the SignedPreKeyRecord.
+		 */
+		@Override
+		public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
+			mXmppConnectionService.databaseBackend.storeSignedPreKey(account, record);
+		}
+
+		/**
+		 * @param signedPreKeyId A SignedPreKeyRecord ID.
+		 * @return true if the store has a record for the signedPreKeyId, otherwise false.
+		 */
+		@Override
+		public boolean containsSignedPreKey(int signedPreKeyId) {
+			return mXmppConnectionService.databaseBackend.containsSignedPreKey(account, signedPreKeyId);
+		}
+
+		/**
+		 * Delete a SignedPreKeyRecord from local storage.
+		 *
+		 * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
+		 */
+		@Override
+		public void removeSignedPreKey(int signedPreKeyId) {
+			mXmppConnectionService.databaseBackend.deleteSignedPreKey(account, signedPreKeyId);
+		}
+	}
+
+	public static class XmppAxolotlSession {
+		private SessionCipher cipher;
+		private boolean isTrusted = false;
+		private SQLiteAxolotlStore sqLiteAxolotlStore;
+		private AxolotlAddress remoteAddress;
+
+		public XmppAxolotlSession(SQLiteAxolotlStore store, AxolotlAddress remoteAddress) {
+			this.cipher = new SessionCipher(store, remoteAddress);
+			this.remoteAddress = remoteAddress;
+			this.sqLiteAxolotlStore = store;
+			this.isTrusted = sqLiteAxolotlStore.isTrustedSession(remoteAddress);
+		}
+
+		public void trust() {
+			sqLiteAxolotlStore.setTrustedSession(remoteAddress, true);
+			this.isTrusted = true;
+		}
+
+		public boolean isTrusted() {
+			return this.isTrusted;
+		}
+
+		public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
+			byte[] plaintext = null;
+			try {
+				try {
+					PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents());
+					Log.d(Config.LOGTAG,"PreKeyWhisperMessage ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
+					plaintext = cipher.decrypt(message);
+				} catch (InvalidMessageException|InvalidVersionException e) {
+					WhisperMessage message = new WhisperMessage(incomingHeader.getContents());
+					plaintext = cipher.decrypt(message);
+				} catch (InvalidKeyException|InvalidKeyIdException| UntrustedIdentityException e) {
+					Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
+				}
+			} catch (LegacyMessageException|InvalidMessageException e) {
+				Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
+			} catch (DuplicateMessageException|NoSessionException e) {
+				Log.d(Config.LOGTAG, "Error decrypting axolotl header: " + e.getMessage());
+			}
+			return plaintext;
+		}
+
+		public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(byte[] outgoingMessage) {
+			CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
+			XmppAxolotlMessage.XmppAxolotlMessageHeader header =
+					new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(),
+							ciphertextMessage.serialize());
+			return header;
+		}
+	}
+
+	private static class AxolotlAddressMap<T> {
+		protected Map<String, Map<Integer,T>> map;
+		protected final Object MAP_LOCK = new Object();
+
+		public AxolotlAddressMap() {
+			this.map = new HashMap<>();
+		}
+
+		public void put(AxolotlAddress address, T value) {
+			synchronized (MAP_LOCK) {
+				Map<Integer, T> devices = map.get(address.getName());
+				if (devices == null) {
+					devices = new HashMap<>();
+					map.put(address.getName(), devices);
+				}
+				devices.put(address.getDeviceId(), value);
+			}
+		}
+
+		public T get(AxolotlAddress address) {
+			synchronized (MAP_LOCK) {
+				Map<Integer, T> devices = map.get(address.getName());
+				if(devices == null) {
+					return null;
+				}
+				return devices.get(address.getDeviceId());
+			}
+		}
+
+		public Map<Integer, T> getAll(AxolotlAddress address) {
+			synchronized (MAP_LOCK) {
+				Map<Integer, T> devices = map.get(address.getName());
+				if(devices == null) {
+					return new HashMap<>();
+				}
+				return devices;
+			}
+		}
+
+		public boolean hasAny(AxolotlAddress address) {
+			synchronized (MAP_LOCK) {
+				Map<Integer, T> devices = map.get(address.getName());
+				return devices != null && !devices.isEmpty();
+			}
+		}
+
+
+	}
+
+	private static class SessionMap extends AxolotlAddressMap<XmppAxolotlSession> {
+
+		public SessionMap(SQLiteAxolotlStore store, Account account) {
+			super();
+			this.fillMap(store, account);
+		}
+
+		private void fillMap(SQLiteAxolotlStore store, Account account) {
+			for(Contact contact:account.getRoster().getContacts()){
+				Jid bareJid = contact.getJid().toBareJid();
+				if(bareJid == null) {
+					continue; // FIXME: handle this?
+				}
+				String address = bareJid.toString();
+				List<Integer> deviceIDs = store.getSubDeviceSessions(address);
+				for(Integer deviceId:deviceIDs) {
+					AxolotlAddress axolotlAddress = new AxolotlAddress(address, deviceId);
+					this.put(axolotlAddress, new XmppAxolotlSession(store, axolotlAddress));
+				}
+			}
+		}
+
+	}
+
+	private static class BundleMap extends AxolotlAddressMap<PreKeyBundle> {
+
+	}
+
+	public AxolotlService(Account account, XmppConnectionService connectionService) {
+		this.mXmppConnectionService = connectionService;
+		this.account = account;
+		this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
+		this.sessions = new SessionMap(axolotlStore, account);
+		this.bundleCache = new BundleMap();
+		this.ownDeviceId = axolotlStore.getLocalRegistrationId();
+	}
+
+	public void trustSession(AxolotlAddress counterpart) {
+		XmppAxolotlSession session = sessions.get(counterpart);
+		if(session != null) {
+			session.trust();
+		}
+	}
+
+	public boolean isTrustedSession(AxolotlAddress counterpart) {
+		XmppAxolotlSession session = sessions.get(counterpart);
+		return session != null && session.isTrusted();
+	}
+
+	private AxolotlAddress getAddressForJid(Jid jid) {
+		return new AxolotlAddress(jid.toString(), 0);
+	}
+
+	private Set<XmppAxolotlSession> findOwnSessions() {
+		AxolotlAddress ownAddress = getAddressForJid(account.getJid());
+		Set<XmppAxolotlSession> ownDeviceSessions = new HashSet<>(this.sessions.getAll(ownAddress).values());
+		return ownDeviceSessions;
+	}
+
+	private Set<XmppAxolotlSession> findSessionsforContact(Contact contact) {
+		AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
+		Set<XmppAxolotlSession> sessions = new HashSet<>(this.sessions.getAll(contactAddress).values());
+		return sessions;
+	}
+
+	private boolean hasAny(Contact contact) {
+		AxolotlAddress contactAddress = getAddressForJid(contact.getJid());
+		return sessions.hasAny(contactAddress);
+	}
+
+	public int getOwnDeviceId() {
+		return ownDeviceId;
+	}
+
+	public void fetchBundleIfNeeded(final Contact contact, final Integer deviceId) {
+		final AxolotlAddress address = new AxolotlAddress(contact.getJid().toString(), deviceId);
+		if (sessions.get(address) != null) {
+			return;
+		}
+
+		synchronized (bundleCache) {
+			PreKeyBundle bundle = bundleCache.get(address);
+			if (bundle == null) {
+				bundle = new PreKeyBundle(0, deviceId, 0, null, 0, null, null, null);
+				bundleCache.put(address, bundle);
+			}
+
+			if(bundle.getPreKey() == null) {
+				Log.d(Config.LOGTAG, "No preKey in cache, fetching...");
+				IqPacket prekeysPacket = mXmppConnectionService.getIqGenerator().retrievePreKeysForDevice(contact.getJid(), deviceId);
+				mXmppConnectionService.sendIqPacket(account, prekeysPacket, new OnIqPacketReceived() {
+					@Override
+					public void onIqPacketReceived(Account account, IqPacket packet) {
+						synchronized (bundleCache) {
+							Log.d(Config.LOGTAG, "Received preKey IQ packet, processing...");
+							final IqParser parser = mXmppConnectionService.getIqParser();
+							final PreKeyBundle bundle = bundleCache.get(address);
+							final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
+							if (preKeyBundleList.isEmpty()) {
+								Log.d(Config.LOGTAG, "preKey IQ packet invalid: " + packet);
+								return;
+							}
+							Random random = new Random();
+							final PreKeyBundle newBundle = preKeyBundleList.get(random.nextInt(preKeyBundleList.size()));
+							if (bundle == null || newBundle == null) {
+								//should never happen
+								return;
+							}
+
+							final PreKeyBundle mergedBundle = new PreKeyBundle(bundle.getRegistrationId(),
+									bundle.getDeviceId(), newBundle.getPreKeyId(), newBundle.getPreKey(),
+									bundle.getSignedPreKeyId(), bundle.getSignedPreKey(),
+									bundle.getSignedPreKeySignature(), bundle.getIdentityKey());
+
+							bundleCache.put(address, mergedBundle);
+						}
+					}
+				});
+			}
+			if(bundle.getIdentityKey() == null) {
+				Log.d(Config.LOGTAG, "No bundle in cache, fetching...");
+				IqPacket bundlePacket = mXmppConnectionService.getIqGenerator().retrieveBundleForDevice(contact.getJid(), deviceId);
+				mXmppConnectionService.sendIqPacket(account, bundlePacket, new OnIqPacketReceived() {
+					@Override
+					public void onIqPacketReceived(Account account, IqPacket packet) {
+						synchronized (bundleCache) {
+							Log.d(Config.LOGTAG, "Received bundle IQ packet, processing...");
+							final IqParser parser = mXmppConnectionService.getIqParser();
+							final PreKeyBundle bundle = bundleCache.get(address);
+							final PreKeyBundle newBundle = parser.bundle(packet);
+							if( bundle == null || newBundle == null ) {
+								Log.d(Config.LOGTAG, "bundle IQ packet invalid: " + packet);
+								//should never happen
+								return;
+							}
+
+							final PreKeyBundle mergedBundle = new PreKeyBundle(bundle.getRegistrationId(),
+									bundle.getDeviceId(), bundle.getPreKeyId(), bundle.getPreKey(),
+									newBundle.getSignedPreKeyId(), newBundle.getSignedPreKey(),
+									newBundle.getSignedPreKeySignature(), newBundle.getIdentityKey());
+
+							axolotlStore.saveIdentity(contact.getJid().toBareJid().toString(), newBundle.getIdentityKey());
+							bundleCache.put(address, mergedBundle);
+						}
+					}
+				});
+			}
+		}
+	}
+
+	public void publishOwnDeviceIdIfNeeded() {
+		IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveDeviceIds(account.getJid().toBareJid());
+		mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+			@Override
+			public void onIqPacketReceived(Account account, IqPacket packet) {
+				Element item = mXmppConnectionService.getIqParser().getItem(packet);
+				List<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+				if(deviceIds == null) {
+					deviceIds = new ArrayList<>();
+				}
+				if(!deviceIds.contains(getOwnDeviceId())) {
+					Log.d(Config.LOGTAG, "Own device " + getOwnDeviceId() + " not in PEP devicelist. Publishing...");
+					deviceIds.add(getOwnDeviceId());
+					IqPacket publish = mXmppConnectionService.getIqGenerator().publishDeviceIds(deviceIds);
+					mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
+						@Override
+						public void onIqPacketReceived(Account account, IqPacket packet) {
+							// TODO: implement this!
+						}
+					});
+				}
+			}
+		});
+	}
+
+	public void publishBundleIfNeeded() {
+		IqPacket packet = mXmppConnectionService.getIqGenerator().retrieveBundleForDevice(account.getJid().toBareJid(), ownDeviceId);
+		mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+			@Override
+			public void onIqPacketReceived(Account account, IqPacket packet) {
+				PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet);
+				if(bundle == null) {
+					Log.d(Config.LOGTAG, "Bundle " + getOwnDeviceId() + " not in PEP. Publishing...");
+					int numSignedPreKeys = axolotlStore.loadSignedPreKeys().size();
+					try {
+						SignedPreKeyRecord signedPreKeyRecord = KeyHelper.generateSignedPreKey(
+								axolotlStore.getIdentityKeyPair(), numSignedPreKeys + 1);
+						axolotlStore.storeSignedPreKey(signedPreKeyRecord.getId(), signedPreKeyRecord);
+						IqPacket publish = mXmppConnectionService.getIqGenerator().publishBundle(
+								signedPreKeyRecord, axolotlStore.getIdentityKeyPair().getPublicKey(),
+								ownDeviceId);
+						mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
+							@Override
+							public void onIqPacketReceived(Account account, IqPacket packet) {
+								// TODO: implement this!
+								Log.d(Config.LOGTAG, "Published bundle, got: " + packet);
+							}
+						});
+					} catch (InvalidKeyException e) {
+						Log.e(Config.LOGTAG, "Failed to publish bundle " + getOwnDeviceId() + ", reason: " + e.getMessage());
+					}
+				}
+			}
+		});
+	}
+
+	public void publishPreKeysIfNeeded() {
+		IqPacket packet = mXmppConnectionService.getIqGenerator().retrievePreKeysForDevice(account.getJid().toBareJid(), ownDeviceId);
+		mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
+			@Override
+			public void onIqPacketReceived(Account account, IqPacket packet) {
+				Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet);
+				if(keys == null || keys.isEmpty()) {
+					Log.d(Config.LOGTAG, "Prekeys " + getOwnDeviceId() + " not in PEP. Publishing...");
+					List<PreKeyRecord> preKeyRecords = KeyHelper.generatePreKeys(
+							axolotlStore.getCurrentPreKeyId(), 100);
+					for(PreKeyRecord record : preKeyRecords) {
+						axolotlStore.storePreKey(record.getId(), record);
+					}
+					IqPacket publish = mXmppConnectionService.getIqGenerator().publishPreKeys(
+							preKeyRecords, ownDeviceId);
+
+					mXmppConnectionService.sendIqPacket(account, publish, new OnIqPacketReceived() {
+						@Override
+						public void onIqPacketReceived(Account account, IqPacket packet) {
+							Log.d(Config.LOGTAG, "Published prekeys, got: " + packet);
+							// TODO: implement this!
+						}
+					});
+				}
+			}
+		});
+	}
+
+
+	public boolean isContactAxolotlCapable(Contact contact) {
+		AxolotlAddress address = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
+		return sessions.hasAny(address) || bundleCache.hasAny(address);
+	}
+
+	public void initiateSynchronousSession(Contact contact) {
+
+	}
+
+	private void createSessionsIfNeeded(Contact contact) throws NoSessionsCreatedException {
+		Log.d(Config.LOGTAG, "Creating axolotl sessions if needed...");
+		AxolotlAddress address = new AxolotlAddress(contact.getJid().toBareJid().toString(), 0);
+		for(Integer deviceId: bundleCache.getAll(address).keySet()) {
+			Log.d(Config.LOGTAG, "Processing device ID: " + deviceId);
+			AxolotlAddress remoteAddress = new AxolotlAddress(contact.getJid().toBareJid().toString(), deviceId);
+			if(sessions.get(remoteAddress) == null) {
+				Log.d(Config.LOGTAG, "Building new sesstion for " + deviceId);
+				SessionBuilder builder = new SessionBuilder(this.axolotlStore, remoteAddress);
+				try {
+					builder.process(bundleCache.get(remoteAddress));
+					XmppAxolotlSession session = new XmppAxolotlSession(this.axolotlStore, remoteAddress);
+					sessions.put(remoteAddress, session);
+				} catch (InvalidKeyException e) {
+					Log.d(Config.LOGTAG, "Error building session for " + deviceId+ ": InvalidKeyException, " +e.getMessage());
+				} catch (UntrustedIdentityException e) {
+					Log.d(Config.LOGTAG, "Error building session for " + deviceId+ ": UntrustedIdentityException, " +e.getMessage());
+				}
+			} else {
+				Log.d(Config.LOGTAG, "Already have session for " + deviceId);
+			}
+		}
+		if(!this.hasAny(contact)) {
+			Log.e(Config.LOGTAG, "No Axolotl sessions available!");
+			throw new NoSessionsCreatedException(); // FIXME: proper error handling
+		}
+	}
+
+	public XmppAxolotlMessage processSending(Contact contact, String outgoingMessage) throws NoSessionsCreatedException {
+		XmppAxolotlMessage message = new XmppAxolotlMessage(contact, ownDeviceId, outgoingMessage);
+		createSessionsIfNeeded(contact);
+		Log.d(Config.LOGTAG, "Building axolotl foreign headers...");
+
+		for(XmppAxolotlSession session : findSessionsforContact(contact)) {
 //            if(!session.isTrusted()) {
-                // TODO: handle this properly
+				// TODO: handle this properly
   //              continue;
-    //        }
-            message.addHeader(session.processSending(message.getInnerKey()));
-        }
-        Log.d(Config.LOGTAG, "Building axolotl own headers...");
-        for(XmppAxolotlSession session : findOwnSessions()) {
-    //        if(!session.isTrusted()) {
-                // TODO: handle this properly
-      //          continue;
-        //    }
-            message.addHeader(session.processSending(message.getInnerKey()));
-        }
-
-        return message;
-    }
-
-    public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceiving(XmppAxolotlMessage message) {
-        XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
-        AxolotlAddress senderAddress = new AxolotlAddress(message.getContact().getJid().toBareJid().toString(),
-                message.getSenderDeviceId());
-
-        XmppAxolotlSession session = sessions.get(senderAddress);
-        if (session == null) {
-            Log.d(Config.LOGTAG, "No axolotl session found while parsing received message " + message);
-            // TODO: handle this properly
-            session = new XmppAxolotlSession(axolotlStore, senderAddress);
-
-        }
-
-        for(XmppAxolotlMessage.XmppAxolotlMessageHeader header : message.getHeaders()) {
-            if (header.getRecipientDeviceId() == ownDeviceId) {
-                Log.d(Config.LOGTAG, "Found axolotl header matching own device ID, processing...");
-                byte[] payloadKey = session.processReceiving(header);
-                if (payloadKey != null) {
-                    Log.d(Config.LOGTAG, "Got payload key from axolotl header. Decrypting message...");
-                    plaintextMessage = message.decrypt(session, payloadKey);
-                }
-            }
-        }
-
-        return plaintextMessage;
-    }
+	//        }
+			message.addHeader(session.processSending(message.getInnerKey()));
+		}
+		Log.d(Config.LOGTAG, "Building axolotl own headers...");
+		for(XmppAxolotlSession session : findOwnSessions()) {
+	//        if(!session.isTrusted()) {
+				// TODO: handle this properly
+	  //          continue;
+		//    }
+			message.addHeader(session.processSending(message.getInnerKey()));
+		}
+
+		return message;
+	}
+
+	public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceiving(XmppAxolotlMessage message) {
+		XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
+		AxolotlAddress senderAddress = new AxolotlAddress(message.getContact().getJid().toBareJid().toString(),
+				message.getSenderDeviceId());
+
+		XmppAxolotlSession session = sessions.get(senderAddress);
+		if (session == null) {
+			Log.d(Config.LOGTAG, "No axolotl session found while parsing received message " + message);
+			// TODO: handle this properly
+			session = new XmppAxolotlSession(axolotlStore, senderAddress);
+
+		}
+
+		for(XmppAxolotlMessage.XmppAxolotlMessageHeader header : message.getHeaders()) {
+			if (header.getRecipientDeviceId() == ownDeviceId) {
+				Log.d(Config.LOGTAG, "Found axolotl header matching own device ID, processing...");
+				byte[] payloadKey = session.processReceiving(header);
+				if (payloadKey != null) {
+					Log.d(Config.LOGTAG, "Got payload key from axolotl header. Decrypting message...");
+					plaintextMessage = message.decrypt(session, payloadKey);
+				}
+			}
+		}
+
+		return plaintextMessage;
+	}
 }

src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java 🔗

@@ -21,164 +21,164 @@ import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.xml.Element;
 
 public class XmppAxolotlMessage {
-    private byte[] innerKey;
-    private byte[] ciphertext;
-    private byte[] iv;
-    private final Set<XmppAxolotlMessageHeader> headers;
-    private final Contact contact;
-    private final int sourceDeviceId;
-
-    public static class XmppAxolotlMessageHeader {
-        private final int recipientDeviceId;
-        private final byte[] content;
-
-        public XmppAxolotlMessageHeader(int deviceId, byte[] content) {
-            this.recipientDeviceId = deviceId;
-            this.content = content;
-        }
-
-        public XmppAxolotlMessageHeader(Element header) {
-            if("header".equals(header.getName())) {
-                this.recipientDeviceId = Integer.parseInt(header.getAttribute("rid"));
-                this.content = Base64.decode(header.getContent(),Base64.DEFAULT);
-            } else {
-                throw new IllegalArgumentException("Argument not a <header> Element!");
-            }
-        }
-        
-        public int getRecipientDeviceId() {
-            return recipientDeviceId;
-        }
-
-        public byte[] getContents() {
-            return content;
-        }
-
-        public Element toXml() {
-            Element headerElement = new Element("header");
-            // TODO: generate XML
-            headerElement.setAttribute("rid", getRecipientDeviceId());
-            headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT));
-            return headerElement;
-        }
-    }
-
-    public static class XmppAxolotlPlaintextMessage {
-        private final AxolotlService.XmppAxolotlSession session;
-        private final String plaintext;
-
-        public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext) {
-            this.session = session;
-            this.plaintext = plaintext;
-        }
-
-        public String getPlaintext() {
-            return plaintext;
-        }
-    }
-
-    public XmppAxolotlMessage(Contact contact, Element axolotlMessage) {
-        this.contact = contact;
-        this.sourceDeviceId = Integer.parseInt(axolotlMessage.getAttribute("id"));
-        this.headers = new HashSet<>();
-        for(Element child:axolotlMessage.getChildren()) {
-            switch(child.getName()) {
-                case "header":
-                    headers.add(new XmppAxolotlMessageHeader(child));
-                    break;
-                case "message":
-                    iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT);
-                    ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    public XmppAxolotlMessage(Contact contact, int sourceDeviceId, String plaintext) {
-        this.contact = contact;
-        this.sourceDeviceId = sourceDeviceId;
-        this.headers = new HashSet<>();
-        this.encrypt(plaintext);
-    }
-
-    private void encrypt(String plaintext) {
-        try {
-            KeyGenerator generator = KeyGenerator.getInstance("AES");
-            generator.init(128);
-            SecretKey secretKey = generator.generateKey();
-            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
-            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
-            this.innerKey = secretKey.getEncoded();
-            this.iv = cipher.getIV();
-            this.ciphertext = cipher.doFinal(plaintext.getBytes());
-        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
-                | IllegalBlockSizeException | BadPaddingException e) {
-
-        }
-    }
-
-    public Contact getContact() {
-        return this.contact;
-    }
-
-    public int getSenderDeviceId() {
-        return sourceDeviceId;
-    }
-    
-    public byte[] getCiphertext() {
-        return ciphertext;
-    }
-
-    public Set<XmppAxolotlMessageHeader> getHeaders() {
-        return headers;
-    }
-
-    public void addHeader(XmppAxolotlMessageHeader header) {
-        headers.add(header);
-    }
-
-    public byte[] getInnerKey(){
-        return innerKey;
-    }
-
-    public byte[] getIV() {
-        return this.iv;
-    }
-
-    public Element toXml() {
-        // TODO: generate outer XML, add in header XML
-        Element message= new Element("axolotl_message", AxolotlService.PEP_PREFIX);
-        message.setAttribute("id", sourceDeviceId);
-        for(XmppAxolotlMessageHeader header: headers) {
-            message.addChild(header.toXml());
-        }
-        Element payload = message.addChild("message");
-        payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT));
-        payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT));
-        return message;
-    }
-
-
-    public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key) {
-        XmppAxolotlPlaintextMessage plaintextMessage = null;
-        try {
-
-            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
-            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
-            IvParameterSpec ivSpec = new IvParameterSpec(iv);
-
-            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
-
-            String plaintext = new String(cipher.doFinal(ciphertext));
-            plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext);
-
-        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
-                | InvalidAlgorithmParameterException | IllegalBlockSizeException
-                | BadPaddingException e) {
-            throw new AssertionError(e);
-        }
-        return plaintextMessage;
-    }
+	private byte[] innerKey;
+	private byte[] ciphertext;
+	private byte[] iv;
+	private final Set<XmppAxolotlMessageHeader> headers;
+	private final Contact contact;
+	private final int sourceDeviceId;
+
+	public static class XmppAxolotlMessageHeader {
+		private final int recipientDeviceId;
+		private final byte[] content;
+
+		public XmppAxolotlMessageHeader(int deviceId, byte[] content) {
+			this.recipientDeviceId = deviceId;
+			this.content = content;
+		}
+
+		public XmppAxolotlMessageHeader(Element header) {
+			if("header".equals(header.getName())) {
+				this.recipientDeviceId = Integer.parseInt(header.getAttribute("rid"));
+				this.content = Base64.decode(header.getContent(),Base64.DEFAULT);
+			} else {
+				throw new IllegalArgumentException("Argument not a <header> Element!");
+			}
+		}
+
+		public int getRecipientDeviceId() {
+			return recipientDeviceId;
+		}
+
+		public byte[] getContents() {
+			return content;
+		}
+
+		public Element toXml() {
+			Element headerElement = new Element("header");
+			// TODO: generate XML
+			headerElement.setAttribute("rid", getRecipientDeviceId());
+			headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT));
+			return headerElement;
+		}
+	}
+
+	public static class XmppAxolotlPlaintextMessage {
+		private final AxolotlService.XmppAxolotlSession session;
+		private final String plaintext;
+
+		public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext) {
+			this.session = session;
+			this.plaintext = plaintext;
+		}
+
+		public String getPlaintext() {
+			return plaintext;
+		}
+	}
+
+	public XmppAxolotlMessage(Contact contact, Element axolotlMessage) {
+		this.contact = contact;
+		this.sourceDeviceId = Integer.parseInt(axolotlMessage.getAttribute("id"));
+		this.headers = new HashSet<>();
+		for(Element child:axolotlMessage.getChildren()) {
+			switch(child.getName()) {
+				case "header":
+					headers.add(new XmppAxolotlMessageHeader(child));
+					break;
+				case "message":
+					iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT);
+					ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT);
+					break;
+				default:
+					break;
+			}
+		}
+	}
+
+	public XmppAxolotlMessage(Contact contact, int sourceDeviceId, String plaintext) {
+		this.contact = contact;
+		this.sourceDeviceId = sourceDeviceId;
+		this.headers = new HashSet<>();
+		this.encrypt(plaintext);
+	}
+
+	private void encrypt(String plaintext) {
+		try {
+			KeyGenerator generator = KeyGenerator.getInstance("AES");
+			generator.init(128);
+			SecretKey secretKey = generator.generateKey();
+			Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
+			cipher.init(Cipher.ENCRYPT_MODE, secretKey);
+			this.innerKey = secretKey.getEncoded();
+			this.iv = cipher.getIV();
+			this.ciphertext = cipher.doFinal(plaintext.getBytes());
+		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+				| IllegalBlockSizeException | BadPaddingException e) {
+
+		}
+	}
+
+	public Contact getContact() {
+		return this.contact;
+	}
+
+	public int getSenderDeviceId() {
+		return sourceDeviceId;
+	}
+
+	public byte[] getCiphertext() {
+		return ciphertext;
+	}
+
+	public Set<XmppAxolotlMessageHeader> getHeaders() {
+		return headers;
+	}
+
+	public void addHeader(XmppAxolotlMessageHeader header) {
+		headers.add(header);
+	}
+
+	public byte[] getInnerKey(){
+		return innerKey;
+	}
+
+	public byte[] getIV() {
+		return this.iv;
+	}
+
+	public Element toXml() {
+		// TODO: generate outer XML, add in header XML
+		Element message= new Element("axolotl_message", AxolotlService.PEP_PREFIX);
+		message.setAttribute("id", sourceDeviceId);
+		for(XmppAxolotlMessageHeader header: headers) {
+			message.addChild(header.toXml());
+		}
+		Element payload = message.addChild("message");
+		payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT));
+		payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT));
+		return message;
+	}
+
+
+	public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key) {
+		XmppAxolotlPlaintextMessage plaintextMessage = null;
+		try {
+
+			Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
+			SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
+			IvParameterSpec ivSpec = new IvParameterSpec(iv);
+
+			cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+
+			String plaintext = new String(cipher.doFinal(ciphertext));
+			plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext);
+
+		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
+				| InvalidAlgorithmParameterException | IllegalBlockSizeException
+				| BadPaddingException e) {
+			throw new AssertionError(e);
+		}
+		return plaintextMessage;
+	}
 }

src/main/java/eu/siacs/conversations/entities/Contact.java 🔗

@@ -310,7 +310,7 @@ public class Contact implements ListItem, Blockable {
 		synchronized (this.keys) {
 			if (getOtrFingerprints().contains(print)) {
 				return false;
-            }
+			}
 			try {
 				JSONArray fingerprints;
 				if (!this.keys.has("otr_fingerprints")) {
@@ -392,12 +392,12 @@ public class Contact implements ListItem, Blockable {
 	public boolean addAxolotlIdentityKey(IdentityKey identityKey) {
 		synchronized (this.keys) {
 			if(!getAxolotlIdentityKeys().contains(identityKey)) {
-                JSONArray keysList;
-                try {
-                    keysList = this.keys.getJSONArray("axolotl_identity_key");
-                } catch (JSONException e) {
-                    keysList = new JSONArray();
-                }
+				JSONArray keysList;
+				try {
+					keysList = this.keys.getJSONArray("axolotl_identity_key");
+				} catch (JSONException e) {
+					keysList = new JSONArray();
+				}
 
 				keysList.put(Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT));
 				try {
@@ -406,10 +406,10 @@ public class Contact implements ListItem, Blockable {
 					Log.e(Config.LOGTAG, "Error adding Identity Key to Contact " + this.getJid() + ": " + e.getMessage());
 					return false;
 				}
-                return true;
+				return true;
 			} else {
-                return false;
-            }
+				return false;
+			}
 		}
 	}
 

src/main/java/eu/siacs/conversations/generator/IqGenerator.java 🔗

@@ -147,16 +147,16 @@ public class IqGenerator extends AbstractGenerator {
 		return packet;
 	}
 
-    public IqPacket publishDeviceIds(final List<Integer> ids) {
-        final Element item = new Element("item");
-        final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
-        for(Integer id:ids) {
-            final Element device = new Element("device");
-            device.setAttribute("id", id);
-            list.addChild(device);
-        }
-        return publish(AxolotlService.PEP_DEVICE_LIST, item);
-    }
+	public IqPacket publishDeviceIds(final List<Integer> ids) {
+		final Element item = new Element("item");
+		final Element list = item.addChild("list", AxolotlService.PEP_PREFIX);
+		for(Integer id:ids) {
+			final Element device = new Element("device");
+			device.setAttribute("id", id);
+			list.addChild(device);
+		}
+		return publish(AxolotlService.PEP_DEVICE_LIST, item);
+	}
 
 	public IqPacket publishBundle(final SignedPreKeyRecord signedPreKeyRecord, IdentityKey identityKey, final int deviceId) {
 		final Element item = new Element("item");
@@ -173,17 +173,17 @@ public class IqGenerator extends AbstractGenerator {
 		return publish(AxolotlService.PEP_BUNDLE+":"+deviceId, item);
 	}
 
-    public IqPacket publishPreKeys(final List<PreKeyRecord> prekeyList, final int deviceId) {
-        final Element item = new Element("item");
-        final Element prekeys = item.addChild("prekeys", AxolotlService.PEP_PREFIX);
-        for(PreKeyRecord preKeyRecord:prekeyList) {
-            final Element prekey = prekeys.addChild("preKeyPublic");
+	public IqPacket publishPreKeys(final List<PreKeyRecord> prekeyList, final int deviceId) {
+		final Element item = new Element("item");
+		final Element prekeys = item.addChild("prekeys", AxolotlService.PEP_PREFIX);
+		for(PreKeyRecord preKeyRecord:prekeyList) {
+			final Element prekey = prekeys.addChild("preKeyPublic");
 			prekey.setAttribute("preKeyId", preKeyRecord.getId());
-            prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
-        }
+			prekey.setContent(Base64.encodeToString(preKeyRecord.getKeyPair().getPublicKey().serialize(), Base64.DEFAULT));
+		}
 
 		return publish(AxolotlService.PEP_PREKEYS+":"+deviceId, item);
-    }
+	}
 
 	public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) {
 		final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);

src/main/java/eu/siacs/conversations/generator/MessageGenerator.java 🔗

@@ -66,19 +66,19 @@ public class MessageGenerator extends AbstractGenerator {
 		delay.setAttribute("stamp", mDateFormat.format(date));
 	}
 
-    public MessagePacket generateAxolotlChat(Message message) throws NoSessionsCreatedException{
-        return generateAxolotlChat(message, false);
-    }
+	public MessagePacket generateAxolotlChat(Message message) throws NoSessionsCreatedException{
+		return generateAxolotlChat(message, false);
+	}
 
 	public MessagePacket generateAxolotlChat(Message message, boolean addDelay) throws NoSessionsCreatedException{
-        MessagePacket packet = preparePacket(message, addDelay);
-        AxolotlService service = message.getConversation().getAccount().getAxolotlService();
-        Log.d(Config.LOGTAG, "Submitting message to axolotl service for send processing...");
-        XmppAxolotlMessage axolotlMessage = service.processSending(message.getContact(),
-                message.getBody());
-        packet.setAxolotlMessage(axolotlMessage.toXml());
-        return packet;
-    }
+		MessagePacket packet = preparePacket(message, addDelay);
+		AxolotlService service = message.getConversation().getAccount().getAxolotlService();
+		Log.d(Config.LOGTAG, "Submitting message to axolotl service for send processing...");
+		XmppAxolotlMessage axolotlMessage = service.processSending(message.getContact(),
+				message.getBody());
+		packet.setAxolotlMessage(axolotlMessage.toXml());
+		return packet;
+	}
 
 	public MessagePacket generateOtrChat(Message message) {
 		return generateOtrChat(message, false);

src/main/java/eu/siacs/conversations/parser/IqParser.java 🔗

@@ -70,7 +70,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
 
 	public String avatarData(final IqPacket packet) {
 		final Element pubsub = packet.findChild("pubsub",
-                "http://jabber.org/protocol/pubsub");
+				"http://jabber.org/protocol/pubsub");
 		if (pubsub == null) {
 			return null;
 		}
@@ -165,19 +165,19 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
 
 	public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
 		Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
-        Element prekeysItem = getItem(packet);
-        if (prekeysItem == null) {
-            Log.d(Config.LOGTAG, "Couldn't find <item> in preKeyPublic IQ packet: " + packet);
-            return null;
-        }
-        final Element prekeysElement = prekeysItem.findChild("prekeys");
-        if(prekeysElement == null) {
-            Log.d(Config.LOGTAG, "Couldn't find <prekeys> in preKeyPublic IQ packet: " + packet);
-            return null;
-        }
+		Element prekeysItem = getItem(packet);
+		if (prekeysItem == null) {
+			Log.d(Config.LOGTAG, "Couldn't find <item> in preKeyPublic IQ packet: " + packet);
+			return null;
+		}
+		final Element prekeysElement = prekeysItem.findChild("prekeys");
+		if(prekeysElement == null) {
+			Log.d(Config.LOGTAG, "Couldn't find <prekeys> in preKeyPublic IQ packet: " + packet);
+			return null;
+		}
 		for(Element preKeyPublicElement : prekeysElement.getChildren()) {
 			if(!preKeyPublicElement.getName().equals("preKeyPublic")){
-                Log.d(Config.LOGTAG, "Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
+				Log.d(Config.LOGTAG, "Encountered unexpected tag in prekeys list: " + preKeyPublicElement);
 				continue;
 			}
 			Integer preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
@@ -192,37 +192,37 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
 		return preKeyRecords;
 	}
 
-    public PreKeyBundle bundle(final IqPacket bundle) {
-        Element bundleItem = getItem(bundle);
-        if(bundleItem == null) {
-            return null;
-        }
-        final Element bundleElement = bundleItem.findChild("bundle");
-        if(bundleElement == null) {
-            return null;
-        }
-        ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
-        Integer signedPreKeyId = signedPreKeyId(bundleElement);
-        byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
-        IdentityKey identityKey = identityKey(bundleElement);
-        if(signedPreKeyPublic == null || identityKey == null) {
-            return null;
-        }
+	public PreKeyBundle bundle(final IqPacket bundle) {
+		Element bundleItem = getItem(bundle);
+		if(bundleItem == null) {
+			return null;
+		}
+		final Element bundleElement = bundleItem.findChild("bundle");
+		if(bundleElement == null) {
+			return null;
+		}
+		ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
+		Integer signedPreKeyId = signedPreKeyId(bundleElement);
+		byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
+		IdentityKey identityKey = identityKey(bundleElement);
+		if(signedPreKeyPublic == null || identityKey == null) {
+			return null;
+		}
 
-        return new PreKeyBundle(0, 0, 0, null,
-                signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
-    }
+		return new PreKeyBundle(0, 0, 0, null,
+				signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
+	}
 
 	public List<PreKeyBundle> preKeys(final IqPacket preKeys) {
 		List<PreKeyBundle> bundles = new ArrayList<>();
 		Map<Integer, ECPublicKey> preKeyPublics = preKeyPublics(preKeys);
-        if ( preKeyPublics != null) {
-            for (Integer preKeyId : preKeyPublics.keySet()) {
-                ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
-                bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
-                        0, null, null, null));
-            }
-        }
+		if ( preKeyPublics != null) {
+			for (Integer preKeyId : preKeyPublics.keySet()) {
+				ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId);
+				bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic,
+						0, null, null, null));
+			}
+		}
 
 		return bundles;
 	}

src/main/java/eu/siacs/conversations/parser/MessageParser.java 🔗

@@ -98,17 +98,17 @@ public class MessageParser extends AbstractParser implements
 		}
 	}
 
-    private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation) {
-        Message finishedMessage = null;
-        AxolotlService service = conversation.getAccount().getAxolotlService();
-        XmppAxolotlMessage xmppAxolotlMessage = new XmppAxolotlMessage(conversation.getContact(), axolotlMessage);
-        XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceiving(xmppAxolotlMessage);
-        if(plaintextMessage != null) {
-            finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, Message.STATUS_RECEIVED);
-        }
+	private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation) {
+		Message finishedMessage = null;
+		AxolotlService service = conversation.getAccount().getAxolotlService();
+		XmppAxolotlMessage xmppAxolotlMessage = new XmppAxolotlMessage(conversation.getContact(), axolotlMessage);
+		XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceiving(xmppAxolotlMessage);
+		if(plaintextMessage != null) {
+			finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, Message.STATUS_RECEIVED);
+		}
 
-        return finishedMessage;
-    }
+		return finishedMessage;
+	}
 
 	private class Invite {
 		Jid jid;
@@ -187,17 +187,17 @@ public class MessageParser extends AbstractParser implements
 				mXmppConnectionService.updateAccountUi();
 			}
 		} else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
-            Log.d(Config.LOGTAG, "Received PEP device list update from "+ from + ", processing...");
-            Element item = items.findChild("item");
+			Log.d(Config.LOGTAG, "Received PEP device list update from "+ from + ", processing...");
+			Element item = items.findChild("item");
 			List<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
-            AxolotlService axolotlService = account.getAxolotlService();
-            if(account.getJid().toBareJid().equals(from)) {
-            } else {
-                Contact contact = account.getRoster().getContact(from);
-                for (Integer deviceId : deviceIds) {
-                    axolotlService.fetchBundleIfNeeded(contact, deviceId);
-                }
-            }
+			AxolotlService axolotlService = account.getAxolotlService();
+			if(account.getJid().toBareJid().equals(from)) {
+			} else {
+				Contact contact = account.getRoster().getContact(from);
+				for (Integer deviceId : deviceIds) {
+					axolotlService.fetchBundleIfNeeded(contact, deviceId);
+				}
+			}
 		}
 	}
 
@@ -262,7 +262,7 @@ public class MessageParser extends AbstractParser implements
 		final String body = packet.getBody();
 		final Element mucUserElement = packet.findChild("x","http://jabber.org/protocol/muc#user");
 		final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
-        final Element axolotlEncrypted = packet.findChild("axolotl_message", AxolotlService.PEP_PREFIX);
+		final Element axolotlEncrypted = packet.findChild("axolotl_message", AxolotlService.PEP_PREFIX);
 		int status;
 		final Jid counterpart;
 		final Jid to = packet.getTo();
@@ -324,13 +324,13 @@ public class MessageParser extends AbstractParser implements
 					message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
 				}
 			} else if (pgpEncrypted != null) {
-                message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
-            } else if (axolotlEncrypted != null) {
-                message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, conversation);
-                if (message == null) {
-                    return;
-                }
-            } else {
+				message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
+			} else if (axolotlEncrypted != null) {
+				message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, conversation);
+				if (message == null) {
+					return;
+				}
+			} else {
 				message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
 			}
 			message.setCounterpart(counterpart);

src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java 🔗

@@ -42,7 +42,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 			+ Contact.JID + " TEXT," + Contact.KEYS + " TEXT,"
 			+ Contact.PHOTOURI + " TEXT," + Contact.OPTIONS + " NUMBER,"
 			+ Contact.SYSTEMACCOUNT + " NUMBER, " + Contact.AVATAR + " TEXT, "
-            + Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
+			+ Contact.LAST_PRESENCE + " TEXT, " + Contact.LAST_TIME + " NUMBER, "
 			+ Contact.GROUPS + " TEXT, FOREIGN KEY(" + Contact.ACCOUNT + ") REFERENCES "
 			+ Account.TABLENAME + "(" + Account.UUID
 			+ ") ON DELETE CASCADE, UNIQUE(" + Contact.ACCOUNT + ", "
@@ -75,14 +75,14 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 	private static String CREATE_SESSIONS_STATEMENT = "CREATE TABLE "
 			+ AxolotlService.SQLiteAxolotlStore.SESSION_TABLENAME + "("
 				+ AxolotlService.SQLiteAxolotlStore.ACCOUNT + " TEXT,  "
-                + AxolotlService.SQLiteAxolotlStore.NAME + " TEXT, "
-                + AxolotlService.SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
+				+ AxolotlService.SQLiteAxolotlStore.NAME + " TEXT, "
+				+ AxolotlService.SQLiteAxolotlStore.DEVICE_ID + " INTEGER, "
 				+ AxolotlService.SQLiteAxolotlStore.TRUSTED + " INTEGER, "
-                + AxolotlService.SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
-                    + AxolotlService.SQLiteAxolotlStore.ACCOUNT
+				+ AxolotlService.SQLiteAxolotlStore.KEY + " TEXT, FOREIGN KEY("
+					+ AxolotlService.SQLiteAxolotlStore.ACCOUNT
 				+ ") REFERENCES " + Account.TABLENAME + "(" + Account.UUID + ") ON DELETE CASCADE, "
-                + "UNIQUE( " + AxolotlService.SQLiteAxolotlStore.ACCOUNT + ", "
-                    + AxolotlService.SQLiteAxolotlStore.NAME + ", "
+				+ "UNIQUE( " + AxolotlService.SQLiteAxolotlStore.ACCOUNT + ", "
+					+ AxolotlService.SQLiteAxolotlStore.NAME + ", "
 					+ AxolotlService.SQLiteAxolotlStore.DEVICE_ID
 				+ ") ON CONFLICT REPLACE"
 			+");";
@@ -157,12 +157,12 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 			db.execSQL("ALTER TABLE " + Conversation.TABLENAME + " ADD COLUMN "
 					+ Conversation.ATTRIBUTES + " TEXT");
 		}
-        if (oldVersion < 9 && newVersion >= 9) {
-            db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
-                    + Contact.LAST_TIME + " NUMBER");
-            db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
-                    + Contact.LAST_PRESENCE + " TEXT");
-        }
+		if (oldVersion < 9 && newVersion >= 9) {
+			db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+					+ Contact.LAST_TIME + " NUMBER");
+			db.execSQL("ALTER TABLE " + Contact.TABLENAME + " ADD COLUMN "
+					+ Contact.LAST_PRESENCE + " TEXT");
+		}
 		if (oldVersion < 10 && newVersion >= 10) {
 			db.execSQL("ALTER TABLE " + Message.TABLENAME + " ADD COLUMN "
 					+ Message.RELATIVE_FILE_PATH + " TEXT");
@@ -557,7 +557,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 
 	public SessionRecord loadSession(Account account, AxolotlAddress contact) {
 		SessionRecord session = null;
-        Cursor cursor = getCursorForSession(account, contact);
+		Cursor cursor = getCursorForSession(account, contact);
 		if(cursor.getCount() != 0) {
 			cursor.moveToFirst();
 			try {
@@ -565,8 +565,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 			} catch (IOException e) {
 				throw new AssertionError(e);
 			}
-        }
-        cursor.close();
+		}
+		cursor.close();
 		return session;
 	}
 
@@ -635,7 +635,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
 		Cursor cursor = getCursorForSession(account, contact);
 		if(cursor.getCount() != 0) {
 			cursor.moveToFirst();
-            trusted = cursor.getInt(cursor.getColumnIndex(
+			trusted = cursor.getInt(cursor.getColumnIndex(
 					AxolotlService.SQLiteAxolotlStore.TRUSTED)) > 0;
 		}
 		cursor.close();

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -274,9 +274,9 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
 					}
 				}
 				syncDirtyContacts(account);
-                account.getAxolotlService().publishOwnDeviceIdIfNeeded();
-                account.getAxolotlService().publishBundleIfNeeded();
-                account.getAxolotlService().publishPreKeysIfNeeded();
+				account.getAxolotlService().publishOwnDeviceIdIfNeeded();
+				account.getAxolotlService().publishBundleIfNeeded();
+				account.getAxolotlService().publishPreKeysIfNeeded();
 
 				scheduleWakeUpCall(Config.PING_MAX_INTERVAL, account.getUuid().hashCode());
 			} else if (account.getStatus() == Account.State.OFFLINE) {

src/main/java/eu/siacs/conversations/ui/ConversationActivity.java 🔗

@@ -752,16 +752,16 @@ public class ConversationActivity extends XmppActivity
 							}
 							break;
 						case R.id.encryption_choice_axolotl:
-                            Log.d(Config.LOGTAG, "Trying to enable axolotl...");
+							Log.d(Config.LOGTAG, "Trying to enable axolotl...");
 							if(conversation.getAccount().getAxolotlService().isContactAxolotlCapable(conversation.getContact())) {
-                                Log.d(Config.LOGTAG, "Enabled axolotl for Contact " + conversation.getContact().getJid() );
+								Log.d(Config.LOGTAG, "Enabled axolotl for Contact " + conversation.getContact().getJid() );
 								conversation.setNextEncryption(Message.ENCRYPTION_AXOLOTL);
 								item.setChecked(true);
 							} else {
-                                Log.d(Config.LOGTAG, "Contact " + conversation.getContact().getJid() + " not axolotl capable!");
+								Log.d(Config.LOGTAG, "Contact " + conversation.getContact().getJid() + " not axolotl capable!");
 								showAxolotlNoSessionsDialog();
 							}
-                            break;
+							break;
 						default:
 							conversation.setNextEncryption(Message.ENCRYPTION_NONE);
 							break;
@@ -794,7 +794,7 @@ public class ConversationActivity extends XmppActivity
 					pgp.setChecked(true);
 					break;
 				case Message.ENCRYPTION_AXOLOTL:
-                    Log.d(Config.LOGTAG, "Axolotl confirmed. Setting menu item checked!");
+					Log.d(Config.LOGTAG, "Axolotl confirmed. Setting menu item checked!");
 					popup.getMenu().findItem(R.id.encryption_choice_axolotl)
 							.setChecked(true);
 					break;