Smack/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/FileBasedOmemoStore.java

879 lines
31 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.FileNotFoundException;
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.SortedSet;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
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 final FileHierarchy hierarchy;
private static final Logger LOGGER = Logger.getLogger(FileBasedOmemoStore.class.getName());
public FileBasedOmemoStore(File basePath) {
super();
if (basePath == null) {
throw new IllegalStateException("No FileBasedOmemoStoreDefaultPath set in OmemoConfiguration.");
}
this.hierarchy = new FileHierarchy(basePath);
}
@Override
public T_IdKeyPair loadOmemoIdentityKeyPair(OmemoDevice userDevice)
throws CorruptedOmemoKeyException {
File identityKeyPairPath = hierarchy.getIdentityKeyPairPath(userDevice);
return keyUtil().identityKeyPairFromBytes(readBytes(identityKeyPairPath));
}
@Override
public void storeOmemoIdentityKeyPair(OmemoDevice userDevice, T_IdKeyPair identityKeyPair) {
File identityKeyPairPath = hierarchy.getIdentityKeyPairPath(userDevice);
writeBytes(identityKeyPairPath, keyUtil().identityKeyPairToBytes(identityKeyPair));
}
@Override
public void removeOmemoIdentityKeyPair(OmemoDevice userDevice) {
File identityKeyPairPath = hierarchy.getIdentityKeyPairPath(userDevice);
if (!identityKeyPairPath.delete()) {
LOGGER.log(Level.WARNING, "Could not delete OMEMO IdentityKeyPair " + identityKeyPairPath.getAbsolutePath());
}
}
@Override
public T_IdKey loadOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice)
throws CorruptedOmemoKeyException {
File identityKeyPath = hierarchy.getContactsIdentityKeyPath(userDevice, contactsDevice);
byte[] bytes = readBytes(identityKeyPath);
return bytes != null ? keyUtil().identityKeyFromBytes(bytes) : null;
}
@Override
public void storeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice, T_IdKey t_idKey) {
File identityKeyPath = hierarchy.getContactsIdentityKeyPath(userDevice, contactsDevice);
writeBytes(identityKeyPath, keyUtil().identityKeyToBytes(t_idKey));
}
@Override
public void removeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File identityKeyPath = hierarchy.getContactsIdentityKeyPath(userDevice, contactsDevice);
if (!identityKeyPath.delete()) {
LOGGER.log(Level.WARNING, "Could not delete OMEMO identityKey " + identityKeyPath.getAbsolutePath());
}
}
@Override
public SortedSet<Integer> localDeviceIdsOf(BareJid localUser) {
SortedSet<Integer> deviceIds = new TreeSet<>();
File userDir = hierarchy.getUserDirectory(localUser);
File[] list = userDir.listFiles();
for (File d : (list != null ? list : new File[] {})) {
if (d.isDirectory()) {
try {
deviceIds.add(Integer.parseInt(d.getName()));
} catch (NumberFormatException e) {
// ignore
}
}
}
return deviceIds;
}
@Override
public void setDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) {
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(userDevice, contactsDevice);
writeLong(lastMessageReceived, date.getTime());
}
@Override
public Date getDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(userDevice, contactsDevice);
Long date = readLong(lastMessageReceived);
return date != null ? new Date(date) : null;
}
@Override
public void setDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) {
File lastDeviceIdPublished = hierarchy.getLastDeviceIdPublicationDatePath(userDevice, contactsDevice);
writeLong(lastDeviceIdPublished, date.getTime());
}
@Override
public Date getDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File lastDeviceIdPublished = hierarchy.getLastDeviceIdPublicationDatePath(userDevice, contactsDevice);
Long date = readLong(lastDeviceIdPublished);
return date != null ? new Date(date) : null;
}
@Override
public void setDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice, Date date) {
File lastSignedPreKeyRenewal = hierarchy.getLastSignedPreKeyRenewal(userDevice);
writeLong(lastSignedPreKeyRenewal, date.getTime());
}
@Override
public Date getDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice) {
File lastSignedPreKeyRenewal = hierarchy.getLastSignedPreKeyRenewal(userDevice);
Long date = readLong(lastSignedPreKeyRenewal);
return date != null ? new Date(date) : null;
}
@Override
public T_PreKey loadOmemoPreKey(OmemoDevice userDevice, int preKeyId) {
File preKeyPath = hierarchy.getPreKeyPath(userDevice, preKeyId);
byte[] bytes = readBytes(preKeyPath);
if (bytes != null) {
try {
return keyUtil().preKeyFromBytes(bytes);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not deserialize preKey from bytes.", e);
}
}
return null;
}
@Override
public void storeOmemoPreKey(OmemoDevice userDevice, int preKeyId, T_PreKey t_preKey) {
File preKeyPath = hierarchy.getPreKeyPath(userDevice, preKeyId);
writeBytes(preKeyPath, keyUtil().preKeyToBytes(t_preKey));
}
@Override
public void removeOmemoPreKey(OmemoDevice userDevice, int preKeyId) {
File preKeyPath = hierarchy.getPreKeyPath(userDevice, preKeyId);
if (!preKeyPath.delete()) {
LOGGER.log(Level.WARNING, "Deleting OMEMO preKey " + preKeyPath.getAbsolutePath() + " failed.");
}
}
@Override
public TreeMap<Integer, T_PreKey> loadOmemoPreKeys(OmemoDevice userDevice) {
File preKeyDirectory = hierarchy.getPreKeysDirectory(userDevice);
TreeMap<Integer, T_PreKey> preKeys = new TreeMap<>();
if (preKeyDirectory == null) {
return preKeys;
}
File[] keys = preKeyDirectory.listFiles();
for (File f : keys != null ? keys : new File[0]) {
byte[] bytes = readBytes(f);
if (bytes != null) {
try {
T_PreKey p = keyUtil().preKeyFromBytes(bytes);
preKeys.put(Integer.parseInt(f.getName()), p);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not deserialize preKey from bytes.", e);
}
}
}
return preKeys;
}
@Override
public T_SigPreKey loadOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId) {
File signedPreKeyPath = new File(hierarchy.getSignedPreKeysDirectory(userDevice), Integer.toString(signedPreKeyId));
byte[] bytes = readBytes(signedPreKeyPath);
if (bytes != null) {
try {
return keyUtil().signedPreKeyFromBytes(bytes);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not deserialize signed preKey from bytes.", e);
}
}
return null;
}
@Override
public TreeMap<Integer, T_SigPreKey> loadOmemoSignedPreKeys(OmemoDevice userDevice) {
File signedPreKeysDirectory = hierarchy.getSignedPreKeysDirectory(userDevice);
TreeMap<Integer, T_SigPreKey> signedPreKeys = new TreeMap<>();
if (signedPreKeysDirectory == null) {
return signedPreKeys;
}
File[] keys = signedPreKeysDirectory.listFiles();
for (File f : keys != null ? keys : new File[0]) {
byte[] bytes = readBytes(f);
if (bytes != null) {
try {
T_SigPreKey p = keyUtil().signedPreKeyFromBytes(bytes);
signedPreKeys.put(Integer.parseInt(f.getName()), p);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not deserialize signed preKey.", e);
}
}
}
return signedPreKeys;
}
@Override
public void storeOmemoSignedPreKey(OmemoDevice userDevice,
int signedPreKeyId,
T_SigPreKey signedPreKey) {
File signedPreKeyPath = new File(hierarchy.getSignedPreKeysDirectory(userDevice), Integer.toString(signedPreKeyId));
writeBytes(signedPreKeyPath, keyUtil().signedPreKeyToBytes(signedPreKey));
}
@Override
public void removeOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId) {
File signedPreKeyPath = new File(hierarchy.getSignedPreKeysDirectory(userDevice), Integer.toString(signedPreKeyId));
if (!signedPreKeyPath.delete()) {
LOGGER.log(Level.WARNING, "Deleting signed OMEMO preKey " + signedPreKeyPath.getAbsolutePath() + " failed.");
}
}
@Override
public T_Sess loadRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File sessionPath = hierarchy.getContactsSessionPath(userDevice, contactsDevice);
byte[] bytes = readBytes(sessionPath);
if (bytes != null) {
try {
return keyUtil().rawSessionFromBytes(bytes);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not deserialize raw session.", e);
}
}
return null;
}
@Override
public HashMap<Integer, T_Sess> loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) {
File contactsDirectory = hierarchy.getContactsDir(userDevice, 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(userDevice, device);
byte[] bytes = readBytes(session);
if (bytes != null) {
try {
T_Sess s = keyUtil().rawSessionFromBytes(bytes);
sessions.put(id, s);
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not deserialize raw session.", e);
}
}
}
return sessions;
}
@Override
public void storeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice, T_Sess session) {
File sessionPath = hierarchy.getContactsSessionPath(userDevice, contactsDevice);
writeBytes(sessionPath, keyUtil().rawSessionToBytes(session));
}
@Override
public void removeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File sessionPath = hierarchy.getContactsSessionPath(userDevice, contactsDevice);
if (!sessionPath.delete()) {
LOGGER.log(Level.WARNING, "Deleting raw OMEMO session " + sessionPath.getAbsolutePath() + " failed.");
}
}
@Override
public void removeAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) {
File contactsDirectory = hierarchy.getContactsDir(userDevice, contact);
String[] devices = contactsDirectory.list();
for (String deviceId : devices != null ? devices : new String[0]) {
int id = Integer.parseInt(deviceId);
OmemoDevice device = new OmemoDevice(contact, id);
File session = hierarchy.getContactsSessionPath(userDevice, device);
if (!session.delete()) {
LOGGER.log(Level.WARNING, "Deleting raw OMEMO session " + session.getAbsolutePath() + "failed.");
}
}
}
@Override
public boolean containsRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File session = hierarchy.getContactsSessionPath(userDevice, contactsDevice);
return session.exists();
}
@Override
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
OmemoCachedDeviceList cachedDeviceList = new OmemoCachedDeviceList();
if (contact == null) {
throw new IllegalArgumentException("Contact can not be null.");
}
// active
File activeDevicesPath = hierarchy.getContactsActiveDevicesPath(userDevice, contact);
Set<Integer> active = readIntegers(activeDevicesPath);
if (active != null) {
cachedDeviceList.getActiveDevices().addAll(active);
}
// inactive
File inactiveDevicesPath = hierarchy.getContactsInactiveDevicesPath(userDevice, contact);
Set<Integer> inactive = readIntegers(inactiveDevicesPath);
if (inactive != null) {
cachedDeviceList.getInactiveDevices().addAll(inactive);
}
return cachedDeviceList;
}
@Override
public void storeCachedDeviceList(OmemoDevice userDevice,
BareJid contact,
OmemoCachedDeviceList contactsDeviceList) {
if (contact == null) {
return;
}
File activeDevices = hierarchy.getContactsActiveDevicesPath(userDevice, contact);
writeIntegers(activeDevices, contactsDeviceList.getActiveDevices());
File inactiveDevices = hierarchy.getContactsInactiveDevicesPath(userDevice, contact);
writeIntegers(inactiveDevices, contactsDeviceList.getInactiveDevices());
}
@Override
public void purgeOwnDeviceKeys(OmemoDevice userDevice) {
File deviceDirectory = hierarchy.getUserDeviceDirectory(userDevice);
deleteDirectory(deviceDirectory);
}
private static void writeLong(File target, long i) {
if (target == null) {
LOGGER.log(Level.WARNING, "Could not write long to null-path.");
return;
}
try {
FileHierarchy.createFile(target);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not create file.", e);
return;
}
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream(target));
out.writeLong(i);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not write longs to file.", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close OutputStream.", e);
}
}
}
}
private static Long readLong(File target) {
if (target == null) {
LOGGER.log(Level.WARNING, "Could not read long from null-path.");
return null;
}
Long l;
DataInputStream in = null;
try {
in = new DataInputStream(new FileInputStream(target));
l = in.readLong();
} catch (FileNotFoundException e) {
l = null;
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not read long from file.", e);
return null;
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close InputStream.", e);
}
}
}
return l;
}
private static void writeBytes(File target, byte[] bytes) {
if (target == null) {
LOGGER.log(Level.WARNING, "Could not write bytes to null-path.");
return;
}
// Create file
try {
FileHierarchy.createFile(target);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not create file.", e);
return;
}
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream(target));
out.write(bytes);
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not write bytes to file.", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close OutputStream.", e);
}
}
}
}
private static byte[] readBytes(File target) {
if (target == null) {
LOGGER.log(Level.WARNING, "Could not read bytes from null-path.");
return null;
}
byte[] b = null;
DataInputStream in = null;
try {
in = new DataInputStream(new FileInputStream(target));
b = new byte[(int) target.length()];
in.read(b);
} catch (FileNotFoundException e) {
b = null;
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not read bytes from file.", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close InputStream.", e);
}
}
}
return b;
}
private static void writeIntegers(File target, Set<Integer> integers) {
if (target == null) {
LOGGER.log(Level.WARNING, "Could not write integers to null-path.");
return;
}
DataOutputStream out = null;
try {
out = new DataOutputStream(new FileOutputStream(target));
for (int i : integers) {
out.writeInt(i);
}
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not write integers to file.", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close OutputStream.", e);
}
}
}
}
private static Set<Integer> readIntegers(File target) {
if (target == null) {
LOGGER.log(Level.WARNING, "Could not read integers from null-path.");
return null;
}
HashSet<Integer> integers = new HashSet<>();
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 (FileNotFoundException e) {
integers = null;
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not read integers.", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
LOGGER.log(Level.SEVERE, "Could not close InputStream.", e);
}
}
}
return integers;
}
/**
* One day... *sheds a tear*
* TODO Use methods below once Smack's minimum Android API level is 19 or higher
*/
/*
private static void writeLong(File target, long i)
throws IOException
{
if (target == null) {
throw new IOException("Could not write long to null-path.");
}
FileHierarchy.createFile(target);
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(target))) {
out.writeLong(i);
}
}
private static Long readLong(File target)
throws IOException
{
if (target == null) {
throw new IOException("Could not read long from null-path.");
}
if (!target.exists() || !target.isFile()) {
return null;
}
try (DataInputStream in = new DataInputStream(new FileInputStream(target))) {
return in.readLong();
}
}
private static 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);
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(target))) {
out.write(bytes);
}
}
private static byte[] readBytes(File target)
throws IOException
{
if (target == null) {
throw new IOException("Could not read bytes from null-path.");
}
if (!target.exists() || !target.isFile()) {
return null;
}
byte[] b = new byte[(int) target.length()];
try (DataInputStream in = new DataInputStream(new FileInputStream(target))) {
in.read(b);
}
return b;
}
private static void writeIntegers(File target, Set<Integer> integers)
throws IOException
{
if (target == null) {
throw new IOException("Could not write integers to null-path.");
}
FileHierarchy.createFile(target);
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(target))) {
for (int i : integers) {
out.writeInt(i);
}
}
}
private static Set<Integer> readIntegers(File target)
throws IOException
{
if (target == null) {
throw new IOException("Could not write integers to null-path.");
}
if (!target.exists() || !target.isFile()) {
return null;
}
HashSet<Integer> integers = new HashSet<>();
try (DataInputStream in = new DataInputStream(new FileInputStream(target))) {
while (true) {
try {
integers.add(in.readInt());
} catch (EOFException e) {
break;
}
}
}
return integers;
}
*/
/**
* Delete a directory with all subdirectories.
* @param root
*/
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 FileBasedOmemoStore.
* The directory looks as follows:
*
* OMEMO_Store/
* 'romeo@montague.lit'/ //Our bareJid
* ...
* 'juliet@capulet.lit'/ //Our other bareJid
* '13371234'/ //deviceId
* identityKeyPair //Our identityKeyPair
* 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 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_DEVICEID_PUBLICATION_DATE = "lastDeviceIdPublicationDate";
static final String SIGNED_PRE_KEYS = "signedPreKeys";
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";
File basePath;
FileHierarchy(File basePath) {
this.basePath = basePath;
basePath.mkdirs();
}
File getStoreDirectory() {
return createDirectory(basePath, STORE);
}
File getUserDirectory(OmemoDevice userDevice) {
return getUserDirectory(userDevice.getJid());
}
File getUserDirectory(BareJid bareJid) {
return createDirectory(getStoreDirectory(), bareJid.toString());
}
File getUserDeviceDirectory(OmemoDevice userDevice) {
return createDirectory(getUserDirectory(userDevice.getJid()),
Integer.toString(userDevice.getDeviceId()));
}
File getContactsDir(OmemoDevice userDevice) {
return createDirectory(getUserDeviceDirectory(userDevice), CONTACTS);
}
File getContactsDir(OmemoDevice userDevice, BareJid contact) {
return createDirectory(getContactsDir(userDevice), contact.toString());
}
File getContactsDir(OmemoDevice userDevice, OmemoDevice contactsDevice) {
return createDirectory(getContactsDir(userDevice, contactsDevice.getJid()),
Integer.toString(contactsDevice.getDeviceId()));
}
File getIdentityKeyPairPath(OmemoDevice userDevice) {
return new File(getUserDeviceDirectory(userDevice), IDENTITY_KEY_PAIR);
}
File getPreKeysDirectory(OmemoDevice userDevice) {
return createDirectory(getUserDeviceDirectory(userDevice), PRE_KEYS);
}
File getPreKeyPath(OmemoDevice userDevice, int preKeyId) {
return new File(getPreKeysDirectory(userDevice), Integer.toString(preKeyId));
}
File getLastMessageReceivedDatePath(OmemoDevice userDevice, OmemoDevice device) {
return new File(getContactsDir(userDevice, device), LAST_MESSAGE_RECEVIED_DATE);
}
File getLastDeviceIdPublicationDatePath(OmemoDevice userDevice, OmemoDevice device) {
return new File(getContactsDir(userDevice, device), LAST_DEVICEID_PUBLICATION_DATE);
}
File getSignedPreKeysDirectory(OmemoDevice userDevice) {
return createDirectory(getUserDeviceDirectory(userDevice), SIGNED_PRE_KEYS);
}
File getLastSignedPreKeyRenewal(OmemoDevice userDevice) {
return new File(getUserDeviceDirectory(userDevice), LAST_SIGNED_PRE_KEY_RENEWAL);
}
File getContactsIdentityKeyPath(OmemoDevice userDevice, OmemoDevice contactsDevice) {
return new File(getContactsDir(userDevice, contactsDevice), IDENTITY_KEY);
}
File getContactsSessionPath(OmemoDevice userDevice, OmemoDevice contactsDevice) {
return new File(getContactsDir(userDevice, contactsDevice), SESSION);
}
File getContactsActiveDevicesPath(OmemoDevice userDevice, BareJid contact) {
return new File(getContactsDir(userDevice, contact), DEVICE_LIST_ACTIVE);
}
File getContactsInactiveDevicesPath(OmemoDevice userDevice, BareJid contact) {
return new File(getContactsDir(userDevice, contact), DEVICE_LIST_INAVTIVE);
}
private static File createFile(File f) throws IOException {
File p = f.getParentFile();
createDirectory(p);
f.createNewFile();
return f;
}
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;
}
}
}