1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-16 16:44:48 +02:00
Smack/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/FileBasedOmemoStore.java
Florian Schmaus 4133eb175c Replace XPP3 by XmlPullParser interface wrapping StAX and XPP3
Introducing Smack's own XmlPullParser interface which tries to stay as
compatible as possible to XPP3. The interface is used to either wrap
StAX's XMLStreamReader if Smack is used on Java SE, and XPP3's
XmlPullParser if Smack is used on on Android.

Fixes SMACK-591.

Also introduce JUnit 5 and non-strict javadoc projects.
2019-05-06 22:10:50 +02:00

878 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.Collections;
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.smack.util.CloseableUtil;
import org.jivesoftware.smack.util.stringencoder.BareJidEncoder;
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());
private static BareJidEncoder bareJidEncoder = new BareJidEncoder.UrlSafeEncoder();
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 void storeOmemoMessageCounter(OmemoDevice userDevice, OmemoDevice contactsDevice, int counter) {
File messageCounterFile = hierarchy.getDevicesMessageCounterPath(userDevice, contactsDevice);
writeIntegers(messageCounterFile, Collections.singleton(counter));
}
@Override
public int loadOmemoMessageCounter(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File messageCounterFile = hierarchy.getDevicesMessageCounterPath(userDevice, contactsDevice);
Set<Integer> integers = readIntegers(messageCounterFile);
if (integers == null || integers.isEmpty()) {
return 0;
}
return integers.iterator().next();
}
@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 {
CloseableUtil.maybeClose(out, LOGGER);
}
}
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 {
CloseableUtil.maybeClose(in, LOGGER);
}
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 {
CloseableUtil.maybeClose(out, LOGGER);
}
}
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 {
CloseableUtil.maybeClose(in, LOGGER);
}
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 {
CloseableUtil.maybeClose(out, LOGGER);
}
}
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 {
CloseableUtil.maybeClose(in, LOGGER);
}
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";
static final String MESSAGE_COUNTER = "messageCounter";
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(), bareJidEncoder.encode(bareJid));
}
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), bareJidEncoder.encode(contact));
}
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);
}
File getDevicesMessageCounterPath(OmemoDevice userDevice, OmemoDevice otherDevice) {
return new File(getContactsDir(userDevice, otherDevice), MESSAGE_COUNTER);
}
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;
}
}
/**
* Convert {@link BareJid BareJids} to Strings using the legacy {@link BareJid#toString()} method instead of the
* proper, url safe {@link BareJid#asUrlEncodedString()} method.
* While it is highly advised to use the new format, you can use this method to stay backwards compatible to data
* sets created by the old implementation.
*/
@SuppressWarnings("deprecation")
public static void useLegacyBareJidEncoding() {
bareJidEncoder = new BareJidEncoder.LegacyEncoder();
}
}