mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-24 04:52:05 +01:00
Add methods to store update dates
This commit is contained in:
parent
48862962db
commit
dab342e97e
2 changed files with 120 additions and 15 deletions
|
@ -23,13 +23,16 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -74,6 +77,7 @@ import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBu
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
||||||
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
|
import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
|
||||||
public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
|
|
||||||
|
@ -133,8 +137,8 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<OpenPgpV4Fingerprint> announcedOpenPgpKeyFingerprints(BareJid contact) {
|
public Map<OpenPgpV4Fingerprint, Date> announcedOpenPgpKeyFingerprints(BareJid contact) {
|
||||||
Set<OpenPgpV4Fingerprint> announcedKeys = new HashSet<>();
|
Map<OpenPgpV4Fingerprint, Date> announcedKeys = new HashMap<>();
|
||||||
File listPath = contactsList(contact);
|
File listPath = contactsList(contact);
|
||||||
if (listPath.exists() && listPath.isFile()) {
|
if (listPath.exists() && listPath.isFile()) {
|
||||||
BufferedReader reader = null;
|
BufferedReader reader = null;
|
||||||
|
@ -149,12 +153,25 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String[] split = line.split(" ");
|
||||||
|
|
||||||
|
OpenPgpV4Fingerprint fingerprint;
|
||||||
|
Date date = null;
|
||||||
try {
|
try {
|
||||||
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(line);
|
fingerprint = new OpenPgpV4Fingerprint(split[0]);
|
||||||
announcedKeys.add(fingerprint);
|
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
LOGGER.log(Level.INFO, "Skip malformed fingerprint " + line + " of " + contact.toString());
|
LOGGER.log(Level.INFO, "Skip malformed fingerprint " + line + " of " + contact.toString());
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (split.length > 1)
|
||||||
|
date = XmppDateTime.parseXEP0082Date(split[1]);
|
||||||
|
}
|
||||||
|
catch (ParseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not parse date", e);
|
||||||
|
}
|
||||||
|
announcedKeys.put(fingerprint, date);
|
||||||
}
|
}
|
||||||
reader.close();
|
reader.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -204,8 +221,11 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
writer = new BufferedWriter(new OutputStreamWriter(
|
writer = new BufferedWriter(new OutputStreamWriter(
|
||||||
new FileOutputStream(listPath), "UTF8"));
|
new FileOutputStream(listPath), "UTF8"));
|
||||||
|
|
||||||
for (OpenPgpV4Fingerprint fingerprint : listElement.getMetadata().keySet()) {
|
for (PublicKeysListElement.PubkeyMetadataElement entry : listElement.getMetadata().values()) {
|
||||||
writer.write(fingerprint.toString());
|
OpenPgpV4Fingerprint fingerprint = entry.getV4Fingerprint();
|
||||||
|
Date date = entry.getDate();
|
||||||
|
String line = fingerprint.toString() + (date != null ? date : "");
|
||||||
|
writer.write(line);
|
||||||
writer.newLine();
|
writer.newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +258,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
public void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element, Date latestMetadataDate)
|
||||||
throws SmackOpenPgpException {
|
throws SmackOpenPgpException {
|
||||||
byte[] base64decoded = Base64.decode(element.getDataElement().getB64Data());
|
byte[] base64decoded = Base64.decode(element.getDataElement().getB64Data());
|
||||||
try {
|
try {
|
||||||
|
@ -249,6 +269,13 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
LOGGER.log(Level.WARNING, "Public Key with ID " + fingerprint.toString() + " of " +
|
LOGGER.log(Level.WARNING, "Public Key with ID " + fingerprint.toString() + " of " +
|
||||||
owner + " is already in memory. Skip.");
|
owner + " is already in memory. Skip.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
writeDateToFile(publicKeyUpdateDatePath(owner, fingerprint), latestMetadataDate);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not store update date for " + fingerprint.toString(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,7 +307,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
.getSecretKey(Util.keyIdFromFingerprint(fingerprint));
|
.getSecretKey(Util.keyIdFromFingerprint(fingerprint));
|
||||||
|
|
||||||
if (secretKey == null) {
|
if (secretKey == null) {
|
||||||
// TODO: Close streams
|
buffer.close();
|
||||||
throw new MissingOpenPgpKeyPairException(user);
|
throw new MissingOpenPgpKeyPairException(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,7 +330,7 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
public void restoreSecretKeyBackup(SecretkeyElement secretkeyElement, String password, SecretKeyRestoreSelectionCallback callback)
|
||||||
throws SmackOpenPgpException, InvalidBackupCodeException {
|
throws SmackOpenPgpException, InvalidBackupCodeException { // TODO: Figure out InvalidBackupCodeException
|
||||||
byte[] base64Decoded = Base64.decode(secretkeyElement.getB64Data());
|
byte[] base64Decoded = Base64.decode(secretkeyElement.getB64Data());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -369,6 +396,11 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getPubkeysLatestUpdateDate(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||||
|
return readDateFromFile(publicKeyUpdateDatePath(owner, fingerprint));
|
||||||
|
}
|
||||||
|
|
||||||
private File secretKeyringPath() {
|
private File secretKeyringPath() {
|
||||||
return new File(contactsPath(user), "secring.skr");
|
return new File(contactsPath(user), "secring.skr");
|
||||||
}
|
}
|
||||||
|
@ -389,6 +421,66 @@ public class FileBasedBcOpenPgpStore implements BCOpenPgpStore {
|
||||||
return new File(contactsPath(contact), "metadata.list");
|
return new File(contactsPath(contact), "metadata.list");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File publicKeyUpdateDatePath(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
|
||||||
|
return new File(contactsPath(owner), fingerprint.toString() + "-update.date");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void writeDateToFile(File file, Date date) throws IOException {
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.getParentFile().mkdirs();
|
||||||
|
file.createNewFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedWriter writer = null;
|
||||||
|
try {
|
||||||
|
writer = new BufferedWriter(new OutputStreamWriter(
|
||||||
|
new FileOutputStream(file), "UTF8"));
|
||||||
|
writer.write(XmppDateTime.formatXEP0082Date(date));
|
||||||
|
writer.flush();
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (writer != null) {
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Date readDateFromFile(File file) {
|
||||||
|
if (!file.exists()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedReader reader = null;
|
||||||
|
Date result = null;
|
||||||
|
try {
|
||||||
|
reader = new BufferedReader(new InputStreamReader(
|
||||||
|
new FileInputStream(file), "UTF8"));
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (!line.isEmpty()) {
|
||||||
|
result = XmppDateTime.parseXEP0082Date(line);
|
||||||
|
}
|
||||||
|
reader.close();
|
||||||
|
reader = null;
|
||||||
|
} catch (UnsupportedEncodingException | FileNotFoundException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Exception while reading date.", e);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not parse date", e);
|
||||||
|
result = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not close reader.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private static void addPublicKeysFromFile(InMemoryKeyring keyring,
|
private static void addPublicKeysFromFile(InMemoryKeyring keyring,
|
||||||
File pubring,
|
File pubring,
|
||||||
KeyringConfigCallback passwordCallback)
|
KeyringConfigCallback passwordCallback)
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
|
@ -53,18 +55,18 @@ public interface OpenPgpStore {
|
||||||
Set<OpenPgpV4Fingerprint> availableOpenPgpKeyPairFingerprints();
|
Set<OpenPgpV4Fingerprint> availableOpenPgpKeyPairFingerprints();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of all currently announced OpenPGP
|
* Return a {@link Map} containing the {@link OpenPgpV4Fingerprint}s of all currently announced OpenPGP
|
||||||
* public keys of a contact.
|
* public keys of a contact along with the dates of their latest update.
|
||||||
* <br>
|
* <br>
|
||||||
* Note: Those are the keys announced in the latest received metadata update.
|
* Note: Those are the keys announced in the latest received metadata update.
|
||||||
* This returns a {@link Set} which might be different from the result of
|
* This returns a {@link Map} which might contain different {@link OpenPgpV4Fingerprint}s than the result of
|
||||||
* {@link #availableOpenPgpPublicKeysFingerprints(BareJid)}.
|
* {@link #availableOpenPgpPublicKeysFingerprints(BareJid)}.
|
||||||
* Messages should be encrypted to the intersection of both sets.
|
* Messages should be encrypted to the intersection of both sets.
|
||||||
*
|
*
|
||||||
* @param contact contact.
|
* @param contact contact.
|
||||||
* @return list of contacts last announced public keys.
|
* @return map of contacts last announced public keys and their update dates.
|
||||||
*/
|
*/
|
||||||
Set<OpenPgpV4Fingerprint> announcedOpenPgpKeyFingerprints(BareJid contact);
|
Map<OpenPgpV4Fingerprint, Date> announcedOpenPgpKeyFingerprints(BareJid contact);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of all OpenPGP public keys of a
|
* Return a {@link Set} containing the {@link OpenPgpV4Fingerprint}s of all OpenPGP public keys of a
|
||||||
|
@ -95,6 +97,7 @@ public interface OpenPgpStore {
|
||||||
* (example: {@code "xmpp:juliet@capulet.lit"}).
|
* (example: {@code "xmpp:juliet@capulet.lit"}).
|
||||||
* Store the key pair in persistent storage and return the public keys {@link OpenPgpV4Fingerprint}.
|
* Store the key pair in persistent storage and return the public keys {@link OpenPgpV4Fingerprint}.
|
||||||
*
|
*
|
||||||
|
* @return {@link OpenPgpV4Fingerprint} of the generated key pair.
|
||||||
* @throws NoSuchAlgorithmException if a Hash algorithm is not available
|
* @throws NoSuchAlgorithmException if a Hash algorithm is not available
|
||||||
* @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider)
|
* @throws NoSuchProviderException id no suitable cryptographic provider (for example BouncyCastleProvider)
|
||||||
* is registered.
|
* is registered.
|
||||||
|
@ -121,12 +124,22 @@ public interface OpenPgpStore {
|
||||||
* @param owner owner of the OpenPGP public key contained in the {@link PubkeyElement}.
|
* @param owner owner of the OpenPGP public key contained in the {@link PubkeyElement}.
|
||||||
* @param fingerprint {@link OpenPgpV4Fingerprint} of the key.
|
* @param fingerprint {@link OpenPgpV4Fingerprint} of the key.
|
||||||
* @param element {@link PubkeyElement} which presumably contains the public key of the {@code owner}.
|
* @param element {@link PubkeyElement} which presumably contains the public key of the {@code owner}.
|
||||||
|
* @param currentMetadataDate {@link Date} which is currently found in the metadata node for this key.
|
||||||
* @throws SmackOpenPgpException if the key found in the {@link PubkeyElement}
|
* @throws SmackOpenPgpException if the key found in the {@link PubkeyElement}
|
||||||
* can not be deserialized or imported.
|
* can not be deserialized or imported.
|
||||||
*/
|
*/
|
||||||
void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element)
|
void storePublicKey(BareJid owner, OpenPgpV4Fingerprint fingerprint, PubkeyElement element, Date currentMetadataDate)
|
||||||
throws SmackOpenPgpException;
|
throws SmackOpenPgpException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link Date} of the last time on which the key has been fetched from PubSub.
|
||||||
|
*
|
||||||
|
* @param owner owner of the key
|
||||||
|
* @param fingerprint fingerprint of the key.
|
||||||
|
* @return {@link Date} or {@code null} if no record found.
|
||||||
|
*/
|
||||||
|
Date getPubkeysLatestUpdateDate(BareJid owner, OpenPgpV4Fingerprint fingerprint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an encrypted backup of our secret keys.
|
* Create an encrypted backup of our secret keys.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in a new issue