org.gcaldaemon.core.notifier.GmailNotifier.java Source code

Java tutorial

Introduction

Here is the source code for org.gcaldaemon.core.notifier.GmailNotifier.java

Source

//
// GCALDaemon is an OS-independent Java program that offers two-way
// synchronization between Google Calendar and various iCalalendar (RFC 2445)
// compatible calendar applications (Sunbird, Rainlendar, iCal, Lightning, etc).
//
// Apache License
// Version 2.0, January 2004
// http://www.apache.org/licenses/
// 
// Project home:
// http://gcaldaemon.sourceforge.net
//
package org.gcaldaemon.core.notifier;

import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.gcaldaemon.core.Configurator;
import org.gcaldaemon.core.FeedUtilities;
import org.gcaldaemon.core.FilterMask;
import org.gcaldaemon.core.GmailContact;
import org.gcaldaemon.logger.QuickWriter;

import com.google.gdata.data.HtmlTextConstruct;
import com.sun.syndication.feed.synd.SyndContent;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndPerson;

/**
 * Gmail notifier thread.
 * 
 * Created: Jan 03, 2007 12:50:56 PM
 * 
 * @author Andras Berkes
 */
public final class GmailNotifier extends Thread {

    // --- CONSTANTS ---

    private static final String FEED_URL = "https://mail.google.com/mail/feed/atom";

    private static final long MINUTE = 1000L * 60;
    private static final long HOUR = 1000L * 60 * 60;

    // --- LOGGER ---

    private static final Log log = LogFactory.getLog(GmailNotifier.class);

    // --- GENERAL PARAMETERS ---

    private final Configurator configurator;
    private final GmailNotifierWindow window;

    private final String username;
    private final String password;
    private final FilterMask[] users;
    private final long pollingTimeout;
    private final String mailtermSubject;

    private final SimpleDateFormat dateFormatter;

    // --- CONSTRUCTOR ---

    public GmailNotifier(ThreadGroup mainGroup, Configurator configurator) throws Exception {
        super(mainGroup, "Gmail notifier");
        this.configurator = configurator;

        // Get Gmail username
        username = configurator.getConfigProperty(Configurator.NOTIFIER_GOOGLE_USERNAME, null);
        if (username == null) {
            throw new NullPointerException("Missing username (" + Configurator.NOTIFIER_GOOGLE_USERNAME + ")!");
        }

        // Acceptable local usernames
        users = configurator.getFilterProperty(Configurator.NOTIFIER_LOCAL_USERS);

        // Debug username filters
        if (log.isDebugEnabled()) {
            if (users == null) {
                log.debug("Notifier accepts all local users.");
            } else {
                log.debug("Allowed local users: "
                        + configurator.getConfigProperty(Configurator.NOTIFIER_LOCAL_USERS, "*"));
            }
        }

        // Get Gmail password
        password = configurator.getPasswordProperty(Configurator.NOTIFIER_GOOGLE_PASSWORD);

        // Get mailbox polling interval
        long timeout = configurator.getConfigProperty(Configurator.NOTIFIER_POLLING_MAILBOX, 600000L);
        if (timeout < 60000L) {
            log.warn("The fastest Gmail feed polling period is '1 min'!");
            timeout = 60000L;
        }
        pollingTimeout = timeout;

        // Get date format
        dateFormatter = new SimpleDateFormat(
                configurator.getConfigProperty(Configurator.NOTIFIER_DATE_FORMAT, "yyyy.MM.dd HH:mm:ss"));

        // Get window style
        String style = configurator.getConfigProperty(Configurator.NOTIFIER_WINDOW_STYLE, "default");

        // Get sound file / mode
        String sound = configurator.getConfigProperty(Configurator.NOTIFIER_WINDOW_SOUND, "beep");

        // Get mailterm subject (or null if disabled)
        boolean enableMailTerm = configurator.getConfigProperty(Configurator.MAILTERM_ENABLED, false);
        if (enableMailTerm) {
            mailtermSubject = configurator.getPasswordProperty(Configurator.MAILTERM_MAIL_SUBJECT);
        } else {
            mailtermSubject = null;
        }

        // Create window
        window = new GmailNotifierWindow(style, sound);

        // Start thread
        start();
    }

