You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

569 lines
17 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
1 year ago
11 months ago
1 year ago
1 year ago
11 months ago
1 year ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
11 months ago
11 months ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
11 months ago
1 year ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. /*
  2. * meli - jmap module.
  3. *
  4. * Copyright 2019 Manos Pitsidianakis
  5. *
  6. * This file is part of meli.
  7. *
  8. * meli is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * meli is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with meli. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. use super::*;
  22. use crate::backends::jmap::rfc8620::bool_false;
  23. use core::marker::PhantomData;
  24. use serde::de::{Deserialize, Deserializer};
  25. use serde_json::Value;
  26. use std::collections::hash_map::DefaultHasher;
  27. use std::collections::HashMap;
  28. use std::hash::Hasher;
  29. // 4.1.1.
  30. // Metadata
  31. // These properties represent metadata about the message in the mail
  32. // store and are not derived from parsing the message itself.
  33. //
  34. // o id: "Id" (immutable; server-set)
  35. //
  36. // The id of the Email object. Note that this is the JMAP object id,
  37. // NOT the Message-ID header field value of the message [RFC5322].
  38. //
  39. // o blobId: "Id" (immutable; server-set)
  40. //
  41. // The id representing the raw octets of the message [RFC5322] for
  42. // this Email. This may be used to download the raw original message
  43. // or to attach it directly to another Email, etc.
  44. //
  45. // o threadId: "Id" (immutable; server-set)
  46. //
  47. // The id of the Thread to which this Email belongs.
  48. //
  49. // o mailboxIds: "Id[Boolean]"
  50. //
  51. // The set of Mailbox ids this Email belongs to. An Email in the
  52. // mail store MUST belong to one or more Mailboxes at all times
  53. // (until it is destroyed). The set is represented as an object,
  54. // with each key being a Mailbox id. The value for each key in the
  55. // object MUST be true.
  56. //
  57. // o keywords: "String[Boolean]" (default: {})
  58. //
  59. // A set of keywords that apply to the Email. The set is represented
  60. // as an object, with the keys being the keywords. The value for
  61. // each key in the object MUST be true.
  62. //
  63. // Keywords are shared with IMAP. The six system keywords from IMAP
  64. // get special treatment. The following four keywords have their
  65. // first character changed from "\" in IMAP to "$" in JMAP and have
  66. // particular semantic meaning:
  67. //
  68. // * "$draft": The Email is a draft the user is composing.
  69. //
  70. // * "$seen": The Email has been read.
  71. //
  72. // * "$flagged": The Email has been flagged for urgent/special
  73. // attention.
  74. //
  75. // * "$answered": The Email has been replied to.
  76. //
  77. // The IMAP "\Recent" keyword is not exposed via JMAP. The IMAP
  78. // "\Deleted" keyword is also not present: IMAP uses a delete+expunge
  79. // model, which JMAP does not. Any message with the "\Deleted"
  80. // keyword MUST NOT be visible via JMAP (and so are not counted in
  81. // the "totalEmails", "unreadEmails", "totalThreads", and
  82. // "unreadThreads" Mailbox properties).
  83. //
  84. // Users may add arbitrary keywords to an Email. For compatibility
  85. // with IMAP, a keyword is a case-insensitive string of 1-255
  86. // characters in the ASCII subset %x21-%x7e (excludes control chars
  87. // and space), and it MUST NOT include any of these characters:
  88. //
  89. // ( ) { ] % * " \
  90. //
  91. // Because JSON is case sensitive, servers MUST return keywords in
  92. // lowercase.
  93. //
  94. // The IANA "IMAP and JMAP Keywords" registry at
  95. // <https://www.iana.org/assignments/imap-jmap-keywords/> as
  96. // established in [RFC5788] assigns semantic meaning to some other
  97. // keywords in common use. New keywords may be established here in
  98. // the future. In particular, note:
  99. //
  100. // * "$forwarded": The Email has been forwarded.
  101. //
  102. // * "$phishing": The Email is highly likely to be phishing.
  103. // Clients SHOULD warn users to take care when viewing this Email
  104. // and disable links and attachments.
  105. //
  106. // * "$junk": The Email is definitely spam. Clients SHOULD set this
  107. // flag when users report spam to help train automated spam-
  108. // detection systems.
  109. //
  110. // * "$notjunk": The Email is definitely not spam. Clients SHOULD
  111. // set this flag when users indicate an Email is legitimate, to
  112. // help train automated spam-detection systems.
  113. //
  114. // o size: "UnsignedInt" (immutable; server-set)
  115. //
  116. // The size, in octets, of the raw data for the message [RFC5322] (as
  117. // referenced by the "blobId", i.e., the number of octets in the file
  118. // the user would download).
  119. //
  120. // o receivedAt: "UTCDate" (immutable; default: time of creation on
  121. // server)
  122. //
  123. // The date the Email was received by the message store. This is the
  124. // "internal date" in IMAP [RFC3501]./
  125. #[derive(Deserialize, Serialize, Debug)]
  126. #[serde(rename_all = "camelCase")]
  127. pub struct EmailObject {
  128. #[serde(default)]
  129. pub id: Id,
  130. #[serde(default)]
  131. pub blob_id: String,
  132. #[serde(default)]
  133. mailbox_ids: HashMap<Id, bool>,
  134. #[serde(default)]
  135. size: u64,
  136. #[serde(default)]
  137. received_at: String,
  138. #[serde(default)]
  139. message_id: Vec<String>,
  140. #[serde(default)]
  141. to: SmallVec<[EmailAddress; 1]>,
  142. #[serde(default)]
  143. bcc: Option<Vec<EmailAddress>>,
  144. #[serde(default)]
  145. reply_to: Option<Vec<EmailAddress>>,
  146. #[serde(default)]
  147. cc: Option<SmallVec<[EmailAddress; 1]>>,
  148. #[serde(default)]
  149. sender: Option<Vec<EmailAddress>>,
  150. #[serde(default)]
  151. from: SmallVec<[EmailAddress; 1]>,
  152. #[serde(default)]
  153. in_reply_to: Option<Vec<String>>,
  154. #[serde(default)]
  155. references: Option<Vec<String>>,
  156. #[serde(default)]
  157. keywords: HashMap<String, bool>,
  158. #[serde(default)]
  159. attached_emails: Option<Id>,
  160. #[serde(default)]
  161. attachments: Vec<Value>,
  162. #[serde(default)]
  163. has_attachment: bool,
  164. #[serde(default)]
  165. #[serde(deserialize_with = "deserialize_header")]
  166. headers: HashMap<String, String>,
  167. #[serde(default)]
  168. html_body: Vec<HtmlBody>,
  169. #[serde(default)]
  170. preview: Option<String>,
  171. #[serde(default)]
  172. sent_at: Option<String>,
  173. #[serde(default)]
  174. subject: Option<String>,
  175. #[serde(default)]
  176. text_body: Vec<TextBody>,
  177. #[serde(default)]
  178. thread_id: Id,
  179. #[serde(flatten)]
  180. extra: HashMap<String, Value>,
  181. }
  182. impl EmailObject {
  183. _impl!(get keywords, keywords: HashMap<String, bool>);
  184. }
  185. #[derive(Deserialize, Serialize, Debug, Default)]
  186. #[serde(rename_all = "camelCase")]
  187. struct Header {
  188. name: String,
  189. value: String,
  190. }
  191. fn deserialize_header<'de, D>(
  192. deserializer: D,
  193. ) -> std::result::Result<HashMap<String, String>, D::Error>
  194. where
  195. D: Deserializer<'de>,
  196. {
  197. let v = <Vec<Header>>::deserialize(deserializer)?;
  198. Ok(v.into_iter().map(|t| (t.name, t.value)).collect())
  199. }
  200. #[derive(Deserialize, Serialize, Debug, Default)]
  201. #[serde(rename_all = "camelCase")]
  202. struct EmailAddress {
  203. email: String,
  204. name: Option<String>,
  205. }
  206. impl Into<crate::email::Address> for EmailAddress {
  207. fn into(self) -> crate::email::Address {
  208. let Self { email, mut name } = self;
  209. crate::make_address!((name.take().unwrap_or_default()), email)
  210. }
  211. }
  212. impl std::fmt::Display for EmailAddress {
  213. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  214. if self.name.is_some() {
  215. write!(f, "{} <{}>", self.name.as_ref().unwrap(), &self.email)
  216. } else {
  217. write!(f, "{}", &self.email)
  218. }
  219. }
  220. }
  221. impl std::convert::From<EmailObject> for crate::Envelope {
  222. fn from(mut t: EmailObject) -> crate::Envelope {
  223. let mut env = crate::Envelope::new(0);
  224. if let Ok(d) = crate::email::parser::generic::date(env.date_as_str().as_bytes()) {
  225. env.set_datetime(d);
  226. }
  227. if let Some(ref mut sent_at) = t.sent_at {
  228. let unix =
  229. crate::datetime::rfc3339_to_timestamp(sent_at.as_bytes().to_vec()).unwrap_or(0);
  230. env.set_datetime(unix);
  231. env.set_date(std::mem::replace(sent_at, String::new()).as_bytes());
  232. }
  233. if let Some(v) = t.message_id.get(0) {
  234. env.set_message_id(v.as_bytes());
  235. }
  236. if let Some(ref in_reply_to) = t.in_reply_to {
  237. env.set_in_reply_to(in_reply_to[0].as_bytes());
  238. env.push_references(in_reply_to[0].as_bytes());
  239. }
  240. if let Some(v) = t.headers.get("References") {
  241. let parse_result = crate::email::parser::address::references(v.as_bytes());
  242. if let Ok((_, v)) = parse_result {
  243. for v in v {
  244. env.push_references(v);
  245. }
  246. }
  247. env.set_references(v.as_bytes());
  248. }
  249. if let Some(v) = t.headers.get("Date") {
  250. env.set_date(v.as_bytes());
  251. if let Ok(d) = crate::email::parser::generic::date(v.as_bytes()) {
  252. env.set_datetime(d);
  253. }
  254. }
  255. env.set_has_attachments(t.has_attachment);
  256. if let Some(ref mut subject) = t.subject {
  257. env.set_subject(std::mem::replace(subject, String::new()).into_bytes());
  258. }
  259. env.set_from(
  260. std::mem::replace(&mut t.from, SmallVec::new())
  261. .into_iter()
  262. .map(|addr| addr.into())
  263. .collect::<SmallVec<[crate::email::Address; 1]>>(),
  264. );
  265. env.set_to(
  266. std::mem::replace(&mut t.to, SmallVec::new())
  267. .into_iter()
  268. .map(|addr| addr.into())
  269. .collect::<SmallVec<[crate::email::Address; 1]>>(),
  270. );
  271. if let Some(ref mut cc) = t.cc {
  272. env.set_cc(
  273. std::mem::replace(cc, SmallVec::new())
  274. .into_iter()
  275. .map(|addr| addr.into())
  276. .collect::<SmallVec<[crate::email::Address; 1]>>(),
  277. );
  278. }
  279. if let Some(ref mut bcc) = t.bcc {
  280. env.set_bcc(
  281. std::mem::replace(bcc, Vec::new())
  282. .into_iter()
  283. .map(|addr| addr.into())
  284. .collect::<Vec<crate::email::Address>>(),
  285. );
  286. }
  287. if env.references.is_some() {
  288. if let Some(pos) = env
  289. .references
  290. .as_ref()
  291. .map(|r| &r.refs)
  292. .unwrap()
  293. .iter()
  294. .position(|r| r == env.message_id())
  295. {
  296. env.references.as_mut().unwrap().refs.remove(pos);
  297. }
  298. }
  299. let mut h = DefaultHasher::new();
  300. h.write(t.id.as_bytes());
  301. env.set_hash(h.finish());
  302. env
  303. }
  304. }
  305. #[derive(Deserialize, Serialize, Debug)]
  306. #[serde(rename_all = "camelCase")]
  307. struct HtmlBody {
  308. blob_id: Id,
  309. #[serde(default)]
  310. charset: String,
  311. #[serde(default)]
  312. cid: Option<String>,
  313. #[serde(default)]
  314. disposition: Option<String>,
  315. #[serde(default)]
  316. headers: Value,
  317. #[serde(default)]
  318. language: Option<Vec<String>>,
  319. #[serde(default)]
  320. location: Option<String>,
  321. #[serde(default)]
  322. name: Option<String>,
  323. #[serde(default)]
  324. part_id: Option<String>,
  325. size: u64,
  326. #[serde(alias = "type")]
  327. content_type: String,
  328. #[serde(default)]
  329. sub_parts: Vec<Value>,
  330. }
  331. #[derive(Deserialize, Serialize, Debug)]
  332. #[serde(rename_all = "camelCase")]
  333. struct TextBody {
  334. blob_id: Id,
  335. #[serde(default)]
  336. charset: String,
  337. #[serde(default)]
  338. cid: Option<String>,
  339. #[serde(default)]
  340. disposition: Option<String>,
  341. #[serde(default)]
  342. headers: Value,
  343. #[serde(default)]
  344. language: Option<Vec<String>>,
  345. #[serde(default)]
  346. location: Option<String>,
  347. #[serde(default)]
  348. name: Option<String>,
  349. #[serde(default)]
  350. part_id: Option<String>,
  351. size: u64,
  352. #[serde(alias = "type")]
  353. content_type: String,
  354. #[serde(default)]
  355. sub_parts: Vec<Value>,
  356. }
  357. impl Object for EmailObject {
  358. const NAME: &'static str = "Email";
  359. }
  360. #[derive(Deserialize, Serialize, Debug)]
  361. #[serde(rename_all = "camelCase")]
  362. pub struct EmailQueryResponse {
  363. pub account_id: Id,
  364. pub can_calculate_changes: bool,
  365. pub collapse_threads: bool,
  366. // FIXME
  367. pub filter: String,
  368. pub ids: Vec<Id>,
  369. pub position: u64,
  370. pub query_state: String,
  371. pub sort: Option<String>,
  372. pub total: usize,
  373. }
  374. #[derive(Serialize, Debug)]
  375. #[serde(rename_all = "camelCase")]
  376. pub struct EmailQuery {
  377. #[serde(flatten)]
  378. pub query_call: Query<EmailFilterCondition, EmailObject>,
  379. //pub filter: EmailFilterCondition, /* "inMailboxes": [ mailbox.id ] },*/
  380. pub collapse_threads: bool,
  381. }
  382. impl Method<EmailObject> for EmailQuery {
  383. const NAME: &'static str = "Email/query";
  384. }
  385. impl EmailQuery {
  386. pub const RESULT_FIELD_IDS: ResultField<EmailQuery, EmailObject> =
  387. ResultField::<EmailQuery, EmailObject> {
  388. field: "/ids",
  389. _ph: PhantomData,
  390. };
  391. pub fn new(query_call: Query<EmailFilterCondition, EmailObject>) -> Self {
  392. EmailQuery {
  393. query_call,
  394. collapse_threads: false,
  395. }
  396. }
  397. _impl!(collapse_threads: bool);
  398. }
  399. #[derive(Deserialize, Serialize, Debug)]
  400. #[serde(rename_all = "camelCase")]
  401. pub struct EmailGet {
  402. #[serde(flatten)]
  403. pub get_call: Get<EmailObject>,
  404. #[serde(skip_serializing_if = "Vec::is_empty")]
  405. pub body_properties: Vec<String>,
  406. #[serde(default = "bool_false")]
  407. pub fetch_text_body_values: bool,
  408. #[serde(default = "bool_false")]
  409. #[serde(rename = "fetchHTMLBodyValues")]
  410. pub fetch_html_body_values: bool,
  411. #[serde(default = "bool_false")]
  412. pub fetch_all_body_values: bool,
  413. #[serde(default)]
  414. #[serde(skip_serializing_if = "u64_zero")]
  415. pub max_body_value_bytes: u64,
  416. }
  417. impl Method<EmailObject> for EmailGet {
  418. const NAME: &'static str = "Email/get";
  419. }
  420. impl EmailGet {
  421. pub fn new(get_call: Get<EmailObject>) -> Self {
  422. EmailGet {
  423. get_call,
  424. body_properties: Vec::new(),
  425. fetch_text_body_values: false,
  426. fetch_html_body_values: false,
  427. fetch_all_body_values: false,
  428. max_body_value_bytes: 0,
  429. }
  430. }
  431. _impl!(body_properties: Vec<String>);
  432. _impl!(fetch_text_body_values: bool);
  433. _impl!(fetch_html_body_values: bool);
  434. _impl!(fetch_all_body_values: bool);
  435. _impl!(max_body_value_bytes: u64);
  436. }
  437. #[derive(Serialize, Deserialize, Default, Debug)]
  438. #[serde(rename_all = "camelCase")]
  439. pub struct EmailFilterCondition {
  440. #[serde(skip_serializing_if = "Option::is_none")]
  441. pub in_mailbox: Option<Id>,
  442. #[serde(skip_serializing_if = "Vec::is_empty")]
  443. pub in_mailbox_other_than: Vec<Id>,
  444. #[serde(skip_serializing_if = "String::is_empty")]
  445. pub before: UtcDate,
  446. #[serde(skip_serializing_if = "String::is_empty")]
  447. pub after: UtcDate,
  448. #[serde(default)]
  449. #[serde(skip_serializing_if = "Option::is_none")]
  450. pub min_size: Option<u64>,
  451. #[serde(default)]
  452. #[serde(skip_serializing_if = "Option::is_none")]
  453. pub max_size: Option<u64>,
  454. #[serde(skip_serializing_if = "String::is_empty")]
  455. pub all_in_thread_have_keyword: String,
  456. #[serde(skip_serializing_if = "String::is_empty")]
  457. pub some_in_thread_have_keyword: String,
  458. #[serde(skip_serializing_if = "String::is_empty")]
  459. pub none_in_thread_have_keyword: String,
  460. #[serde(skip_serializing_if = "String::is_empty")]
  461. pub has_keyword: String,
  462. #[serde(skip_serializing_if = "String::is_empty")]
  463. pub not_keyword: String,
  464. #[serde(skip_serializing_if = "Option::is_none")]
  465. pub has_attachment: Option<bool>,
  466. #[serde(skip_serializing_if = "String::is_empty")]
  467. pub text: String,
  468. #[serde(skip_serializing_if = "String::is_empty")]
  469. pub from: String,
  470. #[serde(skip_serializing_if = "String::is_empty")]
  471. pub to: String,
  472. #[serde(skip_serializing_if = "String::is_empty")]
  473. pub cc: String,
  474. #[serde(skip_serializing_if = "String::is_empty")]
  475. pub bcc: String,
  476. #[serde(skip_serializing_if = "String::is_empty")]
  477. pub subject: String,
  478. #[serde(skip_serializing_if = "String::is_empty")]
  479. pub body: String,
  480. #[serde(skip_serializing_if = "Vec::is_empty")]
  481. pub header: Vec<Value>,
  482. }
  483. impl EmailFilterCondition {
  484. pub fn new() -> Self {
  485. Self::default()
  486. }
  487. _impl!(in_mailbox: Option<Id>);
  488. _impl!(in_mailbox_other_than: Vec<Id>);
  489. _impl!(before: UtcDate);
  490. _impl!(after: UtcDate);
  491. _impl!(min_size: Option<u64>);
  492. _impl!(max_size: Option<u64>);
  493. _impl!(all_in_thread_have_keyword: String);
  494. _impl!(some_in_thread_have_keyword: String);
  495. _impl!(none_in_thread_have_keyword: String);
  496. _impl!(has_keyword: String);
  497. _impl!(not_keyword: String);
  498. _impl!(has_attachment: Option<bool>);
  499. _impl!(text: String);
  500. _impl!(from: String);
  501. _impl!(to: String);
  502. _impl!(cc: String);
  503. _impl!(bcc: String);
  504. _impl!(subject: String);
  505. _impl!(body: String);
  506. _impl!(header: Vec<Value>);
  507. }
  508. impl FilterTrait<EmailObject> for EmailFilterCondition {}
  509. #[derive(Deserialize, Serialize, Debug)]
  510. #[serde(rename_all = "camelCase")]
  511. pub enum MessageProperty {
  512. ThreadId,
  513. MailboxIds,
  514. Keywords,
  515. Size,
  516. ReceivedAt,
  517. IsUnread,
  518. IsFlagged,
  519. IsAnswered,
  520. IsDraft,
  521. HasAttachment,
  522. From,
  523. To,
  524. Cc,
  525. Bcc,
  526. ReplyTo,
  527. Subject,
  528. SentAt,
  529. Preview,
  530. Id,
  531. BlobId,
  532. MessageId,
  533. InReplyTo,
  534. Sender,
  535. }