Mail::M...ge::Body(3)

πŸ’– NAME πŸ’–

Mail::Message::Body β€” the data of a body in a message πŸ’…

This one's Mail::M...ge::Body(3), from the User Contributed Perl Documentation, and bestie it's a long one, so we're doing it in parts. This is part 1. Let's get into it 🫢


🌈 INHERITANCE 🌈

okay so the whole family tree, exactly how the docs lay it out, no edits, no cap:

Mail::Message::Body has extra code in
  Mail::Message::Body::Construct
  Mail::Message::Body::Encode

Mail::Message::Body
  is a Mail::Reporter

Mail::Message::Body
  is a Mail::Reporter

Mail::Message::Body is extended by
  Mail::Message::Body::File
  Mail::Message::Body::Lines
  Mail::Message::Body::Multipart
  Mail::Message::Body::Nested
  Mail::Message::Body::String

Mail::Message::Body is realized by
  Mail::Message::Body::Delayed

πŸ¦‹ SYNOPSIS πŸ¦‹

the vibe check, in code:

 my Mail::Message $msg = ...;
 my $body  = $msg->body;
 my @text  = $body->lines;
 my $text  = $body->string;
 my $file  = $body->file;  # IO::File
 $body->print(\*FILE);

 my $content_type = $body->type;
 my $transfer_encoding = $body->transferEncoding;
 my $encoded = $body->encode(mime_type => 'text/html',
    charset => 'us-ascii', transfer_encoding => 'none');\n";
 my $decoded = $body->decoded;

🌸 DESCRIPTION 🌸

okay so here's the tea β˜•: the encoding + decoding glow-up for a Mail::Message::Body is implemented in the Mail::Message::Body::Encode package. That package gets auto-loaded the moment encoding and decoding of messages needs to happen. And the methods that just build and process body objects? Those are living in Mail::Message::Body::Construct. ✨

The body of a message (that's a Mail::Message object) gets stored in one of the many body types. Every body type has equivalent functionality β€” same energy, same job, periodt β€” but the performance differs. Each body type has its own documentation with all the deets about its implementation. πŸ‘‘

Extends "DESCRIPTION" in Mail::Reporter.

Extends "DESCRIPTION" in Mail::Reporter.


πŸ‘‘ OVERLOADED πŸ‘‘

the magic tricks, bestie β€” the things that let a body cosplay as a string, a number, an array, and an always-truthy babe πŸͺ„

  • overload: '""' stringification β€” hands you back the body as a string, which β€” heads up β€” will trigger completion. Unless it's being called to produce a string for Carp. That last bit is there to avoid deep recursions. πŸ™ˆ

Β» example: stringification of body

 print $msg->body;   # implicit by print

 my $body = $msg->body;
 my $x    = "$body"; # explicit by interpolation
  • overload: '==' numerical comparison β€” compares whether two references point to the same message. This only produces correct results if both arguments are message references within the same folder. ⚠️

Β» example: use of numeric comparison on a body

 my $skip = $folder->message(3);
 foreach my $msg (@$folder)
 {   next if $msg == $skip;
     $msg->send;
 }
  • overload: '@{}' addressed as array β€” when a body object gets used like an array reference, you get the lines of the body back. Same thing as using lines(). πŸ’«

Β» example: using a body as array

 print $body->lines->[1];  # second line
 print $body->[1];         # same

 my @lines = $body->lines;
 my @lines = @$body;       # same
  • overload: bool true/false β€” always returns a true value. That's needed so overloaded objects can be used like if($body). Otherwise you'd have to write if(defined $body) to avoid a runtime error. πŸ”₯

πŸ”₯ METHODS πŸ”₯

Extends "METHODS" in Mail::Reporter.

Extends "METHODS" in Mail::Reporter.

πŸŽ€ Constructors πŸŽ€

Extends "Constructors" in Mail::Reporter.

Extends "Constructors" in Mail::Reporter.

  • $obj->clone() β€” gives you back a copy of this body, usually so it can ride along inside a cloned message. For a whole entire message, use Mail::Message::clone(). 🫢
  • $class->new(%options) β€” BE WARNED that whatever you put in here are encodings and such that are already in place. These options will not trigger conversions. When you need conversions, first create a body with options that tell what you've got, and then call encode() for what you need. Improves base, see "Constructors" in Mail::Reporter. πŸ’…
 -Option           --Default
  based_on           undef
  charset            'PERL'
  checked            false
  content_id         undef
  data               undef
  description        undef
  disposition        undef
  eol                'NATIVE'
  file               undef
  filename           undef
  language           undef
  message            undef
  mime_type          'text/plain'
  modified           false
  transfer_encoding  'none'
  • based_on => $body β€” the info about encodings must be taken from the given $body, unless you specified something differently. 🌸
  • charset => $charset|'PERL'|<undef> β€” sets the character-set that's used in the data. Only useful when paired with a mime_type that refers to text in any shape, and that doesn't already contain an explicit charset. This field is case-insensitive.

When a known CHARSET is provided and the mime-type says text, then the data is expected to be raw octets in that particular encoding (see Encode). When 'PERL' is given, then the data is in Perl's internal encoding β€” either cp1252 or utf8. More details in "Character encoding PERL". 🧚

  • checked => BOOLEAN β€” whether the added information has been checked not to contain illegal octets with respect to the transfer encoding and mime type. If it hasn't been checked, and it then gets set as body for a message, it will be. βœ…
  • content_id => STRING β€” in multipart/related MIME content, the content_id is required so the related content can be accessed through a cid:<...> descriptor of an inline disposition.

A Content-ID is supposed to be globally unique. Because of that, it's common to append @computer.domain to the end of some unique string. And since other content in the multipart/related container also needs to know what this Content-ID is, that should be left to the imagination of the person making the content (for now). πŸ˜…

As a MIME header field, the Content-ID string is expected to sit inside angle brackets. πŸ“

  • data => \@lines|$text β€” the content of the body. The only way to set a body's content is during the creation of the body. So if you wanna modify the content of a message, you need to create a new body with the new content and add that to the body. Why tho? Because correct encodings and body information must be guaranteed. It also saves you the hassle of calculating the number of lines in the body and checking whether bad characters are enclosed in the text. 😌

Give @lines each terminated by a newline, or one $text which may contain multiple lines, separated and terminated by a newline. πŸ“

  • description => STRING|$field β€” informal info about the body content. This data relates to the Content-Description field. Give a STRING which will become the field content, or a real $field object. πŸ’¬
  • disposition => STRING|$field β€” how this message can be decomposed. This data relates to the Content-Disposition field. Give a STRING which will become the field content, or a real $field. 🌷

The content of this field is specified in RFC 1806. The body of the field can be inline, to indicate that the body is intended to be displayed automatically upon display of the message. Use attachment to indicate that it's separate from the main body of the mail message, and that its display should not be automatic, but contingent upon some further action of the user. πŸ–ΌοΈ

The filename attribute specifies a name to which is suggested to the reader of the message when it is extracted. πŸ“Ž

[3.017] RFC9078 adds type reaction. This part content is restricted to a single line of emoji's. For this kind, you need to pass the FIELD here, as well as mime-type text/plain. πŸŽ‰

  • eol => 'CR'|'LF'|'CRLF'|'NATIVE' β€” convert the message into having the specified string as line terminator for all lines in the body. NATIVE is used to represent the \n on the current platform and will be translated in the applicable one.

BE WARNED that folders with a non-native encoding may appear on your platform, for instance in Windows folders handled from a UNIX system. The eol encoding has effect on the size of the body! ⚠️

  • file => $file|$handle|$io β€” read the data from the specified $file name, file $handle, or object of type IO::Handle. πŸ“‚
  • filename => $file β€” [3.001] overrule/set $file name for content-disposition. 🏷️
  • language => STRING|\@lang|$field β€” [3.017] RFC3282 Content-Language field, containing a comma separated list of language codes. 🌍
  • message => $message β€” the $message where this body belongs to. πŸ’Œ
  • mime_type => STRING|$field|$mime β€” the type of data which is added. You may specify a content of a header line as STRING, or a $field object. You may also specify a MIME::Type object. In any case, it will be kept internally as a real field (a Mail::Message::Field object). This relates to the Content-Type header field. πŸ“‹

A mime-type specification consists of two parts: a general class (text, image, application, etc) and a specific sub-class. Examples for specific classes with text are plain, html, and xml. This field is case-insensitive but case preserving. The default mime-type is text/plain. πŸ’…

  • modified => BOOLEAN β€” whether the body is flagged modified, directly from its creation. 🚩
  • transfer_encoding => STRING|$field β€” the encoding that the data has. If the data is to be encoded, then you will have to call encode() after the body is created. That will return a new encoded body. This $field is case-insensitive and relates to the Content-Transfer-Encoding field in the header. πŸ”„

Β» example:

 my $body = Mail::Message::Body::String->new(file => \*IN,
    mime_type => 'text/html; charset="ISO-8859-1"');

 my $body = Mail::Message::Body::Lines->new(data => ["first\n", $second],
    charset => 'ISO-10646', transfer_encoding => 'none');

 my $body = Mail::Message::Body::Lines->new(data => \@lines,
    transfer_encoding => 'base64');

 my $body = Mail::Message::Body::Lines->new(file => 'picture.gif',
    mime_type => 'image/gif', content_id => '<12345@example.com>',
    disposition => 'inline');

πŸ’œ Attributes πŸ’œ

Extends "Attributes" in Mail::Reporter.

Extends "Attributes" in Mail::Reporter.

  • $obj->charsetDetectAlgorithm( [CODE|undef|METHOD] ) β€” inherited, bestie, go peep Attributes over in Mail::Message::Body::Encode 🫢

✨ Constructing a body ✨

  • $obj->attach(@messages, %options) β€” inherited, so slide over to Constructing a body in Mail::Message::Body::Construct πŸ’–
  • $obj->charsetDetect(%options) β€” inherited, same deal: Constructing a body in Mail::Message::Body::Encode ✨
  • $obj->check() β€” inherited, Constructing a body in Mail::Message::Body::Encode, go go go 🌸
  • $obj->concatenate(@components) β€” inherited, Constructing a body in Mail::Message::Body::Construct πŸŽ€
  • $obj->decoded(%options) β€” okay this one is lowkey important so listen up πŸ‘‘ it hands you back a body, like an object that's a (sub-)class of Mail::Message::Body, and that body holds a simplified, chill representation of the textual data. Now β€” plot twist β€” the object you get back might literally be the same object you called it on, or it might be a brand new body of literally any type. No promises, she's mysterious.

So this:

my $dec = $body->decoded;

is equivalent to this whole thing:

my $dec = $body->encode(
  mime_type         => 'text/plain',
  transfer_encoding => 'none',
  charset           => 'PERL',
);

The $dec you get back is a body, periodt. Wanna know what got produced? Ask the mimeType() method, she'll tell you. And heads up: this $dec body is not related to any header. She's independent, she's free πŸ’…

Options, bestie:

  • -Option β€” --Default
    • result_type β€” <same as current>
  • result_type => CLASS
  • $obj->encode(%options) β€” inherited, Constructing a body in Mail::Message::Body::Encode πŸ¦‹
  • $obj->encoded(%options) β€” inherited, Constructing a body in Mail::Message::Body::Encode ⭐
  • $obj->eol( ['CR'|'LF'|'CRLF'|'NATIVE'] ) β€” inherited, Constructing a body in Mail::Message::Body::Encode 🌈
  • $obj->foreachLine(CODE) β€” inherited, Constructing a body in Mail::Message::Body::Construct πŸ’œ
  • $obj->stripSignature(%options) β€” inherited, Constructing a body in Mail::Message::Body::Construct 🧚
  • $obj->unify($body) β€” inherited, Constructing a body in Mail::Message::Body::Encode ✨

πŸ’– The body πŸ’–

  • $obj->isDelayed() β€” gives you back a true or false, depending on whether this body has actually been read from file yet. Fun fact: this can only ever be false for a Mail::Message::Body::Delayed. Iconic behaviour honestly.
  • $obj->isMultipart() β€” tells you whether this message-body has parts inside it that are messages all on their own πŸŽ€
  • $obj->isNested() β€” only ever true for a body that contains exactly one sub-message, aka the Mail::Message::Body::Nested body type 🌸
  • $obj->message( [$message] ) β€” returns the message (or message part) this body belongs to. You can optionally hand it a new $message first and it'll switch to that one. And if you pass undef? The body gets fully disconnected from the message, it's giving ✨independent era✨
  • $obj->partNumberOf($part) β€” returns a string for multiparts and nested bodies, and otherwise it's an error, sorry bestie 😭 It's what Mail::Message::partNumber() uses under the hood.

🫢 About the payload 🫢

  • $obj->charset() β€” returns the character set used in the text body, as a string. This is part of whatever the type method hands back πŸ’…
  • $obj->checked( [BOOLEAN] ) β€” tells you whether the body encoding has been checked or not. Pass a value and you can set that flag to a new one while you're at it 🌟
  • $obj->contentId( [STRING|$field] ) β€” returns (or, if you pass something, sets) the id β€” that's the unique reference β€” of a message part. The header field it's chillin' with is Content-ID. You get back a Mail::Message::Field object (which stringifies into the field content, cute). If no disposition was specified, the field content will be none. The argument you pass can be a STRING (which gets converted into a field) or a fully prepared header $field πŸŽ€
  • $obj->description( [STRING|$field] ) β€” returns (or sets) the informal description of the body content. Its header field bestie is Content-Description. You get back a Mail::Message::Field object (stringifies into the field content). Field content is none if no disposition was specified. Argument can be a STRING (converted into a field) or a fully prepared header field πŸ’–
  • $obj->disposition( [STRING|$field] ) β€” returns (or sets) how the message can be disposed of (aka unpacked). The related header field is Content-Disposition. Back comes a Mail::Message::Field object (stringifying into the field content). The field content will be none if no disposition was specified. Argument: a STRING (converted into a field) or a fully prepared header field ✨
[3.017] heads up: RFC9078 adds the type reaction. That part's content is restricted to a single line of emoji's. Yes really. The manpage said emoji's and I'm not about to edit her πŸ’€
  • $obj->dispositionFilename( [$directory] ) β€” inherited, see About the payload in Mail::Message::Body::Encode πŸ¦‹
  • $obj->isBinary() β€” inherited, About the payload in Mail::Message::Body::Encode 🌈
  • $obj->isText() β€” inherited, About the payload in Mail::Message::Body::Encode ⭐
  • $obj->language( [@langs|\@langs|$langs|$field] ) β€” [3.017] returns (or sets) the Content-Language header, exactly as specified in RFC3282. You get the field back with a comma separated list of languages as its body πŸ’œ
  • $obj->mimeType() β€” returns a MIME::Type object that's related to this body's type. This is different from the type method, which gives you a Mail::Message::Field instead. Know the difference, bestie πŸ‘‘

Β» example:

if($body->mimeType eq 'text/html') {...}
print $body->mimeType->simplified;
  • $obj->nrLines() β€” returns the number of lines in the message body. For multi-part messages, this counts the header lines and the boundaries of all the parts too 🌸
  • $obj->size() β€” the total number of bytes in the message body. And here's the thing: the size is computed in whatever shape the body is currently in. So like, if this is a base64 encoded message, you get the size of the encoded data back. You probably wanna call Mail::Message::decoded() first πŸ™ˆ
  • $obj->transferEncoding( [STRING|$field] ) β€” returns the transfer-encoding of the data inside this body as a Mail::Message::Field (which stringifies to its content). If it needs changing, call the encode() or encoded() method instead. When there's no encoding present, the field contains the text none. The optional STRING or $field forces a new encoding to be set β€” without doing the actual required translations, so watch out 🫢

Β» example:

my $transfer = $msg->decoded->transferEncoding;
$transfer->print;   # --> Content-Encoding: base64
print $transfer;    # --> base64

if($msg->body->transferEncoding eq 'none') {...}
  • $obj->type( [STRING|$field] ) β€” returns the type of information the body contains, as a Mail::Message::Field object. The type is taken from the Content-Type header field. And if the header didn't have that field at all? You get a default field containing text/plain, she's got your back πŸ’…

ngl though, you can usually do better with mimeType(), because that one returns a clever object with actual type information in it. Just saying.

Β» example:

my $msg     = $folder->message(6);
$msg->get('Content-Type')->print;
   # --> Content-Type: text/plain; charset="us-ascii"

my $content = $msg->decoded;
my $type    = $content->type;

print "This is a $type message\n";
   # --> This is a text/plain; charset="us-ascii" message

print "This is a ", $type->body, "message\n";
   # --> This is a text/plain message

print "Comment: ", $type->comment, "\n";
   # --> Comment: charset="us-ascii"

🌈 Access to the payload 🌈

  • $obj->endsOnNewline() β€” tells you whether the last line of the body is terminated by a new-line (in transport it'll become a CRLF). And an empty body returns true as well, because the newline comes from the line before it. Iconic loophole ✨
  • $obj->file() β€” returns the content of the body as a file handle. That stream might be a real file, or a simulated file in any form Perl supports. You might not be able to write to the file handle, but you can absolutely read from it πŸŽ€
WARNING: even if the file handle does support writing, do NOT write to the file handle. If you do, some of the internal values of the Mail::Message::Body may not get updated. And then everything is cursed. Don't do it bestie 😭
  • $obj->lines() β€” returns the content of the body as a list of lines (in LIST context) or as a reference to an array of lines (in SCALAR context). And in scalar context, that array of lines gets cached so you avoid needless copying, which means wayyy faster access for large messages πŸ”₯

If you just want the number of lines, use the nrLines() method instead β€” it's usually much more efficient πŸ‘‘

BE WARNED: for some types of bodies, that reference will point at the original data. You must NOT change the referenced data! If you do, some of the essential internal variables of the Mail::Message::Body may not be updated. Respect the reference, she's sensitive πŸ’€

Β» example:

my @lines    = $body->lines;     # copies lines
my $line3    = ($body->lines)[3] # only one copy
print $lines[0];

my $linesref = $body->lines;     # reference to originals
my $line3    = $body->lines->[3] # only one copy (faster)
print $linesref->[0];

print $body->[0];                # by overloading
  • $obj->print( [$fh] ) β€” prints the body to the specified $fh (defaults to whatever handle is currently selected). An IO::File object works, or... honestly any object with a print() method will do. Nothing useful comes back, she's not about the returns πŸ¦‹
  • $obj->printEscapedFrom($fh) β€” prints the body to the specified $fh, BUT every line that starts with 'From ' (optionally already preceded by >'s) will habe an > added in front. Yes, "habe". That's what the page says, character for character, I'm not touching it πŸ’… Nothing useful is returned.
  • $obj->string() β€” returns the content of the body as a scalar (a single string). And heads up: this is a copy of the internally kept information 🫢

Β» example:

my $text = $body->string;
print "Body: $body\n";     # by overloading
  • $obj->stripTrailingNewline() β€” removes the newline from the last line. Or, if that last line contains nothing other than a newline, it removes the whole line 🌸
  • $obj->write(%options) β€” writes the content of the body to a file. But be warned, bestie: you may want to decode the body before writing it! 😭
    • -Option β€” --Default
      • filename β€” <required>
    • filename => FILENAME

Β» example: write the data to a file

use File::Temp;
my $fn = tempfile;
$message->decoded->write(filename => $fn);

Β» example: using the content-disposition information to write

use File::Temp;
my $dir = tempdir; mkdir $dir or die;
my $fn  = $message->body->dispositionFilename($dir);
$message->decoded->write(filename => $fn);

Internals 🌸

  • $any->addTransferEncHandler( $name, <$class|$object> ) β€” inherited bestie, go peek at "Internals" in Mail::Message::Body::Encode for the full tea πŸ’…
  • $obj->contentInfoFrom($head) β€” moves all the body-related info out of the header and into this body. Like, transferring the vibe over 🫢
  • $obj->contentInfoTo($head) β€” copies the content information (aka the Content-* fields) into the $head you point at. Use this when the body got built out of raw data and is missing the info it needs, so you gotta add it. Also check contentInfoFrom(), they're a little duo ✨
  • $obj->fileLocation( [$begin, $end] ) β€” where the body lives inside the file. Hands you back a list with begin and end. The begin is the offset of the first byte of the folder used for this body, and end is the offset of the first byte of the next message. πŸ“
  • $obj->getTransferEncHandler($type) β€” inherited, see "Internals" in Mail::Message::Body::Encode πŸ’–
  • $obj->isModified() β€” tells you whether the body has changed at all. Simple as that, queen πŸ‘‘
  • $obj->load() β€” makes sure the body is actually loaded, and returns the loaded body. No more lazy vibes ✨
  • $obj->modified( [BOOLEAN] ) β€” flips the body's modification flag. Doing this forces a re-write of the body into the folder file when it gets closed. And ngl, changing the body is pretty dangerous: the very same body might be shared between multiple messages inside your program, so you're messing with more than you think 😭
Especially be warned: you have to change the message-id when you change the body of the message, because no two messages should ever have the same id. Periodt. πŸ’€

Call it with no value and you get the current setting back β€” though honestly you're better off just using isModified().

  • $obj->moveLocation( [$distance] ) β€” moves the registration of the message over $distance to a new location. This gets called when the message is written out to a new version of the same folder-file. πŸ¦‹
  • $obj->read( $parser, $head, $bodytype, [$chars, [$lines]] ) β€” reads the body out of the file using $parser. Heads up: the actual implementation of this method is different for every type of body. The $bodytype argument is either a class name or a code reference to a routine that can produce a class name, and multipart bodies use it to figure out the type of body for each individual part. 🌈

The $chars argument is the estimated number of bytes in the body, or undef when nobody knows. Sometimes you can derive that from the header (the Content-Length line) or from the file-size.

The second argument is the estimated number of $lines in the body. It's less useful than $chars, but it can help you decide whether the message separator is trustworthy. You might find this value in the Lines field of the header. πŸ‘‘


Error handling πŸ’…

Extends "Error handling" in Mail::Reporter.

Extends "Error handling" in Mail::Reporter.

(yes bestie, it really does say it twice, I'm not playing πŸ™ˆ)

  • $obj->AUTOLOAD() β€” when an unknown method gets called on a message body object, it's not automatically a problem. For performance reasons, some methods live in separate files and only get demand-loaded. But if that delayed compilation of the extra modules doesn't save the day, then an error gets produced. Improves base, see "Error handling" in Mail::Reporter ✨
  • $obj->notImplemented() β€” inherited, see "Error handling" in Mail::Reporter πŸŽ€
  • $obj->notImplemented() β€” inherited, see "Error handling" in Mail::Reporter πŸŽ€

Cleanup 🧚

Extends "Cleanup" in Mail::Reporter.

Extends "Cleanup" in Mail::Reporter.

  • $obj->DESTROY() β€” inherited, see "Cleanup" in Mail::Reporter πŸ’œ
  • $obj->DESTROY() β€” inherited, see "Cleanup" in Mail::Reporter πŸ’œ

✨ DETAILS ✨

Access to the body πŸ¦‹

A body can be chilling inside a message, but it can also live completely on its own. Either way it's storing data, and either way you can ask the same questions: what type of data is it, how many bytes and lines does it have, what encoding is being used? Any body can be encoded and decoded, and both of those hand you back a new body object. But here's the thing β€” bodies that are part of a message will always be in a shape where they can be written to a file or sent somewhere: they'll be encoded if that's what's needed. No cap. πŸ’–

Β» Example

my $body    = Mail::Message::Body::String->new(mime_type => 'image/gif');
$body->print(\*OUT);    # this is binary image data...

my $encoded = $message->body($body);
$encoded->print(\*OUT); # ascii data, encoded image

So now encoded refers to the body of the $message, which is the content of $body in a shape that it can actually be transmitted in. Usually base64 encoding is what's going on here. ✨


Body class implementation πŸ‘‘

The body of a message can be stored in so many ways. Roughly speaking, the implementations split into two groups: the data collectors and the complex bodies. The former give you various ways to access data, and they're fully compatible with each other β€” they only differ in performance and memory footprint under different circumstances. The latter were created to handle complex multiparts and lazy extraction. 🌸

Data collector bodies πŸ“

  • Mail::Message::Body::String β€” the whole message body is stored in one single scalar. Small messages can live like this with zero performance penalties. iconic πŸ’…
  • Mail::Message::Body::Lines β€” every line of the message body is stored as its own single scalar. Super useful when you want a detailed look into the message body, which is usually organized line by line. πŸ“
  • Mail::Message::Body::File β€” the message body is stored in an external temporary file. This storage type is especially useful when the body is large, the whole folder is large, or memory is limited. πŸ—‚οΈ
  • Mail::Message::Body::InFolder β€” NOT IMPLEMENTED YET. The message is kept in the folder, and is only taken out when the content changes. πŸ™ˆ
  • Mail::Message::Body::External β€” NOT IMPLEMENTED YET. The message is kept in a separate file, usually because the message body is large. The difference from the ::External object is that this external storage stays that way between closing and opening of a folder. The ::External object only uses a file while the folder is open. πŸ’€

Complex bodies 🌈

  • Mail::Message::Body::Delayed β€” the message-body hasn't been read yet, but the exact location of the body is known, so it can be read whenever it's needed. This is part of the lazy extraction mechanism. Once it gets extracted, the object can become any simple or complex body. 😴
  • Mail::Message::Body::Multipart β€” the message body contains a set of sub-messages (which can themselves contain multipart bodies, it goes deep). Each sub-message is an instance of Mail::Message::Part, which is an extension of Mail::Message. 🧩
  • Mail::Message::Body::Nested β€” nested messages, like message/rfc822: they contain a message inside the body. For most code they just behave like multiparts. πŸͺ†

Character encoding PERL πŸ’œ

A body object can be part of a message, or stand-alone. When it's part of a message, the "transport encoding" and the content have to be in a shape where the data can be transported via SMTP.

But when you want to process the body data in plain Perl (or when you build body data out of normal Perl strings), you need to be aware of Perl's internal representation of strings. That can either be cp1252 (extended latin1) or utf8 (not real UTF-8, but something similar β€” go read the perlunicode manual page for the details, iykyk). So before you start using data from an incoming message, do this:

my $body  = $msg->decoded;
my @lines = $body->lines;

Now the body has character-set 'PERL' (when it's text, obviously).

When you create a new body that contains text content (which is the default), it gets created with character-set 'PERL' unless you explicitly specify a character-set.

my $body = Mail::Box::Body::Lines->new(data => \@lines);
# now mime=text/plain, charset=PERL

my $msg  = Mail::Message->buildFromBody($body);
$msg->body($body);
$msg->attach($body);   # etc
# these all will convert the charset=PERL into real utf-8,
# cp1252 or us-ascii, which depends on the characters found.

Autodetection of character-set πŸ”

This Body object represents data as part of an existing message, or as something about to become part of a message. The body can be in two states:

  1. ready to be processed textually, using Perl's string operations
  2. raw bytes read or to be written

In the first case, the body content has no transfer encoding on it (none), and the character-set is PERL. In the second version, the body may have transfer encoding, and it has an (IANA listed) charset on it, which defaults to us-ascii.

Using encode() (maybe via decoded()), you can convert bodies from one state into a different one. And in one single go, you can change the transfer-encoding, the character-set, or whether it's in PERL string format or raw (in bytes). Slay. ✨

[3.013] A serious problem pops up when a conversion is needed, while the input or output character-set is not explicitly known. The email RFCs state that the default is us-ascii. However β€” plot twist β€” in the real world it can be literally anything. So in those situations, autodetection kicks in. 😭
  1. When a Body is read (using Mail::Message::read() and friends), the character-set may stay undefined until transfer-decoding has been applied. At that moment, (configurable auto-detection) is applied. πŸŽ€
  2. When a Body is created within the program, without a specific character-set, it will use 'PERL'. πŸ¦‹
  3. When a Body is written, the requested character-set is not specified, and the current character-set is PERL, then auto-detection is used. This may result in us-ascii, cp1252 and utf-8. 🌈
  4. In all other cases, the character-set is known, so it's "easy". πŸ’…

🚨 DIAGNOSTICS 🚨

okay bestie, this is the part where the module yells at you. every single one of these is a real message it can throw, so read them like they're texts from a friend who's mildly stressed πŸ’€

  • Warning: Content-Transfer-Encoding '$te' requires explicit charset, defaulted to utf-8. Cast by encode()
  • Error: cannot concatenate element $which Cast by concatenate()
  • Fault: cannot open $file to write body: $! Cast by write()
  • Warning: charset '$name' is not known. So like, the encoding or decoding of a message body ran into a character set that Perl's Encode module simply does not understand. rip. Cast by encode()
  • Error: class $package does not implement method $method. Fatal error: the specific $package (or one of its superclasses) does not implement this method where it should. This message means that some other related classes do implement this method however the class at hand does not. Probably you should investigate this and probably inform the author of the package. Cast by notImplemented()
  • Error: class $package does not implement method $method. Fatal error: the specific $package (or one of its superclasses) does not implement this method where it should. This message means that some other related classes do implement this method however the class at hand does not. Probably you should investigate this and probably inform the author of the package. Cast by notImplemented()
no fr, that one is listed twice in the original and we are keeping it twice. the manpage said it twice, so we say it twice. consistency is the vibe ✨
  • Fault: error closing $file after write body: $! Cast by write()
  • Error: message body: illegal datatype '$type' for data option. Cast by new()
  • Error: message body: illegal datatype '$type' for file option. Cast by new()
  • Warning: no decoder defined for transfer encoding '$encoding'. The data (message body) is encoded in a way which is not currently understood, therefore no decoding (or recoding) can take place. Cast by encode()
  • Warning: no encoder defined for transfer encoding '$encoding'. The data (message body) has been decoded, but the required encoding is unknown. The decoded data is returned. Cast by encode()
  • Error: no filename parameter for write() body. Cast by write()
  • Error: part number needs multi-part or nested. Cast by partNumberOf()
  • Error: unknown line terminator '$eol'. Cast by eol()

πŸ‘€ SEE ALSO πŸ‘€

This module is part of Mail-Message version 4.04, built on February 04, 2026. Website: http://perl.overmeer.net/CPAN/


πŸ“œ LICENSE πŸ“œ

For contributors see file ChangeLog.

This software is copyright (c) 2001-2026 by Mark Overmeer.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.


and that's a wrap on the whole page, bestie!! πŸŽ€ the source you just read is Mail-Message version 4.04, built February 04, 2026, and this particular rendering came off perl v5.42.0 dated 2026-02-05 β€” that footer's the whole receipts section, so consider yourself receipted πŸ’… go forth and encode responsibly, mwah πŸ«ΆπŸ’–

πŸ“¦ where this page comes from