ZeeGee Software

MIME-tools

This module is BETA code, which means that the interfaces are fairly stable BUT it has not been out in the community long enough to guarantea $self->interface(ENTITY_CLASS => 'MIME::MyEntity'); $self->interface(HEAD_CLASS => 'MIME::MyHead'); $self; ### return }

With no VALUE, returns the VALUE currently associated with that ROLE.

new_body_for HEAD
Instance method. Based on the HEAD of a part we are parsing, return a new body object (any desirable subclass of MIME::Body) for receiving that part's data.

If you set the output_to_core option to false before parsing (the default), then we call output_path() and create a new MIME::Body::File on that filename.

If you set the output_to_core option to true before parsing, then you get a MIME::Body::InCore instead.

If you want the parser to do something else entirely, you can override this method in a subclass.

new_tmpfile [RECYCLE]
Instance method. Return an IO handle to be used to hold temporary data during a parse. The default uses the standard IO::File->new_tmpfile() method unless tmp_to_core() dictates otherwise, but you can override this. You shouldn't need to.

If you do override this, make certain that the object you return is set for binmode(), and is able to handle the following methods:

    read(BUF, NBYTES)
    getline()
    getlines()
    print(@ARGS)
    flush() 
    seek(0, 0)

Fatal exception if the stream could not be established.

If RECYCLE is given, it is an object returned by a previous invocation of this method; to recycle it, this method must effectively rewind and truncate it, and return the same object. If you don't want to support recycling, just ignore it and always return a new object.


Top Parse results and error recovery

last_error
Instance method. Return the error (if any) that we ignored in the last parse.

last_head
Instance method. Return the top-level MIME header of the last stream we attempted to parse. This is useful for replying to people who sent us bad MIME messages.
    ### Parse an input stream:
    eval { $entity = $parser->parse(\*STDIN) };
    if (!$entity) {    ### parse failed!
	my $decapitated = $parser->last_head;  
	...
    }

results
Instance method. Return an object containing lots of info from the last entity parsed. This will be an instance of class MIME::Parser::Results.


Top OPTIMIZING YOUR PARSER


Top Maximizing speed

Optimum input mechanisms:

    parse()                    YES (if you give it a globref or a 
				    subclass of IO::File)
    parse_open()               YES
    parse_data()               NO  (see below)
    parse_two()                NO  (see below)

Optimum settings:

    decode_headers()           *** (no real difference; 0 is slightly faster)
    extract_nested_messages()  0   (may be slightly faster, but in 
                                    general you want it set to 1)
    output_to_core()           0   (will be MUCH faster)
    tmp_recycling()            1?  (probably, but should be investigated)
    tmp_to_core()              0   (will be MUCH faster)
    use_inner_files()          0   (if tmp_to_core() is 0; 
				    use 1 otherwise)

File I/O is much faster than in-core I/O. Although it seems like slurping a message into core and processing it in-core should be faster... it isn't. Reason: Perl's filehandle-based I/O translates directly into native operating-system calls, whereas the in-core I/O is implemented in Perl.

Inner files are slower than real tmpfiles, but faster than in-core ones. If speed is your concern, that's why you should set use_inner_files(true) if you set tmp_to_core(true): so that we can bypass the slow in-core tmpfiles if the input stream permits.

Native I/O is much faster than object-oriented I/O. It's much faster to use <$foo> than $foo->getline. For backwards compatibilty, this module must continue to use object-oriented I/O in most places, but if you use parse() with a "real" filehandle (string, globref, or subclass of IO::File) then MIME::Parser is able to perform some crucial optimizations.

The parse_two() call is very inefficient. Currently this is just a front-end onto parse_data(). If your OS supports it, you're far better off doing something like:

    $parser->parse_open("/bin/cat msg.head msg.body |");


Top Minimizing memory

Optimum input mechanisms:

    parse()                    YES
    parse_open()               YES
    parse_data()               NO  (in-core I/O will burn core)
    parse_two()                NO  (in-core I/O will burn core)

Optimum settings:

    decode_headers()           *** (no real difference)
    extract_nested_messages()  *** (no real difference)
    output_to_core()           0   (will use MUCH less memory)
    tmp_recycling()            0?  (promotes faster GC if 
                                    tmp_to_core is 1)
    tmp_to_core()              0   (will use MUCH less memory)
    use_inner_files()          *** (no real difference, but set it to 1 
				    if you *must* have tmp_to_core set to 1,
				    so that you avoid in-core tmpfiles)


Top Maximizing tolerance of bad MIME

Optimum input mechanisms:

    parse()                    *** (doesn't matter)
    parse_open()               *** (doesn't matter)
    parse_data()               *** (doesn't matter)
    parse_two()                *** (doesn't matter)

Optimum settings:

    decode_headers()           0   (sidesteps problem of bad hdr encodings)
    extract_nested_messages()  0   (sidesteps problems of bad nested messages,
                                    but often you want it set to 1 anyway).
    output_to_core()           *** (doesn't matter)
    tmp_recycling()            *** (doesn't matter)
    tmp_to_core()              *** (doesn't matter)
    use_inner_files()          *** (doesn't matter)


Top Avoiding disk-based temporary files

Optimum input mechanisms:

    parse()                    YES (if you give it a seekable handle)
    parse_open()               YES (becomes a seekable handle) 
    parse_data()               NO  (unless you set tmp_to_core(1))
    parse_two()                NO  (unless you set tmp_to_core(1))

Optimum settings:

    decode_headers()           *** (doesn't matter)
    extract_nested_messages()  *** (doesn't matter)
    output_to_core()           *** (doesn't matter)
    tmp_recycling              1   (restricts created files to 1 per parser)
    tmp_to_core()              1 
    use_inner_files()          1

If we can use them, inner files avoid most tmpfiles. If you parse from a seekable-and-tellable filehandle, then the internal process_to_bound() doesn't need to extract each part into a temporary buffer; it can use IO::InnerFile (warning: this will slow down the parsing of messages with large attachments).

You can veto tmpfiles entirely. If you might not be parsing from a seekable-and-tellable filehandle, you can set tmp_to_core() true: this will always use in-core I/O for the buffering (warning: this will slow down the parsing of messages with large attachments).

Final resort. You can always override new_tmpfile() in a subclass.


Top WARNINGS

Multipart messages are always read line-by-line
Multipart document parts are read line-by-line, so that the encapsulation boundaries may easily be detected. However, bad MIME composition agents (for example, naive CGI scripts) might return multipart documents where the parts are, say, unencoded bitmap files... and, consequently, where such "lines" might be veeeeeeeeery long indeed.

A better solution for this case would be to set up some form of state machine for input processing. This will be left for future versions.

Multipart parts read into temp files before decoding
In my original implementation, the MIME::Decoder classes had to be aware of encapsulation boundaries in multipart MIME documents. While this decode-while-parsing approach obviated the need for temporary files, it resulted in inflexible and complex decoder implementations.

The revised implementation uses a temporary file (a la tmpfile()) during parsing to hold the encoded portion of the current MIME document or part. This file is deleted automatically after the current part is decoded and the data is written to the "body stream" object; you'll never see it, and should never need to worry about it.

Some folks have asked for the ability to bypass this temp-file mechanism, I suppose because they assume it would slow down their application. I considered accomodating this wish, but the temp-file approach solves a lot of thorny problems in parsing, and it also protects against hidden bugs in user applications (what if you've directed the encoded part into a scalar, and someone unexpectedly sends you a 6 MB tar file?). Finally, I'm just not conviced that the temp-file use adds significant overhead.

Fuzzing of CRLF and newline on input
RFC-1521 dictates that MIME streams have lines terminated by CRLF ("\r\n"). However, it is extremely likely that folks will want to parse MIME streams where each line ends in the local newline character "\n" instead.

An attempt has been made to allow the parser to handle both CRLF and newline-terminated input.

Fuzzing of CRLF and newline on output
The "7bit" and "8bit" decoders will decode both a "\n" and a "\r\n" end-of-line sequence into a "\n".

The "binary" decoder (default if no encoding specified) still outputs stuff verbatim... so a MIME message with CRLFs and no explicit encoding will be output as a text file that, on many systems, will have an annoying ^M at the end of each line... but this is as it should be.

Inability to handle multipart boundaries that contain newlines
First, let's get something straight: this is an evil, EVIL practice, and is incompatible with RFC-1521... hence, it's not valid MIME.

If your mailer creates multipart boundary strings that contain newlines when they appear in the message body, give it two weeks notice and find another one. If your mail robot receives MIME mail like this, regard it as syntactically incorrect MIME, which it is.

Why do I say that? Well, in RFC-1521, the syntax of a boundary is given quite clearly:

      boundary := 0*69<bchars> bcharsnospace
        
      bchars := bcharsnospace / " "
      
      bcharsnospace :=    DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_"
                   / "," / "-" / "." / "/" / ":" / "=" / "?"

All of which means that a valid boundary string cannot have newlines in it, and any newlines in such a string in the message header are expected to be solely the result of folding the string (i.e., inserting to-be-removed newlines for readability and line-shortening only).

Yet, there is at least one brain-damaged user agent out there that composes mail like this:

      MIME-Version: 1.0
      Content-type: multipart/mixed; boundary="----ABC-
       123----"
      Subject: Hi... I'm a dork!
      
      This is a multipart MIME message (yeah, right...)
      
      ----ABC-
       123----
      
      Hi there! 

We have got to discourage practices like this (and the recent file upload idiocy where binary files that are part of a multipart MIME message aren't base64-encoded) if we want MIME to stay relatively simple, and MIME parsers to be relatively robust.

Thanks to Andreas Koenig for bringing a baaaaaaaaad user agent to my attention.


Top AUTHOR

Eryq (eryq@zeegee.com), ZeeGee Software Inc (http://www.zeegee.com).

All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


Top VERSION

$Revision: 5.406 $ $Date: 2000/11/12 05:55:11 $


Generated Wed Jan 17 01:58:24 2001 by cvu_pod2html
./usr/share/doc/libmime-perl/html/Tools.pm.html0000644000000000000000000022555307231241233021565 0ustar rootroot00000000000000 MIME-tools ZeeGee Software

MIME-tools

This module is BETA code, which means that the interfaces are fairly stable BUT it has not been out in the community long enough to guarantea $self->interface(ENTITY_CLASS => 'MIME::MyEntity'); $self->interface(HEAD_CLASS => 'MIME::MyHead'); $self; ### return }

With no VALUE, returns the VALUE currently associated with that ROLE.

new_body_for HEAD
Instance method. Based on the HEAD of a part we are parsing, return a new body object (any desirable subclass of MIME::Body) for receiving that part's data.

If you set the output_to_core option to false before parsing (the default), then we call output_path() and create a new MIME::Body::File on that filename.

If you set the output_to_core option to true before parsing, then you get a MIME::Body::InCore instead.

If you want the parser to do something else entirely, you can override this method in a subclass.

new_tmpfile [RECYCLE]
Instance method. Return an IO handle to be used to hold temporary data during a parse. The default uses the standard IO::File->new_tmpfile() method unless tmp_to_core() dictates otherwise, but you can override this. You shouldn't need to.

If you do override this, make certain that the object you return is set for binmode(), and is able to handle the following methods:

    read(BUF, NBYTES)
    getline()
    getlines()
    print(@ARGS)
    flush() 
    seek(0, 0)

Fatal exception if the stream could not be established.

If RECYCLE is given, it is an object returned by a previous invocation of this method; to recycle it, this method must effectively rewind and truncate it, and return the same object. If you don't want to support recycling, just ignore it and always return a new object.


Top Parse results and error recovery

last_error
Instance method. Return the error (if any) that we ignored in the last parse.

last_head
Instance method. Return the top-level MIME header of the last stream we attempted to parse. This is useful for replying to people who sent us bad MIME messages.
    ### Parse an input stream:
    eval { $entity = $parser->parse(\*STDIN) };
    if (!$entity) {    ### parse failed!
	my $decapitated = $parser->last_head;  
	...
    }

results
Instance method. Return an object containing lots of info from the last entity parsed. This will be an instance of class MIME::Parser::Results.


Top OPTIMIZING YOUR PARSER


Top Maximizing speed

Optimum input mechanisms:

    parse()                    YES (if you give it a globref or a 
				    subclass of IO::File)
    parse_open()               YES
    parse_data()               NO  (see below)
    parse_two()                NO  (see below)

Optimum settings:

    decode_headers()           *** (no real difference; 0 is slightly faster)
    extract_nested_messages()  0   (may be slightly faster, but in 
                                    general you want it set to 1)
    output_to_core()           0   (will be MUCH faster)
    tmp_recycling()            1?  (probably, but should be investigated)
    tmp_to_core()              0   (will be MUCH faster)
    use_inner_files()          0   (if tmp_to_core() is 0; 
				    use 1 otherwise)

File I/O is much faster than in-core I/O. Although it seems like slurping a message into core and processing it in-core should be faster... it isn't. Reason: Perl's filehandle-based I/O translates directly into native operating-system calls, whereas the in-core I/O is implemented in Perl.

Inner files are slower than real tmpfiles, but faster than in-core ones. If speed is your concern, that's why you should set use_inner_files(true) if you set tmp_to_core(true): so that we can bypass the slow in-core tmpfiles if the input stream permits.

Native I/O is much faster than object-oriented I/O. It's much faster to use <$foo> than $foo->getline. For backwards compatibilty, this module must continue to use object-oriented I/O in most places, but if you use parse() with a "real" filehandle (string, globref, or subclass of IO::File) then MIME::Parser is able to perform some crucial optimizations.

The parse_two() call is very inefficient. Currently this is just a front-end onto parse_data(). If your OS supports it, you're far better off doing something like:

    $parser->parse_open("/bin/cat msg.head msg.body |");


Top Minimizing memory

Optimum input mechanisms:

    parse()                    YES
    parse_open()               YES
    parse_data()               NO  (in-core I/O will burn core)
    parse_two()                NO  (in-core I/O will burn core)

Optimum settings:

    decode_headers()           *** (no real difference)
    extract_nested_messages()  *** (no real difference)
    output_to_core()           0   (will use MUCH less memory)
    tmp_recycling()            0?  (promotes faster GC if 
                                    tmp_to_core is 1)
    tmp_to_core()              0   (will use MUCH less memory)
    use_inner_files()          *** (no real difference, but set it to 1 
				    if you *must* have tmp_to_core set to 1,
				    so that you avoid in-core tmpfiles)


Top Maximizing tolerance of bad MIME

Optimum input mechanisms:

    parse()                    *** (doesn't matter)
    parse_open()               *** (doesn't matter)
    parse_data()               *** (doesn't matter)
    parse_two()                *** (doesn't matter)

Optimum settings:

    decode_headers()           0   (sidesteps problem of bad hdr encodings)
    extract_nested_messages()  0   (sidesteps problems of bad nested messages,
                                    but often you want it set to 1 anyway).
    output_to_core()           *** (doesn't matter)
    tmp_recycling()            *** (doesn't matter)
    tmp_to_core()              *** (doesn't matter)
    use_inner_files()          *** (doesn't matter)


Top Avoiding disk-based temporary files

Optimum input mechanisms:

    parse()                    YES (if you give it a seekable handle)
    parse_open()               YES (becomes a seekable handle) 
    parse_data()               NO  (unless you set tmp_to_core(1))
    parse_two()                NO  (unless you set tmp_to_core(1))

Optimum settings:

    decode_headers()           *** (doesn't matter)
    extract_nested_messages()  *** (doesn't matter)
    output_to_core()           *** (doesn't matter)
    tmp_recycling              1   (restricts created files to 1 per parser)
    tmp_to_core()              1 
    use_inner_files()          1

If we can use them, inner files avoid most tmpfiles. If you parse from a seekable-and-tellable filehandle, then the internal process_to_bound() doesn't need to extract each part into a temporary buffer; it can use IO::InnerFile (warning: this will slow down the parsing of messages with large attachments).

You can veto tmpfiles entirely. If you might not be parsing from a seekable-and-tellable filehandle, you can set tmp_to_core() true: this will always use in-core I/O for the buffering (warning: this will slow down the parsing of messages with large attachments).

Final resort. You can always override new_tmpfile() in a subclass.


Top WARNINGS

Multipart messages are always read line-by-line
Multipart document parts are read line-by-line, so that the encapsulation boundaries may easily be detected. However, bad MIME composition agents (for example, naive CGI scripts) might return multipart documents where the parts are, say, unencoded bitmap files... and, consequently, where such "lines" might be veeeeeeeeery long indeed.

A better solution for this case would be to set up some form of state machine for input processing. This will be left for future versions.

Multipart parts read into temp files before decoding
In my original implementation, the MIME::Decoder classes had to be aware of encapsulation boundaries in multipart MIME documents. While this decode-while-parsing approach obviated the need for temporary files, it resulted in inflexible and complex decoder implementations.

The revised implementation uses a temporary file (a la tmpfile()) during parsing to hold the encoded portion of the current MIME document or part. This file is deleted automatically after the current part is decoded and the data is written to the "body stream" object; you'll never see it, and should never need to worry about it.

Some folks have asked for the ability to bypass this temp-file mechanism, I suppose because they assume it would slow down their application. I considered accomodating this wish, but the temp-file approach solves a lot of thorny problems in parsing, and it also protects against hidden bugs in user applications (what if you've directed the encoded part into a scalar, and someone unexpectedly sends you a 6 MB tar file?). Finally, I'm just not conviced that the temp-file use adds significant overhead.

Fuzzing of CRLF and newline on input
RFC-1521 dictates that MIME streams have lines terminated by CRLF ("\r\n"). However, it is extremely likely that folks will want to parse MIME streams where each line ends in the local newline character "\n" instead.

An attempt has been made to allow the parser to handle both CRLF and newline-terminated input.

Fuzzing of CRLF and newline on output
The "7bit" and "8bit" decoders will decode both a "\n" and a "\r\n" end-of-line sequence into a "\n".

The "binary" decoder (default if no encoding specified) still outputs stuff verbatim... so a MIME message with CRLFs and no explicit encoding will be output as a text file that, on many systems, will have an annoying ^M at the end of each line... but this is as it should be.

Inability to handle multipart boundaries that contain newlines
First, let's get something straight: this is an evil, EVIL practice, and is incompatible with RFC-1521... hence, it's not valid MIME.

If your mailer creates multipart boundary strings that contain newlines when they appear in the message body, give it two weeks notice and find another one. If your mail robot receives MIME mail like this, regard it as syntactically incorrect MIME, which it is.

Why do I say that? Well, in RFC-1521, the syntax of a boundary is given quite clearly:

      boundary := 0*69<bchars> bcharsnospace
        
      bchars := bcharsnospace / " "
      
      bcharsnospace :=    DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_"
                   / "," / "-" / "." / "/" / ":" / "=" / "?"

All of which means that a valid boundary string cannot have newlines in it, and any newlines in such a string in the message header are expected to be solely the result of folding the string (i.e., inserting to-be-removed newlines for readability and line-shortening only).

Yet, there is at least one brain-damaged user agent out there that composes mail like this:

      MIME-Version: 1.0
      Content-type: multipart/mixed; boundary="----ABC-
       123----"
      Subject: Hi... I'm a dork!
      
      This is a multipart MIME message (yeah, right...)
      
      ----ABC-
       123----
      
      Hi there! 

We have got to discourage practices like this (and the recent file upload idiocy where binary files that are part of a multipart MIME message aren't base64-encoded) if we want MIME to stay relatively simple, and MIME parsers to be relatively robust.

Thanks to Andreas Koenig for bringing a baaaaaaaaad user agent to my attention.


Top AUTHOR

Eryq (eryq@zeegee.com), ZeeGee Software Inc (http://www.zeegee.com).

All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


Top VERSION

$Revision: 5.406 $ $Date: 2000/11/12 05:55:11 $


Generated Wed Jan 17 01:58:24 2001 by cvu_pod2html
./usr/share/doc/libmime-perl/html/Tools.pm.html0000644000000000000000000022555307231241233021565 0ustar rootroot00000000000000 MIME-tools ZeeGee Software

MIME-tools

This module is BETA code, which means that the interfaces are fairly stable BUT it has not been out in the community long enough to guarantea $self->interface(ENTITY_CLASS => 'MIME::MyEntity'); $self->interface(HEAD_CLASS => 'MIME::MyHead'); $self; ### return }

With no VALUE, returns the VALUE currently associated with that ROLE.

new_body_for HEAD
Instance method. Based on the HEAD of a part we are parsing, return a new body object (any desirable subclass of MIME::Body) for receiving that part's data.

If you set the output_to_core option to false before parsing (the default), then we call output_path() and create a new MIME::Body::File on that filename.

If you set the output_to_core option to true before parsing, then you get a MIME::Body::InCore instead.

If you want the parser to do something else entirely, you can override this method in a subclass.

new_tmpfile [RECYCLE]
Instance method. Return an IO handle to be used to hold temporary data during a parse. The default uses the standard IO::File->new_tmpfile() method unless tmp_to_core() dictates otherwise, but you can override this. You shouldn't need to.

If you do override this, make certain that the object you return is set for binmode(), and is able to handle the following methods:

    read(BUF, NBYTES)
    getline()
    getlines()
    print(@ARGS)
    flush() 
    seek(0, 0)

Fatal exception if the stream could not be established.

If RECYCLE is given, it is an object returned by a previous invocation of this method; to recycle it, this method must effectively rewind and truncate it, and return the same object. If you don't want to support recycling, just ignore it and always return a new object.


Top Parse results and error recovery

last_error
Instance method. Return the error (if any) that we ignored in the last parse.

last_head
Instance method. Return the top-level MIME header of the last stream we attempted to parse. This is useful for replying to people who sent us bad MIME messages.
    ### Parse an input stream:
    eval { $entity = $parser->parse(\*STDIN) };
    if (!$entity) {    ### parse failed!
	my $decapitated = $parser->last_head;  
	...
    }

results
Instance method. Return an object containing lots of info from the last entity parsed. This will be an instance of class MIME::Parser::Results.


Top OPTIMIZING YOUR PARSER


Top Maximizing speed

Optimum input mechanisms:

    parse()                    YES (if you give it a globref or a 
				    subclass of IO::File)
    parse_open()               YES
    parse_data()               NO  (see below)
    parse_two()                NO  (see below)

Optimum settings:

    decode_headers()           *** (no real difference; 0 is slightly faster)
    extract_nested_messages()  0   (may be slightly faster, but in 
                                    general you want it set to 1)
    output_to_core()           0   (will be MUCH faster)
    tmp_recycling()            1?  (probably, but should be investigated)
    tmp_to_core()              0   (will be MUCH faster)
    use_inner_files()          0   (if tmp_to_core() is 0; 
				    use 1 otherwise)

File I/O is much faster than in-core I/O. Although it seems like slurping a message into core and processing it in-core should be faster... it isn't. Reason: Perl's filehandle-based I/O translates directly into native operating-system calls, whereas the in-core I/O is implemented in Perl.

Inner files are slower than real tmpfiles, but faster than in-core ones. If speed is your concern, that's why you should set use_inner_files(true) if you set tmp_to_core(true): so that we can bypass the slow in-core tmpfiles if the input stream permits.

Native I/O is much faster than object-oriented I/O. It's much faster to use <$foo> than $foo->getline. For backwards compatibilty, this module must continue to use object-oriented I/O in most places, but if you use parse() with a "real" filehandle (string, globref, or subclass of IO::File) then MIME::Parser is able to perform some crucial optimizations.

The parse_two() call is very inefficient. Currently this is just a front-end onto parse_data(). If your OS supports it, you're far better off doing something like:

    $parser->parse_open("/bin/cat msg.head msg.body |");


Top Minimizing memory

Optimum input mechanisms:

    parse()                    YES
    parse_open()               YES
    parse_data()               NO  (in-core I/O will burn core)
    parse_two()                NO  (in-core I/O will burn core)

Optimum settings:

    decode_headers()           *** (no real difference)
    extract_nested_messages()  *** (no real difference)
    output_to_core()           0   (will use MUCH less memory)
    tmp_recycling()            0?  (promotes faster GC if 
                                    tmp_to_core is 1)
    tmp_to_core()              0   (will use MUCH less memory)
    use_inner_files()          *** (no real difference, but set it to 1 
				    if you *must* have tmp_to_core set to 1,
				    so that you avoid in-core tmpfiles)


Top Maximizing tolerance of bad MIME

Optimum input mechanisms:

    parse()                    *** (doesn't matter)
    parse_open()               *** (doesn't matter)
    parse_data()               *** (doesn't matter)
    parse_two()                *** (doesn't matter)

Optimum settings:

    decode_headers()           0   (sidesteps problem of bad hdr encodings)
    extract_nested_messages()  0   (sidesteps problems of bad nested messages,
                                    but often you want it set to 1 anyway).
    output_to_core()           *** (doesn't matter)
    tmp_recycling()            *** (doesn't matter)
    tmp_to_core()              *** (doesn't matter)
    use_inner_files()          *** (doesn't matter)


Top Avoiding disk-based temporary files

Optimum input mechanisms:

    parse()                    YES (if you give it a seekable handle)
    parse_open()               YES (becomes a seekable handle) 
    parse_data()               NO  (unless you set tmp_to_core(1))
    parse_two()                NO  (unless you set tmp_to_core(1))

Optimum settings:

    decode_headers()           *** (doesn't matter)
    extract_nested_messages()  *** (doesn't matter)
    output_to_core()           *** (doesn't matter)
    tmp_recycling              1   (restricts created files to 1 per parser)
    tmp_to_core()              1 
    use_inner_files()          1

If we can use them, inner files avoid most tmpfiles. If you parse from a seekable-and-tellable filehandle, then the internal process_to_bound() doesn't need to extract each part into a temporary buffer; it can use IO::InnerFile (warning: this will slow down the parsing of messages with large attachments).

You can veto tmpfiles entirely. If you might not be parsing from a seekable-and-tellable filehandle, you can set tmp_to_core() true: this will always use in-core I/O for the buffering (warning: this will slow down the parsing of messages with large attachments).

Final resort. You can always override new_tmpfile() in a subclass.


Top WARNINGS

Multipart messages are always read line-by-line
Multipart document parts are read line-by-line, so that the encapsulation boundaries may easily be detected. However, bad MIME composition agents (for example, naive CGI scripts) might return multipart documents where the parts are, say, unencoded bitmap files... and, consequently, where such "lines" might be veeeeeeeeery long indeed.

A better solution for this case would be to set up some form of state machine for input processing. This will be left for future versions.

Multipart parts read into temp files before decoding
In my original implementation, the MIME::Decoder classes had to be aware of encapsulation boundaries in multipart MIME documents. While this decode-while-parsing approach obviated the need for temporary files, it resulted in inflexible and complex decoder implementations.

The revised implementation uses a temporary file (a la tmpfile()) during parsing to hold the encoded portion of the current MIME document or part. This file is deleted automatically after the current part is decoded and the data is written to the "body stream" object; you'll never see it, and should never need to worry about it.

Some folks have asked for the ability to bypass this temp-file mechanism, I suppose because they assume it would slow down their application. I considered accomodating this wish, but the temp-file approach solves a lot of thorny problems in parsing, and it also protects against hidden bugs in user applications (what if you've directed the encoded part into a scalar, and someone unexpectedly sends you a 6 MB tar file?). Finally, I'm just not conviced that the temp-file use adds significant overhead.

Fuzzing of CRLF and newline on input
RFC-1521 dictates that MIME streams have lines terminated by CRLF ("\r\n"). However, it is extremely likely that folks will want to parse MIME streams where each line ends in the local newline character "\n" instead.

An attempt has been made to allow the parser to handle both CRLF and newline-terminated input.

Fuzzing of CRLF and newline on output
The "7bit" and "8bit" decoders will decode both a "\n" and a "\r\n" end-of-line sequence into a "\n".

The "binary" decoder (default if no encoding specified) still outputs stuff verbatim... so a MIME message with CRLFs and no explicit encoding will be output as a text file that, on many systems, will have an annoying ^M at the end of each line... but this is as it should be.

Inability to handle multipart boundaries that contain newlines
First, let's get something straight: this is an evil, EVIL practice, and is incompatible with RFC-1521... hence, it's not valid MIME.

If your mailer creates multipart boundary strings that contain newlines when they appear in the message body, give it two weeks notice and find another one. If your mail robot receives MIME mail like this, regard it as syntactically incorrect MIME, which it is.

Why do I say that? Well, in RFC-1521, the syntax of a boundary is given quite clearly:

      boundary := 0*69<bchars> bcharsnospace
        
      bchars := bcharsnospace / " "
      
      bcharsnospace :=    DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_"
                   / "," / "-" / "." / "/" / ":" / "=" / "?"

All of which means that a valid boundary string cannot have newlines in it, and any newlines in such a string in the message header are expected to be solely the result of folding the string (i.e., inserting to-be-removed newlines for readability and line-shortening only).

Yet, there is at least one brain-damaged user agent out there that composes mail like this:

      MIME-Version: 1.0
      Content-type: multipart/mixed; boundary="----ABC-
       123----"
      Subject: Hi... I'm a dork!
      
      This is a multipart MIME message (yeah, right...)
      
      ----ABC-
       123----
      
      Hi there! 

We have got to discourage practices like this (and the recent file upload idiocy where binary files that are part of a multipart MIME message aren't base64-encoded) if we want MIME to stay relatively simple, and MIME parsers to be relatively robust.

Thanks to Andreas Koenig for bringing a baaaaaaaaad user agent to my attention.


Top AUTHOR

Eryq (eryq@zeegee.com), ZeeGee Software Inc (http://www.zeegee.com).

All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


Top VERSION

$Revision: 5.406 $ $Date: 2000/11/12 05:55:11 $


Generated Wed Jan 17 01:58:24 2001 by cvu_pod2html
./usr/share/doc/libmime-perl/html/Tools.pm.html0000644000000000000000000022555307231241233021565 0ustar rootroot00000000000000 MIME-tools ZeeGee Software

MIME-tools

This module is BETA code, which means that the interfaces are fairly stable BUT it has not been out in the community long enough to guarantea $self->interface(ENTITY_CLASS => 'MIME::MyEntity'); $self->interface(HEAD_CLASS => 'MIME::MyHead'); $self; ### return }

With no VALUE, returns the VALUE currently associated with that ROLE.

new_body_for HEAD
Instance method. Based on the HEAD of a part we are parsing, return a new body object (any desirable subclass of MIME::Body) for receiving that part's data.

If you set the output_to_core option to false before parsing (the default), then we call output_path() and create a new MIME::Body::File on that filename.

If you set the output_to_core option to true before parsing, then you get a MIME::Body::InCore instead.

If you want the parser to do something else entirely, you can override this method in a subclass.

new_tmpfile [RECYCLE]
Instance method. Return an IO handle to be used to hold temporary data during a parse. The default uses the standard IO::File->new_tmpfile() method unless tmp_to_core() dictates otherwise, but you can override this. You shouldn't need to.

If you do override this, make certain that the object you return is set for binmode(), and is able to handle the following methods:

    read(BUF, NBYTES)
    getline()
    getlines()
    print(@ARGS)
    flush() 
    seek(0, 0)

Fatal exception if the stream could not be established.

If RECYCLE is given, it is an object returned by a previous invocation of this method; to recycle it, this method must effectively rewind and truncate it, and return the same object. If you don't want to support recycling, just ignore it and always return a new object.


Top Parse results and error recovery

last_error
Instance method. Return the error (if any) that we ignored in the last parse.

last_head
Instance method. Return the top-level MIME header of the last stream we attempted to parse. This is useful for replying to people who sent us bad MIME messages.
    ### Parse an input stream:
    eval { $entity = $parser->parse(\*STDIN) };
    if (!$entity) {    ### parse failed!
	my $decapitated = $parser->last_head;  
	...
    }

results
Instance method. Return an object containing lots of info from the last entity parsed. This will be an instance of class MIME::Parser::Results.


Top OPTIMIZING YOUR PARSER


Top Maximizing speed

Optimum input mechanisms:

    parse()                    YES (if you give it a globref or a 
				    subclass of IO::File)
    parse_open()               YES
    parse_data()               NO  (see below)
    parse_two()                NO  (see below)

Optimum settings:

    decode_headers()           *** (no real difference; 0 is slightly faster)
    extract_nested_messages()  0   (may be slightly faster, but in 
                                    general you want it set to 1)
    output_to_core()           0   (will be MUCH faster)
    tmp_recycling()            1?  (probably, but should be investigated)
    tmp_to_core()              0   (will be MUCH faster)
    use_inner_files()          0   (if tmp_to_core() is 0; 
				    use 1 otherwise)

File I/O is much faster than in-core I/O. Although it seems like slurping a message into core and processing it in-core should be faster... it isn't. Reason: Perl's filehandle-based I/O translates directly into native operating-system calls, whereas the in-core I/O is implemented in Perl.

Inner files are slower than real tmpfiles, but faster than in-core ones. If speed is your concern, that's why you should set use_inner_files(true) if you set tmp_to_core(true): so that we can bypass the slow in-core tmpfiles if the input stream permits.

Native I/O is much faster than object-oriented I/O. It's much faster to use <$foo> than $foo->getline. For backwards compatibilty, this module must continue to use object-oriented I/O in most places, but if you use parse() with a "real" filehandle (string, globref, or subclass of IO::File) then MIME::Parser is able to perform some crucial optimizations.

The parse_two() call is very inefficient. Currently this is just a front-end onto parse_data(). If your OS supports it, you're far better off doing something like:

    $parser->parse_open("/bin/cat msg.head msg.body |");


Top Minimizing memory

Optimum input mechanisms:

    parse()                    YES
    parse_open()               YES
    parse_data()               NO  (in-core I/O will burn core)
    parse_two()                NO  (in-core I/O will burn core)

Optimum settings:

    decode_headers()           *** (no real difference)
    extract_nested_messages()  *** (no real difference)
    output_to_core()           0   (will use MUCH less memory)
    tmp_recycling()            0?  (promotes faster GC if 
                                    tmp_to_core is 1)
    tmp_to_core()              0   (will use MUCH less memory)
    use_inner_files()          *** (no real difference, but set it to 1 
				    if you *must* have tmp_to_core set to 1,
				    so that you avoid in-core tmpfiles)


Top Maximizing tolerance of bad MIME

Optimum input mechanisms:

    parse()                    *** (doesn't matter)
    parse_open()               *** (doesn't matter)
    parse_data()               *** (doesn't matter)
    parse_two()                *** (doesn't matter)

Optimum settings:

    decode_headers()           0   (sidesteps problem of bad hdr encodings)
    extract_nested_messages()  0   (sidesteps problems of bad nested messages,
                                    but often you want it set to 1 anyway).
    output_to_core()           *** (doesn't matter)
    tmp_recycling()            *** (doesn't matter)
    tmp_to_core()              *** (doesn't matter)
    use_inner_files()          *** (doesn't matter)


Top Avoiding disk-based temporary files

Optimum input mechanisms:

    parse()                    YES (if you give it a seekable handle)
    parse_open()               YES (becomes a seekable handle) 
    parse_data()               NO  (unless you set tmp_to_core(1))
    parse_two()                NO  (unless you set tmp_to_core(1))

Optimum settings:

    decode_headers()           *** (doesn't matter)
    extract_nested_messages()  *** (doesn't matter)
    output_to_core()           *** (doesn't matter)
    tmp_recycling              1   (restricts created files to 1 per parser)
    tmp_to_core()              1 
    use_inner_files()          1

If we can use them, inner files avoid most tmpfiles. If you parse from a seekable-and-tellable filehandle, then the internal process_to_bound() doesn't need to extract each part into a temporary buffer; it can use IO::InnerFile (warning: this will slow down the parsing of messages with large attachments).

You can veto tmpfiles entirely. If you might not be parsing from a seekable-and-tellable filehandle, you can set tmp_to_core() true: this will always use in-core I/O for the buffering (warning: this will slow down the parsing of messages with large attachments).

Final resort. You can always override new_tmpfile() in a subclass.


Top WARNINGS

Multipart messages are always read line-by-line
Multipart document parts are read line-by-line, so that the encapsulation boundaries may easily be detected. However, bad MIME composition agents (for example, naive CGI scripts) might return multipart documents where the parts are, say, unencoded bitmap files... and, consequently, where such "lines" might be veeeeeeeeery long indeed.

A better solution for this case would be to set up some form of state machine for input processing. This will be left for future versions.

Multipart parts read into temp files before decoding
In my original implementation, the MIME::Decoder classes had to be aware of encapsulation boundaries in multipart MIME documents. While this decode-while-parsing approach obviated the need for temporary files, it resulted in inflexible and complex decoder implementations.

The revised implementation uses a temporary file (a la tmpfile()) during parsing to hold the encoded portion of the current MIME document or part. This file is deleted automatically after the current part is decoded and the data is written to the "body stream" object; you'll never see it, and should never need to worry about it.

Some folks have asked for the ability to bypass this temp-file mechanism, I suppose because they assume it would slow down their application. I considered accomodating this wish, but the temp-file approach solves a lot of thorny problems in parsing, and it also protects against hidden bugs in user applications (what if you've directed the encoded part into a scalar, and someone unexpectedly sends you a 6 MB tar file?). Finally, I'm just not conviced that the temp-file use adds significant overhead.

Fuzzing of CRLF and newline on input
RFC-1521 dictates that MIME streams have lines terminated by CRLF ("\r\n"). However, it is extremely likely that folks will want to parse MIME streams where each line ends in the local newline character "\n" instead.

An attempt has been made to allow the parser to handle both CRLF and newline-terminated input.

Fuzzing of CRLF and newline on output
The "7bit" and "8bit" decoders will decode both a "\n" and a "\r\n" end-of-line sequence into a "\n".

The "binary" decoder (default if no encoding specified) still outputs stuff verbatim... so a MIME message with CRLFs and no explicit encoding will be output as a text file that, on many systems, will have an annoying ^M at the end of each line... but this is as it should be.

Inability to handle multipart boundaries that contain newlines
First, let's get something straight: this is an evil, EVIL practice, and is incompatible with RFC-1521... hence, it's not valid MIME.

If your mailer creates multipart boundary strings that contain newlines when they appear in the message body, give it two weeks notice and find another one. If your mail robot receives MIME mail like this, regard it as syntactically incorrect MIME, which it is.

Why do I say that? Well, in RFC-1521, the syntax of a boundary is given quite clearly:

      boundary := 0*69<bchars> bcharsnospace
        
      bchars := bcharsnospace / " "
      
      bcharsnospace :=    DIGIT / ALPHA / "'" / "(" / ")" / "+" /"_"
                   / "," / "-" / "." / "/" / ":" / "=" / "?"

All of which means that a valid boundary string cannot have newlines in it, and any newlines in such a string in the message header are expected to be solely the result of folding the string (i.e., inserting to-be-removed newlines for readability and line-shortening only).

Yet, there is at least one brain-damaged user agent out there that composes mail like this:

      MIME-Version: 1.0
      Content-type: multipart/mixed; boundary="----ABC-
       123----"
      Subject: Hi... I'm a dork!
      
      This is a multipart MIME message (yeah, right...)
      
      ----ABC-
       123----
      
      Hi there! 

We have got to discourage practices like this (and the recent file upload idiocy where binary files that are part of a multipart MIME message aren't base64-encoded) if we want MIME to stay relatively simple, and MIME parsers to be relatively robust.

Thanks to Andreas Koenig for bringing a baaaaaaaaad user agent to my attention.


Top AUTHOR

Eryq (eryq@zeegee.com), ZeeGee Software Inc (http://www.zeegee.com).

All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.


Top VERSION

$Revision: 5.406 $ $Date: 2000/11/12 05:55:11 $


Generated Wed Jan 17 01:58:24 2001 by cvu_pod2html
./usr/share/doc/libmime-perl/html/Tools.pm.html0000644000000000000000000022555307231241233021565 0ustar rootroot00000000000000 MIME-tools ZeeGee Software

MIME-tools

This module is BETA code, which means that the interfaces are fairly stable BUT it has not been out in the community long enough to guarantea $self->interface(ENTITY_CLASS => 'MIME::MyEntity'); $self->interface(HEAD_CLASS => 'MIME::MyHead'); $self; ### return }

With no VALUE, returns the VALUE currently associated with that ROLE.

new_body_for HEAD
Instance method. Based on the HEAD of a part we are parsing, return a new body object (any desirable subclass of MIME::Body) for receiving that part's data.

If you set the output_to_core option to false before parsing (the default), then we call output_path() and create a new MIME::Body::File on that filename.

If you set the output_to_core option to true before parsing, then you get a MIME::Body::InCore instead.

If you want the parser to do something else entirely, you can override this method in a subclass.

new_tmpfile [RECYCLE]
Instance method. Return an IO handle to be used to hold temporary data during a parse. The default uses the standard IO::File->new_tmpfile() method unless tmp_to_core() dictates otherwise, but you can override this. You shouldn't need to.

If you do override this, make certain that the object you return is set for binmode(), and is able to handle the following methods:

    read(BUF, NBYTES)
    getline()
    getlines()
    print(@ARGS)
    flush() 
    seek(0, 0)

Fatal exception if the stream could not be established.

If RECYCLE is given, it is an object returned by a previous invocation of this method; to recycle it, this method must effectively rewind and truncate it, and return the same object. If you don't want to support recycling, just ignore it and always return a new object.


Top Parse results and error recovery

last_error
Instance method. Return the error (if any) that we ignored in the last parse.

last_head
Instance method. Return the top-level MIME header of the last stream we attempted to parse. This is useful for replying to people who sent us bad MIME messages.
    ### Parse an input stream:
    eval { $entity = $parser->parse(\*STDIN) };
    if (!$entity) {    ### parse failed!
	my $decapitated = $parser->last_head;  
	...
    }

results
Instance method. Return an object containing lots of info from the last entity parsed. This will be an instance of class MIME::Parser::Results.


Top OPTIMIZING YOUR PARSER


Top Maximizing speed

Optimum input mechanisms:

    parse()                    YES (if you give it a globref or a 
				    subclass of IO::File)
    parse_open()               YES
    parse_data()               NO  (see below)
    parse_two()                NO  (see below)

Optimum settings:

    decode_headers()           *** (no real difference; 0 is slightly faster)
    extract_nested_messages()  0   (may be slightly faster, but in 
                                    general you want it set to 1)
    output_to_core()           0   (will be MUCH faster)
    tmp_recycling()            1?  (probably, but should be investigated)
    tmp_to_core()              0   (will be MUCH faster)
    use_inner_files()          0   (if tmp_to_core() is 0; 
				    use 1 otherwise)

File I/O is much faster than in-core I/O. Although it seems like slurping a message into core and processing it in-core should be faster... it isn't. Reason: Perl's filehandle-based I/O translates directly into native operating-system calls, whereas the in-core I/O is implemented in Perl.

Inner files are slower than real tmpfiles, but faster than in-core ones. If speed is your concern, that's why you should set use_inner_files(true) if you set tmp_to_core(true): so that we can bypass the slow in-core tmpfiles if the input stream permits.

Native I/O is much faster than object-oriented I/O. It's much faster to use <$foo> than $foo->getline. For backwards compatibilty, this module must continue to use object-oriented I/O in most places, but if you use parse() with a "real" filehandle (string, globref, or subclass of IO::File) then MIME::Parser is able to perform some crucial optimizations.

The parse_two() call is very inefficient. Currently this is just a front-end onto parse_data(). If your OS supports it, you're far better off doing something like:

    $parser->parse_open("/bin/cat msg.head msg.body |");


Top Minimizing memory

Optimum input mechanisms:

    parse()                    YES
    parse_open()               YES
    parse_data()               NO  (in-core I/O will burn core)
    parse_two()                NO  (in-core I/O will burn core)

Optimum settings:

    decode_headers()           *** (no real difference)
    extract_nested_messages()  *** (no real difference)
    output_to_core()           0   (will use MUCH less memory)
    tmp_recycling()            0?  (promotes faster GC if 
                                    tmp_to_core is 1)
    tmp_to_core()              0   (will use MUCH less memory)
    use_inner_files()          *** (no real difference, but set it to 1 
				    if you *must* have tmp_to_core set to 1,
				    so that you avoid in-core tmpfiles)


Top Maximizing tolerance of bad MIME

Optimum input mechanisms:

    parse()                    *** (doesn't matter)
    parse_open()               *** (doesn't matter)
    parse_data()               *** (doesn't matter)
    parse_two()                *** (doesn't matter)

Optimum settings:

    decode_headers()           0   (sidesteps problem of bad hdr encodings)
    extract_nested_messages()  0   (sidesteps problems of bad nested messages,
                                    but often you want it set to 1 anyway).
    output_to_core()           *** (doesn't matter)
    tmp_recycling()            *** (doesn't matter)
    tmp_to_core()              *** (doesn't matter)
    use_inner_files()          *** (doesn't matter)


Top Avoiding disk-based temporary files

Optimum input mechanisms:

    parse()                    YES (if you give it a seekable handle)
    parse_open()               YES (becomes a seekable handle) 
    parse_data()               NO  (unless you set tmp_to_core(1))
    parse_two()                NO  (unless you set tmp_to_core(1))

Optimum settings:

    decode_headers()           *** (doesn't matter)
    extract_nested_messages()  *** (doesn't matter)
    output_to_core()           *** (doesn't matter)
    tmp_recycling              1   (restricts created files to 1 per parser)
    tmp_to_core()              1 
    use_inner_files()          1

If we can use them, inner files avoid most tmpfiles. If you parse from a seekable-and-tellable filehandle, then the internal process_to_bound() doesn't need to extract each part into a temporary buffer; it can use IO::InnerFile (warning: this will slow down the parsing of messages with large attachments).

You can veto tmpfiles entirely. If you might not be parsing from a seekable-and-tellable filehandle, you can set tmp_to_core() true: this will always use in-core I/O for the buffering (warning: this will slow down the parsing of messages with large attachments).

Final resort. You can always override new_tmpfile() in a subclass.


Top WARNINGS

Multipart messages are always read line-by-line
Multipart document parts are read line-by-line, so that the encapsulation boundaries may easily be detected. However, bad MIME composition agents (for example, naive CGI scripts) might return multipart documents where the parts are, say, unencoded bitmap files... and, consequently, where such "lines" might be veeeeeeeeery long indeed.

A better solution for this case would be to set up some form of state machine for input processing. This will be left for future versions.

Multipart parts read into temp files before decoding
In my original implementation, the MIME::Decoder classes had to be aware of encapsulation boundaries in multipart MIME documents. While this decode-while-parsing approach obviated the need for temporary files, it resulted in inflexible and complex decoder implementations.

The revised implementation uses a temporary file (a la tmpfile()) during parsing to hold the encoded portion of the current MIME document or part. This file is deleted automatically after the current part is decoded and the data is written to the "body stream" object; you'll never see it, and should never need to worry about it