/* * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import java.util.*; import java.io.*; import javax.mail.*; import javax.mail.search.*; import javax.mail.internet.*; /** * Program to manage the javamail@Sun.COM mailing list by keeping track * of which messages have been answered. Message that have been answered * have the ANSWERED flag set. Messages that are replies to other messages * have the FLAGGED flag set.
* * Note that this program operates on the log file directly and thus all * access to the log file using this program must be through the same * IMAP server so that the flags are handled consistently.
* * When run with no message number arguments it will list the unanswered * messages. The -u flag will cause it to first update the mailbox to * account for any recent replies.
* * When run with message number arguments it will mark those messages as * answered. This is useful for marking spam and other random messages * that will never be replied to.
* * The -a flag will cause it to mark all messages as answered. Useful for * flushing the ever accumulating spam.
* * @author Bill Shannon */ public class answer { static String protocol = "imap"; static String host = "anybodys.sfbay"; static String user = "javamail"; static String password = "1javamail1"; static String mbox = "/net/anybodys.sfbay/export6/javamail/logs/javamail.log"; static String url = null; static boolean verbose = false; static boolean update = false; static boolean markAll = false; public static void main(String argv[]) { int optind; for (optind = 0; optind < argv.length; optind++) { if (argv[optind].equals("-T")) { protocol = argv[++optind]; } else if (argv[optind].equals("-H")) { host = argv[++optind]; } else if (argv[optind].equals("-U")) { user = argv[++optind]; } else if (argv[optind].equals("-P")) { password = argv[++optind]; } else if (argv[optind].equals("-v")) { verbose = true; } else if (argv[optind].equals("-f")) { mbox = argv[++optind]; } else if (argv[optind].equals("-L")) { url = argv[++optind]; } else if (argv[optind].equals("-u")) { update = true; } else if (argv[optind].equals("-a")) { markAll = true; update = true; } else if (argv[optind].equals("--")) { optind++; break; } else if (argv[optind].startsWith("-")) { System.out.println( "Usage: answer [-L url] [-T protocol] [-H host] [-U user] [-P password]\n" + "\t[-f mailbox] [-v] [-u] [-a] [msgno ...]"); System.exit(1); } else { break; } } try { // Get a Properties object Properties props = System.getProperties(); // Get a Session object Session session = Session.getDefaultInstance(props, new TtyAuthenticator()); // Get a Store object Store store = null; if (url != null) { URLName urln = new URLName(url); store = session.getStore(urln); store.connect(); } else { if (protocol != null) store = session.getStore(protocol); else store = session.getStore(); // Connect if (host != null || user != null || password != null) store.connect(host, user, password); else store.connect(); } // Open the Folder Folder folder = store.getDefaultFolder(); if (folder == null) { System.out.println("No default folder"); System.exit(1); } folder = folder.getFolder(mbox); if (folder == null) { System.out.println("Invalid folder"); System.exit(1); } if (optind < argv.length || update) folder.open(Folder.READ_WRITE); else folder.open(Folder.READ_ONLY); int totalMessages = folder.getMessageCount(); if (totalMessages == 0) { System.out.println("Empty folder"); folder.close(false); store.close(); System.exit(1); } if (verbose) { int newMessages = folder.getNewMessageCount(); pv("Total messages = " + totalMessages); pv("New messages = " + newMessages); pv("-------------------------------"); } if (optind >= argv.length) { // get all messages that aren't answered or flagged Flags f = new Flags(); f.add(Flags.Flag.ANSWERED); f.add(Flags.Flag.FLAGGED); // Use a suitable FetchProfile FetchProfile fp = new FetchProfile(); fp.add(FetchProfile.Item.ENVELOPE); fp.add(FetchProfile.Item.FLAGS); Message[] msgs = folder.search(new FlagTerm(f, false)); folder.fetch(msgs, fp); if (update) { doUpdate(msgs); // re-fetch messages msgs = folder.search(new FlagTerm(f, false)); pv(""); pv(""); } System.out.println("Unanswered messages:"); for (int i = 0; i < msgs.length; i++) { Message msg = msgs[i]; System.out.println("--------------------------"); System.out.println("MESSAGE #" + msg.getMessageNumber() + ":"); Question q = new Question(msg); System.out.println(q); if (q.isReply()) System.out.println( "A reply to a message we haven't seen"); if (markAll) { int msgno = msg.getMessageNumber(); if (q.isReply()) { pv("Flagging message #" + msgno); msg.setFlag(Flags.Flag.FLAGGED, true); } else { pv("Answering message #" + msgno); msg.setFlag(Flags.Flag.ANSWERED, true); } } } } else { for (int i = optind; i < argv.length; i++) { int msgno = Integer.parseInt(argv[i]); Message msg = folder.getMessage(msgno); Question q = new Question(msg); if (q.isReply()) { pv("Flagging message #" + msgno); msg.setFlag(Flags.Flag.FLAGGED, true); } else { pv("Answering message #" + msgno); msg.setFlag(Flags.Flag.ANSWERED, true); } } } folder.close(false); store.close(); } catch (Exception ex) { System.out.println("Oops, got exception! " + ex.getMessage()); ex.printStackTrace(); System.exit(1); } System.exit(0); } /** * Update the mailbox to account for any replies. */ static void doUpdate(Message[] msgs) throws MessagingException { // a hash table of all the unanswered questions Hashtable qt = new Hashtable(); Address javamailAddress = new InternetAddress("javamail@Sun.COM"); SearchTerm javamail = new OrTerm( new RecipientTerm(Message.RecipientType.TO, javamailAddress), new RecipientTerm(Message.RecipientType.CC, javamailAddress)); for (int i = 0; i < msgs.length; i++) { Message msg = msgs[i]; pv("--------------------------"); pv("MESSAGE #" + msg.getMessageNumber() + ":"); Question q = new Question(msg); pv(q.toString()); Question q0; if ((q0 = (Question)qt.get(q)) != null) { pv("Found in table"); if (q.isReply()) { Message qm = q0.getMessage(); if (!qm.isSet(Flags.Flag.ANSWERED)) { pv("Answered:"); pv(q0.toString()); qm.setFlag(Flags.Flag.ANSWERED, true); } pv("is reply, flagging it"); msg.setFlag(Flags.Flag.FLAGGED, true); } else { // a second copy of a message that's not a reply? // shouldn't happen. } } else { pv("NOT found in table"); if (q.isReply()) { // a reply to a message we haven't seen? pv("flagging, but no original msg"); msg.setFlag(Flags.Flag.FLAGGED, true); } else { // an original question, put it in the table qt.put(q, q); // if the message looks like spam, flag it if (!javamail.match(msg)) { pv("SPAM!!!"); msg.setFlag(Flags.Flag.FLAGGED, true); } } } } } /** * Print verbose. */ static void pv(String s) { if (verbose) System.out.println(s); } } /** * This class represents a single "question" sent to javamail@Sun.COM, * or possibly a reply to such a question. */ class Question { Message msg; Address sender; Address[] recipients; String subject; Date date; boolean reply = false; Question(Message msg) throws MessagingException { this.msg = msg; sender = msg.getReplyTo()[0]; // XXX - assume only one subject = msg.getSubject(); if (subject == null) subject = ""; else if (subject.regionMatches(true, 0, "Re: ", 0, 4)) { subject = subject.substring(4); reply = true; recipients = msg.getRecipients(Message.RecipientType.TO); } subject = subject.trim(); date = msg.getSentDate(); if (date == null) date = msg.getReceivedDate(); } public boolean isReply() { return reply; } public Message getMessage() { return msg; } public int hashCode() { return subject.hashCode(); } public boolean equals(Object obj) { if (!(obj instanceof Question)) return false; Question q = (Question)obj; if (this.reply == q.reply) return this.sender.equals(q.sender) && this.subject.equals(q.subject); /* * A reply is equal to a question if the sender of the question * is one of the recipients of the reply. */ Question qq, qr; if (this.reply) { qq = q; qr = this; } else { qq = this; qr = q; } for (int i = 0; i < qr.recipients.length; i++) { if (qq.sender.equals(qr.recipients[i])) return true; } return false; } public String toString() { return "Date: " + date + "\nFrom: " + sender + "\nSubject: " + subject; } }