    // --- MAIL CHECKER LOOP ---

    public final void run() {
        log.info("Gmail notifier started successfully.");
        try {
            sleep(7000);
        } catch (Exception ignored) {
            return;
        }

        // Processed (displayed) mails
        HashSet processedMails = new HashSet();

        // Polling mailbox
        int i;
        for (;;) {
            try {

                // Verify local username
                if (users != null) {

                    // List active users
                    String[] activeUsers = getActiveUsers();
                    boolean enabled = false;
                    if (activeUsers != null && activeUsers.length != 0) {
                        for (i = 0; i < activeUsers.length; i++) {
                            enabled = isUserMatch(activeUsers[i]);
                            if (enabled) {
                                break;
                            }
                        }
                        if (!enabled) {

                            // Sleep for a minute
                            log.debug("Access denied for active local users.");
                            sleep(MINUTE);

                            // Restart loop (verify username)
                            continue;
                        }
                    }
                }

                // Get Gmail address book (or null)
                GmailContact[] contacts = configurator.getAddressBook();
                GmailContact contact;

                // Load feed entries
                SyndEntry[] entries = FeedUtilities.getFeedEntries(FEED_URL, username, password);
                SyndEntry entry;
                HashSet newMails = new HashSet();
                for (i = 0; i < entries.length; i++) {
                    entry = entries[i];
                    String date = getDate(entry);
                    String from = getFrom(entry);
                    if (contacts != null) {
                        for (int n = 0; n < contacts.length; n++) {
                            contact = contacts[n];
                            if (from.equalsIgnoreCase(contact.email)) {
                                from = contact.name;
                                break;
                            }
                        }
                    }
                    String title = getTitle(entry);
                    if (mailtermSubject != null) {
                        if (title.equals(mailtermSubject) || title.equals("Re:" + mailtermSubject)) {

                            // Do not display mailterm commands and responses
                            continue;
                        }
                    }
                    String summary = getSummary(entry);
                    newMails.add(date + '\t' + from + '\t' + title + '\t' + summary);
                }

                // Remove readed mails
                Iterator iterator = processedMails.iterator();
                Object key;
                while (iterator.hasNext()) {
                    key = iterator.next();
                    if (!newMails.contains(key)) {
                        iterator.remove();
                    }
                }

                // Look up unprocessed mails
                LinkedList unprocessedMails = new LinkedList();
                iterator = newMails.iterator();
                while (iterator.hasNext()) {
                    key = iterator.next();
                    if (processedMails.contains(key)) {
                        continue;
                    }
                    processedMails.add(key);
                    unprocessedMails.addLast(key);
                }

                // Display unprocessed mails
                if (!unprocessedMails.isEmpty()) {

                    String[] array = new String[unprocessedMails.size()];
                    unprocessedMails.toArray(array);
                    Arrays.sort(array, String.CASE_INSENSITIVE_ORDER);
                    window.show(array);
                }

                // Sleep
                sleep(pollingTimeout);

            } catch (InterruptedException interrupt) {

                // Dispose window
                if (window != null) {
                    try {
                        window.setVisible(false);
                    } catch (Exception ignored) {
                    }
                }
                break;
            } catch (Exception loadError) {
                log.error("Unable to load Gmail feed!", loadError);
                try {
                    sleep(HOUR);
                } catch (Exception interrupt) {
                    return;
                }
            }
        }
    }

    private final boolean isUserMatch(String string) {
        for (int i = 0; i < users.length; i++) {
            if (users[i].match(string)) {
                return true;
            }
        }
        return false;
    }

    private final String getDate(SyndEntry entry) throws Exception {
        Date date = entry.getPublishedDate();
        if (date == null) {
            date = entry.getUpdatedDate();
        }
        if (date == null) {
            date = new Date();
        }
        return dateFormatter.format(date);
    }

