in reply to Re^2: Using Net::SMTP to send email attachments
in thread Using Net::SMTP to send email attachments

As an update, I am finding that in different email clients, the text part of the email is also coming through as an attachment. So, from having no attachments I now have two attachments! It is probably a boundary problem, but I do not seem to be able to determine just where the problem is.

  • Comment on Re^3: Using Net::SMTP to send email attachments

Replies are listed 'Best First'.
Re^4: Using Net::SMTP to send email attachments
by shmem (Chancellor) on May 01, 2017 at 13:29 UTC

    The second attachment is empty, right? Remove the last boundary. It is followed by nothing, and thus produces an empty attachment. update: Instead, output the boundary with "--" (two hyphens) attached, as per afoken's advice below.

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      The second attachment is empty, right? Remove the last boundary. It is followed by nothing, and thus produces an empty attachment.

      Wrong way. When avoiding perfectly working modules, one should at least read the relevant RFCs, in this case RFC 2046. It clearly states:

      The boundary delimiter line following the last body part is a distinguished delimiter that indicates that no further body parts will follow. Such a delimiter line is identical to the previous delimiter lines, with the addition of two more hyphens after the boundary parameter value.

      (Chapter 5.1.1. "Common Syntax", page 20)

      So, the code line must be changed from

      $smtp->datasend("--$boundary\n");

      to

      $smtp->datasend("--$boundary--\n");

      Also, the boundary string should not appear anywhere else in the mail body. RFC 2046 states:

      NOTE: Because boundary delimiters must not appear in the body parts being encapsulated, a user agent must exercise care to choose a unique boundary parameter value. The boundary parameter value in the example above could have been the result of an algorithm designed to produce boundary delimiters with a very low probability of already existing in the data to be encapsulated without having to prescan the data. Alternate algorithms might result in more "readable" boundary delimiters for a recipient with an old user agent, but would require more attention to the possibility that the boundary delimiter might appear at the beginning of some line in the encapsulated part. The simplest boundary delimiter line possible is something like "---", with a closing boundary delimiter line of "-----".

      And RFC 2045 adds:

      Since the hyphen character ("-") may be represented as itself in the Quoted-Printable encoding, care must be taken, when encapsulating a quoted-printable encoded body inside one or more multipart entities, to ensure that the boundary delimiter does not appear anywhere in the encoded body. (A good strategy is to choose a boundary that includes a character sequence such as "=_" which can never appear in a quoted-printable body. See the definition of multipart messages in RFC 2046.)

      So, a "good" boundary string contains pseudo-random or hashed data and is not a single word.

      Some boundaries found in my inbox:

      Boundary stringUser AgentComment
      ----=_NextPart_000_0013_01D2C28B.E3F62DD0Microsoft Outlook 14.0Several hyphens, an equal sign, and some hashed data
      ----=_Part_5917814_1894675906.1493199820931unknown, used by AmazonVery similar to the above one
      ------------F7B57802990546C5ABB340EDThunderbird 45.8.0 on WindowsSeveral hypens and a single hash
      B_3576043155_12903Microsoft-MacOutlook/14.7.3.170325No hyphens, decimal hash values
      b1_1513f22a95bb051912f3d082319cd009PHPMailer 5.2.14Again no hypehns, long hex hash value
      --_com.samsung.android.email_24350088718877150Unknown mail program running on a Samsung Android smartphone"-_" as recommended in RFC 2045, class name of the main program, and a decimal hash
      _005_17997d6269704c37af1f86b072d23dc6pollinexchangepollindel_Unknown, with traces of MS Exchangelong hex hash value, plus the name of the outgoing mail server with all non-alphanumeric characters removed
      sA6u0I68pY2erg76iB=_hTGmQiLde4Zv1ORogMailerLooks like two concatenated base64 strings, or perhaps just randomly choosen characters.
      b1_f1e8d96630926a3eef6252dc28dfcf72Elaine 5.11Looks very similar to PHPMailer above
      Apple-Mail=_48151E9E-26D6-4966-B9BC-C015767CB661Apple Mail 2.3124Same idea as Samsungs Android app: Application name and some random hex values

      MIME::Lite constructs a boundary string like this:

      # Generate a new boundary to use. # The unsupported $VANILLA is for test purposes only. sub gen_boundary { return ( "_----------=_" . ( $VANILLA ? '' : int(time) . $$ ) . $B +Count++ ); }

      (Ignore $VANILLA.) What you get is a fixed string, plus a few concatenated numbers (timestamp, process ID, and a simple counter). Overall, it should be quite unique, and it is unlikely to occur elsewhere in a message. No, this is not exciting, but it works.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        Wrong way. When avoiding perfectly working modules, one should at least read the relevant RFCs, in this case RFC2046. It clearly states:

        Right. Sometimes memory betrays me; but it is not me who is "avoiding perfectly working modules"...

        So, a "good" boundary string contains pseudo-random or hashed data and is not a single word.

        Elsewhere in this thread: when assembling a multipart mail "avoiding perfectly working modules", if only for the sake of providing an example, I construct the boundary as '==' . encode_base64( join('',gettimeofday), '') which doesn't qualify as a single word also, and should be fairly unique, too.

        perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
