
import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;

import javax.mail.*;
import javax.activation.*;
import javax.mail.internet.*;

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpClient;
import org.apache.util.HttpURL;
import org.apache.util.QName;
import org.apache.webdav.lib.Ace;
import org.apache.webdav.lib.Lock;
import org.apache.webdav.lib.Privilege;
import org.apache.webdav.lib.Property;
import org.apache.webdav.lib.PropertyName;
import org.apache.webdav.lib.ResponseEntity;
import org.apache.webdav.lib.WebdavResource;
import org.apache.webdav.lib.methods.DepthSupport;
import org.apache.webdav.lib.properties.AclProperty;
import org.apache.webdav.lib.properties.LockDiscoveryProperty;
import org.apache.webdav.lib.properties.PrincipalCollectionSetProperty;
import org.apache.webdav.lib.WebdavResource;
import org.apache.util.HttpURL;

 /**
  * 
  * This class is designed to check which documents within the Apache Slide Webdav server have
  * changed, and if they have changed then to send notification emails to all users of the 
  * system who have registered to receive notification for that document. The user interface for
  * registering to receive email notifications can be found at 
  *   http://www.ella-associates.org/cocoon/livedocs/
  *
  * COMMANDLINE USAGE:
  *    java AlexisAutomaticChangeNotification update-interval
  *
  *    The parameter 'update-interval' represents the number of minutes which will be used to check
  *    if the files in the Alexis system have changed. In other words, if a document has changed more
  *    recently than this 'update-interval' then an email notification will be sent to every user of 
  *    the system who has registered to receive notifications for that particular document.
  *    
  * This program relies on the fact that the data file is 'sorted' alphabetically. The data file
  * is in the format 
  *    emailaddress:path/to/document/document.name
  *
  * The user interface which is controlled by the cgi script "/home/cgi-bin/alexis-change-notification.cgi"
  * adds new entries in this format, removes duplicate entries and sorts the file so that all entries
  * with the same email address are grouped together. If the data file is not in this format then this
  * script will either not work or will send multiple emails to each recipient, with each email informing
  * that person of a different document which has been modified. This is not a desirable behaviour. 
  *
  * The log file for this program is located at
  *    /var/www/utils/alexis-notifications.log
  *
  * On the American Redhat Linux Server rented by ella-associates.org it is necessary 
  * to place the following paths on the java classpath
  *   /var/jakarta-slide-1.0.16/client/lib/webdav.jar:/var/jakarta-slide-1.0.16/client/lib/commons-httpclient.jar:
  *   /var/jakarta-slide-1.0.16/client/lib/webdavlib.jar:/var/www/utils/ 
  *
  * @author mjb
  * @see "http://www.ella-associates.org/utils/alexis-change-notification.cgi"
  * @see "http://www.ella-associates.org/utils/TestJavaMail.java"
  * @see "http://www.ella-associates.org/utils/GetDavFile.java"
  * @see "http://www.ella-associates.org/utils/DebugDavFile.java"
  */
