You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by ch...@apache.org on 2001/05/11 11:26:09 UTC
cvs commit: jakarta-james/src/org/apache/james/pop3server POP3Handler.java POP3Server.java POP3Server.xinfo
charlesb 01/05/11 02:26:09
Added: src/java/org/apache/james/mailrepository
AvalonMailRepository.java
AvalonSpoolRepository.java
FileMimeMessageInputStream.java
TownMimeMessageInputStream.java
TownSpoolRepository.java
src/java/org/apache/james/pop3server POP3Handler.java
POP3Server.java POP3Server.xinfo
Removed: src/org/apache/james/mailrepository
AvalonMailRepository.java
AvalonSpoolRepository.java
FileMimeMessageInputStream.java
TownMimeMessageInputStream.java
TownSpoolRepository.java
src/org/apache/james/pop3server POP3Handler.java
POP3Server.java POP3Server.xinfo
Log:
Moving from src/org to src/java/org
Revision Changes Path
1.1 jakarta-james/src/java/org/apache/james/mailrepository/AvalonMailRepository.java
Index: AvalonMailRepository.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.mailrepository;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.configuration.DefaultConfiguration;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.cornerstone.services.store.ObjectRepository;
import org.apache.avalon.cornerstone.services.store.Store;
import org.apache.avalon.cornerstone.services.store.StreamRepository;
import org.apache.james.core.MailImpl;
import org.apache.james.services.MailRepository;
import org.apache.james.services.MailStore;
import org.apache.james.util.Lock;
import org.apache.james.util.LockException;
/**
* Implementation of a MailRepository on a FileSystem.
*
* Requires a configuration element in the .conf.xml file of the form:
* <repository destinationURL="file://path-to-root-dir-for-repository"
* type="MAIL"
* model="SYNCHRONOUS"/>
* Requires a logger called MailRepository.
*
* @version 1.0.0, 24/04/1999
* @author Federico Barbieri <sc...@pop.systemy.it>
* @author Charles Benett <ch...@benett1.demon.co.uk>
*/
public class AvalonMailRepository
extends AbstractLoggable
implements MailRepository, Component, Configurable, Composable {
protected Lock lock;
protected static boolean DEEP_DEBUG = true;
private static final String TYPE = "MAIL";
private Store store;
private StreamRepository sr;
private ObjectRepository or;
private MailStore mailstore;
private String destination;
public void configure(Configuration conf) throws ConfigurationException {
destination = conf.getAttribute("destinationURL");
String checkType = conf.getAttribute("type");
if (! (checkType.equals("MAIL") || checkType.equals("SPOOL")) ) {
getLogger().warn( "Attempt to configure AvalonMailRepository as " +
checkType);
throw new ConfigurationException("Attempt to configure AvalonMailRepository as " + checkType);
}
// ignore model
}
public void compose( final ComponentManager componentManager )
throws ComponentException {
try {
store = (Store)componentManager.
lookup( "org.apache.avalon.cornerstone.services.store.Store" );
//prepare Configurations for object and stream repositories
DefaultConfiguration objectConfiguration
= new DefaultConfiguration( "repository",
"generated:AvalonFileRepository.compose()" );
objectConfiguration.addAttribute("destinationURL", destination);
objectConfiguration.addAttribute("type", "OBJECT");
objectConfiguration.addAttribute("model", "SYNCHRONOUS");
DefaultConfiguration streamConfiguration
= new DefaultConfiguration( "repository",
"generated:AvalonFileRepository.compose()" );
streamConfiguration.addAttribute( "destinationURL", destination );
streamConfiguration.addAttribute( "type", "STREAM" );
streamConfiguration.addAttribute( "model", "SYNCHRONOUS" );
sr = (StreamRepository) store.select(streamConfiguration);
or = (ObjectRepository) store.select(objectConfiguration);
lock = new Lock();
getLogger().debug(this.getClass().getName() + " created in " + destination);
} catch (Exception e) {
final String message = "Failed to retrieve Store component:" + e.getMessage();
getLogger().error( message, e );
throw new ComponentException( message, e );
}
}
public synchronized void unlock(Object key) {
if (lock.unlock(key)) {
notifyAll();
} else {
throw new LockException("Your thread do not own the lock of record " + key);
}
}
public synchronized void lock(Object key) {
if (lock.lock(key)) {
notifyAll();
} else {
throw new LockException("Record " + key + " already locked by another thread");
}
}
public synchronized void store(MailImpl mc) {
try {
String key = mc.getName();
OutputStream out = sr.put(key);
mc.writeMessageTo(out);
out.close();
or.put(key, mc);
if(DEEP_DEBUG) getLogger().debug("Mail " + key + " stored." );
notifyAll();
} catch (Exception e) {
getLogger().error("Exception storing mail: " + e);
e.printStackTrace();
throw new RuntimeException("Exception caught while storing Message Container: " + e);
}
}
public MailImpl retrieve(String key) {
if(DEEP_DEBUG) getLogger().debug("Retrieving mail: " + key);
MailImpl mc = (MailImpl) or.get(key);
try {
InputStream in = new FileMimeMessageInputStream(sr, key);
mc.setMessage(in);
in.close();
} catch (Exception me) {
getLogger().error("Exception retrieving mail: " + me);
throw new RuntimeException("Exception while retrieving mail: " + me.getMessage());
}
return mc;
}
public void remove(MailImpl mail) {
remove(mail.getName());
}
public void remove(String key) {
try {
lock( key);
sr.remove(key);
or.remove(key);
} finally {
unlock(key);
}
}
public Iterator list() {
return sr.list();
}
}
1.1 jakarta-james/src/java/org/apache/james/mailrepository/AvalonSpoolRepository.java
Index: AvalonSpoolRepository.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.mailrepository;
import java.util.Iterator;
import javax.mail.MessagingException;
import org.apache.james.util.Lock;
import org.apache.james.core.MailImpl;
import org.apache.james.services.MailStore;
import org.apache.james.services.SpoolRepository;
import org.apache.mailet.Mail;
/**
* Implementation of a MailRepository on a FileSystem.
*
* Requires a configuration element in the .conf.xml file of the form:
* <repository destinationURL="file://path-to-root-dir-for-repository"
* type="MAIL"
* model="SYNCHRONOUS"/>
* Requires a logger called MailRepository.
*
* @version 1.0.0, 24/04/1999
* @author Federico Barbieri <sc...@pop.systemy.it>
* @author Charles Benett <ch...@benett1.demon.co.uk>
*/
public class AvalonSpoolRepository
extends AvalonMailRepository
implements SpoolRepository {
public synchronized String accept() {
if (DEEP_DEBUG) getLogger().debug("Method accept() called");
while (true) {
for(Iterator it = list(); it.hasNext(); ) {
String s = it.next().toString();
if (DEEP_DEBUG) getLogger().debug("Found item " + s
+ " in spool.");
if (lock.lock(s)) {
if (DEEP_DEBUG) getLogger().debug("accept() has locked: "
+ s);
return s;
}
// Object o = it.next();
//if (lock.lock(o)) {
// return o.toString();
//}
}
try {
wait();
} catch (InterruptedException ignored) {
}
}
}
public synchronized String accept(long delay) {
if (DEEP_DEBUG) getLogger().debug("Method accept(delay) called");
while (true) {
long youngest = 0;
for (Iterator it = list(); it.hasNext(); ) {
String s = it.next().toString();
if (DEEP_DEBUG) getLogger().debug("Found item " + s
+ " in spool.");
if (lock.lock(s)) {
if (DEEP_DEBUG) getLogger().debug("accept(delay) has"
+ " locked: " + s);
//We have a lock on this object... let's grab the message
// and see if it's a valid time.
MailImpl mail = retrieve(s);
if (mail.getState().equals(Mail.ERROR)) {
//Test the time...
long timeToProcess = delay + mail.getLastUpdated().getTime();
if (System.currentTimeMillis() > timeToProcess) {
//We're ready to process this again
return s;
} else {
//We're not ready to process this.
if (youngest == 0 || youngest > timeToProcess) {
//Mark this as the next most likely possible mail to process
youngest = timeToProcess;
}
}
} else {
//This mail is good to go... return the key
return s;
}
}
}
//We did not find any... let's wait for a certain amount of time
try {
if (youngest == 0) {
wait();
} else {
wait(youngest - System.currentTimeMillis());
}
} catch (InterruptedException ignored) {
}
}
}
}
1.1 jakarta-james/src/java/org/apache/james/mailrepository/FileMimeMessageInputStream.java
Index: FileMimeMessageInputStream.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.mailrepository;
import java.io.IOException;
import java.io.InputStream;
import org.apache.avalon.cornerstone.services.store.StreamRepository;
import org.apache.james.core.JamesMimeMessageInputStream;
public class FileMimeMessageInputStream
extends JamesMimeMessageInputStream {
//Define how to get to the data
StreamRepository sr = null;
String key = null;
public FileMimeMessageInputStream( StreamRepository sr, String key) throws IOException {
this.sr = sr;
this.key = key;
}
public StreamRepository getStreamStore() {
return sr;
}
public String getKey() {
return key;
}
protected synchronized InputStream openStream() throws IOException {
return sr.get(key);
}
public boolean equals(Object obj) {
if (obj instanceof FileMimeMessageInputStream) {
FileMimeMessageInputStream in = (FileMimeMessageInputStream)obj;
return in.getStreamStore().equals(sr) && in.getKey().equals(key);
}
return false;
}
}
1.1 jakarta-james/src/java/org/apache/james/mailrepository/TownMimeMessageInputStream.java
Index: TownMimeMessageInputStream.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.mailrepository;
import com.workingdogs.town.ConnDefinition;
import com.workingdogs.town.QueryDataSet;
import com.workingdogs.town.Record;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.james.core.JamesMimeMessageInputStream;
public class TownMimeMessageInputStream
extends JamesMimeMessageInputStream {
//Define how to get to the data
String connDefinition = null;
String table = null;
String key = null;
String repository = null;
public TownMimeMessageInputStream(String connDefinition,
String table,
String key,
String repository) throws IOException {
if (connDefinition == null) {
throw new IOException("Conn definition is null");
}
if (table == null) {
throw new IOException("Table is null");
}
if (key == null) {
throw new IOException("Message name (key) was not defined");
}
if (repository == null) {
throw new IOException("Repository is null");
}
this.connDefinition = connDefinition;
this.table = table;
this.key = key;
this.repository = repository;
}
public String getConnDefinition() {
return connDefinition;
}
public String getTable() {
return table;
}
public String getMessageName() {
return key;
}
public String getRepository() {
return repository;
}
protected synchronized InputStream openStream() throws IOException {
//System.err.println("loading data for " + key + "/" + repository);
//TableDataSet messages = new TableDataSet(ConnDefinition.getInstance(connDefinition), table);
//messages.setWhere("message_name='" + key + "' and repository_name='" + repository + "'");
//messages.setColumns("message_body");
QueryDataSet messages = new QueryDataSet(ConnDefinition.getInstance(connDefinition),
"SELECT message_body "
+ " FROM " + table
+ " WHERE message_name='" + key + "' AND repository_name='" + repository + "'");
if (messages.size() == 0) {
throw new IOException("Could not find message");
}
Record message = messages.getRecord(0);
return new ByteArrayInputStream(message.getAsBytes("message_body"));
}
public boolean equals(Object obj) {
if (obj instanceof TownMimeMessageInputStream) {
TownMimeMessageInputStream in = (TownMimeMessageInputStream)obj;
return in.getConnDefinition().equals(connDefinition)
&& in.getTable().equals(table)
&& in.getMessageName().equals(key)
&& in.getRepository().equals(repository);
}
return false;
}
}
1.1 jakarta-james/src/java/org/apache/james/mailrepository/TownSpoolRepository.java
Index: TownSpoolRepository.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.mailrepository;
import com.workingdogs.town.ConnDefinition;
import com.workingdogs.town.QueryDataSet;
import com.workingdogs.town.Record;
import com.workingdogs.town.TableDataSet;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.mail.internet.MimeMessage;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.james.core.JamesMimeMessage;
import org.apache.james.core.MailImpl;
import org.apache.james.services.SpoolRepository;
import org.apache.james.util.Lock;
import org.apache.james.util.LockException;
import org.apache.mailet.Mail;
import org.apache.mailet.MailAddress;
/**
* Implementation of a SpoolRepository on a database.
*
* <p>Requires a configuration element in the .conf.xml file of the form:
* <br><repository destinationURL="town://path"
* <br> type="MAIL"
* <br> model="SYNCHRONOUS"/>
* <br> <conn>file:///dev/james/dist/var/maildatabase</conn>
* <br> <table>Message</table>
* <br></repository>
* <p>destinationURL specifies..(Serge??)
* <br>Type can be SPOOL or MAIL
* <br>Model is currently not used and may be dropped
* <br>conn is the location of the ...(Serge)
* <br>table is the name of the table in the Database to be used
*
* <p>Requires a logger called MailRepository.
*
* @version 1.0.0, 24/04/1999
* @author Serge Knystautas <se...@lokitech.com>
*/
public class TownSpoolRepository
extends AbstractLoggable
implements SpoolRepository, Component, Configurable {
private Lock lock;
private String destination;
private String repositoryName;
private String conndefinition;
private String tableName;
public void configure(Configuration conf) throws ConfigurationException {
destination = conf.getAttribute("destinationURL");
repositoryName = destination.substring(destination.indexOf("//") + 2);
String checkType = conf.getAttribute("type");
if (! (checkType.equals("MAIL") || checkType.equals("SPOOL")) ) {
final String message =
"Attempt to configure TownSpoolRepository as " + checkType;
getLogger().warn( message );
throw new ConfigurationException( message );
}
// ignore model
conndefinition = conf.getChild("conn").getValue();
tableName = conf.getChild("table").getValue();
}
public synchronized void unlock(Object key) {
if (lock.unlock(key)) {
notifyAll();
} else {
throw new LockException("Your thread do not own the lock of record " + key);
}
}
public synchronized void lock(Object key) {
if (lock.lock(key)) {
notifyAll();
} else {
throw new LockException("Record " + key + " already locked by another thread");
}
}
public void store(MailImpl mc) {
try {
//System.err.println("storing " + mc.getName());
String key = mc.getName();
mc.setLastUpdated(new Date());
TableDataSet messages = new TableDataSet(ConnDefinition.getInstance(conndefinition), tableName);
messages.setWhere("message_name = '" + key + "' and repository_name = '" + repositoryName + "'");
Record mail = null;
boolean inserted = true;
if (messages.size() == 0) {
inserted = true;
//insert the message
mail = messages.addRecord();
mail.setValue("message_name", key);
mail.setValue("repository_name", repositoryName + "");
} else {
//update the message
inserted = false;
mail = messages.getRecord(0);
}
mail.setValue("message_state", mc.getState());
mail.setValue("error_message", mc.getErrorMessage());
mail.setValue("sender", mc.getSender().toString());
StringBuffer recipients = new StringBuffer();
for (Iterator i = mc.getRecipients().iterator(); i.hasNext(); ) {
recipients.append(i.next().toString());
if (i.hasNext()) {
recipients.append("\r\n");
}
}
mail.setValue("recipients", recipients.toString());
mail.setValue("remote_host", mc.getRemoteHost());
mail.setValue("remote_addr", mc.getRemoteAddr());
mail.setValue("last_updated", mc.getLastUpdated());
MimeMessage messageBody = mc.getMessage();
boolean saveInRecord = false;
if (messageBody instanceof JamesMimeMessage) {
JamesMimeMessage jamesmessage = (JamesMimeMessage)messageBody;
if (jamesmessage.isModified()) {
//Just save it...we can't be clever here
saveInRecord = true;
} else {
if (mail.toBeSavedWithUpdate()) {
//Do nothing... the message wasn't changed.
//System.err.println("Not saving message (" + toString() + "... wasn't changed");
} else {
//For now always save the record
saveInRecord = true;
//This message could be a transfer from another source
InputStream in = jamesmessage.getSourceStream();
if (in instanceof TownMimeMessageInputStream) {
//This must already be stored in the same database (hopefully)
// Let's copy the record from here to there
//Once we do this, we'll no longer mark to saveInRecord
}
}
}
} else {
//This is some other unknown MimeMessage
saveInRecord = true;
}
if (saveInRecord) {
//Update this field here
ByteArrayOutputStream bout = new ByteArrayOutputStream();
if (messageBody != null) {
messageBody.writeTo(bout);
}
mail.setValue("message_body", bout.toByteArray());
}
if (mail.toBeSavedWithUpdate() && messageBody instanceof JamesMimeMessage
&& !((JamesMimeMessage)messageBody).isModified()) {
//Do nothing... the message wasn't changed.
} else {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
if (messageBody != null) {
messageBody.writeTo(bout);
}
mail.setValue("message_body", bout.toByteArray());
}
mail.save();
synchronized (this) {
notifyAll();
}
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Exception caught while storing mail Container: " + e);
}
}
public MailImpl retrieve(String key) {
try {
//System.err.println("retrieving " + key);
//TableDataSet messages = new TableDataSet(ConnDefinition.getInstance(conndefinition), tableName);
QueryDataSet messages = new QueryDataSet(ConnDefinition.getInstance(conndefinition),
"SELECT message_name, message_state, error_message, sender, recipients, remote_host, remote_addr, last_updated"
+ " FROM " + tableName
+ " WHERE message_name='" + key + "' and repository_name='" + repositoryName + "'");
//messages.setWhere("message_name='" + key + "' and repository_name='" + repositoryName + "'");
Record message = messages.getRecord(0);
MailImpl mc = new MailImpl();
mc.setName(message.getAsString("message_name"));
mc.setState(message.getAsString("message_state"));
mc.setErrorMessage(message.getAsString("error_message"));
mc.setSender(new MailAddress(message.getAsString("sender")));
StringTokenizer st = new StringTokenizer(message.getAsString("recipients"), "\r\n", false);
Set recipients = new HashSet();
while (st.hasMoreTokens()) {
recipients.add(new MailAddress(st.nextToken()));
}
mc.setRecipients(recipients);
mc.setRemoteHost(message.getAsString("remote_host"));
mc.setRemoteAddr(message.getAsString("remote_addr"));
mc.setLastUpdated(message.getAsUtilDate("last_updated"));
InputStream in = new TownMimeMessageInputStream(conndefinition, tableName, key, repositoryName);
//InputStream in = new ByteArrayInputStream(message.getAsBytes("message_body"));
JamesMimeMessage jamesmessage = new JamesMimeMessage(javax.mail.Session.getDefaultInstance(System.getProperties(), null), in);
mc.setMessage(jamesmessage);
//mc.setMessage(bin);
return mc;
} catch (Exception me) {
me.printStackTrace();
throw new RuntimeException("Exception while retrieving mail: " + me.getMessage());
}
}
public void remove(MailImpl mail) {
remove(mail.getName());
}
public void remove(String key) {
//System.err.println("removing " + key);
try {
lock(key);
TableDataSet messages = new TableDataSet(ConnDefinition.getInstance(conndefinition), tableName);
messages.setWhere("message_name='" + key + "' and repository_name='" + repositoryName + "'");
Record message = messages.getRecord(0);
message.markToBeDeleted();
message.save();
} catch (Exception me) {
throw new RuntimeException("Exception while removing mail: " + me.getMessage());
} finally {
unlock(key);
}
}
public Iterator list() {
try {
QueryDataSet messages = new QueryDataSet(ConnDefinition.getInstance(conndefinition),
"SELECT message_name FROM " + tableName + " WHERE repository_name = '" + repositoryName + "' "
+ "ORDER BY last_updated");
List messageList = new ArrayList(messages.size());
for (int i = 0; i < messages.size(); i++) {
messageList.add(messages.getRecord(i).getAsString("message_name"));
}
return messageList.iterator();
} catch (Exception me) {
me.printStackTrace();
throw new RuntimeException("Exception while listing mail: " + me.getMessage());
}
}
public synchronized String accept() {
while (true) {
for(Iterator it = list(); it.hasNext(); ) {
Object o = it.next();
if (lock.lock(o)) {
return o.toString();
}
}
try {
wait();
} catch (InterruptedException ignored) {
}
}
}
public synchronized String accept(long delay) {
while (true) {
long youngest = 0;
//Really unoptimized query here... should be much smart about this...
for (Iterator it = list(); it.hasNext(); ) {
String s = it.next().toString();
if (lock.lock(s)) {
//We have a lock on this object... let's grab the message
// and see if it's a valid time.
MailImpl mail = retrieve(s);
if (mail.getState().equals(Mail.ERROR)) {
//Test the time...
long timeToProcess = delay + mail.getLastUpdated().getTime();
if (System.currentTimeMillis() > timeToProcess) {
//We're ready to process this again
return s;
} else {
//We're not ready to process this.
if (youngest == 0 || youngest > timeToProcess) {
//Mark this as the next most likely possible mail to process
youngest = timeToProcess;
}
}
} else {
//This guy is good to go... return him
return s;
}
}
}
//We did not find any... let's wait for a certain amount of time
try {
if (youngest == 0) {
wait();
} else {
wait(youngest - System.currentTimeMillis());
}
} catch (InterruptedException ignored) {
}
}
}
}
1.1 jakarta-james/src/java/org/apache/james/pop3server/POP3Handler.java
Index: POP3Handler.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.pop3server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.mail.MessagingException;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.configuration.Configurable;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.cornerstone.services.connection.ConnectionHandler;
import org.apache.avalon.cornerstone.services.scheduler.PeriodicTimeTrigger;
import org.apache.avalon.cornerstone.services.scheduler.Target;
import org.apache.avalon.cornerstone.services.scheduler.TimeScheduler;
import org.apache.avalon.excalibur.collections.ListUtils;
import org.apache.james.Constants;
import org.apache.james.core.MailImpl;
import org.apache.james.services.MailRepository;
import org.apache.james.services.MailServer;
import org.apache.james.services.UsersRepository;
import org.apache.james.services.UsersStore;
import org.apache.james.BaseConnectionHandler;
import org.apache.james.util.InternetPrintWriter;
import org.apache.mailet.Mail;
/**
* @author Federico Barbieri <sc...@systemy.it>
* @version 0.9
*/
public class POP3Handler
extends BaseConnectionHandler
implements ConnectionHandler, Composable, Configurable, Target {
private String softwaretype = "JAMES POP3 Server " + Constants.SOFTWARE_VERSION;
private ComponentManager compMgr;
private MailServer mailServer;
private MailRepository userInbox;
private UsersRepository users;
private TimeScheduler scheduler;
private Socket socket;
private BufferedReader in;
private PrintWriter out;
private OutputStream outs;
private String remoteHost;
private String remoteIP;
private int state;
private String user;
private Vector userMailbox = new Vector();
private Vector backupUserMailbox;
private static final Mail DELETED = new MailImpl();
private static int AUTHENTICATION_READY = 0;
private static int AUTHENTICATION_USERSET = 1;
private static int TRANSACTION = 2;
private final static String OK_RESPONSE = "+OK";
private final static String ERR_RESPONSE = "-ERR";
public void compose( final ComponentManager componentManager )
throws ComponentException {
mailServer = (MailServer)componentManager.
lookup( "org.apache.james.services.MailServer" );
UsersStore usersStore = (UsersStore)componentManager.
lookup( "org.apache.james.services.UsersStore" );
users = usersStore.getRepository("LocalUsers");
scheduler = (TimeScheduler)componentManager.
lookup( "org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" );
}
/**
* Handle a connection.
* This handler is responsible for processing connections as they occur.
*
* @param connection the connection
* @exception IOException if an error reading from socket occurs
* @exception ProtocolException if an error handling connection occurs
*/
public void handleConnection( Socket connection )
throws IOException {
try {
this.socket = connection;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
outs = socket.getOutputStream();
out = new InternetPrintWriter(outs, true);
remoteHost = socket.getInetAddress ().getHostName ();
remoteIP = socket.getInetAddress ().getHostAddress ();
} catch (Exception e) {
getLogger().error( "Cannot open connection from " + remoteHost +
" (" + remoteIP + "): " + e.getMessage(), e );
}
getLogger().info( "Connection from " + remoteHost + " (" + remoteIP + ")" );
try {
final PeriodicTimeTrigger trigger = new PeriodicTimeTrigger( timeout, -1 );
scheduler.addTrigger( this.toString(), trigger, this );
state = AUTHENTICATION_READY;
user = "unknown";
out.println( OK_RESPONSE + " " + this.helloName +
" POP3 server (" + this.softwaretype + ") ready " );
while (parseCommand(in.readLine())) {
scheduler.resetTrigger(this.toString());
}
socket.close();
scheduler.removeTrigger(this.toString());
getLogger().info("Connection closed");
} catch (Exception e) {
out.println(ERR_RESPONSE + " Error closing connection.");
out.flush();
getLogger().error( "Exception during connection from " + remoteHost +
" (" + remoteIP + ") : " + e.getMessage(), e );
try {
socket.close();
} catch (IOException ioe) {
}
}
}
public void targetTriggered( final String triggerName ) {
getLogger().error("Connection timeout on socket");
try {
out.println("Connection timeout. Closing connection");
socket.close();
} catch (IOException e) {
}
}
private void stat() {
userMailbox = new Vector();
userMailbox.addElement(DELETED);
for (Iterator it = userInbox.list(); it.hasNext(); ) {
String key = (String) it.next();
MailImpl mc = userInbox.retrieve(key);
userMailbox.addElement(mc);
}
backupUserMailbox = (Vector) userMailbox.clone();
}
private boolean parseCommand(String commandRaw) {
if (commandRaw == null) return false;
getLogger().info("Command received: " + commandRaw);
String command = commandRaw.trim();
StringTokenizer commandLine = new StringTokenizer(command, " ");
int arguments = commandLine.countTokens();
if (arguments == 0) {
return true;
} else if(arguments > 0) {
command = commandLine.nextToken();
}
String argument = (String) null;
if(arguments > 1) {
argument = commandLine.nextToken();
}
String argument1 = (String) null;
if(arguments > 2) {
argument1 = commandLine.nextToken();
}
if (command.equalsIgnoreCase("USER"))
doUSER(command,argument,argument1);
else if (command.equalsIgnoreCase("PASS"))
doPASS(command,argument,argument1);
else if (command.equalsIgnoreCase("STAT"))
doSTAT(command,argument,argument1);
else if (command.equalsIgnoreCase("LIST"))
doLIST(command,argument,argument1);
else if (command.equalsIgnoreCase("UIDL"))
doUIDL(command,argument,argument1);
else if (command.equalsIgnoreCase("RSET"))
doRSET(command,argument,argument1);
else if (command.equalsIgnoreCase("DELE"))
doDELE(command,argument,argument1);
else if (command.equalsIgnoreCase("NOOP"))
doNOOP(command,argument,argument1);
else if (command.equalsIgnoreCase("RETR"))
doRETR(command,argument,argument1);
else if (command.equalsIgnoreCase("TOP"))
doTOP(command,argument,argument1);
else if (command.equalsIgnoreCase("QUIT"))
doQUIT(command,argument,argument1);
else
doUnknownCmd(command,argument,argument1);
return (command.equalsIgnoreCase("QUIT") == false);
}
private void doUSER(String command,String argument,String argument1) {
if (state == AUTHENTICATION_READY && argument != null) {
user = argument;
state = AUTHENTICATION_USERSET;
out.println(OK_RESPONSE);
} else {
out.println(ERR_RESPONSE);
}
}
private void doPASS(String command,String argument,String argument1) {
if (state == AUTHENTICATION_USERSET && argument != null) {
String passArg = argument;
if (users.test(user, passArg)) {
state = TRANSACTION;
out.println(OK_RESPONSE + " Welcome " + user);
userInbox = mailServer.getUserInbox(user);
stat();
} else {
state = AUTHENTICATION_READY;
out.println(ERR_RESPONSE + " Authentication failed.");
}
} else {
out.println(ERR_RESPONSE);
}
}
private void doSTAT(String command,String argument,String argument1) {
if (state == TRANSACTION) {
long size = 0;
int count = 0;
try {
for (Enumeration e = userMailbox.elements(); e.hasMoreElements(); ) {
MailImpl mc = (MailImpl) e.nextElement();
if (mc != DELETED) {
size += mc.getSize();
count++;
}
}
out.println(OK_RESPONSE + " " + count + " " + size);
} catch (MessagingException me) {
out.println(ERR_RESPONSE);
}
} else {
out.println(ERR_RESPONSE);
}
}
private void doLIST(String command,String argument,String argument1) {
if (state == TRANSACTION) {
if (argument == null) {
long size = 0;
int count = 0;
try {
for (Enumeration e = userMailbox.elements(); e.hasMoreElements(); ) {
MailImpl mc = (MailImpl) e.nextElement();
if (mc != DELETED) {
size += mc.getSize();
count++;
}
}
out.println(OK_RESPONSE + " " + count + " " + size);
count = 0;
for (Enumeration e = userMailbox.elements(); e.hasMoreElements(); count++) {
MailImpl mc = (MailImpl) e.nextElement();
if (mc != DELETED) {
out.println(count + " " + mc.getSize());
}
}
out.println(".");
} catch (MessagingException me) {
out.println(ERR_RESPONSE);
}
} else {
int num = 0;
try {
num = Integer.parseInt(argument);
MailImpl mc = (MailImpl) userMailbox.elementAt(num);
if (mc != DELETED) {
out.println(OK_RESPONSE + " " + num + " " + mc.getSize());
} else {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
}
} catch (ArrayIndexOutOfBoundsException npe) {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
} catch (NumberFormatException nfe) {
out.println(ERR_RESPONSE + " " + argument + " is not a valid number");
} catch (MessagingException me) {
out.println(ERR_RESPONSE);
}
}
} else {
out.println(ERR_RESPONSE);
}
}
private void doUIDL(String command,String argument,String argument1) {
if (state == TRANSACTION) {
if (argument == null) {
out.println(OK_RESPONSE + " unique-id listing follows");
int count = 0;
for (Enumeration e = userMailbox.elements(); e.hasMoreElements(); count++) {
MailImpl mc = (MailImpl) e.nextElement();
if (mc != DELETED) {
out.println(count + " " + mc.getName());
}
}
out.println(".");
} else {
int num = 0;
try {
num = Integer.parseInt(argument);
MailImpl mc = (MailImpl) userMailbox.elementAt(num);
if (mc != DELETED) {
out.println(OK_RESPONSE + " " + num + " " + mc.getName());
} else {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
}
} catch (ArrayIndexOutOfBoundsException npe) {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
} catch (NumberFormatException nfe) {
out.println(ERR_RESPONSE + " " + argument + " is not a valid number");
}
}
} else {
out.println(ERR_RESPONSE);
}
}
private void doRSET(String command,String argument,String argument1) {
if (state == TRANSACTION) {
stat();
out.println(OK_RESPONSE);
} else {
out.println(ERR_RESPONSE);
}
}
private void doDELE(String command,String argument,String argument1) {
if (state == TRANSACTION) {
int num = 0;
try {
num = Integer.parseInt(argument);
} catch (Exception e) {
out.println(ERR_RESPONSE + " Usage: DELE [mail number]");
return;
}
try {
MailImpl mc = (MailImpl) userMailbox.elementAt(num);
if (mc == DELETED) {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
} else {
userMailbox.setElementAt(DELETED, num);
out.println(OK_RESPONSE + " Message removed");
}
} catch (ArrayIndexOutOfBoundsException iob) {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
}
} else {
out.println(ERR_RESPONSE);
}
}
private void doNOOP(String command,String argument,String argument1) {
if (state == TRANSACTION) {
out.println(OK_RESPONSE);
} else {
out.println(ERR_RESPONSE);
}
}
private void doRETR(String command,String argument,String argument1) {
if (state == TRANSACTION) {
int num = 0;
try {
num = Integer.parseInt(argument.trim());
} catch (Exception e) {
out.println(ERR_RESPONSE + " Usage: RETR [mail number]");
return;
}
//?May be written as
//return parseCommand("TOP " + num + " " + Integer.MAX_VALUE);?
try {
MailImpl mc = (MailImpl) userMailbox.elementAt(num);
if (mc != DELETED) {
out.println(OK_RESPONSE + " Message follows");
mc.writeMessageTo(outs);
out.println();
out.println(".");
} else {
out.println(ERR_RESPONSE + " Message (" + num + ") deleted.");
}
} catch (IOException ioe) {
out.println(ERR_RESPONSE + " Error while retrieving message.");
} catch (MessagingException me) {
out.println(ERR_RESPONSE + " Error while retrieving message.");
} catch (ArrayIndexOutOfBoundsException iob) {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
}
// -------------------------------------------?
} else {
out.println(ERR_RESPONSE);
}
}
private void doTOP(String command,String argument,String argument1) {
if (state == TRANSACTION) {
int num = 0;
int lines = 0;
try {
num = Integer.parseInt(argument);
lines = Integer.parseInt(argument1);
} catch (NumberFormatException nfe) {
out.println(ERR_RESPONSE + " Usage: TOP [mail number] [Line number]");
return;
}
try {
MailImpl mc = (MailImpl) userMailbox.elementAt(num);
if (mc != DELETED) {
out.println(OK_RESPONSE + " Message follows");
for (Enumeration e = mc.getMessage().getAllHeaderLines(); e.hasMoreElements(); ) {
out.println(e.nextElement());
}
out.println("");
mc.writeContentTo(outs, lines);
out.println(".");
} else {
out.println(ERR_RESPONSE + " Message (" + num + ") already deleted.");
}
} catch (IOException ioe) {
out.println(ERR_RESPONSE + " Error while retrieving message.");
} catch (MessagingException me) {
out.println(ERR_RESPONSE + " Error while retrieving message.");
} catch (ArrayIndexOutOfBoundsException iob) {
out.println(ERR_RESPONSE + " Message (" + num + ") does not exist.");
}
} else {
out.println(ERR_RESPONSE);
}
}
private void doQUIT(String command,String argument,String argument1) {
if (state == AUTHENTICATION_READY || state == AUTHENTICATION_USERSET) {
return;
}
List toBeRemoved = ListUtils.subtract(backupUserMailbox, userMailbox);
try {
for (Iterator it = toBeRemoved.iterator(); it.hasNext(); ) {
MailImpl mc = (MailImpl) it.next();
userInbox.remove(mc.getName());
}
out.println(OK_RESPONSE + " Apache James POP3 Server signing off.");
} catch (Exception ex) {
out.println(ERR_RESPONSE + " Some deleted messages were not removed");
getLogger().error("Some deleted messages were not removed: " + ex.getMessage());
}
}
private void doUnknownCmd(String command,String argument,String argument1) {
out.println(ERR_RESPONSE);
}
}
1.1 jakarta-james/src/java/org/apache/james/pop3server/POP3Server.java
Index: POP3Server.java
===================================================================
/*
* Copyright (C) The Apache Software Foundation. All rights reserved.
*
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package org.apache.james.pop3server;
import java.net.InetAddress;
import java.net.UnknownHostException;
import org.apache.avalon.framework.configuration.Configuration;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.cornerstone.services.connection.AbstractService;
import org.apache.avalon.cornerstone.services.connection.ConnectionHandlerFactory;
import org.apache.avalon.cornerstone.services.connection.DefaultHandlerFactory;
/**
* @version 1.0.0, 24/04/1999
* @author Federico Barbieri <sc...@pop.systemy.it>
*/
public class POP3Server
extends AbstractService {
protected ConnectionHandlerFactory createFactory()
{
return new DefaultHandlerFactory( POP3Handler.class );
}
public void configure( final Configuration configuration )
throws ConfigurationException {
m_port = configuration.getChild( "port" ).getValueAsInteger( 25 );
try
{
final String bindAddress = configuration.getChild( "bind" ).getValue( null );
if( null != bindAddress )
{
m_bindTo = InetAddress.getByName( bindAddress );
}
}
catch( final UnknownHostException unhe )
{
throw new ConfigurationException( "Malformed bind parameter", unhe );
}
final String useTLS = configuration.getChild("useTLS").getValue( "" );
if( useTLS.equals( "TRUE" ) ) m_serverSocketType = "ssl";
super.configure( configuration.getChild( "handler" ) );
}
public void initialize() throws Exception {
getLogger().info( "POP3Server init..." );
getLogger().info( "POP3Listener using " + m_serverSocketType + " on port " + m_port );
super.initialize();
getLogger().info( "POP3Server ...init end" );
System.out.println("Started POP3 Server "+m_connectionName);
}
}
1.1 jakarta-james/src/java/org/apache/james/pop3server/POP3Server.xinfo
Index: POP3Server.xinfo
===================================================================
<?xml version="1.0"?>
<blockinfo>
<services>
<service name="org.apache.avalon.framework.component.Component" version="1.0"/>
</services>
<dependencies>
<dependency>
<role>org.apache.james.services.MailStore</role>
<service name="org.apache.james.services.MailStore" version="1.0"/>
</dependency>
<dependency>
<role>org.apache.james.services.UsersStore</role>
<service name="org.apache.james.services.UsersStore" version="1.0"/>
</dependency>
<dependency>
<role>org.apache.avalon.cornerstone.services.connection.ConnectionManager</role>
<service name="org.apache.avalon.cornerstone.services.connection.ConnectionManager"
version="1.0"/>
</dependency>
<dependency>
<role>org.apache.avalon.cornerstone.services.sockets.SocketManager</role>
<service name="org.apache.avalon.cornerstone.services.sockets.SocketManager" version="1.0"/>
</dependency>
<dependency>
<role>org.apache.avalon.cornerstone.services.scheduler.TimeScheduler</role>
<service name="org.apache.avalon.cornerstone.services.scheduler.TimeScheduler" version="1.0"/>
</dependency>
<dependency>
<role>org.apache.james.services.MailServer</role>
<service name="org.apache.james.services.MailServer" version="1.0"/>
</dependency>
</dependencies>
</blockinfo>
---------------------------------------------------------------------
To unsubscribe, e-mail: james-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: james-dev-help@jakarta.apache.org