Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow usage of custom imap handlers #235

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ nb-configuration.xml
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm

/*.iml
*.iml

## Directory-based project format:
.idea/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,108 +1,12 @@
/*
* Copyright (c) 2014 Wael Chatila / Icegreen Technologies. All Rights Reserved.
* This software is released under the Apache license 2.0
* This file has been modified by the copyright holder.
* Original file can be found at http://james.apache.org
*/
package com.icegreen.greenmail.imap;

import com.icegreen.greenmail.server.BuildInfo;
import com.icegreen.greenmail.server.ProtocolHandler;
import com.icegreen.greenmail.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.Socket;

/**
* The handler class for IMAP connections.
*
* @author Federico Barbieri <[email protected]>
* @author Peter M. Goldstein <[email protected]>
*/
public class ImapHandler implements ImapConstants, ProtocolHandler {
protected final Logger log = LoggerFactory.getLogger(getClass());
private ImapRequestHandler requestHandler = new ImapRequestHandler();
private ImapSession session;
private final Object closeMonitor = new Object();

/**
* The TCP/IP socket over which the IMAP interaction
* is occurring
*/
private Socket socket;

private ImapResponse response;

UserManager userManager;
private ImapHostManager imapHost;

public ImapHandler(UserManager userManager, ImapHostManager imapHost, Socket socket) {
this.userManager = userManager;
this.imapHost = imapHost;
this.socket = socket;
}

public void forceConnectionClose(final String message) {
response.byeResponse(message);
close();
}

@Override
public void run() {
// Closed automatically when socket is closed via #close()
try (InputStream ins = new BufferedInputStream(socket.getInputStream(), 512);
OutputStream outs = new BufferedOutputStream(socket.getOutputStream(), 1024)
) {

response = new ImapResponse(outs);

// Write welcome message
String responseBuffer = VERSION + " Server GreenMail v" +
BuildInfo.INSTANCE.getProjectVersion() + " ready";
response.okResponse(null, responseBuffer);

session = new ImapSessionImpl(imapHost,
userManager,
this,
socket.getInetAddress().getHostAddress());

while (requestHandler.handleRequest(ins, outs, session)) {
// Loop ...
}
} catch (Exception e) {
log.error("Can not handle IMAP connection", e);
throw new IllegalStateException("Can not handle IMAP connection", e);
} finally {
close();
}
}

/**
* Resets the handler data to a basic state.
*/
@Override
public void close() {
// Use monitor to avoid race between external close and handler thread run()
synchronized (closeMonitor) {
// Close and clear streams, sockets etc.
if (socket != null) {
try {
// Terminates thread blocking on socket read
// and automatically closed depending streams
socket.close();
} catch (IOException e) {
log.warn("Can not close socket", e);
} finally {
socket = null;
}
}

// Clear user data
session = null;
response = null;
}
}
}

package com.icegreen.greenmail.imap;

import com.icegreen.greenmail.server.ProtocolHandler;
import com.icegreen.greenmail.user.UserManager;

import java.net.Socket;

