/*
* Sun Public License
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is available at http://www.sun.com/
*
* The Original Code is the SLAMD Distributed Load Generation Engine.
* The Initial Developer of the Original Code is Neil A. Wilson.
* Portions created by Neil A. Wilson are Copyright (C) 2004-2010.
* Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): Neil A. Wilson
*/
package com.slamd.scripting.mail;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.StringTokenizer;
import javax.net.ssl.SSLSocket;
import com.slamd.jobs.JSSEBlindTrustSocketFactory;
import com.slamd.job.JobClass;
import com.slamd.scripting.engine.Argument;
import com.slamd.scripting.engine.Method;
import com.slamd.scripting.engine.ScriptException;
import com.slamd.scripting.engine.Variable;
import com.slamd.scripting.general.BooleanVariable;
import com.slamd.scripting.general.IntegerVariable;
import com.slamd.scripting.general.StringArrayVariable;
import com.slamd.scripting.general.StringVariable;
/**
* This class defines a variable that maintains a connection to an IMAPv4rev1
* mail server and allows for interaction with that server. An IMAP connection
* has the the following methods:
*
* <UL>
* <LI>authenticate(string userID, string password) -- Authenticates to the
* IMAP server. This method returns a Boolean value indicating whether
* the authentication was successful.</LI>
* <LI>capability() -- Retrieves a string array containing the list of
* capability values reported by the IMAP server.</LI>
* <LI>connect(string host, int port) -- Establishes an IMAP connection to the
* mail server. This method returns a Boolean value indicating whether
* the connection was established successfully.</LI>
* <LI>connect(string host, int port, boolean useSSL) -- Establishes an IMAP
* connection to the mail server, optionally using SSL for secure
* communication. This method returns a Boolean value indicating whether
* the connection was established successfully.</LI>
* <LI>copy(int messageID, string folderName) -- Copies the specified message
* into the indicated folder. This method returns a Boolean value
* indicating whether the message was copied successfully.</LI>
* <LI>countMessages() -- Retrieves the number of messages in the selected
* folder as an integer value.</LI>
* <LI>countUnreadMessages() -- Retrieves the number of unread in the selected
* folder as an integer value.</LI>
* <LI>createFolder(string folderName) -- Creates a new folder with the
* specified name. This method returns a Boolean value indicating whether
* the folder was created successfully.</LI>
* <LI>delete(int messageID) -- Attempts to delete the specified message from
* the current mailbox in the IMAP server. This method returns a Boolean
* value indicating whether the delete was successful.</LI>
* <LI>deleteFolder(string folderName) -- Deletes the folder with the
* specified name. This method returns a Boolean value indicating whether
* the folder was deleted successfully.</LI>
* <LI>disconnect() -- Closes the connection to the IMAP server. This method
* does not return a value.</LI>
* <LI>expunge() -- Indicates that the IMAP server should actually remove any
* deleted messages from the user's mailbox. This method returns a
* Boolean value indicating whether the expunge was successful.</LI>
* <LI>getFailureReason() -- Retrieves a string that provides information
* about the reason for the last failure, if that is available.</LI>
* <LI>listFolders() -- Retrieves a list of all folders in the user's mailbox
* as a string array.</LI>
* <LI>listMessages() -- Retrieves a list of all messages in the selected
* folder in the user's mailbox as a string array.</LI>
* <LI>listNewMessages() -- Retrieves a list of unread messages in the
* selected folder in the user's mailbox as a string array.</LI>
* <LI>move(int messageID, string folderName) -- Moves the specified message
* from the current folder to the specified folder. This method returns a
* Boolean value indicating whether the move was successful.</LI>
* <LI>noOp() -- Sends a NOOP command to the server, which has no effect but
* to prevent the connection from remaining idle for too long. This
* method does not return a value.</LI>
* <LI>retrieve(int messageID) -- Retrieves the specified message from the
* user's mailbox. The message will be returned as a mail message
* object.</LI>
* <LI>selectFolder(string folderName) -- Indicates that the specified folder
* should become the current folder for subsequent operations. This
* method returns a Boolean value indicating whether the operation was
* successful.</LI>
* <LI>sendCommand(string command) -- Sends the specified command to the IMAP
* server and retrieves a string array containing the individual lines of
* the response from the server. The command sent to the server should
* not include an identifier at the beginning, as an appropriate
* identifer will be added automatically.</LI>
* </UL>
*
*
* @author Neil A. Wilson
*/
public class IMAPConnectionVariable
extends Variable
{
/**
* The name that will be used for the data type of IMAP connection variables.
*/
public static final String IMAP_CONNECTION_VARIABLE_TYPE = "imapconnection";
/**
* The name of the method that performs an IMAP authentication.
*/
public static final String AUTHENTICATE_METHOD_NAME = "authenticate";
/**
* The method number for the "authenticate" method.
*/
public static final int AUTHENTICATE_METHOD_NUMBER = 0;
/**
* The name of the method that retrieves the capability list from the IMAP
* server.
*/
public static final String CAPABILITY_METHOD_NAME = "capability";
/**
* The method number for the "capability" method.
*/
public static final int CAPABILITY_METHOD_NUMBER = 1;
/**
* The name of the method that establishes a connection to the IMAP server.
*/
public static final String CONNECT_METHOD_NAME = "connect";
/**
* The method number for the first "connect" method.
*/
public static final int CONNECT_1_METHOD_NUMBER = 2;
/**
* The method number for the second "connect" method.
*/
public static final int CONNECT_2_METHOD_NUMBER = 3;
/**
* The name of the method that can be used to copy a message into another
* folder.
*/
public static final String COPY_METHOD_NAME = "copy";
/**
* The method number for the "copy" method.
*/
public static final int COPY_METHOD_NUMBER = 4;
/**
* The name of the method that can be used to retrieve the number of messages
* in the current folder.
*/
public static final String COUNT_MESSAGES_METHOD_NAME = "countmessages";
/**
* The method number for the "countMessages" method.
*/
public static final int COUNT_MESSAGES_METHOD_NUMBER = 5;
/**
* The name of the method that can be used to retrieve the number of unread
* messages in the current folder.
*/
public static final String COUNT_UNREAD_MESSAGES_METHOD_NAME =
"countunreadmessages";
/**
* The method number for the "countUnreadMessages" method.
*/
public static final int COUNT_UNREAD_MESSAGES_METHOD_NUMBER = 6;
/**
* The name of the method that can be used to create a new mailbox folder.
*/
public static final String CREATE_FOLDER_METHOD_NAME = "createfolder";
/**
* The method number for the "createFolder" method.
*/
public static final int CREATE_FOLDER_METHOD_NUMBER = 7;
/**
* The name of the method that can be used to delete a message from the IMAP
* server.
*/
public static final String DELETE_METHOD_NAME = "delete";
/**
* The method number for the "delete" method.
*/
public static final int DELETE_METHOD_NUMBER = 8;
/**
* The name of the method that can be used to delete a mailbox folder.
*/
public static final String DELETE_FOLDER_METHOD_NAME = "deletefolder";
/**
* The method number for the "deleteFolder" method.
*/
public static final int DELETE_FOLDER_METHOD_NUMBER = 9;
/**
* The name of the method that can be used to disconnect from the IMAP server.
*/
public static final String DISCONNECT_METHOD_NAME = "disconnect";
/**
* The method number for the "disconnect" method.
*/
public static final int DISCONNECT_METHOD_NUMBER = 10;
/**
* The name of the method that can be used to expunge deleted messages from
* the mailbox.
*/
public static final String EXPUNGE_METHOD_NAME = "expunge";
/**
* The method number for the "expunge" method.
*/
public static final int EXPUNGE_METHOD_NUMBER = 11;
/**
* The name of the method that can be used to determine the reason for the
* last failure.
*/
public static final String GET_FAILURE_REASON_METHOD_NAME =
"getfailurereason";
/**
* The method number for the "getFailureReason" method.
*/
public static final int GET_FAILURE_REASON_METHOD_NUMBER = 12;
/**
* The name of the method that can be used to retrieve a list of the folders
* in a user's mailbox.
*/
public static final String LIST_FOLDERS_METHOD_NAME = "listfolders";
/**
* The method number for the "listFolders" method.
*/
public static final int LIST_FOLDERS_METHOD_NUMBER = 13;
/**
* The name of the method that can be used to retrieve a list of the messages
* in the current folder of a user's mailbox.
*/
public static final String LIST_MESSAGES_METHOD_NAME = "listmessages";
/**
* The method number for the "listMessages" method.
*/
public static final int LIST_MESSAGES_METHOD_NUMBER = 14;
/**
* The name of the method that can be used to retrieve a list of the new
* (unread) messages in the current folder of a user's mailbox.
*/
public static final String LIST_NEW_MESSAGES_METHOD_NAME = "listnewmessages";
/**
* The method number for the "listNewMessages" method.
*/
public static final int LIST_NEW_MESSAGES_METHOD_NUMBER = 15;
/**
* The name of the method that can be used to move a message from the current
* folder to another folder in a user's mailbox.
*/
public static final String MOVE_METHOD_NAME = "move";
/**
* The method number for the "move" method.
*/
public static final int MOVE_METHOD_NUMBER = 16;
/**
* The name of the method that sends a "NOOP" to the server.
*/
public static final String NOOP_METHOD_NAME = "noop";
/**
* The method number for the "noop" method.
*/
public static final int NOOP_METHOD_NUMBER = 17;
/**
* The name of the method that can be used to retrieve a specified message
* from the current folder of a user's mailbox.
*/
public static final String RETRIEVE_METHOD_NAME = "retrieve";
/**
* The method number for the "retrieve" method.
*/
public static final int RETRIEVE_METHOD_NUMBER = 18;
/**
* The name of the method that can be used to change the current folder for
* the user.
*/
public static final String SELECT_FOLDER_METHOD_NAME = "selectfolder";
/**
* The method number for the "selectFolder" method.
*/
public static final int SELECT_FOLDER_METHOD_NUMBER = 19;
/**
* The name of the method that can be used to send a raw IMAP command to the
* server.
*/
public static final String SEND_COMMAND_METHOD_NAME = "sendcommand";
/**
* The method number for the "sendCommand" method.
*/
public static final int SEND_COMMAND_METHOD_NUMBER = 20;
/**
* The set of methods associated with IMAP connection variables.
*/
public static final Method[] IMAP_CONNECTION_VARIABLE_METHODS = new Method[]
{
new Method(AUTHENTICATE_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE,
StringVariable.STRING_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(CAPABILITY_METHOD_NAME, new String[0],
StringArrayVariable.STRING_ARRAY_VARIABLE_TYPE),
new Method(CONNECT_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE,
IntegerVariable.INTEGER_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(CONNECT_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE,
IntegerVariable.INTEGER_VARIABLE_TYPE,
BooleanVariable.BOOLEAN_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(COPY_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
StringVariable.STRING_VARIABLE_TYPE},
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(COUNT_MESSAGES_METHOD_NAME, new String[0],
IntegerVariable.INTEGER_VARIABLE_TYPE),
new Method(COUNT_UNREAD_MESSAGES_METHOD_NAME, new String[0],
IntegerVariable.INTEGER_VARIABLE_TYPE),
new Method(CREATE_FOLDER_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(DELETE_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(DELETE_FOLDER_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(DISCONNECT_METHOD_NAME, new String[0], null),
new Method(EXPUNGE_METHOD_NAME, new String[0],
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(GET_FAILURE_REASON_METHOD_NAME, new String[0],
StringVariable.STRING_VARIABLE_TYPE),
new Method(LIST_FOLDERS_METHOD_NAME, new String[0],
StringArrayVariable.STRING_ARRAY_VARIABLE_TYPE),
new Method(LIST_MESSAGES_METHOD_NAME, new String[0],
StringArrayVariable.STRING_ARRAY_VARIABLE_TYPE),
new Method(LIST_NEW_MESSAGES_METHOD_NAME, new String[0],
StringArrayVariable.STRING_ARRAY_VARIABLE_TYPE),
new Method(MOVE_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE,
StringVariable.STRING_VARIABLE_TYPE},
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(NOOP_METHOD_NAME, new String[0], null),
new Method(RETRIEVE_METHOD_NAME,
new String[] { IntegerVariable.INTEGER_VARIABLE_TYPE },
MailMessageVariable.MAIL_MESSAGE_VARIABLE_TYPE),
new Method(SELECT_FOLDER_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE },
BooleanVariable.BOOLEAN_VARIABLE_TYPE),
new Method(SEND_COMMAND_METHOD_NAME,
new String[] { StringVariable.STRING_VARIABLE_TYPE },
StringArrayVariable.STRING_ARRAY_VARIABLE_TYPE)
};
/**
* The end of line character as required by RFC 3501.
*/
public static final String EOL = "\r\n";
// The socket, reader, and writer used to communicate with the IMAP server.
private BufferedReader reader;
private BufferedWriter writer;
private Socket socket;
// The counter that will be used to increment the message ID used for IMAP
// requests.
private int messageIDCounter;
// The reason that the last operation failed.
private String failureReason;
// The name of the currently selected folder.
private String folderName;
/**
* Creates a new variable with no name, to be used only when creating a
* variable with <CODE>Class.newInstance()</CODE>, and only when
* <CODE>setName()</CODE> is called after that to set the name.
*
* @throws ScriptException If a problem occurs while initializing the new
* variable.
*/
public IMAPConnectionVariable()
throws ScriptException
{
messageIDCounter = 0;
failureReason = null;
folderName = null;
}
/**
* Retrieves the name of the variable type for this variable.
*
* @return The name of the variable type for this variable.
*/
@Override()
public String getVariableTypeName()
{
return IMAP_CONNECTION_VARIABLE_TYPE;
}
/**
* Retrieves the request ID that should be used for the next request sent to
* the IMAP server.
*
* @return The request ID that should be used for the next request sent to
* the IMAP server.
*/
private String getRequestID()
{
return "a" + (messageIDCounter++);
}
/**
* Retrieves a list of all methods defined for this variable.
*
* @return A list of all methods defined for this variable.
*/
@Override()
public Method[] getMethods()
{
return IMAP_CONNECTION_VARIABLE_METHODS;
}
/**
* Indicates whether this variable type has a method with the specified name.
*
* @param methodName The name of the method.
*
* @return <CODE>true</CODE> if this variable has a method with the specified
* name, or <CODE>false</CODE> if it does not.
*/
@Override()
public boolean hasMethod(String methodName)
{
for (int i=0; i < IMAP_CONNECTION_VARIABLE_METHODS.length; i++)
{
if (IMAP_CONNECTION_VARIABLE_METHODS[i].getName().equals(methodName))
{
return true;
}
}
return false;
}
/**
* Retrieves the method number for the method that has the specified name and
* argument types, or -1 if there is no such method.
*
* @param methodName The name of the method.
* @param argumentTypes The list of argument types for the method.
*
* @return The method number for the method that has the specified name and
* argument types.
*/
@Override()
public int getMethodNumber(String methodName, String[] argumentTypes)
{
for (int i=0; i < IMAP_CONNECTION_VARIABLE_METHODS.length; i++)
{
if (IMAP_CONNECTION_VARIABLE_METHODS[i].hasSignature(methodName,
argumentTypes))
{
return i;
}
}
return -1;
}
/**
* Retrieves the return type for the method with the specified name and
* argument types.
*
* @param methodName The name of the method.
* @param argumentTypes The set of argument types for the method.
*
* @return The return type for the method, or <CODE>null</CODE> if there is
* no such method defined.
*/
@Override()
public String getReturnTypeForMethod(String methodName,
String[] argumentTypes)
{
for (int i=0; i < IMAP_CONNECTION_VARIABLE_METHODS.length; i++)
{
if (IMAP_CONNECTION_VARIABLE_METHODS[i].hasSignature(methodName,
argumentTypes))
{
return IMAP_CONNECTION_VARIABLE_METHODS[i].getReturnType();
}
}
return null;
}
/**
* Executes the specified method, using the provided variables as arguments
* to the method, and makes the return value available to the caller.
*
* @param lineNumber The line number of the script in which the method
* call occurs.
* @param methodNumber The method number of the method to execute.
* @param arguments The set of arguments to use for the method.
*
* @return The value returned from the method, or <CODE>null</CODE> if it
* does not return a value.
*
* @throws ScriptException If the specified method does not exist, or if a
* problem occurs while attempting to execute it.
*/
@Override()
public Variable executeMethod(int lineNumber, int methodNumber,
Argument[] arguments)
throws ScriptException
{
switch (methodNumber)
{
case AUTHENTICATE_METHOD_NUMBER:
StringVariable sv1 = (StringVariable) arguments[0].getArgumentValue();
StringVariable sv2 = (StringVariable) arguments[1].getArgumentValue();
String userID = sv1.getStringValue();
String userPW = sv2.getStringValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " login " + userID + ' ' + userPW + EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case CAPABILITY_METHOD_NUMBER:
StringArrayVariable sav = new StringArrayVariable();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " capability" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
break;
}
if (line.startsWith("*"))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken(); // The initial asterisk
tokenizer.nextToken(); // The word "capability"
while (tokenizer.hasMoreTokens())
{
sav.addStringValue(tokenizer.nextToken());
}
}
else if (line.startsWith(requestID))
{
break;
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return sav;
case CONNECT_1_METHOD_NUMBER:
sv1 = (StringVariable) arguments[0].getArgumentValue();
IntegerVariable iv1 = (IntegerVariable) arguments[1].getArgumentValue();
String address = sv1.getStringValue();
int port = iv1.getIntValue();
failureReason = null;
try
{
socket = new Socket(address, port);
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
writer = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "No greeting line read from server";
}
reader.close();
writer.close();
socket.close();
return new BooleanVariable(false);
}
return new BooleanVariable(true);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case CONNECT_2_METHOD_NUMBER:
sv1 = (StringVariable) arguments[0].getArgumentValue();
iv1 = (IntegerVariable) arguments[1].getArgumentValue();
BooleanVariable bv1 = (BooleanVariable) arguments[2].getArgumentValue();
address = sv1.getStringValue();
port = iv1.getIntValue();
boolean useSSL = bv1.getBooleanValue();
failureReason = null;
try
{
if (useSSL)
{
JSSEBlindTrustSocketFactory socketFactory =
new JSSEBlindTrustSocketFactory();
socket = socketFactory.makeSocket(address, port);
}
else
{
socket = new Socket(address, port);
}
reader = new BufferedReader(new InputStreamReader(
socket.getInputStream()));
writer = new BufferedWriter(new OutputStreamWriter(
socket.getOutputStream()));
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "No greeting line read from server";
}
reader.close();
writer.close();
socket.close();
return new BooleanVariable(false);
}
return new BooleanVariable(true);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case COPY_METHOD_NUMBER:
iv1 = (IntegerVariable) arguments[0].getArgumentValue();
sv1 = (StringVariable) arguments[1].getArgumentValue();
int messageID = iv1.getIntValue();
String folderName = sv1.getStringValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " copy " + messageID + ' ' + folderName +
EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case COUNT_MESSAGES_METHOD_NUMBER:
failureReason = null;
int numExists = -1;
try
{
// First, find out how many messages there are in this folder.
String requestID = getRequestID();
writer.write(requestID + " status " + this.folderName +
" (messages)" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
String lowerLine = line.toLowerCase();
int messagesPos = lowerLine.indexOf("(messages ");
if (messagesPos > 0)
{
int closePos = lowerLine.indexOf(')', messagesPos+10);
numExists = Integer.parseInt(line.substring(messagesPos+10,
closePos));
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return new IntegerVariable(numExists);
case COUNT_UNREAD_MESSAGES_METHOD_NUMBER:
failureReason = null;
numExists = -1;
try
{
// First, find out how many messages there are in this folder.
String requestID = getRequestID();
writer.write(requestID + " status " + this.folderName +
" (unseen)" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
String lowerLine = line.toLowerCase();
int messagesPos = lowerLine.indexOf("(unseen ");
if (messagesPos > 0)
{
int closePos = lowerLine.indexOf(')', messagesPos+10);
numExists = Integer.parseInt(line.substring(messagesPos+10,
closePos));
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return new IntegerVariable(numExists);
case CREATE_FOLDER_METHOD_NUMBER:
sv1 = (StringVariable) arguments[0].getArgumentValue();
folderName = sv1.getStringValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " create " + folderName + EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case DELETE_METHOD_NUMBER:
iv1 = (IntegerVariable) arguments[0].getArgumentValue();
messageID = iv1.getIntValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " store " + messageID +
" +flags (\\Deleted)" + EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case DELETE_FOLDER_METHOD_NUMBER:
sv1 = (StringVariable) arguments[0].getArgumentValue();
folderName = sv1.getStringValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " delete " + folderName + EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
failureReason = "Unexpected end of input stream from server";
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case DISCONNECT_METHOD_NUMBER:
try
{
writer.write(getRequestID() + " logout" + EOL);
writer.flush();
}
catch (Exception e) {}
try
{
reader.close();
writer.close();
socket.close();
} catch (Exception e) {}
return null;
case EXPUNGE_METHOD_NUMBER:
try
{
failureReason = null;
String requestID = getRequestID();
writer.write(requestID + " expunge" + EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case GET_FAILURE_REASON_METHOD_NUMBER:
return new StringVariable(failureReason);
case LIST_FOLDERS_METHOD_NUMBER:
failureReason = null;
sav = new StringArrayVariable();
try
{
String requestID = getRequestID();
writer.write(requestID + " list \"\" *" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith("*"))
{
if (line.endsWith("\""))
{
int lastQuotePos = line.lastIndexOf('"', line.length()-2);
sav.addStringValue(line.substring(lastQuotePos+1,
line.length()-1));
}
else
{
int lastSpacePos = line.lastIndexOf(' ');
sav.addStringValue(line.substring(lastSpacePos+1));
}
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return sav;
case LIST_MESSAGES_METHOD_NUMBER:
failureReason = null;
sav = new StringArrayVariable();
try
{
// First, find out how many messages there are in this folder.
String requestID = getRequestID();
writer.write(requestID + " status " + this.folderName +
" (messages)" + EOL);
writer.flush();
numExists = 0;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
String lowerLine = line.toLowerCase();
int messagesPos = lowerLine.indexOf("(messages ");
if (messagesPos > 0)
{
int closePos = lowerLine.indexOf(')', messagesPos+10);
numExists = Integer.parseInt(line.substring(messagesPos+10,
closePos));
}
}
// Now that we know how many messages there are, retrieve them.
if (numExists > 0)
{
requestID = getRequestID();
writer.write(requestID + " fetch 1:" + numExists + " all" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
break;
}
else if (line.startsWith("*"))
{
sav.addStringValue(line);
}
}
}
else
{
failureReason = "Unable to determine number of existing messages";
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return sav;
case LIST_NEW_MESSAGES_METHOD_NUMBER:
failureReason = null;
sav = new StringArrayVariable();
try
{
// First, find out how many messages there are in this folder.
String requestID = getRequestID();
writer.write(requestID + " search unseen" + EOL);
writer.flush();
ArrayList<String> unseenList = new ArrayList<String>();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith("*"))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken(); // The asterisk
tokenizer.nextToken(); // The word "search"
while (tokenizer.hasMoreTokens())
{
unseenList.add(tokenizer.nextToken());
}
}
}
// Now that we have a specific message list, retrieve them
// individually.
for (int i=0; i < unseenList.size(); i++)
{
requestID = getRequestID();
writer.write(requestID + " fetch " + unseenList.get(i) + " all" +
EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
break;
}
else if (line.startsWith("*"))
{
sav.addStringValue(line);
}
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return sav;
case MOVE_METHOD_NUMBER:
iv1 = (IntegerVariable) arguments[0].getArgumentValue();
sv1 = (StringVariable) arguments[1].getArgumentValue();
messageID = iv1.getIntValue();
folderName = sv1.getStringValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " copy " + messageID + ' ' + folderName +
EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = "Unable to copy message: " + line;
}
break;
}
}
if (successful)
{
requestID = getRequestID();
writer.write(requestID + " store " + messageID +
" +flags (\\Deleted)" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.length() == 0))
{
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
}
else
{
failureReason = "Unexpected empty response from server";
}
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
}
else
{
failureReason = "Unable to delete original message: " + line;
}
break;
}
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case NOOP_METHOD_NUMBER:
try
{
String requestID = getRequestID();
writer.write(requestID + " noop" + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if ((line == null) || (line.startsWith(requestID)))
{
break;
}
}
} catch (Exception e) {}
return null;
case RETRIEVE_METHOD_NUMBER:
iv1 = (IntegerVariable) arguments[0].getArgumentValue();
messageID = iv1.getIntValue();
failureReason = null;
MailMessageVariable mmv = new MailMessageVariable();
try
{
String requestID = getRequestID();
writer.write(requestID + " fetch " + messageID + " RFC822" + EOL);
writer.flush();
boolean blankLineSeen = false;
while (true)
{
String line = reader.readLine();
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
break;
}
else if (line.length() == 0)
{
blankLineSeen = true;
}
else if (line.startsWith("* " + messageID) || line.equals(")"))
{
// Ignore it -- beginning or end of message wrapper.
}
else if (line.startsWith(requestID + ' '))
{
if (! line.toLowerCase().contains("ok"))
{
failureReason = line;
}
break;
}
else if (blankLineSeen)
{
mmv.addBodyLine(line);
}
else
{
mmv.addHeaderLine(line);
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return mmv;
case SELECT_FOLDER_METHOD_NUMBER:
sv1 = (StringVariable) arguments[0].getArgumentValue();
folderName = sv1.getStringValue();
failureReason = null;
try
{
String requestID = getRequestID();
writer.write(requestID + " select " + folderName + EOL);
writer.flush();
boolean successful = false;
while (true)
{
String line = reader.readLine();
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
break;
}
if (line.startsWith(requestID))
{
StringTokenizer tokenizer = new StringTokenizer(line);
tokenizer.nextToken();
String result = tokenizer.nextToken();
if (result.equalsIgnoreCase("ok"))
{
successful = true;
this.folderName = folderName;
}
else
{
failureReason = line;
}
break;
}
}
return new BooleanVariable(successful);
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
return new BooleanVariable(false);
}
case SEND_COMMAND_METHOD_NUMBER:
sv1 = (StringVariable) arguments[0].getArgumentValue();
String command = sv1.getStringValue();
failureReason = null;
sav = new StringArrayVariable();
try
{
String requestID = getRequestID();
writer.write(requestID + ' ' + command + EOL);
writer.flush();
while (true)
{
String line = reader.readLine();
if (line == null)
{
failureReason = "Unexpected end of input stream from server";
break;
}
sav.addStringValue(line);
if (line.length() > 0)
{
char c = line.charAt(0);
if (line.startsWith(requestID))
{
break;
}
}
}
}
catch (Exception e)
{
failureReason = "Caught exception: " +
JobClass.stackTraceToString(e);
}
return sav;
default:
throw new ScriptException(lineNumber,
"There is no method " + methodNumber +
" defined for " + getArgumentType() +
" variables.");
}
}
/**
* Assigns the value of the provided argument to this variable. The value of
* the provided argument must be of the same type as this variable.
*
* @param argument The argument whose value should be assigned to this
* variable.
*
* @throws ScriptException If a problem occurs while performing the
* assignment.
*/
@Override()
public void assign(Argument argument)
throws ScriptException
{
if (! argument.getArgumentType().equals(IMAP_CONNECTION_VARIABLE_TYPE))
{
throw new ScriptException("Attempt to assign an argument of type " +
argument.getArgumentType() +
" to a variable of type " +
IMAP_CONNECTION_VARIABLE_TYPE + " rejected.");
}
IMAPConnectionVariable icv =
(IMAPConnectionVariable) argument.getArgumentValue();
socket = icv.socket;
reader = icv.reader;
writer = icv.writer;
}
/**
* Retrieves a string representation of the value of this argument.
*
* @return A string representation of the value of this argument.
*/
public String getValueAsString()
{
if (socket == null)
{
return "null";
}
else
{
boolean connected = socket.isConnected();
if (! connected)
{
return "not connected";
}
else
{
String host = socket.getInetAddress().getHostAddress();
int port = socket.getPort();
boolean usingSSL = (socket instanceof SSLSocket);
if (usingSSL)
{
return "imaps://" + host + ':' + port;
}
else
{
return "imap://" + host + ':' + port;
}
}
}
}
}