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
//! # Keyring library
//!
//! Allows for setting and getting passwords on Linux, OSX, Windows, and Android
#![deny(clippy::all)]

use log::*;
use std::path::Path;

mod error;
mod insecure;

pub use error::{KeyringError, Result};
use insecure::InsecureKeyringManager;

/////////////////////////////////////////////////////////////////////////

use cfg_if::*;
cfg_if! {
    if #[cfg(target_os = "linux")] {
        mod linux;
        use linux::LinuxKeyringManager as PlatformKeyringManager;
    } else if #[cfg(target_os = "windows")] {
        mod windows;
        use windows::WindowsKeyringManager as PlatformKeyringManager;
    } else if #[cfg(target_os = "macos")] {
        mod macos;
        use macos::MacosKeyringManager as PlatformKeyringManager;
    } else if #[cfg(target_os = "ios")] {
        mod ios;
        use ios::IosKeyringManager as PlatformKeyringManager;
    } else if #[cfg(target_os = "android")] {
        pub(crate) mod android;
        use android::AndroidKeyringManager as PlatformKeyringManager;
        pub use android::AndroidKeyringContext;
    } else {
        struct PlatformKeyringManager {}

        impl PlatformKeyringManager {
            pub fn with_keyring<F, T>(&self, _service: &str, _key: &str, _func: F) -> Result<T>
            where
                F: FnOnce(&mut dyn Keyring) -> Result<T>,
            {
                Err(KeyringError::NoBackendFound)
            }
        }
    }
}
/////////////////////////////////////////////////////////////////////////

pub trait Keyring {
    fn set_value(&mut self, value: &str) -> Result<()>;
    fn get_value(&self) -> Result<String>;
    fn delete_value(&mut self) -> Result<()>;
}

enum KeyringManagerKind {
    #[allow(dead_code)]
    Secure(PlatformKeyringManager),
    Insecure(InsecureKeyringManager),
}

pub struct KeyringManager {
    kind: KeyringManagerKind,
}

impl KeyringManager {
    cfg_if! {
        if #[cfg(all(target_os = "macos", feature = "macos-specify-keychain"))] {
            pub fn new_secure_with_path(application: &str, path: &Path) -> Result<Self> {
                Ok(KeyringManager {
                    kind: KeyringManagerKind::Secure(PlatformKeyringManager::with_path(application, path)?),
                })
            }
        }
    }
    cfg_if! {
        if #[cfg(target_os = "android")] {
            pub fn new_secure(application: &str, context: AndroidKeyringContext) -> Result<Self> {
                Ok(KeyringManager {
                    kind: KeyringManagerKind::Secure(PlatformKeyringManager::new(application, context)?),
                })
            }
        } else if #[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos", target_os = "ios"))] {    
            pub fn new_secure(application: &str) -> Result<Self> {
                Ok(KeyringManager {
                    kind: KeyringManagerKind::Secure(PlatformKeyringManager::new(application)?),
                })
            }
        } else {
            pub fn new_secure(_application: &str) -> Result<Self> {
                Err(KeyringError::NoBackendFound)
            }
        }
    }

    pub fn new_insecure(application: &str, insecure_keyring_file: &Path) -> Result<Self> {
        Ok(KeyringManager {
            kind: KeyringManagerKind::Insecure(InsecureKeyringManager::new(
                application,
                insecure_keyring_file,
            )?),
        })
    }

    pub fn with_keyring<F, T>(&self, service: &str, key: &str, f: F) -> Result<T>
    where
        F: FnOnce(&mut dyn Keyring) -> Result<T>,
    {
        match &self.kind {
            KeyringManagerKind::Secure(pkm) => pkm.with_keyring(service, key, f),
            KeyringManagerKind::Insecure(ikm) => ikm.with_keyring(service, key, f),
        }
    }
}

pub(crate) fn map_to_generic<E: std::fmt::Display>(e: E) -> KeyringError {
    KeyringError::Generic(format!("{}", e))
}

pub(crate) fn map_log_err<T: std::error::Error>(e: T) -> T {
    error!("{}", e);
    e
}

cfg_if! {
    if #[cfg(test)] {
        mod tests;
    } else if #[cfg(feature="keyring_manager_android_tests")] {
        pub mod tests;
    } else if #[cfg(feature="keyring_manager_ios_tests")] {
        pub mod tests;
    }

}