Advertisement
Guest User

TimmyDude

a guest
Apr 14th, 2008
358
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 10.10 KB | None | 0 0
  1. //
  2.  
  3. // GCALDaemon is an OS-independent Java program that offers two-way
  4.  
  5. // synchronization between Google Calendar and various iCalalendar (RFC 2445)
  6.  
  7. // compatible calendar applications (Sunbird, Rainlendar, iCal, Lightning, etc).
  8.  
  9. //
  10.  
  11. // Apache License
  12.  
  13. // Version 2.0, January 2004
  14.  
  15. // http://www.apache.org/licenses/
  16.  
  17. //
  18.  
  19. // Project home:
  20.  
  21. // http://gcaldaemon.sourceforge.net
  22.  
  23. //
  24.  
  25. package org.gcaldaemon.core.mailterm;
  26.  
  27.  
  28.  
  29. import java.io.File;
  30.  
  31. import java.nio.charset.Charset;
  32.  
  33. import java.util.LinkedList;
  34.  
  35. import java.util.Set;
  36.  
  37. import java.util.SortedMap;
  38.  
  39.  
  40.  
  41. import org.apache.commons.logging.Log;
  42.  
  43. import org.apache.commons.logging.LogFactory;
  44.  
  45. import org.gcaldaemon.core.Configurator;
  46.  
  47. import org.gcaldaemon.core.FilterMask;
  48.  
  49. import org.gcaldaemon.core.GmailEntry;
  50.  
  51. import org.gcaldaemon.core.GmailMessage;
  52.  
  53. import org.gcaldaemon.core.GmailPool;
  54.  
  55. import org.gcaldaemon.core.StringUtils;
  56.  
  57. import org.gcaldaemon.logger.QuickWriter;
  58.  
  59.  
  60.  
  61. import com.google.gdata.data.HtmlTextConstruct;
  62.  
  63.  
  64.  
  65. /**
  66.  
  67.  * Gmail-based command line interface.
  68.  
  69.  *
  70.  
  71.  * Created: Jan 03, 2007 12:50:56 PM
  72.  
  73.  *
  74.  
  75.  * @author Andras Berkes
  76.  
  77.  */
  78.  
  79. public final class MailTerminal extends Thread {
  80.  
  81.  
  82.  
  83.     // --- CONSTANTS ---
  84.  
  85.  
  86.  
  87.     private static final String QUIT_COMMAND = "quit";
  88.  
  89.  
  90.  
  91.     private static final byte COMMAND_NOT_FOUND = 0;
  92.  
  93.     private static final byte COMMAND_EXECUTED = 1;
  94.  
  95.     private static final byte QUIT_REQUESTED = 2;
  96.  
  97.  
  98.  
  99.     private static final long FAST_POLLING_TIMEOUT = 60000L;
  100.  
  101.     private static final long SCRIPT_TIMEOUT = 30000L;
  102.  
  103.  
  104.  
  105.     // --- LOGGER ---
  106.  
  107.  
  108.  
  109.     private static final Log log = LogFactory.getLog(MailTerminal.class);
  110.  
  111.  
  112.  
  113.     // --- VARIABLES ---
  114.  
  115.  
  116.  
  117.     private final Configurator configurator;
  118.  
  119.     private final long pollingTimeout;
  120.  
  121.     private final String username;
  122.  
  123.     private final String password;
  124.  
  125.     private final String subject;
  126.  
  127.     private final String encoding;
  128.  
  129.     private final File scriptDir;
  130.  
  131.  
  132.  
  133.     private final FilterMask[] addresses;
  134.  
  135.  
  136.  
  137.     // --- CONSTRUCTOR ---
  138.  
  139.  
  140.  
  141.     public MailTerminal(ThreadGroup mainGroup, Configurator configurator)
  142.  
  143.             throws Exception {
  144.  
  145.         super(mainGroup, "Mail terminal");
  146.  
  147.         this.configurator = configurator;
  148.  
  149.  
  150.  
  151.         // Get inbox polling time
  152.  
  153.         long timeout = configurator.getConfigProperty(
  154.  
  155.                 Configurator.MAILTERM_POLLING_GOOGLE, 10000L);
  156.  
  157.         if (timeout < 60000L) {
  158.  
  159.             log.warn("The fastest Gmail inbox polling period is '1 min'!");
  160.  
  161.             timeout = 60000L;
  162.  
  163.         }
  164.  
  165.         pollingTimeout = timeout;
  166.  
  167.  
  168.  
  169.         // Get Gmail user
  170.  
  171.         username = configurator.getConfigProperty(
  172.  
  173.                 Configurator.MAILTERM_GOOGLE_USERNAME, null);
  174.  
  175.         if (username == null) {
  176.  
  177.             throw new NullPointerException("Missing username ("
  178.  
  179.                     + Configurator.MAILTERM_GOOGLE_USERNAME + ")!");
  180.  
  181.         }
  182.  
  183.  
  184.  
  185.         // Get Gmail password
  186.  
  187.         password = configurator
  188.  
  189.                 .getPasswordProperty(Configurator.MAILTERM_GOOGLE_PASSWORD);
  190.  
  191.  
  192.  
  193.         // Get subject of the command mails
  194.  
  195.         subject = configurator
  196.  
  197.                 .getPasswordProperty(Configurator.MAILTERM_MAIL_SUBJECT);
  198.  
  199.  
  200.  
  201.         // Get script directory
  202.  
  203.         String path = configurator.getConfigProperty(
  204.  
  205.                 Configurator.MAILTERM_DIR_PATH, "/scripts");
  206.  
  207.         scriptDir = new File(path);
  208.  
  209.         if (!scriptDir.isDirectory()) {
  210.  
  211.             scriptDir.mkdirs();
  212.  
  213.             if (!scriptDir.isDirectory()) {
  214.  
  215.                 throw new Exception("Unable to read script directory (" + path
  216.  
  217.                         + ")! Permission denied!");
  218.  
  219.             }
  220.  
  221.         }
  222.  
  223.  
  224.  
  225.         // Get native console encoding
  226.  
  227.         String consoleEncoding = configurator.getConfigProperty(
  228.  
  229.                 Configurator.MAILTERM_CONSOLE_ENCODING, StringUtils.US_ASCII);
  230.  
  231.         try {
  232.  
  233.             StringUtils.US_ASCII.getBytes(consoleEncoding);
  234.  
  235.         } catch (Exception unsupportedEncoding) {
  236.  
  237.  
  238.  
  239.             // Dump supported encodings
  240.  
  241.             SortedMap map = Charset.availableCharsets();
  242.  
  243.             if (map != null) {
  244.  
  245.                 Set set = map.keySet();
  246.  
  247.                 if (set != null) {
  248.  
  249.                     String[] array = new String[set.size()];
  250.  
  251.                     set.toArray(array);
  252.  
  253.                     QuickWriter writer = new QuickWriter();
  254.  
  255.                     writer.write("Invalid charset (");
  256.  
  257.                     writer.write(consoleEncoding);
  258.  
  259.                     writer.write(")! Supported console encodings:\r\n");
  260.  
  261.                     for (int i = 0; i < array.length; i++) {
  262.  
  263.                         writer.write(array[i]);
  264.  
  265.                         if (i < array.length - 1) {
  266.  
  267.                             writer.write(", ");
  268.  
  269.                         }
  270.  
  271.                         if (i % 6 == 5) {
  272.  
  273.                             writer.write("\r\n");
  274.  
  275.                         }
  276.  
  277.                     }
  278.  
  279.                     log.warn(writer.toString().trim());
  280.  
  281.                 }
  282.  
  283.             }
  284.  
  285.             consoleEncoding = StringUtils.US_ASCII;
  286.  
  287.         }
  288.  
  289.         encoding = consoleEncoding;
  290.  
  291.  
  292.  
  293.         // Get acceptable e-mail addresses
  294.  
  295.         addresses = configurator.getFilterProperty(
  296.  
  297.                 Configurator.MAILTERM_ALLOWED_ADDRESSES, true);
  298.  
  299.  
  300.  
  301.         // Start listener
  302.  
  303.         log.info("Mailterm service started successfully.");
  304.  
  305.         start();
  306.  
  307.     }
  308.  
  309.  
  310.  
  311.     // --- DIRECTORY LISTENER LOOP ---
  312.  
  313.  
  314.  
  315.     public final void run() {
  316.  
  317.         try {
  318.  
  319.             sleep(5000L);
  320.  
  321.         } catch (InterruptedException interrupt) {
  322.  
  323.             log.info("Mailterm service stopped.");
  324.  
  325.             return;
  326.  
  327.         }
  328.  
  329.         for (;;) {
  330.  
  331.             try {
  332.  
  333.  
  334.  
  335.                 // Borrow pooled Gmail connection
  336.  
  337.                 GmailPool pool = configurator.getGmailPool();
  338.  
  339.                 byte responseType = COMMAND_NOT_FOUND;
  340.  
  341.                 GmailEntry entry = null;
  342.  
  343.                 try {
  344.  
  345.                     entry = pool.borrow(username, password);
  346.  
  347.  
  348.  
  349.                     // Receive mails
  350.  
  351.                     responseType = receiveMails(entry);
  352.  
  353.                 } finally {
  354.  
  355.  
  356.  
  357.                     // Recycle pooled connection
  358.  
  359.                     pool.recycle(entry);
  360.  
  361.                 }
  362.  
  363.  
  364.  
  365.                 // Shutdown mailterm
  366.  
  367.                 if (responseType == QUIT_REQUESTED) {
  368.  
  369.                     throw new InterruptedException();
  370.  
  371.                 }
  372.  
  373.  
  374.  
  375.                 // Wait
  376.  
  377.                 if (responseType == COMMAND_NOT_FOUND) {
  378.  
  379.                     sleep(pollingTimeout);
  380.  
  381.                 } else {
  382.  
  383.                     sleep(FAST_POLLING_TIMEOUT);
  384.  
  385.                 }
  386.  
  387.  
  388.  
  389.             } catch (InterruptedException interrupt) {
  390.  
  391.                 log.info("Mailterm service stopped.");
  392.  
  393.                 return;
  394.  
  395.             } catch (Exception poolException) {
  396.  
  397.                 log.warn("Unexpected mailterm error!", poolException);
  398.  
  399.                 log
  400.  
  401.                         .debug("Please verify your username/password and IMAP settings!");
  402.  
  403.                 try {
  404.  
  405.                     sleep(pollingTimeout);
  406.  
  407.                 } catch (InterruptedException interrupt) {
  408.  
  409.                     log.info("Mailterm service stopped.");
  410.  
  411.                     return;
  412.  
  413.                 }
  414.  
  415.             }
  416.  
  417.         }
  418.  
  419.     }
  420.  
  421.  
  422.  
  423.     // --- MAIL RECEIVER ---
  424.  
  425.  
  426.  
  427.     private final byte receiveMails(GmailEntry client) throws Exception {
  428.  
  429.  
  430.  
  431.         // Find new mails
  432.  
  433.         log.debug("Searching commands in mailbox...");
  434.  
  435.         GmailMessage[] unreadMails = client.receive(subject);
  436.  
  437.         if (unreadMails == null || unreadMails.length == 0) {
  438.  
  439.             log.debug("Mailbox is empty or subject not found.");
  440.  
  441.             return COMMAND_NOT_FOUND;
  442.  
  443.         }
  444.  
  445.  
  446.  
  447.         // Read mails
  448.  
  449.         byte responseType = COMMAND_NOT_FOUND;
  450.  
  451.         GmailMessage message;
  452.  
  453.         for (int i = 0; i < unreadMails.length; i++) {
  454.  
  455.             message = unreadMails[i];
  456.  
  457.  
  458.  
  459.             // Get reply address
  460.  
  461.             String replyAddress = message.from;
  462.  
  463.  
  464.  
  465.             // Check access by reply address
  466.  
  467.             if (addresses != null) {
  468.  
  469.                 if (!isAddressMatch(replyAddress)) {
  470.  
  471.                     log.warn("Request refused, forbidden e-mail address ("
  472.  
  473.                             + replyAddress + ")!");
  474.  
  475.                     continue;
  476.  
  477.                 }
  478.  
  479.             }
  480.  
  481.  
  482.  
  483.             // Get command
  484.  
  485.             String command = message.memo;
  486.  
  487.             if (command == null) {
  488.  
  489.                 log.debug("Missing command body!");
  490.  
  491.                 continue;
  492.  
  493.             }
  494.  
  495.             HtmlTextConstruct html = new HtmlTextConstruct(command);
  496.  
  497.             command = html.getPlainText();
  498.  
  499.             command = command.replace('\r', ' ').replace('\n', ' ').trim();
  500.  
  501.             if (command.length() == 0) {
  502.  
  503.                 log.debug("Missing command!");
  504.  
  505.                 continue;
  506.  
  507.             }
  508.  
  509.             log.debug("Executing command from " + replyAddress + " (" + command
  510.  
  511.                     + ")...");
  512.  
  513.             String reply;
  514.  
  515.             if (command.equals(QUIT_COMMAND)) {
  516.  
  517.  
  518.  
  519.                 // Shutdown requested
  520.  
  521.                 reply = "Mailterm service terminated. Bye!";
  522.  
  523.                 responseType = QUIT_REQUESTED;
  524.  
  525.             } else {
  526.  
  527.  
  528.  
  529.                 // Parse command
  530.  
  531.                 String[] args = parseLine(command);
  532.  
  533.  
  534.  
  535.                 // Execute script
  536.  
  537.                 ScriptRunner runner = new ScriptRunner(scriptDir, encoding,
  538.  
  539.                         args);
  540.  
  541.                 runner.join(SCRIPT_TIMEOUT);
  542.  
  543.                 reply = runner.getScriptOutput();
  544.  
  545.                 if (responseType != QUIT_REQUESTED) {
  546.  
  547.                     responseType = COMMAND_EXECUTED;
  548.  
  549.                 }
  550.  
  551.             }
  552.  
  553.  
  554.  
  555.             // Send reply
  556.  
  557.             log.debug("Command output:\r\n" + reply);
  558.  
  559.             client.send(replyAddress, null, null, "Re:" + subject, "<pre>"
  560.  
  561.                     + reply.trim() + "</pre>", true);
  562.  
  563.  
  564.  
  565.             // Wait
  566.  
  567.             Thread.sleep(500);
  568.  
  569.         }
  570.  
  571.         return responseType;
  572.  
  573.     }
  574.  
  575.  
  576.  
  577.     private final boolean isAddressMatch(String string) {
  578.  
  579.         for (int i = 0; i < addresses.length; i++) {
  580.  
  581.             if (addresses[i].match(string)) {
  582.  
  583.                 return true;
  584.  
  585.             }
  586.  
  587.         }
  588.  
  589.         return false;
  590.  
  591.     }
  592.  
  593.  
  594.  
  595.     // --- COMMAND LINE PARSER ---
  596.  
  597.  
  598.  
  599.     /**
  600.  
  601.      * Splitting a string into a command-array.
  602.  
  603.      *
  604.  
  605.      * <BR>
  606.  
  607.      * <BR>
  608.  
  609.      * word1 word2 word3 -> "word1", "word2", "word3" <BR>
  610.  
  611.      * word1 word2="word3 'abc' def" -> "word1", "word2", "word3 'abc' def" <BR>
  612.  
  613.      * 'wo"rd1'="word2" word3 -> "wo\"rd1", "word2", "word3" <BR>
  614.  
  615.      * etc.
  616.  
  617.      *
  618.  
  619.      * @param cmdLine
  620.  
  621.      * @return String[]
  622.  
  623.      */
  624.  
  625.     private static final String[] parseLine(String cmdLine) {
  626.  
  627.         char delimiter = ' ';
  628.  
  629.         boolean inToken = false;
  630.  
  631.         QuickWriter writer = new QuickWriter(100);
  632.  
  633.         LinkedList tokens = new LinkedList();
  634.  
  635.         for (int i = 0; i < cmdLine.length(); i++) {
  636.  
  637.             char c = cmdLine.charAt(i);
  638.  
  639.             if (inToken) {
  640.  
  641.                 if (c == delimiter || (delimiter == ' ' && c == '=')) {
  642.  
  643.                     tokens.add(writer.toString());
  644.  
  645.                     writer.flush();
  646.  
  647.                     if (c == '-') {
  648.  
  649.                         writer.write(c);
  650.  
  651.                     }
  652.  
  653.                     inToken = false;
  654.  
  655.                     continue;
  656.  
  657.                 }
  658.  
  659.                 writer.write(c);
  660.  
  661.             } else {
  662.  
  663.                 if (c == '\'') {
  664.  
  665.                     delimiter = '\'';
  666.  
  667.                     inToken = true;
  668.  
  669.                 } else {
  670.  
  671.                     if (c == '"') {
  672.  
  673.                         delimiter = '"';
  674.  
  675.                         inToken = true;
  676.  
  677.                     } else if (c == ' ') {
  678.  
  679.  
  680.  
  681.                         // Skip
  682.  
  683.                     } else {
  684.  
  685.                         delimiter = ' ';
  686.  
  687.                         writer.write(c);
  688.  
  689.                         inToken = true;
  690.  
  691.                     }
  692.  
  693.                 }
  694.  
  695.             }
  696.  
  697.             if (i == cmdLine.length() - 1 && writer.length() != 0) {
  698.  
  699.                 tokens.add(writer.toString());
  700.  
  701.             }
  702.  
  703.         }
  704.  
  705.         String[] array = new String[tokens.size()];
  706.  
  707.         tokens.toArray(array);
  708.  
  709.         return array;
  710.  
  711.     }
  712.  
  713.  
  714.  
  715. }
  716.  
  717.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement