1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
// a module level circ. dep. but fine as only
// used for more ergonomic helper constructors
use ::context::Context;

#[cfg(feature="serde")]
use serde::{Serialize, Deserialize};

// We can provide a resource through two ways
// 1. providing a Source (iri + media_type_override + name override)
// 2. providing the actual data
// 3. we could provide transfer encoded resources, normally we do not

// We normally use a resource only in a transfer encoded way.
//
// - So if we get a source we have to load the actual data.
// - If we get the actual data we still have to transfer encode it.
//

// We normally do not need to keep the actual data once we did the transfer
// encoding.
//
// BUT the best chosen encoding might depend on the actual case mainly
// the mail type e.g. if the mail type is Mime8Bit or Internationalized
// and our data is utf-8 we might want to send it _without_ encoding (but
// this means we need to check for boundaries).
//
// BUT we also want to cache the encoded data, sometimes more than the
// actual data.
//
// Also we normally want to encode text (any ascii compatible encoding)
// with quoted printable but BUT this is not the case for e.g. chinese
// text in utf-8 which would be horrible to encode with Quoted Printable
// (either no encoding or base64)

/*
      Mail  CtxUtils            Ctx Impl
Source->|    |                     |
        |    | load_data(&Source)  |
        |----+-------------------->|
       XOR   | encode_data(&Data)  |
        |----+-------------------->|
        |    |                     |
        |    |  encode_data(&Data) |
        |    |<-- Data ------------|  uses IRI to cache
        |    |(contain content id) |  return with CID
        |    |                     |  (for supporting caching, data needs to be Arc'ed)
        |    |                     |
        |    |                     |  uses the CID to cache, we can't use
        |    |                     |  the data BUT that means it must be
        |    |---EncData---------->|  immutable
        |<---+-- EncData ----------|  (for supporting caching, enc data needs to be Arc'ed)
        |    |                     |
        |    |                     |  The encoding hint (`EncHint`) can change
        |    |                     |  sometimes this means re-encoding, sometimes
        |    |                     |  this means encoding what didn't need to be
        |                          |  encoded, sometimes this means encoding, sometimes
        |                          |  we can just get the cached data.
        |                          |
        |                          |  The encoding hint should also contain a batch of
        |                          |  boundaries which can be checked "on the fly" while
        |                          |  encoding (or checking for validity).
        |                          |
        |                          |  Most server support Mime8Bit and most text bodies
        |                          |  will be utf-8 so normally we could use represent them
        |                          |  "as is" expect that there is still a line length limit
        |                          |  and potential "wrong" line breaks.
        |                          |  But most mails should not break the hard line length
        |                          |  limit and normally "wrong" line breaks can be "fixed"
        |                          |  on the fly.

    How does this integrate with into encodable mail?

    - will replace all occurrences where Resources are
      Source or Data with encoded Data while also setting
      boundaries, content-transfer-encoded headers

    The last question left open is:
        - how to handle the case where a mail are encoded assuming mime 8bit
          but then it's not supported? What is affected?
          - the data needs to be encoded
          - boundaries do not need to be changed (we generate boundaries
            so that they cant conflict with quoted printable or base64
            encoded data)
          - Content Transfer Encoding header needs to be changed
            - Content-Transfer-Encoding headers are kinda invisible
              you must not add them to header maps, we auto add them
              when turning it into an encodable mail and auto remove
              it when turning it back into a mail

    So what can we do?
        - Instead of adding Content-Transfer-Encoding header just
          "on the fly" encode a non added header here (so we can
          also "on the fly" encode a different header).
        - We can also "on the fly" encode non encoded data and write
          the encoded data instead of the normal data.

    So?
        - encoding non Mime8Bit will "on the fly" take longer,
          but it still _should be_ affordable longer and not cause
          anything like a timeout


  .get_boundary_pool()

  .load_data(&Source, &mut BoundaryPool) -> Result
  .encode_data(&Data, &mut BoundaryPool) -> Result
    (both remove colliding boundaries)

  data.transfer_encode(EncHint, &mut BoundaryPool) -> Result<EncData>

  EncHint:
    - don't assume Mime8Bit
    - Use Base64
    - Use QuotedPrintable
    - NotHint
*/
use headers::header_components::ContentId;

mod source;
mod data;
mod loading;

pub use self::source::*;
pub use self::data::*;
pub use self::loading::*;



/// A enum specifying a "resource" for a mail.
///
/// A resource represents any kind of actual data.
/// It can be anything from a html body of a mail over a embedded
/// image to a attached spread sheet.
///
/// A resource can be specified in 3 ways:
/// 1. As a source specifying what to get and how to handle it.
/// 2. Data (and Metadata) representing a resource.
/// 3. Data (and Metadata) representing a transfer encoded resource.
///
/// Normally auto generated content will be provided as `Data`, embeddings
/// and attachments will be provided as `Source` (potentially referring to
/// a file on in a file system) and transfer encoded data can not be provided
/// by the user.
///
/// When a mail is converted to a encodable mail any resource will be swapped
/// with a version of it which is transfer encoded, so the only way a consumer
/// of this library normally comes in contact with the third variant is by
/// turning a encodable mail back into normal mail.
#[derive(Debug, Clone)]
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
pub enum Resource {
    /// Provide a source which specify what data to use.
    ///
    /// This also allows specifying a media type (if not stored with the data
    /// and sniffing is not wanted or to unreliable).
    ///
    /// Additionally it allows to specify a "file name" which will force the
    /// given name to be used instead of inferring it from the IRI or meta data
    /// associated with the IRI in "some way" (like file name fild in a database
    /// from which the data will loaded).
    Source(Source),

    /// Provide the data used for the mail bodies content.
    ///
    /// This for example could be a png image.
    Data(Data),

    /// Provides a already transfer encoded version of the `Data` variant.
    ///
    /// This can not be created by a consumer of the library and will be
    /// created when turning a mail into a transfer encoded mail.
    EncData(EncData)
}


impl Resource {

    /// Creates a new text `Resource` with `text/plain; charset=utf-8` media type.
    ///
    /// The `Context` is used to generate a `ContentId`.
    pub fn plain_text(content: impl Into<String>, ctx: &impl Context) -> Resource {
        Resource::Data(Data::plain_text(content, ctx.generate_content_id()))
    }

    /// Return the content id, if there is any.
    pub fn content_id(&self) -> Option<&ContentId> {
        match self {
            &Resource::Source(..) => None,
            &Resource::Data(ref data) => Some(data.content_id()),
            &Resource::EncData(ref enc_data) => Some(enc_data.content_id())
        }
    }
}