Send email with an attachmentTag(s): Networking


In this example, Elvis is sending a GIF of his old Gumby friend. The attachment is encodded using the BASE64 algorithm.

In this example, we are sending as attachment this image :
Hi! I'm Gumby.

import java.io.*;
import java.net.*;

public class TestMailMIME {
  static  int SMTPport =  25;
  static  Socket  socket;
  static  DataInputStream in;
  static  DataOutputStream out;
  static  PrintStream prout;

  /* Name of file to be sent. */
  String FileName = "gumby.gif";

 public static void main(String s[]) {
  /*
  ** TestMailMIME [server] [recipient]
  */
  TestMailMIME t = new TestMailMIME();
  t.sendMail(s[0], s[1]);
 }

 public void sendMail(String mailServer, String recipient) {
  System.out.println("Send mail with attached file ");
  try {
    Socket s = new Socket(mailServer, 25);
    BufferedReader in = new BufferedReader
      (new InputStreamReader(s.getInputStream(), "8859_1"));
    BufferedWriter out = new BufferedWriter
      (new OutputStreamWriter(s.getOutputStream(), "8859_1"));

    String boundary = "DataSeparatorString";

    // here you are supposed to send your username
    sendln(in, out, "HELO theWorld");
    // warning : some mail server validate the sender address
    //           in the MAIL FROM command, put your real address here
    sendln(in, out, "MAIL FROM: ");
    sendln(in, out, "RCPT TO: <" + recipient + ">" );
    //   to have more than one recipient, repeat
    //      sendln(in, out, "RCPT TO: <" + recipient2 + ">" );
    //      sendln(in, out, "RCPT TO: <" + recipient3 + ">" );
    //      etc...
    //   thanks to  P-Y Colle for the tip!
    sendln(in, out, "DATA");
    sendln(out, "MIME-Version: 1.0");
    sendln(out, "Subject: remember me");
    sendln(out, "From: Elvis Presley <Elvis.Presley@jailhouse.rock>");
    sendln
     (out, "Content-Type: multipart/mixed; boundary=\"" + boundary +"\"");
    sendln(out, "\r\n--" + boundary);

    // Send the body
    sendln(out, "Content-Type: text/plain; charset=\"us-ascii\"\r\n");
    sendln(out, "I'm alive. Help me!\r\n\r\n");
    sendln(out, "\r\n--" +  boundary );

    // send the GIF
    sendln(out, "Content-Type:image/gif; name="+FileName);
    sendln
      (out, "Content-Disposition: attachment;filename=\""+FileName+"\"");
    sendln(out, "Content-transfer-encoding: base64\r\n");
    MIMEBase64.encode(FileName, out);
    sendln(out, "\r\n--" + boundary);

    sendln(out, "\r\n\r\n--" + boundary + "--\r\n");
    sendln(in, out,".");
    sendln(in, out, "QUIT");
    s.close();
  }
  catch (Exception e) {
    e.printStackTrace();
  }
 }

 public void sendln(BufferedReader in, BufferedWriter out, String s) {
  try {
    out.write(s + "\r\n");
    out.flush();
    // System.out.println(s);
    s = in.readLine();
    // System.out.println(s);
  }
  catch (Exception e) {
    e.printStackTrace();
  }
 }

 public void sendln(BufferedWriter out, String s) {
   try {
    out.write(s + "\r\n");
    out.flush();
    System.out.println(s);
   }
   catch (Exception e) {
    e.printStackTrace();
   }
 }
}

And the MIMEBase64 class

import java.io.*;

public class MIMEBase64 {
  /*
    Base64 uses a 65 character subset of US-ASCII,
    allowing 6 bits for each character so the character
    "m" with a Base64 value of 38, when represented
    in binary form, is 100110.

    With a text string, let's say "men" is encoded this
    is what happens :

    The text string is converted into its US-ASCII value.

       The character "m" has the decimal value of 109
       The character "e" has the decimal value of 101
       The character "n" has the decimal value of 110

    When converted to binary the string looks like this :

        m   01101101
        e   01100101
        n   01101110

    These three "8-bits" are concatenated to make a
    24 bit stream
                   011011010110010101101110

    This 24 bit stream is then split up into 4 6-bit
    sections
                   011011 010110 010101 101110

    We now have 4 values. These binary values are
    converted to decimal form
                     27     22     21     46

    And the corresponding Base64 character are :
                     b      W       V     u

    The encoding is always on a three characters basis
    (to have a set of 4 Base64 characters). To encode one
    or two then, we use the special character "=" to pad
    until 4 base64 characters is reached.

    ex. encode "me"

        01101101  01100101
        0110110101100101
        011011 010110 0101
                      111111    (AND to fill the missing bits)
        011011 010110 010100
           b     W      U
           b     W      U     =  ("=" is the padding character)

        so "bWU="  is the base64 equivalent.

        encode "m"

        01101101
        011011 01
               111111         (AND to fill the missing bits)
        011011 010000
            b     Q     =  =   (two paddings are added)

        Finally, MIME specifies that lines are 76 characters wide maximum.

  */

 static String BaseTable[] = {
    "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
    "Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f",
    "g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v",
    "w","x","y","z","0","1","2","3","4","5","6","7","8","9","+","/"
    };

