Author: sebb Date: Mon Sep 23 00:48:46 2013 New Revision: 1525483 URL: http://svn.apache.org/r1525483 Log: Use IMAP URL to simplify command line Add selector support
Modified: commons/proper/net/trunk/src/main/java/examples/mail/IMAPImportMbox.java Modified: commons/proper/net/trunk/src/main/java/examples/mail/IMAPImportMbox.java URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/main/java/examples/mail/IMAPImportMbox.java?rev=1525483&r1=1525482&r2=1525483&view=diff ============================================================================== --- commons/proper/net/trunk/src/main/java/examples/mail/IMAPImportMbox.java (original) +++ commons/proper/net/trunk/src/main/java/examples/mail/IMAPImportMbox.java Mon Sep 23 00:48:46 2013 @@ -21,6 +21,10 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; import org.apache.commons.net.imap.IMAPClient; import org.apache.commons.net.imap.IMAPSClient; @@ -29,8 +33,13 @@ import org.apache.commons.net.imap.IMAPS * This is an example program demonstrating how to use the IMAP[S]Client class. * This program connects to a IMAP[S] server and imports messages into the folder from an mbox file. * <p> - * Usage: IMAPMail <imap[s] server hostname> <folder> <username> <password> <mboxfile> [secure protocol, e.g. TLS] + * Usage: IMAPImportMbox imap[s]://user:password@host[:port]/folder/path <mboxfile> [selectors] * <p> + * An example selector might be: + * <ul> + * <li>1,2,3,7-10</li> + * <li>-142986- : this is useful for retrieving messages by apmail number, which appears as From xyz-return-142986-apmail-...</li> + * <ul> */ public final class IMAPImportMbox { @@ -39,33 +48,93 @@ public final class IMAPImportMbox public static void main(String[] args) throws IOException { - if (args.length < 5) + if (args.length < 2) { - System.err.println( - "Usage: IMAPImportMbox <imap server hostname> <folder> <username> <password> <mboxfile> [TLS]"); + System.err.println("Usage: IMAPImportMbox imap[s]://user:password@host[:port]/folder/path <mboxfile> [selectors]"); + System.err.println("\tWhere: a selector is a list of numbers/number ranges - 1,2,3-10 - or a list of strings to match in the initial From line"); System.exit(1); } - final String server = args[0]; - final String folder = args[1]; - final String username = args[2]; - final String password = args[3]; - final String file = args[4]; - final String proto = (args.length > 5) ? args[5] : null; + final URI uri = URI.create(args[0]); + final String file = args[1]; final File mbox = new File(file); if (!mbox.isFile() || !mbox.canRead()) { throw new IOException("Cannot read mailbox file: " + mbox); } - IMAPClient imap; + final String userInfo = uri.getUserInfo(); + if (userInfo == null) { + throw new IllegalArgumentException("Missing userInfo details"); + } + + String []userpass = userInfo.split(":"); + if (userpass.length != 2) { + throw new IllegalArgumentException("Invalid userInfo details: '" + userpass + "'"); + } + + String username = userpass[0]; + String password = userpass[1]; + + String path = uri.getPath(); + if (path == null || path.length() < 1) { + throw new IllegalArgumentException("Invalid folderPath: '" + path + "'"); + } + String folder = path.substring(1); // skip the leading / + + List<String> contains = new ArrayList<String>(); // list of strings to find + BitSet msgNums = new BitSet(); // list of message numbers + + for(int i = 2; i < args.length; i++) { + String arg = args[i]; + if (arg.matches("\\d+(-\\d+)(,\\d+(-\\d+))*")) { // number,m-n + for(String entry :arg.split(",")) { + String []parts = entry.split("-"); + if (parts.length == 2) { // m-n + int low = Integer.parseInt(parts[0]); + int high = Integer.parseInt(parts[1]); + for(int j=low; j <= high; j++) { + msgNums.set(j); + } + } else { + msgNums.set(Integer.parseInt(entry)); + } + } + } else { + contains.add(arg); // not a number/number range + } + } + + final IMAPClient imap; - if (proto != null) { - System.out.println("Using secure protocol: " + proto); - imap = new IMAPSClient(proto, true); // implicit + if ("imaps".equalsIgnoreCase(uri.getScheme())) { + System.out.println("Using secure protocol"); + imap = new IMAPSClient(true); // implicit +// } else if ("null".equals(uri.getScheme())) { +// imap = new IMAPClient(){ +// @Override +// public void connect(String host){ } +// @Override +// public boolean login(String user, String pass) {return true;} +// @Override +// public boolean logout() {return true;} +// @Override +// public void disconnect() {} +// @Override +// public void setSoTimeout(int t) {} +// @Override +// public boolean append(String mailboxName, String flags, String datetime, String message) {return true;} +// }; } else { imap = new IMAPClient(); } + + String server = uri.getHost(); + int port = uri.getPort(); + if (port != -1) { + imap.setDefaultPort(port); + } + System.out.println("Connecting to server " + server + " on " + imap.getDefaultPort()); // We want to timeout if a response takes longer than 60 seconds @@ -77,6 +146,8 @@ public final class IMAPImportMbox throw new RuntimeException("Could not connect to server.", e); } + int total = 0; + int loaded = 0; try { if (!imap.login(username, password)) { System.err.println("Could not login to server. Check password."); @@ -90,19 +161,28 @@ public final class IMAPImportMbox String line; StringBuilder sb = new StringBuilder(); + boolean wanted = false; // Skip any leading rubbish while((line=br.readLine())!=null) { if (line.startsWith("From ")) { // start of message; i.e. end of previous (if any) - process(sb, imap, folder); + if (process(sb, imap, folder)) { // process previous message (if any) + loaded++; + } + wanted = wanted(total, line, msgNums, contains); + total ++; sb.setLength(0); } else if (line.startsWith(">From ")) { // Unescape "From " in body text line = line.substring(1); } // TODO process first Received: line to determine arrival date? - sb.append(line); - sb.append(CRLF); + if (wanted) { + sb.append(line); + sb.append(CRLF); + } } br.close(); - process(sb, imap, folder); // end of last message (if any) + if (wanted && process(sb, imap, folder)) { // last message (if any) + loaded++; + } } catch (IOException e) { System.out.println(imap.getReplyString()); e.printStackTrace(); @@ -112,11 +192,13 @@ public final class IMAPImportMbox imap.logout(); imap.disconnect(); } + System.out.println("Processed " + total + " messages, loaded " + loaded); } - private static void process(final StringBuilder sb, final IMAPClient imap, final String folder) throws IOException { + private static boolean process(final StringBuilder sb, final IMAPClient imap, final String folder) throws IOException { final int length = sb.length(); - if (length > 2) { + boolean haveMessage = length > 2; + if (haveMessage) { System.out.println("Length " + length); sb.setLength(length-2); // drop trailing CRLF String msg = sb.toString(); @@ -124,5 +206,37 @@ public final class IMAPImportMbox throw new IOException("Failed to import message: " + imap.getReplyString()); } } + return haveMessage; + } + + /** + * Is the message wanted? + * + * @param msgNum the message number + * @param line the From line + * @param msgNums the list of wanted message numbers + * @param contains the list of strings to be contained + * @return true if the message is wanted + */ + private static boolean wanted(int msgNum, String line, BitSet msgNums, List<String> contains) { + return (msgNums.isEmpty() && contains.isEmpty()) // no selectors + || msgNums.get(msgNum) // matches message number + || listContains(contains, line); // contains string + } + + /** + * Is at least one entry in the list contained in the string? + * @param contains the list of strings to look for + * @param string the String to check against + * @return true if at least one entry in the contains list is contained in the string + */ + private static boolean listContains(List<String> contains, String string) { + for(String entry : contains) { + if (string.contains(entry)) { + return true; + } + } + return false; } + }