Re^4: Using Net::SMTP to send email attachments
by huck (Prior) on Apr 30, 2017 at 19:18 UTC

    It would be helpful to see what you did try

    You may have one too many $smtp->datasend("--$boundary\n"); in there. you dont want a boundary before the main text. The example seems to have one.

      Here is the script I have been trying. I have tried commenting out various boundary lines, but without any improvement on the fact that the text part, "This is some text", is coming through as an attachment and the main body of the email is empty. The binary file attachment is coming through just fine though, so there is progress :-)</P

      Thanks again for looking

      #!/usr/bin/perl print "Content-type: text/html\n\n"; use CGI qw(:standard); use CGI::Carp qw(warningsToBrowser fatalsToBrowser); use warnings; use Net::SMTP; use MIME::Base64; my ($buf, $picture); my $company = 'my_company.com'; my $path = "/home/sites/$company/public_html"; my $attachBinaryFile = "image.jpg"; my $boundary = 'frontier'; my $passwd = "password"; my $contact = "name"; my $email = "info\@$company"; $smtp = Net::SMTP->new("mail.$company", Timeout => 30,Debug => 0,); $smtp->datasend("AUTH LOGIN\n"); $smtp->response(); $smtp->datasend(encode_base64("$contact\@$company") ); $smtp->response(); $smtp->datasend(encode_base64("$passwd") ); $smtp->response(); $smtp->mail("$contact\@$company"); $smtp->to($email); $smtp->cc(); $smtp->data(); $smtp->datasend("To: $email\n"); $smtp->datasend("From: $contact\@$company\n"); $smtp->datasend("Cc: info\@$company\n"); $smtp->datasend("Subject: Trial to see if this will come through\n"); $smtp->datasend("MIME-Version: 1.0\n"); $smtp->datasend("Content-type: multipart/mixed;\n\tboundary=\"$boundar +y\"\n"); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->datasend("Content-type: text/plain; charset=\"UTF-8\"\n"); $smtp->datasend("Content-Disposition: quoted-printable\n"); $smtp->datasend("\n"); $smtp->datasend("\nThis is some text.\n"); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->datasend("Content-Type: image/jpeg; name=\"$attachBinaryFile\"\ +n"); $smtp->datasend("Content-Transfer-Encoding: base64\n"); $smtp->datasend("Content-Disposition: attachment; filename=\"$attachBi +naryFile\"\n"); $smtp->datasend("\n"); open(DAT, "$path/$attachBinaryFile") || die("Could not open binary fil +e!"); binmode(DAT); local $/=undef; while (read(DAT, $picture, 4096)) { $buf = &encode_base64( $picture ); $smtp->datasend($buf); } close(DAT); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->dataend(); $smtp->quit; print "Mail sent\n"; exit; print "</body></html>";

        Try changing this

        $smtp->datasend("Content-type: multipart/mixed;\n\tboundary=\"$boundar +y\"\n"); $smtp->datasend("\n"); $smtp->datasend("--$boundary\n"); $smtp->datasend("Content-type: text/plain; charset=\"UTF-8\"\n"); $smtp->datasend("Content-Disposition: quoted-printable\n"); $smtp->datasend("\n");
        to this
        $smtp->datasend("Content-type: multipart/mixed;\n\tboundary=\"$boundar +y\"\n"); $smtp->datasend("Content-type: text/plain; charset=\"UTF-8\"\n"); $smtp->datasend("Content-Disposition: quoted-printable\n"); $smtp->datasend("\n");
        That matches what i see in my pop3 mailboxes better