IMAPService.java Source code

Java tutorial

Introduction

Here is the source code for IMAPService.java

Source

/* ------------------------------------------------------------------------- */
/*   Copyright (C) 2017 
            Author:  sroig2013@my.fit.edu
            Florida Tech, Computer Science
       
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation; either the current version of the License, or
   (at your option) any later version.
       
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
      
  You should have received a copy of the GNU Affero General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */
/* ------------------------------------------------------------------------- */

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.xml.bind.DatatypeConverter;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;

public class IMAPService {
    private final String mailboxRootDirectory = "Mailbox";
    private String server;
    private int port;

    private boolean deleteAfterDownload;

    private SSLSocket socket;
    private BufferedReader reader;
    private PrintWriter output;

    private ArrayList<EmailFolder> emailFolders;

    public IMAPService(String _server, int _port) {
        File mailboxRootDir = new File(mailboxRootDirectory);
        mailboxRootDir.mkdir();

        this.server = _server;
        this.port = _port;
        this.emailFolders = new ArrayList<EmailFolder>();
        this.deleteAfterDownload = false;
        // TODO Auto-generated constructor stub
        try {
            SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            this.socket = (SSLSocket) sslSocketFactory.createSocket(this.server, this.port);

            this.reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            this.output = new PrintWriter(socket.getOutputStream());
            System.out.println(parseServerResponse());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String parseServerResponse() throws IOException {
        // Need a string to store full response and single line
        StringBuilder response = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            // Add the line to the overall response
            response.append(line + "\n");

            // If the reader isn't ready send back the full response
            if (!reader.ready()) {
                return response.toString();
            }
        }

        return response.toString();
    }

    public boolean login(String userName, String password) {
        try {
            // String for logging into the IMAP server
            String login = "a0 login " + userName + " " + password + "\r";
            // Print imap command to local console
            System.out.println(login);
            // Send imap command to the server
            output.println(login);
            output.flush();
            // Listen for server's response
            System.out.println(parseServerResponse());
            //We've made it this far so login was successful
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        // Login failed
        return false;
    }

    public void logout() {
        try {
            String logout = "a0 logout" + "\r";
            System.out.println(logout);
            output.println(logout);
            output.flush();
            System.out.println(parseServerResponse());
            reader.close();
            output.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void buildMailbox() {
        try {
            // Get a list of all the mailboxes
            String listAllFolders = "a0 list \"\" *" + "\r";
            System.out.println(listAllFolders);
            output.println(listAllFolders);
            output.flush();
            String folderList = parseServerResponse();

            while (!folderList.contains("a0 OK")) {
                folderList += parseServerResponse();
            }

            System.out.println(folderList);

            String[] folders = folderList.split("\n|\"\"");
            folders = Arrays.copyOf(folders, folders.length - 1);

            // fetch each email and create a new email and store it in the respective folder
            buildFolderList(folders);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private void buildFolderList(String[] folderList) {

        for (String folder : folderList) {

            try {
                // Parse the folder string
                String[] folderTokens = folder.split("\"");
                // Create a new folder
                EmailFolder emailFolder = new EmailFolder();
                // Set the name from server response
                emailFolder.name = folderTokens[folderTokens.length - 1];

                // Select the folder
                String selectFolder = "a0 select \"" + emailFolder.name + "\"\r";
                System.out.println(selectFolder);
                output.println(selectFolder);
                output.flush();

                String selectFolderResponse = parseServerResponse();

                while (!selectFolderResponse.contains("a0 OK")
                        && !selectFolderResponse.contains("Unknown Mailbox")) {
                    selectFolderResponse += parseServerResponse();
                }

                System.out.println(selectFolderResponse);

                if (!selectFolderResponse.contains("Unknown Mailbox")) {
                    // Get all emails for this folder
                    emailFolder.emails = buildEmailList();
                } else {
                    emailFolder.emails = new ArrayList<Email>();
                }

                // Add all folders to IMAP Service list of folders
                emailFolders.add(emailFolder);

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private ArrayList<Email> buildEmailList() {

        try {

            // Find all emails
            String fetchEmailList = "a0 fetch 1:* (FLAGS)" + "\r";
            System.out.println(fetchEmailList);
            output.println(fetchEmailList);
            output.flush();

            // Get list of emails in the current mailbox
            String emailList = parseServerResponse();

            while (!emailList.contains("a0 OK")) {
                emailList += parseServerResponse();
            }

            System.out.println(emailList);

            // Split up the email list by newlines
            String[] emailTokenIdList = emailList.split("\n");

            ArrayList<Integer> emailIds = new ArrayList<Integer>();

            for (int i = 0; i < emailTokenIdList.length; i++) {
                if (emailTokenIdList[i].split("\\s+").length > 1) {
                    if (emailTokenIdList[i].split("\\s+")[1].matches("[-+]?\\d*\\.?\\d+")) {
                        if (Integer.parseInt(emailTokenIdList[i].split("\\s+")[1]) > 0)
                            emailIds.add(Integer.parseInt(emailTokenIdList[i].split("\\s+")[1]));
                    }
                }
            }

            ArrayList<Email> emails = new ArrayList<Email>();

            for (int emailID : emailIds) {

                // Instantiate email object and use fetch based on ID to grab it's data
                Email email = new Email();
                email.uid = emailID;

                // Fetch the email header by it's id
                String fetchEmailSender = "a0 fetch " + email.uid + " body[header]" + "\r";
                output.println(fetchEmailSender);
                output.flush();
                System.out.println(fetchEmailSender);

                String headerResponse = parseServerResponse();

                while (!headerResponse.contains("a0 OK")) {
                    headerResponse += parseServerResponse();
                }

                System.out.println(headerResponse);

                String[] headerResponseTokens = headerResponse.split("\n");

                email.header = headerResponse;

                for (String headerItem : headerResponseTokens) {
                    if (headerItem.contains("From")) {
                        // Find who it's from
                        if (headerItem.contains("<")) {
                            String from = headerItem.substring(headerItem.indexOf("<") + 1);
                            from = from.substring(0, from.indexOf(">"));
                            email.from = from;
                        } else {
                            email.from = headerItem.split("\\s+")[1];
                        }
                    }
                    if (headerItem.contains("Subject")) {
                        // Find email subject
                        if (headerItem.indexOf(":") < headerItem.length())
                            email.subject = headerItem.substring(headerItem.indexOf(":") + 1, headerItem.length());
                        else
                            email.subject = "NO SUBJECT";
                    }
                }

                // Fetch the email header by it's id
                String fetchEmailBody = "a0 fetch " + email.uid + " body[]" + "\r";
                output.println(fetchEmailBody);
                output.flush();
                System.out.println(fetchEmailBody);
                String fetchEmailBodyResponse = parseServerResponse();

                // Make sure the message is all there
                while (!fetchEmailBodyResponse.contains("a0 OK Success")) {
                    fetchEmailBodyResponse += parseServerResponse();
                }

                System.out.println(fetchEmailBodyResponse);

                email.body = fetchEmailBodyResponse;

                // Now that the email is fully built tell it to parse the body elements
                email.parseBodyElements();

                emails.add(email);
            }

            return emails;

        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    private void deleteFoldersEmails(EmailFolder folder) {
        try {
            // Get a list of all the mailboxes
            String selectFolder = "a0 select \"" + folder.name + "\"\r";
            System.out.println(selectFolder);
            output.println(selectFolder);
            output.flush();
            String selectFolderResponse = parseServerResponse();

            while (!selectFolderResponse.contains("a0 OK")) {
                selectFolderResponse += parseServerResponse();
            }

            System.out.println(selectFolderResponse);

            for (Email email : folder.emails) {
                // Set emails to deleted
                String deleteEmail = "a0 STORE" + email.uid + " +FLAGS \\Deleted\r";
                System.out.println(deleteEmail);
                output.println(deleteEmail);
                output.flush();
                String deleteEmailResponse = parseServerResponse();

                while (!deleteEmailResponse.contains("a0 OK")) {
                    deleteEmailResponse += parseServerResponse();
                }

                System.out.println(deleteEmailResponse);
            }

            // Expunge deleted emails
            String expungeCommand = "a0 EXPUNGE\r";
            System.out.println(expungeCommand);
            output.println(expungeCommand);
            output.flush();
            String expungeCommandResponse = parseServerResponse();

            while (!expungeCommandResponse.contains("a0 OK")) {
                expungeCommandResponse += parseServerResponse();
            }

            System.out.println(expungeCommandResponse);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void downloadFoldersEmails(EmailFolder folder) {
        // Create the folder inside main mailbox
        File rootDir = new File(mailboxRootDirectory);
        File folderDir = null;
        String[] folderList = folder.name.split("/");

        for (String folderPiece : folderList) {
            folderDir = new File(rootDir, folderPiece);
            folderDir.mkdir();
            rootDir = new File(folderDir.getPath());
        }

        // Create a file for the messages now
        if (folder.emails != null) {
            for (Email email : folder.emails) {
                if (email != null) {
                    if (email.from == null)
                        email.from = "null";
                    if (email.subject == null)
                        email.subject = "null";
                    String emailDirName = email.fiveDigitId + "_" + email.from + "_" + email.subject;
                    emailDirName = emailDirName.replaceAll("[^A-Za-z0-9]", "-");
                    File emailDir = new File(folderDir, emailDirName);
                    emailDir.mkdir();

                    try {
                        File emailContext = new File(emailDir, "context.txt");
                        emailContext.createNewFile();
                        Files.write(emailContext.toPath(), email.body.getBytes());

                        for (BodyElement be : email.bodyElements) {
                            File attachmentContext = new File(emailDir, be.fileName);
                            attachmentContext.createNewFile();
                            // Write base64 encoded information to the attachment
                            Files.write(attachmentContext.toPath(), DatatypeConverter.parseBase64Binary(be.data));
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        if (deleteAfterDownload)
            deleteFoldersEmails(folder);
    }

    public void downloadFoldersEmails(String directoryName) {
        for (EmailFolder folder : emailFolders) {
            if (folder.name.equals(directoryName)) {
                downloadFoldersEmails(folder);
            }
        }
    }

    public void downloadAllFolders() {
        for (EmailFolder folder : emailFolders) {
            downloadFoldersEmails(folder);
        }
    }

    public void deleteEmailsAfterDownload() {
        this.deleteAfterDownload = true;
    }

    public static void main(String[] args) {
        // Arguments
        String server = "";
        int port = -1;
        String login = "";
        String password = "";
        boolean deleteAfterDownload = false;
        boolean downloadAll = false;
        String[] foldersToDownload = null;

        // Set up apache cli
        Options options = new Options();

        Option S = new Option("S", true, "Server Name");
        S.setRequired(true);
        options.addOption(S);

        Option P = new Option("P", true, "Port Number");
        P.setRequired(true);
        options.addOption(P);

        Option l = new Option("l", true, "Login");
        l.setRequired(true);
        options.addOption(l);

        Option p = new Option("p", true, "Password if not on stdin");
        options.addOption(p);

        Option d = new Option("d", false, "Delete after downloading");
        d.setRequired(false);
        options.addOption(d);

        Option a = new Option("a", false, "Download from all folders");
        a.setRequired(false);
        options.addOption(a);

        Option f = new Option("f", true, "Download messages from specified folder");
        options.addOption(f);

        CommandLineParser clp = new DefaultParser();

        try {
            CommandLine cl = clp.parse(options, args);

            if (cl.hasOption("S"))
                server = cl.getOptionValue("S");

            if (cl.hasOption("P"))
                port = Integer.parseInt(cl.getOptionValue("P"));

            if (cl.hasOption("l"))
                login = cl.getOptionValue("l");

            if (cl.hasOption("p"))
                password = cl.getOptionValue("p");

            if (cl.hasOption("d"))
                deleteAfterDownload = true;

            if (cl.hasOption("a"))
                downloadAll = true;
            else if (cl.hasOption("f"))
                foldersToDownload = cl.getOptionValues("f");
            else {
                showArgMenu(options);
                return;
            }

        } catch (Exception e) {
            showArgMenu(options);
            return;
        }

        if (password.isEmpty()) {
            // Grab p/w of stdin if it's there
            Scanner sc = new Scanner(System.in);

            password = sc.nextLine();

            sc.close();
        }

        if (!server.isEmpty() && port != -1 && !login.isEmpty() && !password.isEmpty()
                && (downloadAll || foldersToDownload != null)) {
            //IMAPService imapService = new IMAPService("imap.gmail.com", 993);
            IMAPService imapService = new IMAPService(server, port);
            //imapService.login("kinglibingli@gmail.com", "kingli1bingli");
            imapService.login(login, password);
            imapService.buildMailbox();
            // Check if service should delete emails
            if (deleteAfterDownload)
                imapService.deleteEmailsAfterDownload();
            // Proceed with downloads
            if (downloadAll)
                imapService.downloadAllFolders();
            else {
                for (String folderNames : foldersToDownload) {
                    imapService.downloadFoldersEmails(folderNames);
                }
            }

            imapService.logout();
        }

    }

    private static void showArgMenu(Options options) {
        HelpFormatter helpFormatter = new HelpFormatter();
        helpFormatter.printHelp("Gossip P2P Server", options, true);
    }

}