diff options
| author | fukachan <fukachan> | 2001-04-06 12:54:55 +0000 |
|---|---|---|
| committer | fukachan <fukachan> | 2001-04-06 12:54:55 +0000 |
| commit | eea2c34e5bebdaab143ffd7851c685fd9fa42454 (patch) | |
| tree | c65265919b537ca5a062552f8bfc35ae2ebd8a05 | |
| parent | d5acf8761f04aef6a5f1eb5f2c27c8675521b040 (diff) | |
| download | fml8-eea2c34e5bebdaab143ffd7851c685fd9fa42454.tar.gz fml8-eea2c34e5bebdaab143ffd7851c685fd9fa42454.tar.bz2 fml8-eea2c34e5bebdaab143ffd7851c685fd9fa42454.zip | |
Initial revision
36 files changed, 11704 insertions, 0 deletions
diff --git a/cpan/dist/MIME-Lite/COPYING b/cpan/dist/MIME-Lite/COPYING new file mode 100644 index 00000000..3c68f02b --- /dev/null +++ b/cpan/dist/MIME-Lite/COPYING @@ -0,0 +1,248 @@ + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 1, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/cpan/dist/MIME-Lite/INSTALLING b/cpan/dist/MIME-Lite/INSTALLING new file mode 100644 index 00000000..0e9c6e93 --- /dev/null +++ b/cpan/dist/MIME-Lite/INSTALLING @@ -0,0 +1,26 @@ +------------------------------------------------------------ +STANDARD INSTALLATION + +On most systems, just do this from the command line: + + perl Makefile.PL + make test + make install + +Please note that you'll need permission to write to the +standard installation directories; under Unix-like systems, this +often means that you must be logged in as "root". + +If you're on a non-Unix platform, you might be using 'dmake' +instead of 'make'. + +------------------------------------------------------------ +NON-STANDARD INSTALLATION + +To install to a non-standard place else (e.g., "/home/me/lib"), +see the manual page for ExtUtils::MakeMaker, or try this: + + perl Makefile.PL LIB=/home/me/lib + make test + make install + diff --git a/cpan/dist/MIME-Lite/MANIFEST b/cpan/dist/MIME-Lite/MANIFEST new file mode 100644 index 00000000..44cd881b --- /dev/null +++ b/cpan/dist/MIME-Lite/MANIFEST @@ -0,0 +1,35 @@ +COPYING +INSTALLING +MANIFEST +Makefile.PL +README +README.system +docs/MIME/Lite.pm.html +docs/MIME/icons/h1bullet.gif +docs/MIME/icons/h2bullet.gif +docs/MIME/icons/zeegee.gif +docs/icons/h1bullet.gif +docs/icons/h2bullet.gif +docs/icons/zeegee.gif +docs/index-menu.html +docs/index.html +docs/index.menu +docs/mime_fwd.html +docs/mime_gif.html +docs/mime_hack.html +docs/mime_longlines.html +docs/mime_postcard.html +examples/mime_fwd +examples/mime_gif +examples/mime_hack +examples/mime_longlines +examples/mime_postcard +lib/MIME/Lite.pm +t/ExtUtils/TBone.pm +t/Utils.pm +t/addrs.t +t/data.t +t/head.t +t/verify.t +testin/README +testin/hello diff --git a/cpan/dist/MIME-Lite/Makefile.PL b/cpan/dist/MIME-Lite/Makefile.PL new file mode 100755 index 00000000..447f1cb1 --- /dev/null +++ b/cpan/dist/MIME-Lite/Makefile.PL @@ -0,0 +1,20 @@ +#!/usr/bin/perl +use ExtUtils::MakeMaker; + +#------------------------------------------------------------ +# Makefile: +#------------------------------------------------------------ + +# Write the Makefile: +WriteMakefile( + NAME => 'MIME::Lite', + VERSION_FROM => "lib/MIME/Lite.pm", + DISTNAME => "MIME-Lite", + EXE_FILES => [@EXES], + 'dist' => { + PREOP => 'cvu_perl_preop -m MIME::Lite -f', + COMPRESS => 'gzip', + SUFFIX => 'gz', + } + ); + diff --git a/cpan/dist/MIME-Lite/README b/cpan/dist/MIME-Lite/README new file mode 100644 index 00000000..0f884797 --- /dev/null +++ b/cpan/dist/MIME-Lite/README @@ -0,0 +1,1357 @@ +NAME + MIME::Lite - low-calorie MIME generator + +SYNOPSIS + use MIME::Lite; + + Create a single-part message: + + ### Create a new single-part message, to send a GIF file: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'Helloooooo, nurse!', + Type =>'image/gif', + Encoding =>'base64', + Path =>'hellonurse.gif' + ); + + Create a multipart message (i.e., one with attachments): + + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'multipart/mixed' + ); + + ### Add parts (each "attach" has same arguments as "new"): + $msg->attach(Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif', + Disposition => 'attachment' + ); + + Output a message: + + ### Format as a string: + $str = $msg->as_string; + + ### Print to a filehandle (say, a "sendmail" stream): + $msg->print(\*SENDMAIL); + + Send a message: + + ### Send in the "best" way (the default is to use "sendmail"): + $msg->send; + +DESCRIPTION + In the never-ending quest for great taste with fewer calories, we + proudly present: *MIME::Lite*. + + MIME::Lite is intended as a simple, standalone module for generating + (not parsing!) MIME messages... specifically, it allows you to output a + simple, decent single- or multi-part message with text or binary + attachments. It does not require that you have the Mail:: or MIME:: + modules installed. + + You can specify each message part as either the literal data itself (in + a scalar or array), or as a string which can be given to open() to get a + readable filehandle (e.g., "<filename" or "somecommand|"). + + You don't need to worry about encoding your message data: this module + will do that for you. It handles the 5 standard MIME encodings. + + If you need more sophisticated behavior, please get the MIME-tools + package instead. I will be more likely to add stuff to that toolkit over + this one. + +MORE EXAMPLES + Attach a GIF to a text message + + This will create a multipart message exactly as above, but using the + "attach to singlepart" hack: + + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + + ### Attach a part: + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif' + ); + + Attach a pre-prepared part (allows fine-tuning): + + $part = MIME::Lite->new( + Type =>'text/html', + Data =>'<H1>Hello</H1>', + ); + $part->attr('content-type.charset' => 'UTF8'); + $part->add('X-Comment' => 'A message for you'); + $msg->attach($part); + + Send an HTML document... with images included! + + $msg = MIME::Lite->new( + To =>'you@yourhost.com', + Subject =>'HTML with in-line images!', + Type =>'multipart/related' + ); + $msg->attach(Type => 'text/html', + Data => qq{ <body> + Here's <i>my</i> image: + <img src="cid:myimage.gif"> + </body> } + ); + $msg->attach(Type => 'image/gif', + Id => 'myimage.gif', + Path => '/path/to/somefile.gif', + ); + $msg->send(); + + Output a message to a filehandle + + ### Write it to a filehandle: + $msg->print(\*STDOUT); + + ### Write just the header: + $msg->print_header(\*STDOUT); + + ### Write just the encoded body: + $msg->print_body(\*STDOUT); + + Get a message as a string + + ### Get entire message as a string: + $str = $msg->as_string; + + ### Get just the header: + $str = $msg->header_as_string; + + ### Get just the encoded body: + $str = $msg->body_as_string; + + Change how messages are sent + + ### Do something like this in your 'main': + if ($I_DONT_HAVE_SENDMAIL) { + MIME::Lite->send('smtp', "smtp.myisp.net", Timeout=>60); + } + + ### Now this will do the right thing: + $msg->send; ### will now use Net::SMTP as shown above + +FAQ + How do I prevent "Content" headers from showing up in my mail reader? + + Apparently, some people are using mail readers which display the MIME + headers like "Content-disposition", and they want MIME::Lite not to + generate them "because they look ugly". + + Sigh. + + Y'know, kids, those headers aren't just there for cosmetic purposes. + They help ensure that the message is *understood* correctly by mail + readers. But okay, you asked for it, you got it... here's how you can + suppress the standard MIME headers. Before you send the message, do + this: + + $msg->scrub; + + You can scrub() any part of a multipart message independently; just be + aware that it works recursively. Before you scrub, note the rules that I + follow: + + Content-type + You can safely scrub the "content-type" attribute if, and only if, + the part is of type "text/plain" with charset "us-ascii". + + Content-transfer-encoding + You can safely scrub the "content-transfer-encoding" attribute if, + and only if, the part uses "7bit", "8bit", or "binary" encoding. You + are far better off doing this if your lines are under 1000 + characters. Generally, that means you *can* scrub it for plain text, + and you can *not* scrub this for images, etc. + + Content-disposition + You can safely scrub the "content-disposition" attribute if you + trust the mail reader to do the right thing when it decides whether + to show an attachment inline or as a link. Be aware that scrubbing + both the content-disposition and the content-type means that there + is no way to "recommend" a filename for the attachment! + + Note: there are reports of brain-dead MUAs out there that do the + wrong thing if you *provide* the content-disposition. If your + attachments keep showing up inline or vice-versa, try scrubbing this + attribute. + + Content-length + You can always scrub "content-length" safely. + + How do I give my attachment a [different] recommended filename? + + By using the Filename option (which is different from Path!): + + $msg->attach(Type => "image/gif", + Path => "/here/is/the/real/file.GIF", + Filename => "logo.gif"); + + You should *not* put path information in the Filename. + +PUBLIC INTERFACE + Global configuration + + To alter the way the entire module behaves, you have the following + methods/options: + + MIME::Lite->header_order() + When used as a classmethod, this changes the default order in which + headers are output for *all* messages. + + MIME::Lite->quiet() + This classmethod can be used to suppress/unsuppress all warnings + coming from this module. + + MIME::Lite->send() + When used as a classmethod, this can be used to specify a different + default mechanism for sending message. The initial default is: + + MIME::Lite->send("sendmail", "/usr/lib/sendmail -t -oi -oem"); + + However, you should consider the similar but smarter and taint-safe + variant: + + MIME::Lite->send("sendmail"); + + Or, for non-Unix users: + + MIME::Lite->send("smtp"); + + $MIME::Lite::PARANOID + If true, we won't attempt to use MIME::Base64/MIME::QuotedPrint, + even if they're available. Default is false. + + $MIME::Lite::AUTO_ENCODE + If true, automatically choose the encoding from the content type. + Default is true. + + $MIME::Lite::AUTO_CC + If true, automatically send to the Cc/Bcc addresses for + send_by_smtp(). Default is true. + + $MIME::Lite::AUTO_VERIFY + If true, check paths to attachments right before printing, raising + an exception if any path is unreadable. Default is true. + + Construction + + new [PARAMHASH] + *Class method, constructor.* Create a new message object. + + If any arguments are given, they are passed into `build()'; + otherwise, just the empty object is created. + + attach PART + attach PARAMHASH... + *Instance method.* Add a new part to this message, and return the + new part. + + If you supply a single PART argument, it will be regarded as a + MIME::Lite object to be attached. Otherwise, this method assumes + that you are giving in the pairs of a PARAMHASH which will be sent + into `new()' to create the new part. + + One of the possibly-quite-useful hacks thrown into this is the + "attach-to-singlepart" hack: if you attempt to attach a part (let's + call it "part 1") to a message that doesn't have a content-type of + "multipart" or "message", the following happens: + + * A new part (call it "part 0") is made. + + * The MIME attributes and data (but *not* the other headers) are cut + from the "self" message, and pasted into "part 0". + + * The "self" is turned into a "multipart/mixed" message. + + * The new "part 0" is added to the "self", and *then* "part 1" is + added. + + One of the nice side-effects is that you can create a text message + and then add zero or more attachments to it, much in the same way + that a user agent like Netscape allows you to do. + + build [PARAMHASH] + *Class/instance method, initializer.* Create (or initialize) a MIME + message object. Normally, you'll use the following keys in + PARAMHASH: + + * Data, FH, or Path (either one of these, or none if multipart) + * Type (e.g., "image/jpeg") + * From, To, and Subject (if this is the "top level" of a message) + + The PARAMHASH can contain the following keys: + + (fieldname) + Any field you want placed in the message header, taken from the + standard list of header fields (you don't need to worry about + case): + + Approved Encrypted Received Sender + Bcc From References Subject + Cc Keywords Reply-To To + Comments Message-ID Resent-* X-* + Content-* MIME-Version Return-Path + Date Organization + + To give experienced users some veto power, these fields will be + set *after* the ones I set... so be careful: *don't set any MIME + fields* (like `Content-type') unless you know what you're doing! + + To specify a fieldname that's *not* in the above list, even one + that's identical to an option below, just give it with a + trailing `":"', like `"My-field:"'. When in doubt, that *always* + signals a mail field (and it sort of looks like one too). + + Data *Alternative to "Path" or "FH".* The actual message data. This may + be a scalar or a ref to an array of strings; if the latter, the + message consists of a simple concatenation of all the strings in + the array. + + Datestamp + *Optional.* If given true (or omitted), we force the creation of + a `Date:' field stamped with the current date/time if this is a + top-level message. You may want this if using send_by_smtp(). If + you don't want this to be done, either provide your own Date or + explicitly set this to false. + + Disposition + *Optional.* The content disposition, `"inline"' or + `"attachment"'. The default is `"inline"'. + + Encoding + *Optional.* The content transfer encoding that should be used to + encode your data: + + Use encoding: | If your message contains: + ------------------------------------------------------------ + 7bit | Only 7-bit text, all lines <1000 characters + 8bit | 8-bit text, all lines <1000 characters + quoted-printable | 8-bit text or long lines (more reliable than "8bit") + base64 | Largely non-textual data: a GIF, a tar file, etc. + + The default is taken from the Type; generally it is "binary" (no + encoding) for text/*, message/*, and multipart/*, and "base64" + for everything else. A value of `"binary"' is generally *not* + suitable for sending anything but ASCII text files with lines + under 1000 characters, so consider using one of the other values + instead. + + In the case of "7bit"/"8bit", long lines are automatically + chopped to legal length; in the case of "7bit", all 8-bit + characters are automatically *removed*. This may not be what you + want, so pick your encoding well! For more info, see the section + on "A MIME PRIMER". + + FH *Alternative to "Data" or "Path".* Filehandle containing the data, + opened for reading. See "ReadNow" also. + + Filename + *Optional.* The name of the attachment. You can use this to + supply a recommended filename for the end-user who is saving the + attachment to disk. You only need this if the filename at the + end of the "Path" is inadequate, or if you're using "Data" + instead of "Path". You should *not* put path information in here + (e.g., no "/" or "\" or ":" characters should be used). + + Id *Optional.* Same as setting "content-id". + + Length *Optional.* Set the content length explicitly. Normally, this header + is automatically computed, but only under certain circumstances + (see the section on "Limitations"). + + Path *Alternative to "Data" or "FH".* Path to a file containing the + data... actually, it can be any open()able expression. If it + looks like a path, the last element will automatically be + treated as the filename. See "ReadNow" also. + + ReadNow *Optional, for use with "Path".* If true, will open the path and + slurp the contents into core now. This is useful if the Path + points to a command and you don't want to run the command over + and over if outputting the message several times. Fatal + exception raised if the open fails. + + Top *Optional.* If defined, indicates whether or not this is a "top- + level" MIME message. The parts of a multipart message are *not* + top-level. Default is true. + + Type *Optional.* The MIME content type, or one of these special values + (case-sensitive): + + "TEXT" means "text/plain" + "BINARY" means "application/octet-stream" + + The default is `"TEXT"'. + + A picture being worth 1000 words (which is of course 2000 bytes, so + it's probably more of an "icon" than a "picture", but I digress...), + here are some examples: + + $msg = MIME::Lite->build( + From => 'yelling@inter.com', + To => 'stocking@fish.net', + Subject => "Hi there!", + Type => 'TEXT', + Encoding => '7bit', + Data => "Just a quick note to say hi!"); + + $msg = MIME::Lite->build( + From => 'dorothy@emerald-city.oz', + To => 'gesundheit@edu.edu.edu', + Subject => "A gif for U" + Type => 'image/gif', + Path => "/home/httpd/logo.gif"); + + $msg = MIME::Lite->build( + From => 'laughing@all.of.us', + To => 'scarlett@fiddle.dee.de', + Subject => "A gzipp'ed tar file", + Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + + To show you what's really going on, that last example could also + have been written: + + $msg = new MIME::Lite; + $msg->build(Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + $msg->add(From => "laughing@all.of.us"); + $msg->add(To => "scarlett@fiddle.dee.de"); + $msg->add(Subject => "A gzipp'ed tar file"); + + Setting/getting headers and attributes + + add TAG,VALUE + *Instance method.* Add field TAG with the given VALUE to the end of + the header. The TAG will be converted to all-lowercase, and the + VALUE will be made "safe" (returns will be given a trailing space). + + Beware: any MIME fields you "add" will override any MIME attributes + I have when it comes time to output those fields. Normally, you will + use this method to add *non-MIME* fields: + + $msg->add("Subject" => "Hi there!"); + + Giving VALUE as an arrayref will cause all those values to be added. + This is only useful for special multiple-valued fields like + "Received": + + $msg->add("Received" => ["here", "there", "everywhere"] + + Giving VALUE as the empty string adds an invisible placeholder to + the header, which can be used to suppress the output of the + "Content-*" fields or the special "MIME-Version" field. When + suppressing fields, you should use replace() instead of add(): + + $msg->replace("Content-disposition" => ""); + + *Note:* add() is probably going to be more efficient than + `replace()', so you're better off using it for most applications if + you are certain that you don't need to delete() the field first. + + *Note:* the name comes from Mail::Header. + + attr ATTR,[VALUE] + *Instance method.* Set MIME attribute ATTR to the string VALUE. ATTR + is converted to all-lowercase. This method is normally used to + set/get MIME attributes: + + $msg->attr("content-type" => "text/html"); + $msg->attr("content-type.charset" => "US-ASCII"); + $msg->attr("content-type.name" => "homepage.html"); + + This would cause the final output to look something like this: + + Content-type: text/html; charset=US-ASCII; name="homepage.html" + + Note that the special empty sub-field tag indicates the anonymous + first sub-field. + + Giving VALUE as undefined will cause the contents of the named + subfield to be deleted. + + Supplying no VALUE argument just returns the attribute's value: + + $type = $msg->attr("content-type"); ### returns "text/html" + $name = $msg->attr("content-type.name"); ### returns "homepage.html" + + delete TAG + *Instance method.* Delete field TAG with the given VALUE to the end + of the header. The TAG will be converted to all-lowercase. + + $msg->delete("Subject"); + + *Note:* the name comes from Mail::Header. + + field_order FIELD,...FIELD + *Class/instance method.* Change the order in which header fields are + output for this object: + + $msg->field_order('from', 'to', 'content-type', 'subject'); + + When used as a class method, changes the default settings for all + objects: + + MIME::Lite->field_order('from', 'to', 'content-type', 'subject'); + + Case does not matter: all field names will be coerced to lowercase. + In either case, supply the empty array to restore the default + ordering. + + fields + *Instance method.* Return the full header for the object, as a ref + to an array of `[TAG, VALUE]' pairs, where each TAG is all- + lowercase. Note that any fields the user has explicitly set will + override the corresponding MIME fields that we would otherwise + generate. So, don't say... + + $msg->set("Content-type" => "text/html; charset=US-ASCII"); + + unless you want the above value to override the "Content-type" MIME + field that we would normally generate. + + *Note:* I called this "fields" because the header() method of + Mail::Header returns something different, but similar enough to be + confusing. + + You can change the order of the fields: see the header_order entry + elsewhere in this document . You really shouldn't need to do this, + but some people have to deal with broken mailers. + + filename [FILENAME] + *Instance method.* Set the filename which this data will be reported + as. This actually sets both "standard" attributes. + + With no argument, returns the filename as dictated by the content- + disposition. + + get TAG,[INDEX] + *Instance method.* Get the contents of field TAG, which might have + been set with set() or replace(). Returns the text of the field. + + $ml->get('Subject', 0); + + If the optional 0-based INDEX is given, then we return the INDEX'th + occurence of field TAG. Otherwise, we look at the context: In a + scalar context, only the first (0th) occurence of the field is + returned; in an array context, *all* occurences are returned. + + *Warning:* this should only be used with non-MIME fields. Behavior + with MIME fields is TBD, and will raise an exception for now. + + get_length + *Instance method.* Recompute the content length for the message *if + the process is trivial*, setting the "content-length" attribute as a + side-effect: + + $msg->get_length; + + Returns the length, or undefined if not set. + + *Note:* the content length can be difficult to compute, since it + involves assembling the entire encoded body and taking the length of + it (which, in the case of multipart messages, means freezing all the + sub-parts, etc.). + + This method only sets the content length to a defined value if the + message is a singlepart with `"binary"' encoding, *and* the body is + available either in-core or as a simple file. Otherwise, the content + length is set to the undefined value. + + Since content-length is not a standard MIME field anyway (that's + right, kids: it's not in the MIME RFCs, it's an HTTP thing), this + seems pretty fair. + + replace TAG,VALUE + *Instance method.* Delete all occurences of fields named TAG, and + add a new field with the given VALUE. TAG is converted to all- + lowercase. + + Beware the special MIME fields (MIME-version, Content-*): if you + "replace" a MIME field, the replacement text will override the + *actual* MIME attributes when it comes time to output that field. So + normally you use attr() to change MIME fields and add()/replace() to + change *non-MIME* fields: + + $msg->replace("Subject" => "Hi there!"); + + Giving VALUE as the *empty string* will effectively *prevent* that + field from being output. This is the correct way to suppress the + special MIME fields: + + $msg->replace("Content-disposition" => ""); + + Giving VALUE as *undefined* will just cause all explicit values for + TAG to be deleted, without having any new values added. + + *Note:* the name of this method comes from Mail::Header. + + scrub + *Instance method.* This is Alpha code. If you use it, please let me + know how it goes. Recursively goes through the "parts" tree of this + message and tries to find MIME attributes that can be removed. With + an array argument, removes exactly those attributes; e.g.: + + $msg->scrub(['content-disposition', 'content-length']); + + Is the same as recursively doing: + + $msg->replace('Content-disposition' => ''); + $msg->replace('Content-length' => ''); + + Setting/getting message data + + binmode [OVERRIDE] + *Instance method.* With no argument, returns whether or not it + thinks that the data (as given by the "Path" argument of `build()') + should be read using binmode() (for example, when `read_now()' is + invoked). + + The default behavior is that any content type other than `text/*' or + `message/*' is binmode'd; this should in general work fine. + + With a defined argument, this method sets an explicit "override" + value. An undefined argument unsets the override. The new current + value is returned. + + data [DATA] + *Instance method.* Get/set the literal DATA of the message. The DATA + may be either a scalar, or a reference to an array of scalars (which + will simply be joined). + + *Warning:* setting the data causes the "content-length" attribute to + be recomputed (possibly to nothing). + + path [PATH] + Get/set the PATH to the message data. + + *Warning:* setting the path recomputes any existing "content-length" + field, and re-sets the "filename" (to the last element of the path + if it looks like a simple path, and to nothing if not). + + fh [FILEHANDLE] + Get/set the FILEHANDLE which contains the message data. + + Takes a filehandle as an input and stores it in the object. This + routine is similar to path(); one important difference is that no + attempt is made to set the content length. + + resetfh [FILEHANDLE] + Set the current position of the filehandle back to the beginning. + Only applies if you used "FH" in build() or attach() for this + message. + + Returns false if unable to reset the filehandle (since not all + filehandles are seekable). + + read_now + Forces data from the path/filehandle (as specified by `build()') to + be read into core immediately, just as though you had given it + literally with the `Data' keyword. + + Note that the in-core data will always be used if available. + + Be aware that everything is slurped into a giant scalar: you may not + want to use this if sending tar files! The benefit of *not* reading + in the data is that very large files can be handled by this module + if left on disk until the message is output via `print()' or + `print_body()'. + + sign PARAMHASH + Sign the message. This forces the message to be read into core, + after which the signature is appended to it. + + Data As in `build()': the literal signature data. Can be either a scalar + or a ref to an array of scalars. + + Path As in `build()': the path to the file. + + If no arguments are given, the default is: + + Path => "$ENV{HOME}/.signature" + + The content-length is recomputed. + + verify_data + *Instance method.* Verify that all "paths" to attached data exist, + recursively. It might be a good idea for you to do this before a + print(), to prevent accidental partial output if a file might be + missing. Raises exception if any path is not readable. + + Output + + print [OUTHANDLE] + *Instance method.* Print the message to the given output handle, or + to the currently-selected filehandle if none was given. + + All OUTHANDLE has to be is a filehandle (possibly a glob ref), or + any object that responds to a print() message. + + print_body [OUTHANDLE] + *Instance method.* Print the body of a message to the given output + handle, or to the currently-selected filehandle if none was given. + + All OUTHANDLE has to be is a filehandle (possibly a glob ref), or + any object that responds to a print() message. + + Fatal exception raised if unable to open any of the input files, or + if a part contains no data, or if an unsupported encoding is + encountered. + + print_header [OUTHANDLE] + *Instance method.* Print the header of the message to the given + output handle, or to the currently-selected filehandle if none was + given. + + All OUTHANDLE has to be is a filehandle (possibly a glob ref), or + any object that responds to a print() message. + + as_string + *Instance method.* Return the entire message as a string, with a + header and an encoded body. + + body_as_string + *Instance method.* Return the encoded body as a string. This is the + portion after the header and the blank line. + + *Note:* actually prepares the body by "printing" to a scalar. Proof + that you can hand the `print*()' methods any blessed object that + responds to a `print()' message. + + header_as_string + *Instance method.* Return the header as a string. + + Sending + + send + send HOW, HOWARGS... + *Class/instance method.* This is the principal method for sending + mail, and for configuring how mail will be sent. + + *As an instance method* (with no arguments), sends the message by + whatever means has been set up (the default is to use the Unix + "sendmail" program). Returns whatever the mail-handling routine + returns: this should be true on success, false/exception on error: + + $msg = MIME::Lite->new(From=>...); + $msg->send || die "you DON'T have mail!"; + + *As a class method* (with a HOW argument and optional HOWARGS), sets + up how the instance method will work for all objects until further + notice It treats HOW as a facility name, with optional HOWARGS + handled by the facility (and returns the previous HOW and HOWARGS as + an array). There are three facilities: + + "sendmail", ARGS... + Send a message by piping it into the "sendmail" command. Uses + the send_by_sendmail() method, giving it the ARGS. This usage + implements (and deprecates) the `sendmail()' method. + + "smtp", [HOSTNAME] + Send a message by SMTP, using optional HOSTNAME as SMTP-sending + host. Uses the send_by_smtp() method. + + "sub", \&SUBREF, ARGS... + Sends a message MSG by invoking the subroutine SUBREF of your + choosing, with MSG as the first argument, and ARGS following. + + *For example:* let's say you're on an OS which lacks the usual Unix + "sendmail" facility, but you've installed something a lot like it, + and you need to configure your Perl script to use this + "sendmail.exe" program. Do this following in your script's setup: + + MIME::Lite->send('sendmail', "d:\\programs\\sendmail.exe"); + + Then, whenever you need to send a message $msg, just say: + + $msg->send; + + That's it. Now, if you ever move your script to a Unix box, all you + need to do is change that line in the setup and you're done. All of + your $msg->send invocations will work as expected. + + send_by_sendmail SENDMAILCMD + send_by_sendmail PARAM=>VALUE, ... + *Instance method.* Send message via an external "sendmail" program + (this will probably only work out-of-the-box on Unix systems). + + Returns true on success, false or exception on error. + + You can specify the program and all its arguments by giving a single + string, SENDMAILCMD. Nothing fancy is done; the message is simply + piped in. + + However, if your needs are a little more advanced, you can specify + zero or more of the following PARAM/VALUE pairs; a Unix-style, + taint-safe "sendmail" command will be constructed for you: + + Sendmail + Full path to the program to use. Default is "/usr/lib/sendmail". + + BaseArgs + Ref to the basic array of arguments we start with. Default is + `["-t", "-oi", "-oem"]'. + + SetSender + Unless this is *explicitly* given as false, we attempt to + automatically set the `-f' argument to the first address that + can be extracted from the "From:" field of the message (if there + is one). + + *What is the -f, and why do we use it?* Suppose we did *not* use + `-f', and you gave an explicit "From:" field in your message: in + this case, the sendmail "envelope" would indicate the *real* + user your process was running under, as a way of preventing mail + forgery. Using the `-f' switch causes the sender to be set in + the envelope as well. + + *So when would I NOT want to use it?* If sendmail doesn't regard + you as a "trusted" user, it will permit the `-f' but also add an + "X-Authentication-Warning" header to the message to indicate a + forged envelope. To avoid this, you can either (1) have + SetSender be false, or (2) make yourself a trusted user by + adding a `T' configuration command to your *sendmail.cf* file + (e.g.: `Teryq' if the script is running as user "eryq"). + + FromSender + If defined, this is identical to setting SetSender to true, + except that instead of looking at the "From:" field we use the + address given by this option. Thus: + + FromSender => 'me@myhost.com' + + send_by_smtp ARGS... + *Instance method.* Send message via SMTP, using Net::SMTP. The + optional ARGS are sent into Net::SMTP::new(): usually, these are + + MAILHOST, OPTION=>VALUE, ... + + Note that the list of recipients is taken from the "To", "Cc" and + "Bcc" fields. + + Returns true on success, false or exception on error. + + sendmail COMMAND... + *Class method, DEPRECATED.* Declare the sender to be "sendmail", and + set up the "sendmail" command. *You should use send() instead.* + + Miscellaneous + + quiet ONOFF + *Class method.* Suppress/unsuppress all warnings coming from this + module. + + MIME::Lite->quiet(1); ### I know what I'm doing + + I recommend that you include that comment as well. And while you + type it, say it out loud: if it doesn't feel right, then maybe you + should reconsider the whole line. `;-)' + +NOTES + Benign limitations + + This is "lite", after all... + + * There's no parsing. Get MIME-tools if you need to parse MIME messages. + + * MIME::Lite messages are currently *not* interchangeable with either + Mail::Internet or MIME::Entity objects. This is a completely + separate module. + + * A content-length field is only inserted if the encoding is binary, the + message is a singlepart, and all the document data is available at + `build()' time by virtue of residing in a simple path, or in-core. + Since content-length is not a standard MIME field anyway (that's + right, kids: it's not in the MIME RFCs, it's an HTTP thing), this + seems pretty fair. + + * MIME::Lite alone cannot help you lose weight. You must supplement your + use of MIME::Lite with a healthy diet and exercise. + + Cheap and easy mailing + + I thought putting in a default "sendmail" invocation wasn't too bad an + idea, since a lot of Perlers are on UNIX systems. The out-of-the-box + configuration is: + + MIME::Lite->send('sendmail', "/usr/lib/sendmail -t -oi -oem"); + + By the way, these arguments to sendmail are: + + -t Scan message for To:, Cc:, Bcc:, etc. + + -oi Do NOT treat a single "." on a line as a message terminator. + As in, "-oi vey, it truncated my message... why?!" + + -oem On error, mail back the message (I assume to the + appropriate address, given in the header). + When mail returns, circle is complete. Jai Guru Deva -oem. + + Note that these are the same arguments you get if you configure to use + the smarter, taint-safe mailing: + + MIME::Lite->send('sendmail'); + + If you get "X-Authentication-Warning" headers from this, you can forgo + diddling with the envelope by instead specifying: + + MIME::Lite->send('sendmail', SetSender=>0); + + And, if you're not on a Unix system, or if you'd just rather send mail + some other way, there's always: + + MIME::Lite->send('smtp', "smtp.myisp.net"); + + Or you can set up your own subroutine to call. In any case, check out + the send() method. + +WARNINGS + Good-vs-bad email addresses with send_by_smtp() + + If using send_by_smtp(), be aware that you are forcing MIME::Lite to + extract email addresses out of a possible list provided in the `To:', + `Cc:', and `Bcc:' fields. This is tricky stuff, and as such only the + following sorts of addresses will work reliably: + + username + full.name@some.host.com + "Name, Full" <full.name@some.host.com> + + This last form is discouraged because SMTP must be able to get at the + *name* or *name@domain* portion. + + Disclaimer: MIME::Lite was never intended to be a Mail User Agent, so + please don't expect a full implementation of RFC-822. Restrict yourself + to the common forms of Internet addresses described herein, and you + should be fine. If this is not feasible, then consider using MIME::Lite + to *prepare* your message only, and using Net::SMTP explicitly to *send* + your message. + + Formatting of headers delayed until print() + + This class treats a MIME header in the most abstract sense, as being a + collection of high-level attributes. The actual RFC-822-style header + fields are not constructed until it's time to actually print the darn + thing. + + Encoding of data delayed until print() + + When you specify message bodies (in build() or attach()) -- whether by + FH, Data, or Path -- be warned that we don't attempt to open files, read + filehandles, or encode the data until print() is invoked. + + In the past, this created some confusion for users of sendmail who gave + the wrong path to an attachment body, since enough of the print() would + succeed to get the initial part of the message out. Nowadays, + $AUTO_VERIFY is used to spot-check the Paths given before the mail + facility is employed. A whisker slower, but tons safer. + + Note that if you give a message body via FH, and try to print() a + message twice, the second print() will not do the right thing unless you + explicitly rewind the filehandle. + + You can get past these difficulties by using the ReadNow option, + provided that you have enough memory to handle your messages. + + MIME attributes are separate from header fields! + + Important: the MIME attributes are stored and manipulated separately + from the message header fields; when it comes time to print the header + out, *any explicitly-given header fields override the ones that would be + created from the MIME attributes.* That means that this: + + ### DANGER ### DANGER ### DANGER ### DANGER ### DANGER ### + $msg->add("Content-type", "text/html; charset=US-ASCII"); + + will set the exact `"Content-type"' field in the header I write, + *regardless of what the actual MIME attributes are.* + + *This feature is for experienced users only,* as an escape hatch in case + the code that normally formats MIME header fields isn't doing what you + need. And, like any escape hatch, it's got an alarm on it: MIME::Lite + will warn you if you attempt to `set()' or `replace()' any MIME header + field. Use `attr()' instead. + + Beware of lines consisting of a single dot + + Julian Haight noted that MIME::Lite allows you to compose messages with + lines in the body consisting of a single ".". This is true: it should be + completely harmless so long as "sendmail" is used with the -oi option + (see the section on "Cheap and easy mailing"). + + However, I don't know if using Net::SMTP to transfer such a message is + equally safe. Feedback is welcomed. + + My perspective: I don't want to magically diddle with a user's message + unless absolutely positively necessary. Some users may want to send + files with "." alone on a line; my well-meaning tinkering could + seriously harm them. + + Infinite loops may mean tainted data! + + Stefan Sautter noticed a bug in 2.106 where a m//gc match was failing + due to tainted data, leading to an infinite loop inside MIME::Lite. + + I am attempting to correct for this, but be advised that my fix will + silently untaint the data (given the context in which the problem + occurs, this should be benign: I've labelled the source code with + UNTAINT comments for the curious). + + So: don't depend on taint-checking to save you from outputting tainted + data in a message. + +A MIME PRIMER + Content types + + The "Type" parameter of `build()' is a *content type*. This is the + actual type of data you are sending. Generally this is a string of the + form `"majortype/minortype"'. + + Here are the major MIME types. A more-comprehensive listing may be found + in RFC-2046. + + application + Data which does not fit in any of the other categories, particularly + data to be processed by some type of application program. + `application/octet-stream', `application/gzip', + `application/postscript'... + + audio + Audio data. `audio/basic'... + + image + Graphics data. `image/gif', `image/jpeg'... + + message + A message, usually another mail or MIME message. `message/rfc822'... + + multipart + A message containing other messages. `multipart/mixed', + `multipart/alternative'... + + text + Textual data, meant for humans to read. `text/plain', `text/html'... + + video + Video or video+audio data. `video/mpeg'... + + Content transfer encodings + + The "Encoding" parameter of `build()'. This is how the message body is + packaged up for safe transit. + + Here are the 5 major MIME encodings. A more-comprehensive listing may be + found in RFC-2045. + + 7bit + Basically, no *real* encoding is done. However, this label + guarantees that no 8-bit characters are present, and that lines do + not exceed 1000 characters in length. + + 8bit + Basically, no *real* encoding is done. The message might contain 8- + bit characters, but this encoding guarantees that lines do not + exceed 1000 characters in length. + + binary + No encoding is done at all. Message might contain 8-bit characters, + and lines might be longer than 1000 characters long. + + The most liberal, and the least likely to get through mail gateways. + Use sparingly, or (better yet) not at all. + + base64 + Like "uuencode", but very well-defined. This is how you should send + essentially binary information (tar files, GIFs, JPEGs, etc.). + + quoted-printable + Useful for encoding messages which are textual in nature, yet which + contain non-ASCII characters (e.g., Latin-1, Latin-2, or any other + 8-bit alphabet). + +VERSION + $Id: Lite.pm,v 2.108 2001/03/30 06:16:54 eryq Exp $ + +CHANGE LOG + Version 2.108 + New `field_order()' allows you to set the header order, both on a + per-message basis, and package-wide. *Thanks to Thomas Stromberg for + suggesting this.* + + Added code to try and divine "sendmail" path more intelligently. + *Thanks to Slaven Rezic for the suggestion.* + + Version 2.107 (2001/03/27) + Fixed serious bug where tainted data with quoted-printable encoding + was causing infinite loops. The "fix" untaints the data in question, + which is not optimal, but it's probably benign in this case. *Thanks + to Stefan Sautter for tracking this nasty little beast down.* + *Thanks to Larry Geralds for a related patch.* + + "Doctor, O doctor: + it's painful when I do *this* --" + "Simple: don't *do* that." + + Fixed bugs where a non-local `$_' was being modified... again! Will + I never learn? *Thanks to Maarten Koskamp for reporting this.* + + Dollar-underscore + can poison distant waters; + 'local' must it be. + + Fixed buglet in `add()' where all value references were being + treated as arrayrefs, instead of as possibly-self-stringifying + object refs. Now you can send in an object ref as the 2nd argument. + *Thanks to dLux for the bug report.* + + That ref is a string? + Operator overload + has ruined my day. + + Added "Approved" as an acceptable header field for `new()', as per + RFC1036. *Thanks to Thomax for the suggestion regarding MIME-tools.* + + Small improvements to docs to make different uses of attach() and + various arguments clearer. *Thanks to Sven Rassman and Roland Walter + for the suggestions.* + + Version 2.106 (2000/11/21) + Added Alpha version of scrub() to make it easy for people to + suppress the printing of unwanted MIME attributes (like Content- + length). *Thanks to the many people who asked for this.* + + Headers with empty-strings for their values are no longer printed. + This seems sensible, and helps us implement scrub(). + + Version 2.105 (2000/10/14) + The regression-test failure was identified, and it was my fault. + Apparently some of the \-quoting in my "autoloaded" code was making + Perl 5.6 unhappy. For this nesting-related idiocy, a nesting kaiku. + *Thanks to Scott Schwartz for identifying the problem.* + + In a pattern, my + backslash-s dwells peacefully, + unambiguous -- + + but I embed it + in a double-quoted string + doubling the backslash -- + + interpolating + that same double-quoted string + in other patterns -- + + and, worlds within worlds, + I single-quote the function + to autoload it -- + + changing the meaning + of the backslash and the 's'; + and Five-Point-Six growls. + + Version 2.104 (2000/09/28) + Now attempts to load and use Mail::Address for parsing email + addresses *before* falling back to our own method. *Thanks to + numerous people for suggesting this.* + + Parsing addresses + is too damn hard. One last hope: + Let Graham Barr do it! + + For the curious, the version of Mail::Address appears as the "A" + number in the X-Mailer: + + X-Mailer: MIME::Lite 2.104 (A1.15; B2.09; Q2.03) + + Added FromSender option to send_by_sendmail(). *Thanks to Bill + Moseley for suggesting this feature.* + + Version 2.101 (2000/06/06) + Major revision to print_body() and body_as_string() so that "body" + really means "the part after the header", which is what most people + would want in this context. This is not how it was used 1.x, where + "body" only meant "the body of a simple singlepart". Hopefully, this + change will solve many problems and create very few ones. + + Added support for attaching a part to a "message/rfc822", treating + the "message" type as a multipart-like container. + + Now takes care not to include "Bcc:" in header when using + send_by_smtp, as a safety precaution against qmail's behavior. + *Thanks to Tatsuhiko Miyagawa for identifying this problem.* + + Improved efficiency of many stringifying operations by using string- + arrays which are joined, instead of doing multiple appends to a + scalar. + + Cleaned up the "examples" directory. + + Version 1.147 (2000/06/02) + Fixed buglet where lack of Cc:/Bcc: was causing extract_addrs to + emit "undefined variable" warnings. Also, lack of a "To:" field now + causes a croak. *Thanks to David Mitchell for the bug report and + suggested patch.* + + Version 1.146 (2000/05/18) + Fixed bug in parsing of addresses; please read the WARNINGS section + which describes recommended address formats for "To:", "Cc:", etc. + Also added automatic inclusion of a UT "Date:" at top level unless + explicitly told not to. *Thanks to Andy Jacobs for the bug report + and the suggestion.* + + Version 1.145 (2000/05/06) + Fixed bug in encode_7bit(): a lingering `/e' modifier was removed. + *Thanks to Michael A. Chase for the patch.* + + Version 1.142 (2000/05/02) + Added new, taint-safe invocation of "sendmail", one which also sets + up the `-f' option. Unfortunately, I couldn't make this automatic: + the change could have broken a lot of code out there which used + send_by_sendmail() with unusual "sendmail" variants. So you'll have + to configure "send" to use the new mechanism: + + MIME::Lite->send('sendmail'); ### no args! + + *Thanks to Jeremy Howard for suggesting these features.* + + Version 1.140 (2000/04/27) + Fixed bug in support for "To", "Cc", and "Bcc" in send_by_smtp(): + multiple (comma-separated) addresses should now work fine. We try + real hard to extract addresses from the flat text strings. *Thanks + to John Mason for motivating this change.* + + Added automatic verification that attached data files exist, done + immediately before the "send" action is invoked. To turn this off, + set $MIME::Lite::AUTO_VERIFY to false. + + Version 1.137 (2000/03/22) + Added support for "Cc" and "Bcc" in send_by_smtp(). To turn this + off, set $MIME::Lite::AUTO_CC to false. *Thanks to Lucas Maneos for + the patch, and tons of others for the suggestion.* + + Chooses a better default content-transfer-encoding if the content- + type is "image/*", "audio/*", etc. To turn this off, set + $MIME::Lite::AUTO_ENCODE to false. *Thanks to many folks for the + suggestion.* + + Fixed bug in QP-encoding where a non-local `$_' was being modified. + *Thanks to Jochen Stenzel for finding this very obscure bug!* + + Removed references to `$`', `$'', and `$&' (bad variables which slow + things down). + + Added an example of how to send HTML files with enclosed in-line + images, per popular demand. + + Version 1.133 (1999/04/17) + Fixed bug in "Data" handling: arrayrefs were not being handled + properly. + + Version 1.130 (1998/12/14) + Added much larger and more-flexible send() facility. *Thanks to + Andrew McRae (and Optimation New Zealand Ltd) for the Net::SMTP + interface. Additional thanks to the many folks who requested this + feature.* + + Added get() method for extracting basic attributes. + + New... "t" tests! + + Version 1.124 (1998/11/13) + Folded in filehandle (FH) support in build/attach. *Thanks to Miko + O'Sullivan for the code.* + + Version 1.122 (1998/01/19) + MIME::Base64 and MIME::QuotedPrint are used if available. + + The 7bit encoding no longer does "escapes"; it merely strips 8-bit + characters. + + Version 1.121 (1997/04/08) + Filename attribute is now no longer ignored by build(). *Thanks to + Ian Smith for finding and patching this bug.* + + Version 1.120 (1997/03/29) + Efficiency hack to speed up MIME::Lite::IO_Scalar. *Thanks to David + Aspinwall for the patch.* + + Version 1.116 (1997/03/19) + Small bug in our private copy of encode_base64() was patched. + *Thanks to Andreas Koenig for pointing this out.* + + New, prettier way of specifying mail message headers in `build()'. + + New quiet method to turn off warnings. + + Changed "stringify" methods to more-standard "as_string" methods. + + Version 1.112 (1997/03/06) + Added `read_now()', and `binmode()' method for our non-Unix-using + brethren: file data is now read using binmode() if appropriate. + *Thanks to Xiangzhou Wang for pointing out this bug.* + + Version 1.110 (1997/03/06) + Fixed bug in opening the data filehandle. + + Version 1.102 (1997/03/01) + Initial release. + + Version 1.101 (1997/03/01) + Baseline code. + +TERMS AND CONDITIONS + Copyright (c) 1997 by Eryq. Copyright (c) 1998 by ZeeGee Software Inc. + All rights reserved. This program is free software; you can redistribute + it and/or modify it under the same terms as Perl itself. + + This software comes with NO WARRANTY of any kind. See the COPYING file + in the distribution for details. + +NUTRITIONAL INFORMATION + For some reason, the US FDA says that this is now required by law on any + products that bear the name "Lite"... + + MIME::Lite | + ------------------------------------------------------------ + Serving size: | 1 module + Servings per container: | 1 + Calories: | 0 + Fat: | 0g + Saturated Fat: | 0g + + Warning: for consumption by hardware only! May produce indigestion in + humans if taken internally. + +AUTHOR + Eryq (eryq@zeegee.com). President, ZeeGee Software Inc. + (http://www.zeegee.com). + + Created: 11 December 1996. Ho ho ho. + diff --git a/cpan/dist/MIME-Lite/README.system b/cpan/dist/MIME-Lite/README.system new file mode 100644 index 00000000..ad1fc97c --- /dev/null +++ b/cpan/dist/MIME-Lite/README.system @@ -0,0 +1,8 @@ +DEVELOPMENT SYSTEM: +Linux eryq 2.0.34 #1 Fri May 8 16:05:57 EDT 1998 i586 unknown + +DEVELOPMENT PERL: +This is perl, version 5.005_56 built for i586-linux + +DEVELOPMENT DATE: +Fri Mar 30 01:17:47 EST 2001 diff --git a/cpan/dist/MIME-Lite/docs/MIME/Lite.pm.html b/cpan/dist/MIME-Lite/docs/MIME/Lite.pm.html new file mode 100644 index 00000000..8c13907e --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/MIME/Lite.pm.html @@ -0,0 +1,1976 @@ +<HTML> +<HEAD> + <TITLE>MIME::Lite</TITLE> +</HEAD> +<BODY + bgcolor="#FFFFFF" link="#CC3366" vlink="#993366" alink="#FF6666"> +<FONT FACE="sans-serif" SIZE=-1><A HREF="http://www.zeegee.com" TARGET="_top"><IMG SRC="icons/zeegee.gif" ALT="ZeeGee Software" ALIGN="RIGHT" BORDER="0"></A><A NAME="__TOP__"><H1>MIME::Lite</H1> +</A><UL> +<LI> <A HREF="#NAME">NAME</A> +<LI> <A HREF="#SYNOPSIS">SYNOPSIS</A> +<LI> <A HREF="#DESCRIPTION">DESCRIPTION</A> +<LI> <A HREF="#MORE_EXAMPLES">MORE EXAMPLES</A> +<UL> +<LI> <A HREF="#Attach_a_GIF_to_a_text_message">Attach a GIF to a text message</A> +<LI> <A HREF="#Attach_a_pre-prepared_part_allows_fine-tuning">Attach a pre-prepared part (allows fine-tuning):</A> +<LI> <A HREF="#Send_an_HTML_document_with_images_included">Send an HTML document... with images included!</A> +<LI> <A HREF="#Output_a_message_to_a_filehandle">Output a message to a filehandle</A> +<LI> <A HREF="#Get_a_message_as_a_string">Get a message as a string</A> +<LI> <A HREF="#Change_how_messages_are_sent">Change how messages are sent</A> +</UL> +<LI> <A HREF="#FAQ">FAQ</A> +<UL> +<LI> <A HREF="#How_do_I_prevent_Content_headers_from_showing_up_in_my_mail_reader">How do I prevent "Content" headers from showing up in my mail reader?</A> +<LI> <A HREF="#How_do_I_give_my_attachment_a_different_recommended_filename">How do I give my attachment a [different] recommended filename?</A> +</UL> +<LI> <A HREF="#PUBLIC_INTERFACE">PUBLIC INTERFACE</A> +<UL> +<LI> <A HREF="#Global_configuration">Global configuration</A> +<LI> <A HREF="#Construction">Construction</A> +<LI> <A HREF="#Setting_getting_headers_and_attributes">Setting/getting headers and attributes</A> +<LI> <A HREF="#Setting_getting_message_data">Setting/getting message data</A> +<LI> <A HREF="#Output">Output</A> +<LI> <A HREF="#Sending">Sending</A> +<LI> <A HREF="#Miscellaneous">Miscellaneous</A> +</UL> +<LI> <A HREF="#NOTES">NOTES</A> +<UL> +<LI> <A HREF="#Benign_limitations">Benign limitations</A> +<LI> <A HREF="#Cheap_and_easy_mailing">Cheap and easy mailing</A> +</UL> +<LI> <A HREF="#WARNINGS">WARNINGS</A> +<UL> +<LI> <A HREF="#Good-vs-bad_email_addresses_with_send_by_smtp">Good-vs-bad email addresses with send_by_smtp()</A> +<LI> <A HREF="#Formatting_of_headers_delayed_until_print">Formatting of headers delayed until print()</A> +<LI> <A HREF="#Encoding_of_data_delayed_until_print">Encoding of data delayed until print()</A> +<LI> <A HREF="#MIME_attributes_are_separate_from_header_fields">MIME attributes are separate from header fields!</A> +<LI> <A HREF="#Beware_of_lines_consisting_of_a_single_dot">Beware of lines consisting of a single dot</A> +<LI> <A HREF="#Infinite_loops_may_mean_tainted_data">Infinite loops may mean tainted data!</A> +</UL> +<LI> <A HREF="#A_MIME_PRIMER">A MIME PRIMER</A> +<UL> +<LI> <A HREF="#Content_types">Content types</A> +<LI> <A HREF="#Content_transfer_encodings">Content transfer encodings</A> +</UL> +<LI> <A HREF="#VERSION">VERSION</A> +<LI> <A HREF="#CHANGE_LOG">CHANGE LOG</A> +<LI> <A HREF="#TERMS_AND_CONDITIONS">TERMS AND CONDITIONS</A> +<LI> <A HREF="#NUTRITIONAL_INFORMATION">NUTRITIONAL INFORMATION</A> +<LI> <A HREF="#AUTHOR">AUTHOR</A> +</UL> +</A> + +<P><HR> +<A NAME="NAME"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NAME</H2></A> + + +<P>MIME::Lite - low-calorie MIME generator + + + +<P><HR> +<A NAME="SYNOPSIS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> SYNOPSIS</H2></A> + +<FONT SIZE=3 FACE="courier"><PRE> + use MIME::Lite; + +Create a single-part message: +</PRE></FONT> +<FONT SIZE=3 FACE="courier"><PRE> + ### Create a new single-part message, to send a GIF file: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'Helloooooo, nurse!', + Type =>'image/gif', + Encoding =>'base64', + Path =>'hellonurse.gif' + ); +</PRE></FONT> + +<P>Create a multipart message (i.e., one with attachments): + +<FONT SIZE=3 FACE="courier"><PRE> + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'multipart/mixed' + ); + + ### Add parts (each "attach" has same arguments as "new"): + $msg->attach(Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif', + Disposition => 'attachment' + ); +</PRE></FONT> + +<P>Output a message: + +<FONT SIZE=3 FACE="courier"><PRE> + ### Format as a string: + $str = $msg->as_string; + + ### Print to a filehandle (say, a "sendmail" stream): + $msg->print(\*SENDMAIL); +</PRE></FONT> + +<P>Send a message: + +<FONT SIZE=3 FACE="courier"><PRE> + ### Send in the "best" way (the default is to use "sendmail"): + $msg->send; + +</PRE></FONT> + + +<P><HR> +<A NAME="DESCRIPTION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> DESCRIPTION</H2></A> + + +<P>In the never-ending quest for great taste with fewer calories, +we proudly present: <I>MIME::Lite</I>. + + +<P>MIME::Lite is intended as a simple, standalone module for generating +(not parsing!) MIME messages... specifically, it allows you to +output a simple, decent single- or multi-part message with text or binary +attachments. It does not require that you have the Mail:: or MIME:: +modules installed. + + +<P>You can specify each message part as either the literal data itself (in +a scalar or array), or as a string which can be given to open() to get +a readable filehandle (e.g., "<filename" or "somecommand|"). + + +<P>You don't need to worry about encoding your message data: +this module will do that for you. It handles the 5 standard MIME encodings. + + +<P>If you need more sophisticated behavior, please get the MIME-tools +package instead. I will be more likely to add stuff to that toolkit +over this one. + + + +<P><HR> +<A NAME="MORE_EXAMPLES"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> MORE EXAMPLES</H2></A> + + + +<P><HR> +<A NAME="Attach_a_GIF_to_a_text_message"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Attach a GIF to a text message</H3></A> + + +<P>This will create a multipart message exactly as above, but using the +"attach to singlepart" hack: + +<FONT SIZE=3 FACE="courier"><PRE> + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + + ### Attach a part: + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif' + ); +</PRE></FONT> + + +<P><HR> +<A NAME="Attach_a_pre-prepared_part_allows_fine-tuning"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Attach a pre-prepared part (allows fine-tuning):</H3></A> + +<FONT SIZE=3 FACE="courier"><PRE> + $part = MIME::Lite->new( + Type =>'text/html', + Data =>'<H1>Hello</H1>', + ); + $part->attr('content-type.charset' => 'UTF8'); + $part->add('X-Comment' => 'A message for you'); + $msg->attach($part); +</PRE></FONT> + + +<P><HR> +<A NAME="Send_an_HTML_document_with_images_included"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Send an HTML document... with images included!</H3></A> + +<FONT SIZE=3 FACE="courier"><PRE> + $msg = MIME::Lite->new( + To =>'you@yourhost.com', + Subject =>'HTML with in-line images!', + Type =>'multipart/related' + ); + $msg->attach(Type => 'text/html', + Data => qq{ <body> + Here's <i>my</i> image: + <img src="cid:myimage.gif"> + </body> } + ); + $msg->attach(Type => 'image/gif', + Id => 'myimage.gif', + Path => '/path/to/somefile.gif', + ); + $msg->send(); +</PRE></FONT> + + +<P><HR> +<A NAME="Output_a_message_to_a_filehandle"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Output a message to a filehandle</H3></A> + +<FONT SIZE=3 FACE="courier"><PRE> + ### Write it to a filehandle: + $msg->print(\*STDOUT); + + ### Write just the header: + $msg->print_header(\*STDOUT); + + ### Write just the encoded body: + $msg->print_body(\*STDOUT); +</PRE></FONT> + + +<P><HR> +<A NAME="Get_a_message_as_a_string"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Get a message as a string</H3></A> + +<FONT SIZE=3 FACE="courier"><PRE> + ### Get entire message as a string: + $str = $msg->as_string; + + ### Get just the header: + $str = $msg->header_as_string; + + ### Get just the encoded body: + $str = $msg->body_as_string; +</PRE></FONT> + + +<P><HR> +<A NAME="Change_how_messages_are_sent"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Change how messages are sent</H3></A> + +<FONT SIZE=3 FACE="courier"><PRE> + ### Do something like this in your 'main': + if ($I_DONT_HAVE_SENDMAIL) { + MIME::Lite->send('smtp', "smtp.myisp.net", Timeout=>60); + } + + ### Now this will do the right thing: + $msg->send; ### will now use Net::SMTP as shown above +</PRE></FONT> + + +<P><HR> +<A NAME="FAQ"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> FAQ</H2></A> + + + +<P><HR> +<A NAME="How_do_I_prevent_Content_headers_from_showing_up_in_my_mail_reader"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> How do I prevent "Content" headers from showing up in my mail reader?</H3></A> + + +<P>Apparently, some people are using mail readers which display the MIME +headers like "Content-disposition", and they want MIME::Lite not +to generate them "because they look ugly". + + +<P>Sigh. + + +<P>Y'know, kids, those headers aren't just there for cosmetic purposes. +They help ensure that the message is <I>understood</I> correctly by mail +readers. But okay, you asked for it, you got it... +here's how you can suppress the standard MIME headers. +Before you send the message, do this: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->scrub; +</PRE></FONT> + +<P>You can scrub() any part of a multipart message independently; +just be aware that it works recursively. Before you scrub, +note the rules that I follow: + + + +<DL> +<P><DT><B><A NAME="item:Content-type">Content-type</A></B></DT> +<DD> +You can safely scrub the "content-type" attribute if, and only if, +the part is of type "text/plain" with charset "us-ascii". + +<P><DT><B><A NAME="item:Content-transfer-encoding">Content-transfer-encoding</A></B></DT> +<DD> +You can safely scrub the "content-transfer-encoding" attribute +if, and only if, the part uses "7bit", "8bit", or "binary" encoding. +You are far better off doing this if your lines are under 1000 +characters. Generally, that means you <I>can</I> scrub it for plain +text, and you can <I>not</I> scrub this for images, etc. + +<P><DT><B><A NAME="item:Content-disposition">Content-disposition</A></B></DT> +<DD> +You can safely scrub the "content-disposition" attribute +if you trust the mail reader to do the right thing when it decides +whether to show an attachment inline or as a link. Be aware +that scrubbing both the content-disposition and the content-type +means that there is no way to "recommend" a filename for the attachment! + + +<P><B>Note:</B> there are reports of brain-dead MUAs out there that +do the wrong thing if you <I>provide</I> the content-disposition. +If your attachments keep showing up inline or vice-versa, +try scrubbing this attribute. + +<P><DT><B><A NAME="item:Content-length">Content-length</A></B></DT> +<DD> +You can always scrub "content-length" safely. + +</DL> + + + +<P><HR> +<A NAME="How_do_I_give_my_attachment_a_different_recommended_filename"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> How do I give my attachment a [different] recommended filename?</H3></A> + + +<P>By using the Filename option (which is different from Path!): + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->attach(Type => "image/gif", + Path => "/here/is/the/real/file.GIF", + Filename => "logo.gif"); +</PRE></FONT> + +<P>You should <I>not</I> put path information in the Filename. + + + +<P><HR> +<A NAME="PUBLIC_INTERFACE"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> PUBLIC INTERFACE</H2></A> + + + +<P><HR> +<A NAME="Global_configuration"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Global configuration</H3></A> + + +<P>To alter the way the entire module behaves, you have the following +methods/options: + + + +<DL> +<P><DT><B><A NAME="item:MIME_Lite-_header_order">MIME::Lite->header_order()</A></B></DT> +<DD> +When used as a <A HREF="#item:send">classmethod</A>, this changes the default +order in which headers are output for <I>all</I> messages. + +<P><DT><B><A NAME="item:MIME_Lite-_quiet">MIME::Lite->quiet()</A></B></DT> +<DD> +This <A HREF="#item:quiet">classmethod</A> can be used to suppress/unsuppress +all warnings coming from this module. + +<P><DT><B><A NAME="item:MIME_Lite-_send">MIME::Lite->send()</A></B></DT> +<DD> +When used as a <A HREF="#item:send">classmethod</A>, this can be used to specify +a different default mechanism for sending message. +The initial default is: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send("sendmail", "/usr/lib/sendmail -t -oi -oem"); +</PRE></FONT> + +<P>However, you should consider the similar but smarter and taint-safe variant: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send("sendmail"); +</PRE></FONT> + +<P>Or, for non-Unix users: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send("smtp"); +</PRE></FONT> +<P><DT><B><A NAME="item:MIME_Lite_PARANOID">$MIME::Lite::PARANOID</A></B></DT> +<DD> +If true, we won't attempt to use MIME::Base64/MIME::QuotedPrint, even +if they're available. +Default is <B>false</B>. + +<P><DT><B><A NAME="item:MIME_Lite_AUTO_ENCODE">$MIME::Lite::AUTO_ENCODE</A></B></DT> +<DD> +If true, automatically choose the encoding from the content type. +Default is <B>true</B>. + +<P><DT><B><A NAME="item:MIME_Lite_AUTO_CC">$MIME::Lite::AUTO_CC</A></B></DT> +<DD> +If true, automatically send to the Cc/Bcc addresses for send_by_smtp(). +Default is <B>true</B>. + +<P><DT><B><A NAME="item:MIME_Lite_AUTO_VERIFY">$MIME::Lite::AUTO_VERIFY</A></B></DT> +<DD> +If true, check paths to attachments right before printing, raising an exception +if any path is unreadable. +Default is <B>true</B>. + +</DL> + + + +<P><HR> +<A NAME="Construction"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Construction</H3></A> + + + +<DL> +<P><DT><B><A NAME="item:new">new [PARAMHASH]</A></B></DT> +<DD> +<I>Class method, constructor.</I> +Create a new message object. + + +<P>If any arguments are given, they are passed into <CODE>build()</CODE>; otherwise, +just the empty object is created. + +<P><DT><B><A NAME="item:attach">attach PART</A></B></DT> +<DD> +<P><DT><B><A NAME="item:attach">attach PARAMHASH...</A></B></DT> +<DD> +<I>Instance method.</I> +Add a new part to this message, and return the new part. + + +<P>If you supply a single PART argument, it will be regarded +as a MIME::Lite object to be attached. Otherwise, this +method assumes that you are giving in the pairs of a PARAMHASH +which will be sent into <CODE>new()</CODE> to create the new part. + + +<P>One of the possibly-quite-useful hacks thrown into this is the +"attach-to-singlepart" hack: if you attempt to attach a part (let's +call it "part 1") to a message that doesn't have a content-type +of "multipart" or "message", the following happens: + + + +<UL> +<P><LI> +<P>A new part (call it "part 0") is made. + +<P><LI> +<P>The MIME attributes and data (but <I>not</I> the other headers) +are cut from the "self" message, and pasted into "part 0". + +<P><LI> +<P>The "self" is turned into a "multipart/mixed" message. + +<P><LI> +<P>The new "part 0" is added to the "self", and <I>then</I> "part 1" is added. + +</UL> + + +<P>One of the nice side-effects is that you can create a text message +and then add zero or more attachments to it, much in the same way +that a user agent like Netscape allows you to do. + +<P><DT><B><A NAME="item:build">build [PARAMHASH]</A></B></DT> +<DD> +<I>Class/instance method, initializer.</I> +Create (or initialize) a MIME message object. +Normally, you'll use the following keys in PARAMHASH: + +<P><UL><LI> Data, FH, or Path (either one of these, or none if multipart)<LI> Type (e.g., "image/jpeg")<LI> From, To, and Subject (if this is the "top level" of a message)</UL> +<P>The PARAMHASH can contain the following keys: + + + +<DL> +<P><DT><B><A NAME="item:fieldname">(fieldname)</A></B></DT> +<DD> +Any field you want placed in the message header, taken from the +standard list of header fields (you don't need to worry about case): + +<FONT SIZE=3 FACE="courier"><PRE> + Approved Encrypted Received Sender + Bcc From References Subject + Cc Keywords Reply-To To + Comments Message-ID Resent-* X-* + Content-* MIME-Version Return-Path + Date Organization +</PRE></FONT> + +<P>To give experienced users some veto power, these fields will be set +<I>after</I> the ones I set... so be careful: <I>don't set any MIME fields</I> +(like <CODE>Content-type</CODE>) unless you know what you're doing! + + +<P>To specify a fieldname that's <I>not</I> in the above list, even one that's +identical to an option below, just give it with a trailing <CODE>":"</CODE>, +like <CODE>"My-field:"</CODE>. When in doubt, that <I>always</I> signals a mail +field (and it sort of looks like one too). + +<P><DT><B><A NAME="item:Data">Data</A></B></DT> +<DD> +<I>Alternative to "Path" or "FH".</I> +The actual message data. This may be a scalar or a ref to an array of +strings; if the latter, the message consists of a simple concatenation +of all the strings in the array. + +<P><DT><B><A NAME="item:Datestamp">Datestamp</A></B></DT> +<DD> +<I>Optional.</I> +If given true (or omitted), we force the creation of a <CODE>Date:</CODE> field +stamped with the current date/time if this is a top-level message. +You may want this if using <A HREF="#item:send_by_smtp">send_by_smtp()</A>. +If you don't want this to be done, either provide your own Date +or explicitly set this to false. + +<P><DT><B><A NAME="item:Disposition">Disposition</A></B></DT> +<DD> +<I>Optional.</I> +The content disposition, <CODE>"inline"</CODE> or <CODE>"attachment"</CODE>. +The default is <CODE>"inline"</CODE>. + +<P><DT><B><A NAME="item:Encoding">Encoding</A></B></DT> +<DD> +<I>Optional.</I> +The content transfer encoding that should be used to encode your data: + +<P><TABLE CELLPADDING=4 CELLSPACING=0 BORDER=1 ALIGN=CENTER BGCOLOR=#EEEEEE><TR><TH BGCOLOR=#AA0055 ALIGN=LEFT><FONT SIZE=2 COLOR=#FFFFFF FACE="sans-serif">Use encoding: </FONT></TH BGCOLOR=#AA0055><TH BGCOLOR=#AA0055 ALIGN=LEFT><FONT SIZE=2 COLOR=#FFFFFF FACE="sans-serif">If your message contains: </FONT></TH BGCOLOR=#AA0055></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">7bit</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Only 7-bit text, all lines <1000 characters</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">8bit</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">8-bit text, all lines <1000 characters</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">quoted-printable</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">8-bit text or long lines (more reliable than "8bit")</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">base64</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Largely non-textual data: a GIF, a tar file, etc.</FONT></TD></TR></TABLE> +<P>The default is taken from the Type; generally it is "binary" (no +encoding) for text/*, message/*, and multipart/*, and "base64" for +everything else. A value of <CODE>"binary"</CODE> is generally <I>not</I> suitable +for sending anything but ASCII text files with lines under 1000 +characters, so consider using one of the other values instead. + + +<P>In the case of "7bit"/"8bit", long lines are automatically chopped to +legal length; in the case of "7bit", all 8-bit characters are +automatically <I>removed</I>. This may not be what you want, so pick your +encoding well! For more info, see <A HREF="#A_MIME_PRIMER">A MIME PRIMER</A>. + +<P><DT><B><A NAME="item:FH">FH</A></B></DT> +<DD> +<I>Alternative to "Data" or "Path".</I> +Filehandle containing the data, opened for reading. +See "ReadNow" also. + +<P><DT><B><A NAME="item:Filename">Filename</A></B></DT> +<DD> +<I>Optional.</I> +The name of the attachment. You can use this to supply a +recommended filename for the end-user who is saving the attachment +to disk. You only need this if the filename at the end of the +"Path" is inadequate, or if you're using "Data" instead of "Path". +You should <I>not</I> put path information in here (e.g., no "/" +or "\" or ":" characters should be used). + +<P><DT><B><A NAME="item:Id">Id</A></B></DT> +<DD> +<I>Optional.</I> +Same as setting "content-id". + +<P><DT><B><A NAME="item:Length">Length</A></B></DT> +<DD> +<I>Optional.</I> +Set the content length explicitly. Normally, this header is automatically +computed, but only under certain circumstances (see <A HREF="#Limitations">Limitations</A>). + +<P><DT><B><A NAME="item:Path">Path</A></B></DT> +<DD> +<I>Alternative to "Data" or "FH".</I> +Path to a file containing the data... actually, it can be any open()able +expression. If it looks like a path, the last element will automatically +be treated as the filename. +See "ReadNow" also. + +<P><DT><B><A NAME="item:ReadNow">ReadNow</A></B></DT> +<DD> +<I>Optional, for use with "Path".</I> +If true, will open the path and slurp the contents into core now. +This is useful if the Path points to a command and you don't want +to run the command over and over if outputting the message several +times. <B>Fatal exception</B> raised if the open fails. + +<P><DT><B><A NAME="item:Top">Top</A></B></DT> +<DD> +<I>Optional.</I> +If defined, indicates whether or not this is a "top-level" MIME message. +The parts of a multipart message are <I>not</I> top-level. +Default is true. + +<P><DT><B><A NAME="item:Type">Type</A></B></DT> +<DD> +<I>Optional.</I> +The MIME content type, or one of these special values (case-sensitive): + +<FONT SIZE=3 FACE="courier"><PRE> + "TEXT" means "text/plain" + "BINARY" means "application/octet-stream" +</PRE></FONT> + +<P>The default is <CODE>"TEXT"</CODE>. + +</DL> + + +<P>A picture being worth 1000 words (which +is of course 2000 bytes, so it's probably more of an "icon" than a "picture", +but I digress...), here are some examples: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg = MIME::Lite->build( + From => 'yelling@inter.com', + To => 'stocking@fish.net', + Subject => "Hi there!", + Type => 'TEXT', + Encoding => '7bit', + Data => "Just a quick note to say hi!"); + + $msg = MIME::Lite->build( + From => 'dorothy@emerald-city.oz', + To => 'gesundheit@edu.edu.edu', + Subject => "A gif for U" + Type => 'image/gif', + Path => "/home/httpd/logo.gif"); + + $msg = MIME::Lite->build( + From => 'laughing@all.of.us', + To => 'scarlett@fiddle.dee.de', + Subject => "A gzipp'ed tar file", + Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); +</PRE></FONT> + +<P>To show you what's really going on, that last example could also +have been written: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg = new MIME::Lite; + $msg->build(Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + $msg->add(From => "laughing@all.of.us"); + $msg->add(To => "scarlett@fiddle.dee.de"); + $msg->add(Subject => "A gzipp'ed tar file"); +</PRE></FONT> +</DL> + + + +<P><HR> +<A NAME="Setting_getting_headers_and_attributes"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Setting/getting headers and attributes</H3></A> + + + +<DL> +<P><DT><B><A NAME="item:add">add TAG,VALUE</A></B></DT> +<DD> +<I>Instance method.</I> +Add field TAG with the given VALUE to the end of the header. +The TAG will be converted to all-lowercase, and the VALUE +will be made "safe" (returns will be given a trailing space). + + +<P><B>Beware:</B> any MIME fields you "add" will override any MIME +attributes I have when it comes time to output those fields. +Normally, you will use this method to add <I>non-MIME</I> fields: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->add("Subject" => "Hi there!"); +</PRE></FONT> + +<P>Giving VALUE as an arrayref will cause all those values to be added. +This is only useful for special multiple-valued fields like "Received": + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->add("Received" => ["here", "there", "everywhere"] +</PRE></FONT> + +<P>Giving VALUE as the empty string adds an invisible placeholder +to the header, which can be used to suppress the output of +the "Content-*" fields or the special "MIME-Version" field. +When suppressing fields, you should use replace() instead of add(): + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->replace("Content-disposition" => ""); +</PRE></FONT> + +<P><I>Note:</I> add() is probably going to be more efficient than <CODE>replace()</CODE>, +so you're better off using it for most applications if you are +certain that you don't need to delete() the field first. + + +<P><I>Note:</I> the name comes from Mail::Header. + +<P><DT><B><A NAME="item:attr">attr ATTR,[VALUE]</A></B></DT> +<DD> +<I>Instance method.</I> +Set MIME attribute ATTR to the string VALUE. +ATTR is converted to all-lowercase. +This method is normally used to set/get MIME attributes: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->attr("content-type" => "text/html"); + $msg->attr("content-type.charset" => "US-ASCII"); + $msg->attr("content-type.name" => "homepage.html"); +</PRE></FONT> + +<P>This would cause the final output to look something like this: + +<FONT SIZE=3 FACE="courier"><PRE> + Content-type: text/html; charset=US-ASCII; name="homepage.html" +</PRE></FONT> + +<P>Note that the special empty sub-field tag indicates the anonymous +first sub-field. + + +<P>Giving VALUE as undefined will cause the contents of the named +subfield to be deleted. + + +<P>Supplying no VALUE argument just returns the attribute's value: + +<FONT SIZE=3 FACE="courier"><PRE> + $type = $msg->attr("content-type"); ### returns "text/html" + $name = $msg->attr("content-type.name"); ### returns "homepage.html" +</PRE></FONT> +<P><DT><B><A NAME="item:delete">delete TAG</A></B></DT> +<DD> +<I>Instance method.</I> +Delete field TAG with the given VALUE to the end of the header. +The TAG will be converted to all-lowercase. + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->delete("Subject"); +</PRE></FONT> + +<P><I>Note:</I> the name comes from Mail::Header. + +<P><DT><B><A NAME="item:field_order">field_order FIELD,...FIELD</A></B></DT> +<DD> +<I>Class/instance method.</I> +Change the order in which header fields are output for this object: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->field_order('from', 'to', 'content-type', 'subject'); +</PRE></FONT> + +<P>When used as a class method, changes the default settings for +all objects: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->field_order('from', 'to', 'content-type', 'subject'); +</PRE></FONT> + +<P>Case does not matter: all field names will be coerced to lowercase. +In either case, supply the empty array to restore the default ordering. + +<P><DT><B><A NAME="item:fields">fields</A></B></DT> +<DD> +<I>Instance method.</I> +Return the full header for the object, as a ref to an array +of <CODE>[TAG, VALUE]</CODE> pairs, where each TAG is all-lowercase. +Note that any fields the user has explicitly set will override the +corresponding MIME fields that we would otherwise generate. +So, don't say... + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->set("Content-type" => "text/html; charset=US-ASCII"); +</PRE></FONT> + +<P>unless you want the above value to override the "Content-type" +MIME field that we would normally generate. + + +<P><I>Note:</I> I called this "fields" because the header() method of +Mail::Header returns something different, but similar enough to +be confusing. + + +<P>You can change the order of the fields: see <A HREF="#item:header_order">header_order</A>. +You really shouldn't need to do this, but some people have to +deal with broken mailers. + +<P><DT><B><A NAME="item:filename">filename [FILENAME]</A></B></DT> +<DD> +<I>Instance method.</I> +Set the filename which this data will be reported as. +This actually sets both "standard" attributes. + + +<P>With no argument, returns the filename as dictated by the +content-disposition. + +<P><DT><B><A NAME="item:get">get TAG,[INDEX]</A></B></DT> +<DD> +<I>Instance method.</I> +Get the contents of field TAG, which might have been set +with set() or replace(). Returns the text of the field. + +<FONT SIZE=3 FACE="courier"><PRE> + $ml->get('Subject', 0); +</PRE></FONT> + +<P>If the optional 0-based INDEX is given, then we return the INDEX'th +occurence of field TAG. Otherwise, we look at the context: +In a scalar context, only the first (0th) occurence of the +field is returned; in an array context, <I>all</I> occurences are returned. + + +<P><I>Warning:</I> this should only be used with non-MIME fields. +Behavior with MIME fields is TBD, and will raise an exception for now. + +<P><DT><B><A NAME="item:get_length">get_length</A></B></DT> +<DD> +<I>Instance method.</I> +Recompute the content length for the message <I>if the process is trivial</I>, +setting the "content-length" attribute as a side-effect: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->get_length; +</PRE></FONT> + +<P>Returns the length, or undefined if not set. + + +<P><I>Note:</I> the content length can be difficult to compute, since it +involves assembling the entire encoded body and taking the length +of it (which, in the case of multipart messages, means freezing +all the sub-parts, etc.). + + +<P>This method only sets the content length to a defined value if the +message is a singlepart with <CODE>"binary"</CODE> encoding, <I>and</I> the body is +available either in-core or as a simple file. Otherwise, the content +length is set to the undefined value. + + +<P>Since content-length is not a standard MIME field anyway (that's right, kids: +it's not in the MIME RFCs, it's an HTTP thing), this seems pretty fair. + +<P><DT><B><A NAME="item:replace">replace TAG,VALUE</A></B></DT> +<DD> +<I>Instance method.</I> +Delete all occurences of fields named TAG, and add a new +field with the given VALUE. TAG is converted to all-lowercase. + + +<P><B>Beware</B> the special MIME fields (MIME-version, Content-*): +if you "replace" a MIME field, the replacement text will override +the <I>actual</I> MIME attributes when it comes time to output that field. +So normally you use attr() to change MIME fields and add()/replace() to +change <I>non-MIME</I> fields: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->replace("Subject" => "Hi there!"); +</PRE></FONT> + +<P>Giving VALUE as the <I>empty string</I> will effectively <I>prevent</I> that +field from being output. This is the correct way to suppress +the special MIME fields: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->replace("Content-disposition" => ""); +</PRE></FONT> + +<P>Giving VALUE as <I>undefined</I> will just cause all explicit values +for TAG to be deleted, without having any new values added. + + +<P><I>Note:</I> the name of this method comes from Mail::Header. + +<P><DT><B><A NAME="item:scrub">scrub</A></B></DT> +<DD> +<I>Instance method.</I> +<B>This is Alpha code. If you use it, please let me know how it goes.</B> +Recursively goes through the "parts" tree of this message and tries +to find MIME attributes that can be removed. +With an array argument, removes exactly those attributes; e.g.: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->scrub(['content-disposition', 'content-length']); +</PRE></FONT> + +<P>Is the same as recursively doing: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->replace('Content-disposition' => ''); + $msg->replace('Content-length' => ''); +</PRE></FONT> +</DL> + + + +<P><HR> +<A NAME="Setting_getting_message_data"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Setting/getting message data</H3></A> + + + +<DL> +<P><DT><B><A NAME="item:binmode">binmode [OVERRIDE]</A></B></DT> +<DD> +<I>Instance method.</I> +With no argument, returns whether or not it thinks that the data +(as given by the "Path" argument of <CODE>build()</CODE>) should be read using +binmode() (for example, when <CODE>read_now()</CODE> is invoked). + + +<P>The default behavior is that any content type other than +<CODE>text/*</CODE> or <CODE>message/*</CODE> is binmode'd; this should in general work fine. + + +<P>With a defined argument, this method sets an explicit "override" +value. An undefined argument unsets the override. +The new current value is returned. + +<P><DT><B><A NAME="item:data">data [DATA]</A></B></DT> +<DD> +<I>Instance method.</I> +Get/set the literal DATA of the message. The DATA may be +either a scalar, or a reference to an array of scalars (which +will simply be joined). + + +<P><I>Warning:</I> setting the data causes the "content-length" attribute +to be recomputed (possibly to nothing). + +<P><DT><B><A NAME="item:path">path [PATH]</A></B></DT> +<DD> +Get/set the PATH to the message data. + + +<P><I>Warning:</I> setting the path recomputes any existing "content-length" field, +and re-sets the "filename" (to the last element of the path if it +looks like a simple path, and to nothing if not). + +<P><DT><B><A NAME="item:fh">fh [FILEHANDLE]</A></B></DT> +<DD> +Get/set the FILEHANDLE which contains the message data. + + +<P>Takes a filehandle as an input and stores it in the object. +This routine is similar to path(); one important difference is that +no attempt is made to set the content length. + +<P><DT><B><A NAME="item:resetfh">resetfh [FILEHANDLE]</A></B></DT> +<DD> +Set the current position of the filehandle back to the beginning. +Only applies if you used "FH" in build() or attach() for this message. + + +<P>Returns false if unable to reset the filehandle (since not all filehandles +are seekable). + +<P><DT><B><A NAME="item:read_now">read_now</A></B></DT> +<DD> +Forces data from the path/filehandle (as specified by <CODE>build()</CODE>) +to be read into core immediately, just as though you had given it +literally with the <CODE>Data</CODE> keyword. + + +<P>Note that the in-core data will always be used if available. + + +<P>Be aware that everything is slurped into a giant scalar: you may not want +to use this if sending tar files! The benefit of <I>not</I> reading in the data +is that very large files can be handled by this module if left on disk +until the message is output via <CODE>print()</CODE> or <CODE>print_body()</CODE>. + +<P><DT><B><A NAME="item:sign">sign PARAMHASH</A></B></DT> +<DD> +Sign the message. This forces the message to be read into core, +after which the signature is appended to it. + + + +<DL> +<P><DT><B><A NAME="item:Data">Data</A></B></DT> +<DD> +As in <CODE>build()</CODE>: the literal signature data. +Can be either a scalar or a ref to an array of scalars. + +<P><DT><B><A NAME="item:Path">Path</A></B></DT> +<DD> +As in <CODE>build()</CODE>: the path to the file. + +</DL> + + +<P>If no arguments are given, the default is: + +<FONT SIZE=3 FACE="courier"><PRE> + Path => "$ENV{HOME}/.signature" +</PRE></FONT> + +<P>The content-length is recomputed. + +<P><DT><B><A NAME="item:verify_data">verify_data</A></B></DT> +<DD> +<I>Instance method.</I> +Verify that all "paths" to attached data exist, recursively. +It might be a good idea for you to do this before a print(), to +prevent accidental partial output if a file might be missing. +Raises exception if any path is not readable. + +</DL> + + + +<P><HR> +<A NAME="Output"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Output</H3></A> + + + +<DL> +<P><DT><B><A NAME="item:print">print [OUTHANDLE]</A></B></DT> +<DD> +<I>Instance method.</I> +Print the message to the given output handle, or to the currently-selected +filehandle if none was given. + + +<P>All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +<P><DT><B><A NAME="item:print_body">print_body [OUTHANDLE]</A></B></DT> +<DD> +<I>Instance method.</I> +Print the body of a message to the given output handle, or to +the currently-selected filehandle if none was given. + + +<P>All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + + +<P><B>Fatal exception</B> raised if unable to open any of the input files, +or if a part contains no data, or if an unsupported encoding is +encountered. + +<P><DT><B><A NAME="item:print_header">print_header [OUTHANDLE]</A></B></DT> +<DD> +<I>Instance method.</I> +Print the header of the message to the given output handle, +or to the currently-selected filehandle if none was given. + + +<P>All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +<P><DT><B><A NAME="item:as_string">as_string</A></B></DT> +<DD> +<I>Instance method.</I> +Return the entire message as a string, with a header and an encoded body. + +<P><DT><B><A NAME="item:body_as_string">body_as_string</A></B></DT> +<DD> +<I>Instance method.</I> +Return the encoded body as a string. +This is the portion after the header and the blank line. + + +<P><I>Note:</I> actually prepares the body by "printing" to a scalar. +Proof that you can hand the <CODE>print*()</CODE> methods any blessed object +that responds to a <CODE>print()</CODE> message. + +<P><DT><B><A NAME="item:header_as_string">header_as_string</A></B></DT> +<DD> +<I>Instance method.</I> +Return the header as a string. + +</DL> + + + +<P><HR> +<A NAME="Sending"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Sending</H3></A> + + + +<DL> +<P><DT><B><A NAME="item:send">send</A></B></DT> +<DD> +<P><DT><B><A NAME="item:send">send HOW, HOWARGS...</A></B></DT> +<DD> +<I>Class/instance method.</I> +This is the principal method for sending mail, and for configuring +how mail will be sent. + + +<P><I>As an instance method</I> (with no arguments), sends the message by whatever +means has been set up (the default is to use the Unix "sendmail" program). +Returns whatever the mail-handling routine returns: this should be true +on success, false/exception on error: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg = MIME::Lite->new(From=>...); + $msg->send || die "you DON'T have mail!"; +</PRE></FONT> + +<P><I>As a class method</I> (with a HOW argument and optional HOWARGS), sets up +how the instance method will work for all objects until further notice +It treats HOW as a facility name, with optional HOWARGS handled by +the facility (and returns the previous HOW and HOWARGS as an array). +There are three facilities: + + + +<DL> +<P><DT><B><A NAME="item:sendmail_ARGS">"sendmail", ARGS...</A></B></DT> +<DD> +Send a message by piping it into the "sendmail" command. +Uses the <A HREF="#item:send_by_sendmail">send_by_sendmail()</A> method, giving it the ARGS. +This usage implements (and deprecates) the <CODE>sendmail()</CODE> method. + +<P><DT><B><A NAME="item:smtp_HOSTNAME">"smtp", [HOSTNAME]</A></B></DT> +<DD> +Send a message by SMTP, using optional HOSTNAME as SMTP-sending host. +Uses the <A HREF="#item:send_by_smtp">send_by_smtp()</A> method. + +<P><DT><B><A NAME="item:sub_SUBREF_ARGS">"sub", \&SUBREF, ARGS...</A></B></DT> +<DD> +Sends a message MSG by invoking the subroutine SUBREF of your choosing, +with MSG as the first argument, and ARGS following. + +</DL> + + +<P><I>For example:</I> let's say you're on an OS which lacks the usual Unix +"sendmail" facility, but you've installed something a lot like it, and +you need to configure your Perl script to use this "sendmail.exe" program. +Do this following in your script's setup: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send('sendmail', "d:\\programs\\sendmail.exe"); +</PRE></FONT> + +<P>Then, whenever you need to send a message $msg, just say: + +<FONT SIZE=3 FACE="courier"><PRE> + $msg->send; +</PRE></FONT> + +<P>That's it. Now, if you ever move your script to a Unix box, all you +need to do is change that line in the setup and you're done. +All of your $msg->send invocations will work as expected. + +<P><DT><B><A NAME="item:send_by_sendmail">send_by_sendmail SENDMAILCMD</A></B></DT> +<DD> +<P><DT><B><A NAME="item:send_by_sendmail">send_by_sendmail PARAM=>VALUE, ...</A></B></DT> +<DD> +<I>Instance method.</I> +Send message via an external "sendmail" program +(this will probably only work out-of-the-box on Unix systems). + + +<P>Returns true on success, false or exception on error. + + +<P>You can specify the program and all its arguments by giving a single +string, SENDMAILCMD. Nothing fancy is done; the message is simply +piped in. + + +<P>However, if your needs are a little more advanced, you can specify +zero or more of the following PARAM/VALUE pairs; a Unix-style, +taint-safe "sendmail" command will be constructed for you: + + + +<DL> +<P><DT><B><A NAME="item:Sendmail">Sendmail</A></B></DT> +<DD> +Full path to the program to use. +Default is "/usr/lib/sendmail". + +<P><DT><B><A NAME="item:BaseArgs">BaseArgs</A></B></DT> +<DD> +Ref to the basic array of arguments we start with. +Default is <CODE>["-t", "-oi", "-oem"]</CODE>. + +<P><DT><B><A NAME="item:SetSender">SetSender</A></B></DT> +<DD> +Unless this is <I>explicitly</I> given as false, we attempt to automatically +set the <CODE>-f</CODE> argument to the first address that can be extracted from +the "From:" field of the message (if there is one). + + +<P><I>What is the -f, and why do we use it?</I> +Suppose we did <I>not</I> use <CODE>-f</CODE>, and you gave an explicit "From:" +field in your message: in this case, the sendmail "envelope" would +indicate the <I>real</I> user your process was running under, as a way +of preventing mail forgery. Using the <CODE>-f</CODE> switch causes the sender +to be set in the envelope as well. + + +<P><I>So when would I NOT want to use it?</I> +If sendmail doesn't regard you as a "trusted" user, it will permit +the <CODE>-f</CODE> but also add an "X-Authentication-Warning" header to the message +to indicate a forged envelope. To avoid this, you can either +(1) have SetSender be false, or +(2) make yourself a trusted user by adding a <CODE>T</CODE> configuration + command to your <I>sendmail.cf</I> file + (e.g.: <CODE>Teryq</CODE> if the script is running as user "eryq"). + +<P><DT><B><A NAME="item:FromSender">FromSender</A></B></DT> +<DD> +If defined, this is identical to setting SetSender to true, +except that instead of looking at the "From:" field we use +the address given by this option. +Thus: + +<FONT SIZE=3 FACE="courier"><PRE> + FromSender => 'me@myhost.com' +</PRE></FONT> +</DL> + +<P><DT><B><A NAME="item:send_by_smtp">send_by_smtp ARGS...</A></B></DT> +<DD> +<I>Instance method.</I> +Send message via SMTP, using Net::SMTP. +The optional ARGS are sent into Net::SMTP::new(): usually, these are + +<FONT SIZE=3 FACE="courier"><PRE> + MAILHOST, OPTION=>VALUE, ... +</PRE></FONT> + +<P>Note that the list of recipients is taken from the +"To", "Cc" and "Bcc" fields. + + +<P>Returns true on success, false or exception on error. + +<P><DT><B><A NAME="item:sendmail">sendmail COMMAND...</A></B></DT> +<DD> +<I>Class method, DEPRECATED.</I> +Declare the sender to be "sendmail", and set up the "sendmail" command. +<I>You should use send() instead.</I> + +</DL> + + + +<P><HR> +<A NAME="Miscellaneous"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Miscellaneous</H3></A> + + + +<DL> +<P><DT><B><A NAME="item:quiet">quiet ONOFF</A></B></DT> +<DD> +<I>Class method.</I> +Suppress/unsuppress all warnings coming from this module. + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->quiet(1); ### I know what I'm doing +</PRE></FONT> + +<P>I recommend that you include that comment as well. And while +you type it, say it out loud: if it doesn't feel right, then maybe +you should reconsider the whole line. <CODE>;-)</CODE> + +</DL> + + + +<P><HR> +<A NAME="NOTES"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NOTES</H2></A> + + + +<P><HR> +<A NAME="Benign_limitations"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Benign limitations</H3></A> + + +<P>This is "lite", after all... + + + +<UL> +<P><LI> +<P>There's no parsing. Get MIME-tools if you need to parse MIME messages. + +<P><LI> +<P>MIME::Lite messages are currently <I>not</I> interchangeable with +either Mail::Internet or MIME::Entity objects. This is a completely +separate module. + +<P><LI> +<P>A content-length field is only inserted if the encoding is binary, +the message is a singlepart, and all the document data is available +at <CODE>build()</CODE> time by virtue of residing in a simple path, or in-core. +Since content-length is not a standard MIME field anyway (that's right, kids: +it's not in the MIME RFCs, it's an HTTP thing), this seems pretty fair. + +<P><LI> +<P>MIME::Lite alone cannot help you lose weight. You must supplement +your use of MIME::Lite with a healthy diet and exercise. + +</UL> + + + +<P><HR> +<A NAME="Cheap_and_easy_mailing"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Cheap and easy mailing</H3></A> + + +<P>I thought putting in a default "sendmail" invocation wasn't too bad an +idea, since a lot of Perlers are on UNIX systems. +The out-of-the-box configuration is: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send('sendmail', "/usr/lib/sendmail -t -oi -oem"); +</PRE></FONT> + +<P>By the way, these arguments to sendmail are: + +<FONT SIZE=3 FACE="courier"><PRE> + -t Scan message for To:, Cc:, Bcc:, etc. + + -oi Do NOT treat a single "." on a line as a message terminator. + As in, "-oi vey, it truncated my message... why?!" + + -oem On error, mail back the message (I assume to the + appropriate address, given in the header). + When mail returns, circle is complete. Jai Guru Deva -oem. +</PRE></FONT> + +<P>Note that these are the same arguments you get if you configure to use +the smarter, taint-safe mailing: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send('sendmail'); +</PRE></FONT> + +<P>If you get "X-Authentication-Warning" headers from this, you can forgo +diddling with the envelope by instead specifying: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send('sendmail', SetSender=>0); +</PRE></FONT> + +<P>And, if you're not on a Unix system, or if you'd just rather send mail +some other way, there's always: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send('smtp', "smtp.myisp.net"); +</PRE></FONT> + +<P>Or you can set up your own subroutine to call. +In any case, check out the <A HREF="#item:send">send()</A> method. + + + +<P><HR> +<A NAME="WARNINGS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> WARNINGS</H2></A> + + + +<P><HR> +<A NAME="Good-vs-bad_email_addresses_with_send_by_smtp"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Good-vs-bad email addresses with send_by_smtp()</H3></A> + + +<P>If using <A HREF="#item:send_by_smtp">send_by_smtp()</A>, be aware that you are +forcing MIME::Lite to extract email addresses out of a possible list +provided in the <CODE>To:</CODE>, <CODE>Cc:</CODE>, and <CODE>Bcc:</CODE> fields. This is tricky +stuff, and as such only the following sorts of addresses will work +reliably: + +<FONT SIZE=3 FACE="courier"><PRE> + username + full.name@some.host.com + "Name, Full" <full.name@some.host.com> +</PRE></FONT> + +<P>This last form is discouraged because SMTP must be able to get +at the <I>name</I> or <I>name@domain</I> portion. + + +<P><B>Disclaimer:</B> +MIME::Lite was never intended to be a Mail User Agent, so please +don't expect a full implementation of RFC-822. Restrict yourself to +the common forms of Internet addresses described herein, and you should +be fine. If this is not feasible, then consider using MIME::Lite +to <I>prepare</I> your message only, and using Net::SMTP explicitly to +<I>send</I> your message. + + + +<P><HR> +<A NAME="Formatting_of_headers_delayed_until_print"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Formatting of headers delayed until print()</H3></A> + + +<P>This class treats a MIME header in the most abstract sense, +as being a collection of high-level attributes. The actual +RFC-822-style header fields are not constructed until it's time +to actually print the darn thing. + + + +<P><HR> +<A NAME="Encoding_of_data_delayed_until_print"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Encoding of data delayed until print()</H3></A> + + +<P>When you specify message bodies +(in <A HREF="#item:build">build()</A> or <A HREF="#item:attach">attach()</A>) -- +whether by <B>FH</B>, <B>Data</B>, or <B>Path</B> -- be warned that we don't +attempt to open files, read filehandles, or encode the data until +<A HREF="#item:print">print()</A> is invoked. + + +<P>In the past, this created some confusion for users of sendmail +who gave the wrong path to an attachment body, since enough of +the print() would succeed to get the initial part of the message out. +Nowadays, $AUTO_VERIFY is used to spot-check the Paths given before +the mail facility is employed. A whisker slower, but tons safer. + + +<P>Note that if you give a message body via FH, and try to print() +a message twice, the second print() will not do the right thing +unless you explicitly rewind the filehandle. + + +<P>You can get past these difficulties by using the <B>ReadNow</B> option, +provided that you have enough memory to handle your messages. + + + +<P><HR> +<A NAME="MIME_attributes_are_separate_from_header_fields"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> MIME attributes are separate from header fields!</H3></A> + + +<P><B>Important:</B> the MIME attributes are stored and manipulated separately +from the message header fields; when it comes time to print the +header out, <I>any explicitly-given header fields override the ones that +would be created from the MIME attributes.</I> That means that this: + +<FONT SIZE=3 FACE="courier"><PRE> + ### DANGER ### DANGER ### DANGER ### DANGER ### DANGER ### + $msg->add("Content-type", "text/html; charset=US-ASCII"); +</PRE></FONT> + +<P>will set the exact <CODE>"Content-type"</CODE> field in the header I write, +<I>regardless of what the actual MIME attributes are.</I> + + +<P><I>This feature is for experienced users only,</I> as an escape hatch in case +the code that normally formats MIME header fields isn't doing what +you need. And, like any escape hatch, it's got an alarm on it: +MIME::Lite will warn you if you attempt to <CODE>set()</CODE> or <CODE>replace()</CODE> +any MIME header field. Use <CODE>attr()</CODE> instead. + + + +<P><HR> +<A NAME="Beware_of_lines_consisting_of_a_single_dot"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Beware of lines consisting of a single dot</H3></A> + + +<P>Julian Haight noted that MIME::Lite allows you to compose messages +with lines in the body consisting of a single ".". +This is true: it should be completely harmless so long as "sendmail" +is used with the -oi option (see <A HREF="#Cheap_and_easy_mailing">Cheap and easy mailing</A>). + + +<P>However, I don't know if using Net::SMTP to transfer such a message +is equally safe. Feedback is welcomed. + + +<P>My perspective: I don't want to magically diddle with a user's +message unless absolutely positively necessary. +Some users may want to send files with "." alone on a line; +my well-meaning tinkering could seriously harm them. + + + +<P><HR> +<A NAME="Infinite_loops_may_mean_tainted_data"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Infinite loops may mean tainted data!</H3></A> + + +<P>Stefan Sautter noticed a bug in 2.106 where a m//gc match was +failing due to tainted data, leading to an infinite loop inside +MIME::Lite. + + +<P>I am attempting to correct for this, but be advised that my fix will +silently untaint the data (given the context in which the problem +occurs, this should be benign: I've labelled the source code with +UNTAINT comments for the curious). + + +<P>So: don't depend on taint-checking to save you from outputting +tainted data in a message. + + + +<P><HR> +<A NAME="A_MIME_PRIMER"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> A MIME PRIMER</H2></A> + + + +<P><HR> +<A NAME="Content_types"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Content types</H3></A> + + +<P>The "Type" parameter of <CODE>build()</CODE> is a <I>content type</I>. +This is the actual type of data you are sending. +Generally this is a string of the form <CODE>"majortype/minortype"</CODE>. + + +<P>Here are the major MIME types. +A more-comprehensive listing may be found in RFC-2046. + + + +<DL> +<P><DT><B><A NAME="item:application">application</A></B></DT> +<DD> +Data which does not fit in any of the other categories, particularly +data to be processed by some type of application program. +<CODE>application/octet-stream</CODE>, <CODE>application/gzip</CODE>, <CODE>application/postscript</CODE>... + +<P><DT><B><A NAME="item:audio">audio</A></B></DT> +<DD> +Audio data. +<CODE>audio/basic</CODE>... + +<P><DT><B><A NAME="item:image">image</A></B></DT> +<DD> +Graphics data. +<CODE>image/gif</CODE>, <CODE>image/jpeg</CODE>... + +<P><DT><B><A NAME="item:message">message</A></B></DT> +<DD> +A message, usually another mail or MIME message. +<CODE>message/rfc822</CODE>... + +<P><DT><B><A NAME="item:multipart">multipart</A></B></DT> +<DD> +A message containing other messages. +<CODE>multipart/mixed</CODE>, <CODE>multipart/alternative</CODE>... + +<P><DT><B><A NAME="item:text">text</A></B></DT> +<DD> +Textual data, meant for humans to read. +<CODE>text/plain</CODE>, <CODE>text/html</CODE>... + +<P><DT><B><A NAME="item:video">video</A></B></DT> +<DD> +Video or video+audio data. +<CODE>video/mpeg</CODE>... + +</DL> + + + +<P><HR> +<A NAME="Content_transfer_encodings"><H3><A HREF="#__TOP__"><IMG SRC="icons/h2bullet.gif" ALT="Top" BORDER="0"></A> Content transfer encodings</H3></A> + + +<P>The "Encoding" parameter of <CODE>build()</CODE>. +This is how the message body is packaged up for safe transit. + + +<P>Here are the 5 major MIME encodings. +A more-comprehensive listing may be found in RFC-2045. + + + +<DL> +<P><DT><B><A NAME="item:7bit">7bit</A></B></DT> +<DD> +Basically, no <I>real</I> encoding is done. However, this label guarantees that no +8-bit characters are present, and that lines do not exceed 1000 characters +in length. + +<P><DT><B><A NAME="item:8bit">8bit</A></B></DT> +<DD> +Basically, no <I>real</I> encoding is done. The message might contain 8-bit +characters, but this encoding guarantees that lines do not exceed 1000 +characters in length. + +<P><DT><B><A NAME="item:binary">binary</A></B></DT> +<DD> +No encoding is done at all. Message might contain 8-bit characters, +and lines might be longer than 1000 characters long. + + +<P>The most liberal, and the least likely to get through mail gateways. +Use sparingly, or (better yet) not at all. + +<P><DT><B><A NAME="item:base64">base64</A></B></DT> +<DD> +Like "uuencode", but very well-defined. This is how you should send +essentially binary information (tar files, GIFs, JPEGs, etc.). + +<P><DT><B><A NAME="item:quoted-printable">quoted-printable</A></B></DT> +<DD> +Useful for encoding messages which are textual in nature, yet which contain +non-ASCII characters (e.g., Latin-1, Latin-2, or any other 8-bit alphabet). + +</DL> + + + +<P><HR> +<A NAME="VERSION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> VERSION</H2></A> + + +<P>$Id: Lite.pm,v 2.108 2001/03/30 06:16:54 eryq Exp $ + + + +<P><HR> +<A NAME="CHANGE_LOG"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> CHANGE LOG</H2></A> + + + +<DL> +<P><DT><B><A NAME="item:Version">Version 2.108</A></B></DT> +<DD> +New <CODE>field_order()</CODE> allows you to set the header order, both on a +per-message basis, and package-wide. +<I>Thanks to Thomas Stromberg for suggesting this.</I> + + +<P>Added code to try and divine "sendmail" path more intelligently. +<I>Thanks to Slaven Rezic for the suggestion.</I> + +<P><DT><B><A NAME="item:Version">Version 2.107 (2001/03/27)</A></B></DT> +<DD> +Fixed serious bug where tainted data with quoted-printable encoding +was causing infinite loops. The "fix" untaints the data in question, +which is not optimal, but it's probably benign in this case. +<I>Thanks to Stefan Sautter for tracking this nasty little beast down.</I> +<I>Thanks to Larry Geralds for a related patch.</I> + +<FONT SIZE=3 FACE="courier"><PRE> + "Doctor, O doctor: + it's painful when I do *this* --" + "Simple: don't *do* that." +</PRE></FONT> + +<P>Fixed bugs where a non-local <CODE>$_</CODE> was being modified... again! +Will I never learn? +<I>Thanks to Maarten Koskamp for reporting this.</I> + +<FONT SIZE=3 FACE="courier"><PRE> + Dollar-underscore + can poison distant waters; + 'local' must it be. +</PRE></FONT> + +<P>Fixed buglet in <CODE>add()</CODE> where all value references were being treated +as arrayrefs, instead of as possibly-self-stringifying object refs. +Now you can send in an object ref as the 2nd argument. +<I>Thanks to dLux for the bug report.</I> + +<FONT SIZE=3 FACE="courier"><PRE> + That ref is a string? + Operator overload + has ruined my day. +</PRE></FONT> + +<P>Added "Approved" as an acceptable header field for <CODE>new()</CODE>, as per RFC1036. +<I>Thanks to Thomax for the suggestion regarding MIME-tools.</I> + + +<P>Small improvements to docs to make different uses of attach() +and various arguments clearer. +<I>Thanks to Sven Rassman and Roland Walter for the suggestions.</I> + +<P><DT><B><A NAME="item:Version">Version 2.106 (2000/11/21)</A></B></DT> +<DD> +Added Alpha version of scrub() to make it easy for people to suppress +the printing of unwanted MIME attributes (like Content-length). +<I>Thanks to the many people who asked for this.</I> + + +<P>Headers with empty-strings for their values are no longer +printed. This seems sensible, and helps us implement scrub(). + +<P><DT><B><A NAME="item:Version">Version 2.105 (2000/10/14)</A></B></DT> +<DD> +The regression-test failure was identified, and it was my fault. +Apparently some of the \-quoting in my "autoloaded" code was +making Perl 5.6 unhappy. For this nesting-related idiocy, +a nesting kaiku. +<I>Thanks to Scott Schwartz for identifying the problem.</I> + +<FONT SIZE=3 FACE="courier"><PRE> + In a pattern, my + backslash-s dwells peacefully, + unambiguous -- + + but I embed it + in a double-quoted string + doubling the backslash -- + + interpolating + that same double-quoted string + in other patterns -- + + and, worlds within worlds, + I single-quote the function + to autoload it -- + + changing the meaning + of the backslash and the 's'; + and Five-Point-Six growls. +</PRE></FONT> +<P><DT><B><A NAME="item:Version">Version 2.104 (2000/09/28)</A></B></DT> +<DD> +Now attempts to load and use Mail::Address for parsing email +addresses <I>before</I> falling back to our own method. +<I>Thanks to numerous people for suggesting this.</I> + +<FONT SIZE=3 FACE="courier"><PRE> + Parsing addresses + is too damn hard. One last hope: + Let Graham Barr do it! +</PRE></FONT> + +<P>For the curious, the version of Mail::Address appears +as the "A" number in the X-Mailer: + +<FONT SIZE=3 FACE="courier"><PRE> + X-Mailer: MIME::Lite 2.104 (A1.15; B2.09; Q2.03) +</PRE></FONT> + +<P>Added <B>FromSender</B> option to send_by_sendmail(). +<I>Thanks to Bill Moseley for suggesting this feature.</I> + +<P><DT><B><A NAME="item:Version">Version 2.101 (2000/06/06)</A></B></DT> +<DD> +Major revision to print_body() and body_as_string() so that +"body" really means "the part after the header", which is what most +people would want in this context. This is <B>not</B> how it was used +1.x, where "body" only meant "the body of a simple singlepart". +Hopefully, this change will solve many problems and create very few ones. + + +<P>Added support for attaching a part to a "message/rfc822", treating +the "message" type as a multipart-like container. + + +<P>Now takes care not to include "Bcc:" in header when using send_by_smtp, +as a safety precaution against qmail's behavior. +<I>Thanks to Tatsuhiko Miyagawa for identifying this problem.</I> + + +<P>Improved efficiency of many stringifying operations by using +string-arrays which are joined, instead of doing multiple appends +to a scalar. + + +<P>Cleaned up the "examples" directory. + +<P><DT><B><A NAME="item:Version">Version 1.147 (2000/06/02)</A></B></DT> +<DD> +Fixed buglet where lack of Cc:/Bcc: was causing extract_addrs +to emit "undefined variable" warnings. Also, lack of a "To:" field +now causes a croak. +<I>Thanks to David Mitchell for the bug report and suggested patch.</I> + +<P><DT><B><A NAME="item:Version">Version 1.146 (2000/05/18)</A></B></DT> +<DD> +Fixed bug in parsing of addresses; please read the WARNINGS section +which describes recommended address formats for "To:", "Cc:", etc. +Also added automatic inclusion of a UT "Date:" at top level unless +explicitly told not to. +<I>Thanks to Andy Jacobs for the bug report and the suggestion.</I> + +<P><DT><B><A NAME="item:Version">Version 1.145 (2000/05/06)</A></B></DT> +<DD> +Fixed bug in encode_7bit(): a lingering <CODE>/e</CODE> modifier was removed. +<I>Thanks to Michael A. Chase for the patch.</I> + +<P><DT><B><A NAME="item:Version">Version 1.142 (2000/05/02)</A></B></DT> +<DD> +Added new, taint-safe invocation of "sendmail", one which also +sets up the <CODE>-f</CODE> option. Unfortunately, I couldn't make this automatic: +the change could have broken a lot of code out there which used +send_by_sendmail() with unusual "sendmail" variants. +So you'll have to configure "send" to use the new mechanism: + +<FONT SIZE=3 FACE="courier"><PRE> + MIME::Lite->send('sendmail'); ### no args! +</PRE></FONT> + +<P><I>Thanks to Jeremy Howard for suggesting these features.</I> + +<P><DT><B><A NAME="item:Version">Version 1.140 (2000/04/27)</A></B></DT> +<DD> +Fixed bug in support for "To", "Cc", and "Bcc" in send_by_smtp(): +multiple (comma-separated) addresses should now work fine. +We try real hard to extract addresses from the flat text strings. +<I>Thanks to John Mason for motivating this change.</I> + + +<P>Added automatic verification that attached data files exist, +done immediately before the "send" action is invoked. +To turn this off, set $MIME::Lite::AUTO_VERIFY to false. + +<P><DT><B><A NAME="item:Version">Version 1.137 (2000/03/22)</A></B></DT> +<DD> +Added support for "Cc" and "Bcc" in send_by_smtp(). +To turn this off, set $MIME::Lite::AUTO_CC to false. +<I>Thanks to Lucas Maneos for the patch, and tons of others for +the suggestion.</I> + + +<P>Chooses a better default content-transfer-encoding if the content-type +is "image/*", "audio/*", etc. +To turn this off, set $MIME::Lite::AUTO_ENCODE to false. +<I>Thanks to many folks for the suggestion.</I> + + +<P>Fixed bug in QP-encoding where a non-local <CODE>$_</CODE> was being modified. +<I>Thanks to Jochen Stenzel for finding this very obscure bug!</I> + + +<P>Removed references to <CODE>$`</CODE>, <CODE>$'</CODE>, and <CODE>$&</CODE> (bad variables +which slow things down). + + +<P>Added an example of how to send HTML files with enclosed in-line +images, per popular demand. + +<P><DT><B><A NAME="item:Version">Version 1.133 (1999/04/17)</A></B></DT> +<DD> +Fixed bug in "Data" handling: arrayrefs were not being handled +properly. + +<P><DT><B><A NAME="item:Version">Version 1.130 (1998/12/14)</A></B></DT> +<DD> +Added much larger and more-flexible send() facility. +<I>Thanks to Andrew McRae (and Optimation New Zealand Ltd) +for the Net::SMTP interface. Additional thanks to the many folks +who requested this feature.</I> + + +<P>Added get() method for extracting basic attributes. + + +<P>New... "t" tests! + +<P><DT><B><A NAME="item:Version">Version 1.124 (1998/11/13)</A></B></DT> +<DD> +Folded in filehandle (FH) support in build/attach. +<I>Thanks to Miko O'Sullivan for the code.</I> + +<P><DT><B><A NAME="item:Version">Version 1.122 (1998/01/19)</A></B></DT> +<DD> +MIME::Base64 and MIME::QuotedPrint are used if available. + + +<P>The 7bit encoding no longer does "escapes"; it merely strips 8-bit characters. + +<P><DT><B><A NAME="item:Version">Version 1.121 (1997/04/08)</A></B></DT> +<DD> +Filename attribute is now no longer ignored by build(). +<I>Thanks to Ian Smith for finding and patching this bug.</I> + +<P><DT><B><A NAME="item:Version">Version 1.120 (1997/03/29)</A></B></DT> +<DD> +Efficiency hack to speed up MIME::Lite::IO_Scalar. +<I>Thanks to David Aspinwall for the patch.</I> + +<P><DT><B><A NAME="item:Version">Version 1.116 (1997/03/19)</A></B></DT> +<DD> +Small bug in our private copy of encode_base64() was patched. +<I>Thanks to Andreas Koenig for pointing this out.</I> + + +<P>New, prettier way of specifying mail message headers in <CODE>build()</CODE>. + + +<P>New quiet method to turn off warnings. + + +<P>Changed "stringify" methods to more-standard "as_string" methods. + +<P><DT><B><A NAME="item:Version">Version 1.112 (1997/03/06)</A></B></DT> +<DD> +Added <CODE>read_now()</CODE>, and <CODE>binmode()</CODE> method for our non-Unix-using brethren: +file data is now read using binmode() if appropriate. +<I>Thanks to Xiangzhou Wang for pointing out this bug.</I> + +<P><DT><B><A NAME="item:Version">Version 1.110 (1997/03/06)</A></B></DT> +<DD> +Fixed bug in opening the data filehandle. + +<P><DT><B><A NAME="item:Version">Version 1.102 (1997/03/01)</A></B></DT> +<DD> +Initial release. + +<P><DT><B><A NAME="item:Version">Version 1.101 (1997/03/01)</A></B></DT> +<DD> +Baseline code. + +</DL> + + + +<P><HR> +<A NAME="TERMS_AND_CONDITIONS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> TERMS AND CONDITIONS</H2></A> + + +<P>Copyright (c) 1997 by Eryq. +Copyright (c) 1998 by ZeeGee Software Inc. +All rights reserved. This program is free software; you can redistribute +it and/or modify it under the same terms as Perl itself. + + +<P>This software comes with <B>NO WARRANTY</B> of any kind. +See the COPYING file in the distribution for details. + + + +<P><HR> +<A NAME="NUTRITIONAL_INFORMATION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NUTRITIONAL INFORMATION</H2></A> + + +<P>For some reason, the US FDA says that this is now required by law +on any products that bear the name "Lite"... + +<P><TABLE CELLPADDING=4 CELLSPACING=0 BORDER=1 ALIGN=CENTER BGCOLOR=#EEEEEE><TR><TH BGCOLOR=#AA0055 ALIGN=LEFT><FONT SIZE=2 COLOR=#FFFFFF FACE="sans-serif">MIME::Lite </FONT></TH BGCOLOR=#AA0055><TH BGCOLOR=#AA0055 ALIGN=LEFT><FONT SIZE=2 COLOR=#FFFFFF FACE="sans-serif"> </FONT></TH BGCOLOR=#AA0055></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Serving size:</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">1 module</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Servings per container:</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">1</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Calories:</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">0</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Fat:</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">0g</FONT></TD></TR><TR><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">Saturated Fat:</FONT></TD><TD ALIGN=LEFT><FONT SIZE=2 COLOR=#000000 FACE="sans-serif">0g</FONT></TD></TR></TABLE> +<P>Warning: for consumption by hardware only! May produce +indigestion in humans if taken internally. + + + +<P><HR> +<A NAME="AUTHOR"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> AUTHOR</H2></A> + + +<P>Eryq (<I><FILE><A HREF="mailto:eryq@zeegee.com">eryq@zeegee.com</A></FILE></I>). +President, ZeeGee Software Inc. (<I><FILE><A HREF="http://www.zeegee.com">http://www.zeegee.com</A></FILE></I>). + + +<P>Created: 11 December 1996. Ho ho ho. + +<P><HR> +<ADDRESS><FONT SIZE=-1> +Generated Fri Mar 30 01:17:20 2001 by cvu_pod2html +</FONT></ADDRESS> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/docs/MIME/icons/h1bullet.gif b/cpan/dist/MIME-Lite/docs/MIME/icons/h1bullet.gif Binary files differnew file mode 100644 index 00000000..86986436 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/MIME/icons/h1bullet.gif diff --git a/cpan/dist/MIME-Lite/docs/MIME/icons/h2bullet.gif b/cpan/dist/MIME-Lite/docs/MIME/icons/h2bullet.gif Binary files differnew file mode 100644 index 00000000..d26510cd --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/MIME/icons/h2bullet.gif diff --git a/cpan/dist/MIME-Lite/docs/MIME/icons/zeegee.gif b/cpan/dist/MIME-Lite/docs/MIME/icons/zeegee.gif Binary files differnew file mode 100644 index 00000000..f6001a5f --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/MIME/icons/zeegee.gif diff --git a/cpan/dist/MIME-Lite/docs/icons/h1bullet.gif b/cpan/dist/MIME-Lite/docs/icons/h1bullet.gif Binary files differnew file mode 100644 index 00000000..86986436 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/icons/h1bullet.gif diff --git a/cpan/dist/MIME-Lite/docs/icons/h2bullet.gif b/cpan/dist/MIME-Lite/docs/icons/h2bullet.gif Binary files differnew file mode 100644 index 00000000..d26510cd --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/icons/h2bullet.gif diff --git a/cpan/dist/MIME-Lite/docs/icons/zeegee.gif b/cpan/dist/MIME-Lite/docs/icons/zeegee.gif Binary files differnew file mode 100644 index 00000000..f6001a5f --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/icons/zeegee.gif diff --git a/cpan/dist/MIME-Lite/docs/index-menu.html b/cpan/dist/MIME-Lite/docs/index-menu.html new file mode 100644 index 00000000..b8b68308 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/index-menu.html @@ -0,0 +1,27 @@ +<HTML> +<HEAD> +<TITLE>perlmod</TITLE> +</HEAD> +<BODY BGCOLOR="#FFFFFF" LINK="#CC3366" ALINK="#FF6666" VLINK="#993366"> +<FONT FACE="sans-serif"><TABLE> + +<TR VALIGN="TOP"><TH ALIGN="LEFT"><FONT FACE="sans-serif"><B>Overview</B></FONT></TH> +<TR VALIGN="TOP"><TD><A HREF="MIME/Lite.pm.html" TARGET="perlmod.content"><FONT SIZE="-1" FACE="sans-serif">MIME::Lite</FONT></A></TD></TR> +</TABLE> +<HR> +<TABLE> + +<TR VALIGN="TOP"><TH ALIGN="LEFT"><FONT FACE="sans-serif"><B>Examples</B></FONT></TH> +<TR VALIGN="TOP"><TD><A HREF="mime_fwd.html" TARGET="perlmod.content"><FONT SIZE="-1" FACE="sans-serif">mime_fwd</FONT></A></TD></TR> + +<TR VALIGN="TOP"><TD><A HREF="mime_gif.html" TARGET="perlmod.content"><FONT SIZE="-1" FACE="sans-serif">mime_gif</FONT></A></TD></TR> + +<TR VALIGN="TOP"><TD><A HREF="mime_hack.html" TARGET="perlmod.content"><FONT SIZE="-1" FACE="sans-serif">mime_hack</FONT></A></TD></TR> + +<TR VALIGN="TOP"><TD><A HREF="mime_longlines.html" TARGET="perlmod.content"><FONT SIZE="-1" FACE="sans-serif">mime_longlines</FONT></A></TD></TR> + +<TR VALIGN="TOP"><TD><A HREF="mime_postcard.html" TARGET="perlmod.content"><FONT SIZE="-1" FACE="sans-serif">mime_postcard</FONT></A></TD></TR> +</TABLE> +<HR> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/docs/index.html b/cpan/dist/MIME-Lite/docs/index.html new file mode 100644 index 00000000..d825be82 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/index.html @@ -0,0 +1,13 @@ +<HEAD> +<TITLE>perlmod</TITLE> +</HEAD> +<FRAMESET COLS="20%,*" BORDER=1 FRAMEBORDER=1 FRAMESPACING=10> + <FRAME NAME="perlmod.menu" SRC="index-menu.html"> + <FRAME NAME="perlmod.content" SRC="MIME/Lite.pm.html"> +</FRAMESET> + +<NOFRAMES> + <BODY> + Go <A HREF="menu.html">here</A> + </BODY> +</NOFRAMES> diff --git a/cpan/dist/MIME-Lite/docs/index.menu b/cpan/dist/MIME-Lite/docs/index.menu new file mode 100644 index 00000000..66830c98 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/index.menu @@ -0,0 +1,24 @@ +MENU perlmod + +SECTION Overview + +ITEM MIME::Lite +HREF MIME/Lite.pm.html + +SECTION Examples + +ITEM mime_fwd +HREF mime_fwd.html + +ITEM mime_gif +HREF mime_gif.html + +ITEM mime_hack +HREF mime_hack.html + +ITEM mime_longlines +HREF mime_longlines.html + +ITEM mime_postcard +HREF mime_postcard.html + diff --git a/cpan/dist/MIME-Lite/docs/mime_fwd.html b/cpan/dist/MIME-Lite/docs/mime_fwd.html new file mode 100644 index 00000000..923bd707 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/mime_fwd.html @@ -0,0 +1,55 @@ +<HTML> +<HEAD> + <TITLE>mime_fwd</TITLE> +</HEAD> +<BODY + bgcolor="#FFFFFF" link="#CC3366" vlink="#993366" alink="#FF6666"> +<FONT FACE="sans-serif" SIZE=-1><A HREF="http://www.zeegee.com" TARGET="_top"><IMG SRC="icons/zeegee.gif" ALT="ZeeGee Software" ALIGN="RIGHT" BORDER="0"></A><A NAME="__TOP__"><H1>mime_fwd</H1> +</A><UL> +<LI> <A HREF="#NAME">NAME</A> +<LI> <A HREF="#SYNOPSIS">SYNOPSIS</A> +<LI> <A HREF="#DESCRIPTION">DESCRIPTION</A> +<LI> <A HREF="#AUTHOR">AUTHOR</A> +</UL> +</A> + +<P><HR> +<A NAME="NAME"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NAME</H2></A> + + +<P>mime_fwd - test the ability to embed messages + + + +<P><HR> +<A NAME="SYNOPSIS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> SYNOPSIS</H2></A> + + +<P>Usage: + +<FONT SIZE=3 FACE="courier"><PRE> + mime_fwd +</PRE></FONT> + + +<P><HR> +<A NAME="DESCRIPTION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> DESCRIPTION</H2></A> + + +<P>Create a simple message, then wrap it in a "forward" and +then a "reply". + + + +<P><HR> +<A NAME="AUTHOR"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> AUTHOR</H2></A> + + +<P>Eryq, eryq@zeegee.com + +<P><HR> +<ADDRESS><FONT SIZE=-1> +Generated Fri Mar 30 01:17:33 2001 by cvu_pod2html +</FONT></ADDRESS> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/docs/mime_gif.html b/cpan/dist/MIME-Lite/docs/mime_gif.html new file mode 100644 index 00000000..e078b1dc --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/mime_gif.html @@ -0,0 +1,56 @@ +<HTML> +<HEAD> + <TITLE>mime_gif</TITLE> +</HEAD> +<BODY + bgcolor="#FFFFFF" link="#CC3366" vlink="#993366" alink="#FF6666"> +<FONT FACE="sans-serif" SIZE=-1><A HREF="http://www.zeegee.com" TARGET="_top"><IMG SRC="icons/zeegee.gif" ALT="ZeeGee Software" ALIGN="RIGHT" BORDER="0"></A><A NAME="__TOP__"><H1>mime_gif</H1> +</A><UL> +<LI> <A HREF="#NAME">NAME</A> +<LI> <A HREF="#SYNOPSIS">SYNOPSIS</A> +<LI> <A HREF="#DESCRIPTION">DESCRIPTION</A> +<LI> <A HREF="#AUTHOR">AUTHOR</A> +</UL> +</A> + +<P><HR> +<A NAME="NAME"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NAME</H2></A> + + +<P>mime_gif - encode a single gif by reading data different ways + + + +<P><HR> +<A NAME="SYNOPSIS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> SYNOPSIS</H2></A> + + +<P>Usage: + +<FONT SIZE=3 FACE="courier"><PRE> + mime_gif /path/to/some.gif +</PRE></FONT> + + +<P><HR> +<A NAME="DESCRIPTION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> DESCRIPTION</H2></A> + + +<P>Encode a multipart message where each part contains the same GIF +file, but where the GIF file has been read-in in different ways. +The subject line of each part will tell you how the GIF was read. + + + +<P><HR> +<A NAME="AUTHOR"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> AUTHOR</H2></A> + + +<P>Eryq, eryq@zeegee.com + +<P><HR> +<ADDRESS><FONT SIZE=-1> +Generated Fri Mar 30 01:17:36 2001 by cvu_pod2html +</FONT></ADDRESS> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/docs/mime_hack.html b/cpan/dist/MIME-Lite/docs/mime_hack.html new file mode 100644 index 00000000..41bd7ce1 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/mime_hack.html @@ -0,0 +1,55 @@ +<HTML> +<HEAD> + <TITLE>mime_hack</TITLE> +</HEAD> +<BODY + bgcolor="#FFFFFF" link="#CC3366" vlink="#993366" alink="#FF6666"> +<FONT FACE="sans-serif" SIZE=-1><A HREF="http://www.zeegee.com" TARGET="_top"><IMG SRC="icons/zeegee.gif" ALT="ZeeGee Software" ALIGN="RIGHT" BORDER="0"></A><A NAME="__TOP__"><H1>mime_hack</H1> +</A><UL> +<LI> <A HREF="#NAME">NAME</A> +<LI> <A HREF="#SYNOPSIS">SYNOPSIS</A> +<LI> <A HREF="#DESCRIPTION">DESCRIPTION</A> +<LI> <A HREF="#AUTHOR">AUTHOR</A> +</UL> +</A> + +<P><HR> +<A NAME="NAME"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NAME</H2></A> + + +<P>mime_hack - test the "attach to singlepart" hack + + + +<P><HR> +<A NAME="SYNOPSIS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> SYNOPSIS</H2></A> + + +<P>Usage: + +<FONT SIZE=3 FACE="courier"><PRE> + mime_hack /path/to/some.gif +</PRE></FONT> + + +<P><HR> +<A NAME="DESCRIPTION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> DESCRIPTION</H2></A> + + +<P>Encode a multipart message by starting with a text message, +and attaching a GIF file to it. + + + +<P><HR> +<A NAME="AUTHOR"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> AUTHOR</H2></A> + + +<P>Eryq, eryq@zeegee.com + +<P><HR> +<ADDRESS><FONT SIZE=-1> +Generated Fri Mar 30 01:17:39 2001 by cvu_pod2html +</FONT></ADDRESS> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/docs/mime_longlines.html b/cpan/dist/MIME-Lite/docs/mime_longlines.html new file mode 100644 index 00000000..da87b792 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/mime_longlines.html @@ -0,0 +1,72 @@ +<HTML> +<HEAD> + <TITLE>mime_longlines</TITLE> +</HEAD> +<BODY + bgcolor="#FFFFFF" link="#CC3366" vlink="#993366" alink="#FF6666"> +<FONT FACE="sans-serif" SIZE=-1><A HREF="http://www.zeegee.com" TARGET="_top"><IMG SRC="icons/zeegee.gif" ALT="ZeeGee Software" ALIGN="RIGHT" BORDER="0"></A><A NAME="__TOP__"><H1>mime_longlines</H1> +</A><UL> +<LI> <A HREF="#NAME">NAME</A> +<LI> <A HREF="#SYNOPSIS">SYNOPSIS</A> +<LI> <A HREF="#DESCRIPTION">DESCRIPTION</A> +<LI> <A HREF="#AUTHOR">AUTHOR</A> +</UL> +</A> + +<P><HR> +<A NAME="NAME"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NAME</H2></A> + + +<P>mime_longlines - generate a test message with long lines + + + +<P><HR> +<A NAME="SYNOPSIS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> SYNOPSIS</H2></A> + + +<P>Usage: + +<FONT SIZE=3 FACE="courier"><PRE> + mime_longlines [-s] +</PRE></FONT> + +<P>Options: + +<FONT SIZE=3 FACE="courier"><PRE> + -s Stringify message first, and print the *string* to STDOUT. +</PRE></FONT> + + +<P><HR> +<A NAME="DESCRIPTION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> DESCRIPTION</H2></A> + + +<P>Assemble and print (to the standard output) a multipart message +with 5 attachments, for the purpose of "eyeballing" how well the +encoders are working. + + +<P>Each attachments holds the same data -- some 8-bit text, and a long +line consisting of 1000 "a"s followed by a few "b"s) -- but each +has a different encoding (the "Content-transfer-encoding" field +will tell you which is which). + + +<P>All of the encodings (except for the first one, "binary") should +break the long line before the b's. + + + +<P><HR> +<A NAME="AUTHOR"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> AUTHOR</H2></A> + + +<P>Eryq, eryq@zeegee.com + +<P><HR> +<ADDRESS><FONT SIZE=-1> +Generated Fri Mar 30 01:17:41 2001 by cvu_pod2html +</FONT></ADDRESS> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/docs/mime_postcard.html b/cpan/dist/MIME-Lite/docs/mime_postcard.html new file mode 100644 index 00000000..cd406f44 --- /dev/null +++ b/cpan/dist/MIME-Lite/docs/mime_postcard.html @@ -0,0 +1,59 @@ +<HTML> +<HEAD> + <TITLE>mime_postcard</TITLE> +</HEAD> +<BODY + bgcolor="#FFFFFF" link="#CC3366" vlink="#993366" alink="#FF6666"> +<FONT FACE="sans-serif" SIZE=-1><A HREF="http://www.zeegee.com" TARGET="_top"><IMG SRC="icons/zeegee.gif" ALT="ZeeGee Software" ALIGN="RIGHT" BORDER="0"></A><A NAME="__TOP__"><H1>mime_postcard</H1> +</A><UL> +<LI> <A HREF="#NAME">NAME</A> +<LI> <A HREF="#SYNOPSIS">SYNOPSIS</A> +<LI> <A HREF="#DESCRIPTION">DESCRIPTION</A> +<LI> <A HREF="#AUTHOR">AUTHOR</A> +</UL> +</A> + +<P><HR> +<A NAME="NAME"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> NAME</H2></A> + + +<P>mime_postcard - output a multipart/alternative message + + + +<P><HR> +<A NAME="SYNOPSIS"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> SYNOPSIS</H2></A> + + +<P>Usage: + +<FONT SIZE=3 FACE="courier"><PRE> + mime_postcard /path/to/some/graphic.jpg to@addr.com +</PRE></FONT> + +<P>You can point it at a .gif file as well. +The special address "-" just causes the message to go to STDOUT. + + + +<P><HR> +<A NAME="DESCRIPTION"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> DESCRIPTION</H2></A> + + +<P>This send a mesasge both as HTML and plain text. +I use "Data"; you would probably use "Path". + + + +<P><HR> +<A NAME="AUTHOR"><H2><A HREF="#__TOP__"><IMG SRC="icons/h1bullet.gif" ALT="Top" BORDER="0"></A> AUTHOR</H2></A> + + +<P>Eryq, eryq@zeegee.com + +<P><HR> +<ADDRESS><FONT SIZE=-1> +Generated Fri Mar 30 01:17:44 2001 by cvu_pod2html +</FONT></ADDRESS> +</FONT></BODY> +</HTML> diff --git a/cpan/dist/MIME-Lite/examples/mime_fwd b/cpan/dist/MIME-Lite/examples/mime_fwd new file mode 100755 index 00000000..388d38d7 --- /dev/null +++ b/cpan/dist/MIME-Lite/examples/mime_fwd @@ -0,0 +1,69 @@ +#!/usr/bin/perl -w + + +=head1 NAME + +mime_fwd - test the ability to embed messages + + +=head1 SYNOPSIS + +Usage: + + mime_fwd + + +=head1 DESCRIPTION + +Create a simple message, then wrap it in a "forward" and +then a "reply". + + +=head1 AUTHOR + +Eryq, eryq@zeegee.com + +=cut + +use strict; +use MIME::Lite; +use Getopt::Std; + +#------------------------------ +# main +#------------------------------ +sub main { + my %opts; + my $subj = "hello world"; + + ### Create a message: + my $msg = MIME::Lite->new(From => 'user0', + To => 'user1', + Subject => $subj, + Type => 'TEXT', + Data => ["This is the original message.\n", + "Let's see if we can embed it!\n"]); + + ### Forward it: + my $fwd = MIME::Lite->new(From => 'user1', + To => 'user2', + Subject => ("Fwd: ".$msg->get('subject')), + Type => 'message/rfc822'); + $fwd->attach($msg); + + ### Reply: + my $re = MIME::Lite->new(From => 'user2', + To => 'user0', + Bcc => 'archives', + Subject => ("Re: ".$fwd->get('subject')), + Type => 'message/rfc822'); + $re->attach($fwd); + + ### Output! + $re->print; +} +exit (&main ? 0 : -1); + +__END__ + + diff --git a/cpan/dist/MIME-Lite/examples/mime_gif b/cpan/dist/MIME-Lite/examples/mime_gif new file mode 100755 index 00000000..de4ea76c --- /dev/null +++ b/cpan/dist/MIME-Lite/examples/mime_gif @@ -0,0 +1,93 @@ +#!/usr/bin/perl -w + +=head1 NAME + +mime_gif - encode a single gif by reading data different ways + + +=head1 SYNOPSIS + +Usage: + + mime_gif /path/to/some.gif + + +=head1 DESCRIPTION + +Encode a multipart message where each part contains the same GIF +file, but where the GIF file has been read-in in different ways. +The subject line of each part will tell you how the GIF was read. + + +=head1 AUTHOR + +Eryq, eryq@zeegee.com + +=cut + +use strict; +use MIME::Lite; +use Getopt::Std; + +#------------------------------ +# main +#------------------------------ +sub main { + my %opts; + + ### Get options: + getopts('', \%opts) or die "usage error\n"; + my $gifpath = $ARGV[0] || die "missing path to GIF\n"; + + ### Create message: + my $msg = MIME::Lite->new(To => 'me@somewhere.com', + Subject => 'GIF test', + Type => 'multipart/mixed'); + + ### Read data: + open IN, "<$gifpath" or die "open $gifpath: $!\n"; + binmode IN; + my @data; + local $_ = ''; + while (read(IN, $_, 1024)) { + push @data, $_; + } + close IN; + + ### Direct path: + if (1) { + my $path = $gifpath; + $msg->attach(Subject => "Read path directly", + Path => $path, + Type => 'image/gif'); + } + + ### Cat (Unix only): + if (1) { + my $path = "cat $gifpath |"; + $msg->attach(Subject => "Cat path to pipe, and read that", + Path => $path, + Type => 'image/gif'); + } + + ### Array: + if (1) { + $msg->attach(Subject => "Read data as array", + Data => \@data, + Type => 'image/gif'); + } + + ### String: + if (1) { + $msg->attach(Subject => "Read data as string", + Data => join('', @data), + Type => 'image/gif'); + } + + ### Output: + $msg->print(\*STDOUT); +} +exit(&main ? 0 : -1); +1; +__END__ + diff --git a/cpan/dist/MIME-Lite/examples/mime_hack b/cpan/dist/MIME-Lite/examples/mime_hack new file mode 100755 index 00000000..74aa82fb --- /dev/null +++ b/cpan/dist/MIME-Lite/examples/mime_hack @@ -0,0 +1,58 @@ +#!/usr/bin/perl -w + + +=head1 NAME + +mime_hack - test the "attach to singlepart" hack + + +=head1 SYNOPSIS + +Usage: + + mime_hack /path/to/some.gif + + +=head1 DESCRIPTION + +Encode a multipart message by starting with a text message, +and attaching a GIF file to it. + + +=head1 AUTHOR + +Eryq, eryq@zeegee.com + +=cut + +use strict; +use MIME::Lite; +use Getopt::Std; + +#------------------------------ +# main +#------------------------------ +sub main { + my %opts; + my $gif = $ARGV[0] || die "usage error: missing GIF path\n"; + + ### Create a new multipart message: + my $msg = MIME::Lite->new(From => 'me@myhost.com', + To => 'you@yourhost.com', + Subject =>'Test the "attach to singlepart" hack', + Type => 'TEXT', + Data => ["This is a simple text message... ", + "can we attach a file to it?\n"]); + + ### Attach a part: + $msg->attach(Type => 'image/gif', + Path => $gif); + + ### Output! + $msg->print; +} +exit (&main ? 0 : -1); + +__END__ + + diff --git a/cpan/dist/MIME-Lite/examples/mime_longlines b/cpan/dist/MIME-Lite/examples/mime_longlines new file mode 100755 index 00000000..1d95ade9 --- /dev/null +++ b/cpan/dist/MIME-Lite/examples/mime_longlines @@ -0,0 +1,92 @@ +#!/usr/bin/perl -w + + +=head1 NAME + +mime_longlines - generate a test message with long lines + + +=head1 SYNOPSIS + +Usage: + + mime_longlines [-s] + +Options: + + -s Stringify message first, and print the *string* to STDOUT. + + +=head1 DESCRIPTION + +Assemble and print (to the standard output) a multipart message +with 5 attachments, for the purpose of "eyeballing" how well the +encoders are working. + +Each attachments holds the same data -- some 8-bit text, and a long +line consisting of 1000 "a"s followed by a few "b"s) -- but each +has a different encoding (the "Content-transfer-encoding" field +will tell you which is which). + +All of the encodings (except for the first one, "binary") should +break the long line before the b's. + + +=head1 AUTHOR + +Eryq, eryq@zeegee.com + +=cut + +use strict; +use MIME::Lite; +use Getopt::Std; + +### Set up a long message: +my $DATA = <<EOF; +Here's a line with some 8-bit characters... the "7bit" encoding should +strip them out: + + \xABFran\xE7ois M\xFCller\xBB. + +The line below is REALLY long. It contains 1000 a's, followed by some b's. +All of the encodings (except binary) should break the line before the b's: + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbb + +EOF + +#------------------------------ +# main +#------------------------------ +sub main { + my %opts; + + ### Get options: + getopts('s', \%opts) or die "usage error\n"; + + ### Create a new multipart message: + my $msg = new MIME::Lite + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'The Magnificent Five (encodings, that is)', + Type =>'multipart/mixed'; + + ### Add parts: + foreach my $enc (qw(binary 8bit 7bit quoted-printable base64)) { + $msg->attach(Type => 'TEXT', + Data => $DATA, + Encoding => $enc); + } + + ### Print: + if ($opts{'s'}) { print $msg->stringify } + else { $msg->print(\*STDOUT) } + 1; +} +exit (&main ? 0 : -1); +1; + +__END__ + diff --git a/cpan/dist/MIME-Lite/examples/mime_postcard b/cpan/dist/MIME-Lite/examples/mime_postcard new file mode 100755 index 00000000..19271717 --- /dev/null +++ b/cpan/dist/MIME-Lite/examples/mime_postcard @@ -0,0 +1,79 @@ +#!/usr/bin/perl -w + +=head1 NAME + +mime_postcard - output a multipart/alternative message + + +=head1 SYNOPSIS + +Usage: + + mime_postcard /path/to/some/graphic.jpg to@addr.com + +You can point it at a .gif file as well. +The special address "-" just causes the message to go to STDOUT. + + +=head1 DESCRIPTION + +This send a mesasge both as HTML and plain text. +I use "Data"; you would probably use "Path". + + +=head1 AUTHOR + +Eryq, eryq@zeegee.com + + +=cut + +use MIME::Lite 1.137; +use strict; +$SIG{__DIE__} = sub { die "mime_postcard: $_[0]\n" }; + +### Get graphic: +my $graphic = shift @ARGV || die "usage error: missing path to graphic\n"; +(-r $graphic) or die "$graphic unreadable\n"; +my $graphic_type; +if ($graphic =~ /\.gif$/i) { $graphic_type = "image/gif" } +elsif ($graphic =~ /\.jpe?g$/i) { $graphic_type = "image/jpeg" } +elsif ($graphic =~ /\.png$/i) { $graphic_type = "image/png" } +else { die "unknown type for: $graphic\n"; } +my $gid = "my-graphic"; + +### Get destination: +my $dest = shift @ARGV || die "missing destination\n"; + +### The top-level message: +my $msg = MIME::Lite->new(To => $dest, + Subject => 'A postcard for you', + Type => 'multipart/alternative'); + + +### Alternative #1 is the plain text: +my $plain = $msg->attach(Type => 'text/plain', + Data => ["Having a wonderful time... \n", + "wish you were looking at HTML \n", + "instead of this boring text!\n"]); + +### Alternative #2 is the HTML-with-content: +my $fancy = $msg->attach(Type => 'multipart/related'); +$fancy->attach(Type => 'text/html', + Data => [qq< <H1>Hey there!</H1> \n>, + qq< Having a <I>wonderful</I> time... take a look!\n >, + qq< <BR><IMG SRC="cid:$gid" ALT="Snapshot"> <HR> >]); +$fancy->attach(Type => $graphic_type, + Path => $graphic, + Id => $gid); + + +if ($dest eq '-') { + $msg->print; +} +else { + $msg->send; +} + + + diff --git a/cpan/dist/MIME-Lite/lib/MIME/Lite.pm b/cpan/dist/MIME-Lite/lib/MIME/Lite.pm new file mode 100644 index 00000000..ad8ac3cf --- /dev/null +++ b/cpan/dist/MIME-Lite/lib/MIME/Lite.pm @@ -0,0 +1,3227 @@ +package MIME::Lite; + + +=head1 NAME + +MIME::Lite - low-calorie MIME generator + + +=head1 SYNOPSIS + + use MIME::Lite; + +Create a single-part message: + + ### Create a new single-part message, to send a GIF file: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'Helloooooo, nurse!', + Type =>'image/gif', + Encoding =>'base64', + Path =>'hellonurse.gif' + ); + +Create a multipart message (i.e., one with attachments): + + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'multipart/mixed' + ); + + ### Add parts (each "attach" has same arguments as "new"): + $msg->attach(Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif', + Disposition => 'attachment' + ); + +Output a message: + + ### Format as a string: + $str = $msg->as_string; + + ### Print to a filehandle (say, a "sendmail" stream): + $msg->print(\*SENDMAIL); + + +Send a message: + + ### Send in the "best" way (the default is to use "sendmail"): + $msg->send; + + + +=head1 DESCRIPTION + +In the never-ending quest for great taste with fewer calories, +we proudly present: I<MIME::Lite>. + +MIME::Lite is intended as a simple, standalone module for generating +(not parsing!) MIME messages... specifically, it allows you to +output a simple, decent single- or multi-part message with text or binary +attachments. It does not require that you have the Mail:: or MIME:: +modules installed. + +You can specify each message part as either the literal data itself (in +a scalar or array), or as a string which can be given to open() to get +a readable filehandle (e.g., "<filename" or "somecommand|"). + +You don't need to worry about encoding your message data: +this module will do that for you. It handles the 5 standard MIME encodings. + +If you need more sophisticated behavior, please get the MIME-tools +package instead. I will be more likely to add stuff to that toolkit +over this one. + + +=head1 MORE EXAMPLES + +=head2 Attach a GIF to a text message + +This will create a multipart message exactly as above, but using the +"attach to singlepart" hack: + + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + + ### Attach a part: + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif' + ); + + +=head2 Attach a pre-prepared part (allows fine-tuning): + + $part = MIME::Lite->new( + Type =>'text/html', + Data =>'<H1>Hello</H1>', + ); + $part->attr('content-type.charset' => 'UTF8'); + $part->add('X-Comment' => 'A message for you'); + $msg->attach($part); + + +=head2 Send an HTML document... with images included! + + $msg = MIME::Lite->new( + To =>'you@yourhost.com', + Subject =>'HTML with in-line images!', + Type =>'multipart/related' + ); + $msg->attach(Type => 'text/html', + Data => qq{ <body> + Here's <i>my</i> image: + <img src="cid:myimage.gif"> + </body> } + ); + $msg->attach(Type => 'image/gif', + Id => 'myimage.gif', + Path => '/path/to/somefile.gif', + ); + $msg->send(); + + +=head2 Output a message to a filehandle + + ### Write it to a filehandle: + $msg->print(\*STDOUT); + + ### Write just the header: + $msg->print_header(\*STDOUT); + + ### Write just the encoded body: + $msg->print_body(\*STDOUT); + + +=head2 Get a message as a string + + ### Get entire message as a string: + $str = $msg->as_string; + + ### Get just the header: + $str = $msg->header_as_string; + + ### Get just the encoded body: + $str = $msg->body_as_string; + + +=head2 Change how messages are sent + + ### Do something like this in your 'main': + if ($I_DONT_HAVE_SENDMAIL) { + MIME::Lite->send('smtp', "smtp.myisp.net", Timeout=>60); + } + + ### Now this will do the right thing: + $msg->send; ### will now use Net::SMTP as shown above + + + + + + +=head1 FAQ + + +=head2 How do I prevent "Content" headers from showing up in my mail reader? + +Apparently, some people are using mail readers which display the MIME +headers like "Content-disposition", and they want MIME::Lite not +to generate them "because they look ugly". + +Sigh. + +Y'know, kids, those headers aren't just there for cosmetic purposes. +They help ensure that the message is I<understood> correctly by mail +readers. But okay, you asked for it, you got it... +here's how you can suppress the standard MIME headers. +Before you send the message, do this: + + $msg->scrub; + +You can scrub() any part of a multipart message independently; +just be aware that it works recursively. Before you scrub, +note the rules that I follow: + +=over 4 + +=item Content-type + +You can safely scrub the "content-type" attribute if, and only if, +the part is of type "text/plain" with charset "us-ascii". + +=item Content-transfer-encoding + +You can safely scrub the "content-transfer-encoding" attribute +if, and only if, the part uses "7bit", "8bit", or "binary" encoding. +You are far better off doing this if your lines are under 1000 +characters. Generally, that means you I<can> scrub it for plain +text, and you can I<not> scrub this for images, etc. + +=item Content-disposition + +You can safely scrub the "content-disposition" attribute +if you trust the mail reader to do the right thing when it decides +whether to show an attachment inline or as a link. Be aware +that scrubbing both the content-disposition and the content-type +means that there is no way to "recommend" a filename for the attachment! + +B<Note:> there are reports of brain-dead MUAs out there that +do the wrong thing if you I<provide> the content-disposition. +If your attachments keep showing up inline or vice-versa, +try scrubbing this attribute. + +=item Content-length + +You can always scrub "content-length" safely. + +=back + + +=head2 How do I give my attachment a [different] recommended filename? + +By using the Filename option (which is different from Path!): + + $msg->attach(Type => "image/gif", + Path => "/here/is/the/real/file.GIF", + Filename => "logo.gif"); + +You should I<not> put path information in the Filename. + + + +=head1 PUBLIC INTERFACE + +=head2 Global configuration + +To alter the way the entire module behaves, you have the following +methods/options: + +=over 4 + + +=item MIME::Lite->header_order() + +When used as a L<classmethod|/send>, this changes the default +order in which headers are output for I<all> messages. + + +=item MIME::Lite->quiet() + +This L<classmethod|/quiet> can be used to suppress/unsuppress +all warnings coming from this module. + + +=item MIME::Lite->send() + +When used as a L<classmethod|/send>, this can be used to specify +a different default mechanism for sending message. +The initial default is: + + MIME::Lite->send("sendmail", "/usr/lib/sendmail -t -oi -oem"); + +However, you should consider the similar but smarter and taint-safe variant: + + MIME::Lite->send("sendmail"); + +Or, for non-Unix users: + + MIME::Lite->send("smtp"); + + +=item $MIME::Lite::PARANOID + +If true, we won't attempt to use MIME::Base64/MIME::QuotedPrint, even +if they're available. +Default is B<false>. + + +=item $MIME::Lite::AUTO_ENCODE + +If true, automatically choose the encoding from the content type. +Default is B<true>. + + +=item $MIME::Lite::AUTO_CC + +If true, automatically send to the Cc/Bcc addresses for send_by_smtp(). +Default is B<true>. + + +=item $MIME::Lite::AUTO_VERIFY + +If true, check paths to attachments right before printing, raising an exception +if any path is unreadable. +Default is B<true>. + +=back + +=cut + +require 5.004; ### for /c modifier in m/\G.../gc modifier + +use Carp; +use FileHandle; + +use strict; +use vars qw( + $AUTO_CC + $AUTO_ENCODE + $AUTO_VERIFY + $PARANOID + $QUIET + $VANILLA + $VERSION + ); + + + +#============================== +#============================== +# +# GLOBALS, EXTERNAL/CONFIGURATION... + +### The package version, both in 1.23 style *and* usable by MakeMaker: +$VERSION = substr q$Revision: 2.108 $, 10; + +### Don't warn me about dangerous activities: +$QUIET = undef; + +### Set this true if you don't want to use MIME::Base64/MIME::QuotedPrint: +$PARANOID = 0; + +### Unsupported (for tester use): don't qualify boundary with time/pid: +$VANILLA = 0; + +### Automatically choose encoding from content type: +$AUTO_ENCODE = 1; + +### Automatically interpret CC/BCC for SMTP: +$AUTO_CC = 1; + +### Check paths right before printing: +$AUTO_VERIFY = 1; + + +#============================== +#============================== +# +# GLOBALS, INTERNAL... + +### Find sendmail: +my $SENDMAIL = "/usr/lib/sendmail"; +(-x $SENDMAIL) or ($SENDMAIL = "/usr/sbin/sendmail"); +(-x $SENDMAIL) or ($SENDMAIL = "sendmail"); + +### Our sending facilities: +my $Sender = "sendmail"; +my %SenderArgs = ( + "sendmail" => ["$SENDMAIL -t -oi -oem"], + "smtp" => [], + "sub" => [], +); + +### Boundary counter: +my $BCount = 0; + +### Known Mail/MIME fields... these, plus some general forms like +### "x-*", are recognized by build(): +my %KnownField = map {$_=>1} +qw( + bcc cc comments date encrypted + from keywords message-id mime-version organization + received references reply-to return-path sender + subject to + + approved + ); + +### What external packages do we use for encoding? +my @Uses; + +### Header order: +my @FieldOrder; + + +#============================== +#============================== +# +# PRIVATE UTILITY FUNCTIONS... + +#------------------------------ +# +# fold STRING +# +# Make STRING safe as a field value. Remove leading/trailing whitespace, +# and make sure newlines are represented as newline+space + +sub fold { + my $str = shift; + $str =~ s/^\s*|\s*$//g; ### trim + $str =~ s/\n/\n /g; + $str; +} + +#------------------------------ +# +# gen_boundary +# +# Generate a new boundary to use. +# The unsupported $VANILLA is for test purposes only. + +sub gen_boundary { + return ("_----------=_".($VANILLA ? '' : int(time).$$).$BCount++); +} + +#------------------------------ +# +# known_field FIELDNAME +# +# Is this a recognized Mail/MIME field? + +sub known_field { + my $field = lc(shift); + $KnownField{$field} or ($field =~ m{^(content|resent|x)-.}); +} + +#------------------------------ +# +# is_mime_field FIELDNAME +# +# Is this a field I manage? + +sub is_mime_field { + $_[0] =~ /^(mime\-|content\-)/i; +} + +#------------------------------ +# +# extract_addrs STRING +# +# Split STRING into an array of email addresses: somewhat of a KLUDGE. +# +# Unless paranoid, we try to load the real code before supplying our own. + +my $ATOM = '[^ \000-\037()<>@,;:\134"\056\133\135]+'; +my $QSTR = '".*?"'; +my $WORD = '(?:' . $QSTR . '|' . $ATOM . ')'; +my $DOMAIN = '(?:' . $ATOM . '(?:' . '\\.' . $ATOM . ')*' . ')'; +my $LOCALPART = '(?:' . $WORD . '(?:' . '\\.' . $WORD . ')*' . ')'; +my $ADDR = '(?:' . $LOCALPART . '@' . $DOMAIN . ')'; +my $PHRASE = '(?:' . $WORD . ')+'; +my $SEP = "(?:^\\s*|\\s*,\\s*)"; ### before elems in a list + +sub my_extract_addrs { + my $str = shift; + my @addrs; + $str =~ s/\s/ /g; ### collapse whitespace + + pos($str) = 0; + while ($str !~ m{\G\s*\Z}gco) { + ### print STDERR "TACKLING: ".substr($str, pos($str))."\n"; + if ($str =~ m{\G$SEP$PHRASE\s*<\s*($ADDR)\s*>}gco) {push @addrs,$1} + elsif ($str =~ m{\G$SEP($ADDR)}gco) {push @addrs,$1} + elsif ($str =~ m{\G$SEP($ATOM)}gco) {push @addrs,$1} + else { + my $problem = substr($str, pos($str)); + die "can't extract address at <$problem> in <$str>\n"; + } + } + return @addrs; +} + +if (eval "require Mail::Address") { + push @Uses, "A$Mail::Address::VERSION"; + eval q{ + sub extract_addrs { + return map { $_->format } Mail::Address->parse($_[0]); + } + }; ### q +} +else { + eval q{ + sub extract_addrs { + return my_extract_addrs(@_); + } + }; ### q +} ### if + + + +#============================== +#============================== +# +# PRIVATE ENCODING FUNCTIONS... + +#------------------------------ +# +# encode_base64 STRING +# +# Encode the given string using BASE64. +# Unless paranoid, we try to load the real code before supplying our own. + +if (!$PARANOID and eval "require MIME::Base64") { + import MIME::Base64 qw(encode_base64); + push @Uses, "B$MIME::Base64::VERSION"; +} +else { + eval q{ +sub encode_base64 { + my $res = ""; + my $eol = "\n"; + + pos($_[0]) = 0; ### thanks, Andreas! + while ($_[0] =~ /(.{1,45})/gs) { + $res .= substr(pack('u', $1), 1); + chop($res); + } + $res =~ tr|` -_|AA-Za-z0-9+/|; + + ### Fix padding at the end: + my $padding = (3 - length($_[0]) % 3) % 3; + $res =~ s/.{$padding}$/'=' x $padding/e if $padding; + + ### Break encoded string into lines of no more than 76 characters each: + $res =~ s/(.{1,76})/$1$eol/g if (length $eol); + return $res; +} ### sub + } ### q +} ### if + +#------------------------------ +# +# encode_qp STRING +# +# Encode the given string, LINE BY LINE, using QUOTED-PRINTABLE. +# Stolen from MIME::QuotedPrint by Gisle Aas, with a slight bug fix: we +# break lines earlier. Notice that this seems not to work unless +# encoding line by line. +# +# Unless paranoid, we try to load the real code before supplying our own. + +if (!$PARANOID and eval "require MIME::QuotedPrint") { + import MIME::QuotedPrint qw(encode_qp); + push @Uses, "Q$MIME::QuotedPrint::VERSION"; +} +else { + eval q{ +sub encode_qp { + my $res = shift; + local($_); + $res =~ s/([^ \t\n!-<>-~])/sprintf("=%02X", ord($1))/eg; ### rule #2,#3 + $res =~ s/([ \t]+)$/ + join('', map { sprintf("=%02X", ord($_)) } + split('', $1) + )/egm; ### rule #3 (encode whitespace at eol) + + ### rule #5 (lines shorter than 76 chars, but can't break =XX escapes: + my $brokenlines = ""; + $brokenlines .= "$1=\n" while $res =~ s/^(.{70}([^=]{2})?)//; ### 70 was 74 + $brokenlines =~ s/=\n$// unless length $res; + "$brokenlines$res"; +} ### sub + } ### q +} ### if + + +#------------------------------ +# +# encode_8bit STRING +# +# Encode the given string using 8BIT. +# This breaks long lines into shorter ones. + +sub encode_8bit { + my $str = shift; + $str =~ s/^(.{990})/$1\n/mg; + $str; +} + +#------------------------------ +# +# encode_7bit STRING +# +# Encode the given string using 7BIT. +# This NO LONGER protects people through encoding. + +sub encode_7bit { + my $str = shift; + $str =~ s/[\x80-\xFF]//g; + $str =~ s/^(.{990})/$1\n/mg; + $str; +} + +#============================== +#============================== + +=head2 Construction + +=over 4 + +=cut + + +#------------------------------ + +=item new [PARAMHASH] + +I<Class method, constructor.> +Create a new message object. + +If any arguments are given, they are passed into C<build()>; otherwise, +just the empty object is created. + +=cut + +sub new { + my $class = shift; + + ### Create basic object: + my $self = { + Attrs => {}, ### MIME attributes + Header => [], ### explicit message headers + Parts => [], ### array of parts + }; + bless $self, $class; + + ### Build, if needed: + return (@_ ? $self->build(@_) : $self); +} + + +#------------------------------ + +=item attach PART + +=item attach PARAMHASH... + +I<Instance method.> +Add a new part to this message, and return the new part. + +If you supply a single PART argument, it will be regarded +as a MIME::Lite object to be attached. Otherwise, this +method assumes that you are giving in the pairs of a PARAMHASH +which will be sent into C<new()> to create the new part. + +One of the possibly-quite-useful hacks thrown into this is the +"attach-to-singlepart" hack: if you attempt to attach a part (let's +call it "part 1") to a message that doesn't have a content-type +of "multipart" or "message", the following happens: + +=over 4 + +=item * + +A new part (call it "part 0") is made. + +=item * + +The MIME attributes and data (but I<not> the other headers) +are cut from the "self" message, and pasted into "part 0". + +=item * + +The "self" is turned into a "multipart/mixed" message. + +=item * + +The new "part 0" is added to the "self", and I<then> "part 1" is added. + +=back + +One of the nice side-effects is that you can create a text message +and then add zero or more attachments to it, much in the same way +that a user agent like Netscape allows you to do. + +=cut + +sub attach { + my $self = shift; + + ### Create new part, if necessary: + my $part1 = ((@_ == 1) ? shift : ref($self)->new(Top=>0, @_)); + + ### Do the "attach-to-singlepart" hack: + if ($self->attr('content-type') !~ m{^(multipart|message)/}i) { + + ### Create part zero: + my $part0 = ref($self)->new; + + ### Cut MIME stuff from self, and paste into part zero: + foreach (qw(Attrs Data Path FH)) { + $part0->{$_} = $self->{$_}; delete($self->{$_}); + } + $part0->top_level(0); ### clear top-level attributes + + ### Make self a top-level multipart: + $self->{Attrs} ||= {}; ### reset + $self->attr('content-type' => 'multipart/mixed'); + $self->attr('content-type.boundary' => gen_boundary()); + $self->attr('content-transfer-encoding' => '7bit'); + $self->top_level(1); ### activate top-level attributes + + ### Add part 0: + push @{$self->{Parts}}, $part0; + } + + ### Add the new part: + push @{$self->{Parts}}, $part1; + $part1; +} + +#------------------------------ + +=item build [PARAMHASH] + +I<Class/instance method, initializer.> +Create (or initialize) a MIME message object. +Normally, you'll use the following keys in PARAMHASH: + + * Data, FH, or Path (either one of these, or none if multipart) + * Type (e.g., "image/jpeg") + * From, To, and Subject (if this is the "top level" of a message) + +The PARAMHASH can contain the following keys: + +=over 4 + +=item (fieldname) + +Any field you want placed in the message header, taken from the +standard list of header fields (you don't need to worry about case): + + Approved Encrypted Received Sender + Bcc From References Subject + Cc Keywords Reply-To To + Comments Message-ID Resent-* X-* + Content-* MIME-Version Return-Path + Date Organization + +To give experienced users some veto power, these fields will be set +I<after> the ones I set... so be careful: I<don't set any MIME fields> +(like C<Content-type>) unless you know what you're doing! + +To specify a fieldname that's I<not> in the above list, even one that's +identical to an option below, just give it with a trailing C<":">, +like C<"My-field:">. When in doubt, that I<always> signals a mail +field (and it sort of looks like one too). + +=item Data + +I<Alternative to "Path" or "FH".> +The actual message data. This may be a scalar or a ref to an array of +strings; if the latter, the message consists of a simple concatenation +of all the strings in the array. + +=item Datestamp + +I<Optional.> +If given true (or omitted), we force the creation of a C<Date:> field +stamped with the current date/time if this is a top-level message. +You may want this if using L<send_by_smtp()|/send_by_smtp>. +If you don't want this to be done, either provide your own Date +or explicitly set this to false. + +=item Disposition + +I<Optional.> +The content disposition, C<"inline"> or C<"attachment">. +The default is C<"inline">. + +=item Encoding + +I<Optional.> +The content transfer encoding that should be used to encode your data: + + Use encoding: | If your message contains: + ------------------------------------------------------------ + 7bit | Only 7-bit text, all lines <1000 characters + 8bit | 8-bit text, all lines <1000 characters + quoted-printable | 8-bit text or long lines (more reliable than "8bit") + base64 | Largely non-textual data: a GIF, a tar file, etc. + +The default is taken from the Type; generally it is "binary" (no +encoding) for text/*, message/*, and multipart/*, and "base64" for +everything else. A value of C<"binary"> is generally I<not> suitable +for sending anything but ASCII text files with lines under 1000 +characters, so consider using one of the other values instead. + +In the case of "7bit"/"8bit", long lines are automatically chopped to +legal length; in the case of "7bit", all 8-bit characters are +automatically I<removed>. This may not be what you want, so pick your +encoding well! For more info, see L<"A MIME PRIMER">. + +=item FH + +I<Alternative to "Data" or "Path".> +Filehandle containing the data, opened for reading. +See "ReadNow" also. + +=item Filename + +I<Optional.> +The name of the attachment. You can use this to supply a +recommended filename for the end-user who is saving the attachment +to disk. You only need this if the filename at the end of the +"Path" is inadequate, or if you're using "Data" instead of "Path". +You should I<not> put path information in here (e.g., no "/" +or "\" or ":" characters should be used). + +=item Id + +I<Optional.> +Same as setting "content-id". + +=item Length + +I<Optional.> +Set the content length explicitly. Normally, this header is automatically +computed, but only under certain circumstances (see L<"Limitations">). + +=item Path + +I<Alternative to "Data" or "FH".> +Path to a file containing the data... actually, it can be any open()able +expression. If it looks like a path, the last element will automatically +be treated as the filename. +See "ReadNow" also. + +=item ReadNow + +I<Optional, for use with "Path".> +If true, will open the path and slurp the contents into core now. +This is useful if the Path points to a command and you don't want +to run the command over and over if outputting the message several +times. B<Fatal exception> raised if the open fails. + +=item Top + +I<Optional.> +If defined, indicates whether or not this is a "top-level" MIME message. +The parts of a multipart message are I<not> top-level. +Default is true. + +=item Type + +I<Optional.> +The MIME content type, or one of these special values (case-sensitive): + + "TEXT" means "text/plain" + "BINARY" means "application/octet-stream" + +The default is C<"TEXT">. + +=back + +A picture being worth 1000 words (which +is of course 2000 bytes, so it's probably more of an "icon" than a "picture", +but I digress...), here are some examples: + + $msg = MIME::Lite->build( + From => 'yelling@inter.com', + To => 'stocking@fish.net', + Subject => "Hi there!", + Type => 'TEXT', + Encoding => '7bit', + Data => "Just a quick note to say hi!"); + + $msg = MIME::Lite->build( + From => 'dorothy@emerald-city.oz', + To => 'gesundheit@edu.edu.edu', + Subject => "A gif for U" + Type => 'image/gif', + Path => "/home/httpd/logo.gif"); + + $msg = MIME::Lite->build( + From => 'laughing@all.of.us', + To => 'scarlett@fiddle.dee.de', + Subject => "A gzipp'ed tar file", + Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + +To show you what's really going on, that last example could also +have been written: + + $msg = new MIME::Lite; + $msg->build(Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + $msg->add(From => "laughing@all.of.us"); + $msg->add(To => "scarlett@fiddle.dee.de"); + $msg->add(Subject => "A gzipp'ed tar file"); + +=cut + +sub build { + my $self = shift; + my %params = @_; + my @params = @_; + my $key; + + ### Miko's note: reorganized to check for exactly one of Data, Path, or FH + (defined($params{Data})+defined($params{Path})+defined($params{FH}) <= 1) + or croak "supply exactly zero or one of (Data|Path|FH).\n"; + + ### Create new instance, if necessary: + ref($self) or $self = $self->new; + + + ### CONTENT-TYPE.... + ### + + ### Get content-type: + my $type = ($params{Type} || 'TEXT'); + ($type eq 'TEXT') and $type = 'text/plain'; + ($type eq 'BINARY') and $type = 'application/octet-stream'; + $type = lc($type); + $self->attr('content-type' => $type); + + ### Get some basic attributes from the content type: + my $is_multipart = ($type =~ m{^(multipart)/}i); + + ### Add in the multipart boundary: + if ($is_multipart) { + my $boundary = gen_boundary(); + $self->attr('content-type.boundary' => $boundary); + } + + + ### CONTENT-ID... + ### + $self->attr('content-id' => $params{Id}) if defined($params{Id}); + + + ### DATA OR PATH... + ### Note that we must do this *after* we get the content type, + ### in case read_now() is invoked, since it needs the binmode(). + + ### Get data, as... + ### ...either literal data: + if (defined($params{Data})) { + $self->data($params{Data}); + } + ### ...or a path to data: + elsif (defined($params{Path})) { + $self->path($params{Path}); ### also sets filename + $self->read_now if $params{ReadNow}; + } + ### ...or a filehandle to data: + ### Miko's note: this part works much like the path routine just above, + elsif (defined($params{FH})) { + $self->fh($params{FH}); + $self->read_now if $params{ReadNow}; ### implement later + } + + + ### FILENAME... (added by Ian Smith <ian@safeway.dircon.co.uk> on 8/4/97) + ### Need this to make sure the filename is added. The Filename + ### attribute is ignored, otherwise. + if (defined($params{Filename})) { + $self->filename($params{Filename}); + } + + + ### CONTENT-TRANSFER-ENCODING... + ### + + ### Get it: + my $enc = ($params{Encoding} || + ($AUTO_ENCODE and $self->suggest_encoding($type)) || + 'binary'); + $self->attr('content-transfer-encoding' => lc($enc)); + + ### Sanity check: + if ($type =~ m{^(multipart|message)/}) { + ($enc =~ m{^(7bit|8bit|binary)\Z}) or + croak "illegal MIME: can't have encoding $enc with type $type\n"; + } + + ### CONTENT-DISPOSITION... + ### Default is inline for single, none for multis: + ### + my $disp = ($params{Disposition} or ($is_multipart ? undef : 'inline')); + $self->attr('content-disposition' => $disp); + + ### CONTENT-LENGTH... + ### + my $length; + if (exists($params{Length})) { ### given by caller: + $self->attr('content-length' => $params{Length}); + } + else { ### compute it ourselves + $self->get_length; + } + + ### Init the top-level fields: + my $is_top = defined($params{Top}) ? $params{Top} : 1; + $self->top_level($is_top); + + ### Datestamp if desired: + my $ds_wanted = $params{Datestamp}; + my $ds_defaulted = ($is_top and !exists($params{Datestamp})); + if (($ds_wanted or $ds_defaulted) and !exists($params{Date})) { + my ($u_wdy, $u_mon, $u_mdy, $u_time, $u_y4) = + split /\s+/, gmtime().""; ### should be non-locale-dependent + my $date = "$u_wdy, $u_mdy $u_mon $u_y4 $u_time UT"; + $self->add("date", $date); + } + + ### Set message headers: + my @paramz = @params; + my $field; + while (@paramz) { + my ($tag, $value) = (shift(@paramz), shift(@paramz)); + + ### Get tag, if a tag: + if ($tag =~ /^-(.*)/) { ### old style, backwards-compatibility + $field = lc($1); + } + elsif ($tag =~ /^(.*):$/) { ### new style + $field = lc($1); + } + elsif (known_field($field = lc($tag))) { ### known field + ### no-op + } + else { ### not a field: + next; + } + + ### Add it: + $self->add($field, $value); + } + + ### Done! + $self; +} + +=back + +=cut + + +#============================== +#============================== + +=head2 Setting/getting headers and attributes + +=over 4 + +=cut + +#------------------------------ +# +# top_level ONOFF +# +# Set/unset the top-level attributes and headers. +# This affects "MIME-Version" and "X-Mailer". + +sub top_level { + my ($self, $onoff) = @_; + if ($onoff) { + $self->attr('MIME-Version' => '1.0'); + my $uses = (@Uses ? ("(" . join("; ", @Uses) . ")") : ''); + $self->replace('X-Mailer' => "MIME::Lite $VERSION $uses") + unless $VANILLA; + } + else { + $self->attr('MIME-Version' => undef); + $self->delete('X-Mailer'); + } +} + +#------------------------------ + +=item add TAG,VALUE + +I<Instance method.> +Add field TAG with the given VALUE to the end of the header. +The TAG will be converted to all-lowercase, and the VALUE +will be made "safe" (returns will be given a trailing space). + +B<Beware:> any MIME fields you "add" will override any MIME +attributes I have when it comes time to output those fields. +Normally, you will use this method to add I<non-MIME> fields: + + $msg->add("Subject" => "Hi there!"); + +Giving VALUE as an arrayref will cause all those values to be added. +This is only useful for special multiple-valued fields like "Received": + + $msg->add("Received" => ["here", "there", "everywhere"] + +Giving VALUE as the empty string adds an invisible placeholder +to the header, which can be used to suppress the output of +the "Content-*" fields or the special "MIME-Version" field. +When suppressing fields, you should use replace() instead of add(): + + $msg->replace("Content-disposition" => ""); + +I<Note:> add() is probably going to be more efficient than C<replace()>, +so you're better off using it for most applications if you are +certain that you don't need to delete() the field first. + +I<Note:> the name comes from Mail::Header. + +=cut + +sub add { + my $self = shift; + my $tag = lc(shift); + my $value = shift; + + ### If a dangerous option, warn them: + carp "Explicitly setting a MIME header field ($tag) is dangerous:\n". + "use the attr() method instead.\n" + if (is_mime_field($tag) && !$QUIET); + + ### Get array of clean values: + my @vals = ((ref($value) and (ref($value) eq 'ARRAY')) + ? @{$value} + : ($value.'')); + map { s/\n/\n /g } @vals; + + ### Add them: + foreach (@vals) { + push @{$self->{Header}}, [$tag, $_]; + } +} + +#------------------------------ + +=item attr ATTR,[VALUE] + +I<Instance method.> +Set MIME attribute ATTR to the string VALUE. +ATTR is converted to all-lowercase. +This method is normally used to set/get MIME attributes: + + $msg->attr("content-type" => "text/html"); + $msg->attr("content-type.charset" => "US-ASCII"); + $msg->attr("content-type.name" => "homepage.html"); + +This would cause the final output to look something like this: + + Content-type: text/html; charset=US-ASCII; name="homepage.html" + +Note that the special empty sub-field tag indicates the anonymous +first sub-field. + +Giving VALUE as undefined will cause the contents of the named +subfield to be deleted. + +Supplying no VALUE argument just returns the attribute's value: + + $type = $msg->attr("content-type"); ### returns "text/html" + $name = $msg->attr("content-type.name"); ### returns "homepage.html" + +=cut + +sub attr { + my ($self, $attr, $value) = @_; + $attr = lc($attr); + + ### Break attribute name up: + my ($tag, $subtag) = split /\./, $attr; + defined($subtag) or $subtag = ''; + + ### Set or get? + if (@_ > 2) { ### set: + $self->{Attrs}{$tag} ||= {}; ### force hash + delete $self->{Attrs}{$tag}{$subtag}; ### delete first + if (defined($value)) { ### set... + $value =~ s/[\r\n]//g; ### make clean + $self->{Attrs}{$tag}{$subtag} = $value; + } + } + + ### Return current value: + $self->{Attrs}{$tag}{$subtag}; +} + +sub _safe_attr { + my ($self, $attr) = @_; + my $v = $self->attr($attr); + defined($v) ? $v : ''; +} + +#------------------------------ + +=item delete TAG + +I<Instance method.> +Delete field TAG with the given VALUE to the end of the header. +The TAG will be converted to all-lowercase. + + $msg->delete("Subject"); + +I<Note:> the name comes from Mail::Header. + +=cut + +sub delete { + my $self = shift; + my $tag = lc(shift); + + ### Delete from the header: + my $hdr = []; + my $field; + foreach $field (@{$self->{Header}}) { + push @$hdr, $field if ($field->[0] ne $tag); + } + $self->{Header} = $hdr; + $self; +} + + +#------------------------------ + +=item field_order FIELD,...FIELD + +I<Class/instance method.> +Change the order in which header fields are output for this object: + + $msg->field_order('from', 'to', 'content-type', 'subject'); + +When used as a class method, changes the default settings for +all objects: + + MIME::Lite->field_order('from', 'to', 'content-type', 'subject'); + +Case does not matter: all field names will be coerced to lowercase. +In either case, supply the empty array to restore the default ordering. + +=cut + +sub field_order { + my $self = shift; + if (ref($self)) { $self->{FieldOrder} = [ map { lc($_) } @_ ] } + else { @FieldOrder = map { lc($_) } @_ } +} + +#------------------------------ + +=item fields + +I<Instance method.> +Return the full header for the object, as a ref to an array +of C<[TAG, VALUE]> pairs, where each TAG is all-lowercase. +Note that any fields the user has explicitly set will override the +corresponding MIME fields that we would otherwise generate. +So, don't say... + + $msg->set("Content-type" => "text/html; charset=US-ASCII"); + +unless you want the above value to override the "Content-type" +MIME field that we would normally generate. + +I<Note:> I called this "fields" because the header() method of +Mail::Header returns something different, but similar enough to +be confusing. + +You can change the order of the fields: see L</header_order>. +You really shouldn't need to do this, but some people have to +deal with broken mailers. + +=cut + +sub fields { + my $self = shift; + my @fields; + + ### Get a lookup-hash of all *explicitly-given* fields: + my %explicit = map { $_->[0] => 1 } @{$self->{Header}}; + + ### Start with any MIME attributes not given explicitly: + my $tag; + foreach $tag (sort keys %{$self->{Attrs}}) { + + ### Skip if explicit: + next if ($explicit{$tag}); + + ### Skip if no subtags: + my @subtags = keys %{$self->{Attrs}{$tag}}; + @subtags or next; + + ### Create string: + my $value; + defined($value = $self->{Attrs}{$tag}{''}) or next; ### need default + foreach (sort @subtags) { + next if ($_ eq ''); + $value .= qq{; $_="$self->{Attrs}{$tag}{$_}"}; + } + + ### Add to running fields; + push @fields, [$tag, $value]; + } + + ### Add remaining fields (note that we duplicate the array for safety): + foreach (@{$self->{Header}}) { + push @fields, [@{$_}]; + } + + ### Final step: + ### If a suggested ordering was given, we "sort" by that ordering. + ### The idea is that we give each field a numeric rank, which is + ### (1000 * order(field)) + origposition. + my @order = @{$self->{FieldOrder} || []}; ### object-specific + @order or @order = @FieldOrder; ### no? maybe generic + if (@order) { ### either? + + ### Create hash mapping field names to 1-based rank: + my %rank = map {$order[$_] => (1+$_)} (0..$#order); + + ### Create parallel array to @fields, called @ranked. + ### It contains fields tagged with numbers like 2003, where the + ### 3 is the original 0-based position, and 2000 indicates that + ### we wanted ths type of field to go second. + my @ranked = map { + [ + ($_ + 1000*($rank{lc($fields[$_][0])} || (2+$#order))), + $fields[$_] + ] + } (0..$#fields); + # foreach (@ranked) { + # print STDERR "RANKED: $_->[0] $_->[1][0] $_->[1][1]\n"; + # } + + ### That was half the Schwartzian transform. Here's the rest: + @fields = map { $_->[1] } + sort { $a->[0] <=> $b->[0] } + @ranked; + } + + ### Done! + return \@fields; +} + + +#------------------------------ + +=item filename [FILENAME] + +I<Instance method.> +Set the filename which this data will be reported as. +This actually sets both "standard" attributes. + +With no argument, returns the filename as dictated by the +content-disposition. + +=cut + +sub filename { + my ($self, $filename) = @_; + if (@_ > 1) { + $self->attr('content-type.name' => $filename); + $self->attr('content-disposition.filename' => $filename); + } + $self->attr('content-disposition.filename'); +} + +#------------------------------ + +=item get TAG,[INDEX] + +I<Instance method.> +Get the contents of field TAG, which might have been set +with set() or replace(). Returns the text of the field. + + $ml->get('Subject', 0); + +If the optional 0-based INDEX is given, then we return the INDEX'th +occurence of field TAG. Otherwise, we look at the context: +In a scalar context, only the first (0th) occurence of the +field is returned; in an array context, I<all> occurences are returned. + +I<Warning:> this should only be used with non-MIME fields. +Behavior with MIME fields is TBD, and will raise an exception for now. + +=cut + +sub get { + my ($self, $tag, $index) = @_; + $tag = lc($tag); + croak "get: can't be used with MIME fields\n" if is_mime_field($tag); + + my @all = map { ($_->[0] eq $tag) ? $_->[1] : ()} @{$self->{Header}}; + (defined($index) ? $all[$index] : (wantarray ? @all : $all[0])); +} + +#------------------------------ + +=item get_length + +I<Instance method.> +Recompute the content length for the message I<if the process is trivial>, +setting the "content-length" attribute as a side-effect: + + $msg->get_length; + +Returns the length, or undefined if not set. + +I<Note:> the content length can be difficult to compute, since it +involves assembling the entire encoded body and taking the length +of it (which, in the case of multipart messages, means freezing +all the sub-parts, etc.). + +This method only sets the content length to a defined value if the +message is a singlepart with C<"binary"> encoding, I<and> the body is +available either in-core or as a simple file. Otherwise, the content +length is set to the undefined value. + +Since content-length is not a standard MIME field anyway (that's right, kids: +it's not in the MIME RFCs, it's an HTTP thing), this seems pretty fair. + +=cut + +#---- +# Miko's note: I wasn't quite sure how to handle this, so I waited to hear +# what you think. Given that the content-length isn't always required, +# and given the performance cost of calculating it from a file handle, +# I thought it might make more sense to add some some sort of computelength +# property. If computelength is false, then the length simply isn't +# computed. What do you think? +# +# Eryq's reply: I agree; for now, we can silently leave out the content-type. + +sub get_length { + my $self = shift; + + my $is_multipart = ($self->attr('content-type') =~ m{^multipart/}i); + my $enc = lc($self->attr('content-transfer-encoding') || 'binary'); + my $length; + if (!$is_multipart && ($enc eq "binary")){ ### might figure it out cheap: + if (defined($self->{Data})) { ### it's in core + $length = length($self->{Data}); + } + elsif (defined($self->{FH})) { ### it's in a filehandle + ### no-op: it's expensive, so don't bother + } + elsif (defined($self->{Path})) { ### it's a simple file! + $length = (-s $self->{Path}) if (-e $self->{Path}); + } + } + $self->attr('content-length' => $length); + return $length; +} + +#------------------------------ + +=item replace TAG,VALUE + +I<Instance method.> +Delete all occurences of fields named TAG, and add a new +field with the given VALUE. TAG is converted to all-lowercase. + +B<Beware> the special MIME fields (MIME-version, Content-*): +if you "replace" a MIME field, the replacement text will override +the I<actual> MIME attributes when it comes time to output that field. +So normally you use attr() to change MIME fields and add()/replace() to +change I<non-MIME> fields: + + $msg->replace("Subject" => "Hi there!"); + +Giving VALUE as the I<empty string> will effectively I<prevent> that +field from being output. This is the correct way to suppress +the special MIME fields: + + $msg->replace("Content-disposition" => ""); + +Giving VALUE as I<undefined> will just cause all explicit values +for TAG to be deleted, without having any new values added. + +I<Note:> the name of this method comes from Mail::Header. + +=cut + +sub replace { + my ($self, $tag, $value) = @_; + $self->delete($tag); + $self->add($tag, $value) if defined($value); +} + + +#------------------------------ + +=item scrub + +I<Instance method.> +B<This is Alpha code. If you use it, please let me know how it goes.> +Recursively goes through the "parts" tree of this message and tries +to find MIME attributes that can be removed. +With an array argument, removes exactly those attributes; e.g.: + + $msg->scrub(['content-disposition', 'content-length']); + +Is the same as recursively doing: + + $msg->replace('Content-disposition' => ''); + $msg->replace('Content-length' => ''); + +=cut + +sub scrub { + my ($self, @a) = @_; + my ($expl) = @a; + local $QUIET = 1; + + ### Scrub me: + if (!@a) { ### guess + + ### Scrub length always: + $self->replace('content-length', ''); + + ### Scrub disposition if no filename, or if content-type has same info: + if (!$self->_safe_attr('content-disposition.filename') || + $self->_safe_attr('content-type.name')) { + $self->replace('content-disposition', ''); + } + + ### Scrub encoding if effectively unencoded: + if ($self->_safe_attr('content-transfer-encoding') =~ + /^(7bit|8bit|binary)$/i) { + $self->replace('content-transfer-encoding', ''); + } + + ### Scrub charset if US-ASCII: + if ($self->_safe_attr('content-type.charset') =~ /^(us-ascii)/i) { + $self->attr('content-type.charset' => undef); + } + + ### TBD: this is not really right for message/digest: + if ((keys %{$self->{Attrs}{'content-type'}} == 1) and + ($self->_safe_attr('content-type') eq 'text/plain')) { + $self->replace('content-type', ''); + } + } + elsif ($expl and (ref($expl) eq 'ARRAY')) { + foreach (@{$expl}) { $self->replace($_, ''); } + } + + ### Scrub my kids: + foreach (@{$self->{Parts}}) { $_->scrub(@a); } +} + +=back + +=cut + + +#============================== +#============================== + +=head2 Setting/getting message data + +=over 4 + +=cut + +#------------------------------ + +=item binmode [OVERRIDE] + +I<Instance method.> +With no argument, returns whether or not it thinks that the data +(as given by the "Path" argument of C<build()>) should be read using +binmode() (for example, when C<read_now()> is invoked). + +The default behavior is that any content type other than +C<text/*> or C<message/*> is binmode'd; this should in general work fine. + +With a defined argument, this method sets an explicit "override" +value. An undefined argument unsets the override. +The new current value is returned. + +=cut + +sub binmode { + my $self = shift; + $self->{Binmode} = shift if (@_); ### argument? set override + return (defined($self->{Binmode}) + ? $self->{Binmode} + : ($self->attr("content-type") !~ m{^(text|message)/}i)); +} + +#------------------------------ + +=item data [DATA] + +I<Instance method.> +Get/set the literal DATA of the message. The DATA may be +either a scalar, or a reference to an array of scalars (which +will simply be joined). + +I<Warning:> setting the data causes the "content-length" attribute +to be recomputed (possibly to nothing). + +=cut + +sub data { + my $self = shift; + if (@_) { + $self->{Data} = ((ref($_[0]) eq 'ARRAY') ? join('', @{$_[0]}) : $_[0]); + $self->get_length; + } + $self->{Data}; +} + + +#------------------------------ + +=item path [PATH] + +Get/set the PATH to the message data. + +I<Warning:> setting the path recomputes any existing "content-length" field, +and re-sets the "filename" (to the last element of the path if it +looks like a simple path, and to nothing if not). + +=cut + +sub path { + my $self = shift; + if (@_) { + + ### Set the path, and invalidate the content length: + $self->{Path} = shift; + + ### Re-set filename, extracting it from path if possible: + my $filename; + if ($self->{Path} and ($self->{Path} !~ /\|$/)) { ### non-shell path: + ($filename = $self->{Path}) =~ s/^<//; + ($filename) = ($filename =~ m{([^\/]+)\Z}); + } + $self->filename($filename); + + ### Reset the length: + $self->get_length; + } + $self->{Path}; +} + +#------------------------------ + +=item fh [FILEHANDLE] + +Get/set the FILEHANDLE which contains the message data. + +Takes a filehandle as an input and stores it in the object. +This routine is similar to path(); one important difference is that +no attempt is made to set the content length. + +=cut + +sub fh { + my $self = shift; + $self->{FH} = shift if @_; + $self->{FH}; +} + +#------------------------------ + +=item resetfh [FILEHANDLE] + +Set the current position of the filehandle back to the beginning. +Only applies if you used "FH" in build() or attach() for this message. + +Returns false if unable to reset the filehandle (since not all filehandles +are seekable). + +=cut + +#---- +# Miko's note: With the Data and Path, the same data could theoretically +# be reused. However, file handles need to be reset to be reused, +# so I added this routine. +# +# Eryq reply: beware... not all filehandles are seekable (think about STDIN)! + +sub resetfh { + my $self = shift; + seek($self->{FH},0,0); +} + +#------------------------------ + +=item read_now + +Forces data from the path/filehandle (as specified by C<build()>) +to be read into core immediately, just as though you had given it +literally with the C<Data> keyword. + +Note that the in-core data will always be used if available. + +Be aware that everything is slurped into a giant scalar: you may not want +to use this if sending tar files! The benefit of I<not> reading in the data +is that very large files can be handled by this module if left on disk +until the message is output via C<print()> or C<print_body()>. + +=cut + +sub read_now { + my $self = shift; + local $/ = undef; + + if ($self->{FH}) { ### data from a filehandle: + my $chunk; + my @chunks; + CORE::binmode($self->{FH}) if $self->binmode; + while (read($self->{FH}, $chunk, 1024)) { + push @chunks, $chunk; + } + $self->{Data} = join '', @chunks; + } + elsif ($self->{Path}) { ### data from a path: + open SLURP, $self->{Path} or croak "open $self->{Path}: $!\n"; + CORE::binmode(SLURP) if $self->binmode; + $self->{Data} = <SLURP>; ### sssssssssssssslurp... + close SLURP; ### ...aaaaaaaaahhh! + } +} + +#------------------------------ + +=item sign PARAMHASH + +Sign the message. This forces the message to be read into core, +after which the signature is appended to it. + +=over 4 + +=item Data + +As in C<build()>: the literal signature data. +Can be either a scalar or a ref to an array of scalars. + +=item Path + +As in C<build()>: the path to the file. + +=back + +If no arguments are given, the default is: + + Path => "$ENV{HOME}/.signature" + +The content-length is recomputed. + +=cut + +sub sign { + my $self = shift; + my %params = @_; + + ### Default: + @_ or $params{Path} = "$ENV{HOME}/.signature"; + + ### Force message in-core: + defined($self->{Data}) or $self->read_now; + + ### Load signature: + my $sig; + if (!defined($sig = $params{Data})) { ### not given explicitly: + local $/ = undef; + open SIG, $params{Path} or croak "open sig $params{Path}: $!\n"; + $sig = <SIG>; ### sssssssssssssslurp... + close SIG; ### ...aaaaaaaaahhh! + } + $sig = join('',@$sig) if (ref($sig) and (ref($sig) eq 'ARRAY')); + + ### Append, following Internet conventions: + $self->{Data} .= "\n-- \n$sig"; + + ### Re-compute length: + $self->get_length; + 1; +} + +#------------------------------ +# +# =item suggest_encoding CONTENTTYPE +# +# I<Class/instance method.> +# Based on the CONTENTTYPE, return a good suggested encoding. +# C<text> and C<message> types have their bodies scanned line-by-line +# for 8-bit characters and long lines; lack of either means that the +# message is 7bit-ok. Other types are chosen independent of their body: +# +# Major type: 7bit ok? Suggested encoding: +# ------------------------------------------------------------ +# text yes 7bit +# no quoted-printable +# unknown binary +# +# message yes 7bit +# no binary +# unknown binary +# +# multipart n/a binary (in case some parts are not ok) +# +# (other) n/a base64 +# +#=cut + +sub suggest_encoding { + my ($self, $ctype) = @_; + + my ($type) = split '/', lc($ctype); + if (($type eq 'text') || ($type eq 'message')) { ### scan message body + return 'binary'; + } + else { + return ($type eq 'multipart') ? 'binary' : 'base64'; + } +} + +#------------------------------ + +=item verify_data + +I<Instance method.> +Verify that all "paths" to attached data exist, recursively. +It might be a good idea for you to do this before a print(), to +prevent accidental partial output if a file might be missing. +Raises exception if any path is not readable. + +=cut + +sub verify_data { + my $self = shift; + + ### Verify self: + my $path = $self->{Path}; + if ($path and ($path !~ /\|$/)) { ### non-shell path: + $path =~ s/^<//; + (-r $path) or die "$path: not readable\n"; + } + + ### Verify parts: + foreach my $part (@{$self->{Parts}}) { $part->verify_data } + 1; +} + +=back + +=cut + + +#============================== +#============================== + +=head2 Output + +=over 4 + +=cut + +#------------------------------ + +=item print [OUTHANDLE] + +I<Instance method.> +Print the message to the given output handle, or to the currently-selected +filehandle if none was given. + +All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +=cut + +sub print { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Output head, separator, and body: + $out->print($self->header_as_string, "\n"); + $self->print_body($out); +} + +#------------------------------ +# +# print_for_smtp +# +# Instance method, private. +# Print, but filter out the topmost "Bcc" field. +# This is because qmail apparently doesn't do this for us! +# +sub print_for_smtp { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Create a safe head: + my @fields = grep { $_->[0] ne 'bcc' } @{$self->fields}; + my $header = $self->fields_as_string(\@fields); + + ### Output head, separator, and body: + $out->print($header, "\n"); + $self->print_body($out); +} + +#------------------------------ + +=item print_body [OUTHANDLE] + +I<Instance method.> +Print the body of a message to the given output handle, or to +the currently-selected filehandle if none was given. + +All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +B<Fatal exception> raised if unable to open any of the input files, +or if a part contains no data, or if an unsupported encoding is +encountered. + +=cut + +sub print_body { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Output either the body or the parts. + ### Notice that we key off of the content-type! We expect fewer + ### accidents that way, since the syntax will always match the MIME type. + my $type = $self->attr('content-type'); + if ($type =~ m{^multipart/}i) { + my $boundary = $self->attr('content-type.boundary'); + + ### Preamble: + $out->print("This is a multi-part message in MIME format.\n"); + + ### Parts: + my $part; + foreach $part (@{$self->{Parts}}) { + $out->print("\n--$boundary\n"); + $part->print($out); + } + + ### Epilogue: + $out->print("\n--$boundary--\n\n"); + } + elsif ($type =~ m{^message/}) { + my @parts = @{$self->{Parts}}; + + ### It's a toss-up; try both data and parts: + if (@parts == 0) { $self->print_simple_body($out) } + elsif (@parts == 1) { $parts[0]->print($out) } + else { croak "can't handle message with >1 part\n"; } + } + else { + $self->print_simple_body($out); + } + 1; +} + +#------------------------------ +# +# print_simple_body [OUTHANDLE] +# +# I<Instance method, private.> +# Print the body of a simple singlepart message to the given +# output handle, or to the currently-selected filehandle if none +# was given. +# +# Note that if you want to print "the portion after +# the header", you don't want this method: you want +# L<print_body()|/print_body>. +# +# All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +# any object that responds to a print() message. +# +# B<Fatal exception> raised if unable to open any of the input files, +# or if a part contains no data, or if an unsupported encoding is +# encountered. +# +sub print_simple_body { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Get content-transfer-encoding: + my $encoding = uc($self->attr('content-transfer-encoding')); + + ### Notice that we don't just attempt to slurp the data in from a file: + ### by processing files piecemeal, we still enable ourselves to prepare + ### very large MIME messages... + + ### Is the data in-core? If so, blit it out... + if (defined($self->{Data})) { + DATA: + { local $_ = $encoding; + + /^BINARY$/ and do { + $out->print($self->{Data}); + last DATA; + }; + /^8BIT$/ and do { + $out->print(encode_8bit($self->{Data})); + last DATA; + }; + /^7BIT$/ and do { + $out->print(encode_7bit($self->{Data})); + last DATA; + }; + /^QUOTED-PRINTABLE$/ and do { + ### UNTAINT since m//mg on tainted data loops forever: + my ($untainted) = ($self->{Data} =~ m/\A(.*)\Z/s); + + ### Encode it line by line: + while ($untainted =~ m{^(.*[\r\n]*)}mg) { + $out->print(encode_qp($1)); ### have to do it line by line... + } + last DATA; + }; + /^BASE64/ and do { + $out->print(encode_base64($self->{Data})); + last DATA; + }; + croak "unsupported encoding: `$_'\n"; + } + } + + ### Else, is the data in a file? If so, output piecemeal... + ### Miko's note: this routine pretty much works the same with a path + ### or a filehandle. the only difference in behaviour is that it does + ### not attempt to open anything if it already has a filehandle + elsif (defined($self->{Path}) || defined($self->{FH})) { + no strict 'refs'; ### in case FH is not an object + my $DATA; + + ### Open file if necessary: + if (defined($self->{Path})) { + $DATA = new FileHandle || croak "can't get new filehandle\n"; + $DATA->open("$self->{Path}") or croak "open $self->{Path}: $!\n"; + } + else { + $DATA=$self->{FH}; + } + CORE::binmode($DATA) if $self->binmode; + + ### Encode piece by piece: + PATH: + { local $_ = $encoding; + + /^BINARY$/ and do { + $out->print($_) while read($DATA, $_, 2048); + last PATH; + }; + /^8BIT$/ and do { + $out->print(encode_8bit($_)) while (<$DATA>); + last PATH; + }; + /^7BIT$/ and do { + $out->print(encode_7bit($_)) while (<$DATA>); + last PATH; + }; + /^QUOTED-PRINTABLE$/ and do { + $out->print(encode_qp($_)) while (<$DATA>); + last PATH; + }; + /^BASE64$/ and do { + $out->print(encode_base64($_)) while (read($DATA, $_, 45)); + last PATH; + }; + croak "unsupported encoding: `$_'\n"; + } + + ### Close file: + close $DATA if defined($self->{Path}); + } + + else { + croak "no data in this part\n"; + } + 1; +} + +#------------------------------ + +=item print_header [OUTHANDLE] + +I<Instance method.> +Print the header of the message to the given output handle, +or to the currently-selected filehandle if none was given. + +All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +=cut + +sub print_header { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Output the header: + $out->print($self->header_as_string); + 1; +} + +#------------------------------ + +=item as_string + +I<Instance method.> +Return the entire message as a string, with a header and an encoded body. + +=cut + +sub as_string { + my $self = shift; + my @buf; + my $io = (wrap MIME::Lite::IO_ScalarArray \@buf); + $self->print($io); + join '', @buf; +} +*stringify = \&as_string; ### backwards compatibility + +#------------------------------ + +=item body_as_string + +I<Instance method.> +Return the encoded body as a string. +This is the portion after the header and the blank line. + +I<Note:> actually prepares the body by "printing" to a scalar. +Proof that you can hand the C<print*()> methods any blessed object +that responds to a C<print()> message. + +=cut + +sub body_as_string { + my $self = shift; + my @buf; + my $io = (wrap MIME::Lite::IO_ScalarArray \@buf); + $self->print_body($io); + join '', @buf; +} +*stringify_body = \&body_as_string; ### backwards compatibility + +#------------------------------ +# +# fields_as_string FIELDS +# +# PRIVATE! Return a stringified version of the given header +# fields, where FIELDS is an arrayref like that returned by fields(). +# +sub fields_as_string { + my ($self, $fields) = @_; + my @lines; + foreach (@$fields) { + my ($tag, $value) = @$_; + next if ($value eq ''); ### skip empties + $tag =~ s/\b([a-z])/uc($1)/ge; ### make pretty + $tag =~ s/^mime-/MIME-/ig; ### even prettier + push @lines, "$tag: $value\n"; + } + join '', @lines; +} + +#------------------------------ + +=item header_as_string + +I<Instance method.> +Return the header as a string. + +=cut + +sub header_as_string { + my $self = shift; + $self->fields_as_string($self->fields); +} +*stringify_header = \&header_as_string; ### backwards compatibility + +=back + +=cut + + + +#============================== +#============================== + +=head2 Sending + +=over 4 + +=cut + +#------------------------------ + +=item send + +=item send HOW, HOWARGS... + +I<Class/instance method.> +This is the principal method for sending mail, and for configuring +how mail will be sent. + +I<As an instance method> (with no arguments), sends the message by whatever +means has been set up (the default is to use the Unix "sendmail" program). +Returns whatever the mail-handling routine returns: this should be true +on success, false/exception on error: + + $msg = MIME::Lite->new(From=>...); + $msg->send || die "you DON'T have mail!"; + +I<As a class method> (with a HOW argument and optional HOWARGS), sets up +how the instance method will work for all objects until further notice +It treats HOW as a facility name, with optional HOWARGS handled by +the facility (and returns the previous HOW and HOWARGS as an array). +There are three facilities: + +=over 4 + +=item "sendmail", ARGS... + +Send a message by piping it into the "sendmail" command. +Uses the L<send_by_sendmail()|/send_by_sendmail> method, giving it the ARGS. +This usage implements (and deprecates) the C<sendmail()> method. + +=item "smtp", [HOSTNAME] + +Send a message by SMTP, using optional HOSTNAME as SMTP-sending host. +Uses the L<send_by_smtp()|/send_by_smtp> method. + +=item "sub", \&SUBREF, ARGS... + +Sends a message MSG by invoking the subroutine SUBREF of your choosing, +with MSG as the first argument, and ARGS following. + +=back + +I<For example:> let's say you're on an OS which lacks the usual Unix +"sendmail" facility, but you've installed something a lot like it, and +you need to configure your Perl script to use this "sendmail.exe" program. +Do this following in your script's setup: + + MIME::Lite->send('sendmail', "d:\\programs\\sendmail.exe"); + +Then, whenever you need to send a message $msg, just say: + + $msg->send; + +That's it. Now, if you ever move your script to a Unix box, all you +need to do is change that line in the setup and you're done. +All of your $msg-E<gt>send invocations will work as expected. + +=cut + +sub send { + my $self = shift; + + if (ref($self)) { ### instance method: + my $method = "send_by_$Sender"; + my @args = @{$SenderArgs{$Sender} || []}; + $self->verify_data if $AUTO_VERIFY; ### prevents missing parts! + return $self->$method(@args); + } + else { ### class method: + my @old = ($Sender, @{$SenderArgs{$Sender}}); + $Sender = shift; + $SenderArgs{$Sender} = [@_]; ### remaining args + return @old; + } +} + +#------------------------------ + +=item send_by_sendmail SENDMAILCMD + +=item send_by_sendmail PARAM=>VALUE, ... + +I<Instance method.> +Send message via an external "sendmail" program +(this will probably only work out-of-the-box on Unix systems). + +Returns true on success, false or exception on error. + +You can specify the program and all its arguments by giving a single +string, SENDMAILCMD. Nothing fancy is done; the message is simply +piped in. + +However, if your needs are a little more advanced, you can specify +zero or more of the following PARAM/VALUE pairs; a Unix-style, +taint-safe "sendmail" command will be constructed for you: + +=over 4 + +=item Sendmail + +Full path to the program to use. +Default is "/usr/lib/sendmail". + +=item BaseArgs + +Ref to the basic array of arguments we start with. +Default is C<["-t", "-oi", "-oem"]>. + +=item SetSender + +Unless this is I<explicitly> given as false, we attempt to automatically +set the C<-f> argument to the first address that can be extracted from +the "From:" field of the message (if there is one). + +I<What is the -f, and why do we use it?> +Suppose we did I<not> use C<-f>, and you gave an explicit "From:" +field in your message: in this case, the sendmail "envelope" would +indicate the I<real> user your process was running under, as a way +of preventing mail forgery. Using the C<-f> switch causes the sender +to be set in the envelope as well. + +I<So when would I NOT want to use it?> +If sendmail doesn't regard you as a "trusted" user, it will permit +the C<-f> but also add an "X-Authentication-Warning" header to the message +to indicate a forged envelope. To avoid this, you can either +(1) have SetSender be false, or +(2) make yourself a trusted user by adding a C<T> configuration + command to your I<sendmail.cf> file + (e.g.: C<Teryq> if the script is running as user "eryq"). + +=item FromSender + +If defined, this is identical to setting SetSender to true, +except that instead of looking at the "From:" field we use +the address given by this option. +Thus: + + FromSender => 'me@myhost.com' + +=back + +=cut + +sub send_by_sendmail { + my $self = shift; + + if (@_ == 1) { ### Use the given command... + my $sendmailcmd = shift @_; + + ### Do it: + open SENDMAIL, "|$sendmailcmd" or croak "open |$sendmailcmd: $!\n"; + $self->print(\*SENDMAIL); + close SENDMAIL; + return (($? >> 8) ? undef : 1); + } + else { ### Build the command... + my %p = @_; + $p{Sendmail} ||= "/usr/lib/sendmail"; + + ### Start with the command and basic args: + my @cmd = ($p{Sendmail}, @{$p{BaseArgs} || ['-t', '-oi', '-oem']}); + + ### See if we are forcibly setting the sender: + $p{SetSender} = 1 if defined($p{FromSender}); + + ### Add the -f argument, unless we're explicitly told NOT to: + unless (exists($p{SetSender}) and !$p{SetSender}) { + my $from = $p{FromSender} || ($self->get('From'))[0]; + if ($from) { + my ($from_addr) = extract_addrs($from); + push @cmd, "-f$from_addr" if $from_addr; + } + } + + ### Open the command in a taint-safe fashion: + my $pid = open SENDMAIL, "|-"; + defined($pid) or die "open of pipe failed: $!\n"; + if (!$pid) { ### child + exec(@cmd) or die "can't exec $p{Sendmail}: $!\n"; + ### NOTREACHED + } + else { ### parent + $self->print(\*SENDMAIL); + close SENDMAIL || die "error closing $p{Sendmail}: $! (exit $?)\n"; + return 1; + } + } +} + +#------------------------------ + +=item send_by_smtp ARGS... + +I<Instance method.> +Send message via SMTP, using Net::SMTP. +The optional ARGS are sent into Net::SMTP::new(): usually, these are + + MAILHOST, OPTION=>VALUE, ... + +Note that the list of recipients is taken from the +"To", "Cc" and "Bcc" fields. + +Returns true on success, false or exception on error. + +=cut + +### Provided by Andrew McRae. Version 0.2 anm 09Sep97 +### Copyright 1997 Optimation New Zealand Ltd. +### May be modified/redistributed under the same terms as Perl. +# +sub send_by_smtp { + my ($self, @args) = @_; + + ### We need the "From:" and "To:" headers to pass to the SMTP mailer: + my $hdr = $self->fields(); + my $from = $self->get('From'); + my $to = $self->get('To'); + + ### Sanity check: + defined($to) or croak "send_by_smtp: missing 'To:' address\n"; + + ### Get the destinations as a simple array of addresses: + my @to_all = extract_addrs($to); + if ($AUTO_CC) { + foreach my $field (qw(Cc Bcc)) { + my $value = $self->get($field); + push @to_all, extract_addrs($value) if defined($value); + } + } + + ### Create SMTP client: + require Net::SMTP; + my $smtp = MIME::Lite::SMTP->new(@args) + or croak "Failed to connect to mail server: $!\n"; + $smtp->mail($from) + or croak "SMTP MAIL command failed: $!\n"; + $smtp->to(@to_all) + or croak "SMTP RCPT command failed: $!\n"; + $smtp->data() + or croak "SMTP DATA command failed: $!\n"; + + ### MIME::Lite can print() to anything with a print() method: + $self->print_for_smtp($smtp); + $smtp->dataend(); + $smtp->quit; + 1; +} + +#------------------------------ +# +# send_by_sub [\&SUBREF, [ARGS...]] +# +# I<Instance method, private.> +# Send the message via an anonymous subroutine. +# +sub send_by_sub { + my ($self, $subref, @args) = @_; + &$subref($self, @args); +} + +#------------------------------ + +=item sendmail COMMAND... + +I<Class method, DEPRECATED.> +Declare the sender to be "sendmail", and set up the "sendmail" command. +I<You should use send() instead.> + +=cut + +sub sendmail { + my $self = shift; + $self->send('sendmail', join(' ', @_)); +} + +=back + +=cut + + + +#============================== +#============================== + +=head2 Miscellaneous + +=over 4 + +=cut + +#------------------------------ + +=item quiet ONOFF + +I<Class method.> +Suppress/unsuppress all warnings coming from this module. + + MIME::Lite->quiet(1); ### I know what I'm doing + +I recommend that you include that comment as well. And while +you type it, say it out loud: if it doesn't feel right, then maybe +you should reconsider the whole line. C<;-)> + +=cut + +sub quiet { + my $class = shift; + $QUIET = shift if @_; + $QUIET; +} + +=back + +=cut + + + +#============================================================ + +package MIME::Lite::SMTP; + +#============================================================ +# This class just adds a print() method to Net::SMTP. +# Notice that we don't use/require it until it's needed! + +use strict; +use vars qw( @ISA ); +@ISA = qw(Net::SMTP); + +sub print { shift->datasend(@_) } + + + +#============================================================ + +package MIME::Lite::IO_Handle; + +#============================================================ + +### Wrap a non-object filehandle inside a blessed, printable interface: +### Does nothing if the given $fh is already a blessed object. +sub wrap { + my ($class, $fh) = @_; + no strict 'refs'; + + ### Get default, if necessary: + $fh or $fh = select; ### no filehandle means selected one + ref($fh) or $fh = \*$fh; ### scalar becomes a globref + + ### Stop right away if already a printable object: + return $fh if (ref($fh) and (ref($fh) ne 'GLOB')); + + ### Get and return a printable interface: + bless \$fh, $class; ### wrap it in a printable interface +} + +### Print: +sub print { + my $self = shift; + print {$$self} @_; +} + + +#============================================================ + +package MIME::Lite::IO_Scalar; + +#============================================================ + +### Wrap a scalar inside a blessed, printable interface: +sub wrap { + my ($class, $scalarref) = @_; + defined($scalarref) or $scalarref = \""; + bless $scalarref, $class; +} + +### Print: +sub print { + my $self = shift; + $$self .= join('', @_); + 1; +} + + +#============================================================ + +package MIME::Lite::IO_ScalarArray; + +#============================================================ + +### Wrap an array inside a blessed, printable interface: +sub wrap { + my ($class, $arrayref) = @_; + defined($arrayref) or $arrayref = []; + bless $arrayref, $class; +} + +### Print: +sub print { + my $self = shift; + push @$self, @_; + 1; +} + +1; +__END__ + + +#============================================================ + +=head1 NOTES + + +=head2 Benign limitations + +This is "lite", after all... + +=over 4 + +=item * + +There's no parsing. Get MIME-tools if you need to parse MIME messages. + +=item * + +MIME::Lite messages are currently I<not> interchangeable with +either Mail::Internet or MIME::Entity objects. This is a completely +separate module. + +=item * + +A content-length field is only inserted if the encoding is binary, +the message is a singlepart, and all the document data is available +at C<build()> time by virtue of residing in a simple path, or in-core. +Since content-length is not a standard MIME field anyway (that's right, kids: +it's not in the MIME RFCs, it's an HTTP thing), this seems pretty fair. + +=item * + +MIME::Lite alone cannot help you lose weight. You must supplement +your use of MIME::Lite with a healthy diet and exercise. + +=back + + +=head2 Cheap and easy mailing + +I thought putting in a default "sendmail" invocation wasn't too bad an +idea, since a lot of Perlers are on UNIX systems. +The out-of-the-box configuration is: + + MIME::Lite->send('sendmail', "/usr/lib/sendmail -t -oi -oem"); + +By the way, these arguments to sendmail are: + + -t Scan message for To:, Cc:, Bcc:, etc. + + -oi Do NOT treat a single "." on a line as a message terminator. + As in, "-oi vey, it truncated my message... why?!" + + -oem On error, mail back the message (I assume to the + appropriate address, given in the header). + When mail returns, circle is complete. Jai Guru Deva -oem. + +Note that these are the same arguments you get if you configure to use +the smarter, taint-safe mailing: + + MIME::Lite->send('sendmail'); + +If you get "X-Authentication-Warning" headers from this, you can forgo +diddling with the envelope by instead specifying: + + MIME::Lite->send('sendmail', SetSender=>0); + +And, if you're not on a Unix system, or if you'd just rather send mail +some other way, there's always: + + MIME::Lite->send('smtp', "smtp.myisp.net"); + +Or you can set up your own subroutine to call. +In any case, check out the L<send()|/send> method. + + + +=head1 WARNINGS + +=head2 Good-vs-bad email addresses with send_by_smtp() + +If using L<send_by_smtp()|/send_by_smtp>, be aware that you are +forcing MIME::Lite to extract email addresses out of a possible list +provided in the C<To:>, C<Cc:>, and C<Bcc:> fields. This is tricky +stuff, and as such only the following sorts of addresses will work +reliably: + + username + full.name@some.host.com + "Name, Full" <full.name@some.host.com> + +This last form is discouraged because SMTP must be able to get +at the I<name> or I<name@domain> portion. + +B<Disclaimer:> +MIME::Lite was never intended to be a Mail User Agent, so please +don't expect a full implementation of RFC-822. Restrict yourself to +the common forms of Internet addresses described herein, and you should +be fine. If this is not feasible, then consider using MIME::Lite +to I<prepare> your message only, and using Net::SMTP explicitly to +I<send> your message. + + +=head2 Formatting of headers delayed until print() + +This class treats a MIME header in the most abstract sense, +as being a collection of high-level attributes. The actual +RFC-822-style header fields are not constructed until it's time +to actually print the darn thing. + + +=head2 Encoding of data delayed until print() + +When you specify message bodies +(in L<build()|/build> or L<attach()|/attach>) -- +whether by B<FH>, B<Data>, or B<Path> -- be warned that we don't +attempt to open files, read filehandles, or encode the data until +L<print()|/print> is invoked. + +In the past, this created some confusion for users of sendmail +who gave the wrong path to an attachment body, since enough of +the print() would succeed to get the initial part of the message out. +Nowadays, $AUTO_VERIFY is used to spot-check the Paths given before +the mail facility is employed. A whisker slower, but tons safer. + +Note that if you give a message body via FH, and try to print() +a message twice, the second print() will not do the right thing +unless you explicitly rewind the filehandle. + +You can get past these difficulties by using the B<ReadNow> option, +provided that you have enough memory to handle your messages. + + +=head2 MIME attributes are separate from header fields! + +B<Important:> the MIME attributes are stored and manipulated separately +from the message header fields; when it comes time to print the +header out, I<any explicitly-given header fields override the ones that +would be created from the MIME attributes.> That means that this: + + ### DANGER ### DANGER ### DANGER ### DANGER ### DANGER ### + $msg->add("Content-type", "text/html; charset=US-ASCII"); + +will set the exact C<"Content-type"> field in the header I write, +I<regardless of what the actual MIME attributes are.> + +I<This feature is for experienced users only,> as an escape hatch in case +the code that normally formats MIME header fields isn't doing what +you need. And, like any escape hatch, it's got an alarm on it: +MIME::Lite will warn you if you attempt to C<set()> or C<replace()> +any MIME header field. Use C<attr()> instead. + + +=head2 Beware of lines consisting of a single dot + +Julian Haight noted that MIME::Lite allows you to compose messages +with lines in the body consisting of a single ".". +This is true: it should be completely harmless so long as "sendmail" +is used with the -oi option (see L<"Cheap and easy mailing">). + +However, I don't know if using Net::SMTP to transfer such a message +is equally safe. Feedback is welcomed. + +My perspective: I don't want to magically diddle with a user's +message unless absolutely positively necessary. +Some users may want to send files with "." alone on a line; +my well-meaning tinkering could seriously harm them. + + +=head2 Infinite loops may mean tainted data! + +Stefan Sautter noticed a bug in 2.106 where a m//gc match was +failing due to tainted data, leading to an infinite loop inside +MIME::Lite. + +I am attempting to correct for this, but be advised that my fix will +silently untaint the data (given the context in which the problem +occurs, this should be benign: I've labelled the source code with +UNTAINT comments for the curious). + +So: don't depend on taint-checking to save you from outputting +tainted data in a message. + + +=head1 A MIME PRIMER + +=head2 Content types + +The "Type" parameter of C<build()> is a I<content type>. +This is the actual type of data you are sending. +Generally this is a string of the form C<"majortype/minortype">. + +Here are the major MIME types. +A more-comprehensive listing may be found in RFC-2046. + +=over 4 + +=item application + +Data which does not fit in any of the other categories, particularly +data to be processed by some type of application program. +C<application/octet-stream>, C<application/gzip>, C<application/postscript>... + +=item audio + +Audio data. +C<audio/basic>... + +=item image + +Graphics data. +C<image/gif>, C<image/jpeg>... + +=item message + +A message, usually another mail or MIME message. +C<message/rfc822>... + +=item multipart + +A message containing other messages. +C<multipart/mixed>, C<multipart/alternative>... + +=item text + +Textual data, meant for humans to read. +C<text/plain>, C<text/html>... + +=item video + +Video or video+audio data. +C<video/mpeg>... + +=back + + +=head2 Content transfer encodings + +The "Encoding" parameter of C<build()>. +This is how the message body is packaged up for safe transit. + +Here are the 5 major MIME encodings. +A more-comprehensive listing may be found in RFC-2045. + +=over 4 + +=item 7bit + +Basically, no I<real> encoding is done. However, this label guarantees that no +8-bit characters are present, and that lines do not exceed 1000 characters +in length. + +=item 8bit + +Basically, no I<real> encoding is done. The message might contain 8-bit +characters, but this encoding guarantees that lines do not exceed 1000 +characters in length. + +=item binary + +No encoding is done at all. Message might contain 8-bit characters, +and lines might be longer than 1000 characters long. + +The most liberal, and the least likely to get through mail gateways. +Use sparingly, or (better yet) not at all. + +=item base64 + +Like "uuencode", but very well-defined. This is how you should send +essentially binary information (tar files, GIFs, JPEGs, etc.). + +=item quoted-printable + +Useful for encoding messages which are textual in nature, yet which contain +non-ASCII characters (e.g., Latin-1, Latin-2, or any other 8-bit alphabet). + +=back + + + +=head1 VERSION + +$Id: Lite.pm,v 2.108 2001/03/30 06:16:54 eryq Exp $ + + +=head1 CHANGE LOG + +=over 4 + + +=item Version 2.108 + +New C<field_order()> allows you to set the header order, both on a +per-message basis, and package-wide. +I<Thanks to Thomas Stromberg for suggesting this.> + +Added code to try and divine "sendmail" path more intelligently. +I<Thanks to Slaven Rezic for the suggestion.> + + +=item Version 2.107 (2001/03/27) + +Fixed serious bug where tainted data with quoted-printable encoding +was causing infinite loops. The "fix" untaints the data in question, +which is not optimal, but it's probably benign in this case. +I<Thanks to Stefan Sautter for tracking this nasty little beast down.> +I<Thanks to Larry Geralds for a related patch.> + + "Doctor, O doctor: + it's painful when I do *this* --" + "Simple: don't *do* that." + +Fixed bugs where a non-local C<$_> was being modified... again! +Will I never learn? +I<Thanks to Maarten Koskamp for reporting this.> + + Dollar-underscore + can poison distant waters; + 'local' must it be. + +Fixed buglet in C<add()> where all value references were being treated +as arrayrefs, instead of as possibly-self-stringifying object refs. +Now you can send in an object ref as the 2nd argument. +I<Thanks to dLux for the bug report.> + + That ref is a string? + Operator overload + has ruined my day. + +Added "Approved" as an acceptable header field for C<new()>, as per RFC1036. +I<Thanks to Thomax for the suggestion regarding MIME-tools.> + +Small improvements to docs to make different uses of attach() +and various arguments clearer. +I<Thanks to Sven Rassman and Roland Walter for the suggestions.> + + +=item Version 2.106 (2000/11/21) + +Added Alpha version of scrub() to make it easy for people to suppress +the printing of unwanted MIME attributes (like Content-length). +I<Thanks to the many people who asked for this.> + +Headers with empty-strings for their values are no longer +printed. This seems sensible, and helps us implement scrub(). + + +=item Version 2.105 (2000/10/14) + +The regression-test failure was identified, and it was my fault. +Apparently some of the \-quoting in my "autoloaded" code was +making Perl 5.6 unhappy. For this nesting-related idiocy, +a nesting kaiku. +I<Thanks to Scott Schwartz for identifying the problem.> + + In a pattern, my + backslash-s dwells peacefully, + unambiguous -- + + but I embed it + in a double-quoted string + doubling the backslash -- + + interpolating + that same double-quoted string + in other patterns -- + + and, worlds within worlds, + I single-quote the function + to autoload it -- + + changing the meaning + of the backslash and the 's'; + and Five-Point-Six growls. + + +=item Version 2.104 (2000/09/28) + +Now attempts to load and use Mail::Address for parsing email +addresses I<before> falling back to our own method. +I<Thanks to numerous people for suggesting this.> + + Parsing addresses + is too damn hard. One last hope: + Let Graham Barr do it! + +For the curious, the version of Mail::Address appears +as the "A" number in the X-Mailer: + + X-Mailer: MIME::Lite 2.104 (A1.15; B2.09; Q2.03) + +Added B<FromSender> option to send_by_sendmail(). +I<Thanks to Bill Moseley for suggesting this feature.> + + +=item Version 2.101 (2000/06/06) + +Major revision to print_body() and body_as_string() so that +"body" really means "the part after the header", which is what most +people would want in this context. This is B<not> how it was used +1.x, where "body" only meant "the body of a simple singlepart". +Hopefully, this change will solve many problems and create very few ones. + +Added support for attaching a part to a "message/rfc822", treating +the "message" type as a multipart-like container. + +Now takes care not to include "Bcc:" in header when using send_by_smtp, +as a safety precaution against qmail's behavior. +I<Thanks to Tatsuhiko Miyagawa for identifying this problem.> + +Improved efficiency of many stringifying operations by using +string-arrays which are joined, instead of doing multiple appends +to a scalar. + +Cleaned up the "examples" directory. + + +=item Version 1.147 (2000/06/02) + +Fixed buglet where lack of Cc:/Bcc: was causing extract_addrs +to emit "undefined variable" warnings. Also, lack of a "To:" field +now causes a croak. +I<Thanks to David Mitchell for the bug report and suggested patch.> + + +=item Version 1.146 (2000/05/18) + +Fixed bug in parsing of addresses; please read the WARNINGS section +which describes recommended address formats for "To:", "Cc:", etc. +Also added automatic inclusion of a UT "Date:" at top level unless +explicitly told not to. +I<Thanks to Andy Jacobs for the bug report and the suggestion.> + +=item Version 1.145 (2000/05/06) + +Fixed bug in encode_7bit(): a lingering C</e> modifier was removed. +I<Thanks to Michael A. Chase for the patch.> + + +=item Version 1.142 (2000/05/02) + +Added new, taint-safe invocation of "sendmail", one which also +sets up the C<-f> option. Unfortunately, I couldn't make this automatic: +the change could have broken a lot of code out there which used +send_by_sendmail() with unusual "sendmail" variants. +So you'll have to configure "send" to use the new mechanism: + + MIME::Lite->send('sendmail'); ### no args! + +I<Thanks to Jeremy Howard for suggesting these features.> + + +=item Version 1.140 (2000/04/27) + +Fixed bug in support for "To", "Cc", and "Bcc" in send_by_smtp(): +multiple (comma-separated) addresses should now work fine. +We try real hard to extract addresses from the flat text strings. +I<Thanks to John Mason for motivating this change.> + +Added automatic verification that attached data files exist, +done immediately before the "send" action is invoked. +To turn this off, set $MIME::Lite::AUTO_VERIFY to false. + +=item Version 1.137 (2000/03/22) + +Added support for "Cc" and "Bcc" in send_by_smtp(). +To turn this off, set $MIME::Lite::AUTO_CC to false. +I<Thanks to Lucas Maneos for the patch, and tons of others for +the suggestion.> + +Chooses a better default content-transfer-encoding if the content-type +is "image/*", "audio/*", etc. +To turn this off, set $MIME::Lite::AUTO_ENCODE to false. +I<Thanks to many folks for the suggestion.> + +Fixed bug in QP-encoding where a non-local C<$_> was being modified. +I<Thanks to Jochen Stenzel for finding this very obscure bug!> + +Removed references to C<$`>, C<$'>, and C<$&> (bad variables +which slow things down). + +Added an example of how to send HTML files with enclosed in-line +images, per popular demand. + + +=item Version 1.133 (1999/04/17) + +Fixed bug in "Data" handling: arrayrefs were not being handled +properly. + + +=item Version 1.130 (1998/12/14) + +Added much larger and more-flexible send() facility. +I<Thanks to Andrew McRae (and Optimation New Zealand Ltd) +for the Net::SMTP interface. Additional thanks to the many folks +who requested this feature.> + +Added get() method for extracting basic attributes. + +New... "t" tests! + + +=item Version 1.124 (1998/11/13) + +Folded in filehandle (FH) support in build/attach. +I<Thanks to Miko O'Sullivan for the code.> + + +=item Version 1.122 (1998/01/19) + +MIME::Base64 and MIME::QuotedPrint are used if available. + +The 7bit encoding no longer does "escapes"; it merely strips 8-bit characters. + + +=item Version 1.121 (1997/04/08) + +Filename attribute is now no longer ignored by build(). +I<Thanks to Ian Smith for finding and patching this bug.> + + +=item Version 1.120 (1997/03/29) + +Efficiency hack to speed up MIME::Lite::IO_Scalar. +I<Thanks to David Aspinwall for the patch.> + + +=item Version 1.116 (1997/03/19) + +Small bug in our private copy of encode_base64() was patched. +I<Thanks to Andreas Koenig for pointing this out.> + +New, prettier way of specifying mail message headers in C<build()>. + +New quiet method to turn off warnings. + +Changed "stringify" methods to more-standard "as_string" methods. + + +=item Version 1.112 (1997/03/06) + +Added C<read_now()>, and C<binmode()> method for our non-Unix-using brethren: +file data is now read using binmode() if appropriate. +I<Thanks to Xiangzhou Wang for pointing out this bug.> + + +=item Version 1.110 (1997/03/06) + +Fixed bug in opening the data filehandle. + + +=item Version 1.102 (1997/03/01) + +Initial release. + + +=item Version 1.101 (1997/03/01) + +Baseline code. + +=back + + +=head1 TERMS AND CONDITIONS + +Copyright (c) 1997 by Eryq. +Copyright (c) 1998 by ZeeGee Software Inc. +All rights reserved. This program is free software; you can redistribute +it and/or modify it under the same terms as Perl itself. + +This software comes with B<NO WARRANTY> of any kind. +See the COPYING file in the distribution for details. + + +=head1 NUTRITIONAL INFORMATION + +For some reason, the US FDA says that this is now required by law +on any products that bear the name "Lite"... + + MIME::Lite | + ------------------------------------------------------------ + Serving size: | 1 module + Servings per container: | 1 + Calories: | 0 + Fat: | 0g + Saturated Fat: | 0g + +Warning: for consumption by hardware only! May produce +indigestion in humans if taken internally. + + +=head1 AUTHOR + +Eryq (F<eryq@zeegee.com>). +President, ZeeGee Software Inc. (F<http://www.zeegee.com>). + +Created: 11 December 1996. Ho ho ho. + +=cut + diff --git a/cpan/dist/MIME-Lite/t/ExtUtils/TBone.pm b/cpan/dist/MIME-Lite/t/ExtUtils/TBone.pm new file mode 100644 index 00000000..88dbe29c --- /dev/null +++ b/cpan/dist/MIME-Lite/t/ExtUtils/TBone.pm @@ -0,0 +1,534 @@ +package ExtUtils::TBone; + + +=head1 NAME + +ExtUtils::TBone - a "skeleton" for writing "t/*.t" test files. + + +=head1 SYNOPSIS + +Include a copy of this module in your t directory (as t/ExtUtils/TBone.pm), +and then write your t/*.t files like this: + + use lib "./t"; # to pick up a ExtUtils::TBone + use ExtUtils::TBone; + + # Make a tester... here are 3 different alternatives: + my $T = typical ExtUtils::TBone; # standard log + my $T = new ExtUtils::TBone; # no log + my $T = new ExtUtils::TBone "testout/Foo.tlog"; # explicit log + + # Begin testing, and expect 3 tests in all: + $T->begin(3); # expect 3 tests + $T->msg("Something for the log file"); # message for the log + + # Run some tests: + $T->ok($this); # test 1: no real info logged + $T->ok($that, # test 2: logs a comment + "Is that ok, or isn't it?"); + $T->ok(($this eq $that), # test 3: logs comment + vars + "Do they match?", + This => $this, + That => $that); + + # That last one could have also been written... + $T->ok_eq($this, $that); # does 'eq' and logs operands + $T->ok_eqnum($this, $that); # does '==' and logs operands + + # End testing: + $T->end; + + +=head1 DESCRIPTION + +This module is intended for folks who release CPAN modules with +"t/*.t" tests. It makes it easy for you to output syntactically +correct test-output while at the same time logging all test +activity to a log file. Hopefully, bug reports which include +the contents of this file will be easier for you to investigate. + + +=head1 LOG FILE + +A typical log file output by this module looks like this: + + 1..3 + + ** A message logged with msg(). + ** Another one. + 1: My first test, using test(): how'd I do? + 1: ok 1 + + ** Yet another message. + 2: My second test, using test_eq()... + 2: A: The first string + 2: B: The second string + 2: not ok 2 + + 3: My third test. + 3: ok 3 + + END + +Each test() is logged with the test name and results, and +the test-number prefixes each line. +This allows you to scan a large file easily with "grep" (or, ahem, "perl"). +A blank line follows each test's record, for clarity. + + +=head1 PUBLIC INTERFACE + +=cut + +# Globals: +use strict; +use vars qw($VERSION); +use FileHandle; +use File::Basename; + +# The package version, both in 1.23 style *and* usable by MakeMaker: +$VERSION = substr q$Revision: 1.117 $, 10; + + + +#------------------------------ + +=head2 Construction + +=over 4 + +=cut + +#------------------------------ + +=item new [ARGS...] + +I<Class method, constructor.> +Create a new tester. Any arguments are sent to log_open(). + +=cut + +sub new { + my $self = bless { + OUT =>\*STDOUT, + Begin=>0, + End =>0, + Count=>0, + }, shift; + $self->log_open(@_) if @_; + $self; +} + +#------------------------------ + +=item typical + +I<Class method, constructor.> +Create a typical tester. Use this instead of new() for most applicaitons. +The directory "testout" is created for you automatically, to hold +the output log file. + +=cut + +sub typical { + my $class = shift; + my ($tfile) = basename $0; + unless (-d "testout") { + mkdir "testout", 0755 + or die "Couldn't create a 'testout' subdirectory: $!\n"; + ### warn "$class: created 'testout' directory\n"; + } + $class->new($class->catfile('.', 'testout', "${tfile}log")); +} + +#------------------------------ +# DESTROY +#------------------------------ +# Class method, destructor. +# Automatically closes the log. +# +sub DESTROY { + $_[0]->log_close; +} + + +#------------------------------ + +=back + +=head2 Doing tests + +=over 4 + +=cut + +#------------------------------ + +=item begin NUMTESTS + +I<Instance method.> +Start testing. + +=cut + +sub begin { + my ($self, $n) = @_; + return if $self->{Begin}++; + $self->l_print("1..$n\n\n"); + print {$self->{OUT}} "1..$n\n"; +} + +#------------------------------ + +=item end + +I<Instance method.> +End testing. + +=cut + +sub end { + my ($self) = @_; + return if $self->{End}++; + $self->l_print("END\n"); + print {$self->{OUT}} "END\n"; +} + +#------------------------------ + +=item ok BOOL, [TESTNAME], [PARAMHASH...] + +I<Instance method.> +Do a test, and log some information connected with it. +Use it like this: + + $T->ok(-e $dotforward); + +Or better yet, like this: + + $T->ok((-e $dotforward), + "Does the user have a .forward file?"); + +Or even better, like this: + + $T->ok((-e $dotforward), + "Does the user have a .forward file?", + User => $ENV{USER}, + Path => $dotforward, + Fwd => $ENV{FWD}); + +That last one, if it were test #3, would be logged as: + + 3: Does the user have a .forward file? + 3: User: "alice" + 3: Path: "/home/alice/.forward" + 3: Fwd: undef + 3: ok + +You get the idea. Note that defined quantities are logged with delimiters +and with all nongraphical characters suitably escaped, so you can see +evidence of unexpected whitespace and other badnasties. +Had "Fwd" been the string "this\nand\nthat", you'd have seen: + + 3: Fwd: "this\nand\nthat" + +And unblessed array refs like ["this", "and", "that"] are +treated as multiple values: + + 3: Fwd: "this" + 3: Fwd: "and" + 3: Fwd: "that" + +=cut + +sub ok { + my ($self, $ok, $test, @ps) = @_; + ++($self->{Count}); # next test + + # Report to harness: + my $status = ($ok ? "ok " : "not ok ") . $self->{Count}; + print {$self->{OUT}} $status, "\n"; + + # Log: + $self->ln_print($test, "\n") if $test; + while (@ps) { + my ($k, $v) = (shift @ps, shift @ps); + my @vs = ((ref($v) and (ref($v) eq 'ARRAY'))? @$v : ($v)); + foreach (@vs) { + if (!defined($_)) { # value not defined: output keyword + $self->ln_print(qq{ $k: undef\n}); + } + else { # value defined: output quoted, encoded form + s{([\n\t\x00-\x1F\x7F-\xFF\\\"])} + {'\\'.sprintf("%02X",ord($1)) }exg; + s{\\0A}{\\n}g; + $self->ln_print(qq{ $k: "$_"\n}); + } + } + } + $self->ln_print($status, "\n"); + $self->l_print("\n"); + 1; +} + + +#------------------------------ + +=item ok_eq ASTRING, BSTRING, [TESTNAME], [PARAMHASH...] + +I<Instance method.> +Convenience front end to ok(): test whether C<ASTRING eq BSTRING>, and +logs the operands as 'A' and 'B'. + +=cut + +sub ok_eq { + my ($self, $this, $that, $test, @ps) = @_; + $self->ok(($this eq $that), + ($test || "(Is 'A' string-equal to 'B'?)"), + A => $this, + B => $that, + @ps); +} + + +#------------------------------ + +=item ok_eqnum ANUM, BNUM, [TESTNAME], [PARAMHASH...] + +I<Instance method.> +Convenience front end to ok(): test whether C<ANUM == BNUM>, and +logs the operands as 'A' and 'B'. + +=cut + +sub ok_eqnum { + my ($self, $this, $that, $test, @ps) = @_; + $self->ok(($this == $that), + ($test || "(Is 'A' numerically-equal to 'B'?)"), + A => $this, + B => $that, + @ps); +} + +#------------------------------ + +=back + +=head2 Logging messages + +=over 4 + +=cut + +#------------------------------ + +=item log_open PATH + +I<Instance method.> +Open a log file for messages to be output to. This is invoked +for you automatically by C<new(PATH)> and C<typical()>. + +=cut + +sub log_open { + my ($self, $path) = @_; + $self->{LogPath} = $path; + $self->{LOG} = FileHandle->new(">$path") || die "open $path: $!"; + $self; +} + +#------------------------------ + +=item log_close + +I<Instance method.> +Close the log file and stop logging. +You shouldn't need to invoke this directly; the destructor does it. + +=cut + +sub log_close { + my $self = shift; + close(delete $self->{LOG}) if $self->{LOG}; +} + +#------------------------------ + +=item log MESSAGE... + +I<Instance method.> +Log a message to the log file. No alterations are made on the +text of the message. See msg() for an alternative. + +=cut + +sub log { + my $self = shift; + print {$self->{LOG}} @_ if $self->{LOG}; +} + +#------------------------------ + +=item msg MESSAGE... + +I<Instance method.> +Log a message to the log file. Lines are prefixed with "** " for clarity, +and a terminating newline is forced. + +=cut + +sub msg { + my $self = shift; + my $text = join '', @_; + chomp $text; + $text =~ s{^}{** }gm; + $self->l_print($text, "\n"); +} + +#------------------------------ +# +# l_print MESSAGE... +# +# Instance method, private. +# Print to the log file if there is one. +# +sub l_print { + my $self = shift; + print { $self->{LOG} } @_ if $self->{LOG}; +} + +#------------------------------ +# +# ln_print MESSAGE... +# +# Instance method, private. +# Print to the log file, prefixed by message number. +# +sub ln_print { + my $self = shift; + foreach (split /\n/, join('', @_)) { + $self->l_print("$self->{Count}: $_\n"); + } +} + +#------------------------------ + +=back + +=head2 Utilities + +=over 4 + +=cut + +#------------------------------ + +=item catdir DIR, ..., DIR + +I<Class/instance method.> +Concatenate several directories into a path ending in a directory. +Lightweight version of the one in the (very new) File::Spec. + +Paths are assumed to be absolute. +To signify a relative path, the first DIR must be ".", +which is processed specially. + +On Mac, the path I<does> end in a ':'. +On Unix, the path I<does not> end in a '/'. + +=cut + +sub catdir { + my $self = shift; + my $relative = shift @_ if ($_[0] eq '.'); + if ($^O eq 'Mac') { + return ($relative ? ':' : '') . (join ':', @_) . ':'; + } + else { + return ($relative ? './' : '/') . join '/', @_; + } +} + +#------------------------------ + +=item catfile DIR, ..., DIR, FILE + +I<Class/instance method.> +Like catdir(), but last element is assumed to be a file. +Note that, at a minimum, you must supply at least a single DIR. + +=cut + +sub catfile { + my $self = shift; + my $file = pop; + if ($^O eq 'Mac') { + return $self->catdir(@_) . $file; + } + else { + return $self->catdir(@_) . "/$file"; + } +} + +#------------------------------ + +=back + + +=head1 CHANGE LOG + +B<Current version:> +$Id: TBone.pm,v 1.117 2000/08/16 05:08:09 eryq Exp $ + +=over 4 + +=item Version 1.116 + +Cosmetic improvements only. + + +=item Version 1.112 + +Added lightweight catdir() and catfile() (a la File::Spec) +to enhance portability to Mac environment. + + +=item Version 1.111 + +Now uses File::Basename to create "typical" logfile name, +for portability. + + +=item Version 1.110 + +Fixed bug in constructor that surfaced if no log was being used. + +=back + +Created: Friday-the-13th of February, 1998. + + +=head1 AUTHOR + +Eryq (F<eryq@zeegee.com>). +President, ZeeGee Software Inc. (F<http://www.zeegee.com>) + +=cut + +#------------------------------ + +1; +__END__ + +my $T = new ExtUtils::TBone "testout/foo.tlog"; +$T->begin(3); +$T->msg("before 1\nor 2"); +$T->ok(1, "one"); +$T->ok(2, "Two"); +$T->ok(3, "Three", Roman=>'III', Arabic=>[3, '03'], Misc=>"3\nor 3"); +$T->end; + +1; + diff --git a/cpan/dist/MIME-Lite/t/Utils.pm b/cpan/dist/MIME-Lite/t/Utils.pm new file mode 100644 index 00000000..5d14e9d4 --- /dev/null +++ b/cpan/dist/MIME-Lite/t/Utils.pm @@ -0,0 +1,23 @@ +package Utils; + +@ISA = qw(Exporter); +@EXPORT = qw(slurp spew cmp); + +sub slurp { + my $path = shift; + open IN, "<$path"; my $data = join('',<IN>); close IN; $data; +} + +sub spew { + my ($path, $data) = @_; + open OUT, ">$path"; print OUT $data; close OUT; +} + +sub cmp { + my ($a, $b) = @_; + $a =~ s/\r//g; + $b =~ s/\r//g; + return ($a eq $b); +} + +1; diff --git a/cpan/dist/MIME-Lite/t/addrs.t b/cpan/dist/MIME-Lite/t/addrs.t new file mode 100644 index 00000000..d5758fa6 --- /dev/null +++ b/cpan/dist/MIME-Lite/t/addrs.t @@ -0,0 +1,87 @@ +#!/usr/bin/perl +use lib "lib", "t"; +use MIME::Lite; +use ExtUtils::TBone; +use Utils; + +# Make a tester... here are 3 different alternatives: +my $T = typical ExtUtils::TBone; # standard log +$MIME::Lite::VANILLA = 1; +$MIME::Lite::PARANOID = 1; + +# Pairs: +my @pairs = + ( + [' me@myhost.com ', + 1, + '<me@myhost.com>'], + + [' mylogin ', + 1, + '<mylogin>'], + + [' "Me, Jr." < me@myhost.com > ', + 1, + '<me@myhost.com>'], + + [' Me <me@myhost.com>', + 1, + '<me@myhost.com>'], + + ['"Me, Jr." <me@myhost.com>', + 1, + '<me@myhost.com>'], + + ['"Me@somewhere.com, Jr." <me@myhost.com>', + 1, + '<me@myhost.com>'], + + ['me@myhost.com,you@yourhost.com', + 2, + '<me@myhost.com> <you@yourhost.com>'], + + ['"Me" <me@myhost.com>, "You"<you@yourhost.com>', + 2, + '<me@myhost.com> <you@yourhost.com>'], + + ['"Me" <me@myhost.com>, you@yourhost.com, "And also" <she@herhost.com>', + 3, + '<me@myhost.com> <you@yourhost.com> <she@herhost.com>'], + + ['"Me" <me@myhost.com>, mylogin ,yourlogin , She <she@herhost.com>', + 4, + '<me@myhost.com> <mylogin> <yourlogin> <she@herhost.com>'] + ); + + +# Abort? +if (eval "require Mail::Address") { + $T->begin(1); + $T->ok(1, "we have and trust Mail::Address"); + $T->end; + exit 0; +} + +# Begin testing: +$T->begin(2 * @pairs); + +# New: +foreach my $pair (@pairs) { + my ($to, $count, $result) = @$pair; + my @addrs = MIME::Lite::extract_addrs($to); + + $T->ok_eqnum(int(@addrs), $count, + "compare count", + In => $to); + $T->ok_eq(join(' ', map {"<$_>"} @addrs), + $result, + "compare result", + In => $to); +} + +$T->end; + + + + + diff --git a/cpan/dist/MIME-Lite/t/data.t b/cpan/dist/MIME-Lite/t/data.t new file mode 100644 index 00000000..e3e5ef71 --- /dev/null +++ b/cpan/dist/MIME-Lite/t/data.t @@ -0,0 +1,55 @@ +#!/usr/bin/perl +use lib "lib", "t"; +use MIME::Lite; +use ExtUtils::TBone; +use Utils; + +# Make a tester... here are 3 different alternatives: +my $T = typical ExtUtils::TBone; # standard log +$MIME::Lite::VANILLA = 1; +$MIME::Lite::PARANOID = 1; + +# Begin testing: +$T->begin(4); + +my ($me, $str); + +#------------------------------ +$me = MIME::Lite->build(Type => 'text/plain', + Data => "Hello\nWorld\n"); +$str = $me->as_string; +$T->ok(($str =~ m{Hello\nWorld\n}), + $from, + "Data string"); + +#------------------------------ +$me = MIME::Lite->build(Type => 'text/plain', + Data => ["Hel", "lo\n", "World\n"]); +$str = $me->as_string; +$T->ok(($str =~ m{Hello\nWorld\n}), + $from, + "Data array 1"); + +#------------------------------ +$me = MIME::Lite->build(Type => 'text/plain', + Data => ["Hel", "lo", "\n", "", "World", "", "","\n"]); +$str = $me->as_string; +$T->ok(($str =~ m{Hello\nWorld\n}), + $from, + "Data array 2"); + +#------------------------------ +$me = MIME::Lite->build(Type => 'text/plain', + Path => "./testin/hello"); +$str = $me->as_string; +$T->ok(($str =~ m{Hello\nWorld\n}), + $from, + "Data file"); + + +$T->end; + + + + + diff --git a/cpan/dist/MIME-Lite/t/head.t b/cpan/dist/MIME-Lite/t/head.t new file mode 100644 index 00000000..a6680fb0 --- /dev/null +++ b/cpan/dist/MIME-Lite/t/head.t @@ -0,0 +1,87 @@ +#!/usr/bin/perl +use lib "lib", "t"; +use MIME::Lite; +use ExtUtils::TBone; +use Utils; + +# Make a tester... here are 3 different alternatives: +my $T = typical ExtUtils::TBone; # standard log +$MIME::Lite::VANILLA = 1; +$MIME::Lite::PARANOID = 1; + +# Begin testing: +$T->begin(14); + +# New: +my $from = 'me@myhost.com'; +my $me = MIME::Lite->build(From => $from, + To => 'you@yourhost.com', + Subject => 'Me!', + Type => 'text/plain', + Data => "Hello!\n"); + +# Test "get" [4 tests]: +$T->ok_eq(scalar($me->get('From')), + $from, + "get: simple get of 'From'"); +$T->ok_eq($me->get('From',0), + $from, + "get: indexed get(0) of 'From' gets first"); +$T->ok_eq($me->get('From',-1), + $from, + "get: indexed get(-1) of 'From' gets first"); +$T->ok_eq($me->get('FROM',0), + $from, + "get: indexed get(0) of 'FROM' gets From"); + +# Test "add": add one, then two [6 tests]: +$me->add('Received', 'sined'); +$me->add('Received', ['seeled', 'delivered']); +$T->ok_eq(scalar($me->get('Received')), + 'sined', + "add: scalar context get of 'Received'"); +$T->ok_eq($me->get('Received',0), + 'sined', + "add: scalar context get(0) of 'Received'"); +$T->ok_eq($me->get('Received',1), + 'seeled', + "add: scalar context get(1) of 'Received'"); +$T->ok_eq($me->get('Received',2), + 'delivered', + "add: scalar context get(2) of 'Received'"); +$T->ok_eq($me->get('Received',-1), + 'delivered', + "add: scalar context get(-1) of 'Received'"); +$T->ok_eq(($me->get('Received'))[1], + 'seeled', + "add: array context get of 'Received', indexed to 1'th elem"); + +# Test "delete" [1 test]: +$me->delete('RECEIVED'); +$T->ok(!defined($me->get('Received')), + "delete: deletion of RECEIVED worked"); + +# Test "replace" [1 test]: +$me->replace('subject', "Hellooooo, nurse!"); +$T->ok_eq($me->get('SUBJECT'), + "Hellooooo, nurse!", + "replace: replace of SUBJECT worked"); + +# Test "attr" [2 tests]: +$me->attr('content-type.charset', 'US-ASCII'); +$T->ok_eq($me->attr('content-type.charset'), + 'US-ASCII', + "attr: replace of charset worked"); +# +my ($ct) = map {($_->[0] eq 'content-type') ? $_->[1] : ()} @{$me->fields}; +$T->ok_eq($ct, + 'text/plain; charset="US-ASCII"', + "attr: replace of charset worked on whole line"); + + +$T->end; + + + + + diff --git a/cpan/dist/MIME-Lite/t/verify.t b/cpan/dist/MIME-Lite/t/verify.t new file mode 100644 index 00000000..bb254239 --- /dev/null +++ b/cpan/dist/MIME-Lite/t/verify.t @@ -0,0 +1,39 @@ +#!/usr/bin/perl +use lib "lib", "t"; +use MIME::Lite; +use ExtUtils::TBone; +use Utils; + +# Make a tester... here are 3 different alternatives: +my $T = typical ExtUtils::TBone; # standard log +$MIME::Lite::VANILLA = 1; +$MIME::Lite::PARANOID = 1; + +# Begin testing: +$T->begin(2); + +my $msg; + +$msg = MIME::Lite->new(From=>"me", To=>"you"); +$msg->attach(Path => "boguscmd |"); +$msg->attach(Data => "Hello"); +$msg->attach(Path => "<path.to.missing.file"); +eval { $msg->verify_data }; +$T->ok($@ =~ /path\.to\.missing\.file/, + "Did we detect a missing file?", + Error => $@); + +$msg = MIME::Lite->new(From=>"me", To=>"you"); +$msg->attach(Data => "Hello"); +eval { $msg->verify_data }; +$T->ok(!$@, + "Did we detect NO missing file?", + Error => $@); + + +$T->end; + + + + + diff --git a/cpan/dist/MIME-Lite/testin/README b/cpan/dist/MIME-Lite/testin/README new file mode 100644 index 00000000..84a5b0e7 --- /dev/null +++ b/cpan/dist/MIME-Lite/testin/README @@ -0,0 +1 @@ +Test input directory diff --git a/cpan/dist/MIME-Lite/testin/hello b/cpan/dist/MIME-Lite/testin/hello new file mode 100644 index 00000000..f9264f7f --- /dev/null +++ b/cpan/dist/MIME-Lite/testin/hello @@ -0,0 +1,2 @@ +Hello +World diff --git a/cpan/lib/MIME/Lite.pm b/cpan/lib/MIME/Lite.pm new file mode 100644 index 00000000..ad8ac3cf --- /dev/null +++ b/cpan/lib/MIME/Lite.pm @@ -0,0 +1,3227 @@ +package MIME::Lite; + + +=head1 NAME + +MIME::Lite - low-calorie MIME generator + + +=head1 SYNOPSIS + + use MIME::Lite; + +Create a single-part message: + + ### Create a new single-part message, to send a GIF file: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'Helloooooo, nurse!', + Type =>'image/gif', + Encoding =>'base64', + Path =>'hellonurse.gif' + ); + +Create a multipart message (i.e., one with attachments): + + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'multipart/mixed' + ); + + ### Add parts (each "attach" has same arguments as "new"): + $msg->attach(Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif', + Disposition => 'attachment' + ); + +Output a message: + + ### Format as a string: + $str = $msg->as_string; + + ### Print to a filehandle (say, a "sendmail" stream): + $msg->print(\*SENDMAIL); + + +Send a message: + + ### Send in the "best" way (the default is to use "sendmail"): + $msg->send; + + + +=head1 DESCRIPTION + +In the never-ending quest for great taste with fewer calories, +we proudly present: I<MIME::Lite>. + +MIME::Lite is intended as a simple, standalone module for generating +(not parsing!) MIME messages... specifically, it allows you to +output a simple, decent single- or multi-part message with text or binary +attachments. It does not require that you have the Mail:: or MIME:: +modules installed. + +You can specify each message part as either the literal data itself (in +a scalar or array), or as a string which can be given to open() to get +a readable filehandle (e.g., "<filename" or "somecommand|"). + +You don't need to worry about encoding your message data: +this module will do that for you. It handles the 5 standard MIME encodings. + +If you need more sophisticated behavior, please get the MIME-tools +package instead. I will be more likely to add stuff to that toolkit +over this one. + + +=head1 MORE EXAMPLES + +=head2 Attach a GIF to a text message + +This will create a multipart message exactly as above, but using the +"attach to singlepart" hack: + + ### Create a new multipart message: + $msg = MIME::Lite->new( + From =>'me@myhost.com', + To =>'you@yourhost.com', + Cc =>'some@other.com, some@more.com', + Subject =>'A message with 2 parts...', + Type =>'TEXT', + Data =>"Here's the GIF file you wanted" + ); + + ### Attach a part: + $msg->attach(Type =>'image/gif', + Path =>'aaa000123.gif', + Filename =>'logo.gif' + ); + + +=head2 Attach a pre-prepared part (allows fine-tuning): + + $part = MIME::Lite->new( + Type =>'text/html', + Data =>'<H1>Hello</H1>', + ); + $part->attr('content-type.charset' => 'UTF8'); + $part->add('X-Comment' => 'A message for you'); + $msg->attach($part); + + +=head2 Send an HTML document... with images included! + + $msg = MIME::Lite->new( + To =>'you@yourhost.com', + Subject =>'HTML with in-line images!', + Type =>'multipart/related' + ); + $msg->attach(Type => 'text/html', + Data => qq{ <body> + Here's <i>my</i> image: + <img src="cid:myimage.gif"> + </body> } + ); + $msg->attach(Type => 'image/gif', + Id => 'myimage.gif', + Path => '/path/to/somefile.gif', + ); + $msg->send(); + + +=head2 Output a message to a filehandle + + ### Write it to a filehandle: + $msg->print(\*STDOUT); + + ### Write just the header: + $msg->print_header(\*STDOUT); + + ### Write just the encoded body: + $msg->print_body(\*STDOUT); + + +=head2 Get a message as a string + + ### Get entire message as a string: + $str = $msg->as_string; + + ### Get just the header: + $str = $msg->header_as_string; + + ### Get just the encoded body: + $str = $msg->body_as_string; + + +=head2 Change how messages are sent + + ### Do something like this in your 'main': + if ($I_DONT_HAVE_SENDMAIL) { + MIME::Lite->send('smtp', "smtp.myisp.net", Timeout=>60); + } + + ### Now this will do the right thing: + $msg->send; ### will now use Net::SMTP as shown above + + + + + + +=head1 FAQ + + +=head2 How do I prevent "Content" headers from showing up in my mail reader? + +Apparently, some people are using mail readers which display the MIME +headers like "Content-disposition", and they want MIME::Lite not +to generate them "because they look ugly". + +Sigh. + +Y'know, kids, those headers aren't just there for cosmetic purposes. +They help ensure that the message is I<understood> correctly by mail +readers. But okay, you asked for it, you got it... +here's how you can suppress the standard MIME headers. +Before you send the message, do this: + + $msg->scrub; + +You can scrub() any part of a multipart message independently; +just be aware that it works recursively. Before you scrub, +note the rules that I follow: + +=over 4 + +=item Content-type + +You can safely scrub the "content-type" attribute if, and only if, +the part is of type "text/plain" with charset "us-ascii". + +=item Content-transfer-encoding + +You can safely scrub the "content-transfer-encoding" attribute +if, and only if, the part uses "7bit", "8bit", or "binary" encoding. +You are far better off doing this if your lines are under 1000 +characters. Generally, that means you I<can> scrub it for plain +text, and you can I<not> scrub this for images, etc. + +=item Content-disposition + +You can safely scrub the "content-disposition" attribute +if you trust the mail reader to do the right thing when it decides +whether to show an attachment inline or as a link. Be aware +that scrubbing both the content-disposition and the content-type +means that there is no way to "recommend" a filename for the attachment! + +B<Note:> there are reports of brain-dead MUAs out there that +do the wrong thing if you I<provide> the content-disposition. +If your attachments keep showing up inline or vice-versa, +try scrubbing this attribute. + +=item Content-length + +You can always scrub "content-length" safely. + +=back + + +=head2 How do I give my attachment a [different] recommended filename? + +By using the Filename option (which is different from Path!): + + $msg->attach(Type => "image/gif", + Path => "/here/is/the/real/file.GIF", + Filename => "logo.gif"); + +You should I<not> put path information in the Filename. + + + +=head1 PUBLIC INTERFACE + +=head2 Global configuration + +To alter the way the entire module behaves, you have the following +methods/options: + +=over 4 + + +=item MIME::Lite->header_order() + +When used as a L<classmethod|/send>, this changes the default +order in which headers are output for I<all> messages. + + +=item MIME::Lite->quiet() + +This L<classmethod|/quiet> can be used to suppress/unsuppress +all warnings coming from this module. + + +=item MIME::Lite->send() + +When used as a L<classmethod|/send>, this can be used to specify +a different default mechanism for sending message. +The initial default is: + + MIME::Lite->send("sendmail", "/usr/lib/sendmail -t -oi -oem"); + +However, you should consider the similar but smarter and taint-safe variant: + + MIME::Lite->send("sendmail"); + +Or, for non-Unix users: + + MIME::Lite->send("smtp"); + + +=item $MIME::Lite::PARANOID + +If true, we won't attempt to use MIME::Base64/MIME::QuotedPrint, even +if they're available. +Default is B<false>. + + +=item $MIME::Lite::AUTO_ENCODE + +If true, automatically choose the encoding from the content type. +Default is B<true>. + + +=item $MIME::Lite::AUTO_CC + +If true, automatically send to the Cc/Bcc addresses for send_by_smtp(). +Default is B<true>. + + +=item $MIME::Lite::AUTO_VERIFY + +If true, check paths to attachments right before printing, raising an exception +if any path is unreadable. +Default is B<true>. + +=back + +=cut + +require 5.004; ### for /c modifier in m/\G.../gc modifier + +use Carp; +use FileHandle; + +use strict; +use vars qw( + $AUTO_CC + $AUTO_ENCODE + $AUTO_VERIFY + $PARANOID + $QUIET + $VANILLA + $VERSION + ); + + + +#============================== +#============================== +# +# GLOBALS, EXTERNAL/CONFIGURATION... + +### The package version, both in 1.23 style *and* usable by MakeMaker: +$VERSION = substr q$Revision: 2.108 $, 10; + +### Don't warn me about dangerous activities: +$QUIET = undef; + +### Set this true if you don't want to use MIME::Base64/MIME::QuotedPrint: +$PARANOID = 0; + +### Unsupported (for tester use): don't qualify boundary with time/pid: +$VANILLA = 0; + +### Automatically choose encoding from content type: +$AUTO_ENCODE = 1; + +### Automatically interpret CC/BCC for SMTP: +$AUTO_CC = 1; + +### Check paths right before printing: +$AUTO_VERIFY = 1; + + +#============================== +#============================== +# +# GLOBALS, INTERNAL... + +### Find sendmail: +my $SENDMAIL = "/usr/lib/sendmail"; +(-x $SENDMAIL) or ($SENDMAIL = "/usr/sbin/sendmail"); +(-x $SENDMAIL) or ($SENDMAIL = "sendmail"); + +### Our sending facilities: +my $Sender = "sendmail"; +my %SenderArgs = ( + "sendmail" => ["$SENDMAIL -t -oi -oem"], + "smtp" => [], + "sub" => [], +); + +### Boundary counter: +my $BCount = 0; + +### Known Mail/MIME fields... these, plus some general forms like +### "x-*", are recognized by build(): +my %KnownField = map {$_=>1} +qw( + bcc cc comments date encrypted + from keywords message-id mime-version organization + received references reply-to return-path sender + subject to + + approved + ); + +### What external packages do we use for encoding? +my @Uses; + +### Header order: +my @FieldOrder; + + +#============================== +#============================== +# +# PRIVATE UTILITY FUNCTIONS... + +#------------------------------ +# +# fold STRING +# +# Make STRING safe as a field value. Remove leading/trailing whitespace, +# and make sure newlines are represented as newline+space + +sub fold { + my $str = shift; + $str =~ s/^\s*|\s*$//g; ### trim + $str =~ s/\n/\n /g; + $str; +} + +#------------------------------ +# +# gen_boundary +# +# Generate a new boundary to use. +# The unsupported $VANILLA is for test purposes only. + +sub gen_boundary { + return ("_----------=_".($VANILLA ? '' : int(time).$$).$BCount++); +} + +#------------------------------ +# +# known_field FIELDNAME +# +# Is this a recognized Mail/MIME field? + +sub known_field { + my $field = lc(shift); + $KnownField{$field} or ($field =~ m{^(content|resent|x)-.}); +} + +#------------------------------ +# +# is_mime_field FIELDNAME +# +# Is this a field I manage? + +sub is_mime_field { + $_[0] =~ /^(mime\-|content\-)/i; +} + +#------------------------------ +# +# extract_addrs STRING +# +# Split STRING into an array of email addresses: somewhat of a KLUDGE. +# +# Unless paranoid, we try to load the real code before supplying our own. + +my $ATOM = '[^ \000-\037()<>@,;:\134"\056\133\135]+'; +my $QSTR = '".*?"'; +my $WORD = '(?:' . $QSTR . '|' . $ATOM . ')'; +my $DOMAIN = '(?:' . $ATOM . '(?:' . '\\.' . $ATOM . ')*' . ')'; +my $LOCALPART = '(?:' . $WORD . '(?:' . '\\.' . $WORD . ')*' . ')'; +my $ADDR = '(?:' . $LOCALPART . '@' . $DOMAIN . ')'; +my $PHRASE = '(?:' . $WORD . ')+'; +my $SEP = "(?:^\\s*|\\s*,\\s*)"; ### before elems in a list + +sub my_extract_addrs { + my $str = shift; + my @addrs; + $str =~ s/\s/ /g; ### collapse whitespace + + pos($str) = 0; + while ($str !~ m{\G\s*\Z}gco) { + ### print STDERR "TACKLING: ".substr($str, pos($str))."\n"; + if ($str =~ m{\G$SEP$PHRASE\s*<\s*($ADDR)\s*>}gco) {push @addrs,$1} + elsif ($str =~ m{\G$SEP($ADDR)}gco) {push @addrs,$1} + elsif ($str =~ m{\G$SEP($ATOM)}gco) {push @addrs,$1} + else { + my $problem = substr($str, pos($str)); + die "can't extract address at <$problem> in <$str>\n"; + } + } + return @addrs; +} + +if (eval "require Mail::Address") { + push @Uses, "A$Mail::Address::VERSION"; + eval q{ + sub extract_addrs { + return map { $_->format } Mail::Address->parse($_[0]); + } + }; ### q +} +else { + eval q{ + sub extract_addrs { + return my_extract_addrs(@_); + } + }; ### q +} ### if + + + +#============================== +#============================== +# +# PRIVATE ENCODING FUNCTIONS... + +#------------------------------ +# +# encode_base64 STRING +# +# Encode the given string using BASE64. +# Unless paranoid, we try to load the real code before supplying our own. + +if (!$PARANOID and eval "require MIME::Base64") { + import MIME::Base64 qw(encode_base64); + push @Uses, "B$MIME::Base64::VERSION"; +} +else { + eval q{ +sub encode_base64 { + my $res = ""; + my $eol = "\n"; + + pos($_[0]) = 0; ### thanks, Andreas! + while ($_[0] =~ /(.{1,45})/gs) { + $res .= substr(pack('u', $1), 1); + chop($res); + } + $res =~ tr|` -_|AA-Za-z0-9+/|; + + ### Fix padding at the end: + my $padding = (3 - length($_[0]) % 3) % 3; + $res =~ s/.{$padding}$/'=' x $padding/e if $padding; + + ### Break encoded string into lines of no more than 76 characters each: + $res =~ s/(.{1,76})/$1$eol/g if (length $eol); + return $res; +} ### sub + } ### q +} ### if + +#------------------------------ +# +# encode_qp STRING +# +# Encode the given string, LINE BY LINE, using QUOTED-PRINTABLE. +# Stolen from MIME::QuotedPrint by Gisle Aas, with a slight bug fix: we +# break lines earlier. Notice that this seems not to work unless +# encoding line by line. +# +# Unless paranoid, we try to load the real code before supplying our own. + +if (!$PARANOID and eval "require MIME::QuotedPrint") { + import MIME::QuotedPrint qw(encode_qp); + push @Uses, "Q$MIME::QuotedPrint::VERSION"; +} +else { + eval q{ +sub encode_qp { + my $res = shift; + local($_); + $res =~ s/([^ \t\n!-<>-~])/sprintf("=%02X", ord($1))/eg; ### rule #2,#3 + $res =~ s/([ \t]+)$/ + join('', map { sprintf("=%02X", ord($_)) } + split('', $1) + )/egm; ### rule #3 (encode whitespace at eol) + + ### rule #5 (lines shorter than 76 chars, but can't break =XX escapes: + my $brokenlines = ""; + $brokenlines .= "$1=\n" while $res =~ s/^(.{70}([^=]{2})?)//; ### 70 was 74 + $brokenlines =~ s/=\n$// unless length $res; + "$brokenlines$res"; +} ### sub + } ### q +} ### if + + +#------------------------------ +# +# encode_8bit STRING +# +# Encode the given string using 8BIT. +# This breaks long lines into shorter ones. + +sub encode_8bit { + my $str = shift; + $str =~ s/^(.{990})/$1\n/mg; + $str; +} + +#------------------------------ +# +# encode_7bit STRING +# +# Encode the given string using 7BIT. +# This NO LONGER protects people through encoding. + +sub encode_7bit { + my $str = shift; + $str =~ s/[\x80-\xFF]//g; + $str =~ s/^(.{990})/$1\n/mg; + $str; +} + +#============================== +#============================== + +=head2 Construction + +=over 4 + +=cut + + +#------------------------------ + +=item new [PARAMHASH] + +I<Class method, constructor.> +Create a new message object. + +If any arguments are given, they are passed into C<build()>; otherwise, +just the empty object is created. + +=cut + +sub new { + my $class = shift; + + ### Create basic object: + my $self = { + Attrs => {}, ### MIME attributes + Header => [], ### explicit message headers + Parts => [], ### array of parts + }; + bless $self, $class; + + ### Build, if needed: + return (@_ ? $self->build(@_) : $self); +} + + +#------------------------------ + +=item attach PART + +=item attach PARAMHASH... + +I<Instance method.> +Add a new part to this message, and return the new part. + +If you supply a single PART argument, it will be regarded +as a MIME::Lite object to be attached. Otherwise, this +method assumes that you are giving in the pairs of a PARAMHASH +which will be sent into C<new()> to create the new part. + +One of the possibly-quite-useful hacks thrown into this is the +"attach-to-singlepart" hack: if you attempt to attach a part (let's +call it "part 1") to a message that doesn't have a content-type +of "multipart" or "message", the following happens: + +=over 4 + +=item * + +A new part (call it "part 0") is made. + +=item * + +The MIME attributes and data (but I<not> the other headers) +are cut from the "self" message, and pasted into "part 0". + +=item * + +The "self" is turned into a "multipart/mixed" message. + +=item * + +The new "part 0" is added to the "self", and I<then> "part 1" is added. + +=back + +One of the nice side-effects is that you can create a text message +and then add zero or more attachments to it, much in the same way +that a user agent like Netscape allows you to do. + +=cut + +sub attach { + my $self = shift; + + ### Create new part, if necessary: + my $part1 = ((@_ == 1) ? shift : ref($self)->new(Top=>0, @_)); + + ### Do the "attach-to-singlepart" hack: + if ($self->attr('content-type') !~ m{^(multipart|message)/}i) { + + ### Create part zero: + my $part0 = ref($self)->new; + + ### Cut MIME stuff from self, and paste into part zero: + foreach (qw(Attrs Data Path FH)) { + $part0->{$_} = $self->{$_}; delete($self->{$_}); + } + $part0->top_level(0); ### clear top-level attributes + + ### Make self a top-level multipart: + $self->{Attrs} ||= {}; ### reset + $self->attr('content-type' => 'multipart/mixed'); + $self->attr('content-type.boundary' => gen_boundary()); + $self->attr('content-transfer-encoding' => '7bit'); + $self->top_level(1); ### activate top-level attributes + + ### Add part 0: + push @{$self->{Parts}}, $part0; + } + + ### Add the new part: + push @{$self->{Parts}}, $part1; + $part1; +} + +#------------------------------ + +=item build [PARAMHASH] + +I<Class/instance method, initializer.> +Create (or initialize) a MIME message object. +Normally, you'll use the following keys in PARAMHASH: + + * Data, FH, or Path (either one of these, or none if multipart) + * Type (e.g., "image/jpeg") + * From, To, and Subject (if this is the "top level" of a message) + +The PARAMHASH can contain the following keys: + +=over 4 + +=item (fieldname) + +Any field you want placed in the message header, taken from the +standard list of header fields (you don't need to worry about case): + + Approved Encrypted Received Sender + Bcc From References Subject + Cc Keywords Reply-To To + Comments Message-ID Resent-* X-* + Content-* MIME-Version Return-Path + Date Organization + +To give experienced users some veto power, these fields will be set +I<after> the ones I set... so be careful: I<don't set any MIME fields> +(like C<Content-type>) unless you know what you're doing! + +To specify a fieldname that's I<not> in the above list, even one that's +identical to an option below, just give it with a trailing C<":">, +like C<"My-field:">. When in doubt, that I<always> signals a mail +field (and it sort of looks like one too). + +=item Data + +I<Alternative to "Path" or "FH".> +The actual message data. This may be a scalar or a ref to an array of +strings; if the latter, the message consists of a simple concatenation +of all the strings in the array. + +=item Datestamp + +I<Optional.> +If given true (or omitted), we force the creation of a C<Date:> field +stamped with the current date/time if this is a top-level message. +You may want this if using L<send_by_smtp()|/send_by_smtp>. +If you don't want this to be done, either provide your own Date +or explicitly set this to false. + +=item Disposition + +I<Optional.> +The content disposition, C<"inline"> or C<"attachment">. +The default is C<"inline">. + +=item Encoding + +I<Optional.> +The content transfer encoding that should be used to encode your data: + + Use encoding: | If your message contains: + ------------------------------------------------------------ + 7bit | Only 7-bit text, all lines <1000 characters + 8bit | 8-bit text, all lines <1000 characters + quoted-printable | 8-bit text or long lines (more reliable than "8bit") + base64 | Largely non-textual data: a GIF, a tar file, etc. + +The default is taken from the Type; generally it is "binary" (no +encoding) for text/*, message/*, and multipart/*, and "base64" for +everything else. A value of C<"binary"> is generally I<not> suitable +for sending anything but ASCII text files with lines under 1000 +characters, so consider using one of the other values instead. + +In the case of "7bit"/"8bit", long lines are automatically chopped to +legal length; in the case of "7bit", all 8-bit characters are +automatically I<removed>. This may not be what you want, so pick your +encoding well! For more info, see L<"A MIME PRIMER">. + +=item FH + +I<Alternative to "Data" or "Path".> +Filehandle containing the data, opened for reading. +See "ReadNow" also. + +=item Filename + +I<Optional.> +The name of the attachment. You can use this to supply a +recommended filename for the end-user who is saving the attachment +to disk. You only need this if the filename at the end of the +"Path" is inadequate, or if you're using "Data" instead of "Path". +You should I<not> put path information in here (e.g., no "/" +or "\" or ":" characters should be used). + +=item Id + +I<Optional.> +Same as setting "content-id". + +=item Length + +I<Optional.> +Set the content length explicitly. Normally, this header is automatically +computed, but only under certain circumstances (see L<"Limitations">). + +=item Path + +I<Alternative to "Data" or "FH".> +Path to a file containing the data... actually, it can be any open()able +expression. If it looks like a path, the last element will automatically +be treated as the filename. +See "ReadNow" also. + +=item ReadNow + +I<Optional, for use with "Path".> +If true, will open the path and slurp the contents into core now. +This is useful if the Path points to a command and you don't want +to run the command over and over if outputting the message several +times. B<Fatal exception> raised if the open fails. + +=item Top + +I<Optional.> +If defined, indicates whether or not this is a "top-level" MIME message. +The parts of a multipart message are I<not> top-level. +Default is true. + +=item Type + +I<Optional.> +The MIME content type, or one of these special values (case-sensitive): + + "TEXT" means "text/plain" + "BINARY" means "application/octet-stream" + +The default is C<"TEXT">. + +=back + +A picture being worth 1000 words (which +is of course 2000 bytes, so it's probably more of an "icon" than a "picture", +but I digress...), here are some examples: + + $msg = MIME::Lite->build( + From => 'yelling@inter.com', + To => 'stocking@fish.net', + Subject => "Hi there!", + Type => 'TEXT', + Encoding => '7bit', + Data => "Just a quick note to say hi!"); + + $msg = MIME::Lite->build( + From => 'dorothy@emerald-city.oz', + To => 'gesundheit@edu.edu.edu', + Subject => "A gif for U" + Type => 'image/gif', + Path => "/home/httpd/logo.gif"); + + $msg = MIME::Lite->build( + From => 'laughing@all.of.us', + To => 'scarlett@fiddle.dee.de', + Subject => "A gzipp'ed tar file", + Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + +To show you what's really going on, that last example could also +have been written: + + $msg = new MIME::Lite; + $msg->build(Type => 'x-gzip', + Path => "gzip < /usr/inc/somefile.tar |", + ReadNow => 1, + Filename => "somefile.tgz"); + $msg->add(From => "laughing@all.of.us"); + $msg->add(To => "scarlett@fiddle.dee.de"); + $msg->add(Subject => "A gzipp'ed tar file"); + +=cut + +sub build { + my $self = shift; + my %params = @_; + my @params = @_; + my $key; + + ### Miko's note: reorganized to check for exactly one of Data, Path, or FH + (defined($params{Data})+defined($params{Path})+defined($params{FH}) <= 1) + or croak "supply exactly zero or one of (Data|Path|FH).\n"; + + ### Create new instance, if necessary: + ref($self) or $self = $self->new; + + + ### CONTENT-TYPE.... + ### + + ### Get content-type: + my $type = ($params{Type} || 'TEXT'); + ($type eq 'TEXT') and $type = 'text/plain'; + ($type eq 'BINARY') and $type = 'application/octet-stream'; + $type = lc($type); + $self->attr('content-type' => $type); + + ### Get some basic attributes from the content type: + my $is_multipart = ($type =~ m{^(multipart)/}i); + + ### Add in the multipart boundary: + if ($is_multipart) { + my $boundary = gen_boundary(); + $self->attr('content-type.boundary' => $boundary); + } + + + ### CONTENT-ID... + ### + $self->attr('content-id' => $params{Id}) if defined($params{Id}); + + + ### DATA OR PATH... + ### Note that we must do this *after* we get the content type, + ### in case read_now() is invoked, since it needs the binmode(). + + ### Get data, as... + ### ...either literal data: + if (defined($params{Data})) { + $self->data($params{Data}); + } + ### ...or a path to data: + elsif (defined($params{Path})) { + $self->path($params{Path}); ### also sets filename + $self->read_now if $params{ReadNow}; + } + ### ...or a filehandle to data: + ### Miko's note: this part works much like the path routine just above, + elsif (defined($params{FH})) { + $self->fh($params{FH}); + $self->read_now if $params{ReadNow}; ### implement later + } + + + ### FILENAME... (added by Ian Smith <ian@safeway.dircon.co.uk> on 8/4/97) + ### Need this to make sure the filename is added. The Filename + ### attribute is ignored, otherwise. + if (defined($params{Filename})) { + $self->filename($params{Filename}); + } + + + ### CONTENT-TRANSFER-ENCODING... + ### + + ### Get it: + my $enc = ($params{Encoding} || + ($AUTO_ENCODE and $self->suggest_encoding($type)) || + 'binary'); + $self->attr('content-transfer-encoding' => lc($enc)); + + ### Sanity check: + if ($type =~ m{^(multipart|message)/}) { + ($enc =~ m{^(7bit|8bit|binary)\Z}) or + croak "illegal MIME: can't have encoding $enc with type $type\n"; + } + + ### CONTENT-DISPOSITION... + ### Default is inline for single, none for multis: + ### + my $disp = ($params{Disposition} or ($is_multipart ? undef : 'inline')); + $self->attr('content-disposition' => $disp); + + ### CONTENT-LENGTH... + ### + my $length; + if (exists($params{Length})) { ### given by caller: + $self->attr('content-length' => $params{Length}); + } + else { ### compute it ourselves + $self->get_length; + } + + ### Init the top-level fields: + my $is_top = defined($params{Top}) ? $params{Top} : 1; + $self->top_level($is_top); + + ### Datestamp if desired: + my $ds_wanted = $params{Datestamp}; + my $ds_defaulted = ($is_top and !exists($params{Datestamp})); + if (($ds_wanted or $ds_defaulted) and !exists($params{Date})) { + my ($u_wdy, $u_mon, $u_mdy, $u_time, $u_y4) = + split /\s+/, gmtime().""; ### should be non-locale-dependent + my $date = "$u_wdy, $u_mdy $u_mon $u_y4 $u_time UT"; + $self->add("date", $date); + } + + ### Set message headers: + my @paramz = @params; + my $field; + while (@paramz) { + my ($tag, $value) = (shift(@paramz), shift(@paramz)); + + ### Get tag, if a tag: + if ($tag =~ /^-(.*)/) { ### old style, backwards-compatibility + $field = lc($1); + } + elsif ($tag =~ /^(.*):$/) { ### new style + $field = lc($1); + } + elsif (known_field($field = lc($tag))) { ### known field + ### no-op + } + else { ### not a field: + next; + } + + ### Add it: + $self->add($field, $value); + } + + ### Done! + $self; +} + +=back + +=cut + + +#============================== +#============================== + +=head2 Setting/getting headers and attributes + +=over 4 + +=cut + +#------------------------------ +# +# top_level ONOFF +# +# Set/unset the top-level attributes and headers. +# This affects "MIME-Version" and "X-Mailer". + +sub top_level { + my ($self, $onoff) = @_; + if ($onoff) { + $self->attr('MIME-Version' => '1.0'); + my $uses = (@Uses ? ("(" . join("; ", @Uses) . ")") : ''); + $self->replace('X-Mailer' => "MIME::Lite $VERSION $uses") + unless $VANILLA; + } + else { + $self->attr('MIME-Version' => undef); + $self->delete('X-Mailer'); + } +} + +#------------------------------ + +=item add TAG,VALUE + +I<Instance method.> +Add field TAG with the given VALUE to the end of the header. +The TAG will be converted to all-lowercase, and the VALUE +will be made "safe" (returns will be given a trailing space). + +B<Beware:> any MIME fields you "add" will override any MIME +attributes I have when it comes time to output those fields. +Normally, you will use this method to add I<non-MIME> fields: + + $msg->add("Subject" => "Hi there!"); + +Giving VALUE as an arrayref will cause all those values to be added. +This is only useful for special multiple-valued fields like "Received": + + $msg->add("Received" => ["here", "there", "everywhere"] + +Giving VALUE as the empty string adds an invisible placeholder +to the header, which can be used to suppress the output of +the "Content-*" fields or the special "MIME-Version" field. +When suppressing fields, you should use replace() instead of add(): + + $msg->replace("Content-disposition" => ""); + +I<Note:> add() is probably going to be more efficient than C<replace()>, +so you're better off using it for most applications if you are +certain that you don't need to delete() the field first. + +I<Note:> the name comes from Mail::Header. + +=cut + +sub add { + my $self = shift; + my $tag = lc(shift); + my $value = shift; + + ### If a dangerous option, warn them: + carp "Explicitly setting a MIME header field ($tag) is dangerous:\n". + "use the attr() method instead.\n" + if (is_mime_field($tag) && !$QUIET); + + ### Get array of clean values: + my @vals = ((ref($value) and (ref($value) eq 'ARRAY')) + ? @{$value} + : ($value.'')); + map { s/\n/\n /g } @vals; + + ### Add them: + foreach (@vals) { + push @{$self->{Header}}, [$tag, $_]; + } +} + +#------------------------------ + +=item attr ATTR,[VALUE] + +I<Instance method.> +Set MIME attribute ATTR to the string VALUE. +ATTR is converted to all-lowercase. +This method is normally used to set/get MIME attributes: + + $msg->attr("content-type" => "text/html"); + $msg->attr("content-type.charset" => "US-ASCII"); + $msg->attr("content-type.name" => "homepage.html"); + +This would cause the final output to look something like this: + + Content-type: text/html; charset=US-ASCII; name="homepage.html" + +Note that the special empty sub-field tag indicates the anonymous +first sub-field. + +Giving VALUE as undefined will cause the contents of the named +subfield to be deleted. + +Supplying no VALUE argument just returns the attribute's value: + + $type = $msg->attr("content-type"); ### returns "text/html" + $name = $msg->attr("content-type.name"); ### returns "homepage.html" + +=cut + +sub attr { + my ($self, $attr, $value) = @_; + $attr = lc($attr); + + ### Break attribute name up: + my ($tag, $subtag) = split /\./, $attr; + defined($subtag) or $subtag = ''; + + ### Set or get? + if (@_ > 2) { ### set: + $self->{Attrs}{$tag} ||= {}; ### force hash + delete $self->{Attrs}{$tag}{$subtag}; ### delete first + if (defined($value)) { ### set... + $value =~ s/[\r\n]//g; ### make clean + $self->{Attrs}{$tag}{$subtag} = $value; + } + } + + ### Return current value: + $self->{Attrs}{$tag}{$subtag}; +} + +sub _safe_attr { + my ($self, $attr) = @_; + my $v = $self->attr($attr); + defined($v) ? $v : ''; +} + +#------------------------------ + +=item delete TAG + +I<Instance method.> +Delete field TAG with the given VALUE to the end of the header. +The TAG will be converted to all-lowercase. + + $msg->delete("Subject"); + +I<Note:> the name comes from Mail::Header. + +=cut + +sub delete { + my $self = shift; + my $tag = lc(shift); + + ### Delete from the header: + my $hdr = []; + my $field; + foreach $field (@{$self->{Header}}) { + push @$hdr, $field if ($field->[0] ne $tag); + } + $self->{Header} = $hdr; + $self; +} + + +#------------------------------ + +=item field_order FIELD,...FIELD + +I<Class/instance method.> +Change the order in which header fields are output for this object: + + $msg->field_order('from', 'to', 'content-type', 'subject'); + +When used as a class method, changes the default settings for +all objects: + + MIME::Lite->field_order('from', 'to', 'content-type', 'subject'); + +Case does not matter: all field names will be coerced to lowercase. +In either case, supply the empty array to restore the default ordering. + +=cut + +sub field_order { + my $self = shift; + if (ref($self)) { $self->{FieldOrder} = [ map { lc($_) } @_ ] } + else { @FieldOrder = map { lc($_) } @_ } +} + +#------------------------------ + +=item fields + +I<Instance method.> +Return the full header for the object, as a ref to an array +of C<[TAG, VALUE]> pairs, where each TAG is all-lowercase. +Note that any fields the user has explicitly set will override the +corresponding MIME fields that we would otherwise generate. +So, don't say... + + $msg->set("Content-type" => "text/html; charset=US-ASCII"); + +unless you want the above value to override the "Content-type" +MIME field that we would normally generate. + +I<Note:> I called this "fields" because the header() method of +Mail::Header returns something different, but similar enough to +be confusing. + +You can change the order of the fields: see L</header_order>. +You really shouldn't need to do this, but some people have to +deal with broken mailers. + +=cut + +sub fields { + my $self = shift; + my @fields; + + ### Get a lookup-hash of all *explicitly-given* fields: + my %explicit = map { $_->[0] => 1 } @{$self->{Header}}; + + ### Start with any MIME attributes not given explicitly: + my $tag; + foreach $tag (sort keys %{$self->{Attrs}}) { + + ### Skip if explicit: + next if ($explicit{$tag}); + + ### Skip if no subtags: + my @subtags = keys %{$self->{Attrs}{$tag}}; + @subtags or next; + + ### Create string: + my $value; + defined($value = $self->{Attrs}{$tag}{''}) or next; ### need default + foreach (sort @subtags) { + next if ($_ eq ''); + $value .= qq{; $_="$self->{Attrs}{$tag}{$_}"}; + } + + ### Add to running fields; + push @fields, [$tag, $value]; + } + + ### Add remaining fields (note that we duplicate the array for safety): + foreach (@{$self->{Header}}) { + push @fields, [@{$_}]; + } + + ### Final step: + ### If a suggested ordering was given, we "sort" by that ordering. + ### The idea is that we give each field a numeric rank, which is + ### (1000 * order(field)) + origposition. + my @order = @{$self->{FieldOrder} || []}; ### object-specific + @order or @order = @FieldOrder; ### no? maybe generic + if (@order) { ### either? + + ### Create hash mapping field names to 1-based rank: + my %rank = map {$order[$_] => (1+$_)} (0..$#order); + + ### Create parallel array to @fields, called @ranked. + ### It contains fields tagged with numbers like 2003, where the + ### 3 is the original 0-based position, and 2000 indicates that + ### we wanted ths type of field to go second. + my @ranked = map { + [ + ($_ + 1000*($rank{lc($fields[$_][0])} || (2+$#order))), + $fields[$_] + ] + } (0..$#fields); + # foreach (@ranked) { + # print STDERR "RANKED: $_->[0] $_->[1][0] $_->[1][1]\n"; + # } + + ### That was half the Schwartzian transform. Here's the rest: + @fields = map { $_->[1] } + sort { $a->[0] <=> $b->[0] } + @ranked; + } + + ### Done! + return \@fields; +} + + +#------------------------------ + +=item filename [FILENAME] + +I<Instance method.> +Set the filename which this data will be reported as. +This actually sets both "standard" attributes. + +With no argument, returns the filename as dictated by the +content-disposition. + +=cut + +sub filename { + my ($self, $filename) = @_; + if (@_ > 1) { + $self->attr('content-type.name' => $filename); + $self->attr('content-disposition.filename' => $filename); + } + $self->attr('content-disposition.filename'); +} + +#------------------------------ + +=item get TAG,[INDEX] + +I<Instance method.> +Get the contents of field TAG, which might have been set +with set() or replace(). Returns the text of the field. + + $ml->get('Subject', 0); + +If the optional 0-based INDEX is given, then we return the INDEX'th +occurence of field TAG. Otherwise, we look at the context: +In a scalar context, only the first (0th) occurence of the +field is returned; in an array context, I<all> occurences are returned. + +I<Warning:> this should only be used with non-MIME fields. +Behavior with MIME fields is TBD, and will raise an exception for now. + +=cut + +sub get { + my ($self, $tag, $index) = @_; + $tag = lc($tag); + croak "get: can't be used with MIME fields\n" if is_mime_field($tag); + + my @all = map { ($_->[0] eq $tag) ? $_->[1] : ()} @{$self->{Header}}; + (defined($index) ? $all[$index] : (wantarray ? @all : $all[0])); +} + +#------------------------------ + +=item get_length + +I<Instance method.> +Recompute the content length for the message I<if the process is trivial>, +setting the "content-length" attribute as a side-effect: + + $msg->get_length; + +Returns the length, or undefined if not set. + +I<Note:> the content length can be difficult to compute, since it +involves assembling the entire encoded body and taking the length +of it (which, in the case of multipart messages, means freezing +all the sub-parts, etc.). + +This method only sets the content length to a defined value if the +message is a singlepart with C<"binary"> encoding, I<and> the body is +available either in-core or as a simple file. Otherwise, the content +length is set to the undefined value. + +Since content-length is not a standard MIME field anyway (that's right, kids: +it's not in the MIME RFCs, it's an HTTP thing), this seems pretty fair. + +=cut + +#---- +# Miko's note: I wasn't quite sure how to handle this, so I waited to hear +# what you think. Given that the content-length isn't always required, +# and given the performance cost of calculating it from a file handle, +# I thought it might make more sense to add some some sort of computelength +# property. If computelength is false, then the length simply isn't +# computed. What do you think? +# +# Eryq's reply: I agree; for now, we can silently leave out the content-type. + +sub get_length { + my $self = shift; + + my $is_multipart = ($self->attr('content-type') =~ m{^multipart/}i); + my $enc = lc($self->attr('content-transfer-encoding') || 'binary'); + my $length; + if (!$is_multipart && ($enc eq "binary")){ ### might figure it out cheap: + if (defined($self->{Data})) { ### it's in core + $length = length($self->{Data}); + } + elsif (defined($self->{FH})) { ### it's in a filehandle + ### no-op: it's expensive, so don't bother + } + elsif (defined($self->{Path})) { ### it's a simple file! + $length = (-s $self->{Path}) if (-e $self->{Path}); + } + } + $self->attr('content-length' => $length); + return $length; +} + +#------------------------------ + +=item replace TAG,VALUE + +I<Instance method.> +Delete all occurences of fields named TAG, and add a new +field with the given VALUE. TAG is converted to all-lowercase. + +B<Beware> the special MIME fields (MIME-version, Content-*): +if you "replace" a MIME field, the replacement text will override +the I<actual> MIME attributes when it comes time to output that field. +So normally you use attr() to change MIME fields and add()/replace() to +change I<non-MIME> fields: + + $msg->replace("Subject" => "Hi there!"); + +Giving VALUE as the I<empty string> will effectively I<prevent> that +field from being output. This is the correct way to suppress +the special MIME fields: + + $msg->replace("Content-disposition" => ""); + +Giving VALUE as I<undefined> will just cause all explicit values +for TAG to be deleted, without having any new values added. + +I<Note:> the name of this method comes from Mail::Header. + +=cut + +sub replace { + my ($self, $tag, $value) = @_; + $self->delete($tag); + $self->add($tag, $value) if defined($value); +} + + +#------------------------------ + +=item scrub + +I<Instance method.> +B<This is Alpha code. If you use it, please let me know how it goes.> +Recursively goes through the "parts" tree of this message and tries +to find MIME attributes that can be removed. +With an array argument, removes exactly those attributes; e.g.: + + $msg->scrub(['content-disposition', 'content-length']); + +Is the same as recursively doing: + + $msg->replace('Content-disposition' => ''); + $msg->replace('Content-length' => ''); + +=cut + +sub scrub { + my ($self, @a) = @_; + my ($expl) = @a; + local $QUIET = 1; + + ### Scrub me: + if (!@a) { ### guess + + ### Scrub length always: + $self->replace('content-length', ''); + + ### Scrub disposition if no filename, or if content-type has same info: + if (!$self->_safe_attr('content-disposition.filename') || + $self->_safe_attr('content-type.name')) { + $self->replace('content-disposition', ''); + } + + ### Scrub encoding if effectively unencoded: + if ($self->_safe_attr('content-transfer-encoding') =~ + /^(7bit|8bit|binary)$/i) { + $self->replace('content-transfer-encoding', ''); + } + + ### Scrub charset if US-ASCII: + if ($self->_safe_attr('content-type.charset') =~ /^(us-ascii)/i) { + $self->attr('content-type.charset' => undef); + } + + ### TBD: this is not really right for message/digest: + if ((keys %{$self->{Attrs}{'content-type'}} == 1) and + ($self->_safe_attr('content-type') eq 'text/plain')) { + $self->replace('content-type', ''); + } + } + elsif ($expl and (ref($expl) eq 'ARRAY')) { + foreach (@{$expl}) { $self->replace($_, ''); } + } + + ### Scrub my kids: + foreach (@{$self->{Parts}}) { $_->scrub(@a); } +} + +=back + +=cut + + +#============================== +#============================== + +=head2 Setting/getting message data + +=over 4 + +=cut + +#------------------------------ + +=item binmode [OVERRIDE] + +I<Instance method.> +With no argument, returns whether or not it thinks that the data +(as given by the "Path" argument of C<build()>) should be read using +binmode() (for example, when C<read_now()> is invoked). + +The default behavior is that any content type other than +C<text/*> or C<message/*> is binmode'd; this should in general work fine. + +With a defined argument, this method sets an explicit "override" +value. An undefined argument unsets the override. +The new current value is returned. + +=cut + +sub binmode { + my $self = shift; + $self->{Binmode} = shift if (@_); ### argument? set override + return (defined($self->{Binmode}) + ? $self->{Binmode} + : ($self->attr("content-type") !~ m{^(text|message)/}i)); +} + +#------------------------------ + +=item data [DATA] + +I<Instance method.> +Get/set the literal DATA of the message. The DATA may be +either a scalar, or a reference to an array of scalars (which +will simply be joined). + +I<Warning:> setting the data causes the "content-length" attribute +to be recomputed (possibly to nothing). + +=cut + +sub data { + my $self = shift; + if (@_) { + $self->{Data} = ((ref($_[0]) eq 'ARRAY') ? join('', @{$_[0]}) : $_[0]); + $self->get_length; + } + $self->{Data}; +} + + +#------------------------------ + +=item path [PATH] + +Get/set the PATH to the message data. + +I<Warning:> setting the path recomputes any existing "content-length" field, +and re-sets the "filename" (to the last element of the path if it +looks like a simple path, and to nothing if not). + +=cut + +sub path { + my $self = shift; + if (@_) { + + ### Set the path, and invalidate the content length: + $self->{Path} = shift; + + ### Re-set filename, extracting it from path if possible: + my $filename; + if ($self->{Path} and ($self->{Path} !~ /\|$/)) { ### non-shell path: + ($filename = $self->{Path}) =~ s/^<//; + ($filename) = ($filename =~ m{([^\/]+)\Z}); + } + $self->filename($filename); + + ### Reset the length: + $self->get_length; + } + $self->{Path}; +} + +#------------------------------ + +=item fh [FILEHANDLE] + +Get/set the FILEHANDLE which contains the message data. + +Takes a filehandle as an input and stores it in the object. +This routine is similar to path(); one important difference is that +no attempt is made to set the content length. + +=cut + +sub fh { + my $self = shift; + $self->{FH} = shift if @_; + $self->{FH}; +} + +#------------------------------ + +=item resetfh [FILEHANDLE] + +Set the current position of the filehandle back to the beginning. +Only applies if you used "FH" in build() or attach() for this message. + +Returns false if unable to reset the filehandle (since not all filehandles +are seekable). + +=cut + +#---- +# Miko's note: With the Data and Path, the same data could theoretically +# be reused. However, file handles need to be reset to be reused, +# so I added this routine. +# +# Eryq reply: beware... not all filehandles are seekable (think about STDIN)! + +sub resetfh { + my $self = shift; + seek($self->{FH},0,0); +} + +#------------------------------ + +=item read_now + +Forces data from the path/filehandle (as specified by C<build()>) +to be read into core immediately, just as though you had given it +literally with the C<Data> keyword. + +Note that the in-core data will always be used if available. + +Be aware that everything is slurped into a giant scalar: you may not want +to use this if sending tar files! The benefit of I<not> reading in the data +is that very large files can be handled by this module if left on disk +until the message is output via C<print()> or C<print_body()>. + +=cut + +sub read_now { + my $self = shift; + local $/ = undef; + + if ($self->{FH}) { ### data from a filehandle: + my $chunk; + my @chunks; + CORE::binmode($self->{FH}) if $self->binmode; + while (read($self->{FH}, $chunk, 1024)) { + push @chunks, $chunk; + } + $self->{Data} = join '', @chunks; + } + elsif ($self->{Path}) { ### data from a path: + open SLURP, $self->{Path} or croak "open $self->{Path}: $!\n"; + CORE::binmode(SLURP) if $self->binmode; + $self->{Data} = <SLURP>; ### sssssssssssssslurp... + close SLURP; ### ...aaaaaaaaahhh! + } +} + +#------------------------------ + +=item sign PARAMHASH + +Sign the message. This forces the message to be read into core, +after which the signature is appended to it. + +=over 4 + +=item Data + +As in C<build()>: the literal signature data. +Can be either a scalar or a ref to an array of scalars. + +=item Path + +As in C<build()>: the path to the file. + +=back + +If no arguments are given, the default is: + + Path => "$ENV{HOME}/.signature" + +The content-length is recomputed. + +=cut + +sub sign { + my $self = shift; + my %params = @_; + + ### Default: + @_ or $params{Path} = "$ENV{HOME}/.signature"; + + ### Force message in-core: + defined($self->{Data}) or $self->read_now; + + ### Load signature: + my $sig; + if (!defined($sig = $params{Data})) { ### not given explicitly: + local $/ = undef; + open SIG, $params{Path} or croak "open sig $params{Path}: $!\n"; + $sig = <SIG>; ### sssssssssssssslurp... + close SIG; ### ...aaaaaaaaahhh! + } + $sig = join('',@$sig) if (ref($sig) and (ref($sig) eq 'ARRAY')); + + ### Append, following Internet conventions: + $self->{Data} .= "\n-- \n$sig"; + + ### Re-compute length: + $self->get_length; + 1; +} + +#------------------------------ +# +# =item suggest_encoding CONTENTTYPE +# +# I<Class/instance method.> +# Based on the CONTENTTYPE, return a good suggested encoding. +# C<text> and C<message> types have their bodies scanned line-by-line +# for 8-bit characters and long lines; lack of either means that the +# message is 7bit-ok. Other types are chosen independent of their body: +# +# Major type: 7bit ok? Suggested encoding: +# ------------------------------------------------------------ +# text yes 7bit +# no quoted-printable +# unknown binary +# +# message yes 7bit +# no binary +# unknown binary +# +# multipart n/a binary (in case some parts are not ok) +# +# (other) n/a base64 +# +#=cut + +sub suggest_encoding { + my ($self, $ctype) = @_; + + my ($type) = split '/', lc($ctype); + if (($type eq 'text') || ($type eq 'message')) { ### scan message body + return 'binary'; + } + else { + return ($type eq 'multipart') ? 'binary' : 'base64'; + } +} + +#------------------------------ + +=item verify_data + +I<Instance method.> +Verify that all "paths" to attached data exist, recursively. +It might be a good idea for you to do this before a print(), to +prevent accidental partial output if a file might be missing. +Raises exception if any path is not readable. + +=cut + +sub verify_data { + my $self = shift; + + ### Verify self: + my $path = $self->{Path}; + if ($path and ($path !~ /\|$/)) { ### non-shell path: + $path =~ s/^<//; + (-r $path) or die "$path: not readable\n"; + } + + ### Verify parts: + foreach my $part (@{$self->{Parts}}) { $part->verify_data } + 1; +} + +=back + +=cut + + +#============================== +#============================== + +=head2 Output + +=over 4 + +=cut + +#------------------------------ + +=item print [OUTHANDLE] + +I<Instance method.> +Print the message to the given output handle, or to the currently-selected +filehandle if none was given. + +All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +=cut + +sub print { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Output head, separator, and body: + $out->print($self->header_as_string, "\n"); + $self->print_body($out); +} + +#------------------------------ +# +# print_for_smtp +# +# Instance method, private. +# Print, but filter out the topmost "Bcc" field. +# This is because qmail apparently doesn't do this for us! +# +sub print_for_smtp { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Create a safe head: + my @fields = grep { $_->[0] ne 'bcc' } @{$self->fields}; + my $header = $self->fields_as_string(\@fields); + + ### Output head, separator, and body: + $out->print($header, "\n"); + $self->print_body($out); +} + +#------------------------------ + +=item print_body [OUTHANDLE] + +I<Instance method.> +Print the body of a message to the given output handle, or to +the currently-selected filehandle if none was given. + +All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +B<Fatal exception> raised if unable to open any of the input files, +or if a part contains no data, or if an unsupported encoding is +encountered. + +=cut + +sub print_body { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Output either the body or the parts. + ### Notice that we key off of the content-type! We expect fewer + ### accidents that way, since the syntax will always match the MIME type. + my $type = $self->attr('content-type'); + if ($type =~ m{^multipart/}i) { + my $boundary = $self->attr('content-type.boundary'); + + ### Preamble: + $out->print("This is a multi-part message in MIME format.\n"); + + ### Parts: + my $part; + foreach $part (@{$self->{Parts}}) { + $out->print("\n--$boundary\n"); + $part->print($out); + } + + ### Epilogue: + $out->print("\n--$boundary--\n\n"); + } + elsif ($type =~ m{^message/}) { + my @parts = @{$self->{Parts}}; + + ### It's a toss-up; try both data and parts: + if (@parts == 0) { $self->print_simple_body($out) } + elsif (@parts == 1) { $parts[0]->print($out) } + else { croak "can't handle message with >1 part\n"; } + } + else { + $self->print_simple_body($out); + } + 1; +} + +#------------------------------ +# +# print_simple_body [OUTHANDLE] +# +# I<Instance method, private.> +# Print the body of a simple singlepart message to the given +# output handle, or to the currently-selected filehandle if none +# was given. +# +# Note that if you want to print "the portion after +# the header", you don't want this method: you want +# L<print_body()|/print_body>. +# +# All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +# any object that responds to a print() message. +# +# B<Fatal exception> raised if unable to open any of the input files, +# or if a part contains no data, or if an unsupported encoding is +# encountered. +# +sub print_simple_body { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Get content-transfer-encoding: + my $encoding = uc($self->attr('content-transfer-encoding')); + + ### Notice that we don't just attempt to slurp the data in from a file: + ### by processing files piecemeal, we still enable ourselves to prepare + ### very large MIME messages... + + ### Is the data in-core? If so, blit it out... + if (defined($self->{Data})) { + DATA: + { local $_ = $encoding; + + /^BINARY$/ and do { + $out->print($self->{Data}); + last DATA; + }; + /^8BIT$/ and do { + $out->print(encode_8bit($self->{Data})); + last DATA; + }; + /^7BIT$/ and do { + $out->print(encode_7bit($self->{Data})); + last DATA; + }; + /^QUOTED-PRINTABLE$/ and do { + ### UNTAINT since m//mg on tainted data loops forever: + my ($untainted) = ($self->{Data} =~ m/\A(.*)\Z/s); + + ### Encode it line by line: + while ($untainted =~ m{^(.*[\r\n]*)}mg) { + $out->print(encode_qp($1)); ### have to do it line by line... + } + last DATA; + }; + /^BASE64/ and do { + $out->print(encode_base64($self->{Data})); + last DATA; + }; + croak "unsupported encoding: `$_'\n"; + } + } + + ### Else, is the data in a file? If so, output piecemeal... + ### Miko's note: this routine pretty much works the same with a path + ### or a filehandle. the only difference in behaviour is that it does + ### not attempt to open anything if it already has a filehandle + elsif (defined($self->{Path}) || defined($self->{FH})) { + no strict 'refs'; ### in case FH is not an object + my $DATA; + + ### Open file if necessary: + if (defined($self->{Path})) { + $DATA = new FileHandle || croak "can't get new filehandle\n"; + $DATA->open("$self->{Path}") or croak "open $self->{Path}: $!\n"; + } + else { + $DATA=$self->{FH}; + } + CORE::binmode($DATA) if $self->binmode; + + ### Encode piece by piece: + PATH: + { local $_ = $encoding; + + /^BINARY$/ and do { + $out->print($_) while read($DATA, $_, 2048); + last PATH; + }; + /^8BIT$/ and do { + $out->print(encode_8bit($_)) while (<$DATA>); + last PATH; + }; + /^7BIT$/ and do { + $out->print(encode_7bit($_)) while (<$DATA>); + last PATH; + }; + /^QUOTED-PRINTABLE$/ and do { + $out->print(encode_qp($_)) while (<$DATA>); + last PATH; + }; + /^BASE64$/ and do { + $out->print(encode_base64($_)) while (read($DATA, $_, 45)); + last PATH; + }; + croak "unsupported encoding: `$_'\n"; + } + + ### Close file: + close $DATA if defined($self->{Path}); + } + + else { + croak "no data in this part\n"; + } + 1; +} + +#------------------------------ + +=item print_header [OUTHANDLE] + +I<Instance method.> +Print the header of the message to the given output handle, +or to the currently-selected filehandle if none was given. + +All OUTHANDLE has to be is a filehandle (possibly a glob ref), or +any object that responds to a print() message. + +=cut + +sub print_header { + my ($self, $out) = @_; + + ### Coerce into a printable output handle: + $out = wrap MIME::Lite::IO_Handle $out; + + ### Output the header: + $out->print($self->header_as_string); + 1; +} + +#------------------------------ + +=item as_string + +I<Instance method.> +Return the entire message as a string, with a header and an encoded body. + +=cut + +sub as_string { + my $self = shift; + my @buf; + my $io = (wrap MIME::Lite::IO_ScalarArray \@buf); + $self->print($io); + join '', @buf; +} +*stringify = \&as_string; ### backwards compatibility + +#------------------------------ + +=item body_as_string + +I<Instance method.> +Return the encoded body as a string. +This is the portion after the header and the blank line. + +I<Note:> actually prepares the body by "printing" to a scalar. +Proof that you can hand the C<print*()> methods any blessed object +that responds to a C<print()> message. + +=cut + +sub body_as_string { + my $self = shift; + my @buf; + my $io = (wrap MIME::Lite::IO_ScalarArray \@buf); + $self->print_body($io); + join '', @buf; +} +*stringify_body = \&body_as_string; ### backwards compatibility + +#------------------------------ +# +# fields_as_string FIELDS +# +# PRIVATE! Return a stringified version of the given header +# fields, where FIELDS is an arrayref like that returned by fields(). +# +sub fields_as_string { + my ($self, $fields) = @_; + my @lines; + foreach (@$fields) { + my ($tag, $value) = @$_; + next if ($value eq ''); ### skip empties + $tag =~ s/\b([a-z])/uc($1)/ge; ### make pretty + $tag =~ s/^mime-/MIME-/ig; ### even prettier + push @lines, "$tag: $value\n"; + } + join '', @lines; +} + +#------------------------------ + +=item header_as_string + +I<Instance method.> +Return the header as a string. + +=cut + +sub header_as_string { + my $self = shift; + $self->fields_as_string($self->fields); +} +*stringify_header = \&header_as_string; ### backwards compatibility + +=back + +=cut + + + +#============================== +#============================== + +=head2 Sending + +=over 4 + +=cut + +#------------------------------ + +=item send + +=item send HOW, HOWARGS... + +I<Class/instance method.> +This is the principal method for sending mail, and for configuring +how mail will be sent. + +I<As an instance method> (with no arguments), sends the message by whatever +means has been set up (the default is to use the Unix "sendmail" program). +Returns whatever the mail-handling routine returns: this should be true +on success, false/exception on error: + + $msg = MIME::Lite->new(From=>...); + $msg->send || die "you DON'T have mail!"; + +I<As a class method> (with a HOW argument and optional HOWARGS), sets up +how the instance method will work for all objects until further notice +It treats HOW as a facility name, with optional HOWARGS handled by +the facility (and returns the previous HOW and HOWARGS as an array). +There are three facilities: + +=over 4 + +=item "sendmail", ARGS... + +Send a message by piping it into the "sendmail" command. +Uses the L<send_by_sendmail()|/send_by_sendmail> method, giving it the ARGS. +This usage implements (and deprecates) the C<sendmail()> method. + +=item "smtp", [HOSTNAME] + +Send a message by SMTP, using optional HOSTNAME as SMTP-sending host. +Uses the L<send_by_smtp()|/send_by_smtp> method. + +=item "sub", \&SUBREF, ARGS... + +Sends a message MSG by invoking the subroutine SUBREF of your choosing, +with MSG as the first argument, and ARGS following. + +=back + +I<For example:> let's say you're on an OS which lacks the usual Unix +"sendmail" facility, but you've installed something a lot like it, and +you need to configure your Perl script to use this "sendmail.exe" program. +Do this following in your script's setup: + + MIME::Lite->send('sendmail', "d:\\programs\\sendmail.exe"); + +Then, whenever you need to send a message $msg, just say: + + $msg->send; + +That's it. Now, if you ever move your script to a Unix box, all you +need to do is change that line in the setup and you're done. +All of your $msg-E<gt>send invocations will work as expected. + +=cut + +sub send { + my $self = shift; + + if (ref($self)) { ### instance method: + my $method = "send_by_$Sender"; + my @args = @{$SenderArgs{$Sender} || []}; + $self->verify_data if $AUTO_VERIFY; ### prevents missing parts! + return $self->$method(@args); + } + else { ### class method: + my @old = ($Sender, @{$SenderArgs{$Sender}}); + $Sender = shift; + $SenderArgs{$Sender} = [@_]; ### remaining args + return @old; + } +} + +#------------------------------ + +=item send_by_sendmail SENDMAILCMD + +=item send_by_sendmail PARAM=>VALUE, ... + +I<Instance method.> +Send message via an external "sendmail" program +(this will probably only work out-of-the-box on Unix systems). + +Returns true on success, false or exception on error. + +You can specify the program and all its arguments by giving a single +string, SENDMAILCMD. Nothing fancy is done; the message is simply +piped in. + +However, if your needs are a little more advanced, you can specify +zero or more of the following PARAM/VALUE pairs; a Unix-style, +taint-safe "sendmail" command will be constructed for you: + +=over 4 + +=item Sendmail + +Full path to the program to use. +Default is "/usr/lib/sendmail". + +=item BaseArgs + +Ref to the basic array of arguments we start with. +Default is C<["-t", "-oi", "-oem"]>. + +=item SetSender + +Unless this is I<explicitly> given as false, we attempt to automatically +set the C<-f> argument to the first address that can be extracted from +the "From:" field of the message (if there is one). + +I<What is the -f, and why do we use it?> +Suppose we did I<not> use C<-f>, and you gave an explicit "From:" +field in your message: in this case, the sendmail "envelope" would +indicate the I<real> user your process was running under, as a way +of preventing mail forgery. Using the C<-f> switch causes the sender +to be set in the envelope as well. + +I<So when would I NOT want to use it?> +If sendmail doesn't regard you as a "trusted" user, it will permit +the C<-f> but also add an "X-Authentication-Warning" header to the message +to indicate a forged envelope. To avoid this, you can either +(1) have SetSender be false, or +(2) make yourself a trusted user by adding a C<T> configuration + command to your I<sendmail.cf> file + (e.g.: C<Teryq> if the script is running as user "eryq"). + +=item FromSender + +If defined, this is identical to setting SetSender to true, +except that instead of looking at the "From:" field we use +the address given by this option. +Thus: + + FromSender => 'me@myhost.com' + +=back + +=cut + +sub send_by_sendmail { + my $self = shift; + + if (@_ == 1) { ### Use the given command... + my $sendmailcmd = shift @_; + + ### Do it: + open SENDMAIL, "|$sendmailcmd" or croak "open |$sendmailcmd: $!\n"; + $self->print(\*SENDMAIL); + close SENDMAIL; + return (($? >> 8) ? undef : 1); + } + else { ### Build the command... + my %p = @_; + $p{Sendmail} ||= "/usr/lib/sendmail"; + + ### Start with the command and basic args: + my @cmd = ($p{Sendmail}, @{$p{BaseArgs} || ['-t', '-oi', '-oem']}); + + ### See if we are forcibly setting the sender: + $p{SetSender} = 1 if defined($p{FromSender}); + + ### Add the -f argument, unless we're explicitly told NOT to: + unless (exists($p{SetSender}) and !$p{SetSender}) { + my $from = $p{FromSender} || ($self->get('From'))[0]; + if ($from) { + my ($from_addr) = extract_addrs($from); + push @cmd, "-f$from_addr" if $from_addr; + } + } + + ### Open the command in a taint-safe fashion: + my $pid = open SENDMAIL, "|-"; + defined($pid) or die "open of pipe failed: $!\n"; + if (!$pid) { ### child + exec(@cmd) or die "can't exec $p{Sendmail}: $!\n"; + ### NOTREACHED + } + else { ### parent + $self->print(\*SENDMAIL); + close SENDMAIL || die "error closing $p{Sendmail}: $! (exit $?)\n"; + return 1; + } + } +} + +#------------------------------ + +=item send_by_smtp ARGS... + +I<Instance method.> +Send message via SMTP, using Net::SMTP. +The optional ARGS are sent into Net::SMTP::new(): usually, these are + + MAILHOST, OPTION=>VALUE, ... + +Note that the list of recipients is taken from the +"To", "Cc" and "Bcc" fields. + +Returns true on success, false or exception on error. + +=cut + +### Provided by Andrew McRae. Version 0.2 anm 09Sep97 +### Copyright 1997 Optimation New Zealand Ltd. +### May be modified/redistributed under the same terms as Perl. +# +sub send_by_smtp { + my ($self, @args) = @_; + + ### We need the "From:" and "To:" headers to pass to the SMTP mailer: + my $hdr = $self->fields(); + my $from = $self->get('From'); + my $to = $self->get('To'); + + ### Sanity check: + defined($to) or croak "send_by_smtp: missing 'To:' address\n"; + + ### Get the destinations as a simple array of addresses: + my @to_all = extract_addrs($to); + if ($AUTO_CC) { + foreach my $field (qw(Cc Bcc)) { + my $value = $self->get($field); + push @to_all, extract_addrs($value) if defined($value); + } + } + + ### Create SMTP client: + require Net::SMTP; + my $smtp = MIME::Lite::SMTP->new(@args) + or croak "Failed to connect to mail server: $!\n"; + $smtp->mail($from) + or croak "SMTP MAIL command failed: $!\n"; + $smtp->to(@to_all) + or croak "SMTP RCPT command failed: $!\n"; + $smtp->data() + or croak "SMTP DATA command failed: $!\n"; + + ### MIME::Lite can print() to anything with a print() method: + $self->print_for_smtp($smtp); + $smtp->dataend(); + $smtp->quit; + 1; +} + +#------------------------------ +# +# send_by_sub [\&SUBREF, [ARGS...]] +# +# I<Instance method, private.> +# Send the message via an anonymous subroutine. +# +sub send_by_sub { + my ($self, $subref, @args) = @_; + &$subref($self, @args); +} + +#------------------------------ + +=item sendmail COMMAND... + +I<Class method, DEPRECATED.> +Declare the sender to be "sendmail", and set up the "sendmail" command. +I<You should use send() instead.> + +=cut + +sub sendmail { + my $self = shift; + $self->send('sendmail', join(' ', @_)); +} + +=back + +=cut + + + +#============================== +#============================== + +=head2 Miscellaneous + +=over 4 + +=cut + +#------------------------------ + +=item quiet ONOFF + +I<Class method.> +Suppress/unsuppress all warnings coming from this module. + + MIME::Lite->quiet(1); ### I know what I'm doing + +I recommend that you include that comment as well. And while +you type it, say it out loud: if it doesn't feel right, then maybe +you should reconsider the whole line. C<;-)> + +=cut + +sub quiet { + my $class = shift; + $QUIET = shift if @_; + $QUIET; +} + +=back + +=cut + + + +#============================================================ + +package MIME::Lite::SMTP; + +#============================================================ +# This class just adds a print() method to Net::SMTP. +# Notice that we don't use/require it until it's needed! + +use strict; +use vars qw( @ISA ); +@ISA = qw(Net::SMTP); + +sub print { shift->datasend(@_) } + + + +#============================================================ + +package MIME::Lite::IO_Handle; + +#============================================================ + +### Wrap a non-object filehandle inside a blessed, printable interface: +### Does nothing if the given $fh is already a blessed object. +sub wrap { + my ($class, $fh) = @_; + no strict 'refs'; + + ### Get default, if necessary: + $fh or $fh = select; ### no filehandle means selected one + ref($fh) or $fh = \*$fh; ### scalar becomes a globref + + ### Stop right away if already a printable object: + return $fh if (ref($fh) and (ref($fh) ne 'GLOB')); + + ### Get and return a printable interface: + bless \$fh, $class; ### wrap it in a printable interface +} + +### Print: +sub print { + my $self = shift; + print {$$self} @_; +} + + +#============================================================ + +package MIME::Lite::IO_Scalar; + +#============================================================ + +### Wrap a scalar inside a blessed, printable interface: +sub wrap { + my ($class, $scalarref) = @_; + defined($scalarref) or $scalarref = \""; + bless $scalarref, $class; +} + +### Print: +sub print { + my $self = shift; + $$self .= join('', @_); + 1; +} + + +#============================================================ + +package MIME::Lite::IO_ScalarArray; + +#============================================================ + +### Wrap an array inside a blessed, printable interface: +sub wrap { + my ($class, $arrayref) = @_; + defined($arrayref) or $arrayref = []; + bless $arrayref, $class; +} + +### Print: +sub print { + my $self = shift; + push @$self, @_; + 1; +} + +1; +__END__ + + +#============================================================ + +=head1 NOTES + + +=head2 Benign limitations + +This is "lite", after all... + +=over 4 + +=item * + +There's no parsing. Get MIME-tools if you need to parse MIME messages. + +=item * + +MIME::Lite messages are currently I<not> interchangeable with +either Mail::Internet or MIME::Entity objects. This is a completely +separate module. + +=item * + +A content-length field is only inserted if the encoding is binary, +the message is a singlepart, and all the document data is available +at C<build()> time by virtue of residing in a simple path, or in-core. +Since content-length is not a standard MIME field anyway (that's right, kids: +it's not in the MIME RFCs, it's an HTTP thing), this seems pretty fair. + +=item * + +MIME::Lite alone cannot help you lose weight. You must supplement +your use of MIME::Lite with a healthy diet and exercise. + +=back + + +=head2 Cheap and easy mailing + +I thought putting in a default "sendmail" invocation wasn't too bad an +idea, since a lot of Perlers are on UNIX systems. +The out-of-the-box configuration is: + + MIME::Lite->send('sendmail', "/usr/lib/sendmail -t -oi -oem"); + +By the way, these arguments to sendmail are: + + -t Scan message for To:, Cc:, Bcc:, etc. + + -oi Do NOT treat a single "." on a line as a message terminator. + As in, "-oi vey, it truncated my message... why?!" + + -oem On error, mail back the message (I assume to the + appropriate address, given in the header). + When mail returns, circle is complete. Jai Guru Deva -oem. + +Note that these are the same arguments you get if you configure to use +the smarter, taint-safe mailing: + + MIME::Lite->send('sendmail'); + +If you get "X-Authentication-Warning" headers from this, you can forgo +diddling with the envelope by instead specifying: + + MIME::Lite->send('sendmail', SetSender=>0); + +And, if you're not on a Unix system, or if you'd just rather send mail +some other way, there's always: + + MIME::Lite->send('smtp', "smtp.myisp.net"); + +Or you can set up your own subroutine to call. +In any case, check out the L<send()|/send> method. + + + +=head1 WARNINGS + +=head2 Good-vs-bad email addresses with send_by_smtp() + +If using L<send_by_smtp()|/send_by_smtp>, be aware that you are +forcing MIME::Lite to extract email addresses out of a possible list +provided in the C<To:>, C<Cc:>, and C<Bcc:> fields. This is tricky +stuff, and as such only the following sorts of addresses will work +reliably: + + username + full.name@some.host.com + "Name, Full" <full.name@some.host.com> + +This last form is discouraged because SMTP must be able to get +at the I<name> or I<name@domain> portion. + +B<Disclaimer:> +MIME::Lite was never intended to be a Mail User Agent, so please +don't expect a full implementation of RFC-822. Restrict yourself to +the common forms of Internet addresses described herein, and you should +be fine. If this is not feasible, then consider using MIME::Lite +to I<prepare> your message only, and using Net::SMTP explicitly to +I<send> your message. + + +=head2 Formatting of headers delayed until print() + +This class treats a MIME header in the most abstract sense, +as being a collection of high-level attributes. The actual +RFC-822-style header fields are not constructed until it's time +to actually print the darn thing. + + +=head2 Encoding of data delayed until print() + +When you specify message bodies +(in L<build()|/build> or L<attach()|/attach>) -- +whether by B<FH>, B<Data>, or B<Path> -- be warned that we don't +attempt to open files, read filehandles, or encode the data until +L<print()|/print> is invoked. + +In the past, this created some confusion for users of sendmail +who gave the wrong path to an attachment body, since enough of +the print() would succeed to get the initial part of the message out. +Nowadays, $AUTO_VERIFY is used to spot-check the Paths given before +the mail facility is employed. A whisker slower, but tons safer. + +Note that if you give a message body via FH, and try to print() +a message twice, the second print() will not do the right thing +unless you explicitly rewind the filehandle. + +You can get past these difficulties by using the B<ReadNow> option, +provided that you have enough memory to handle your messages. + + +=head2 MIME attributes are separate from header fields! + +B<Important:> the MIME attributes are stored and manipulated separately +from the message header fields; when it comes time to print the +header out, I<any explicitly-given header fields override the ones that +would be created from the MIME attributes.> That means that this: + + ### DANGER ### DANGER ### DANGER ### DANGER ### DANGER ### + $msg->add("Content-type", "text/html; charset=US-ASCII"); + +will set the exact C<"Content-type"> field in the header I write, +I<regardless of what the actual MIME attributes are.> + +I<This feature is for experienced users only,> as an escape hatch in case +the code that normally formats MIME header fields isn't doing what +you need. And, like any escape hatch, it's got an alarm on it: +MIME::Lite will warn you if you attempt to C<set()> or C<replace()> +any MIME header field. Use C<attr()> instead. + + +=head2 Beware of lines consisting of a single dot + +Julian Haight noted that MIME::Lite allows you to compose messages +with lines in the body consisting of a single ".". +This is true: it should be completely harmless so long as "sendmail" +is used with the -oi option (see L<"Cheap and easy mailing">). + +However, I don't know if using Net::SMTP to transfer such a message +is equally safe. Feedback is welcomed. + +My perspective: I don't want to magically diddle with a user's +message unless absolutely positively necessary. +Some users may want to send files with "." alone on a line; +my well-meaning tinkering could seriously harm them. + + +=head2 Infinite loops may mean tainted data! + +Stefan Sautter noticed a bug in 2.106 where a m//gc match was +failing due to tainted data, leading to an infinite loop inside +MIME::Lite. + +I am attempting to correct for this, but be advised that my fix will +silently untaint the data (given the context in which the problem +occurs, this should be benign: I've labelled the source code with +UNTAINT comments for the curious). + +So: don't depend on taint-checking to save you from outputting +tainted data in a message. + + +=head1 A MIME PRIMER + +=head2 Content types + +The "Type" parameter of C<build()> is a I<content type>. +This is the actual type of data you are sending. +Generally this is a string of the form C<"majortype/minortype">. + +Here are the major MIME types. +A more-comprehensive listing may be found in RFC-2046. + +=over 4 + +=item application + +Data which does not fit in any of the other categories, particularly +data to be processed by some type of application program. +C<application/octet-stream>, C<application/gzip>, C<application/postscript>... + +=item audio + +Audio data. +C<audio/basic>... + +=item image + +Graphics data. +C<image/gif>, C<image/jpeg>... + +=item message + +A message, usually another mail or MIME message. +C<message/rfc822>... + +=item multipart + +A message containing other messages. +C<multipart/mixed>, C<multipart/alternative>... + +=item text + +Textual data, meant for humans to read. +C<text/plain>, C<text/html>... + +=item video + +Video or video+audio data. +C<video/mpeg>... + +=back + + +=head2 Content transfer encodings + +The "Encoding" parameter of C<build()>. +This is how the message body is packaged up for safe transit. + +Here are the 5 major MIME encodings. +A more-comprehensive listing may be found in RFC-2045. + +=over 4 + +=item 7bit + +Basically, no I<real> encoding is done. However, this label guarantees that no +8-bit characters are present, and that lines do not exceed 1000 characters +in length. + +=item 8bit + +Basically, no I<real> encoding is done. The message might contain 8-bit +characters, but this encoding guarantees that lines do not exceed 1000 +characters in length. + +=item binary + +No encoding is done at all. Message might contain 8-bit characters, +and lines might be longer than 1000 characters long. + +The most liberal, and the least likely to get through mail gateways. +Use sparingly, or (better yet) not at all. + +=item base64 + +Like "uuencode", but very well-defined. This is how you should send +essentially binary information (tar files, GIFs, JPEGs, etc.). + +=item quoted-printable + +Useful for encoding messages which are textual in nature, yet which contain +non-ASCII characters (e.g., Latin-1, Latin-2, or any other 8-bit alphabet). + +=back + + + +=head1 VERSION + +$Id: Lite.pm,v 2.108 2001/03/30 06:16:54 eryq Exp $ + + +=head1 CHANGE LOG + +=over 4 + + +=item Version 2.108 + +New C<field_order()> allows you to set the header order, both on a +per-message basis, and package-wide. +I<Thanks to Thomas Stromberg for suggesting this.> + +Added code to try and divine "sendmail" path more intelligently. +I<Thanks to Slaven Rezic for the suggestion.> + + +=item Version 2.107 (2001/03/27) + +Fixed serious bug where tainted data with quoted-printable encoding +was causing infinite loops. The "fix" untaints the data in question, +which is not optimal, but it's probably benign in this case. +I<Thanks to Stefan Sautter for tracking this nasty little beast down.> +I<Thanks to Larry Geralds for a related patch.> + + "Doctor, O doctor: + it's painful when I do *this* --" + "Simple: don't *do* that." + +Fixed bugs where a non-local C<$_> was being modified... again! +Will I never learn? +I<Thanks to Maarten Koskamp for reporting this.> + + Dollar-underscore + can poison distant waters; + 'local' must it be. + +Fixed buglet in C<add()> where all value references were being treated +as arrayrefs, instead of as possibly-self-stringifying object refs. +Now you can send in an object ref as the 2nd argument. +I<Thanks to dLux for the bug report.> + + That ref is a string? + Operator overload + has ruined my day. + +Added "Approved" as an acceptable header field for C<new()>, as per RFC1036. +I<Thanks to Thomax for the suggestion regarding MIME-tools.> + +Small improvements to docs to make different uses of attach() +and various arguments clearer. +I<Thanks to Sven Rassman and Roland Walter for the suggestions.> + + +=item Version 2.106 (2000/11/21) + +Added Alpha version of scrub() to make it easy for people to suppress +the printing of unwanted MIME attributes (like Content-length). +I<Thanks to the many people who asked for this.> + +Headers with empty-strings for their values are no longer +printed. This seems sensible, and helps us implement scrub(). + + +=item Version 2.105 (2000/10/14) + +The regression-test failure was identified, and it was my fault. +Apparently some of the \-quoting in my "autoloaded" code was +making Perl 5.6 unhappy. For this nesting-related idiocy, +a nesting kaiku. +I<Thanks to Scott Schwartz for identifying the problem.> + + In a pattern, my + backslash-s dwells peacefully, + unambiguous -- + + but I embed it + in a double-quoted string + doubling the backslash -- + + interpolating + that same double-quoted string + in other patterns -- + + and, worlds within worlds, + I single-quote the function + to autoload it -- + + changing the meaning + of the backslash and the 's'; + and Five-Point-Six growls. + + +=item Version 2.104 (2000/09/28) + +Now attempts to load and use Mail::Address for parsing email +addresses I<before> falling back to our own method. +I<Thanks to numerous people for suggesting this.> + + Parsing addresses + is too damn hard. One last hope: + Let Graham Barr do it! + +For the curious, the version of Mail::Address appears +as the "A" number in the X-Mailer: + + X-Mailer: MIME::Lite 2.104 (A1.15; B2.09; Q2.03) + +Added B<FromSender> option to send_by_sendmail(). +I<Thanks to Bill Moseley for suggesting this feature.> + + +=item Version 2.101 (2000/06/06) + +Major revision to print_body() and body_as_string() so that +"body" really means "the part after the header", which is what most +people would want in this context. This is B<not> how it was used +1.x, where "body" only meant "the body of a simple singlepart". +Hopefully, this change will solve many problems and create very few ones. + +Added support for attaching a part to a "message/rfc822", treating +the "message" type as a multipart-like container. + +Now takes care not to include "Bcc:" in header when using send_by_smtp, +as a safety precaution against qmail's behavior. +I<Thanks to Tatsuhiko Miyagawa for identifying this problem.> + +Improved efficiency of many stringifying operations by using +string-arrays which are joined, instead of doing multiple appends +to a scalar. + +Cleaned up the "examples" directory. + + +=item Version 1.147 (2000/06/02) + +Fixed buglet where lack of Cc:/Bcc: was causing extract_addrs +to emit "undefined variable" warnings. Also, lack of a "To:" field +now causes a croak. +I<Thanks to David Mitchell for the bug report and suggested patch.> + + +=item Version 1.146 (2000/05/18) + +Fixed bug in parsing of addresses; please read the WARNINGS section +which describes recommended address formats for "To:", "Cc:", etc. +Also added automatic inclusion of a UT "Date:" at top level unless +explicitly told not to. +I<Thanks to Andy Jacobs for the bug report and the suggestion.> + +=item Version 1.145 (2000/05/06) + +Fixed bug in encode_7bit(): a lingering C</e> modifier was removed. +I<Thanks to Michael A. Chase for the patch.> + + +=item Version 1.142 (2000/05/02) + +Added new, taint-safe invocation of "sendmail", one which also +sets up the C<-f> option. Unfortunately, I couldn't make this automatic: +the change could have broken a lot of code out there which used +send_by_sendmail() with unusual "sendmail" variants. +So you'll have to configure "send" to use the new mechanism: + + MIME::Lite->send('sendmail'); ### no args! + +I<Thanks to Jeremy Howard for suggesting these features.> + + +=item Version 1.140 (2000/04/27) + +Fixed bug in support for "To", "Cc", and "Bcc" in send_by_smtp(): +multiple (comma-separated) addresses should now work fine. +We try real hard to extract addresses from the flat text strings. +I<Thanks to John Mason for motivating this change.> + +Added automatic verification that attached data files exist, +done immediately before the "send" action is invoked. +To turn this off, set $MIME::Lite::AUTO_VERIFY to false. + +=item Version 1.137 (2000/03/22) + +Added support for "Cc" and "Bcc" in send_by_smtp(). +To turn this off, set $MIME::Lite::AUTO_CC to false. +I<Thanks to Lucas Maneos for the patch, and tons of others for +the suggestion.> + +Chooses a better default content-transfer-encoding if the content-type +is "image/*", "audio/*", etc. +To turn this off, set $MIME::Lite::AUTO_ENCODE to false. +I<Thanks to many folks for the suggestion.> + +Fixed bug in QP-encoding where a non-local C<$_> was being modified. +I<Thanks to Jochen Stenzel for finding this very obscure bug!> + +Removed references to C<$`>, C<$'>, and C<$&> (bad variables +which slow things down). + +Added an example of how to send HTML files with enclosed in-line +images, per popular demand. + + +=item Version 1.133 (1999/04/17) + +Fixed bug in "Data" handling: arrayrefs were not being handled +properly. + + +=item Version 1.130 (1998/12/14) + +Added much larger and more-flexible send() facility. +I<Thanks to Andrew McRae (and Optimation New Zealand Ltd) +for the Net::SMTP interface. Additional thanks to the many folks +who requested this feature.> + +Added get() method for extracting basic attributes. + +New... "t" tests! + + +=item Version 1.124 (1998/11/13) + +Folded in filehandle (FH) support in build/attach. +I<Thanks to Miko O'Sullivan for the code.> + + +=item Version 1.122 (1998/01/19) + +MIME::Base64 and MIME::QuotedPrint are used if available. + +The 7bit encoding no longer does "escapes"; it merely strips 8-bit characters. + + +=item Version 1.121 (1997/04/08) + +Filename attribute is now no longer ignored by build(). +I<Thanks to Ian Smith for finding and patching this bug.> + + +=item Version 1.120 (1997/03/29) + +Efficiency hack to speed up MIME::Lite::IO_Scalar. +I<Thanks to David Aspinwall for the patch.> + + +=item Version 1.116 (1997/03/19) + +Small bug in our private copy of encode_base64() was patched. +I<Thanks to Andreas Koenig for pointing this out.> + +New, prettier way of specifying mail message headers in C<build()>. + +New quiet method to turn off warnings. + +Changed "stringify" methods to more-standard "as_string" methods. + + +=item Version 1.112 (1997/03/06) + +Added C<read_now()>, and C<binmode()> method for our non-Unix-using brethren: +file data is now read using binmode() if appropriate. +I<Thanks to Xiangzhou Wang for pointing out this bug.> + + +=item Version 1.110 (1997/03/06) + +Fixed bug in opening the data filehandle. + + +=item Version 1.102 (1997/03/01) + +Initial release. + + +=item Version 1.101 (1997/03/01) + +Baseline code. + +=back + + +=head1 TERMS AND CONDITIONS + +Copyright (c) 1997 by Eryq. +Copyright (c) 1998 by ZeeGee Software Inc. +All rights reserved. This program is free software; you can redistribute +it and/or modify it under the same terms as Perl itself. + +This software comes with B<NO WARRANTY> of any kind. +See the COPYING file in the distribution for details. + + +=head1 NUTRITIONAL INFORMATION + +For some reason, the US FDA says that this is now required by law +on any products that bear the name "Lite"... + + MIME::Lite | + ------------------------------------------------------------ + Serving size: | 1 module + Servings per container: | 1 + Calories: | 0 + Fat: | 0g + Saturated Fat: | 0g + +Warning: for consumption by hardware only! May produce +indigestion in humans if taken internally. + + +=head1 AUTHOR + +Eryq (F<eryq@zeegee.com>). +President, ZeeGee Software Inc. (F<http://www.zeegee.com>). + +Created: 11 December 1996. Ho ho ho. + +=cut + |
