use super::SetOpt;
use curl::easy::{Easy2, SslOpt};
use std::{
iter::FromIterator,
ops::{BitOr, BitOrAssign},
path::PathBuf,
};
#[derive(Clone, Debug)]
pub struct ClientCertificate {
format: &'static str,
path: PathBuf,
private_key: Option<PrivateKey>,
password: Option<String>,
}
impl ClientCertificate {
pub fn pem_file(path: impl Into<PathBuf>, private_key: impl Into<Option<PrivateKey>>) -> Self {
Self {
format: "PEM",
path: path.into(),
private_key: private_key.into(),
password: None,
}
}
pub fn der_file(path: impl Into<PathBuf>, private_key: impl Into<Option<PrivateKey>>) -> Self {
Self {
format: "DER",
path: path.into(),
private_key: private_key.into(),
password: None,
}
}
pub fn p12_file(path: impl Into<PathBuf>, password: impl Into<Option<String>>) -> Self {
Self {
format: "P12",
path: path.into(),
private_key: None,
password: password.into(),
}
}
}
impl SetOpt for ClientCertificate {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.ssl_cert_type(self.format)?;
easy.ssl_cert(&self.path)?;
if let Some(key) = self.private_key.as_ref() {
key.set_opt(easy)?;
}
if let Some(password) = self.password.as_ref() {
easy.key_password(password)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct PrivateKey {
format: &'static str,
path: PathBuf,
password: Option<String>,
}
impl PrivateKey {
pub fn pem_file(path: impl Into<PathBuf>, password: impl Into<Option<String>>) -> Self {
Self {
format: "PEM",
path: path.into(),
password: password.into(),
}
}
pub fn der_file(path: impl Into<PathBuf>, password: impl Into<Option<String>>) -> Self {
Self {
format: "DER",
path: path.into(),
password: password.into(),
}
}
}
impl SetOpt for PrivateKey {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.ssl_key(&self.path)?;
easy.ssl_key_type(self.format)?;
if let Some(password) = self.password.as_ref() {
easy.key_password(password)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct CaCertificate {
path: PathBuf,
}
impl CaCertificate {
pub fn file(ca_bundle_path: impl Into<PathBuf>) -> Self {
Self {
path: ca_bundle_path.into(),
}
}
}
impl SetOpt for CaCertificate {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.cainfo(&self.path)
}
}
#[derive(Clone, Debug)]
pub(crate) struct Ciphers(String);
impl FromIterator<String> for Ciphers {
fn from_iter<I: IntoIterator<Item = String>>(iter: I) -> Self {
Ciphers(iter.into_iter().collect::<Vec<_>>().join(":"))
}
}
impl SetOpt for Ciphers {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
easy.ssl_cipher_list(&self.0)
}
}
#[derive(Clone, Copy, Debug)]
pub struct SslOption(usize);
impl Default for SslOption {
fn default() -> Self {
Self::NONE
}
}
impl SslOption {
pub const NONE: Self = SslOption(0);
pub const DANGER_ACCEPT_INVALID_CERTS: Self = SslOption(0b0001);
pub const DANGER_ACCEPT_INVALID_HOSTS: Self = SslOption(0b0010);
pub const DANGER_ACCEPT_REVOKED_CERTS: Self = SslOption(0b0100);
const fn contains(self, other: Self) -> bool {
(self.0 & other.0) == other.0
}
}
impl BitOr for SslOption {
type Output = Self;
fn bitor(mut self, other: Self) -> Self {
self |= other;
self
}
}
impl BitOrAssign for SslOption {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl SetOpt for SslOption {
fn set_opt<H>(&self, easy: &mut Easy2<H>) -> Result<(), curl::Error> {
let mut opt = SslOpt::new();
opt.no_revoke(self.contains(Self::DANGER_ACCEPT_REVOKED_CERTS));
easy.ssl_options(&opt)?;
easy.ssl_verify_peer(!self.contains(Self::DANGER_ACCEPT_INVALID_CERTS))?;
easy.ssl_verify_host(!self.contains(Self::DANGER_ACCEPT_INVALID_HOSTS))
}
}
#[cfg(test)]
mod tests {
use super::SslOption;
#[test]
fn default_ssl_options() {
let options = SslOption::default();
assert!(!options.contains(SslOption::DANGER_ACCEPT_INVALID_CERTS));
assert!(!options.contains(SslOption::DANGER_ACCEPT_INVALID_HOSTS));
assert!(!options.contains(SslOption::DANGER_ACCEPT_REVOKED_CERTS));
}
#[test]
fn ssl_option_invalid_certs() {
let options = SslOption::DANGER_ACCEPT_INVALID_CERTS;
assert!(options.contains(SslOption::DANGER_ACCEPT_INVALID_CERTS));
assert!(!options.contains(SslOption::DANGER_ACCEPT_INVALID_HOSTS));
let options = SslOption::DANGER_ACCEPT_INVALID_HOSTS;
assert!(!options.contains(SslOption::DANGER_ACCEPT_INVALID_CERTS));
assert!(options.contains(SslOption::DANGER_ACCEPT_INVALID_HOSTS));
}
}