diff --git a/Cargo.lock b/Cargo.lock index 4176932..7164a6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -324,7 +324,7 @@ dependencies = [ "async-channel 2.3.1", "async-compression", "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "chrono", "futures", "imap-proto", @@ -370,12 +370,12 @@ dependencies = [ [[package]] name = "async-nats" -version = "0.44.2" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f834a80c3ab6109b9c8f5ca6661a578cf31e088e831b6ce07c6b23cca04f6742" +checksum = "86dde77d8a733a9dbaf865a9eb65c72e09c88f3d14d3dd0d2aecf511920ee4fe" dependencies = [ "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "futures-util", "memchr", "nkeys", @@ -488,7 +488,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3dc5ca11ac66da7ba331bd707c8d6bca90a559c107aada6ee6abf1d4d5dbc2c" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "futures", "regex", "serde", @@ -790,9 +790,9 @@ checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] @@ -1072,9 +1072,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.49" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" +checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" dependencies = [ "clap_builder", "clap_derive", @@ -1082,9 +1082,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.49" +version = "4.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" +checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" dependencies = [ "anstream", "anstyle", @@ -1713,7 +1713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a11dd7f04a6a6d2aea0153c6e31f5ea7af8b2efdf52cdaeea7a9a592c7fefef9" dependencies = [ "bumpalo", - "bytes 1.7.2", + "bytes 1.10.1", "domain-macros", "futures-util", "hashbrown 0.14.5", @@ -2396,7 +2396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", - "bytes 1.7.2", + "bytes 1.10.1", "fnv", "futures-core", "futures-sink", @@ -2483,7 +2483,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ "base64 0.21.7", - "bytes 1.7.2", + "bytes 1.10.1", "headers-core", "http 1.3.1", "httpdate", @@ -2544,7 +2544,7 @@ checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", "bitflags 2.6.0", - "bytes 1.7.2", + "bytes 1.10.1", "cfg-if", "data-encoding", "enum-as-inner 0.6.1", @@ -2676,7 +2676,7 @@ version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "fnv", "itoa", ] @@ -2687,7 +2687,7 @@ version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "fnv", "itoa", ] @@ -2698,7 +2698,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "http 1.3.1", ] @@ -2708,7 +2708,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "futures-util", "http 1.3.1", "http-body", @@ -2769,7 +2769,7 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "futures-channel", "futures-util", "h2", @@ -2810,7 +2810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "futures-channel", "futures-core", "futures-util", @@ -3083,7 +3083,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22fa7ee6be451ea0b1912b962c91c8380835e97cf1584a77e18264e908448dcb" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "log", "nom 7.1.3", "smallvec", @@ -3575,7 +3575,7 @@ dependencies = [ "smtp-proto", "tokio", "tokio-rustls", - "webpki-roots 1.0.3", + "webpki-roots 1.0.4", ] [[package]] @@ -3788,7 +3788,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "encoding_rs", "futures-util", "http 1.3.1", @@ -4167,7 +4167,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "126c3ca37c9c44cec575247f43a3e4374d8927684f129d2beeb0d2cef262fe12" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "serde", "smallvec", ] @@ -4564,7 +4564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f977080932c87287147dca052951c3e2696f8759863f6b4e4c0c9ffe7a4cc8b" dependencies = [ "async-compression", - "bytes 1.7.2", + "bytes 1.10.1", "futures-util", "headers", "hex", @@ -4622,7 +4622,7 @@ dependencies = [ "async-compression", "async-stream", "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "fastrand 2.1.1", "futures-util", "http 1.3.1", @@ -4642,7 +4642,7 @@ dependencies = [ "tokio-rustls", "tokio-stream", "tower-service", - "webpki-roots 1.0.3", + "webpki-roots 1.0.4", ] [[package]] @@ -4666,7 +4666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ccbcc395bf4dd03df1da32da351b6b6732e4074ce27ddec315650e52a2be44c" dependencies = [ "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "derive_more", "email_address", "futures-util", @@ -4805,9 +4805,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.101" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] @@ -4852,7 +4852,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "prost-derive", ] @@ -4862,7 +4862,7 @@ version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c1318b19085f08681016926435853bbf7858f9c082d0999b80550ff5d9abe15" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "heck 0.5.0", "itertools 0.13.0", "log", @@ -5067,7 +5067,7 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "pin-project-lite", "quinn-proto", "quinn-udp", @@ -5085,7 +5085,7 @@ version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "rand 0.8.5", "ring", "rustc-hash", @@ -5435,7 +5435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "futures-channel", "futures-core", "futures-util", @@ -5469,7 +5469,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.3", + "webpki-roots 1.0.4", ] [[package]] @@ -5564,9 +5564,9 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rust-embed" -version = "8.8.0" +version = "8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb44e1917075637ee8c7bcb865cf8830e3a92b5b1189e44e3a0ab5a0d5be314b" +checksum = "947d7f3fad52b283d261c4c99a084937e2fe492248cb9a68a8435a861b8798ca" dependencies = [ "rust-embed-impl", "rust-embed-utils", @@ -5575,9 +5575,9 @@ dependencies = [ [[package]] name = "rust-embed-impl" -version = "8.8.0" +version = "8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382499b49db77a7c19abd2a574f85ada7e9dbe125d5d1160fa5cad7c4cf71fc9" +checksum = "5fa2c8c9e8711e10f9c4fd2d64317ef13feaab820a4c51541f1a8c8e2e851ab2" dependencies = [ "proc-macro2", "quote", @@ -5588,9 +5588,9 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.8.0" +version = "8.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21fcbee55c2458836bcdbfffb6ec9ba74bbc23ca7aa6816015a3dd2c4d8fc185" +checksum = "60b161f275cb337fe0a44d924a5f4df0ed69c2c39519858f931ce61c779d3475" dependencies = [ "sha2 0.10.9", "walkdir", @@ -5658,9 +5658,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.33" +version = "0.23.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "aws-lc-rs", "log", @@ -5709,9 +5709,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ "zeroize", ] @@ -5740,7 +5740,7 @@ dependencies = [ [[package]] name = "rustmailer" -version = "1.5.3" +version = "1.6.0" dependencies = [ "ahash", "async-imap", @@ -5748,6 +5748,7 @@ dependencies = [ "autoconfig", "base64 0.22.1", "bb8", + "bytes 1.10.1", "cacache", "chrono", "clap", @@ -5816,7 +5817,7 @@ dependencies = [ "urlencoding", "utf7-imap", "vrl", - "webpki-roots 1.0.3", + "webpki-roots 1.0.4", ] [[package]] @@ -6911,7 +6912,7 @@ version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "libc", "mio", "parking_lot 0.12.3", @@ -6983,7 +6984,7 @@ version = "0.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" dependencies = [ - "bytes 1.7.2", + "bytes 1.10.1", "futures-core", "futures-sink", "pin-project-lite", @@ -6997,7 +6998,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f591660438b3038dd04d16c938271c79e7e06260ad2ea2885a4861bfb238605d" dependencies = [ "base64 0.22.1", - "bytes 1.7.2", + "bytes 1.10.1", "futures-core", "futures-sink", "http 1.3.1", @@ -7078,7 +7079,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "bitflags 2.6.0", - "bytes 1.7.2", + "bytes 1.10.1", "futures-util", "http 1.3.1", "http-body", @@ -7507,16 +7508,16 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vrl" -version = "0.27.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374906b686967832d85e5ce8f43581924e8382787e538c61c1e2158e09fc6518" +checksum = "a00427df1a5af971424054933850c6aa6ed13c6e1c6fc35e9f373d3ab5d071f8" dependencies = [ "aes 0.8.4", "aes-siv", "base16", "base62", "base64-simd", - "bytes 1.7.2", + "bytes 1.10.1", "cbc", "cfb-mode", "cfg-if", @@ -7526,7 +7527,6 @@ dependencies = [ "chrono-tz", "ciborium", "cidr", - "clap", "codespan-reporting", "community-id", "convert_case", @@ -7808,9 +7808,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" dependencies = [ "rustls-pki-types", ] diff --git a/Cargo.toml b/Cargo.toml index 1c0c260..1bcced3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustmailer" -version = "1.5.3" +version = "1.6.0" edition = "2021" [[bin]] @@ -20,7 +20,7 @@ codegen-units = 1 [dependencies] chrono = "0.4.42" -clap = { version = "4.5.49", features = ["derive", "env"] } +clap = { version = "4.5.51", features = ["derive", "env"] } mimalloc = "0.1.48" native_db = "0.8.2" redb = "2.6.2" @@ -83,7 +83,7 @@ oauth2 = { version = "5.0.0", default-features = false, features = [ url = { version = "2.5.7", features = ["serde"] } sysinfo = "0.37.2" num_cpus = "1.17.0" -proc-macro2 = "1.0.101" +proc-macro2 = "1.0.103" cacache = { version = "13.1.0", default-features = false, features = [ "tokio-runtime", "mmap", @@ -94,9 +94,9 @@ async-imap = { version = "0.11.1", default-features = false, features = [ "runtime-tokio", "compress", ] } -webpki-roots = "1.0.3" -rustls = { version = "0.23.33", default-features = false, features = ["ring"] } -rustls-pki-types = "1.12.0" +webpki-roots = "1.0.4" +rustls = { version = "0.23.35", default-features = false, features = ["ring"] } +rustls-pki-types = "1.13.0" tokio-io-timeout = "1.2.1" bb8 = "0.9.0" # Note: Keep prost and prost-types at the same version to avoid potential conflicts. @@ -105,7 +105,7 @@ prost = "0.13.5" prost-types = "0.13.5" semver = "1.0.27" governor = "0.10.1" -async-nats = { version = "0.44.2" } +async-nats = { version = "0.45.0" } lru = "0.16.2" mime_guess = "2.0.5" pulldown-cmark = "0.13.0" @@ -118,11 +118,11 @@ time = { version = "0.3.44", features = [ "local-offset", ] } time-tz = "2.0.0" -rust-embed = "8.8.0" +rust-embed = "8.9.0" murmur3 = "0.5.2" autoconfig = "0.4.0" urlencoding = "2.1.3" -vrl = "0.27.0" +vrl = "0.28.0" dashmap = "6.1.0" prometheus = "0.14.0" # Statically links OpenSSL by compiling from source, avoiding system library dependencies @@ -130,6 +130,7 @@ openssl-sys = { version = "0.9.110", optional = true, features = ["vendored"] } image = "0.25.8" gethostname = "1.1.0" glob = "0.3.3" +bytes = "1.10.1" [build-dependencies] poem-grpc-build = "0.5.7" diff --git a/src/modules/cache/vendor/gmail/sync/client.rs b/src/modules/cache/vendor/gmail/sync/client.rs index 114fe1d..2680d4d 100644 --- a/src/modules/cache/vendor/gmail/sync/client.rs +++ b/src/modules/cache/vendor/gmail/sync/client.rs @@ -18,8 +18,8 @@ use crate::{ messages::{FullMessage, MessageList, MessageMeta, PartBody}, }, }, + common::http::HttpClient, error::{code::ErrorCode, RustMailerResult}, - hook::http::HttpClient, mailbox::{create::CreateMailboxRequest, rename::MailboxUpdateRequest}, message::append::ReplyDraft, oauth2::token::OAuth2AccessToken, diff --git a/src/modules/cache/vendor/outlook/sync/client.rs b/src/modules/cache/vendor/outlook/sync/client.rs index 4b65e90..30af39b 100644 --- a/src/modules/cache/vendor/outlook/sync/client.rs +++ b/src/modules/cache/vendor/outlook/sync/client.rs @@ -1,3 +1,4 @@ +use bytes::Bytes; use serde_json::json; use crate::{ @@ -5,8 +6,8 @@ use crate::{ cache::vendor::outlook::model::{ MailFolder, MailFoldersResponse, Message, MessageListResponse, }, + common::http::HttpClient, error::{code::ErrorCode, RustMailerResult}, - hook::http::HttpClient, message::append::ReplyDraft, oauth2::token::OAuth2AccessToken, }, @@ -230,6 +231,18 @@ impl OutlookClient { Ok(message) } + pub async fn get_raw_message( + account_id: u64, + use_proxy: Option, + id: &str, + ) -> RustMailerResult { + let url = format!("https://graph.microsoft.com/v1.0/me/messages/{id}/$value"); + let client = HttpClient::new(use_proxy).await?; + let access_token = Self::get_access_token(account_id).await?; + let value = client.get_bytes(url.as_str(), &access_token).await?; + Ok(value) + } + pub async fn get_attachment( account_id: u64, use_proxy: Option, diff --git a/src/modules/cache/vendor/outlook/sync/delta.rs b/src/modules/cache/vendor/outlook/sync/delta.rs index 14f8d49..d2756fb 100644 --- a/src/modules/cache/vendor/outlook/sync/delta.rs +++ b/src/modules/cache/vendor/outlook/sync/delta.rs @@ -7,24 +7,17 @@ use serde::{Deserialize, Serialize}; use crate::{ modules::{ - account::migration::AccountModel, - cache::vendor::outlook::{ + account::migration::AccountModel, cache::vendor::outlook::{ model::DeltaResponse, sync::{client::OutlookClient, envelope::OutlookEnvelope, folders::OutlookFolder}, - }, - database::{ + }, common::http::HttpClient, database::{ async_find_impl, batch_delete_impl, filter_by_secondary_key_impl, manager::DB_MANAGER, upsert_impl, - }, - error::{code::ErrorCode, RustMailerResult}, - hook::{ - channel::{Event, EVENT_CHANNEL}, - events::{payload::EmailAddedToFolder, EventPayload, EventType, RustMailerEvent}, - http::HttpClient, + }, error::{RustMailerResult, code::ErrorCode}, hook::{ + channel::{EVENT_CHANNEL, Event}, + events::{EventPayload, EventType, RustMailerEvent, payload::EmailAddedToFolder}, task::EventHookTask, - }, - message::content::FullMessageContent, - utils::mailbox_id, + }, message::content::FullMessageContent, utils::mailbox_id }, raise_error, utc_now, }; diff --git a/src/modules/cache/vendor/outlook/tests.rs b/src/modules/cache/vendor/outlook/tests.rs index 7a8685b..ff19552 100644 --- a/src/modules/cache/vendor/outlook/tests.rs +++ b/src/modules/cache/vendor/outlook/tests.rs @@ -2,21 +2,18 @@ // Licensed under RustMailer License Agreement v1.0 // Unauthorized copying, modification, or distribution is prohibited. -use http::header::CONTENT_TYPE; +use http::header::{ACCEPT, CONTENT_TYPE}; use poem_grpc::{ClientConfig, CompressionEncoding}; use reqwest::{header::AUTHORIZATION, Client}; -use std::{future::Future, pin::Pin, time::Duration}; +use std::{fs::File, future::Future, io::Write, pin::Pin, time::Duration}; use crate::{ modules::{ - cache::vendor::outlook::model::{ - MailFolder, MailFoldersResponse, Message, MessageListResponse, - }, + cache::vendor::outlook::model::{MailFolder, MailFoldersResponse, MessageListResponse}, common::rustls::RustMailerTls, context::Initialize, error::{code::ErrorCode, RustMailerResult}, grpc::service::rustmailer_grpc::{GetOAuth2TokensRequest, OAuth2ServiceClient}, - hook::http::HttpClient, }, raise_error, rustmailer_version, }; @@ -488,3 +485,36 @@ async fn create_reply_draft() { eprintln!("Error: {} - {:?}", res.status(), res.text().await.unwrap()); } } + +#[tokio::test] +async fn get_raw_message() { + let access_token = access_token().await; + let url = format!( + "https://graph.microsoft.com/v1.0/me/messages/{}/$value", + "AQMkADAwATMwMAItNzE0OC1jZTEzLTAwAi0wMAoARgAAA_KUk7xWPSBEntPHShr61lgHAOo9V4GwHndCjf0x1uoIcwUAAAIBDAAAAOo9V4GwHndCjf0x1uoIcwUAAYiJVIEAAAA=" + ); + let client = reqwest::Client::builder() + .user_agent(rustmailer_version!()) + .timeout(Duration::from_secs(10)) + .connect_timeout(Duration::from_secs(10)) + .proxy(reqwest::Proxy::all("http://127.0.0.1:22307").unwrap()) + .redirect(reqwest::redirect::Policy::none()) + .build() + .unwrap(); + + let res = client + .get(&url) + .header(AUTHORIZATION, format!("Bearer {}", access_token)) + .header(ACCEPT, "application/octet-stream") + .send() + .await + .unwrap(); + + if res.status().is_success() { + let bytes = res.bytes().await.unwrap(); + let mut file = File::create("e:\\message.eml").unwrap(); + file.write_all(&bytes).unwrap(); + } else { + eprintln!("Error: {} - {:?}", res.status(), res.text().await.unwrap()); + } +} diff --git a/src/modules/hook/http/mod.rs b/src/modules/common/http/mod.rs similarity index 82% rename from src/modules/hook/http/mod.rs rename to src/modules/common/http/mod.rs index 81f6058..666a828 100644 --- a/src/modules/hook/http/mod.rs +++ b/src/modules/common/http/mod.rs @@ -2,8 +2,9 @@ // Licensed under RustMailer License Agreement v1.0 // Unauthorized copying, modification, or distribution is prohibited. +use bytes::Bytes; use dashmap::DashMap; -use http::header::{AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE}; +use http::header::{ACCEPT, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE}; use http::StatusCode; use serde::Serialize; use tracing::error; @@ -218,6 +219,84 @@ impl HttpClient { } } + pub async fn get_bytes( + &self, + url: &str, + access_token: &str, + ) -> RustMailerResult { + let mut attempt = 0; + let max_attempts = 4; + let mut delay_ms = 500; + + loop { + attempt += 1; + let res_result = self + .client + .get(url) + .header(AUTHORIZATION, format!("Bearer {}", access_token)) + .header(ACCEPT, "application/octet-stream") + .send() + .await; + + match res_result { + Ok(res) => { + if res.status().is_success() { + let bytes = res.bytes().await.map_err(|e| { + raise_error!( + format!("Failed to parse response: {:#?}", e), + ErrorCode::InternalError + ) + })?; + return Ok(bytes); + } else { + let status = res.status(); + let text = res.text().await.unwrap_or_default(); + + if attempt < max_attempts && status.is_server_error() { + tracing::warn!( + "API call to {} returned server error {} on attempt {}. Retrying after {}ms...", + url, status, attempt, delay_ms + ); + tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await; + delay_ms *= 2; + continue; + } + + return Err(raise_error!( + format!( + "API call to {} failed with status {}: {}", + url, status, text + ), + ErrorCode::ApiCallFailed + )); + } + } + Err(e) => { + if attempt < max_attempts { + tracing::warn!( + "Request to {} failed on attempt {}: {:#?}, retrying after {}ms", + url, + attempt, + e, + delay_ms + ); + tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await; + delay_ms *= 2; + continue; + } else { + return Err(raise_error!( + format!( + "Request to {} failed after {} attempts: {:#?}", + url, attempt, e + ), + ErrorCode::ApiCallFailed + )); + } + } + } + } + } + /// Wrapper around the Gmail API `POST` request. pub async fn post( &self, diff --git a/src/modules/hook/http/tests.rs b/src/modules/common/http/tests.rs similarity index 96% rename from src/modules/hook/http/tests.rs rename to src/modules/common/http/tests.rs index 6a4a6b6..63e88f8 100644 --- a/src/modules/hook/http/tests.rs +++ b/src/modules/common/http/tests.rs @@ -4,10 +4,7 @@ use std::time::Duration; -use crate::{ - modules::{error::code::ErrorCode, hook::http::HttpClient}, - raise_error, rustmailer_version, -}; +use crate::{modules::{common::http::HttpClient, error::code::ErrorCode}, raise_error, rustmailer_version}; #[tokio::test] async fn test_connect_timeout() { @@ -103,7 +100,7 @@ async fn test_connect_use_proxy() { let client = HttpClient::create(client); let url = "https://discord.com/api/webhooks/1397150752484622416/9yb6QJSJkszn-uiDge3No3ri9B2-shKMKOT1ruijnPbVtd_k9HAuqspn8C2cOXIqu4l5"; let payload = json!({ - "avatar_url": "https://github.com/rustmailer.png", + "avatar_url": "https://github.com/rustmailer.png", "content": "hello world", "embeds": [ { @@ -145,7 +142,7 @@ async fn test_connect_use_proxy() { let result = client .send_json_request(None, HttpMethod::Post, url, &payload, None) .await; - + match result { Err(e) => { let err_str = e.to_string(); diff --git a/src/modules/common/mod.rs b/src/modules/common/mod.rs index 37ea03b..8fdfe44 100644 --- a/src/modules/common/mod.rs +++ b/src/modules/common/mod.rs @@ -23,6 +23,7 @@ use tracing::error; pub mod auth; pub mod error; +pub mod http; pub mod log; pub mod lru; pub mod paginated; diff --git a/src/modules/hook/mod.rs b/src/modules/hook/mod.rs index 2f1d27e..10701f5 100644 --- a/src/modules/hook/mod.rs +++ b/src/modules/hook/mod.rs @@ -5,7 +5,6 @@ pub mod channel; pub mod entity; pub mod events; -pub mod http; pub mod nats; pub mod payload; pub mod task; diff --git a/src/modules/hook/task.rs b/src/modules/hook/task.rs index 43e6c30..76e6e7d 100644 --- a/src/modules/hook/task.rs +++ b/src/modules/hook/task.rs @@ -5,11 +5,12 @@ use std::collections::HashMap; use std::time::Instant; +use crate::modules::common::http::HttpClient; use crate::modules::error::code::ErrorCode; use crate::modules::error::RustMailerError; +use crate::modules::hook::entity::EventHooks; use crate::modules::hook::vrl::payload::VrlScriptTestRequest; use crate::modules::hook::vrl::resolve_vrl_input; -use crate::modules::hook::{entity::EventHooks, http::HttpClient}; use crate::modules::metrics::{ FAILURE, RUSTMAILER_EVENT_DISPATCH_DURATION_SECONDS_BY_TYPE_STATUS_AND_DESTINATION, RUSTMAILER_EVENT_DISPATCH_TOTAL_BY_TYPE_STATUS_AND_DESTINATION, SUCCESS, diff --git a/src/modules/message/full.rs b/src/modules/message/full.rs index 542ad22..617d1e5 100644 --- a/src/modules/message/full.rs +++ b/src/modules/message/full.rs @@ -6,7 +6,10 @@ use crate::{ base64_decode_url_safe, modules::{ account::{entity::MailerType, migration::AccountModel}, - cache::{disk::DISK_CACHE, vendor::gmail::sync::client::GmailClient}, + cache::{ + disk::DISK_CACHE, + vendor::{gmail::sync::client::GmailClient, outlook::sync::client::OutlookClient}, + }, context::executors::RUST_MAIL_CONTEXT, error::{code::ErrorCode, RustMailerResult}, }, @@ -35,7 +38,11 @@ fn gmail_raw_email_diskcache_key(account_id: u64, mid: &str) -> String { format!("gmail_raw_email_{}_{}", account_id, mid) } -pub async fn retrieve_raw_email( +fn outlook_raw_email_diskcache_key(account_id: u64, mid: &str) -> String { + format!("outlook_raw_email_{}_{}", account_id, mid) +} + +pub async fn retrieve_raw_email( account_id: u64, mailbox: Option<&str>, id: &str, @@ -58,7 +65,7 @@ pub async fn retrieve_raw_email( retrieve_imap_raw_email(account_id, mailbox, uid).await } MailerType::GmailApi => retrieve_gmail_raw_email(&account, id).await, - MailerType::GraphApi => todo!(), + MailerType::GraphApi => retrieve_outlook_raw_email(&account, id).await, } } @@ -148,3 +155,19 @@ async fn retrieve_gmail_raw_email( .await? .ok_or_else(|| raise_error!("Unexpected cache miss".into(), ErrorCode::InternalError)) } + +async fn retrieve_outlook_raw_email( + account: &AccountModel, + mid: &str, +) -> RustMailerResult { + let cache_key = outlook_raw_email_diskcache_key(account.id, mid); + if let Some(reader) = DISK_CACHE.get_cache(&cache_key).await? { + return Ok(reader); + } + let data = OutlookClient::get_raw_message(account.id, account.use_proxy, mid).await?; + DISK_CACHE.put_cache(&cache_key, &data, false).await?; + DISK_CACHE + .get_cache(&cache_key) + .await? + .ok_or_else(|| raise_error!("Unexpected cache miss".into(), ErrorCode::InternalError)) +}