public interface ImapHandler extends ProtocolHandler {
ImapHandler init(UserManager userManager, ImapHostManager imapHost, Socket socket);

void forceConnectionClose(String message);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2014 Wael Chatila / Icegreen Technologies. All Rights Reserved.
* This software is released under the Apache license 2.0
* This file has been modified by the copyright holder.
* Original file can be found at http://james.apache.org
*/
package com.icegreen.greenmail.imap;

import com.icegreen.greenmail.server.BuildInfo;
import com.icegreen.greenmail.server.ProtocolHandler;
import com.icegreen.greenmail.user.UserManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.Socket;

/**
* The handler class for IMAP connections.
*
* @author Federico Barbieri <[email protected]>
* @author Peter M. Goldstein <[email protected]>
*/
public class ImapHandlerImpl implements ImapConstants, ProtocolHandler, ImapHandler {
protected final Logger log = LoggerFactory.getLogger(getClass());
private ImapRequestHandler requestHandler = new ImapRequestHandler();
private ImapSession session;
private final Object closeMonitor = new Object();

/**
* The TCP/IP socket over which the IMAP interaction
* is occurring
*/
private Socket socket;

private ImapResponse response;

UserManager userManager;
private ImapHostManager imapHost;

@Override
public ImapHandler init(UserManager userManager, ImapHostManager imapHost, Socket socket) {
this.userManager = userManager;
this.imapHost = imapHost;
this.socket = socket;
return this;
}

@Override
public void forceConnectionClose(final String message) {
response.byeResponse(message);
close();
}

@Override
public void run() {
// Closed automatically when socket is closed via #close()
try (InputStream ins = new BufferedInputStream(socket.getInputStream(), 512);
OutputStream outs = new BufferedOutputStream(socket.getOutputStream(), 1024)
) {

response = new ImapResponse(outs);

// Write welcome message
String responseBuffer = VERSION + " Server GreenMail v" +
BuildInfo.INSTANCE.getProjectVersion() + " ready";
response.okResponse(null, responseBuffer);

session = new ImapSessionImpl(imapHost,
userManager,
this,
socket.getInetAddress().getHostAddress());

while (requestHandler.handleRequest(ins, outs, session)) {
// Loop ...
}
} catch (Exception e) {
log.error("Can not handle IMAP connection", e);
throw new IllegalStateException("Can not handle IMAP connection", e);
} finally {
close();
}
}

/**
* Resets the handler data to a basic state.
*/
@Override
public void close() {
// Use monitor to avoid race between external close and handler thread run()
synchronized (closeMonitor) {
// Close and clear streams, sockets etc.
if (socket != null) {
try {
// Terminates thread blocking on socket read
// and automatically closed depending streams
socket.close();
} catch (IOException e) {
log.warn("Can not close socket", e);
} finally {
socket = null;
}
}

// Clear user data
session = null;
response = null;
}
}

public ImapRequestHandler getRequestHandler() {
return requestHandler;
}

public void setRequestHandler(ImapRequestHandler requestHandler) {
this.requestHandler = requestHandler;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
package com.icegreen.greenmail.imap;

import com.icegreen.greenmail.imap.commands.CommandParser;
import com.icegreen.greenmail.imap.commands.parsers.CommandParser;
import com.icegreen.greenmail.imap.commands.ImapCommand;
import com.icegreen.greenmail.imap.commands.ImapCommandFactory;
import org.slf4j.Logger;
Expand All @@ -19,7 +19,7 @@
* @author Darrell DeBoer <[email protected]>
* @version $Revision: 109034 $
*/
public final class ImapRequestHandler {
public class ImapRequestHandler {
protected final Logger log = LoggerFactory.getLogger(getClass());
private ImapCommandFactory imapCommands = new ImapCommandFactory();
private CommandParser parser = new CommandParser();
Expand Down Expand Up @@ -96,5 +96,11 @@ private void doProcessRequest(ImapRequestLineReader request,
command.process(request, response, session);
}

public ImapCommandFactory getImapCommands() {
return imapCommands;
}

public void setImapCommands(ImapCommandFactory imapCommands) {
this.imapCommands = imapCommands;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ public ImapServer(ServerSetup setup, Managers managers) {

@Override
protected ProtocolHandler createProtocolHandler(Socket clientSocket) {
return new ImapHandler(managers.getUserManager(), managers.getImapHostManager(), clientSocket);
if (null != setup.getImapHandler()) {
return setup.getImapHandler().init(managers.getUserManager(), managers.getImapHostManager(), clientSocket);
} else {
return new ImapHandlerImpl().init(managers.getUserManager(), managers.getImapHostManager(), clientSocket);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.icegreen.greenmail.imap.ImapResponse;
import com.icegreen.greenmail.imap.ImapSession;
import com.icegreen.greenmail.imap.ProtocolException;
import com.icegreen.greenmail.imap.commands.parsers.AppendCommandParser;
import com.icegreen.greenmail.store.FolderException;
import com.icegreen.greenmail.store.MailFolder;
import com.icegreen.greenmail.util.GreenMailUtil;
Expand All @@ -25,13 +26,13 @@
* @author Darrell DeBoer <[email protected]>
* @version $Revision: 109034 $
*/
class AppendCommand extends AuthenticatedStateCommand {
public class AppendCommand extends AuthenticatedStateCommand {
public static final String NAME = "APPEND";
public static final String ARGS = "<mailbox> [<flag_list>] [<date_time>] literal";

private AppendCommandParser appendCommandParser = new AppendCommandParser();

AppendCommand() {
public AppendCommand() {
super(NAME, ARGS);
}

Expand Down Expand Up @@ -68,58 +69,6 @@ protected void doProcess(ImapRequestLineReader request,
session.unsolicitedResponses(response);
response.commandComplete(this, "APPENDUID" + SP + folder.getUidValidity() + SP + uid);
}

private static class AppendCommandParser extends CommandParser {
/**
* If the next character in the request is a '(', tries to read
* a "flag_list" argument from the request. If not, returns a
* MessageFlags with no flags set.
*/
public Flags optionalAppendFlags(ImapRequestLineReader request)
throws ProtocolException {
char next = request.nextWordChar();
if (next == '(') {
return flagList(request);
} else {
return null;
}
}

/**
* If the next character in the request is a '"', tries to read
* a DateTime argument. If not, returns null.
*/
public Date optionalDateTime(ImapRequestLineReader request)
throws ProtocolException {
char next = request.nextWordChar();
if (next == '"') {
return dateTime(request);
} else {
return null;
}
}

/**
* Reads a MimeMessage encoded as a string literal from the request.
* TODO shouldn't need to read as a string and write out bytes
* use FixedLengthInputStream instead. Hopefully it can then be dynamic.
*
* @param request The Imap APPEND request
* @return A MimeMessage read off the request.
*/
public MimeMessage mimeMessage(ImapRequestLineReader request)
throws ProtocolException {
request.nextWordChar();
byte[] mail = consumeLiteralAsBytes(request);

try {
return GreenMailUtil.newMimeMessage(new ByteArrayInputStream(mail));
} catch (Exception e) {
throw new ProtocolException("Can not create new mime message", e);
}
}

}
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@
* @author Darrell DeBoer <[email protected]>
* @version $Revision: 109034 $
*/
class AuthenticateCommand extends NonAuthenticatedStateCommand {
public class AuthenticateCommand extends NonAuthenticatedStateCommand {
public static final String NAME = "AUTHENTICATE";
public static final String ARGS = "<auth_type> *(CRLF base64)";

AuthenticateCommand() {
public AuthenticateCommand() {
super(NAME, ARGS);
}

Expand Down
Loading