/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.commons.net.imap;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.EOFException;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.net.SocketClient;
import org.apache.commons.net.io.CRLFLineReader;


The IMAP class provides the basic the functionality necessary to implement your own IMAP client.
/** * The IMAP class provides the basic the functionality necessary to implement your * own IMAP client. */
public class IMAP extends SocketClient {
The default IMAP port (RFC 3501).
/** The default IMAP port (RFC 3501). */
public static final int DEFAULT_PORT = 143; public enum IMAPState {
A constant representing the state where the client is not yet connected to a server.
/** A constant representing the state where the client is not yet connected to a server. */
DISCONNECTED_STATE,
A constant representing the "not authenticated" state.
/** A constant representing the "not authenticated" state. */
NOT_AUTH_STATE,
A constant representing the "authenticated" state.
/** A constant representing the "authenticated" state. */
AUTH_STATE,
A constant representing the "logout" state.
/** A constant representing the "logout" state. */
LOGOUT_STATE; } // RFC 3501, section 5.1.3. It should be "modified UTF-7".
The default control socket ecoding.
/** * The default control socket ecoding. */
protected static final String __DEFAULT_ENCODING = "ISO-8859-1"; private IMAPState __state; protected BufferedWriter __writer; protected BufferedReader _reader; private int _replyCode; private final List<String> _replyLines;
Implement this interface and register it via IMAP.setChunkListener(IMAPChunkListener) in order to get access to multi-line partial command responses. Useful when processing large FETCH responses.
/** * Implement this interface and register it via {@link #setChunkListener(IMAPChunkListener)} * in order to get access to multi-line partial command responses. * Useful when processing large FETCH responses. */
public interface IMAPChunkListener {
Called when a multi-line partial response has been received.
Params:
Returns:true if the reply buffer is to be cleared on return
/** * Called when a multi-line partial response has been received. * @param imap the instance, get the response * by calling {@link #getReplyString()} or {@link #getReplyStrings()} * @return {@code true} if the reply buffer is to be cleared on return */
boolean chunkReceived(IMAP imap); }

Implementation of IMAPChunkListener that returns true but otherwise does nothing.

This is intended for use with a suitable ProtocolCommandListener. If the IMAP response contains multiple-line data, the protocol listener will be called for each multi-line chunk. The accumulated reply data will be cleared after calling the listener. If the response is very long, this can significantly reduce memory requirements. The listener will also start receiving response data earlier, as it does not have to wait for the entire response to be read.

The ProtocolCommandListener must be prepared to accept partial responses. This should not be a problem for listeners that just log the input.

See Also:
Since:3.4
/** * <p> * Implementation of IMAPChunkListener that returns {@code true} * but otherwise does nothing. * </p> * <p> * This is intended for use with a suitable ProtocolCommandListener. * If the IMAP response contains multiple-line data, the protocol listener * will be called for each multi-line chunk. * The accumulated reply data will be cleared after calling the listener. * If the response is very long, this can significantly reduce memory requirements. * The listener will also start receiving response data earlier, as it does not have * to wait for the entire response to be read. * </p> * <p> * The ProtocolCommandListener must be prepared to accept partial responses. * This should not be a problem for listeners that just log the input. * </p> * @see #setChunkListener(IMAPChunkListener) * @since 3.4 */
public static final IMAPChunkListener TRUE_CHUNK_LISTENER = new IMAPChunkListener(){ @Override public boolean chunkReceived(IMAP imap) { return true; } }; private volatile IMAPChunkListener __chunkListener; private final char[] _initialID = { 'A', 'A', 'A', 'A' };
The default IMAPClient constructor. Initializes the state to DISCONNECTED_STATE.
/** * The default IMAPClient constructor. Initializes the state * to <code>DISCONNECTED_STATE</code>. */
public IMAP() { setDefaultPort(DEFAULT_PORT); __state = IMAPState.DISCONNECTED_STATE; _reader = null; __writer = null; _replyLines = new ArrayList<String>(); createCommandSupport(); }
Get the reply for a command that expects a tagged response.
Throws:
  • IOException –
/** * Get the reply for a command that expects a tagged response. * * @throws IOException */
private void __getReply() throws IOException { __getReply(true); // tagged response }
Get the reply for a command, reading the response until the reply is found.
Params:
  • wantTag – true if the command expects a tagged response.
Throws:
/** * Get the reply for a command, reading the response until the * reply is found. * * @param wantTag {@code true} if the command expects a tagged response. * @throws IOException */
private void __getReply(boolean wantTag) throws IOException { _replyLines.clear(); String line = _reader.readLine(); if (line == null) { throw new EOFException("Connection closed without indication."); } _replyLines.add(line); if (wantTag) { while(IMAPReply.isUntagged(line)) { int literalCount = IMAPReply.literalCount(line); final boolean isMultiLine = literalCount >= 0; while (literalCount >= 0) { line=_reader.readLine(); if (line == null) { throw new EOFException("Connection closed without indication."); } _replyLines.add(line); literalCount -= (line.length() + 2); // Allow for CRLF } if (isMultiLine) { IMAPChunkListener il = __chunkListener; if (il != null) { boolean clear = il.chunkReceived(this); if (clear) { fireReplyReceived(IMAPReply.PARTIAL, getReplyString()); _replyLines.clear(); } } } line = _reader.readLine(); // get next chunk or final tag if (line == null) { throw new EOFException("Connection closed without indication."); } _replyLines.add(line); } // check the response code on the last line _replyCode = IMAPReply.getReplyCode(line); } else { _replyCode = IMAPReply.getUntaggedReplyCode(line); } fireReplyReceived(_replyCode, getReplyString()); }
Overrides SocketClient.fireReplyReceived(int, String) so as to avoid creating the reply string if there are no listeners to invoke.
Params:
  • replyCode – passed to the listeners
  • ignored – the string is only created if there are listeners defined.
See Also:
Since:3.4
/** * Overrides {@link SocketClient#fireReplyReceived(int, String)} so as to * avoid creating the reply string if there are no listeners to invoke. * * @param replyCode passed to the listeners * @param ignored the string is only created if there are listeners defined. * @see #getReplyString() * @since 3.4 */
@Override protected void fireReplyReceived(int replyCode, String ignored) { if (getCommandSupport().getListenerCount() > 0) { getCommandSupport().fireReplyReceived(replyCode, getReplyString()); } }
Performs connection initialization and sets state to IMAPState.NOT_AUTH_STATE.
/** * Performs connection initialization and sets state to * {@link IMAPState#NOT_AUTH_STATE}. */
@Override protected void _connectAction_() throws IOException { super._connectAction_(); _reader = new CRLFLineReader(new InputStreamReader(_input_, __DEFAULT_ENCODING)); __writer = new BufferedWriter(new OutputStreamWriter(_output_, __DEFAULT_ENCODING)); int tmo = getSoTimeout(); if (tmo <= 0) { // none set currently setSoTimeout(connectTimeout); // use connect timeout to ensure we don't block forever } __getReply(false); // untagged response if (tmo <= 0) { setSoTimeout(tmo); // restore the original value } setState(IMAPState.NOT_AUTH_STATE); }
Sets IMAP client state. This must be one of the _STATE constants.
Params:
  • state – The new state.
/** * Sets IMAP client state. This must be one of the * <code>_STATE</code> constants. * * @param state The new state. */
protected void setState(IMAP.IMAPState state) { __state = state; }
Returns the current IMAP client state.
Returns:The current IMAP client state.
/** * Returns the current IMAP client state. * * @return The current IMAP client state. */
public IMAP.IMAPState getState() { return __state; }
Disconnects the client from the server, and sets the state to DISCONNECTED_STATE . The reply text information from the last issued command is voided to allow garbage collection of the memory used to store that information.
Throws:
  • IOException – If there is an error in disconnecting.
/** * Disconnects the client from the server, and sets the state to * <code> DISCONNECTED_STATE </code>. The reply text information * from the last issued command is voided to allow garbage collection * of the memory used to store that information. * * @throws IOException If there is an error in disconnecting. */
@Override public void disconnect() throws IOException { super.disconnect(); _reader = null; __writer = null; _replyLines.clear(); setState(IMAPState.DISCONNECTED_STATE); }
Sends a command an arguments to the server and returns the reply code.
Params:
  • commandID – The ID (tag) of the command.
  • command – The IMAP command to send.
  • args – The command arguments.
Returns: The server reply code (either IMAPReply.OK, IMAPReply.NO or IMAPReply.BAD).
/** * Sends a command an arguments to the server and returns the reply code. * * @param commandID The ID (tag) of the command. * @param command The IMAP command to send. * @param args The command arguments. * @return The server reply code (either IMAPReply.OK, IMAPReply.NO or IMAPReply.BAD). */
private int sendCommandWithID(String commandID, String command, String args) throws IOException { StringBuilder __commandBuffer = new StringBuilder(); if (commandID != null) { __commandBuffer.append(commandID); __commandBuffer.append(' '); } __commandBuffer.append(command); if (args != null) { __commandBuffer.append(' '); __commandBuffer.append(args); } __commandBuffer.append(SocketClient.NETASCII_EOL); String message = __commandBuffer.toString(); __writer.write(message); __writer.flush(); fireCommandSent(command, message); __getReply(); return _replyCode; }
Sends a command an arguments to the server and returns the reply code.
Params:
  • command – The IMAP command to send.
  • args – The command arguments.
Throws:
Returns: The server reply code (see IMAPReply).
/** * Sends a command an arguments to the server and returns the reply code. * * @param command The IMAP command to send. * @param args The command arguments. * @return The server reply code (see IMAPReply). * @throws IOException on error */
public int sendCommand(String command, String args) throws IOException { return sendCommandWithID(generateCommandID(), command, args); }
Sends a command with no arguments to the server and returns the reply code.
Params:
  • command – The IMAP command to send.
Throws:
Returns: The server reply code (see IMAPReply).
/** * Sends a command with no arguments to the server and returns the * reply code. * * @param command The IMAP command to send. * @return The server reply code (see IMAPReply). * @throws IOException on error */
public int sendCommand(String command) throws IOException { return sendCommand(command, null); }
Sends a command and arguments to the server and returns the reply code.
Params:
  • command – The IMAP command to send (one of the IMAPCommand constants).
  • args – The command arguments.
Throws:
Returns: The server reply code (see IMAPReply).
/** * Sends a command and arguments to the server and returns the reply code. * * @param command The IMAP command to send * (one of the IMAPCommand constants). * @param args The command arguments. * @return The server reply code (see IMAPReply). * @throws IOException on error */
public int sendCommand(IMAPCommand command, String args) throws IOException { return sendCommand(command.getIMAPCommand(), args); }
Sends a command and arguments to the server and return whether successful.
Params:
  • command – The IMAP command to send (one of the IMAPCommand constants).
  • args – The command arguments.
Throws:
Returns: true if the command was successful
/** * Sends a command and arguments to the server and return whether successful. * * @param command The IMAP command to send * (one of the IMAPCommand constants). * @param args The command arguments. * @return {@code true} if the command was successful * @throws IOException on error */
public boolean doCommand(IMAPCommand command, String args) throws IOException { return IMAPReply.isSuccess(sendCommand(command, args)); }
Sends a command with no arguments to the server and returns the reply code.
Params:
  • command – The IMAP command to send (one of the IMAPCommand constants).
Throws:
Returns: The server reply code (see IMAPReply).
/** * Sends a command with no arguments to the server and returns the * reply code. * * @param command The IMAP command to send * (one of the IMAPCommand constants). * @return The server reply code (see IMAPReply). * @throws IOException on error **/
public int sendCommand(IMAPCommand command) throws IOException { return sendCommand(command, null); }
Sends a command to the server and return whether successful.
Params:
  • command – The IMAP command to send (one of the IMAPCommand constants).
Throws:
Returns: true if the command was successful
/** * Sends a command to the server and return whether successful. * * @param command The IMAP command to send * (one of the IMAPCommand constants). * @return {@code true} if the command was successful * @throws IOException on error */
public boolean doCommand(IMAPCommand command) throws IOException { return IMAPReply.isSuccess(sendCommand(command)); }
Sends data to the server and returns the reply code.
Params:
  • command – The IMAP command to send.
Throws:
Returns: The server reply code (see IMAPReply).
/** * Sends data to the server and returns the reply code. * * @param command The IMAP command to send. * @return The server reply code (see IMAPReply). * @throws IOException on error */
public int sendData(String command) throws IOException { return sendCommandWithID(null, command, null); }
Returns an array of lines received as a reply to the last command sent to the server. The lines have end of lines truncated.
Returns:The last server response.
/** * Returns an array of lines received as a reply to the last command * sent to the server. The lines have end of lines truncated. * @return The last server response. */
public String[] getReplyStrings() { return _replyLines.toArray(new String[_replyLines.size()]); }
Returns the reply to the last command sent to the server. The value is a single string containing all the reply lines including newlines.
Returns:The last server response.
/** * Returns the reply to the last command sent to the server. * The value is a single string containing all the reply lines including * newlines. * * @return The last server response. */
public String getReplyString() { StringBuilder buffer = new StringBuilder(256); for (String s : _replyLines) { buffer.append(s); buffer.append(SocketClient.NETASCII_EOL); } return buffer.toString(); }
Sets the current chunk listener. If a listener is registered and the implementation returns true, then any registered PrintCommandListener instances will be invoked with the partial response and a status of IMAPReply.PARTIAL to indicate that the final reply code is not yet known.
Params:
  • listener – the class to use, or null to disable
See Also:
Since:3.4
/** * Sets the current chunk listener. * If a listener is registered and the implementation returns true, * then any registered * {@link org.apache.commons.net.PrintCommandListener PrintCommandListener} * instances will be invoked with the partial response and a status of * {@link IMAPReply#PARTIAL} to indicate that the final reply code is not yet known. * @param listener the class to use, or {@code null} to disable * @see #TRUE_CHUNK_LISTENER * @since 3.4 */
public void setChunkListener(IMAPChunkListener listener) { __chunkListener = listener; }
Generates a new command ID (tag) for a command.
Returns:a new command ID (tag) for an IMAP command.
/** * Generates a new command ID (tag) for a command. * @return a new command ID (tag) for an IMAP command. */
protected String generateCommandID() { String res = new String (_initialID); // "increase" the ID for the next call boolean carry = true; // want to increment initially for (int i = _initialID.length-1; carry && i>=0; i--) { if (_initialID[i] == 'Z') { _initialID[i] = 'A'; } else { _initialID[i]++; carry = false; // did not wrap round } } return res; } } /* kate: indent-width 4; replace-tabs on; */