mail_core/resource/
mod.rs

1// a module level circ. dep. but fine as only
2// used for more ergonomic helper constructors
3use ::context::Context;
4
5#[cfg(feature="serde")]
6use serde::{Serialize, Deserialize};
7
8// We can provide a resource through two ways
9// 1. providing a Source (iri + media_type_override + name override)
10// 2. providing the actual data
11// 3. we could provide transfer encoded resources, normally we do not
12
13// We normally use a resource only in a transfer encoded way.
14//
15// - So if we get a source we have to load the actual data.
16// - If we get the actual data we still have to transfer encode it.
17//
18
19// We normally do not need to keep the actual data once we did the transfer
20// encoding.
21//
22// BUT the best chosen encoding might depend on the actual case mainly
23// the mail type e.g. if the mail type is Mime8Bit or Internationalized
24// and our data is utf-8 we might want to send it _without_ encoding (but
25// this means we need to check for boundaries).
26//
27// BUT we also want to cache the encoded data, sometimes more than the
28// actual data.
29//
30// Also we normally want to encode text (any ascii compatible encoding)
31// with quoted printable but BUT this is not the case for e.g. chinese
32// text in utf-8 which would be horrible to encode with Quoted Printable
33// (either no encoding or base64)
34
35/*
36      Mail  CtxUtils            Ctx Impl
37Source->|    |                     |
38        |    | load_data(&Source)  |
39        |----+-------------------->|
40       XOR   | encode_data(&Data)  |
41        |----+-------------------->|
42        |    |                     |
43        |    |  encode_data(&Data) |
44        |    |<-- Data ------------|  uses IRI to cache
45        |    |(contain content id) |  return with CID
46        |    |                     |  (for supporting caching, data needs to be Arc'ed)
47        |    |                     |
48        |    |                     |  uses the CID to cache, we can't use
49        |    |                     |  the data BUT that means it must be
50        |    |---EncData---------->|  immutable
51        |<---+-- EncData ----------|  (for supporting caching, enc data needs to be Arc'ed)
52        |    |                     |
53        |    |                     |  The encoding hint (`EncHint`) can change
54        |    |                     |  sometimes this means re-encoding, sometimes
55        |    |                     |  this means encoding what didn't need to be
56        |                          |  encoded, sometimes this means encoding, sometimes
57        |                          |  we can just get the cached data.
58        |                          |
59        |                          |  The encoding hint should also contain a batch of
60        |                          |  boundaries which can be checked "on the fly" while
61        |                          |  encoding (or checking for validity).
62        |                          |
63        |                          |  Most server support Mime8Bit and most text bodies
64        |                          |  will be utf-8 so normally we could use represent them
65        |                          |  "as is" expect that there is still a line length limit
66        |                          |  and potential "wrong" line breaks.
67        |                          |  But most mails should not break the hard line length
68        |                          |  limit and normally "wrong" line breaks can be "fixed"
69        |                          |  on the fly.
70
71    How does this integrate with into encodable mail?
72
73    - will replace all occurrences where Resources are
74      Source or Data with encoded Data while also setting
75      boundaries, content-transfer-encoded headers
76
77    The last question left open is:
78        - how to handle the case where a mail are encoded assuming mime 8bit
79          but then it's not supported? What is affected?
80          - the data needs to be encoded
81          - boundaries do not need to be changed (we generate boundaries
82            so that they cant conflict with quoted printable or base64
83            encoded data)
84          - Content Transfer Encoding header needs to be changed
85            - Content-Transfer-Encoding headers are kinda invisible
86              you must not add them to header maps, we auto add them
87              when turning it into an encodable mail and auto remove
88              it when turning it back into a mail
89
90    So what can we do?
91        - Instead of adding Content-Transfer-Encoding header just
92          "on the fly" encode a non added header here (so we can
93          also "on the fly" encode a different header).
94        - We can also "on the fly" encode non encoded data and write
95          the encoded data instead of the normal data.
96
97    So?
98        - encoding non Mime8Bit will "on the fly" take longer,
99          but it still _should be_ affordable longer and not cause
100          anything like a timeout
101
102
103  .get_boundary_pool()
104
105  .load_data(&Source, &mut BoundaryPool) -> Result
106  .encode_data(&Data, &mut BoundaryPool) -> Result
107    (both remove colliding boundaries)
108
109  data.transfer_encode(EncHint, &mut BoundaryPool) -> Result<EncData>
110
111  EncHint:
112    - don't assume Mime8Bit
113    - Use Base64
114    - Use QuotedPrintable
115    - NotHint
116*/
117use headers::header_components::ContentId;
118
119mod source;
120mod data;
121mod loading;
122
123pub use self::source::*;
124pub use self::data::*;
125pub use self::loading::*;
126
127
128
129/// A enum specifying a "resource" for a mail.
130///
131/// A resource represents any kind of actual data.
132/// It can be anything from a html body of a mail over a embedded
133/// image to a attached spread sheet.
134///
135/// A resource can be specified in 3 ways:
136/// 1. As a source specifying what to get and how to handle it.
137/// 2. Data (and Metadata) representing a resource.
138/// 3. Data (and Metadata) representing a transfer encoded resource.
139///
140/// Normally auto generated content will be provided as `Data`, embeddings
141/// and attachments will be provided as `Source` (potentially referring to
142/// a file on in a file system) and transfer encoded data can not be provided
143/// by the user.
144///
145/// When a mail is converted to a encodable mail any resource will be swapped
146/// with a version of it which is transfer encoded, so the only way a consumer
147/// of this library normally comes in contact with the third variant is by
148/// turning a encodable mail back into normal mail.
149#[derive(Debug, Clone)]
150#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
151pub enum Resource {
152    /// Provide a source which specify what data to use.
153    ///
154    /// This also allows specifying a media type (if not stored with the data
155    /// and sniffing is not wanted or to unreliable).
156    ///
157    /// Additionally it allows to specify a "file name" which will force the
158    /// given name to be used instead of inferring it from the IRI or meta data
159    /// associated with the IRI in "some way" (like file name fild in a database
160    /// from which the data will loaded).
161    Source(Source),
162
163    /// Provide the data used for the mail bodies content.
164    ///
165    /// This for example could be a png image.
166    Data(Data),
167
168    /// Provides a already transfer encoded version of the `Data` variant.
169    ///
170    /// This can not be created by a consumer of the library and will be
171    /// created when turning a mail into a transfer encoded mail.
172    EncData(EncData)
173}
174
175
176impl Resource {
177
178    /// Creates a new text `Resource` with `text/plain; charset=utf-8` media type.
179    ///
180    /// The `Context` is used to generate a `ContentId`.
181    pub fn plain_text(content: impl Into<String>, ctx: &impl Context) -> Resource {
182        Resource::Data(Data::plain_text(content, ctx.generate_content_id()))
183    }
184
185    /// Return the content id, if there is any.
186    pub fn content_id(&self) -> Option<&ContentId> {
187        match self {
188            &Resource::Source(..) => None,
189            &Resource::Data(ref data) => Some(data.content_id()),
190            &Resource::EncData(ref enc_data) => Some(enc_data.content_id())
191        }
192    }
193}