    private static final String getFrom(SyndEntry entry) throws Exception {
        List list = entry.getAuthors();
        String from = null;
        if (list != null && !list.isEmpty()) {
            try {
                SyndPerson person = (SyndPerson) list.get(0);
                from = person.getEmail();
            } catch (Exception e) {
                from = null;
            }
        }
        if (from == null) {
            from = entry.getAuthor();
        }
        if (from == null) {
            from = "-";
        }
        return from;
    }

    private static final String getTitle(SyndEntry entry) throws Exception {
        String title = entry.getTitle();
        if (title == null || title.length() == 0) {
            title = "Mail from " + getFrom(entry);
        }
        return title;
    }

    private static final String getSummary(SyndEntry entry) throws Exception {
        SyndContent syndContent = entry.getDescription();
        String summary = null;
        if (syndContent != null) {
            String content = syndContent.getValue();
            if (content != null && content.length() != 0 && content.indexOf('<') == -1) {
                summary = content.trim();
            }
        }
        if (summary == null) {
            summary = "-";
        } else {
            try {
                HtmlTextConstruct html = new HtmlTextConstruct(summary);
                summary = html.getPlainText();
                html = new HtmlTextConstruct(summary);
                summary = html.getPlainText();
                summary = summary.replace('\r', ' ').replace('\n', ' ');
            } catch (Exception ignored) {
                summary = "-";
            }
        }
        return summary;
    }

    // --- LIST OF ACTIVE USERS ---

    private static final String[] TASK_COMMAND = { "tasklist", "/V", "/NH", "/FO", "CSV" };
    private static boolean commandExecutable = true;

    private static final String[] getActiveUsers() {
        HashSet users = new HashSet();
        try {
            String me = System.getProperty("user.name");
            if (me != null) {
                users.add(me);
            }
        } catch (Exception ignored) {
        }
        try {
            String os = System.getProperty("os.name", "unknown");
            if (commandExecutable && os.toLowerCase().indexOf("windows") != -1) {

                // Execute script
                ProcessBuilder builder = new ProcessBuilder(TASK_COMMAND);
                Process tasklist = builder.start();

                // Read command output
                InputStream in = tasklist.getInputStream();
                QuickWriter buffer = new QuickWriter();
                BufferedInputStream bis = new BufferedInputStream(in);
                InputStreamReader isr = new InputStreamReader(bis);
                char[] chars = new char[1024];
                int len;
                while ((len = isr.read(chars)) != -1) {
                    buffer.write(chars, 0, len);
                }

                // Parse output
                String token, out = buffer.toString();
                StringTokenizer lines = new StringTokenizer(out, "\r\n");
                StringTokenizer tokens;
                int i;
                while (lines.hasMoreTokens()) {
                    tokens = new StringTokenizer(lines.nextToken(), "\"", false);
                    while (tokens.hasMoreTokens()) {
                        token = tokens.nextToken();
                        i = token.indexOf('\\');
                        if (i != -1) {
                            token = token.substring(i + 1);
                            if (token.length() != 0) {
                                users.add(token);
                                break;
                            }
                        }
                    }
                }

            }
        } catch (Exception invalidSyntax) {
            commandExecutable = false;
            log.debug(invalidSyntax);
        }
        String[] array = new String[users.size()];
        if (array.length > 0) {
            users.toArray(array);
            if (log.isDebugEnabled()) {
                QuickWriter writer = new QuickWriter(100);
                for (int i = 0; i < array.length; i++) {
                    writer.write(array[i]);
                    if (i < array.length - 1) {
                        writer.write(", ");
                    }
                }
                log.debug("Active users: " + writer.toString());
            }
        }
        return array;
    }

    // --- STOP SERVICE ---

    public final void interrupt() {

        // Dispose window
        if (window != null) {
            try {
                window.setVisible(false);
            } catch (Exception ignored) {
            }
        }

        // Interrupt thread
        super.interrupt();
    }

}