After a lot of effort and excellent help here, I have a script working to send email with attachments. The last hurdle seems to be in getting the encoding right.

My script below results in text versus the actual attachment. I've looked it over and over but can't see why the chunk_split(base64_encode($data)) isn't working for me.

<?php
$to = 'Joe@Joe.com'; 
$from = 'Jim@Jim.com'; 
$sub = 'Test'; 
$filelist = '/Volumes/server/sites/thesite/some.jpg,/Volumes/server/sites/thesite/some.pdf';
$files = explode(",",$filelist);
$msg = lvMessage; 
$headers = "From: $from"; 

$semi_rand = md5(time()); 
$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x"; 

// headers for attachment 
$headers .= "\nMIME-Version: 1.0\n" . "Content-Type: multipart/mixed;\n" . " boundary=\"{$mime_boundary}\""; 

// multipart boundary 
$msg = "This is a multi-part message in MIME format.\n\n" . "โ€“{$mime_boundary}\n" . "Content-Type: text/plain; charset=\"iso-8859-1\"\n" . "Content-Transfer-Encoding: 7bit\n\n" . $msg . "\n\n"; 

// attachments code starts 
for($x=0;$x<count($files);$x++) 
{ 
$msg .= "โ€“{$mime_boundary}\n"; 
$file = fopen($files[$x],"rb"); 
$data = fread($file,filesize($files[$x])); 
fclose($file); 
$data = chunk_split(base64_encode($data)); 
$msg .= "Content-Type: {\"application/octet-stream\"};\n" . " name=\"$files[$x]\"\n" . 
"Content-Disposition: attachment;\n" . " filename=\"$files[$x]\"\n" . 
"Content-Transfer-Encoding: base64\n\n" . $data . "\n\n"; 
} 
$msg .= "โ€“{$mime_boundary}โ€“\n"; 
$res = @mail($to, $sub, $msg, $headers); 

?>

    You have several errors in your mail format.

    1. Headers are separated by CRLF, i.e. \r\n, not \n.

    2. Headers are separated from body by 2 CRLF, i.e. \r\n\r\n.

    The boundary speciffied in

    content-type: multipart/mixed boundary="yourboundary"
    

    should appear between each part on it's own line, i.e. be surrounded by "\r\n" on both sides, prepended with two - (0x2d), not one &#8211; (0xe2).

    After all parts, the boundary should appear on its own line prepended by 2 - and appended with 2 -.

      20 days later

      johanafm: I've been trying to send you a private message about getting help from you. I can't seem to manage it from your profile page, can you get one to me?

        The forum works fine as far as I'm concerned, and a private message requesting help most likely wouldn't result in any response. Also, you can't really count on me reading any private messages at all, at least not in any decent amount of time since I'm likely to not notice that I have any. Also, I come here to check the forum and possibly give help if I feel like it, meaning I'd first read the forum and after that I'm likely to just leave.

        But, since the forum seems to be working perfectly, what's up?

          I've been trying to incorporate the changes you suggested, but somewhere I'm just failing to make it work. I'm not sure if I've over done it with the \r\n\ and I completely failed to understand the "(0x2d), not one &#8211; (0xe2)"

          This is the only place I've gotten totally hung up with the PHP. Since I'm somewhat desperate to get this resolved in order to complete my project - and I feel guilty to ask for help - I felt compelled to offer something in return for your assistance to get this to work.

            MichiganJim;10988397 wrote:

            I've been trying to incorporate the changes you suggested, but somewhere I'm just failing to make it work. I'm not sure if I've over done it with the \r\n\

            Then repost the code you have and we'll find out ๐Ÿ™‚

            MichiganJim;10988397 wrote:

            and I completely failed to understand the "(0x2d), not one โ€“ (0xe2)"
            [/code]
            The 0x2d and 0xe2 references was, ironically, just added for clarification, in case the difference between the two characters was not graphically obvious. Those 2 values are the hex values of those characters, i.e.

            echo chr(0x2d);	# prints -
            echo ord('-');	# prints 0x2d
            

            The important thing is that you use the correct character, as they are no more the same as - and any other character, just slightly more visually close.

            MichiganJim;10988397 wrote:

            I feel guilty to ask for help - I felt compelled to offer something in return for your assistance to get this to work.

            Don't! Neither feel guilty for asking for help, and don't offer anything either. I'm here by my own free will, and I am certainly capable of deciding to help someone or not. Also, not asking for help, in a place like this, when you get stuck would most likely mean remaining stuck. Or taking ages to solve something which you'd learn a lot quicker here. And others having a similar problem may also benefit in the future.

            So once again, code repost, and we'll have a second go at it.

              Thanks for your response. Until trying this particular script I was progressing with PHP pretty well. This one has had me bogged down for a while.

              Below is where I'm at and still getting the attachments as text gibberish. I've added comments to note where I made changes per my best understanding of your comments - though there have been several other iterations.

              <?php 
              $to = 'Joe@Joe.com'; 
              $from = 'Jim@Jim.com'; 
              $sub = 'Test'; 
              $filelist = '/Volumes/server/sites/thesite/some.jpg,/Volumes/server/sites/thesite/some.pdf'; 
              $files = explode(",",$filelist); 
              $msg = lvMessage; 
              $headers = "From: $from"; 
              
              $semi_rand = md5(time()); 
              $mime_boundary = "==Multipart_Boundary_x{$semi_rand}x"; 
              
              // headers for attachment 
              $headers .= "\nMIME-Version: 1.0\r\n" . "Content-Type: multipart/mixed;\r\n" . " boundary=\"{$mime_boundary}\"\r\n\r\n";
              //  \r\n added between the headers and two sets on the end to separate it from the body
              
              // multipart boundary 
              $msg = "This is a multi-part message in MIME format.\n\n" . "โ€“{$mime_boundary}\n" . "Content-Type: text/plain; charset=\"iso-8859-1\"\n" . "Content-Transfer-Encoding: 7bit\n\n" . $msg . "\n\n";
              
              // attachments code starts 
              for($x=0;$x<count($files);$x++) 
              { 
              $msg .= "\r\nโ€“{$mime_boundary}\r\n"; 
              // \r\n added to both ends of the boundary
              
              $file = fopen($files[$x],"rb"); 
              $data = fread($file,filesize($files[$x])); 
              fclose($file); 
              $data = chunk_split(base64_encode($data)); 
              $msg .= "Content-Type: {\"application/octet-stream\"};\n" . " name=\"$files[$x]\"\n" . 
              "Content-Disposition: attachment;\n" . " filename=\"$files[$x]\"\n" . 
              "Content-Transfer-Encoding: base64\n\n" . $data . "\n\n"; 
              $msg .= "\r\nโ€“{$mime_boundary}โ€“\r\n"; 
              // \r\n added to both ends of the boundary and the ending boundary moved inside the bracket below to follow each part
              } 
              
              $res = @mail($to, $sub, $msg, $headers); 
              
              ?>

                Summary: Terminating header lines with \r\n is NOT optional, it is really important. The same goes for preceeding each boundary line with CRLF and starting the line with -- and also ending each such line with CRLF. Each part of a multipart message has to follow RFC822, except that it doesn't have to contain headers. The specifications exists for a reason, and if you do not follow them, things will not work.

                Every line that goes into the mail headers, in your code represented by the variable $headers, should be terminated by CRLF (carriage return, line feed - or expressed as hex values 0xA, 0xD - or expressed as escape sequences \r\n), as per the RFC 822 spec.
                Moreover, the headers should be separated from the body by \r\n, which means that the last line of the headers should be terminated by \r\n\r\n.

                Also note that in PHP, there is a difference between single quoted strings and double quoted strings for some sequences of characters, including \r and \n. This is not a misstake you made, but I just wanted to point it out in case you didn't know this.

                "\r";		# carriage return
                "\n";		# line feed
                '\r';		# backslash followed by r character
                '\n';		# backslash followed by n character
                
                # Here you do not terminate the line with \r\n
                $headers = "From: $from"; 
                

                Once again, you do not stick to the CRLF to end lines in the header. Do note that AFTER a boundary, which indicates the start of a new part in the multipart message, you must either have headers for the new part, or CRLF.
                RFC822 (before multipart was introduced) stated that

                A message consists of header fields and, optionally, a body.
                The body is simply a sequence of lines containing ASCII charac-
                ters. It is separated from the headers by a null line (i.e., a
                line with nothing preceding the CRLF).

                And from RFC 1341

                Thus, a typical multipart Content-Type header field might look like this:

                 Content-Type: multipart/mixed; 
                      boundary=gc0p4Jq0M2Yt08jU534c0p

                This indicates that the entity consists of several parts, each itself with a structure that is syntactically identical to an RFC 822 message, except that the header area might be completely empty, and that the parts are each preceded by the line

                 --gc0p4Jq0M2Yt08jU534c0p

                Note the "identical to RFC 822 message", except that the header area might be empty. Well, you still need a null line before the body. Also, somewhere else it's written that the boundary between parts (not in the conte-type header field of the containing email) must start on a new line, so you must always always have

                CRLF--boundaryCRLF
                

                Thus, the CRLF just infront of the --boundary is considered part of the boundary. If you want to end the previous part with a CRLF, you have to have two of them,

                body of preceeding messageCRLF
                CRLF--boundary
                

                Anyway, you still do not follow the requirements of CRLF ending each line of headers.

                # First line is fine, it's the last line of the previous part, and thus NOT a header.
                # Do note that otherwise the \n\n is NOT the same as \r\n.
                $msg = "This is a multi-part message in MIME format.\n\n" . 
                /* This is intended to be a boundary to indicate the next part, but it is not!
                	And if it was, it must either be followed by CRLF+headers or 2*CRLF,
                	which it is not. It is followed by \n + headers, and those headers are NOT
                	followed by CRLF, but LFLF.
                */
                "โ€“{$mime_boundary}\n" . "Content-Type: text/plain; charset=\"iso-8859-1\"\n" . "Content-Transfer-Encoding: 7bit\n\n" . $msg . "\n\n";
                

                Now let's have a look at why you are NOT indicating a boundary between parts, once again by looking at the RFC1341 spec

                The Content-Type field for multipart entities requires one parameter, "boundary", which is used to specify the encapsulation boundary. The encapsulation boundary is defined as a line consisting entirely of two hyphen characters ("-", decimal code 45) followed by the boundary parameter value from the Content-Type header field.

                You did specify that the email is a multipart message and you did specify the boundary

                $headers .= "\nMIME-Version: 1.0\r\n" . "Content-Type: multipart/mixed;\r\n" . " boundary=\"{$mime_boundary}\"\r\n\r\n";
                

                So far, so good. But now look at the middle sentence

                The encapsulation boundary is defined as a line consisting entirely of two hyphen characters ("-", decimal code 45)

                And compare that to

                /* This line is part of the body of the previous part
                $msg = "This is a multi-part message in MIME format.\n\n" .
                /* but your mime boundary isn't preceeded by CRLF, nor does the line
                	start with --, nor is it terminated by CRLF
                */
                "โ€“{$mime_boundary}\n"
                

                The encapsulation boundary has to be preceeded by \r\n, but it isn't. The encapsulation boundary has to be indicated by two - characters, but it is instead indicated by one occurence of an entirely different character. Just because the character used here is more graphically similar than a +, * or Q doesn't mean you can just use it as you please.

                And then you once again, here and there, fail to terminate lines with CRLF and use \n or \n\n

                $msg .= "Content-Type: {\"application/octet-stream\"};\n" . " 
                

                Hint: there are more lines than this to deal with.

                  Obviously I need some time to review and digest this but THANKS. I certainly recognize that you've given me some valuable information to work with. I'm not afraid of work as long as I know that I'm heading in the right direction.

                    Do I feel humiliated or what? I've crawled everything I can find about my line breaks and I'm sure they're good at this point. I've even read up on PHP_EOL (haven't tried it yet), but I still haven't gotten past my original problem.

                    The email arrives just fine and includes the right number of attachments but each attached file is nothing but lines and lines of text like: "b2RlUGFybXM8PC9Db2x1bW5zIDUvUHJlZGljdG9yIDEyPj4vU2l6"

                    Am I barking up the wrong tree to keep looking at the line breaks? It would seem that it's something in the file type or other.

                    My headers are cleaned up:

                    // headers for attachment 
                    $headers = "From: $from\r\n" .
                    "MIME-Version: 1.0\r\n" .
                    "Content-Type: multipart/mixed;\r\n" .
                    " boundary=\"{$mime_boundary}\""; 

                    as are my boundaries:

                    // multipart boundary 
                    $msg = "This is a multi-part msg in MIME format.\n\n" .
                    "--{$mime_boundary}\n" .
                    "Content-Type: text/plain; charset=\"iso-8859-1\"\n" .
                    "Content-Transfer-Encoding: 7bit\n\n" .
                    $msg . "\n\n";

                    So I'm inclined to think the issue is somewhere in the attachment code though I can't find it:

                    // attachments code starts 
                    for($x=0;$x<count($_files);$x++) 
                    { 
                    $msg .= "\r\nโ€“{$mime_boundary}\r\n"; 
                    $file = fopen($_files[$x],"rb"); 
                    $data = fread($file,filesize($_files[$x])); 
                    $type = $file['type'];
                    fclose($file); 
                    $data = chunk_split(base64_encode($data)); 
                    $msg .= "Content-Type: {\"application/octet-stream\"};\n" . " name=\"$_files[$x]\"\n" . 
                    "Content-Disposition: attachment;\n" . " filename=\"$_files[$x]\"\n" . 
                    "Content-Transfer-Encoding: base64\n\n" . $data . "\n\n"; 
                    } 
                    
                    $msg.="--{$mime_boundary}--\n";

                      All line breaks should be CRLF (carriage return followed by a line feed). You do that just fine in the initial headers, but suddenly you completely forget about the carriage return in the body/attachment sections and only use line feed characters. Why?

                        Why?

                        Well, looking at every example I can find on the web, most use \n and they're followed by responses about how great it all works. I've tried to follow the advice of johanafm and carefully converted those that seemed to need it to \r\n. I didn't understand that every break should be \r\n.

                        BUT, making that change just now still results in the attachments being a string of text. Sigh....

                          MichiganJim;10988620 wrote:

                          Well, looking at every example I can find on the web, most use \n and they're followed by responses about how great it all works.

                          Sounds like you're looking at all the wrong examples. For example, take the PHP manual itself on the [man]mail/man page:

                          PHP Manual wrote:

                          Multiple extra headers should be separated with a CRLF (\r\n).

                          as well as:

                          PHP Manual wrote:

                          If messages are not received, try using a LF (\n) only. Some poor quality Unix mail transfer agents replace LF by CRLF automatically (which leads to doubling CR if CRLF is used). This should be a last resort, as it does not comply with ยป RFC 2822.

                          MichiganJim;10988620 wrote:

                          BUT, making that change just now still results in the attachments being a string of text. Sigh....

                          In that case, perhaps there is a separate issue as well. Can you re-post your current code now that you've modified it?

                            Here's the script as I have it now. The initial values are actually being passed from a database and are working fine. The ending echo is also working and feeding into a message about success or failure. That too is working.

                            As I wrote earlier, the whole thing is actually working in every way except for either proper encoding or decoding of the attachments.

                            And as you noted, I'm probably looking at the wrong examples. With johanafm's advice in hand I was able to toss a lot of them and yes, there are a lot. And since to \n or not to \n seems to be where they all vary the most I was attracted to the idea of PHP_EOL but chose to leave that alone until I get this working. In act I plan to add a file_exists( to the loop to reduce potential errors. First I just need to get it working though.

                            <?php 
                            
                            $to = 'Joe@Joe.com'; 
                            $from = 'Jim@Jim.com'; 
                            $subject = 'Test'; 
                            $message = 'Some Words Here'; 
                            $filelist = '/Volumes/server/sites/thesite/some.jpg,/Volumes/server/sites/thesite/some.pdf';
                            $_files = explode(",",$filelist);
                            
                            $random_hash = md5(time()); 
                            $mime_boundary="==Multipart_Boundary_x{$random_hash}x";
                            
                            // headers for attachment 
                            $headers = "From: $from\r\n" .
                            "MIME-Version: 1.0\r\n" .
                            "Content-Type: multipart/mixed;\r\n" .
                            " boundary=\"{$mime_boundary}\""; 
                            
                            // multipart boundary 
                            $message = "This is a multi-part message in MIME format.\r\n" .
                            "-{$mime_boundary}\r\n" .
                            "Content-Type: text/plain; charset=\"iso-8859-1\"\r\n" .
                            "Content-Transfer-Encoding: 7bit\r\n" .
                            $message . "\r\n";
                            
                            // attachments code starts 
                            for($x=0;$x<count($_files);$x++) 
                            { 
                            $message .= "\r\nโ€“{$mime_boundary}\r\n"; 
                            $file = fopen($_files[$x],"rb"); 
                            $data = fread($file,filesize($_files[$x])); 
                            $type = $file['type'];
                            fclose($file); 
                            $data = chunk_split(base64_encode($data)); 
                            $message .= "Content-Type: {\"application/octet-stream\"};\r\n" . " name=\"$_files[$x]\"\r\n" . 
                            "Content-Disposition: attachment;\r\n" . " filename=\"$_files[$x]\"\r\n" . 
                            "Content-Transfer-Encoding: base64\r\n" . $data . "\r\n"; 
                            } 
                            
                            $message.="--{$mime_boundary}--\r\n";
                            
                            $res = @mail($to, $subject, $message, $headers); 
                            
                            if($res) 
                            { 
                            echo " was sent with the following attachments: <br />"; 
                            } 
                            else 
                            { 
                            echo " was not sent. An error occurred and the submission was not completed."; 
                            }
                            
                            ?>

                              Ahh! I just discovered A problem but it's not THE problem. Out of necessity I'm supplying the full path to the file on the server. The attachment is being sent with the whole path as it's name.

                              So my task is to find out how to extract the File Name in PHP and assign that as the file name.

                              One step closer - I suppose.

                                Please note that I've amended the code to handle the file name properly by extracting it from the full path using basename(.

                                $message .= "Content-Type: {\"application/octet-stream\"};\r\n" . " name=\"".basename($fname)."\"\r\n" . 
                                "Content-Disposition: attachment;\r\n" . " filename=\"".basename($fname)."\"\r\n". 
                                "Content-Transfer-Encoding: base64\r\n" . $data . "\r\n"; 
                                

                                The decoding failure persists.

                                  A thought - and question....

                                  In the script there are two places that have curly brackets on application/octet-stream and it comes through in the email that way. Should they be there? Removing them doesn't appear to change a thing, so why are they there in the first place and what is their influence?

                                  $message .= "Content-Type: {\"application/octet-stream\"}

                                  So ends the second morning til night effort on correcting this issue.

                                    MichiganJim;10988646 wrote:

                                    Ahh! I just discovered A problem but it's not THE problem. Out of necessity I'm supplying the full path to the file on the server. The attachment is being sent with the whole path as it's name.

                                    which has nothing to do with the data being sent at all. Do note that

                                    $filename = 'path/fo/file';
                                    $fh = fopen($filename);
                                    
                                    # $data contains the data
                                    $data = fread($filename);
                                    
                                    # This does no longer matter the least bit - you have allready read the data!
                                    $filename ='abcdefjklf';
                                    
                                    # Why? The filename part of the header no longer has anything to do with the
                                    # data being sent, since the data has allready been read. So, what does the
                                    # "filename" part do? It supplies the mail receiving application with a suggestion
                                    # of a filename to use if the user wishes to save the file.
                                    $header .= "Content-Disposition: attachment; filename=\"$_files[$x]\"\r\n";
                                    
                                    MichiganJim;10988648 wrote:

                                    Please note that I've amended the code to handle the file name properly

                                    Acutally you havn't. Well, there is of course no use of supplying a path to a user which most likely will not have that path on his system, so you really should supply only a file name. However, you do not handle the filename part properly (when it comes to following the RFCs, which is all that matters).

                                    The ony question you need to answer is: "How are the headers I need to use supposed to look?". Once you have that answer, all you need to do is follow them. Just like with the line endings (CRLF) you really do have to follow them, exactly, not almost. You also have to always follow them, not just most of the time. And until you do, anything can happen: email not arriving where it should, data not being handled like it should or anything else.

                                    This means that you have to be able to understand the applicable RFC(s). Look at this part of RFC 822

                                    3.2. HEADER FIELD DEFINITIONS
                                    These rules show a field meta-syntax, without regard for the particular type or internal syntax. Their purpose is to permit detection of fields; also, they present to higher-level parsers an image of each field as fitting on one line.

                                    field = field-name ":" [ field-body ] CRLF

                                    It goes on to define field-name, field-body and CRLF. But what this little part tells you is that each header field has a field-name followed by :, then comes the field body and the entire header field is terminated by CRLF. Other parts tells you that this is followed by either a new heder field, or by CRLF (optionally followed by body).

                                    If you then look at your code

                                    "Content-Type: {\"application/octet-stream\"};\r\n" . " name=\"".basename($fname)."\"\r\n"
                                    

                                    it becomes evident that this part involves two header fields: "Content-Type" and "name" (since each header field is separated by CRLF). However, as there exist no name headerfield, it will have no impact on a receiving mail client. The exact same thing goes for the next four header fields "Content-Disposition", "filename", "Content-Transfer-Encoding" and the information contained in your $data variable. Also note that the information in $data most likely breaks RFC 822, which tells you that the field-name should end with :, which the contents of $data most likely does not. Also note that "filename" is not supposed to be a separate header field, but belongs in the parameter part of another header field, which means that there should be no CRLF before it, and that $data is not supposed to appear in the header at all, which means there should be 2 CRLFs preceeding it.

                                    When it comes to each particular header field, you have to look at their definitions and then follow them. If you google "rfc content-type header" the first hit should be RFC 2045 which tells you

                                    This initial document specifies the various headers used to describe
                                    the structure of MIME messages. The second document, RFC 2046,
                                    defines the general structure of the MIME media typing system and
                                    defines an initial set of media types. The third document, RFC 2047,
                                    describes extensions to RFC 822 to allow non-US-ASCII text data

                                    So, looking at 5.1 of RFC 2045

                                    5.1. Syntax of the Content-Type Header Field

                                    In the Augmented BNF notation of RFC 822, a Content-Type header field
                                    value is defined as follows:

                                     content := "Content-Type" ":" type "/" subtype
                                                *(";" parameter)
                                                ; Matching of media type and subtype
                                                ; is ALWAYS case-insensitive.
                                    
                                     type := discrete-type / composite-type
                                    
                                     discrete-type := "text" / "image" / "audio" / "video" /
                                                      "application" / extension-token
                                    
                                     composite-type := "message" / "multipart" / extension-token
                                    
                                     extension-token := ietf-token / x-token
                                    
                                     ietf-token := <An extension token defined by a
                                                    standards-track RFC and registered
                                                    with IANA.>
                                    
                                     x-token := <The two characters "X-" or "x-" followed, with
                                                 no intervening white space, by any token>
                                    
                                     subtype := extension-token / iana-token
                                    
                                     iana-token := <A publicly-defined extension token. Tokens
                                                    of this form must be registered with IANA
                                                    as specified in RFC 2048.>
                                    
                                     parameter := attribute "=" value
                                    
                                     attribute := token
                                                  ; Matching of attributes
                                                  ; is ALWAYS case-insensitive.
                                    
                                     value := token / quoted-string
                                    
                                     token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
                                                 or tspecials>
                                    
                                     tspecials :=  "(" / ")" / "<" / ">" / "@" /
                                                   "," / ";" / ":" / "\" / <">
                                                   "/" / "[" / "]" / "?" / "="
                                                   ; Must be in quoted-string,
                                                   ; to use within parameter values

                                    gives you the spec for what the content-type header should look like, and it even contains two (equivalent) examples

                                    Content-type: text/plain; charset=us-ascii (Plain text)

                                    Content-type: text/plain; charset="us-ascii"

                                    In these exampes, "text" refers to a media type, and plain refers to a media subtype. These types have to be defined (at the very least by the sending and receiving applications). Do note that it's not {text/plain} or "text/plain", it's text/plain, which means that you should not use {"application/octet-stream"}, but application/octet-stream. There should, as seen before as well, be no CRLF after the subtype, just a ; followed by the parameter (attribute-value pair) name="filename"

                                    $message .= "Content-Type: {\"application/octet-stream\"};\r\n" . " name=\"".basename($fname)."\"\r\n" . 
                                    "Content-Disposition: attachment;\r\n" . " filename=\"".basename($fname)."\"\r\n". 
                                    "Content-Transfer-Encoding: base64\r\n" . $data . "\r\n"; 
                                    
                                    MichiganJim;10988646 wrote:

                                    The decoding failure persists.

                                    and will continue to do so until you follow the specs. The above information should enable you to fix another couple of things, and using the RFCs I've referred to and/or google, wikipedia etc you should be able to find the information you need.

                                      Thank you once again for your effort and patience. I've printed your post and am reading it over and over along with the references you provided. I do want to understand this and in spite of my frustration I'm learning a lot more about PHP. I've used PHP for simpler tasks; simple email, uploads, includes and such, but never had to do something quite this involved with it.

                                      there is of course no use of supplying a path to a user which most likely will not have that path on his system, so you really should supply only a file name.

                                      Actually in this case I do need the whole path. Teachers are checking off selections in a form which may be pdf, jpg, doc or txt that are located on the web server and are almost always in a variety of folders. Their selection then gets emailed to an email address they provide in the form. That's why I start with the full path for each file and that much is working. My database is passing a variable bearing the file list to PHP and it's all been right on the money.

                                      But now I'm back to absorbing your info on headers and trying again to get my breaks right.

                                        Success!!

                                        The script is below, but here's what I've done with the help received here and after crawling email with attachments in their raw format.

                                        I converted all \r\n and \n to PHP_EOL. This was big since my web server is running UNIX. On UNIX, they interpret differently than on Windows. PHP_EOL automatically adjusts. The data itself always showed a double return. That was actually the same issue but brought on by the chunk_split that defaults to Windows format, so I used the optional parameters to correct that with length of chunks and the ending of PHP_EOL for each chunk.

                                        There were many minor tweaks too, correcting quotes within quotes that were mismatched, adding a dash, etc. Misplaced line breaks were also an issue as I was told here.

                                        I would like to pick up the file type, but it works flawlessly without it, so that's just blind ambition.

                                        [FONT="Arial"]This script will send one, many or none in the way of attachments and includes a text message in the body. It uses complete file paths since it was written to run on the server and send attachments already located on the server. It will run on Windows or UNIX.[/FONT]

                                        It's built for use with a database that actually loads the opening set of variables including the list of files.

                                        <?php 
                                        
                                        $to = 'Joe@Joe.com'; 
                                        $from = 'Jim@Jim.com'; 
                                        $subject = 'Test'; 
                                        $message = 'Some Words Here'; 
                                        $filelist = '/Volumes/server/sites/thesite/some.jpg,/Volumes/server/sites/thesite/some.pdf'; 
                                        $_files = explode(",",$filelist); 
                                        
                                        $random_hash = md5(time()); 
                                        $mime_boundary="Multipart_Boundary_x{$random_hash}x";
                                        $eol = PHP_EOL;
                                        
                                        // headers for attachment 
                                        $headers .= "From: ".$from.$eol .
                                        "Mime-Version: 1.0".$eol .
                                        "Content-Type: multipart/mixed;".$eol.
                                        " boundary=\"".$mime_boundary."\""; 
                                        
                                        // multipart boundary 
                                        $message ="--".$mime_boundary.$eol .
                                        "Content-Type: text/plain; charset=\"iso-8859-1\"".$eol .
                                        "Content-Transfer-Encoding: 7bit".$eol.$eol.
                                        $message.$eol;
                                        $message.="--".$mime_boundary.$eol;
                                        
                                        // attachments code starts 
                                        for($x=0;$x<count($_files);$x++) 
                                        { 
                                         if (file_exists($_files[$x])){
                                         $file = fopen($_files[$x],"rb"); 
                                         $data = fread($file,filesize($_files[$x])); 
                                         fclose($file); 
                                         $type = $_files[$x]['type'];
                                         $fname = basename($_files[$x]);
                                         $data = chunk_split(base64_encode($data),76,$eol); 
                                         //$message .= "Content-Type: ".$type.";".$eol." name=\"".basename($fname)."\"".$eol. 
                                         $message .= "Content-Type: application/octet-stream;".$eol." name=\"".basename($fname)."\"".$eol. 
                                         "Content-Disposition: attachment;".$eol." filename=\"".basename($fname)."\"".$eol.
                                         "Content-Transfer-Encoding: base64".$eol.$eol.$data.$eol; 
                                         $message.="--".$mime_boundary.$eol;
                                         }
                                        } 
                                        
                                        $res = @mail($to, $subject, $message, $headers); 
                                        
                                        if($res) 
                                        { 
                                        echo " was sent with the following attachments: <br />"; 
                                        } 
                                        else 
                                        { 
                                        echo " was not sent. An error occured and the submisison was not completed."; 
                                        } 
                                        
                                        ?>
                                        

                                        My thanks to johanafm and bradgrafelman for the help. It got me down the right trail but wow!, what a hike. Rather than simply correct my errors for me, you pointed me in the right direction but made me figure it out - that's good. I really know what this script is doing now.