All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.groupbyinc.common.apache.commons.net.imap.IMAP Maven / Gradle / Ivy

There is a newer version: 198
Show newest version
/*
 * 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.
 */
public class IMAP extends SocketClient
{
    /** 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. */
        DISCONNECTED_STATE,
        /**  A constant representing the "not authenticated" state. */
        NOT_AUTH_STATE,
        /**  A constant representing the "authenticated" state. */
        AUTH_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.
     */
    protected static final String __DEFAULT_ENCODING = "ISO-8859-1";

    private IMAPState __state;
    protected BufferedWriter __writer;

    protected BufferedReader _reader;
    private int _replyCode;
    private final List _replyLines;

    /**
     * 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.
         * @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 {@code 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 #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. */ public IMAP() { setDefaultPort(DEFAULT_PORT); __state = IMAPState.DISCONNECTED_STATE; _reader = null; __writer = null; _replyLines = new ArrayList(); createCommandSupport(); } /** * 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. * * @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 {@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 * {@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. * * @param state The new state. */ protected void setState(IMAP.IMAPState state) { __state = 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. * * @exception 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. * * @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. * * @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. * * @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. * * @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. * * @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. * * @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. * * @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. * * @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. * @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. * * @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 * {@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. * @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; */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy