Skip to content
Advertisement

Calling Linux’s “mailx” through java code: message-text always goes into attachment

Since my SMTP provider has limit on number of emails that can be sent in a day, I wrote a Java code to call the “mailx” of Linux system, my java-program is running on.

Here is that code:

package sys.cmd;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

public class IntermediateJavaLinuxMailX {

    public static void main(String[]args) throws IOException{
        email(
                new ArrayList<String>(){{
                    add("myemailid@myserver.com");
                    add("myothermailid@otherserver.com");
                }},
                "Error Message",
                "Hello World!rn This is message"
        );
    }

    public static void email(
            List<String>toEmailIds,
            String subject,
            String msgText
            ) throws IOException{
        String toEmails = toString(toEmailIds);
        String[]args=new String[]{"/bin/sh" , "-c", "mailx -s ""+subject+"" "+toEmails};
        System.out.println("The command for bash is: "+args[2]);
        Process proc= Runtime.getRuntime().exec(args);
        OutputStream o = proc.getOutputStream();//probable output for text
        InputStream i = new ByteArrayInputStream(msgText.getBytes());//probable input for message-text
        read2end(i, o);
        o.close();
    }

    private static String toString(List<String> toEmailIds) {
        StringBuilder sb= new StringBuilder();
        for(String toEmailId:toEmailIds){
            sb.append(toEmailId).append(' ');
        }
        return sb.toString();
    }

    private static void read2end(InputStream i, OutputStream o) throws IOException {
        byte[]b=new byte[1000];
        for(int a=0;(a=i.read(b))>-1;)
            o.write(b, 0, a);
        i.close();
    }

}

The problem is: The email received on the recipients side, the text is not in the message-body, but it is in an attachment-file named “noname”.

The question is: How can I make the string in msgText appear in email’s message-body.


Adding one more thing I did so far:


I wrote another code, that uses temporary files for storing message-text & then uses file-redirection (<) for adding message-text & it gives desired result. But that is an indirect way. Is there any direct-way? Here is that another code:

    public static void email(
            List<String>toEmailIds,
            List<String>ccEmailIds,
            List<String>bccEmailIds,
            String subject,
            byte[][]attachContents,
            String messageText
            ) throws IOException{
        String toEmails=toString(" " , toEmailIds,' ');
        String ccEmails=notEmpty(ccEmailIds)?toString(" -c ", ccEmailIds,','):"";
        String bcEmails=notEmpty(bccEmailIds)?toString(" -b ", bccEmailIds,','):"";
        String recip=bcEmails+ccEmails+toEmails;
        String[]attachmentTempFiles=new String[notEmpty(attachContents)?attachContents.length:0];
        String attachFilePaths="";
        for(int x = 0;x<attachmentTempFiles.length;++x){
            String attachTempPath = "/path/temp/attach_"+x+".file";
            byteArray2File(attachContents[x],attachTempPath);
            attachmentTempFiles[x]=" -a "+attachTempPath;
            attachFilePaths+=attachmentTempFiles[x];
        }
        String msgTxtTempFilePath="/path/temp/msg.txt";
        byteArray2File(messageText.getBytes(), msgTxtTempFilePath);
        msgTxtTempFilePath=" < "+msgTxtTempFilePath;
        String mailxCommand = "mailx " + attachFilePaths + " -s "" + subject +"" "+ recip + msgTxtTempFilePath;
        Runtime.getRuntime().exec(new String[]{"/bin/sh" , "-c", mailxCommand});
    }

    private static void byteArray2File(byte[] bs, String path) throws IOException {
        FileOutputStream fos=new FileOutputStream(path);
        ByteArrayInputStream bais=new ByteArrayInputStream(bs);
        read2end(bais, fos);
        fos.close();
    }

    private static boolean notEmpty(byte[][] bs) {
        return bs!=null && bs.length>0;
    }

    private static boolean notEmpty(List<String> strings) {
        return strings!=null && !strings.isEmpty();
    }

    private static String toString(String pre, List<String> toEmailIds,char separator) {
        StringBuilder sb= new StringBuilder(pre);
        for(String toEmailId:toEmailIds){
            sb.append(toEmailId).append(separator);
        }
        return sb.substring(0,sb.length()-1);
    }

    private static void read2end(InputStream i, OutputStream o) throws IOException {
        byte[]b=new byte[1000];
        for(int a=0;(a=i.read(b))>-1;)
            o.write(b, 0, a);
        i.close();
    }

–Edit– Added after @Serge Ballesta’s comment:

“Hmm, I tried googling around and found Plain text file pipelined to Linux mailx turns to “Content-Type: application/octet-stream” (an attachment). Could your problem be the same ? To be sure could you control the headers of the received messages ?”


This code too had same effect:

        email(
                new ArrayList<String>(){{add("user_abc@mail1.com");add("person-xyz@mailer2.com");}},
                "Error Message",
                "Content-Type: text/plain; charset=us-asciirn" +
                "Content-Disposition: inlinernrn" +
                "Hello World!rn" +
                "This is message.rnrnrn"
        );

Still, all the message text goes into an attachment named “noname”.

Advertisement

Answer

EDIT : Replaced stupid things by a correct solution

The mailx command found in the majority of Linux distribution is heirloom mailx. It does much more things than the original BSD mailx and automatically encodes its input if there are any non printable characters in it*.

The problem here is that it consideres that the r characters are non standards and so it adds the following headers to the mail :

Content-Type: application/octet-stream
Content-Transfert-Encoding: base64

and the text of the mail is effectively base 64 encoded. It is not a true attachement, but many mail readers treat such mails as an empty body with an unnamed attached file.

So the solution is to remove all the r from the body of the mail.

In fact, if your LANG environment variable declares a locale that can use non 7 bits chars (éèûôüö …) mailx seems to be clever enough to declare an extended charset (ISO-8859-1 for fr locale) and to do quoted-printable encoding. So even with (at least west european) non 7bits ASCII characters in the message, provided there are no control chararacters, the mail should be sent normally.

The last chance solution would be not to use mailx and directly use sendmail.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement