@@ -112,7 +112,84 @@ fn main() {
112112 } ,
113113 OriginalMessageBody :: Error ( _) => None ,
114114 } ;
115- tracing:: debug!( could_parse = original_parsed. is_some( ) , "parsed message" ) ;
115+ tracing:: debug!(
116+ could_parse = original_parsed. is_some( ) ,
117+ ?original_parsed,
118+ "parsed message"
119+ ) ;
120+
121+ // Try to create an inline attachment for the receivers's convenience of not
122+ // having to double-click the attachment.
123+ //
124+ // This is surprisingly tricky, as the message/rfc822 MIME type only allows
125+ // Content-Transfer-Encoding 7bit, 8bit or binary.
126+ // Any other encoding (quoted-printable, base64) will break in
127+ // Gmail and AppleMail, probably elsewhere. The exact kind of breakage depends:
128+ // in AppleMail, only the `From`, `To`, and `Subject`
129+ // headers are shown inline, and the rest of the message is not visible / accessible.
130+ // In Gmail, it always shows as an attachment and one gets an error when clicking on it.
131+ let re_encoded = ( || {
132+ let Some ( original_parsed) = & original_parsed else {
133+ debug ! ( "not parseable" ) ;
134+ return None ;
135+ } ;
136+ if original_parsed. ctype . mimetype != "text/plain" {
137+ // TODO: implement support.
138+ // Multi-part would be tricky as we'd possible need to use different
139+ // boundaries to avoid collisions with the boundaries that our wrapper
140+ // message will add.
141+ debug ! ( "not text/plain content-type" ) ;
142+ return None ;
143+ }
144+ let mut builder = SinglePart :: builder ( ) ;
145+ for header in & original_parsed. headers {
146+ #[ derive( Clone ) ]
147+ struct RawHeader ( HeaderName , String ) ;
148+ impl lettre:: message:: header:: Header for RawHeader {
149+ fn name ( ) -> HeaderName {
150+ unimplemented ! ( "not needed, we only use display" )
151+ }
152+
153+ fn parse ( _: & str ) -> Result < Self , Box < dyn std:: error:: Error + Send + Sync > > {
154+ unimplemented ! ( "not needed, we only use display" )
155+ }
156+
157+ fn display ( & self ) -> lettre:: message:: header:: HeaderValue {
158+ HeaderValue :: new ( self . 0 . clone ( ) , self . 1 . clone ( ) )
159+ }
160+ }
161+ impl RawHeader {
162+ fn new ( hdr : & mailparse:: MailHeader ) -> Option < Self > {
163+ let header_name =
164+ HeaderName :: new_from_ascii ( hdr. get_key ( ) ) . ok ( ) . or_else ( || {
165+ debug ! ( hdr=?hdr. get_key( ) , "header is not ascii" ) ;
166+ None
167+ } ) ?;
168+ let header_value = hdr. get_value_utf8 ( ) . ok ( ) . or_else ( || {
169+ debug ! ( hdr=?hdr, "header value is not utf-8" ) ;
170+ None
171+ } ) ?;
172+ Some ( Self ( header_name, header_value) )
173+ }
174+ }
175+ builder = builder. header ( RawHeader :: new ( header) . or_else ( || {
176+ debug ! ( "can't adapt libraries into each other" ) ;
177+ None
178+ } ) ?) ;
179+ }
180+ Some (
181+ builder. body (
182+ Body :: new_with_encoding (
183+ original_parsed. get_body ( ) . ok ( ) . or_else ( || {
184+ debug ! ( "cannot get body" ) ;
185+ None
186+ } ) ?,
187+ lettre:: message:: header:: ContentTransferEncoding :: Base64 ,
188+ )
189+ . unwrap ( ) ,
190+ ) ,
191+ )
192+ } ) ( ) ;
116193
117194 // Put together the wrapper message
118195 let sender = {
@@ -209,10 +286,14 @@ fn main() {
209286 writeln ! ( & mut body, "WARNING: could not determine permissions of the config file, they may or may not be too lax: {e}" ) ?;
210287 } ,
211288 }
212- writeln ! (
213- & mut body,
214- "The original message is attached inline to this wrapper message."
215- ) ?;
289+ writeln ! ( & mut body) ?;
290+ {
291+ write ! ( & mut body, "The original message is attached to this wrapper message." ) ?;
292+ if re_encoded. is_some ( ) {
293+ write ! ( & mut body, " For convenience, a re-encoded copy is attached inline." ) ?;
294+ }
295+ writeln ! ( & mut body) ?;
296+ }
216297 writeln ! ( & mut body) ?;
217298 writeln ! ( & mut body, "Invocation args: {args}" ) ?;
218299 writeln ! ( & mut body) ?;
@@ -261,83 +342,7 @@ fn main() {
261342 . multipart ( {
262343 let mut mp_builder = MultiPart :: mixed ( ) . singlepart ( SinglePart :: plain ( body) ) ;
263344
264- // Try to create an inline attachment for the receivers's convenience of not
265- // having to double-click the attachment.
266- //
267- // This is surprisingly tricky, as the message/rfc822 MIME type only allows
268- // Content-Transfer-Encoding 7bit, 8bit or binary.
269- // Any other encoding (quoted-printable, base64) will break in
270- // Gmail and AppleMail, probably elsewhere. The exact kind of breakage depends:
271- // in AppleMail, only the `From`, `To`, and `Subject`
272- // headers are shown inline, and the rest of the message is not visible / accessible.
273- // In Gmail, it always shows as an attachment and one gets an error when clicking on it.
274- //
275- // So, try to re-encode the message body. If that doesn't work, the user can fallback
276- // to the attachment.
277345 mp_builder = {
278- let re_encoded = ( || {
279- let Some ( original_parsed) = original_parsed else {
280- debug ! ( "not parseable" ) ;
281- return None ;
282- } ;
283- if original_parsed. ctype . mimetype != "text/plain" {
284- debug ! ( "not text/plain content-type" ) ;
285- return None ;
286- }
287- let mut builder = SinglePart :: builder ( ) ;
288- for header in & original_parsed. headers {
289- #[ derive( Clone ) ]
290- struct RawHeader ( HeaderName , String ) ;
291- impl lettre:: message:: header:: Header for RawHeader {
292- fn name ( ) -> HeaderName {
293- unimplemented ! ( "not needed, we only use display" )
294- }
295-
296- fn parse (
297- _: & str ,
298- ) -> Result < Self , Box < dyn std:: error:: Error + Send + Sync > >
299- {
300- unimplemented ! ( "not needed, we only use display" )
301- }
302-
303- fn display ( & self ) -> lettre:: message:: header:: HeaderValue {
304- HeaderValue :: new ( self . 0 . clone ( ) , self . 1 . clone ( ) )
305- }
306- }
307- impl RawHeader {
308- fn new ( hdr : & mailparse:: MailHeader ) -> Option < Self > {
309- let header_name = HeaderName :: new_from_ascii ( hdr. get_key ( ) )
310- . ok ( )
311- . or_else ( || {
312- debug ! ( hdr=?hdr. get_key( ) , "header is not ascii" ) ;
313- None
314- } ) ?;
315- let header_value = hdr. get_value_utf8 ( ) . ok ( ) . or_else ( || {
316- debug ! ( hdr=?hdr, "header value is not utf-8" ) ;
317- None
318- } ) ?;
319- Some ( Self ( header_name, header_value) )
320- }
321- }
322- builder = builder. header ( RawHeader :: new ( header) . or_else ( || {
323- debug ! ( "can't adapt libraries into each other" ) ;
324- None
325- } ) ?) ;
326- }
327- Some (
328- builder. body (
329- Body :: new_with_encoding (
330- original_parsed. get_body ( ) . ok ( ) . or_else ( || {
331- debug ! ( "cannot get body" ) ;
332- None
333- } ) ?,
334- lettre:: message:: header:: ContentTransferEncoding :: Base64 ,
335- )
336- . unwrap ( ) ,
337- ) ,
338- )
339- } ) ( ) ;
340-
341346 if let Some ( re_encoded) = re_encoded {
342347 mp_builder. singlepart (
343348 SinglePart :: builder ( )
0 commit comments