mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-09-27 10:09:32 +02:00
08e897113d
And matching ImportOrder settings for Eclipse in resources/eclipse/smack.importorder
942 lines
32 KiB
Java
942 lines
32 KiB
Java
/**
|
|
*
|
|
* Copyright 2017 Paul Schaub
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.jivesoftware.smackx.omemo;
|
|
|
|
import java.io.DataInputStream;
|
|
import java.io.DataOutputStream;
|
|
import java.io.EOFException;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.Stack;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import org.jivesoftware.smack.util.StringUtils;
|
|
|
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
|
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
|
|
|
import org.jxmpp.jid.BareJid;
|
|
|
|
/**
|
|
* Like a rocket!
|
|
*
|
|
* @author Paul Schaub
|
|
*/
|
|
public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
|
extends OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
|
|
|
private static final Logger LOGGER = Logger.getLogger(FileBasedOmemoStore.class.getSimpleName());
|
|
private final FileHierarchy hierarchy;
|
|
|
|
public FileBasedOmemoStore() {
|
|
this(OmemoConfiguration.getFileBasedOmemoStoreDefaultPath());
|
|
}
|
|
|
|
public FileBasedOmemoStore(File basePath) {
|
|
super();
|
|
if (basePath == null) {
|
|
throw new IllegalStateException("No FileBasedOmemoStoreDefaultPath set in OmemoConfiguration.");
|
|
}
|
|
this.hierarchy = new FileHierarchy(basePath);
|
|
}
|
|
|
|
@Override
|
|
public boolean isFreshInstallation(OmemoManager omemoManager) {
|
|
File userDirectory = hierarchy.getUserDeviceDirectory(omemoManager);
|
|
File[] files = userDirectory.listFiles();
|
|
return files == null || files.length == 0;
|
|
}
|
|
|
|
@Override
|
|
public int getDefaultDeviceId(BareJid user) {
|
|
try {
|
|
return readInt(hierarchy.getDefaultDeviceIdPath(user));
|
|
} catch (IOException e) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setDefaultDeviceId(BareJid user, int defaultDeviceId) {
|
|
File defaultDeviceIdPath = hierarchy.getDefaultDeviceIdPath(user);
|
|
|
|
if (defaultDeviceIdPath == null) {
|
|
LOGGER.log(Level.SEVERE, "defaultDeviceIdPath is null!");
|
|
}
|
|
|
|
try {
|
|
writeInt(defaultDeviceIdPath, defaultDeviceId);
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write defaultDeviceId: " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int loadLastPreKeyId(OmemoManager omemoManager) {
|
|
try {
|
|
int l = readInt(hierarchy.getLastPreKeyIdPath(omemoManager));
|
|
return l == -1 ? 0 : l;
|
|
} catch (IOException e) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void storeLastPreKeyId(OmemoManager omemoManager, int currentPreKeyId) {
|
|
try {
|
|
writeInt(hierarchy.getLastPreKeyIdPath(omemoManager), currentPreKeyId);
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write lastPreKeyId: " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public T_IdKeyPair loadOmemoIdentityKeyPair(OmemoManager omemoManager) throws CorruptedOmemoKeyException {
|
|
File identityKeyPairPath = hierarchy.getIdentityKeyPairPath(omemoManager);
|
|
try {
|
|
byte[] bytes = readBytes(identityKeyPairPath);
|
|
return bytes != null ? keyUtil().identityKeyPairFromBytes(bytes) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void storeOmemoIdentityKeyPair(OmemoManager omemoManager, T_IdKeyPair identityKeyPair) {
|
|
File identityKeyPairPath = hierarchy.getIdentityKeyPairPath(omemoManager);
|
|
try {
|
|
writeBytes(identityKeyPairPath, keyUtil().identityKeyPairToBytes(identityKeyPair));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write omemoIdentityKeyPair: " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public T_IdKey loadOmemoIdentityKey(OmemoManager omemoManager, OmemoDevice device) throws CorruptedOmemoKeyException {
|
|
File identityKeyPath = hierarchy.getContactsIdentityKeyPath(omemoManager, device);
|
|
try {
|
|
byte[] bytes = readBytes(identityKeyPath);
|
|
return bytes != null ? keyUtil().identityKeyFromBytes(bytes) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void storeOmemoIdentityKey(OmemoManager omemoManager, OmemoDevice device, T_IdKey t_idKey) {
|
|
File identityKeyPath = hierarchy.getContactsIdentityKeyPath(omemoManager, device);
|
|
try {
|
|
writeBytes(identityKeyPath, keyUtil().identityKeyToBytes(t_idKey));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write omemoIdentityKey of " + device + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isTrustedOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint) {
|
|
File trustPath = hierarchy.getContactsTrustPath(omemoManager, device);
|
|
try {
|
|
String depositedFingerprint = new String(readBytes(trustPath), StringUtils.UTF8);
|
|
|
|
return depositedFingerprint.length() > 2
|
|
&& depositedFingerprint.charAt(0) == '1'
|
|
&& new OmemoFingerprint(depositedFingerprint.substring(2)).equals(fingerprint);
|
|
} catch (IOException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isDecidedOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint) {
|
|
File trustPath = hierarchy.getContactsTrustPath(omemoManager, device);
|
|
try {
|
|
String depositedFingerprint = new String(readBytes(trustPath), StringUtils.UTF8);
|
|
|
|
return depositedFingerprint.length() > 2
|
|
&& (depositedFingerprint.charAt(0) == '1' || depositedFingerprint.charAt(0) == '2')
|
|
&& new OmemoFingerprint(depositedFingerprint.substring(2)).equals(fingerprint);
|
|
} catch (IOException e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void trustOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint) {
|
|
File trustPath = hierarchy.getContactsTrustPath(omemoManager, device);
|
|
try {
|
|
writeBytes(trustPath, ("1 " + fingerprint.toString()).getBytes(StringUtils.UTF8));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not trust " + device + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void distrustOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint) {
|
|
File trustPath = hierarchy.getContactsTrustPath(omemoManager, device);
|
|
try {
|
|
writeBytes(trustPath, ("2 " + fingerprint.toString()).getBytes(StringUtils.UTF8));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not distrust " + device + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setDateOfLastReceivedMessage(OmemoManager omemoManager, OmemoDevice from, Date date) {
|
|
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(omemoManager, from);
|
|
try {
|
|
writeLong(lastMessageReceived, date.getTime());
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write date of last received message from " + from + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Date getDateOfLastReceivedMessage(OmemoManager omemoManager, OmemoDevice from) {
|
|
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(omemoManager, from);
|
|
try {
|
|
long date = readLong(lastMessageReceived);
|
|
return date != -1 ? new Date(date) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setDateOfLastSignedPreKeyRenewal(OmemoManager omemoManager, Date date) {
|
|
File lastSignedPreKeyRenewal = hierarchy.getLastSignedPreKeyRenewal(omemoManager);
|
|
try {
|
|
writeLong(lastSignedPreKeyRenewal, date.getTime());
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write date of last singedPreKey renewal for "
|
|
+ omemoManager.getOwnDevice() + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Date getDateOfLastSignedPreKeyRenewal(OmemoManager omemoManager) {
|
|
File lastSignedPreKeyRenewal = hierarchy.getLastSignedPreKeyRenewal(omemoManager);
|
|
|
|
try {
|
|
long date = readLong(lastSignedPreKeyRenewal);
|
|
return date != -1 ? new Date(date) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public T_PreKey loadOmemoPreKey(OmemoManager omemoManager, int preKeyId) {
|
|
File preKeyPath = hierarchy.getPreKeyPath(omemoManager, preKeyId);
|
|
try {
|
|
byte[] bytes = readBytes(preKeyPath);
|
|
return bytes != null ? keyUtil().preKeyFromBytes(bytes) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void storeOmemoPreKey(OmemoManager omemoManager, int preKeyId, T_PreKey t_preKey) {
|
|
File preKeyPath = hierarchy.getPreKeyPath(omemoManager, preKeyId);
|
|
try {
|
|
writeBytes(preKeyPath, keyUtil().preKeyToBytes(t_preKey));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write preKey with id " + preKeyId + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeOmemoPreKey(OmemoManager omemoManager, int preKeyId) {
|
|
File preKeyPath = hierarchy.getPreKeyPath(omemoManager, preKeyId);
|
|
preKeyPath.delete();
|
|
}
|
|
|
|
@Override
|
|
public int loadCurrentSignedPreKeyId(OmemoManager omemoManager) {
|
|
File currentSignedPreKeyIdPath = hierarchy.getCurrentSignedPreKeyIdPath(omemoManager);
|
|
try {
|
|
int i = readInt(currentSignedPreKeyIdPath);
|
|
return i == -1 ? 0 : i;
|
|
} catch (IOException e) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void storeCurrentSignedPreKeyId(OmemoManager omemoManager, int currentSignedPreKeyId) {
|
|
File currentSignedPreKeyIdPath = hierarchy.getCurrentSignedPreKeyIdPath(omemoManager);
|
|
try {
|
|
writeInt(currentSignedPreKeyIdPath, currentSignedPreKeyId);
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write currentSignedPreKeyId "
|
|
+ currentSignedPreKeyId + " for " + omemoManager.getOwnDevice() + ": "
|
|
+ e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public HashMap<Integer, T_PreKey> loadOmemoPreKeys(OmemoManager omemoManager) {
|
|
File preKeyDirectory = hierarchy.getPreKeysDirectory(omemoManager);
|
|
HashMap<Integer, T_PreKey> preKeys = new HashMap<>();
|
|
|
|
if (preKeyDirectory == null) {
|
|
return preKeys;
|
|
}
|
|
|
|
File[] keys = preKeyDirectory.listFiles();
|
|
for (File f : keys != null ? keys : new File[0]) {
|
|
|
|
try {
|
|
byte[] bytes = readBytes(f);
|
|
if (bytes == null) {
|
|
continue;
|
|
}
|
|
T_PreKey p = keyUtil().preKeyFromBytes(bytes);
|
|
preKeys.put(Integer.parseInt(f.getName()), p);
|
|
|
|
} catch (IOException e) {
|
|
//Do nothing
|
|
}
|
|
}
|
|
return preKeys;
|
|
}
|
|
|
|
@Override
|
|
public T_SigPreKey loadOmemoSignedPreKey(OmemoManager omemoManager, int signedPreKeyId) {
|
|
File signedPreKeyPath = new File(hierarchy.getSignedPreKeysDirectory(omemoManager), Integer.toString(signedPreKeyId));
|
|
try {
|
|
byte[] bytes = readBytes(signedPreKeyPath);
|
|
return bytes != null ? keyUtil().signedPreKeyFromBytes(bytes) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public HashMap<Integer, T_SigPreKey> loadOmemoSignedPreKeys(OmemoManager omemoManager) {
|
|
File signedPreKeysDirectory = hierarchy.getSignedPreKeysDirectory(omemoManager);
|
|
HashMap<Integer, T_SigPreKey> signedPreKeys = new HashMap<>();
|
|
|
|
if (signedPreKeysDirectory == null) {
|
|
return signedPreKeys;
|
|
}
|
|
|
|
File[] keys = signedPreKeysDirectory.listFiles();
|
|
for (File f : keys != null ? keys : new File[0]) {
|
|
|
|
try {
|
|
byte[] bytes = readBytes(f);
|
|
if (bytes == null) {
|
|
continue;
|
|
}
|
|
T_SigPreKey p = keyUtil().signedPreKeyFromBytes(bytes);
|
|
signedPreKeys.put(Integer.parseInt(f.getName()), p);
|
|
|
|
} catch (IOException e) {
|
|
//Do nothing
|
|
}
|
|
}
|
|
return signedPreKeys;
|
|
}
|
|
|
|
@Override
|
|
public void storeOmemoSignedPreKey(OmemoManager omemoManager, int signedPreKeyId, T_SigPreKey signedPreKey) {
|
|
File signedPreKeyPath = new File(hierarchy.getSignedPreKeysDirectory(omemoManager), Integer.toString(signedPreKeyId));
|
|
try {
|
|
writeBytes(signedPreKeyPath, keyUtil().signedPreKeyToBytes(signedPreKey));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write signedPreKey " + signedPreKey
|
|
+ " for " + omemoManager.getOwnDevice() + ": " + e, e);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeOmemoSignedPreKey(OmemoManager omemoManager, int signedPreKeyId) {
|
|
File signedPreKeyPath = new File(hierarchy.getSignedPreKeysDirectory(omemoManager), Integer.toString(signedPreKeyId));
|
|
signedPreKeyPath.delete();
|
|
}
|
|
|
|
@Override
|
|
public T_Sess loadRawSession(OmemoManager omemoManager, OmemoDevice device) {
|
|
File sessionPath = hierarchy.getContactsSessionPath(omemoManager, device);
|
|
try {
|
|
byte[] bytes = readBytes(sessionPath);
|
|
return bytes != null ? keyUtil().rawSessionFromBytes(bytes) : null;
|
|
} catch (IOException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public HashMap<Integer, T_Sess> loadAllRawSessionsOf(OmemoManager omemoManager, BareJid contact) {
|
|
File contactsDirectory = hierarchy.getContactsDir(omemoManager, contact);
|
|
HashMap<Integer, T_Sess> sessions = new HashMap<>();
|
|
String[] devices = contactsDirectory.list();
|
|
|
|
for (String deviceId : devices != null ? devices : new String[0]) {
|
|
int id;
|
|
try {
|
|
id = Integer.parseInt(deviceId);
|
|
} catch (NumberFormatException e) {
|
|
continue;
|
|
}
|
|
OmemoDevice device = new OmemoDevice(contact, id);
|
|
File session = hierarchy.getContactsSessionPath(omemoManager, device);
|
|
|
|
try {
|
|
byte[] bytes = readBytes(session);
|
|
if (bytes == null) {
|
|
continue;
|
|
}
|
|
T_Sess s = keyUtil().rawSessionFromBytes(bytes);
|
|
sessions.put(id, s);
|
|
|
|
} catch (IOException e) {
|
|
//Do nothing
|
|
}
|
|
}
|
|
return sessions;
|
|
}
|
|
|
|
@Override
|
|
public void storeRawSession(OmemoManager omemoManager, OmemoDevice device, T_Sess session) {
|
|
File sessionPath = hierarchy.getContactsSessionPath(omemoManager, device);
|
|
try {
|
|
writeBytes(sessionPath, keyUtil().rawSessionToBytes(session));
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write session between our device " + omemoManager.getOwnDevice()
|
|
+ " and their device " + device + ": " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeRawSession(OmemoManager omemoManager, OmemoDevice device) {
|
|
File sessionPath = hierarchy.getContactsSessionPath(omemoManager, device);
|
|
sessionPath.delete();
|
|
}
|
|
|
|
@Override
|
|
public void removeAllRawSessionsOf(OmemoManager omemoManager, BareJid contact) {
|
|
File contactsDirectory = hierarchy.getContactsDir(omemoManager, contact);
|
|
String[] devices = contactsDirectory.list();
|
|
|
|
for (String deviceId : devices != null ? devices : new String[0]) {
|
|
int id;
|
|
try {
|
|
id = Integer.parseInt(deviceId);
|
|
} catch (NumberFormatException e) {
|
|
continue;
|
|
}
|
|
OmemoDevice device = new OmemoDevice(contact, id);
|
|
File session = hierarchy.getContactsSessionPath(omemoManager, device);
|
|
session.delete();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean containsRawSession(OmemoManager omemoManager, OmemoDevice device) {
|
|
File session = hierarchy.getContactsSessionPath(omemoManager, device);
|
|
return session.exists();
|
|
}
|
|
|
|
@Override
|
|
public CachedDeviceList loadCachedDeviceList(OmemoManager omemoManager, BareJid contact) {
|
|
CachedDeviceList cachedDeviceList = new CachedDeviceList();
|
|
|
|
if (contact == null) {
|
|
return null;
|
|
}
|
|
|
|
//active
|
|
File activeDevicesPath = hierarchy.getContactsActiveDevicesPath(omemoManager, contact);
|
|
try {
|
|
cachedDeviceList.getActiveDevices().addAll(readIntegers(activeDevicesPath));
|
|
} catch (IOException e) {
|
|
// Don't worry...
|
|
}
|
|
|
|
//inactive
|
|
File inactiveDevicesPath = hierarchy.getContactsInactiveDevicesPath(omemoManager, contact);
|
|
try {
|
|
cachedDeviceList.getInactiveDevices().addAll(readIntegers(inactiveDevicesPath));
|
|
} catch (IOException e) {
|
|
//It's ok :)
|
|
}
|
|
|
|
return cachedDeviceList;
|
|
}
|
|
|
|
@Override
|
|
public void storeCachedDeviceList(OmemoManager omemoManager, BareJid contact, CachedDeviceList deviceList) {
|
|
if (contact == null) {
|
|
return;
|
|
}
|
|
|
|
File activeDevices = hierarchy.getContactsActiveDevicesPath(omemoManager, contact);
|
|
try {
|
|
writeIntegers(activeDevices, deviceList.getActiveDevices());
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write active devices of deviceList of "
|
|
+ contact + ": " + e.getMessage());
|
|
}
|
|
|
|
File inactiveDevices = hierarchy.getContactsInactiveDevicesPath(omemoManager, contact);
|
|
try {
|
|
writeIntegers(inactiveDevices, deviceList.getInactiveDevices());
|
|
} catch (IOException e) {
|
|
LOGGER.log(Level.SEVERE, "Could not write inactive devices of deviceList of "
|
|
+ contact + ": " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void purgeOwnDeviceKeys(OmemoManager omemoManager) {
|
|
File deviceDirectory = hierarchy.getUserDeviceDirectory(omemoManager);
|
|
deleteDirectory(deviceDirectory);
|
|
}
|
|
|
|
private void writeInt(File target, int i) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not write integer to null-path.");
|
|
}
|
|
|
|
FileHierarchy.createFile(target);
|
|
|
|
IOException io = null;
|
|
DataOutputStream out = null;
|
|
try {
|
|
out = new DataOutputStream(new FileOutputStream(target));
|
|
out.writeInt(i);
|
|
} catch (IOException e) {
|
|
io = e;
|
|
} finally {
|
|
if (out != null) {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
}
|
|
|
|
private int readInt(File target) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not read integer from null-path.");
|
|
}
|
|
|
|
IOException io = null;
|
|
int i = -1;
|
|
DataInputStream in = null;
|
|
|
|
try {
|
|
in = new DataInputStream(new FileInputStream(target));
|
|
i = in.readInt();
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (in != null) {
|
|
in.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
private void writeLong(File target, long i) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not write long to null-path.");
|
|
}
|
|
|
|
FileHierarchy.createFile(target);
|
|
|
|
IOException io = null;
|
|
DataOutputStream out = null;
|
|
try {
|
|
out = new DataOutputStream(new FileOutputStream(target));
|
|
out.writeLong(i);
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (out != null) {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
}
|
|
|
|
private long readLong(File target) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not read long from null-path.");
|
|
}
|
|
|
|
IOException io = null;
|
|
long l = -1;
|
|
DataInputStream in = null;
|
|
|
|
try {
|
|
in = new DataInputStream(new FileInputStream(target));
|
|
l = in.readLong();
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (in != null) {
|
|
in.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
|
|
return l;
|
|
}
|
|
|
|
private void writeBytes(File target, byte[] bytes) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not write bytes to null-path.");
|
|
}
|
|
|
|
//Create file
|
|
FileHierarchy.createFile(target);
|
|
|
|
IOException io = null;
|
|
DataOutputStream out = null;
|
|
|
|
try {
|
|
out = new DataOutputStream(new FileOutputStream(target));
|
|
out.write(bytes);
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (out != null) {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
}
|
|
|
|
private byte[] readBytes(File target) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not read bytes from null-path.");
|
|
}
|
|
|
|
byte[] b = null;
|
|
IOException io = null;
|
|
DataInputStream in = null;
|
|
|
|
try {
|
|
in = new DataInputStream(new FileInputStream(target));
|
|
b = new byte[in.available()];
|
|
in.read(b);
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (in != null) {
|
|
in.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
private void writeIntegers(File target, Set<Integer> integers) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not write integers to null-path.");
|
|
}
|
|
|
|
IOException io = null;
|
|
DataOutputStream out = null;
|
|
|
|
try {
|
|
out = new DataOutputStream(new FileOutputStream(target));
|
|
for (int i : integers) {
|
|
out.writeInt(i);
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (out != null) {
|
|
out.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
}
|
|
|
|
private Set<Integer> readIntegers(File target) throws IOException {
|
|
if (target == null) {
|
|
throw new IOException("Could not write integers to null-path.");
|
|
}
|
|
|
|
HashSet<Integer> integers = new HashSet<>();
|
|
IOException io = null;
|
|
DataInputStream in = null;
|
|
|
|
try {
|
|
in = new DataInputStream(new FileInputStream(target));
|
|
|
|
try {
|
|
while (true) {
|
|
integers.add(in.readInt());
|
|
}
|
|
} catch (EOFException e) {
|
|
//Reached end of the list.
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
io = e;
|
|
|
|
} finally {
|
|
if (in != null) {
|
|
in.close();
|
|
}
|
|
}
|
|
|
|
if (io != null) {
|
|
throw io;
|
|
}
|
|
|
|
return integers;
|
|
}
|
|
|
|
public static void deleteDirectory(File root) {
|
|
File[] currList;
|
|
Stack<File> stack = new Stack<>();
|
|
stack.push(root);
|
|
while (!stack.isEmpty()) {
|
|
if (stack.lastElement().isDirectory()) {
|
|
currList = stack.lastElement().listFiles();
|
|
if (currList != null && currList.length > 0) {
|
|
for (File curr : currList) {
|
|
stack.push(curr);
|
|
}
|
|
} else {
|
|
stack.pop().delete();
|
|
}
|
|
} else {
|
|
stack.pop().delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class represents the directory structure of the FileBasedOmemoStoreV2.
|
|
* The directory looks as follows:
|
|
*
|
|
* OMEMO_Store/
|
|
* 'romeo@montague.lit'/ //Our bareJid
|
|
* ...
|
|
* 'juliet@capulet.lit'/ //Our other bareJid
|
|
* defaultDeviceId
|
|
* '13371234'/ //deviceId
|
|
* identityKeyPair //Our identityKeyPair
|
|
* lastPreKeyId //Id of the last preKey we generated
|
|
* currentSignedPreKeyId //Id of the currently used signedPreKey
|
|
* lastSignedPreKeyRenewal //Date of when the signedPreKey was last renewed.
|
|
* preKeys/ //Our preKeys
|
|
* '1'
|
|
* '2'
|
|
* ...
|
|
* signedPreKeys/ //Our signedPreKeys
|
|
* '1'
|
|
* '2'
|
|
* ...
|
|
* contacts/
|
|
* 'romeo@capulet.lit'/ //Juliets contact Romeo
|
|
* activeDevice //List of Romeos active devices
|
|
* inactiveDevices //List of his inactive devices
|
|
* 'deviceId'/ //Romeos deviceId
|
|
* identityKey //Romeos identityKey
|
|
* session //Our session with romeo
|
|
* trust //Records about the trust in romeos device
|
|
* (lastReceivedMessageDate) //Only, for our own other devices:
|
|
* //date of the last received message
|
|
*
|
|
*/
|
|
public static class FileHierarchy {
|
|
|
|
static final String STORE = "OMEMO_Store";
|
|
static final String CONTACTS = "contacts";
|
|
static final String DEFAULT_DEVICE_ID = "defaultDeviceId";
|
|
static final String IDENTITY_KEY = "identityKey";
|
|
static final String IDENTITY_KEY_PAIR = "identityKeyPair";
|
|
static final String PRE_KEYS = "preKeys";
|
|
static final String LAST_MESSAGE_RECEVIED_DATE = "lastMessageReceivedDate";
|
|
static final String LAST_PRE_KEY_ID = "lastPreKeyId";
|
|
static final String SIGNED_PRE_KEYS = "signedPreKeys";
|
|
static final String CURRENT_SIGNED_PRE_KEY_ID = "currentSignedPreKeyId";
|
|
static final String LAST_SIGNED_PRE_KEY_RENEWAL = "lastSignedPreKeyRenewal";
|
|
static final String SESSION = "session";
|
|
static final String DEVICE_LIST_ACTIVE = "activeDevices";
|
|
static final String DEVICE_LIST_INAVTIVE = "inactiveDevices";
|
|
static final String TRUST = "trust";
|
|
|
|
File basePath;
|
|
|
|
FileHierarchy(File basePath) {
|
|
this.basePath = basePath;
|
|
basePath.mkdirs();
|
|
}
|
|
|
|
File getStoreDirectory() {
|
|
return createDirectory(basePath, STORE);
|
|
}
|
|
|
|
File getUserDirectory(BareJid bareJid) {
|
|
return createDirectory(getStoreDirectory(), bareJid.toString());
|
|
}
|
|
|
|
File getUserDeviceDirectory(OmemoManager omemoManager) {
|
|
return createDirectory(getUserDirectory(omemoManager.getOwnJid()),
|
|
Integer.toString(omemoManager.getDeviceId()));
|
|
}
|
|
|
|
File getContactsDir(OmemoManager omemoManager) {
|
|
return createDirectory(getUserDeviceDirectory(omemoManager), CONTACTS);
|
|
}
|
|
|
|
File getContactsDir(OmemoManager omemoManager, BareJid contact) {
|
|
return createDirectory(getContactsDir(omemoManager), contact.toString());
|
|
}
|
|
|
|
File getContactsDir(OmemoManager omemoManager, OmemoDevice omemoDevice) {
|
|
return createDirectory(getContactsDir(omemoManager, omemoDevice.getJid()),
|
|
Integer.toString(omemoDevice.getDeviceId()));
|
|
}
|
|
|
|
File getIdentityKeyPairPath(OmemoManager omemoManager) {
|
|
return new File(getUserDeviceDirectory(omemoManager), IDENTITY_KEY_PAIR);
|
|
}
|
|
|
|
File getPreKeysDirectory(OmemoManager omemoManager) {
|
|
return createDirectory(getUserDeviceDirectory(omemoManager), PRE_KEYS);
|
|
}
|
|
|
|
File getPreKeyPath(OmemoManager omemoManager, int preKeyId) {
|
|
return new File(getPreKeysDirectory(omemoManager), Integer.toString(preKeyId));
|
|
}
|
|
|
|
File getLastMessageReceivedDatePath(OmemoManager omemoManager, OmemoDevice device) {
|
|
return new File(getContactsDir(omemoManager, device), LAST_MESSAGE_RECEVIED_DATE);
|
|
}
|
|
|
|
File getLastPreKeyIdPath(OmemoManager omemoManager) {
|
|
return new File(getUserDeviceDirectory(omemoManager), LAST_PRE_KEY_ID);
|
|
}
|
|
|
|
File getSignedPreKeysDirectory(OmemoManager omemoManager) {
|
|
return createDirectory(getUserDeviceDirectory(omemoManager), SIGNED_PRE_KEYS);
|
|
}
|
|
|
|
File getCurrentSignedPreKeyIdPath(OmemoManager omemoManager) {
|
|
return new File(getUserDeviceDirectory(omemoManager), CURRENT_SIGNED_PRE_KEY_ID);
|
|
}
|
|
|
|
File getLastSignedPreKeyRenewal(OmemoManager omemoManager) {
|
|
return new File(getUserDeviceDirectory(omemoManager), LAST_SIGNED_PRE_KEY_RENEWAL);
|
|
}
|
|
|
|
File getDefaultDeviceIdPath(BareJid bareJid) {
|
|
return new File(getUserDirectory(bareJid), DEFAULT_DEVICE_ID);
|
|
}
|
|
|
|
File getContactsIdentityKeyPath(OmemoManager omemoManager, OmemoDevice omemoDevice) {
|
|
return new File(getContactsDir(omemoManager, omemoDevice), IDENTITY_KEY);
|
|
|
|
}
|
|
|
|
File getContactsSessionPath(OmemoManager omemoManager, OmemoDevice omemoDevice) {
|
|
return new File(getContactsDir(omemoManager, omemoDevice), SESSION);
|
|
}
|
|
|
|
File getContactsActiveDevicesPath(OmemoManager omemoManager, BareJid contact) {
|
|
return new File(getContactsDir(omemoManager, contact), DEVICE_LIST_ACTIVE);
|
|
}
|
|
|
|
File getContactsInactiveDevicesPath(OmemoManager omemoManager, BareJid contact) {
|
|
return new File(getContactsDir(omemoManager, contact), DEVICE_LIST_INAVTIVE);
|
|
}
|
|
|
|
File getContactsTrustPath(OmemoManager omemoManager, OmemoDevice omemoDevice) {
|
|
return new File(getContactsDir(omemoManager, omemoDevice), TRUST);
|
|
|
|
}
|
|
|
|
private static File createFile(File f) throws IOException {
|
|
File p = f.getParentFile();
|
|
createDirectory(p);
|
|
f.createNewFile();
|
|
return f;
|
|
|
|
}
|
|
|
|
private static File createFile(File dir, String filename) throws IOException {
|
|
return createFile(new File(dir, filename));
|
|
}
|
|
|
|
private static File createDirectory(File dir, String subdir) {
|
|
File f = new File(dir, subdir);
|
|
return createDirectory(f);
|
|
}
|
|
|
|
private static File createDirectory(File f) {
|
|
if (f.exists() && f.isDirectory()) {
|
|
return f;
|
|
}
|
|
|
|
f.mkdirs();
|
|
return f;
|
|
}
|
|
}
|
|
}
|