public class AlexisAutomaticChangeNotification 
{
  public static void main (String[]args) throws Exception
  {

    String sMailServerHost = "www.ella-associates.org";
    String sFilePath = "addresses.txt";
    String sRecipientAddress = "";
    String sPreviousRecipientAddress = "";
    String sMessageSubject = "Alexis document change automatic notification";
    StringBuffer sbMessageBody = new StringBuffer("");
    boolean bMessageRequired = false;       //-- determines if an email needs to be sent to a 
                                            //-- particular user
    
    StringBuffer sbDocumentList = new StringBuffer("");  //-- A list of all the documents which a particular
                                                         //-- user is registered to receive.

    String sDataFile = "/home/cgi-bin/alexis-change-registrations.txt";
    String sLogFile = "/var/www/utils/alexis-notifications.log";

    String sWebdavUser = "matthew";        //-- Username to use to connect to Webdav Server
    String sWebdavPassword = "bish";       //-- Password to connect to Webdav Server
    String sWebdavServer = "http://ella-associates.org:8081/";
    String sWebdavDocumentName = "";       //-- Webdav document to check for changes

    String sTextLine = new String("");     //-- 
    long lNotificationInterval = 1200000;   //-- How often email notifications are sent. This is the 
                                           //-- inteval between each notification (probably in milliseconds)
                                           //-- It is very important that this value synchronises (is the same)
					   //-- as the value in the file /etc/crontab which is the file on
					   //-- the American server which schedules the execution of this 
					   //-- program. In the crontab file the value will not be expressed
					   //-- in milliseconds but it needs to be equivalent
					   //-- 1200000 = 20 minutes
    boolean bDebug = false;

    //-- Append messages to the log file
    PrintStream out = new PrintStream(new FileOutputStream(sLogFile, true));

    if (args.length == 1)
    {
      //
      //-- Assign the notification interval based on a paramter which represents
      //-- the number of minutes
      try
      {
        lNotificationInterval = Long.parseLong(args[0]) * 60 * 1000;
      } 
      catch (NumberFormatException e)
      {
	System.out.println("  The parameter for the duration of the update-interval is not in the ");
	System.out.println("  correct format. This parameter should be a number representing the number of ");
	System.out.println("  of minutes which the program will use as the interval." );
	System.out.println("  Type: java AlexisAutomaticChangeNotification   for more help " );
	System.exit(-1);
      }
    }
    else 
    {
      displayHelpInformation();
      System.exit (-1);
       
    }  //-- if, else [Command Line Arguments]

  
    String host = "ella-associates.org";
    String sSenderAddress = "automatic@ella-associates.org";
    //-- Establish when the last email notification occurred.
    Date dtNow = new Date();
    Date dtLastNotification = new Date((dtNow.getTime()) - lNotificationInterval);

    
    // Setup mail server
    Properties props = new Properties();
    props.put("mail.smtp.host", host);
    props.put("mail.smtp.auth", "true");
		 
    javax.mail.Authenticator auth = new SimpleSmtpAuthenticator();
    Session session = Session.getDefaultInstance(props, auth);
    // session.setDebugOut(null);

    HttpURL httpURL = new HttpURL(sWebdavServer);
    httpURL.setUserInfo(sWebdavUser, sWebdavPassword);
    WebdavResource webdavResource = new WebdavResource(httpURL);

    BufferedReader brdTextFile = 
      new BufferedReader(new FileReader(sDataFile));


    sTextLine = brdTextFile.readLine();
    while (sTextLine != null)
    {

      sPreviousRecipientAddress = sRecipientAddress;
      sRecipientAddress = sTextLine.replaceAll("\\s*([^:]*):.*$", "$1");
      sWebdavDocumentName = sTextLine.replaceAll("^.*:([^\\s]*)\\s*$", "$1");

      if (bDebug)
      {	
	out.println("   ");
	out.println("   sTextLine=" + sTextLine);
	out.println("   sPreviousRecipientAddress=" + sPreviousRecipientAddress);
	out.println("   sRecipientAddress=" + sRecipientAddress);
	out.println("   sWebdavDocumentName=" + sWebdavDocumentName);
	out.println("   sbMessageBody=" + sbMessageBody.toString());
      } //-- if debug

      //-- If any documents have changed then send a notification message, but only
      //-- send one notification email per address.
      if (!sRecipientAddress.equals(sPreviousRecipientAddress))
      {	
	if ( bMessageRequired)
	{
	  //-- Add header and footer information to the message body eg: all registrations
	  sbMessageBody.insert(0, 
	    "This email has been sent to you automatically in order to inform you  \n" +
	    "that certain documents in the Alexis documentation system have changed. \n" +
	    "If you would like to no longer receive these \n" +
	    "notifications, please so the information below. \n\n" +
	    "The list of documents which have changed is as follows \n" + 
	    "------------------------------------------------------ \n");

	  sbMessageBody.append(
	    "\n\n" + 
	    "You are currently registered to receive notification for the following documents: \n" +
	    "----------------------------------------------------------------------------- \n" +
	    sbDocumentList.toString() + "\n\n" +
	    "If you would like to no longer receive email notifications for all or some of these \n" +
	    "document, or register to receive notifications for different documents you need to \n" +
	    "go to the web-page http://www.ella-associates.org/cocoon/livedocs/  and then the \n" +
	    "documents for which you are registered by navigating the folder tree. Then click on the \n" +
	    "button which says something like 'Register or manage notifications' "
	    );
	    
	  MimeMessage mmNotificationEmail = new MimeMessage(session);
	  mmNotificationEmail.setFrom(new InternetAddress(sSenderAddress));
	  mmNotificationEmail.addRecipient(Message.RecipientType.TO, new InternetAddress(sPreviousRecipientAddress));
	  mmNotificationEmail.setSubject(sMessageSubject);
	  mmNotificationEmail.setText(sbMessageBody.toString());
	  
	  out.println(">>> SENDING EMAIL to " + sPreviousRecipientAddress + " " + dtNow);

	  Transport.send(mmNotificationEmail);
	  //out.println("   Just attempted to send message");
	  //-- set message body back here	

	  bMessageRequired = false;
	}  //-- if there is a message to send
	sbMessageBody.delete(0, sbMessageBody.length());
	sbDocumentList.delete(0, sbDocumentList.length());

        out.println(dtNow + " Examining documents for user: " + sRecipientAddress);
      }  //-- if new recipient
      
      sbDocumentList.append(sWebdavDocumentName + "\n");

      if (! sWebdavDocumentName.equals(""))
      {
        webdavResource.setPath(sWebdavDocumentName);            

	if (!webdavResource.exists())
	{
	  out.println("  WARNING: The WebDav Document '" + sWebdavDocumentName + "' does not exist ");
	  out.println("         : You should edit the file '" + sDataFile + "' and delete references to this  ");
	  out.println("         : document");
	}
	else if (webdavResource.isCollection())
	{
	  out.println("  NOTE: The WebDav Resource '" + sWebdavDocumentName + "' is a folder   ");
	  out.println("      : This program doesnot support checking folders for modification  ");
	  out.println("      : You probably should edit the file '" + sDataFile + "' and delete references to this  ");
	  out.println("      : document");
	  
	}
	else
	{

	  Date dtLastModified = new Date(webdavResource.getGetLastModified());
	  if (bDebug)
	  {
	    out.println("   dtLastModified=" + dtLastModified);
	    out.println("   dtLastNotification=" + dtLastNotification);
	    out.println("   dtNow=" + dtNow);
	  } //-- if debug

	  if (dtLastModified.after(dtLastNotification))
	  {
	    bMessageRequired = true;
	    sbMessageBody.append("   " + sWebdavDocumentName + "\n");
	  } //-- if
	} //-- if document exists and is a file
      } //-- if

      sTextLine = brdTextFile.readLine();
    }  //-- while more lines

 
    //-- This block is required otherwise the last recipient in the data file will not
    //-- receive her message
    if ( bMessageRequired)
    {
      //-- Add header and footer information to the message body eg: all registrations
      sbMessageBody.insert(0, 
	"This email has been sent to you automatically in order to inform you  \n" +
	"that certain documents in the Alexis documentation system have changed. \n" +
	"If you would like to no longer receive these \n" +
	"notifications, please so the information below. \n\n" +
	"The list of documents which have changed is as follows \n" + 
	"------------------------------------------------------ \n");

      sbMessageBody.append(
	"\n\n" + 
	"You are currently registered to receive notification for the following documents: \n" +
	"----------------------------------------------------------------------------- \n" +
	sbDocumentList.toString() + "\n\n" +
	"If you would like to no longer receive email notifications for all or some of these \n" +
	"document, or register to receive notifications for different documents you need to \n" +
	"go to the web-page http://www.ella-associates.org/cocoon/livedocs/  and then the \n" +
	"documents for which you are registered by navigating the folder tree. Then click on the \n" +
	"button which says something like 'Register or manage notifications' "
	);
	
      MimeMessage mmNotificationEmail = new MimeMessage(session);
      mmNotificationEmail.setFrom(new InternetAddress(sSenderAddress));
      mmNotificationEmail.addRecipient(Message.RecipientType.TO, new InternetAddress(sPreviousRecipientAddress));
      mmNotificationEmail.setSubject(sMessageSubject);
      mmNotificationEmail.setText(sbMessageBody.toString());
      
      out.println(">>> SENDING EMAIL to " + sPreviousRecipientAddress + " " + dtNow);

      Transport.send(mmNotificationEmail);
      //out.println("   Just attempted to send message");
      //-- set message body back here	

      bMessageRequired = false;
    }  //-- if there is a message to send
    sbMessageBody.delete(0, sbMessageBody.length());
    sbDocumentList.delete(0, sbMessageBody.length());

    out.close();
  }   // main

  static void displayHelpInformation() throws Exception
  {
    //-- The following attempts to display the javadoc from this file
    //--
    BufferedReader brdProgramSourceFile = 
      new BufferedReader (new FileReader ("/var/www/utils/AlexisAutomaticChangeNotification.java"));


    String sTextLine = brdProgramSourceFile.readLine();
    while (sTextLine != null)
    {
      if (sTextLine.matches("\\s*\\*.*"))
       { System.out.println(sTextLine); }

      sTextLine = brdProgramSourceFile.readLine();
    }
  } //-- method: displayHelpInformation
}   // class: AlexisAutomaticChangeNotification 

// I could make this an anonymous inner class I suppose, to allow parametrization of 
// the username and password
class SimpleSmtpAuthenticator extends javax.mail.Authenticator
{
  public javax.mail.PasswordAuthentication getPasswordAuthentication()
  {
    return new javax.mail.PasswordAuthentication("automatic", "amet11a");
  }
} 
