1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! Pluggable persistence.
//!
//! The persistence is a simple key-value store. The intention is to make it simple to implement
//! other persistence mechanisms than the provided ones, such as against a databases.

use std::collections::hash_map::DefaultHasher;
use std::collections::hash_map::HashMap;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};

use crate::Result;

/// Kinds of [persistence keys](struct.PersistKey.html).
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum PersistKind {
    /// Persisted account private key.
    AccountPrivateKey,
    /// Persisted private key.
    PrivateKey,
    /// Persisted certificate.
    Certificate,
}

impl PersistKind {
    fn name(self) -> &'static str {
        match self {
            PersistKind::Certificate => "crt",
            PersistKind::PrivateKey => "key",
            PersistKind::AccountPrivateKey => "key",
        }
    }
}

/// Key for a value in the persistence.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub struct PersistKey<'a> {
    pub realm: u64,
    pub kind: PersistKind,
    pub key: &'a str,
}

impl<'a> PersistKey<'a> {
    /// Create a new key under a "realm", kind and key. The realm is an opaque hash
    /// of the given realm string.
    ///
    /// The realm is currently defined as the account contact email, but this might change.
    pub fn new(realm: &str, kind: PersistKind, key: &'a str) -> Self {
        let mut h = DefaultHasher::new();
        realm.hash(&mut h);
        let realm = h.finish();
        PersistKey { realm, kind, key }
    }

    /// Make a string representation of this key. Same as the Display trait.
    pub fn to_string(&self) -> String {
        format!(
            "{}_{}_{}",
            self.realm,
            self.kind.name(),
            self.key.replace('.', "_").replace('*', "STAR")
        )
    }
}

impl<'a> ::std::fmt::Display for PersistKey<'a> {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        write!(f, "{}", self.to_string())
    }
}

/// Trait for a persistence implementation.
///
/// Implementation must be clonable and thread safe (Send). This can easily be done by
/// wrapping the implemetation an `Arc<Mutex<P>>`.
pub trait Persist: Clone + Send {
    /// Store the given bytes under the given key.
    fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()>;
    /// Read the bytes stored under the given key.
    ///
    /// `None` if the value doesn't exist.
    fn get(&self, key: &PersistKey) -> Result<Option<Vec<u8>>>;
}

/// Memory implementation for dev/testing.
///
/// The entries in memory are never saved to disk and are gone when the process dies.
///
/// Since the API is [rate limited] it's not a good idea to use this in production code.
///
/// [rate limited]: ../index.html#rate-limits
#[derive(Clone, Default)]
pub struct MemoryPersist {
    inner: Arc<Mutex<HashMap<String, Vec<u8>>>>,
}

impl MemoryPersist {
    /// Create a memory persistence for testing.
    pub fn new() -> Self {
        MemoryPersist {
            ..Default::default()
        }
    }
}

impl Persist for MemoryPersist {
    fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()> {
        let mut lock = self.inner.lock().unwrap();
        lock.insert(key.to_string(), value.to_owned());
        Ok(())
    }
    fn get(&self, key: &PersistKey) -> Result<Option<Vec<u8>>> {
        let lock = self.inner.lock().unwrap();
        Ok(lock.get(&key.to_string()).cloned())
    }
}

/// Simple file persistence.
///
/// Each key is saved under a unique filename.
#[derive(Clone)]
pub struct FilePersist {
    dir: PathBuf,
}

impl FilePersist {
    /// Create a file persistence in the directory pointed out by the `dir` given.
    ///
    /// The directory must be writable.
    pub fn new<P: AsRef<Path>>(dir: P) -> Self {
        FilePersist {
            dir: dir.as_ref().to_path_buf(),
        }
    }
}

impl Persist for FilePersist {
    fn put(&self, key: &PersistKey, value: &[u8]) -> Result<()> {
        let f_name = file_name_of(&self.dir, &key);
        let mut file = fs::File::create(f_name)?;
        file.write_all(value)?;
        Ok(())
    }
    fn get(&self, key: &PersistKey) -> Result<Option<Vec<u8>>> {
        let f_name = file_name_of(&self.dir, &key);
        let ret = if let Ok(mut file) = fs::File::open(f_name) {
            let mut v = vec![];
            file.read_to_end(&mut v)?;
            Some(v)
        } else {
            None
        };
        Ok(ret)
    }
}

fn file_name_of(dir: &PathBuf, key: &PersistKey) -> PathBuf {
    let mut f_name = dir.clone();
    f_name.push(key.to_string());
    f_name.set_extension(key.kind.name());
    f_name
}