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.