mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-24 04:52:05 +01:00
Implement FileBasedOpenPgpMetadataStore
This commit is contained in:
parent
2c09218887
commit
4e909abb7e
5 changed files with 124 additions and 49 deletions
|
@ -16,10 +16,13 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox;
|
package org.jivesoftware.smackx.ox;
|
||||||
|
|
||||||
|
import javax.xml.bind.DatatypeConverter;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smackx.ox.util.Util;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.util.encoders.Hex;
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
@ -37,22 +40,22 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
||||||
* XEP-0373 §4.1: The OpenPGP Public-Key Data Node about how to obtain the fingerprint</a>
|
* XEP-0373 §4.1: The OpenPGP Public-Key Data Node about how to obtain the fingerprint</a>
|
||||||
* @param fingerprint hexadecimal representation of the fingerprint.
|
* @param fingerprint hexadecimal representation of the fingerprint.
|
||||||
*/
|
*/
|
||||||
public OpenPgpV4Fingerprint(String fingerprint) {
|
public OpenPgpV4Fingerprint(String fingerprint) throws ParseException {
|
||||||
String fp = Objects.requireNonNull(fingerprint)
|
String fp = Objects.requireNonNull(fingerprint)
|
||||||
.trim()
|
.trim()
|
||||||
.toUpperCase();
|
.toUpperCase();
|
||||||
if (!isValid(fp)) {
|
if (!isValid(fp)) {
|
||||||
throw new IllegalArgumentException("Fingerprint " + fingerprint +
|
throw new ParseException("Fingerprint " + fingerprint +
|
||||||
" does not appear to be a valid OpenPGP v4 fingerprint.");
|
" does not appear to be a valid OpenPGP v4 fingerprint.", 0);
|
||||||
}
|
}
|
||||||
this.fingerprint = fp;
|
this.fingerprint = fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint(byte[] bytes) {
|
public OpenPgpV4Fingerprint(byte[] bytes) throws ParseException {
|
||||||
this(new String(bytes, Charset.forName("UTF-8")));
|
this(new String(bytes, Charset.forName("UTF-8")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenPgpV4Fingerprint(PGPPublicKey key) {
|
public OpenPgpV4Fingerprint(PGPPublicKey key) throws ParseException {
|
||||||
this(Hex.encode(key.getFingerprint()));
|
this(Hex.encode(key.getFingerprint()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,14 +70,18 @@ public class OpenPgpV4Fingerprint implements CharSequence, Comparable<OpenPgpV4F
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the key id of the OpenPGP public key this {@link OpenPgpV4Fingerprint} belongs to.
|
* Return the key id of the OpenPGP public key this {@link OpenPgpV4Fingerprint} belongs to.
|
||||||
* This method uses {@link Util#keyIdFromFingerprint(OpenPgpV4Fingerprint)}.
|
|
||||||
*
|
*
|
||||||
* @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2">
|
* @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2">
|
||||||
* RFC-4880 §12.2: Key IDs and Fingerprints</a>
|
* RFC-4880 §12.2: Key IDs and Fingerprints</a>
|
||||||
* @return key id
|
* @return key id
|
||||||
*/
|
*/
|
||||||
public long getKeyId() {
|
public long getKeyId() {
|
||||||
return Util.keyIdFromFingerprint(this);
|
byte[] bytes = DatatypeConverter.parseHexBinary(this.toString());
|
||||||
|
byte[] lower8Bytes = Arrays.copyOfRange(bytes, 12, 20);
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
|
||||||
|
byteBuffer.put(lower8Bytes);
|
||||||
|
byteBuffer.flip();
|
||||||
|
return byteBuffer.getLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,27 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.util;
|
package org.jivesoftware.smackx.ox.util;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Arrays;
|
|
||||||
import javax.xml.bind.DatatypeConverter;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
|
||||||
|
|
||||||
public class Util {
|
public class Util {
|
||||||
|
|
||||||
/**
|
public static final Charset UTF8 = Charset.forName("UTF-8");
|
||||||
* Calculate the key id of the OpenPGP key, the given {@link OpenPgpV4Fingerprint} belongs to.
|
|
||||||
*
|
|
||||||
* @see <a href="https://tools.ietf.org/html/rfc4880#section-12.2"> RFC-4880 §12.2</a>
|
|
||||||
* @param fingerprint {@link OpenPgpV4Fingerprint}.
|
|
||||||
* @return key id
|
|
||||||
*/
|
|
||||||
public static long keyIdFromFingerprint(OpenPgpV4Fingerprint fingerprint) {
|
|
||||||
byte[] bytes = DatatypeConverter.parseHexBinary(fingerprint.toString());
|
|
||||||
byte[] lower8Bytes = Arrays.copyOfRange(bytes, 12, 20);
|
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocate(8);
|
|
||||||
byteBuffer.put(lower8Bytes);
|
|
||||||
byteBuffer.flip();
|
|
||||||
return byteBuffer.getLong();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
package org.jivesoftware.smackx.ox.v2.store;
|
package org.jivesoftware.smackx.ox.v2.store;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
||||||
|
|
||||||
|
@ -27,11 +27,11 @@ import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
public abstract class AbstractOpenPgpMetadataStore implements OpenPgpMetadataStore {
|
public abstract class AbstractOpenPgpMetadataStore implements OpenPgpMetadataStore {
|
||||||
|
|
||||||
private final Map<BareJid, Set<OpenPgpV4Fingerprint>> announcedFingerprints = new HashMap<>();
|
private final Map<BareJid, Map<OpenPgpV4Fingerprint, Date>> announcedFingerprints = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<OpenPgpV4Fingerprint> getAnnouncedFingerprintsOf(BareJid contact) throws IOException {
|
public Map<OpenPgpV4Fingerprint, Date> getAnnouncedFingerprintsOf(BareJid contact) throws IOException {
|
||||||
Set<OpenPgpV4Fingerprint> fingerprints = announcedFingerprints.get(contact);
|
Map<OpenPgpV4Fingerprint, Date> fingerprints = announcedFingerprints.get(contact);
|
||||||
if (fingerprints == null) {
|
if (fingerprints == null) {
|
||||||
fingerprints = readAnnouncedFingerprintsOf(contact);
|
fingerprints = readAnnouncedFingerprintsOf(contact);
|
||||||
announcedFingerprints.put(contact, fingerprints);
|
announcedFingerprints.put(contact, fingerprints);
|
||||||
|
|
|
@ -17,7 +17,8 @@
|
||||||
package org.jivesoftware.smackx.ox.v2.store;
|
package org.jivesoftware.smackx.ox.v2.store;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Set;
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
|
@ -26,9 +27,9 @@ import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
public interface OpenPgpMetadataStore {
|
public interface OpenPgpMetadataStore {
|
||||||
|
|
||||||
Set<OpenPgpV4Fingerprint> getAnnouncedFingerprintsOf(BareJid contact) throws IOException;
|
Map<OpenPgpV4Fingerprint, Date> getAnnouncedFingerprintsOf(BareJid contact) throws IOException;
|
||||||
|
|
||||||
Set<OpenPgpV4Fingerprint> readAnnouncedFingerprintsOf(BareJid contact) throws IOException;
|
Map<OpenPgpV4Fingerprint, Date> readAnnouncedFingerprintsOf(BareJid contact) throws IOException;
|
||||||
|
|
||||||
void writeAnnouncedFingerprintsOf(BareJid contact, PublicKeysListElement metadata) throws IOException;
|
void writeAnnouncedFingerprintsOf(BareJid contact, PublicKeysListElement metadata) throws IOException;
|
||||||
|
|
||||||
|
|
|
@ -16,24 +16,32 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.ox.v2.store.filebased;
|
package org.jivesoftware.smackx.ox.v2.store.filebased;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.ox.util.FileUtils.prepareFileInputStream;
|
import java.io.BufferedReader;
|
||||||
import static org.jivesoftware.smackx.ox.util.FileUtils.prepareFileOutputStream;
|
import java.io.BufferedWriter;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.nio.file.Files;
|
||||||
import java.io.OutputStream;
|
import java.text.ParseException;
|
||||||
import java.util.Set;
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
import org.jivesoftware.smackx.ox.OpenPgpV4Fingerprint;
|
||||||
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
import org.jivesoftware.smackx.ox.element.PublicKeysListElement;
|
||||||
|
import org.jivesoftware.smackx.ox.util.Util;
|
||||||
import org.jivesoftware.smackx.ox.v2.store.AbstractOpenPgpMetadataStore;
|
import org.jivesoftware.smackx.ox.v2.store.AbstractOpenPgpMetadataStore;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.jxmpp.util.XmppDateTime;
|
||||||
|
|
||||||
public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore {
|
public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore {
|
||||||
|
|
||||||
public static final String METADATA = "announced.list";
|
public static final String ANNOUNCED = "announced.list";
|
||||||
|
public static final String RETRIEVED = "retrieved.list";
|
||||||
|
|
||||||
|
private static final Logger LOGGER = Logger.getLogger(FileBasedOpenPgpMetadataStore.class.getName());
|
||||||
|
|
||||||
private final File basePath;
|
private final File basePath;
|
||||||
|
|
||||||
|
@ -42,17 +50,94 @@ public class FileBasedOpenPgpMetadataStore extends AbstractOpenPgpMetadataStore
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<OpenPgpV4Fingerprint> readAnnouncedFingerprintsOf(BareJid contact) throws IOException {
|
public Map<OpenPgpV4Fingerprint, Date> readAnnouncedFingerprintsOf(BareJid contact) throws IOException {
|
||||||
InputStream inputStream = prepareFileInputStream(getMetadataPath(contact));
|
return readFingerprintsAndDates(getAnnouncedFingerprintsPath(contact));
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeAnnouncedFingerprintsOf(BareJid contact, PublicKeysListElement metadata) throws IOException {
|
public void writeAnnouncedFingerprintsOf(BareJid contact, PublicKeysListElement metadata)
|
||||||
OutputStream outputStream = prepareFileOutputStream(getMetadataPath(contact));
|
throws IOException {
|
||||||
|
File destination = getAnnouncedFingerprintsPath(contact);
|
||||||
|
Map<OpenPgpV4Fingerprint, Date> fingerprintDateMap = new HashMap<>();
|
||||||
|
for (OpenPgpV4Fingerprint fingerprint : metadata.getMetadata().keySet()) {
|
||||||
|
fingerprintDateMap.put(fingerprint, metadata.getMetadata().get(fingerprint).getDate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getMetadataPath(BareJid contact) {
|
writeFingerprintsAndDates(fingerprintDateMap, destination);
|
||||||
return new File(FileBasedOpenPgpStore.getContactsPath(basePath, contact), METADATA);
|
}
|
||||||
|
|
||||||
|
private Map<OpenPgpV4Fingerprint, Date> readFingerprintsAndDates(File source) throws IOException {
|
||||||
|
BufferedReader reader = null;
|
||||||
|
try {
|
||||||
|
reader = Files.newBufferedReader(source.toPath(), Util.UTF8);
|
||||||
|
Map<OpenPgpV4Fingerprint, Date> fingerprintDateMap = new HashMap<>();
|
||||||
|
|
||||||
|
String line; int lineNr = 0;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
lineNr++;
|
||||||
|
|
||||||
|
line = line.trim();
|
||||||
|
String[] split = line.split(" ");
|
||||||
|
if (split.length != 2) {
|
||||||
|
LOGGER.log(Level.FINE, "Skipping invalid line " + lineNr + " in file " + source.getAbsolutePath());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(split[0]);
|
||||||
|
Date date = XmppDateTime.parseXEP0082Date(split[1]);
|
||||||
|
fingerprintDateMap.put(fingerprint, date);
|
||||||
|
} catch (ParseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Error parsing fingerprint/date touple in line " + lineNr +
|
||||||
|
" of file " + source.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.close();
|
||||||
|
return fingerprintDateMap;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (reader != null) {
|
||||||
|
try {
|
||||||
|
reader.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// Don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFingerprintsAndDates(Map<OpenPgpV4Fingerprint, Date> data, File destination)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
BufferedWriter writer = null;
|
||||||
|
try {
|
||||||
|
writer = Files.newBufferedWriter(destination.toPath(), Util.UTF8);
|
||||||
|
for (OpenPgpV4Fingerprint fingerprint : data.keySet()) {
|
||||||
|
Date date = data.get(fingerprint);
|
||||||
|
String line = fingerprint.toString() + " " +
|
||||||
|
(date != null ? XmppDateTime.formatXEP0082Date(date) : XmppDateTime.formatXEP0082Date(new Date()));
|
||||||
|
writer.write(line);
|
||||||
|
}
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (writer != null) {
|
||||||
|
try {
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
// Don't care
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getAnnouncedFingerprintsPath(BareJid contact) {
|
||||||
|
return new File(FileBasedOpenPgpStore.getContactsPath(basePath, contact), ANNOUNCED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getRetrievedFingerprintsPath(BareJid contact) {
|
||||||
|
return new File(FileBasedOpenPgpStore.getContactsPath(basePath, contact), RETRIEVED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue