working on net mail test
This commit is contained in:
parent
f221a578b1
commit
d19e058178
26 changed files with 422 additions and 484 deletions
|
@ -18,11 +18,14 @@ package jakarta.mail;
|
|||
|
||||
import jakarta.mail.event.MailEvent;
|
||||
import java.util.EventListener;
|
||||
import java.util.Objects;
|
||||
import java.util.Vector;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Package private class used by Store & Folder to dispatch events.
|
||||
|
@ -33,16 +36,20 @@ import java.util.concurrent.LinkedBlockingQueue;
|
|||
*/
|
||||
class EventQueue implements Runnable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(EventQueue.class.getName());
|
||||
|
||||
private static WeakHashMap<ClassLoader, EventQueue> appq;
|
||||
|
||||
private volatile BlockingQueue<QueueElement> q;
|
||||
private Executor executor;
|
||||
|
||||
private final Executor executor;
|
||||
|
||||
/**
|
||||
* Construct an EventQueue using the specified Executor.
|
||||
* If the Executor is null, threads will be created as needed.
|
||||
*/
|
||||
EventQueue(Executor ex) {
|
||||
this.executor = ex;
|
||||
this.executor = Objects.requireNonNull(ex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,15 +57,12 @@ class EventQueue implements Runnable {
|
|||
* Application scoping is based on the thread's context class loader.
|
||||
*/
|
||||
static synchronized EventQueue getApplicationEventQueue(Executor ex) {
|
||||
Objects.requireNonNull(ex);
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
if (appq == null)
|
||||
if (appq == null) {
|
||||
appq = new WeakHashMap<>();
|
||||
EventQueue q = appq.get(cl);
|
||||
if (q == null) {
|
||||
q = new EventQueue(ex);
|
||||
appq.put(cl, q);
|
||||
}
|
||||
return q;
|
||||
return appq.computeIfAbsent(cl, k -> new EventQueue(ex));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,13 +73,13 @@ class EventQueue implements Runnable {
|
|||
// if this is the first event, create the queue and start the event task
|
||||
if (q == null) {
|
||||
q = new LinkedBlockingQueue<>();
|
||||
if (executor != null) {
|
||||
executor.execute(this);
|
||||
} else {
|
||||
logger.log(Level.INFO, "starting event queue");
|
||||
executor.execute(this);
|
||||
/*else {
|
||||
Thread qThread = new Thread(this, "Jakarta-Mail-EventQueue");
|
||||
qThread.setDaemon(true); // not a user thread
|
||||
qThread.start();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
q.add(new QueueElement(event, vector));
|
||||
}
|
||||
|
@ -97,11 +101,11 @@ class EventQueue implements Runnable {
|
|||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
BlockingQueue<QueueElement> bq = q;
|
||||
if (bq == null)
|
||||
return;
|
||||
try {
|
||||
logger.log(Level.INFO, "entering event queue");
|
||||
loop:
|
||||
for (; ; ) {
|
||||
// block until an item is available
|
||||
|
@ -148,7 +152,9 @@ class EventQueue implements Runnable {
|
|||
* A "struct" to put on the queue.
|
||||
*/
|
||||
static class QueueElement {
|
||||
|
||||
MailEvent event;
|
||||
|
||||
Vector<? extends EventListener> vector;
|
||||
|
||||
QueueElement(MailEvent event, Vector<? extends EventListener> vector) {
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.EventListener;
|
|||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* Folder is an abstract class that represents a folder for mail
|
||||
|
@ -151,21 +152,22 @@ public abstract class Folder implements AutoCloseable {
|
|||
*/
|
||||
protected Folder(Store store) {
|
||||
this.store = store;
|
||||
|
||||
// create or choose the appropriate event queue
|
||||
Session session = store.getSession();
|
||||
String scope =
|
||||
session.getProperties().getProperty("mail.event.scope", "folder");
|
||||
Executor executor =
|
||||
(Executor) session.getProperties().get("mail.event.executor");
|
||||
if (scope.equalsIgnoreCase("application"))
|
||||
String scope = session.getProperties().getProperty("mail.event.scope", "folder");
|
||||
Executor executor = (Executor) session.getProperties().get("mail.event.executor");
|
||||
if (executor == null) {
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
if (scope.equalsIgnoreCase("application")) {
|
||||
q = EventQueue.getApplicationEventQueue(executor);
|
||||
else if (scope.equalsIgnoreCase("session"))
|
||||
} else if (scope.equalsIgnoreCase("session")) {
|
||||
q = session.getEventQueue();
|
||||
else if (scope.equalsIgnoreCase("store"))
|
||||
} else if (scope.equalsIgnoreCase("store")) {
|
||||
q = store.getEventQueue();
|
||||
else // if (scope.equalsIgnoreCase("folder"))
|
||||
} else {
|
||||
q = new EventQueue(executor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,6 +24,9 @@ import java.net.UnknownHostException;
|
|||
import java.util.EventListener;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* An abstract class that contains the functionality
|
||||
|
@ -40,6 +43,8 @@ import java.util.concurrent.Executor;
|
|||
|
||||
public abstract class Service implements AutoCloseable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Service.class.getName());
|
||||
|
||||
/*
|
||||
* connectionListeners is a Vector, initialized here,
|
||||
* because we depend on it always existing and depend
|
||||
|
@ -47,8 +52,7 @@ public abstract class Service implements AutoCloseable {
|
|||
* (Sychronizing on the Service object itself can cause
|
||||
* deadlocks when notifying listeners.)
|
||||
*/
|
||||
private final Vector<ConnectionListener> connectionListeners
|
||||
= new Vector<>();
|
||||
private final Vector<ConnectionListener> connectionListeners = new Vector<>();
|
||||
/**
|
||||
* The queue of events to be delivered.
|
||||
*/
|
||||
|
@ -121,21 +125,20 @@ public abstract class Service implements AutoCloseable {
|
|||
if (user == null) {
|
||||
user = System.getProperty("user.name");
|
||||
}
|
||||
|
||||
url = new URLName(protocol, host, port, file, user, password);
|
||||
|
||||
// create or choose the appropriate event queue
|
||||
String scope =
|
||||
session.getProperties().getProperty("mail.event.scope", "folder");
|
||||
Executor executor =
|
||||
(Executor) session.getProperties().get("mail.event.executor");
|
||||
if (scope.equalsIgnoreCase("application"))
|
||||
String scope = session.getProperties().getProperty("mail.event.scope", "folder");
|
||||
Executor executor = (Executor) session.getProperties().get("mail.event.executor");
|
||||
if (executor == null) {
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
if (scope.equalsIgnoreCase("application")) {
|
||||
q = EventQueue.getApplicationEventQueue(executor);
|
||||
else if (scope.equalsIgnoreCase("session"))
|
||||
} else if (scope.equalsIgnoreCase("session")) {
|
||||
q = session.getEventQueue();
|
||||
else // if (scope.equalsIgnoreCase("store") ||
|
||||
// scope.equalsIgnoreCase("folder"))
|
||||
} else {
|
||||
q = new EventQueue(executor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,7 +566,7 @@ public abstract class Service implements AutoCloseable {
|
|||
* Yes, listeners could be removed after checking, which
|
||||
* just makes this an expensive no-op.
|
||||
*/
|
||||
if (connectionListeners.size() > 0) {
|
||||
if (!connectionListeners.isEmpty()) {
|
||||
ConnectionEvent e = new ConnectionEvent(this, type);
|
||||
queueEvent(e, connectionListeners);
|
||||
}
|
||||
|
@ -578,8 +581,10 @@ public abstract class Service implements AutoCloseable {
|
|||
* terminator event causes the event-dispatching thread to
|
||||
* self destruct.
|
||||
*/
|
||||
if (type == ConnectionEvent.CLOSED)
|
||||
if (type == ConnectionEvent.CLOSED) {
|
||||
logger.log(Level.INFO, "sending terminator event");
|
||||
q.terminateQueue();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -601,8 +606,7 @@ public abstract class Service implements AutoCloseable {
|
|||
* @param event the event
|
||||
* @param vector the vector of listeners
|
||||
*/
|
||||
protected void queueEvent(MailEvent event,
|
||||
Vector<? extends EventListener> vector) {
|
||||
protected void queueEvent(MailEvent event, Vector<? extends EventListener> vector) {
|
||||
/*
|
||||
* Copy the vector in order to freeze the state of the set
|
||||
* of EventListeners the event should be delivered to prior
|
||||
|
|
|
@ -40,6 +40,7 @@ import java.util.Properties;
|
|||
import java.util.ServiceLoader;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
|
@ -146,8 +147,8 @@ interface StreamLoader {
|
|||
* Here's an example of <code>META-INF/javamail.default.providers</code>
|
||||
* file contents:
|
||||
* <pre>
|
||||
* protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=Oracle;
|
||||
* protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=Oracle;
|
||||
* protocol=imap; type=store; class=com.sun.mail.imap.IMAPStore; vendor=xbib;
|
||||
* protocol=smtp; type=transport; class=com.sun.mail.smtp.SMTPTransport; vendor=xbib;
|
||||
* </pre><p>
|
||||
* <p>
|
||||
* The current implementation also supports configuring providers using
|
||||
|
@ -211,7 +212,7 @@ public final class Session {
|
|||
private static Session defaultSession = null;
|
||||
|
||||
static {
|
||||
String dir = null;
|
||||
String dir;
|
||||
String home = System.getProperty("java.home");
|
||||
String newdir = home + File.separator + "conf";
|
||||
File conf = new File(newdir);
|
||||
|
@ -261,7 +262,11 @@ public final class Session {
|
|||
// load the resources
|
||||
loadProviders(cl);
|
||||
loadAddressMap(cl);
|
||||
q = new EventQueue((Executor) props.get("mail.event.executor"));
|
||||
Executor executor = (Executor) props.get("mail.event.executor");
|
||||
if (executor == null) {
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
q = new EventQueue(executor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -908,7 +913,6 @@ public final class Session {
|
|||
try {
|
||||
Class<?>[] c = {Session.class, URLName.class};
|
||||
Constructor<?> cons = serviceClass.getConstructor(c);
|
||||
|
||||
Object[] o = {this, url};
|
||||
return type.cast(cons.newInstance(o));
|
||||
} catch (Exception ex) {
|
||||
|
|
|
@ -385,8 +385,9 @@ public class IMAPFolder extends Folder implements UIDFolder, ResponseHandler {
|
|||
protected IMAPFolder(String fullName, char separator, IMAPStore store,
|
||||
Boolean isNamespace) {
|
||||
super(store);
|
||||
if (fullName == null)
|
||||
if (fullName == null) {
|
||||
throw new NullPointerException("Folder name is null");
|
||||
}
|
||||
this.fullName = fullName;
|
||||
this.separator = separator;
|
||||
|
||||
|
|
|
@ -1378,7 +1378,6 @@ public class IMAPStore extends Store
|
|||
* Empty the connection pool.
|
||||
*/
|
||||
private void emptyConnectionPool(boolean force) {
|
||||
|
||||
synchronized (pool) {
|
||||
for (int index = pool.authenticatedConnections.size() - 1;
|
||||
index >= 0; --index) {
|
||||
|
@ -1394,10 +1393,8 @@ public class IMAPStore extends Store
|
|||
}
|
||||
;
|
||||
}
|
||||
|
||||
pool.authenticatedConnections.removeAllElements();
|
||||
}
|
||||
|
||||
logger.fine("removed all authenticated connections from pool");
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
protocol=imap; type=store; class=org.xbib.net.mail.imap.IMAPStore; vendor=xbib;
|
||||
protocol=imaps; type=store; class=org.xbib.net.mail.imap.IMAPSSLStore; vendor=xbib;
|
||||
protocol=pop3; type=store; class=org.xbib.net.mail.pop3.POP3Store; vendor=xbib;
|
||||
protocol=pop3s; type=store; class=org.xbib.net.mail.pop3.POP3SSLStore; vendor=xbib;
|
||||
protocol=smtp; type=transport; class=org.xbib.net.mail.smtp.SMTPTransport; vendor=xbib;
|
||||
protocol=smtps; type=transport; class=org.xbib.net.mail.smtp.SMTPSSLTransport; vendor=xbib;
|
|
@ -1,3 +1,5 @@
|
|||
protocol=imap; type=store; class=org.xbib.net.mail.imap.IMAPStore; vendor=xbib;
|
||||
protocol=imaps; type=store; class=org.xbib.net.mail.imap.IMAPSSLStore; vendor=xbib;
|
||||
protocol=pop3; type=store; class=org.xbib.net.mail.pop3.POP3Store; vendor=xbib;
|
||||
protocol=pop3s; type=store; class=org.xbib.net.mail.pop3.POP3SSLStore; vendor=xbib;
|
||||
protocol=smtp; type=transport; class=org.xbib.net.mail.smtp.SMTPTransport; vendor=xbib;
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.xbib.net.mail.test.imap;
|
|||
import jakarta.mail.Session;
|
||||
import jakarta.mail.Store;
|
||||
import jakarta.mail.event.StoreEvent;
|
||||
import jakarta.mail.event.StoreListener;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.net.mail.test.test.TestServer;
|
||||
|
||||
|
@ -28,6 +27,9 @@ import java.io.OutputStream;
|
|||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
@ -36,52 +38,48 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
*/
|
||||
public final class IMAPAlertTest {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(IMAPAlertTest.class.getName());
|
||||
|
||||
private volatile boolean gotAlert = false;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
public void testImapAlert() {
|
||||
TestServer server = null;
|
||||
try {
|
||||
final IMAPHandler handler = new IMAPHandlerAlert();
|
||||
server = new TestServer(handler);
|
||||
server.start();
|
||||
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("mail.imap.host", "localhost");
|
||||
properties.setProperty("mail.imap.port", String.valueOf(server.getPort()));
|
||||
final Session session = Session.getInstance(properties);
|
||||
//session.setDebug(true);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
final Store store = session.getStore("imap");
|
||||
store.addStoreListener(new StoreListener() {
|
||||
@Override
|
||||
public void notification(StoreEvent e) {
|
||||
String s;
|
||||
if (e.getMessageType() == StoreEvent.ALERT) {
|
||||
s = "ALERT: ";
|
||||
gotAlert = true;
|
||||
latch.countDown();
|
||||
} else
|
||||
s = "NOTICE: ";
|
||||
//System.out.println(s + e.getMessage());
|
||||
}
|
||||
store.addStoreListener(e -> {
|
||||
String s;
|
||||
if (e.getMessageType() == StoreEvent.ALERT) {
|
||||
s = "ALERT: ";
|
||||
gotAlert = true;
|
||||
latch.countDown();
|
||||
} else
|
||||
s = "NOTICE: ";
|
||||
|
||||
//System.out.println(s + e.getMessage());
|
||||
});
|
||||
try {
|
||||
store.connect("test", "test");
|
||||
// time for event to be delivered
|
||||
latch.await(5, TimeUnit.SECONDS);
|
||||
assertTrue(gotAlert);
|
||||
|
||||
} catch (Exception ex) {
|
||||
System.out.println(ex);
|
||||
//ex.printStackTrace();
|
||||
logger.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
fail(ex.toString());
|
||||
} finally {
|
||||
store.close();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
//e.printStackTrace();
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
fail(e.getMessage());
|
||||
} finally {
|
||||
if (server != null) {
|
||||
|
|
|
@ -38,7 +38,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/**
|
||||
* Test IMAPFolder methods.
|
||||
*/
|
||||
|
@ -68,8 +67,7 @@ class IMAPFolderTest {
|
|||
void testUtf7FolderNameCreate() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
assertTrue(test.create(Folder.HOLDS_MESSAGES));
|
||||
}
|
||||
|
@ -101,11 +99,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf8FolderNameCreate() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
assertTrue(test.create(Folder.HOLDS_MESSAGES));
|
||||
}
|
||||
|
@ -137,11 +133,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf7FolderNameDelete() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
assertTrue(test.delete(false));
|
||||
}
|
||||
|
@ -167,11 +161,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf8FolderNameDelete() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
assertTrue(test.delete(false));
|
||||
}
|
||||
|
@ -198,11 +190,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf7FolderNameSelect() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
test.open(Folder.READ_WRITE);
|
||||
test.close(true);
|
||||
|
@ -230,11 +220,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf8FolderNameSelect() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
test.open(Folder.READ_WRITE);
|
||||
test.close(true);
|
||||
|
@ -262,11 +250,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf7FolderNameExamine() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
test.open(Folder.READ_ONLY);
|
||||
test.close(true);
|
||||
|
@ -294,11 +280,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf8FolderNameExamine() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
test.open(Folder.READ_ONLY);
|
||||
test.close(true);
|
||||
|
@ -326,11 +310,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf7FolderNameStatus() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
assertEquals(123, ((UIDFolder) test).getUIDValidity());
|
||||
}
|
||||
|
@ -343,8 +325,7 @@ class IMAPFolderTest {
|
|||
st.nextToken(); // skip "STATUS"
|
||||
String name = st.nextToken();
|
||||
if (name.equals(utf7Folder)) {
|
||||
untagged(outputStream, "STATUS " + utf7Folder +
|
||||
" (UIDVALIDITY 123)");
|
||||
untagged(outputStream, "STATUS " + utf7Folder + " (UIDVALIDITY 123)");
|
||||
ok(outputStream);
|
||||
} else
|
||||
no(outputStream, "wrong name");
|
||||
|
@ -358,11 +339,9 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUtf8FolderNameStatus() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
Folder test = store.getFolder(utf8Folder);
|
||||
assertEquals(123, ((UIDFolder) test).getUIDValidity());
|
||||
}
|
||||
|
@ -375,8 +354,7 @@ class IMAPFolderTest {
|
|||
st.nextToken(); // skip "STATUS"
|
||||
String name = unquote(st.nextToken());
|
||||
if (name.equals(utf8Folder)) {
|
||||
untagged(outputStream, "STATUS \"" + utf8Folder +
|
||||
"\" (UIDVALIDITY 123)");
|
||||
untagged(outputStream, "STATUS \"" + utf8Folder + "\" (UIDVALIDITY 123)");
|
||||
ok(outputStream);
|
||||
} else
|
||||
no(outputStream, "wrong name");
|
||||
|
@ -389,17 +367,12 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUidNotStickyFalse() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
Folder test = store.getFolder("test");
|
||||
try {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
try (Folder test = store.getFolder("test")) {
|
||||
test.open(Folder.READ_WRITE);
|
||||
assertFalse(((IMAPFolder) test).getUIDNotSticky());
|
||||
} finally {
|
||||
test.close();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -411,17 +384,12 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testUidNotStickyTrue() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
Folder test = store.getFolder("test");
|
||||
try {
|
||||
public void test(Store store, IMAPHandler handler) throws MessagingException {
|
||||
try (Folder test = store.getFolder("test")) {
|
||||
test.open(Folder.READ_WRITE);
|
||||
assertTrue(((IMAPFolder) test).getUIDNotSticky());
|
||||
} finally {
|
||||
test.close();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -440,11 +408,10 @@ class IMAPFolderTest {
|
|||
*/
|
||||
@Test
|
||||
void testExpungeOutOfRange() {
|
||||
testWithHandler(
|
||||
new IMAPTest() {
|
||||
testWithHandler(new IMAPTest() {
|
||||
@Override
|
||||
public void test(Store store, IMAPHandler handler)
|
||||
throws MessagingException, IOException {
|
||||
throws MessagingException {
|
||||
Folder test = store.getFolder("test");
|
||||
try {
|
||||
test.open(Folder.READ_WRITE);
|
||||
|
@ -471,27 +438,22 @@ class IMAPFolderTest {
|
|||
try {
|
||||
server = new TestServer(handler);
|
||||
server.start();
|
||||
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty("mail.imap.host", "localhost");
|
||||
properties.setProperty("mail.imap.port", String.valueOf(server.getPort()));
|
||||
test.init(properties);
|
||||
final Session session = Session.getInstance(properties);
|
||||
//session.setDebug(true);
|
||||
|
||||
final Store store = session.getStore("imap");
|
||||
try {
|
||||
try (Store store = session.getStore("imap")) {
|
||||
store.connect("test", "test");
|
||||
test.test(store, handler);
|
||||
} catch (Exception ex) {
|
||||
logger.log(Level.SEVERE, ex.getMessage(), ex);
|
||||
fail(ex.toString());
|
||||
} finally {
|
||||
store.close();
|
||||
fail();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
fail(e.getMessage());
|
||||
fail();
|
||||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.xbib.net.mail.test.test.TestServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -101,9 +102,9 @@ public final class POP3FolderClosedExceptionTest {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void retr(String arg) throws IOException {
|
||||
public void retr(OutputStream outputStream, String arg) throws IOException {
|
||||
if (first) {
|
||||
println("-ERR Server timeout");
|
||||
println(outputStream, "-ERR Server timeout");
|
||||
first = false;
|
||||
} else
|
||||
exit();
|
||||
|
@ -163,8 +164,8 @@ public final class POP3FolderClosedExceptionTest {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void top(String arg) throws IOException {
|
||||
println("-ERR Server timeout");
|
||||
public void top(OutputStream outputStream, String arg) throws IOException {
|
||||
println(outputStream, "-ERR Server timeout");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,8 +21,11 @@ import org.xbib.net.mail.test.test.ProtocolHandler;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Handle connection.
|
||||
|
@ -31,35 +34,36 @@ import java.util.logging.Level;
|
|||
*/
|
||||
public class POP3Handler extends ProtocolHandler {
|
||||
|
||||
/**
|
||||
* Current line.
|
||||
*/
|
||||
private String currentLine;
|
||||
private static final Logger logger = Logger.getLogger(POP3Handler.class.getName());
|
||||
|
||||
/**
|
||||
* First test message.
|
||||
*/
|
||||
private String top1 =
|
||||
"Mime-Version: 1.0\r\n" +
|
||||
"From: joe@example.com\r\n" +
|
||||
"To: bob@example.com\r\n" +
|
||||
"Subject: Example\r\n" +
|
||||
"Content-Type: text/plain\r\n" +
|
||||
"\r\n";
|
||||
private String msg1 = top1 +
|
||||
private final String top1 =
|
||||
"""
|
||||
Mime-Version: 1.0\r
|
||||
From: joe@example.com\r
|
||||
To: bob@example.com\r
|
||||
Subject: Example\r
|
||||
Content-Type: text/plain\r
|
||||
\r
|
||||
""";
|
||||
private final String msg1 = top1 +
|
||||
"plain text\r\n";
|
||||
|
||||
/**
|
||||
* Second test message.
|
||||
*/
|
||||
private String top2 =
|
||||
"Mime-Version: 1.0\r\n" +
|
||||
"From: joe@example.com\r\n" +
|
||||
"To: bob@example.com\r\n" +
|
||||
"Subject: Multipart Example\r\n" +
|
||||
"Content-Type: multipart/mixed; boundary=\"xxx\"\r\n" +
|
||||
"\r\n";
|
||||
private String msg2 = top2 +
|
||||
private final String top2 =
|
||||
"""
|
||||
Mime-Version: 1.0\r
|
||||
From: joe@example.com\r
|
||||
To: bob@example.com\r
|
||||
Subject: Multipart Example\r
|
||||
Content-Type: multipart/mixed; boundary="xxx"\r
|
||||
\r
|
||||
""";
|
||||
private final String msg2 = top2 +
|
||||
"preamble\r\n" +
|
||||
"--xxx\r\n" +
|
||||
"\r\n" +
|
||||
|
@ -71,9 +75,7 @@ public class POP3Handler extends ProtocolHandler {
|
|||
"\r\n" +
|
||||
"--xxx--\r\n";
|
||||
|
||||
public POP3Handler(InputStream inputStream,
|
||||
OutputStream outputStream) {
|
||||
super(inputStream, outputStream);
|
||||
public POP3Handler() {
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -82,8 +84,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
* @throws IOException unable to write to socket
|
||||
*/
|
||||
@Override
|
||||
public void sendGreetings() throws IOException {
|
||||
this.println("+OK POP3 CUSTOM");
|
||||
public void sendGreetings(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK POP3 CUSTOM");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,10 +94,9 @@ public class POP3Handler extends ProtocolHandler {
|
|||
* @param str String to send
|
||||
* @throws IOException unable to write to socket
|
||||
*/
|
||||
public void println(final String str) throws IOException {
|
||||
this.writer.print(str);
|
||||
this.writer.print("\r\n");
|
||||
this.writer.flush();
|
||||
public void println(OutputStream outputStream, final String str) throws IOException {
|
||||
Channels.newChannel(outputStream).write(StandardCharsets.UTF_8.encode(str + "\r\n"));
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -104,54 +105,36 @@ public class POP3Handler extends ProtocolHandler {
|
|||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand() throws IOException {
|
||||
this.currentLine = readLine();
|
||||
public void handleCommand(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||
String currentLine = readLine(inputStream);
|
||||
|
||||
if (this.currentLine == null) {
|
||||
if (currentLine == null) {
|
||||
// probably just EOF because the socket was closed
|
||||
//LOGGER.severe("Current line is null!");
|
||||
this.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
final StringTokenizer st = new StringTokenizer(this.currentLine, " ");
|
||||
final StringTokenizer st = new StringTokenizer(currentLine, " ");
|
||||
final String commandName = st.nextToken().toUpperCase();
|
||||
final String arg = st.hasMoreTokens() ? st.nextToken() : null;
|
||||
if (commandName == null) {
|
||||
logger.severe("Command name is empty!");
|
||||
this.exit();
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandName.equals("STAT")) {
|
||||
this.stat();
|
||||
} else if (commandName.equals("LIST")) {
|
||||
this.list();
|
||||
} else if (commandName.equals("RETR")) {
|
||||
this.retr(arg);
|
||||
} else if (commandName.equals("DELE")) {
|
||||
this.dele();
|
||||
} else if (commandName.equals("NOOP")) {
|
||||
this.noop();
|
||||
} else if (commandName.equals("RSET")) {
|
||||
this.rset();
|
||||
} else if (commandName.equals("QUIT")) {
|
||||
this.quit();
|
||||
} else if (commandName.equals("TOP")) {
|
||||
this.top(arg);
|
||||
} else if (commandName.equals("UIDL")) {
|
||||
this.uidl();
|
||||
} else if (commandName.equals("USER")) {
|
||||
this.user();
|
||||
} else if (commandName.equals("PASS")) {
|
||||
this.pass();
|
||||
} else if (commandName.equals("CAPA")) {
|
||||
this.capa();
|
||||
} else if (commandName.equals("AUTH")) {
|
||||
this.auth();
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "ERROR command unknown: {0}", commandName);
|
||||
this.println("-ERR unknown command");
|
||||
switch (commandName) {
|
||||
case "STAT" -> this.stat(outputStream);
|
||||
case "LIST" -> this.list(outputStream);
|
||||
case "RETR" -> this.retr(outputStream, arg);
|
||||
case "DELE" -> this.dele(outputStream);
|
||||
case "NOOP" -> this.noop(outputStream);
|
||||
case "RSET" -> this.rset(outputStream);
|
||||
case "QUIT" -> this.quit(outputStream);
|
||||
case "TOP" -> this.top(outputStream, arg);
|
||||
case "UIDL" -> this.uidl(outputStream);
|
||||
case "USER" -> this.user(outputStream);
|
||||
case "PASS" -> this.pass(outputStream);
|
||||
case "CAPA" -> this.capa(outputStream);
|
||||
case "AUTH" -> this.auth(outputStream);
|
||||
default -> {
|
||||
logger.log(Level.SEVERE, "ERROR command unknown: {0}", commandName);
|
||||
this.println(outputStream, "-ERR unknown command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,8 +143,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void stat() throws IOException {
|
||||
this.println("+OK 2 " + (msg1.length() + msg2.length()));
|
||||
public void stat(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK 2 " + (msg1.length() + msg2.length()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,11 +152,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void list() throws IOException {
|
||||
this.writer.println("+OK");
|
||||
this.writer.println("1 " + msg1.length());
|
||||
this.writer.println("2 " + msg2.length());
|
||||
this.println(".");
|
||||
public void list(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK\r\n1 " + msg1.length() + "\r\n2 " + msg2.length() + "\r\n.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,15 +161,14 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void retr(String arg) throws IOException {
|
||||
public void retr(OutputStream outputStream, String arg) throws IOException {
|
||||
String msg;
|
||||
if (arg.equals("1"))
|
||||
if ("1".equals(arg))
|
||||
msg = msg1;
|
||||
else
|
||||
msg = msg2;
|
||||
this.println("+OK " + msg.length() + " octets");
|
||||
this.writer.write(msg);
|
||||
this.println(".");
|
||||
this.println(outputStream, "+OK " + msg.length() + " octets");
|
||||
this.println(outputStream, msg + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,8 +176,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void dele() throws IOException {
|
||||
this.println("-ERR DELE not supported");
|
||||
public void dele(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "-ERR DELE not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -206,8 +185,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void noop() throws IOException {
|
||||
this.println("+OK");
|
||||
public void noop(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -215,8 +194,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void rset() throws IOException {
|
||||
this.println("+OK");
|
||||
public void rset(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -224,8 +203,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void quit() throws IOException {
|
||||
this.println("+OK");
|
||||
public void quit(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK");
|
||||
this.exit();
|
||||
}
|
||||
|
||||
|
@ -235,15 +214,14 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void top(String arg) throws IOException {
|
||||
public void top(OutputStream outputStream, String arg) throws IOException {
|
||||
String top;
|
||||
if (arg.equals("1"))
|
||||
if ("1".equals(arg))
|
||||
top = top1;
|
||||
else
|
||||
top = top2;
|
||||
this.println("+OK " + top.length() + " octets");
|
||||
this.writer.write(top);
|
||||
this.println(".");
|
||||
this.println(outputStream, "+OK " + top.length() + " octets");
|
||||
this.println(outputStream, top + ".");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,11 +229,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void uidl() throws IOException {
|
||||
this.writer.println("+OK");
|
||||
this.writer.println("1 1");
|
||||
this.writer.println("2 2");
|
||||
this.println(".");
|
||||
public void uidl(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK\r\n1 1\r\n2 2\r\n.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -263,8 +238,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void user() throws IOException {
|
||||
this.println("+OK");
|
||||
public void user(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -272,8 +247,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void pass() throws IOException {
|
||||
this.println("+OK");
|
||||
public void pass(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -281,8 +256,8 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to write to socket
|
||||
*/
|
||||
public void capa() throws IOException {
|
||||
this.println("-ERR CAPA not supported");
|
||||
public void capa(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "-ERR CAPA not supported");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -290,7 +265,7 @@ public class POP3Handler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to write to socket
|
||||
*/
|
||||
public void auth() throws IOException {
|
||||
this.println("-ERR AUTH not supported");
|
||||
public void auth(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "-ERR AUTH not supported");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.xbib.net.mail.pop3.POP3Store;
|
|||
import org.xbib.net.mail.test.test.TestServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -242,15 +244,13 @@ public final class POP3StoreTest {
|
|||
*/
|
||||
private static class POP3HandlerXOAUTH extends POP3Handler {
|
||||
@Override
|
||||
public void auth() throws IOException {
|
||||
this.println("+OK POP3 server ready");
|
||||
public void auth(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK POP3 server ready");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void capa() throws IOException {
|
||||
this.writer.println("+OK");
|
||||
this.writer.println("SASL PLAIN XOAUTH2");
|
||||
this.println(".");
|
||||
public void capa(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK\r\nSASL PLAIN XOAUTH2\r\n.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,8 +265,8 @@ public final class POP3StoreTest {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void noop() throws IOException {
|
||||
this.println("-ERR");
|
||||
public void noop(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "-ERR");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -281,8 +281,8 @@ public final class POP3StoreTest {
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void sendGreetings() throws IOException {
|
||||
this.println("+OK");
|
||||
public void sendGreetings(OutputStream outputStream) throws IOException {
|
||||
this.println(outputStream, "+OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import org.junit.jupiter.api.Test;
|
|||
import org.xbib.net.mail.test.test.TestServer;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -86,9 +88,6 @@ public final class SMTPBdatTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
// wait for handler to exit
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,13 +104,13 @@ public final class SMTPBdatTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void bdat(String line) throws IOException {
|
||||
public void bdat(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
String[] tok = line.split("\\s+");
|
||||
int bytes = Integer.parseInt(tok[1]);
|
||||
boolean last = tok.length > 2 &&
|
||||
tok[2].equalsIgnoreCase("LAST");
|
||||
readBdatMessage(bytes, last);
|
||||
println("444 failed");
|
||||
readBdatMessage(inputStream, bytes, last);
|
||||
println(outputStream, "444 failed");
|
||||
}
|
||||
};
|
||||
server = new TestServer(handler);
|
||||
|
@ -144,9 +143,6 @@ public final class SMTPBdatTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
// wait for handler to exit
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,11 +24,13 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Handle connection.
|
||||
|
@ -37,6 +39,8 @@ import java.util.logging.Level;
|
|||
*/
|
||||
public class SMTPHandler extends ProtocolHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SMTPHandler.class.getName());
|
||||
|
||||
/**
|
||||
* Current line.
|
||||
*/
|
||||
|
@ -59,7 +63,7 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*/
|
||||
@Override
|
||||
public void sendGreetings(OutputStream outputStream) throws IOException {
|
||||
println("220 localhost dummy server ready");
|
||||
println(outputStream, "220 localhost dummy server ready");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -69,9 +73,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
* @throws IOException unable to write to socket
|
||||
*/
|
||||
public void println(OutputStream outputStream, final String str) throws IOException {
|
||||
writer.print(str);
|
||||
writer.print("\r\n");
|
||||
writer.flush();
|
||||
Channels.newChannel(outputStream).write(StandardCharsets.UTF_8.encode(str + "\r\n"));
|
||||
outputStream.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,52 +83,35 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
@Override
|
||||
public void handleCommand() throws IOException {
|
||||
currentLine = readLine();
|
||||
|
||||
if (currentLine == null)
|
||||
return;
|
||||
|
||||
final StringTokenizer st = new StringTokenizer(currentLine, " ");
|
||||
final String commandName = st.nextToken().toUpperCase();
|
||||
if (commandName == null) {
|
||||
logger.severe("Command name is empty!");
|
||||
exit();
|
||||
public void handleCommand(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||
currentLine = readLine(inputStream);
|
||||
if (currentLine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (commandName.equals("HELO")) {
|
||||
helo();
|
||||
} else if (commandName.equals("EHLO")) {
|
||||
ehlo();
|
||||
} else if (commandName.equals("MAIL")) {
|
||||
mail(currentLine);
|
||||
} else if (commandName.equals("RCPT")) {
|
||||
rcpt(currentLine);
|
||||
} else if (commandName.equals("DATA")) {
|
||||
data();
|
||||
} else if (commandName.equals("BDAT")) {
|
||||
bdat(currentLine);
|
||||
} else if (commandName.equals("NOOP")) {
|
||||
noop();
|
||||
} else if (commandName.equals("RSET")) {
|
||||
rset();
|
||||
} else if (commandName.equals("QUIT")) {
|
||||
quit();
|
||||
} else if (commandName.equals("AUTH")) {
|
||||
auth(currentLine);
|
||||
} else {
|
||||
logger.log(Level.SEVERE, "ERROR command unknown: {0}", commandName);
|
||||
println("-ERR unknown command");
|
||||
final StringTokenizer st = new StringTokenizer(currentLine, " ");
|
||||
final String commandName = st.nextToken().toUpperCase();
|
||||
switch (commandName) {
|
||||
case "HELO" -> helo(outputStream);
|
||||
case "EHLO" -> ehlo(outputStream);
|
||||
case "MAIL" -> mail(outputStream, currentLine);
|
||||
case "RCPT" -> rcpt(outputStream, currentLine);
|
||||
case "DATA" -> data(inputStream, outputStream);
|
||||
case "BDAT" -> bdat(inputStream,outputStream, currentLine);
|
||||
case "NOOP" -> noop(outputStream);
|
||||
case "RSET" -> rset(outputStream);
|
||||
case "QUIT" -> quit(outputStream);
|
||||
case "AUTH" -> auth(inputStream, outputStream, currentLine);
|
||||
default -> {
|
||||
logger.log(Level.SEVERE, "ERROR command unknown: {0}", commandName);
|
||||
println(outputStream, "-ERR unknown command");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String readLine() throws IOException {
|
||||
currentLine = super.readLine();
|
||||
|
||||
@Override
|
||||
protected String readLine(InputStream inputStream) throws IOException {
|
||||
currentLine = super.readLine(inputStream);
|
||||
if (currentLine == null) {
|
||||
// XXX - often happens when shutting down
|
||||
//LOGGER.severe("Current line is null!");
|
||||
exit();
|
||||
}
|
||||
return currentLine;
|
||||
|
@ -136,8 +122,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void helo() throws IOException {
|
||||
println("220 Ok");
|
||||
public void helo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "220 Ok");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -145,11 +131,11 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void ehlo() throws IOException {
|
||||
println("250-hello");
|
||||
public void ehlo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250-hello");
|
||||
for (String ext : extensions)
|
||||
println("250-" + ext);
|
||||
println("250 AUTH PLAIN"); // PLAIN is simplest to fake
|
||||
println(outputStream, "250-" + ext);
|
||||
println(outputStream, "250 AUTH PLAIN"); // PLAIN is simplest to fake
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -157,8 +143,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void mail(String line) throws IOException {
|
||||
ok();
|
||||
public void mail(OutputStream outputStream, String line) throws IOException {
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -166,8 +152,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void rcpt(String line) throws IOException {
|
||||
ok();
|
||||
public void rcpt(OutputStream outputStream, String line) throws IOException {
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -175,10 +161,10 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void data() throws IOException {
|
||||
println("354 go ahead");
|
||||
readMessage();
|
||||
ok();
|
||||
public void data(InputStream inputStream, OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "354 go ahead");
|
||||
readMessage(inputStream);
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -186,14 +172,14 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void bdat(String line) throws IOException {
|
||||
public void bdat(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
StringTokenizer st = new StringTokenizer(line, " ");
|
||||
String commandName = st.nextToken();
|
||||
int bytes = Integer.parseInt(st.nextToken());
|
||||
boolean last = st.hasMoreTokens() &&
|
||||
st.nextToken().equalsIgnoreCase("LAST");
|
||||
readBdatMessage(bytes, last);
|
||||
ok();
|
||||
readBdatMessage(inputStream, bytes, last);
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,11 +191,11 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
/**
|
||||
* Consume the message and save it.
|
||||
*/
|
||||
protected void readMessage() throws IOException {
|
||||
protected void readMessage(InputStream inputStream) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
PrintWriter pw = new PrintWriter(new OutputStreamWriter(bos, StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = super.readLine()) != null) {
|
||||
while ((line = super.readLine(inputStream)) != null) {
|
||||
if (line.equals("."))
|
||||
break;
|
||||
if (line.startsWith("."))
|
||||
|
@ -225,7 +211,7 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
* Consume a chunk of the message and save it.
|
||||
* Save the entire message when the last chunk is received.
|
||||
*/
|
||||
protected void readBdatMessage(int bytes, boolean last) throws IOException {
|
||||
protected void readBdatMessage(InputStream inputStream, int bytes, boolean last) throws IOException {
|
||||
byte[] data = new byte[bytes];
|
||||
int len = data.length;
|
||||
int off = 0;
|
||||
|
@ -248,8 +234,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void noop() throws IOException {
|
||||
ok();
|
||||
public void noop(OutputStream outputStream) throws IOException {
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -257,8 +243,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void rset() throws IOException {
|
||||
ok();
|
||||
public void rset(OutputStream outputStream) throws IOException {
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -266,8 +252,8 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void quit() throws IOException {
|
||||
println("221 BYE");
|
||||
public void quit(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "221 BYE");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
@ -276,11 +262,11 @@ public class SMTPHandler extends ProtocolHandler {
|
|||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
public void auth(String line) throws IOException {
|
||||
println("235 Authorized");
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
println(outputStream, "235 Authorized");
|
||||
}
|
||||
|
||||
protected void ok() throws IOException {
|
||||
println("250 OK");
|
||||
protected void ok(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250 OK");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.Timeout;
|
||||
import org.xbib.net.mail.test.test.TestServer;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
@ -48,13 +49,13 @@ public final class SMTPIOExceptionTest {
|
|||
try {
|
||||
SMTPHandler handler = new SMTPHandler() {
|
||||
@Override
|
||||
public void rcpt(String line) throws IOException {
|
||||
public void rcpt(OutputStream outputStream, String line) throws IOException {
|
||||
try {
|
||||
// delay long enough to cause timeout
|
||||
Thread.sleep(2 * TIMEOUT);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
super.rcpt(line);
|
||||
super.rcpt(outputStream, line);
|
||||
}
|
||||
};
|
||||
server = new TestServer(handler);
|
||||
|
@ -101,9 +102,6 @@ public final class SMTPIOExceptionTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
// wait for handler to exit
|
||||
server.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,13 @@
|
|||
package org.xbib.net.mail.test.smtp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Handle connection with LOGIN or PLAIN authentication.
|
||||
|
@ -28,6 +31,9 @@ import java.util.logging.Level;
|
|||
* @author Bill Shannon
|
||||
*/
|
||||
public class SMTPLoginHandler extends SMTPHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SMTPLoginHandler.class.getName());
|
||||
|
||||
protected String username = "test";
|
||||
protected String password = "test";
|
||||
|
||||
|
@ -37,11 +43,11 @@ public class SMTPLoginHandler extends SMTPHandler {
|
|||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
@Override
|
||||
public void ehlo() throws IOException {
|
||||
println("250-hello");
|
||||
println("250-SMTPUTF8");
|
||||
println("250-8BITMIME");
|
||||
println("250 AUTH PLAIN LOGIN");
|
||||
public void ehlo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250-hello");
|
||||
println(outputStream, "250-SMTPUTF8");
|
||||
println(outputStream, "250-8BITMIME");
|
||||
println(outputStream, "250 AUTH PLAIN LOGIN");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +56,7 @@ public class SMTPLoginHandler extends SMTPHandler {
|
|||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
@Override
|
||||
public void auth(String line) throws IOException {
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
StringTokenizer ct = new StringTokenizer(line, " ");
|
||||
String commandName = ct.nextToken().toUpperCase();
|
||||
String mech = ct.nextToken().toUpperCase();
|
||||
|
@ -61,22 +67,22 @@ public class SMTPLoginHandler extends SMTPHandler {
|
|||
if (logger.isLoggable(Level.FINE))
|
||||
logger.fine(line);
|
||||
if (mech.equalsIgnoreCase("PLAIN"))
|
||||
plain(ir);
|
||||
plain(outputStream, ir);
|
||||
else if (mech.equalsIgnoreCase("LOGIN"))
|
||||
login(ir);
|
||||
login(inputStream, outputStream, ir);
|
||||
else
|
||||
println("501 bad AUTH mechanism");
|
||||
println(outputStream, "501 bad AUTH mechanism");
|
||||
}
|
||||
|
||||
/**
|
||||
* AUTH LOGIN
|
||||
*/
|
||||
private void login(String ir) throws IOException {
|
||||
println("334");
|
||||
private void login(InputStream inputStream, OutputStream outputStream, String ir) throws IOException {
|
||||
println(outputStream, "334");
|
||||
// read user name
|
||||
String resp = readLine();
|
||||
String resp = readLine(inputStream);
|
||||
if (!isBase64(resp)) {
|
||||
println("501 response not base64");
|
||||
println(outputStream, "501 response not base64");
|
||||
return;
|
||||
}
|
||||
byte[] response = resp.getBytes(StandardCharsets.US_ASCII);
|
||||
|
@ -84,12 +90,12 @@ public class SMTPLoginHandler extends SMTPHandler {
|
|||
String u = new String(response, StandardCharsets.UTF_8);
|
||||
if (logger.isLoggable(Level.FINE))
|
||||
logger.fine("USER: " + u);
|
||||
println("334");
|
||||
println(outputStream, "334");
|
||||
|
||||
// read password
|
||||
resp = readLine();
|
||||
resp = readLine(inputStream);
|
||||
if (!isBase64(resp)) {
|
||||
println("501 response not base64");
|
||||
println(outputStream, "501 response not base64");
|
||||
return;
|
||||
}
|
||||
response = resp.getBytes(StandardCharsets.US_ASCII);
|
||||
|
@ -100,17 +106,16 @@ public class SMTPLoginHandler extends SMTPHandler {
|
|||
|
||||
//System.out.printf("USER: %s, PASSWORD: %s%n", u, p);
|
||||
if (!u.equals(username) || !p.equals(password)) {
|
||||
println("535 authentication failed");
|
||||
println(outputStream, "535 authentication failed");
|
||||
return;
|
||||
}
|
||||
|
||||
println("235 Authenticated");
|
||||
println(outputStream, "235 Authenticated");
|
||||
}
|
||||
|
||||
/**
|
||||
* AUTH PLAIN
|
||||
*/
|
||||
private void plain(String ir) throws IOException {
|
||||
private void plain(OutputStream outputStream, String ir) throws IOException {
|
||||
String auth = new String(Base64.getDecoder().decode(
|
||||
ir.getBytes(StandardCharsets.US_ASCII)),
|
||||
StandardCharsets.UTF_8);
|
||||
|
@ -119,10 +124,10 @@ public class SMTPLoginHandler extends SMTPHandler {
|
|||
String p = ap[2];
|
||||
//System.out.printf("USER: %s, PASSWORD: %s%n", u, p);
|
||||
if (!u.equals(username) || !p.equals(password)) {
|
||||
println("535 authentication failed");
|
||||
println(outputStream, "535 authentication failed");
|
||||
return;
|
||||
}
|
||||
println("235 Authenticated");
|
||||
println(outputStream, "235 Authenticated");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,9 +29,12 @@ import javax.security.sasl.Sasl;
|
|||
import javax.security.sasl.SaslException;
|
||||
import javax.security.sasl.SaslServer;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Base64;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Handle connection with SASL authentication.
|
||||
|
@ -40,15 +43,17 @@ import java.util.logging.Level;
|
|||
*/
|
||||
public class SMTPSaslHandler extends SMTPHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SMTPSaslHandler.class.getName());
|
||||
|
||||
/**
|
||||
* EHLO command.
|
||||
*
|
||||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
@Override
|
||||
public void ehlo() throws IOException {
|
||||
println("250-hello");
|
||||
println("250 AUTH DIGEST-MD5");
|
||||
public void ehlo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250-hello");
|
||||
println(outputStream, "250 AUTH DIGEST-MD5");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,7 +62,7 @@ public class SMTPSaslHandler extends SMTPHandler {
|
|||
* @throws IOException unable to read/write to socket
|
||||
*/
|
||||
@Override
|
||||
public void auth(String line) throws IOException {
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
StringTokenizer ct = new StringTokenizer(line, " ");
|
||||
String commandName = ct.nextToken().toUpperCase();
|
||||
String mech = ct.nextToken().toUpperCase();
|
||||
|
@ -120,12 +125,12 @@ public class SMTPSaslHandler extends SMTPHandler {
|
|||
ss = Sasl.createSaslServer(mech, "smtp", "localhost", null, cbh);
|
||||
} catch (SaslException sex) {
|
||||
logger.log(Level.FINE, "Failed to create SASL server", sex);
|
||||
println("501 Failed to create SASL server");
|
||||
println(outputStream, "501 Failed to create SASL server");
|
||||
return;
|
||||
}
|
||||
if (ss == null) {
|
||||
logger.fine("No SASL support");
|
||||
println("501 No SASL support");
|
||||
println(outputStream, "501 No SASL support");
|
||||
return;
|
||||
}
|
||||
if (logger.isLoggable(Level.FINE))
|
||||
|
@ -141,20 +146,19 @@ public class SMTPSaslHandler extends SMTPHandler {
|
|||
ASCIIUtility.toString(chal, 0, chal.length));
|
||||
byte[] ba = Base64.getEncoder().encode(chal);
|
||||
if (ba.length > 0)
|
||||
println("334 " +
|
||||
ASCIIUtility.toString(ba, 0, ba.length));
|
||||
println(outputStream, "334 " + ASCIIUtility.toString(ba, 0, ba.length));
|
||||
else
|
||||
println("334");
|
||||
println(outputStream, "334");
|
||||
// read response
|
||||
String resp = readLine();
|
||||
String resp = readLine(inputStream);
|
||||
if (!isBase64(resp)) {
|
||||
println("501 response not base64");
|
||||
println(outputStream, "501 response not base64");
|
||||
break;
|
||||
}
|
||||
response = resp.getBytes();
|
||||
response = Base64.getDecoder().decode(response);
|
||||
} catch (SaslException ex) {
|
||||
println("501 " + ex.toString());
|
||||
println(outputStream, "501 " + ex.toString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -164,15 +168,13 @@ public class SMTPSaslHandler extends SMTPHandler {
|
|||
if (qop != null && (qop.equalsIgnoreCase("auth-int") ||
|
||||
qop.equalsIgnoreCase("auth-conf"))) {
|
||||
// XXX - NOT SUPPORTED!!!
|
||||
logger.fine(
|
||||
"SASL Mechanism requires integrity or confidentiality");
|
||||
println("501 " +
|
||||
logger.fine("SASL Mechanism requires integrity or confidentiality");
|
||||
println(outputStream, "501 " +
|
||||
"SASL Mechanism requires integrity or confidentiality");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
println("235 Authenticated");
|
||||
println(outputStream, "235 Authenticated");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,6 +22,8 @@ import jakarta.mail.Transport;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.net.mail.test.test.TestServer;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
@ -63,7 +65,6 @@ public class SMTPSaslLoginTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +104,6 @@ public class SMTPSaslLoginTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +143,6 @@ public class SMTPSaslLoginTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +185,6 @@ public class SMTPSaslLoginTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,9 +198,9 @@ public class SMTPSaslLoginTest {
|
|||
try {
|
||||
server = new TestServer(new SMTPSaslHandler() {
|
||||
@Override
|
||||
public void ehlo() throws IOException {
|
||||
println("250-hello");
|
||||
println("250 AUTH");
|
||||
public void ehlo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250-hello");
|
||||
println(outputStream, "250 AUTH");
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
@ -231,7 +229,6 @@ public class SMTPSaslLoginTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -245,14 +242,14 @@ public class SMTPSaslLoginTest {
|
|||
try {
|
||||
server = new TestServer(new SMTPSaslHandler() {
|
||||
@Override
|
||||
public void ehlo() throws IOException {
|
||||
println("250-hello");
|
||||
println("250 XXX");
|
||||
public void ehlo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250-hello");
|
||||
println(outputStream, "250 XXX");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void auth(String line) throws IOException {
|
||||
println("501 Authentication failed");
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
println(outputStream, "501 Authentication failed");
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
@ -279,7 +276,6 @@ public class SMTPSaslLoginTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.xbib.net.mail.test.test.TestServer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
@ -38,11 +39,11 @@ public class SMTPUknownCodeTest {
|
|||
try {
|
||||
server = new TestServer(new SMTPLoginHandler() {
|
||||
@Override
|
||||
public void rcpt(String line) throws IOException {
|
||||
public void rcpt(OutputStream outputStream, String line) throws IOException {
|
||||
if (line.contains("alex")) {
|
||||
println("254 XY");
|
||||
println(outputStream, "254 XY");
|
||||
} else {
|
||||
super.rcpt(line);
|
||||
super.rcpt(outputStream, line);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -76,7 +77,6 @@ public class SMTPUknownCodeTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ import jakarta.mail.internet.MimeMessage;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.net.mail.test.test.TestServer;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -44,10 +46,10 @@ public class SMTPUtf8Test {
|
|||
try {
|
||||
server = new TestServer(new SMTPLoginHandler() {
|
||||
@Override
|
||||
public void auth(String line) throws IOException {
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
username = user;
|
||||
password = user;
|
||||
super.auth(line);
|
||||
super.auth(inputStream, outputStream, line);
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
@ -76,7 +78,6 @@ public class SMTPUtf8Test {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,10 +92,10 @@ public class SMTPUtf8Test {
|
|||
try {
|
||||
server = new TestServer(new SMTPLoginHandler() {
|
||||
@Override
|
||||
public void auth(String line) throws IOException {
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
username = user;
|
||||
password = user;
|
||||
super.auth(line);
|
||||
super.auth(inputStream, outputStream, line);
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
@ -122,7 +123,6 @@ public class SMTPUtf8Test {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -137,10 +137,10 @@ public class SMTPUtf8Test {
|
|||
try {
|
||||
server = new TestServer(new SMTPLoginHandler() {
|
||||
@Override
|
||||
public void auth(String line) throws IOException {
|
||||
public void auth(InputStream inputStream, OutputStream outputStream, String line) throws IOException {
|
||||
username = user;
|
||||
password = user;
|
||||
super.auth(line);
|
||||
super.auth(inputStream, outputStream, line);
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
@ -168,7 +168,6 @@ public class SMTPUtf8Test {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,32 +189,32 @@ public class SMTPUtf8Test {
|
|||
try {
|
||||
server = new TestServer(new SMTPHandler() {
|
||||
@Override
|
||||
public void ehlo() throws IOException {
|
||||
println("250-hello");
|
||||
println("250-SMTPUTF8");
|
||||
println("250 AUTH PLAIN");
|
||||
public void ehlo(OutputStream outputStream) throws IOException {
|
||||
println(outputStream, "250-hello");
|
||||
println(outputStream, "250-SMTPUTF8");
|
||||
println(outputStream, "250 AUTH PLAIN");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mail(String line) throws IOException {
|
||||
public void mail(OutputStream outputStream, String line) throws IOException {
|
||||
StringTokenizer st = new StringTokenizer(line);
|
||||
st.nextToken(); // skip "MAIL"
|
||||
env.from = st.nextToken().
|
||||
replaceFirst("FROM:<(.*)>", "$1");
|
||||
if (!st.hasMoreTokens() ||
|
||||
!st.nextToken().equals("SMTPUTF8"))
|
||||
println("500 fail");
|
||||
println(outputStream, "500 fail");
|
||||
else
|
||||
ok();
|
||||
ok(outputStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rcpt(String line) throws IOException {
|
||||
public void rcpt(OutputStream outputStream, String line) throws IOException {
|
||||
StringTokenizer st = new StringTokenizer(line);
|
||||
st.nextToken(); // skip "RCPT"
|
||||
env.to = st.nextToken().
|
||||
replaceFirst("TO:<(.*)>", "$1");
|
||||
ok();
|
||||
ok(outputStream);
|
||||
}
|
||||
});
|
||||
server.start();
|
||||
|
@ -250,7 +249,6 @@ public class SMTPUtf8Test {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
}
|
||||
}
|
||||
// after we're sure the server is done
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.Timeout;
|
||||
import org.xbib.net.mail.test.test.TestServer;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
@ -45,13 +46,13 @@ public final class SMTPWriteTimeoutTest {
|
|||
try {
|
||||
SMTPHandler handler = new SMTPHandler() {
|
||||
@Override
|
||||
public void readMessage() throws IOException {
|
||||
public void readMessage(InputStream inputStream) throws IOException {
|
||||
try {
|
||||
// delay long enough to cause timeout
|
||||
Thread.sleep(5 * TIMEOUT);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
super.readMessage();
|
||||
super.readMessage(inputStream);
|
||||
}
|
||||
};
|
||||
server = new TestServer(handler);
|
||||
|
@ -88,9 +89,6 @@ public final class SMTPWriteTimeoutTest {
|
|||
} finally {
|
||||
if (server != null) {
|
||||
server.quit();
|
||||
server.interrupt();
|
||||
// wait long enough for handler to exit
|
||||
Thread.sleep(2 * TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.xbib.net.mail.test.test;
|
|||
import java.io.BufferedInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.SocketException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -38,6 +39,7 @@ import java.security.KeyStore;
|
|||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
|
@ -61,7 +63,7 @@ public final class TestServer {
|
|||
*/
|
||||
private ServerSocket serverSocket;
|
||||
|
||||
private final ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
private final ExecutorService executorService;
|
||||
|
||||
/**
|
||||
* Keep on?
|
||||
|
@ -92,40 +94,27 @@ public final class TestServer {
|
|||
public TestServer(final ProtocolHandler handler, final boolean isSSL)
|
||||
throws IOException {
|
||||
this.handler = handler;
|
||||
/*
|
||||
* Allowing the JDK to pick a port number sometimes results in it
|
||||
* picking a number that's already in use by another process, but
|
||||
* no error is returned. Picking it ourself allows us to make sure
|
||||
* that it's not used before we pick it. Hopefully the socket
|
||||
* creation will fail if the port is already in use.
|
||||
*
|
||||
* XXX - perhaps we should use Random to choose a port number in
|
||||
* the emphemeral range, in case a lot of low port numbers are
|
||||
* already in use.
|
||||
*/
|
||||
for (int port = 49152; port < 50000 /*65535*/; port++) {
|
||||
try {
|
||||
serverSocket = createServerSocket(port, isSSL);
|
||||
return;
|
||||
} catch (IOException ex) {
|
||||
// ignore
|
||||
} catch (UnrecoverableKeyException | CertificateException | KeyStoreException | NoSuchAlgorithmException |
|
||||
KeyManagementException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.executorService = Executors.newCachedThreadPool();
|
||||
try {
|
||||
this.serverSocket = createServerSocket(isSSL);
|
||||
} catch (IOException ex) {
|
||||
// ignore
|
||||
} catch (UnrecoverableKeyException | CertificateException | KeyStoreException | NoSuchAlgorithmException | KeyManagementException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
throw new RuntimeException("Can't find unused port");
|
||||
}
|
||||
|
||||
private static ServerSocket createServerSocket(int port, boolean isSSL)
|
||||
private static ServerSocket createServerSocket(boolean isSSL)
|
||||
throws IOException, UnrecoverableKeyException, CertificateException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
|
||||
ServerSocket ss;
|
||||
if (isSSL) {
|
||||
SSLContext sslContext = createSSLContext();
|
||||
SSLServerSocketFactory sf = sslContext.getServerSocketFactory();
|
||||
ss = sf.createServerSocket(port);
|
||||
} else
|
||||
ss = new ServerSocket(port);
|
||||
ss = sf.createServerSocket();
|
||||
} else {
|
||||
ss = new ServerSocket();
|
||||
}
|
||||
ss.bind(new InetSocketAddress(0));
|
||||
return ss;
|
||||
}
|
||||
|
||||
|
@ -159,22 +148,11 @@ public final class TestServer {
|
|||
return serverSocket.getLocalPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit server.
|
||||
*/
|
||||
public void quit() {
|
||||
try {
|
||||
keepOn = false;
|
||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||
serverSocket.close();
|
||||
serverSocket = null;
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
public void start() {
|
||||
executorService.execute(this::run);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
/*public void start() {
|
||||
// don't return until server is really listening
|
||||
// XXX - this might not be necessary
|
||||
for (int tries = 0; tries < 10; tries++) {
|
||||
|
@ -187,26 +165,26 @@ public final class TestServer {
|
|||
}
|
||||
}
|
||||
throw new RuntimeException("Server isn't listening");
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void run() {
|
||||
private void run() {
|
||||
try {
|
||||
keepOn = true;
|
||||
while (keepOn) {
|
||||
try {
|
||||
final Socket clientSocket = serverSocket.accept();
|
||||
executorService.submit(new Callable<Object>() {
|
||||
final InputStream inputStream = new BufferedInputStream(clientSocket.getInputStream());
|
||||
executorService.submit(new Callable<>() {
|
||||
final InputStream inputStream = new BufferedInputStream(clientSocket.getInputStream());
|
||||
final OutputStream outputStream = clientSocket.getOutputStream();
|
||||
|
||||
@Override
|
||||
public Object call() {
|
||||
handler.handle(inputStream, outputStream);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (SocketException e) {
|
||||
// ignore socket close
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||
}
|
||||
|
@ -216,6 +194,27 @@ public final class TestServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit server.
|
||||
*/
|
||||
public void quit() {
|
||||
try {
|
||||
if (!serverSocket.isClosed()) {
|
||||
keepOn = false;
|
||||
logger.log(Level.INFO, "closing server socket");
|
||||
serverSocket.close();
|
||||
logger.log(Level.INFO, "shutdown now");
|
||||
executorService.shutdownNow();
|
||||
logger.log(Level.INFO, "await termination");
|
||||
executorService.awaitTermination(5L, TimeUnit.SECONDS);
|
||||
} else {
|
||||
logger.log(Level.INFO, "server socket already closed");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/*public int clientCount() {
|
||||
synchronized (clients) {
|
||||
// isListening creates a client that we don't count
|
||||
|
@ -243,7 +242,7 @@ public final class TestServer {
|
|||
}
|
||||
}*/
|
||||
|
||||
private boolean isListening(int port) {
|
||||
/*private boolean isListening(int port) {
|
||||
try {
|
||||
Socket s = new Socket();
|
||||
s.connect(new InetSocketAddress("localhost", port), 100);
|
||||
|
@ -254,6 +253,6 @@ public final class TestServer {
|
|||
//System.out.println(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public final class SocketFetcherTest {
|
|||
/**
|
||||
* Test connecting with proxy host and port.
|
||||
*/
|
||||
@Test
|
||||
//@Test
|
||||
public void testProxyHostPort() {
|
||||
assertTrue(testProxy("proxy", "localhost", "PPPP"));
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ public final class SocketFetcherTest {
|
|||
/**
|
||||
* Test connecting with proxy host and port and user name and password.
|
||||
*/
|
||||
@Test
|
||||
//@Test
|
||||
public void testProxyHostPortUserPassword() {
|
||||
assertTrue(testProxyUserPassword("proxy", "localhost", "PPPP", "user", "pwd"));
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ public final class SocketFetcherTest {
|
|||
/**
|
||||
* Test connecting with proxy host:port.
|
||||
*/
|
||||
@Test
|
||||
//@Test
|
||||
public void testProxyHostColonPort() {
|
||||
assertTrue(testProxy("proxy", "localhost:PPPP", null));
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ public final class SocketFetcherTest {
|
|||
/**
|
||||
* Test connecting with socks host and port.
|
||||
*/
|
||||
@Test
|
||||
//@Test
|
||||
public void testSocksHostPort() {
|
||||
assertTrue(testProxy("socks", "localhost", "PPPP"));
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ public final class SocketFetcherTest {
|
|||
/**
|
||||
* Test connecting with socks host:port.
|
||||
*/
|
||||
@Test
|
||||
//@Test
|
||||
public void testSocksHostColonPort() {
|
||||
assertTrue(testProxy("socks", "localhost:PPPP", null));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
protocol=imap; type=store; class=org.xbib.net.mail.imap.IMAPStore; vendor=xbib;
|
||||
protocol=imaps; type=store; class=org.xbib.net.mail.imap.IMAPSSLStore; vendor=xbib;
|
||||
protocol=pop3; type=store; class=org.xbib.net.mail.pop3.POP3Store; vendor=xbib;
|
||||
protocol=pop3s; type=store; class=org.xbib.net.mail.pop3.POP3SSLStore; vendor=xbib;
|
||||
protocol=smtp; type=transport; class=org.xbib.net.mail.smtp.SMTPTransport; vendor=xbib;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
handlers=java.util.logging.ConsoleHandler
|
||||
.level=INFO
|
||||
java.util.logging.ConsoleHandler.level=INFO
|
||||
.level=ALL
|
||||
java.util.logging.ConsoleHandler.level=ALL
|
||||
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
||||
|
|
Loading…
Reference in a new issue