001/**
002 *
003 * Copyright 2018 Paul Schaub.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smackx.ox.store.filebased;
018
019import java.io.BufferedReader;
020import java.io.BufferedWriter;
021import java.io.File;
022import java.io.FileNotFoundException;
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.OutputStream;
027import java.io.OutputStreamWriter;
028import java.util.logging.Level;
029import java.util.logging.Logger;
030
031import org.jivesoftware.smackx.ox.store.abstr.AbstractOpenPgpTrustStore;
032import org.jivesoftware.smackx.ox.store.definition.OpenPgpTrustStore;
033import org.jivesoftware.smackx.ox.util.FileUtils;
034import org.jivesoftware.smackx.ox.util.Util;
035
036import org.jxmpp.jid.BareJid;
037import org.pgpainless.key.OpenPgpV4Fingerprint;
038
039/**
040 * Implementation of the {@link OpenPgpTrustStore} which stores information in a directory structure.
041 *
042 * <pre>
043 * {@code
044 * <basePath>/
045 *     <userjid@server.tld>/
046 *         <fingerprint>.trust      // Trust record for a key
047 * }
048 * </pre>
049 */
050public class FileBasedOpenPgpTrustStore extends AbstractOpenPgpTrustStore {
051
052    private static final Logger LOGGER = Logger.getLogger(FileBasedOpenPgpTrustStore.class.getName());
053
054    private final File basePath;
055
056    public static String TRUST_RECORD(OpenPgpV4Fingerprint fingerprint) {
057        return fingerprint.toString() + ".trust";
058    }
059
060    public FileBasedOpenPgpTrustStore(File basePath) {
061        this.basePath = basePath;
062    }
063
064    @Override
065    protected Trust readTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint) throws IOException {
066        File file = getTrustPath(owner, fingerprint);
067        BufferedReader reader = null;
068        try {
069            InputStream inputStream = FileUtils.prepareFileInputStream(file);
070            InputStreamReader isr = new InputStreamReader(inputStream, Util.UTF8);
071            reader = new BufferedReader(isr);
072
073            Trust trust = null;
074            String line; int lineNr = 0;
075            while ((line = reader.readLine()) != null) {
076                lineNr++;
077                try {
078                    trust = Trust.valueOf(line);
079                    break;
080                } catch (IllegalArgumentException e) {
081                    LOGGER.log(Level.WARNING, "Skipping invalid trust record in line " + lineNr + " of file " +
082                            file.getAbsolutePath());
083                }
084            }
085            reader.close();
086            return trust != null ? trust : Trust.undecided;
087        } catch (IOException e) {
088            if (reader != null) {
089                try {
090                    reader.close();
091                } catch (IOException ignored) {
092                    // Don't care
093                }
094            }
095
096            if (e instanceof FileNotFoundException) {
097                return Trust.undecided;
098            }
099            throw e;
100        }
101    }
102
103    @Override
104    protected void writeTrust(BareJid owner, OpenPgpV4Fingerprint fingerprint, Trust trust) throws IOException {
105        File file = getTrustPath(owner, fingerprint);
106
107        if (trust == null || trust == Trust.undecided) {
108            if (!file.exists()) {
109                return;
110            }
111            if (!file.delete()) {
112                throw new IOException("Could not delete file " + file.getAbsolutePath());
113            }
114        }
115
116        File parent = file.getParentFile();
117        if (!parent.exists() && !parent.mkdirs()) {
118            throw new IOException("Cannot create directory " + parent.getAbsolutePath());
119        }
120        if (!file.exists()) {
121            if (!file.createNewFile()) {
122                throw new IOException("Cannot create file " + file.getAbsolutePath());
123            }
124        } else {
125            if (file.isDirectory()) {
126                throw new IOException("File " + file.getAbsolutePath() + " is a directory.");
127            }
128        }
129
130        BufferedWriter writer = null;
131        try {
132            OutputStream outputStream = FileUtils.prepareFileOutputStream(file);
133            OutputStreamWriter osw = new OutputStreamWriter(outputStream, Util.UTF8);
134            writer = new BufferedWriter(osw);
135
136            writer.write(trust.toString());
137            writer.flush();
138            writer.close();
139        } catch (IOException e) {
140            if (writer != null) {
141                try {
142                    writer.close();
143                } catch (IOException ignored) {
144                    // Don't care
145                }
146            }
147            throw e;
148        }
149    }
150
151    private File getTrustPath(BareJid owner, OpenPgpV4Fingerprint fingerprint) {
152        return new File(FileBasedOpenPgpStore.getContactsPath(basePath, owner), TRUST_RECORD(fingerprint));
153    }
154}