  public static void encode(String filename, BufferedWriter out) {
    try {
      File f              = new File(filename);
      FileInputStream fin = new FileInputStream(filename);

      // read the entire file into the byte array
      byte bytes[]        = new byte[(int)(f.length())];
      int n               = fin.read(bytes);

      if (n < 1) return;          // no bytes to encode!?!

      byte buf[] = new byte[4];   // array of base64 characters

      int n3byt      = n / 3;     // how 3 bytes groups?
      int nrest      = n % 3;     // the remaining bytes from the grouping
      int k          = n3byt * 3; // we are doing 3 bytes at a time
      int linelength = 0;         // current linelength
      int i          = 0;         // index

     // do the 3-bytes groups ...
     while ( i < k ) {
       buf[0] = (byte)(( bytes[i]   & 0xFC) >> 2);
       buf[1] = (byte)(((bytes[i]   & 0x03) << 4) |
                  ((bytes[i+1] & 0xF0) >> 4));
       buf[2] = (byte)(((bytes[i+1] & 0x0F) << 2) |
                  ((bytes[i+2] & 0xC0) >> 6));
       buf[3] = (byte)(  bytes[i+2] & 0x3F);
       send(out, BaseTable[buf[0]]);
       send(out, BaseTable[buf[1]]);
       send(out, BaseTable[buf[2]]);
       send(out, BaseTable[buf[3]]);
       /*
          The above code can be written in more "optimized"
          way. Harder to understand but more compact.
          Thanks to J. Tordera for the tip!
           buf[0]= (byte)(b[i] >> 2);
           buf[1]= (byte)(((b[i] & 0x03)  << 4)|(b[i+1]>> 4));
           buf[2]= (byte)(((b[i+1] & 0x0F)<< 2)|(b[i+2]>> 6));
           buf[3]= (byte)(b[i+2] & 0x3F);
           send(out,BaseTable[buf[0]]+BaseTable[buf[1]]+
                    BaseTable[buf[2]]+BaseTable[buf[3]]);
       */

       if ((linelength += 4) >= 76) {
          send(out, "\r\n");
          linelength = 0;
          }
       i += 3;
       }

     // deals with with the padding ...
     if (nrest==2) {
        // 2 bytes left
        buf[0] = (byte)(( bytes[k] & 0xFC)   >> 2);
        buf[1] = (byte)(((bytes[k] & 0x03)   << 4) |
                   ((bytes[k+1] & 0xF0) >> 4));
        buf[2] = (byte)(( bytes[k+1] & 0x0F) << 2);
        }
     else if (nrest==1) {
        // 1 byte left
        buf[0] = (byte)((bytes[k] & 0xFC) >> 2);
        buf[1] = (byte)((bytes[k] & 0x03) << 4);
        }
     if (nrest > 0) {
        // send the padding
        if ((linelength += 4) >= 76) send(out, "\r\n");
        send(out, BaseTable[buf[0]]);
        send(out, BaseTable[buf[1]]);
        // Thanks to R. Claerman for the bug fix here!
        if (nrest==2) {
           send(out, BaseTable[buf[2]]);
           }
        else {
          send(out, "=");
          }
        send(out, "=");
        }
      out.flush();
      }
     catch (Exception e) {
       e.printStackTrace();
       }
   }

 public static void send(BufferedWriter out, String s) {
    try {
      out.write(s);
      System.out.print(s);
      }
    catch (Exception e) {
      e.printStackTrace();
      }
    }
}
If Elvis want to send a GIF and a text file about his Gumby old friend then he would change his sendMail() for something like this
...

   /* Name of files to be sent. */
   String FileName1 = "gumby.gif";
   String FileName2 = "gumby.txt";

...

  public void sendMail(String mailServer, String recipient) {
   System.out.println("Send mail with attached file");
   try {
     Socket s = new Socket(mailServer, 25);
     BufferedReader in = new BufferedReader
       (new InputStreamReader(s.getInputStream(), "8859_1"));
     BufferedWriter out = new BufferedWriter
       (new OutputStreamWriter(s.getOutputStream(), "8859_1"));

     String boundary = "DataSeparatorString";

     // here you are supposed to send your username
     sendln(in, out, "HELO world");
     sendln(in, out, "MAIL FROM: <real@gagnon.com>");
     sendln(in, out, "RCPT TO: <" + recipient + ">" );
     sendln(in, out, "DATA");
     sendln(out, "MIME-Version: 1.0");
     sendln(out, "Subject: remember me");
     sendln(out, "From: Elvis Presley <Elvis.Presley@jailhouse.rock<");
     sendln
       (out,"Content-Type: multipart/mixed; boundary=\"" + boundary +"\"");
     sendln(out, "\r\n--" + boundary);

     // send the GIF
     sendln(out, "Content-Type: text/plain; charset=\"us-ascii\"\r\n");
     sendln(out, "I'm alive. Help me!\n\n");
     sendln(out, "\r\n" + "--" +  boundary );
     sendln(out, "Content-Type:image/gif; name="+FileName1);
     sendln
       (out, "Content-Disposition: attachment;filename=\""+FileName1+"\"");
     sendln(out, "Content-transfer-encoding: base64\r\n");
     MIMEBase64.encode(FileName1, out);
     sendln(out, "\r\n--" + boundary);

     // the text file
     sendln(out, "Content-Type: text/plain; name="+FileName2);
     sendln(out, "Content-Disposition: inline;filename=\""+FileName2+"\"");
     // to send the file as an attachment instead of "inline" use :
     // sendln
     // (out, "Content-Type: text/plain; name="+FileName2);
     // sendln
     // (out,"Content-Disposition: attachment;filename=\""+FileName2+"\"");
     sendln(out, "Content-Transfer-Encoding: base64\r\n");
     MIMEBase64.encode(FileName2, out);
     sendln(out, "\r\n\r\n--" + boundary + "--\r\n");

     // done
     sendln(in, out,".");
     sendln(in, out, "QUIT");
     s.close();
     }
   catch (Exception e) {
     e.printStackTrace();
     }
   }
...
NOTE: A compact algorithm to encode string as Base64 can be found here. Check out the Javascript, it is very short!

NOTE: Check this related HowTo.


blog comments powered by Disqus