Compare commits
10 Commits
5434b2d26e
...
324ef442a2
Author | SHA1 | Date |
---|---|---|
|
324ef442a2 | |
|
4633f8997e | |
|
f7920dd84f | |
|
1e47fe8d08 | |
|
75da82b008 | |
|
92c1406b4f | |
|
5b6595e770 | |
|
c3fc33ee57 | |
|
a55af189d4 | |
|
ab6a9e55c2 |
|
@ -1,3 +1,7 @@
|
|||
jmap-client 0.3.2
|
||||
================================
|
||||
- Bump to `rustls` 0.22.
|
||||
|
||||
jmap-client 0.3.0
|
||||
================================
|
||||
- JMAP for Sieve Scripts DRAFT-14 support.
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "jmap-client"
|
||||
description = "JMAP client library for Rust"
|
||||
version = "0.3.0"
|
||||
version = "0.3.2"
|
||||
edition = "2021"
|
||||
authors = [ "Stalwart Labs Ltd. <hello@stalw.art>"]
|
||||
license = "Apache-2.0 OR MIT"
|
||||
|
@ -13,12 +13,13 @@ readme = "README.md"
|
|||
resolver = "2"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls"]}
|
||||
tokio-tungstenite = { version = "0.19", features = ["rustls-tls-webpki-roots"], optional = true}
|
||||
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls-webpki-roots"]}
|
||||
tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"], optional = true}
|
||||
tokio = { version = "1.16", default-features = false, features = ["io-util"], optional = true }
|
||||
futures-util = { version = "0.3", optional = true}
|
||||
async-stream = { version = "0.3", optional = true}
|
||||
rustls = { version = "0.21.0", features = ["dangerous_configuration"], optional = true }
|
||||
rustls = { version = "0.22", optional = true }
|
||||
rustls-pki-types = { version = "1" }
|
||||
serde = { version = "1.0", features = ["derive"]}
|
||||
serde_json = "1.0"
|
||||
chrono = { version = "0.4", features = ["serde"]}
|
||||
|
@ -28,7 +29,7 @@ base64 = "0.13"
|
|||
maybe-async = "0.2"
|
||||
|
||||
[features]
|
||||
default = ["async"]
|
||||
default = ["async", "websockets"]
|
||||
async = ["futures-util", "async-stream", "reqwest/stream"]
|
||||
websockets = ["tokio", "tokio-tungstenite", "rustls"]
|
||||
blocking = ["reqwest/blocking", "maybe-async/is_sync"]
|
||||
|
|
|
@ -87,6 +87,9 @@ impl Default for ClientBuilder {
|
|||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
/// Creates a new `ClientBuilder`.
|
||||
///
|
||||
/// Setting the credentials is required to connect to the JMAP API.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
credentials: None,
|
||||
|
@ -97,21 +100,70 @@ impl ClientBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set up client credentials to connect to the JMAP API.
|
||||
///
|
||||
/// The JMAP API URL is set using the [ClientBuilder.connect()](struct.ClientBuilder.html#method.connect) method.
|
||||
///
|
||||
/// # Bearer authentication
|
||||
/// Pass a `&str` with the API Token.
|
||||
///
|
||||
/// ```rust
|
||||
/// Client::new().credentials("some-api-token");
|
||||
/// ```
|
||||
///
|
||||
/// Or use the longer form by using [Credentials::bearer()](enum.Credentials.html#method.bearer).
|
||||
/// ```rust
|
||||
/// let credentials = Credentials::bearer("some-api-token");
|
||||
/// Client::new().credentials(credentials);
|
||||
/// ```
|
||||
///
|
||||
/// # Basic authentication
|
||||
/// Pass a `(&str, &str)` tuple, with the first position containing a username and the second containing a password.
|
||||
///
|
||||
/// **It is not suggested to use this approach in production;** instead, if possible, use [Bearer authentication](struct.ClientBuilder.html#bearer-authentication).
|
||||
///
|
||||
/// ```rust
|
||||
/// Client::new().credentials(("user@domain.com", "password"));
|
||||
/// ```
|
||||
///
|
||||
/// Or use the longer form by using [Credentials::basic()](enum.Credentials.html#method.basic).
|
||||
/// ```rust
|
||||
/// let credentials = Credentials::basic("user@domain.com", "password");
|
||||
/// Client::new().credentials(credentials);
|
||||
/// ```
|
||||
pub fn credentials(mut self, credentials: impl Into<Credentials>) -> Self {
|
||||
self.credentials = Some(credentials.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a timeout for all the requests to the JMAP API.
|
||||
///
|
||||
/// The timeout can be changed after the `Client` has been created by using [Client.set_timeout()](struct.Client.html#method.set_timeout).
|
||||
///
|
||||
/// By default the timeout is 10 seconds.
|
||||
pub fn timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
|
||||
/// Accepts invalid certificates for all the requests to the JMAP API.
|
||||
///
|
||||
/// By default certificates are validated.
|
||||
///
|
||||
/// # Warning
|
||||
/// **It is not suggested to use this approach in production;** this method should be used only for testing and as a last resort.
|
||||
///
|
||||
/// [Read more in the reqwest docs](https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.danger_accept_invalid_certs)
|
||||
pub fn accept_invalid_certs(mut self, accept_invalid_certs: bool) -> Self {
|
||||
self.accept_invalid_certs = accept_invalid_certs;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set a list of trusted hosts that will be checked when a redirect is required.
|
||||
///
|
||||
/// The list can be changed after the `Client` has been created by using [Client.set_follow_redirects()](struct.Client.html#method.set_follow_redirects).
|
||||
///
|
||||
/// The client will follow at most 5 redirects.
|
||||
pub fn follow_redirects(
|
||||
mut self,
|
||||
trusted_hosts: impl IntoIterator<Item = impl Into<String>>,
|
||||
|
@ -120,6 +172,7 @@ impl ClientBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set the originating IP address of the client connecting to the JMAP API.
|
||||
pub fn forwarded_for(mut self, forwarded_for: IpAddr) -> Self {
|
||||
self.forwarded_for = Some(match forwarded_for {
|
||||
IpAddr::V4(addr) => format!("for={}", addr),
|
||||
|
@ -128,6 +181,9 @@ impl ClientBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Connects to the JMAP API Session URL.
|
||||
///
|
||||
/// Setting up [Credentials](struct.ClientBuilder.html#method.credentials) must be done before calling this function.
|
||||
#[maybe_async::maybe_async]
|
||||
pub async fn connect(self, url: &str) -> crate::Result<Client> {
|
||||
let authorization = match self.credentials.expect("Missing credentials") {
|
||||
|
|
|
@ -14,8 +14,8 @@ use std::{pin::Pin, sync::Arc};
|
|||
use ahash::AHashMap;
|
||||
use futures_util::{stream::SplitSink, SinkExt, Stream, StreamExt};
|
||||
use rustls::{
|
||||
client::{ServerCertVerified, ServerCertVerifier},
|
||||
Certificate, ClientConfig, ServerName,
|
||||
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
|
||||
ClientConfig, SignatureScheme,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::TcpStream;
|
||||
|
@ -167,20 +167,56 @@ pub struct WsStream {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Debug)]
|
||||
struct DummyVerifier;
|
||||
|
||||
impl ServerCertVerifier for DummyVerifier {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_e: &Certificate,
|
||||
_i: &[Certificate],
|
||||
_sn: &ServerName,
|
||||
_sc: &mut dyn Iterator<Item = &[u8]>,
|
||||
_o: &[u8],
|
||||
_n: std::time::SystemTime,
|
||||
_end_entity: &rustls_pki_types::CertificateDer<'_>,
|
||||
_intermediates: &[rustls_pki_types::CertificateDer<'_>],
|
||||
_server_name: &rustls_pki_types::ServerName<'_>,
|
||||
_ocsp_response: &[u8],
|
||||
_now: rustls_pki_types::UnixTime,
|
||||
) -> Result<ServerCertVerified, rustls::Error> {
|
||||
Ok(ServerCertVerified::assertion())
|
||||
}
|
||||
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_cert: &rustls_pki_types::CertificateDer<'_>,
|
||||
_dss: &rustls::DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||
Ok(HandshakeSignatureValid::assertion())
|
||||
}
|
||||
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
_message: &[u8],
|
||||
_cert: &rustls_pki_types::CertificateDer<'_>,
|
||||
_dss: &rustls::DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||
Ok(HandshakeSignatureValid::assertion())
|
||||
}
|
||||
|
||||
fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
|
||||
vec![
|
||||
SignatureScheme::RSA_PKCS1_SHA1,
|
||||
SignatureScheme::ECDSA_SHA1_Legacy,
|
||||
SignatureScheme::RSA_PKCS1_SHA256,
|
||||
SignatureScheme::ECDSA_NISTP256_SHA256,
|
||||
SignatureScheme::RSA_PKCS1_SHA384,
|
||||
SignatureScheme::ECDSA_NISTP384_SHA384,
|
||||
SignatureScheme::RSA_PKCS1_SHA512,
|
||||
SignatureScheme::ECDSA_NISTP521_SHA512,
|
||||
SignatureScheme::RSA_PSS_SHA256,
|
||||
SignatureScheme::RSA_PSS_SHA384,
|
||||
SignatureScheme::RSA_PSS_SHA512,
|
||||
SignatureScheme::ED25519,
|
||||
SignatureScheme::ED448,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl Client {
|
||||
|
@ -206,7 +242,7 @@ impl Client {
|
|||
false,
|
||||
Connector::Rustls(Arc::new(
|
||||
ClientConfig::builder()
|
||||
.with_safe_defaults()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(Arc::new(DummyVerifier {}))
|
||||
.with_no_client_auth(),
|
||||
))
|
||||
|
|
|
@ -189,6 +189,14 @@ impl<O: SetObject> SetRequest<O> {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn create_with_id(&mut self, create_id: impl Into<String>) -> &mut O {
|
||||
let create_id = create_id.into();
|
||||
self.create
|
||||
.get_or_insert_with(AHashMap::new)
|
||||
.insert(create_id.clone(), O::new(0.into()));
|
||||
self.create.as_mut().unwrap().get_mut(&create_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn create_item(&mut self, item: O) -> String {
|
||||
let create_id = self.create.as_ref().map_or(0, |c| c.len());
|
||||
let create_id_str = format!("c{}", create_id);
|
||||
|
@ -430,7 +438,7 @@ impl Display for SetErrorType {
|
|||
}
|
||||
|
||||
pub fn from_timestamp(timestamp: i64) -> DateTime<Utc> {
|
||||
DateTime::<Utc>::from_utc(
|
||||
DateTime::from_naive_utc_and_offset(
|
||||
NaiveDateTime::from_timestamp_opt(timestamp, 0).unwrap_or_default(),
|
||||
Utc,
|
||||
)
|
||||
|
|
|
@ -71,7 +71,10 @@ impl Client {
|
|||
V: IntoIterator<Item = W>,
|
||||
W: Into<String>,
|
||||
{
|
||||
let blob_id = self.upload(None, raw_message, None).await?.take_blob_id();
|
||||
let blob_id = self
|
||||
.upload(account_id.into(), raw_message, None)
|
||||
.await?
|
||||
.take_blob_id();
|
||||
let mut request = self.build();
|
||||
let import_request = request
|
||||
.import_email()
|
||||
|
|
|
@ -348,8 +348,6 @@ pub enum StateChangeType {
|
|||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct StateChange {
|
||||
#[serde(rename = "@type")]
|
||||
pub type_: StateChangeType,
|
||||
pub changed: AHashMap<String, AHashMap<TypeState, String>>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue