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.

                                Edit: just realized you beat me to this post, so you can simply ignore this since you allready have it working. Congrats ๐Ÿ™‚

                                MichiganJim;10988704 wrote:

                                Actually in this case I do need the whole path. (...) That's why I start with the full path for each file and that much is working.

                                The two cases are completely separate. Yes, you do need the full path to access the files on disk. After you retrieve the file data, it is this data you put in the email, and that is all that matters.

                                So no, you don't need the path or even the filename when you send the email. You really can put anything here, with one little exception.

                                Some email clients (iirc: incorrectly) use the name parameter of the content-type header field, while others use the filename parameter of the content-disposition header field for a suggested filename for the end user to save the file under.

                                However, some email clients incorrectly ignore the mime type specified by the content-type header field and only use the extension provided by the name attribute in that same header field. As such, you will need to provide the correct file extension for things to work properly with such email clients, but apart from that, you could set name and filename parameters to whatever you like.

                                Anyway, I'd like to suggest something that might make it easier for you to build correct email headers. Wrap the functionality in separate functions, and use these functions to do the work for you. That way, there is only one single place where you may screw up. In other words, your email headers will either all be correct when it comes to line breaks etc, or they will all be wrong. Presently some are ok, others are not, and you have to inspect all of them separately looking for inconsistencies.

                                /*
                                 * Each headerfield should, as seen in RFC 822, look like
                                 *		fieldname: fieldvalue; attributename=attributevalue
                                 * where the attribute name-value pair is optional, meaning it could look like
                                 *		fieldname: fieldvalue
                                 */
                                function createHeaderField($fname, $fvalue, $aname = null, $avalue = null)
                                {
                                	$fname = trim($fname);
                                	$fvalue = trim($fvalue);
                                	$aname = trim($aname);
                                	$avalue = trim($avalue);
                                
                                if (empty($fname) || empty($fvalue))
                                	return null;
                                if (empty($aname) || empty($avalue))
                                	return sprintf('%s: %s', $fname, $fvalue);
                                
                                return sprintf('%s: %s; %s="%s"', $fname, $fvalue, $aname, $avalue);
                                }
                                
                                /*
                                 * As seen in RFC 822, each headerfield should be terminated by CRLF ("\r\n" in PHP),
                                 * and the entire header should be terminated by CRLF.
                                 * So just implode an array with "\r\n", and add 2x "\r\n" after the generated string.
                                 */
                                function createHeader($headerFields)
                                {
                                	/* Remove any empty headers so you don't end up with "\r\n\r\n" in the middle
                                	 * of your headers, since that would tell the email client that the rest is part
                                	 * of the body
                                	 */
                                	foreach ($headerFields as &$h)
                                	{
                                		if (empty($h))
                                			unset($h);
                                	}
                                	return implode("\r\n", $headerFields) . "\r\n\r\n";
                                }
                                
                                function addPart($boundary, $headers, $body)
                                {
                                	return sprintf("\r\n--%s\r\n%s%s", $boundary, createHeader($headers), $body);
                                }
                                function endOfParts($boundary)
                                {
                                	return sprintf("\r\n--%s--\r\n", $boundary);
                                }
                                
                                # define a boundary
                                $boundary = '=__mp_boundary';
                                $mail = '';
                                
                                # reset the headers array for each new part sent
                                # this part just contains plain text
                                $headers = array();
                                $headers[] = createHeaderField('content-type', 'text/plain');
                                $body = 'first part';
                                $mail .= addPart($boundary, $headers, $body);
                                
                                # inline image
                                $headers = array();
                                $headers[] = createHeaderField('content-type', 'image/jpeg');
                                $headers[] = createHeaderField('content-disposition', 'inline');
                                $headers[] = createHeaderField('content-transfer-encoding', 'base64');
                                $img = file_get_contents('path/to/file.jpg');
                                $img = chunk_split(base64_encode($img));
                                $mail .= addPart($boundary, $headers, $img);
                                
                                # and some more text
                                $headers = array();
                                $headers[] = createHeaderField('content-type', 'text/plain');
                                $body = 'last part';
                                $mail .= addPart($boundary, $headers, $body);
                                
                                # needed after last part
                                $mail.= endOfParts($boundary);
                                
                                # Finally the headers for the email containing the parts
                                $headers = array();
                                $headers[] = createHeaderField('content-type', 'multipart/mixed', 'boundary', $boundary);
                                $headers[] = createHeaderField('from', 'me@example.com');
                                
                                $header = createHeaders($headers);
                                
                                # This is what your email looks like (in a browser)
                                printf('<pre>%s</pre>', $header.$mail);
                                # or if you inspect it in a terminal window, just
                                echo $header.$mail;
                                
                                # And the reason we build the mail headers last is that the mail() function expects
                                # the mail headers separate, while all multipart part headers are found in the 
                                # email body.
                                mail('you@example.com', 'multi multi parts', $mail, createHeader($headers));
                                

                                You can use this approach both for the email headers as well as for the headers of each part in a multipart message.

                                  just realized you beat me to this post, so you can simply ignore this since you allready have it working.

                                  I won't ignore it. I've saved it for reference in the near future when I have time to go back and add polish. For now, this particular process set me back by days and I need to get the overall project caught up. This was a key element.

                                  Congrats

                                  To you as well. I appreciate what you and others like you add to forums like this.

                                    MichiganJim;10988721 wrote:

                                    I converted all \r\n and \n to PHP_EOL.

                                    That's bad. The line ending used in SMTP communications is defined in an RFC (can't be bothered to go look it up right now - it might already have been mentioned in this thread). In other words, it's fixed at CRLF, or "\r\n" - this doesn't change no matter which SMTP server you're connecting to or from.

                                    The constant PHP_EOL, however, is not fixed. It varies from OS to OS (e.g. LF on *nix, CRLF on Windows, CR on (old?) Mac's, etc.). Notice the problem?

                                    SMTP doesn't care what your native line break is; if you've got a ("poor quality") MTA that's automatically rewriting all LF's to CRLF, well that to me seems broken (at least, it might be a nice feature... if it were consistent across all popular MTAs in use).

                                      The line ending used in SMTP communications is defined in an RFC (can't be bothered to go look it up right now - it might already have been mentioned in this thread). In other words, it's fixed at CRLF, or "\r\n" - this doesn't change no matter which SMTP server you're connecting to or from.

                                      Ouch! Just when I thought I had this completed. So now I need to track down which breaks you're referring to.

                                      If your problem has been solved, PLEASE click the RESOLVED LINK under "Thread Tools"

                                      I was actually looking for that last night. I knew there was something, somewhere....

                                        bradgrafelman;10988730 wrote:

                                        That's bad. The line ending used in SMTP communications is defined in an RFC

                                        RFC 822

                                        MichiganJim;10988759 wrote:

                                        Ouch! Just when I thought I had this completed. So now I need to track down which breaks you're referring to.

                                        Every single line ending. That is, wherever you intend a line to end, no matter what character(s) are used, you are supposed to use "\r\n". I havn't looked at your last code version, but I'm guessing you now use PHP_EOL everywhere, which means that PHP_EOL needs to be replaced by "\r\n".

                                        The only time you'd want to use "\n" (== PHP_EOL on unix systems) instead of \r\n is if your mail can't be sent when using \r\n. That is if your mail transfer agent, like the php manual for [man]mail[/man] states, converts all \n to \r\n, which means that it would turn \r\n into \r\r\n.

                                        MichiganJim;10988759 wrote:

                                        I was actually looking for that last night. I knew there was something, somewhere....

                                        Just above the topmost post is a menu called "Thread Tools" which contains "mark as resolved".

                                          Write a Reply...