james-server-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From btell...@apache.org
Subject svn commit: r1720566 - in /james/project/trunk/server: ./ protocols/protocols-managesieve/ protocols/protocols-managesieve/src/ protocols/protocols-managesieve/src/main/ protocols/protocols-managesieve/src/main/java/ protocols/protocols-managesieve/src...
Date Thu, 17 Dec 2015 14:53:00 GMT
Author: btellier
Date: Thu Dec 17 14:53:00 2015
New Revision: 1720566

URL: http://svn.apache.org/viewvc?rev=1720566&view=rev
Log:
JAMES-1618 Write a ManageSieve server

Added:
    james/project/trunk/server/protocols/protocols-managesieve/
    james/project/trunk/server/protocols/protocols-managesieve/pom.xml
    james/project/trunk/server/protocols/protocols-managesieve/src/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java
    james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServerFactory.java
Modified:
    james/project/trunk/server/pom.xml

Modified: james/project/trunk/server/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/pom.xml?rev=1720566&r1=1720565&r2=1720566&view=diff
==============================================================================
--- james/project/trunk/server/pom.xml (original)
+++ james/project/trunk/server/pom.xml Thu Dec 17 14:53:00 2015
@@ -94,6 +94,7 @@
         <module>protocols/jmap-integration-testing</module>
         <module>protocols/protocols-library</module>
         <module>protocols/protocols-lmtp</module>
+        <module>protocols/protocols-managesieve</module>
         <module>protocols/protocols-pop3</module>
         <module>protocols/protocols-smtp</module>
 

Added: james/project/trunk/server/protocols/protocols-managesieve/pom.xml
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/protocols-managesieve/pom.xml?rev=1720566&view=auto
==============================================================================
--- james/project/trunk/server/protocols/protocols-managesieve/pom.xml (added)
+++ james/project/trunk/server/protocols/protocols-managesieve/pom.xml Thu Dec 17 14:53:00
2015
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>james-server</artifactId>
+        <groupId>org.apache.james</groupId>
+        <version>3.0.0-beta5-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>james-server-protocols-managesieve</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>james-server-protocols-library</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james</groupId>
+            <artifactId>james-server-filesystem-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.james.protocols</groupId>
+            <artifactId>protocols-managesieve</artifactId>
+            <version>${protocols.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.james.protocols</groupId>
+            <artifactId>protocols-netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-configuration</groupId>
+            <artifactId>commons-configuration</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-annotation_1.1_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.inject</groupId>
+            <artifactId>javax.inject</artifactId>
+        </dependency>
+    </dependencies>
+
+
+</project>
\ No newline at end of file

Added: james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java?rev=1720566&view=auto
==============================================================================
--- james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
(added)
+++ james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ChannelManageSieveResponseWriter.java
Thu Dec 17 14:53:00 2015
@@ -0,0 +1,43 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver.netty;
+
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.handler.stream.ChunkedStream;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+public class ChannelManageSieveResponseWriter {
+    private final Channel channel;
+
+    public ChannelManageSieveResponseWriter(Channel channel) {
+        this.channel = channel;
+    }
+
+    public void write(String response) throws IOException {
+        if (channel.isConnected()) {
+            InputStream in = new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8));
+            channel.write(new ChunkedStream(in));
+        }
+    }
+}

Added: james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java?rev=1720566&view=auto
==============================================================================
--- james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
(added)
+++ james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java
Thu Dec 17 14:53:00 2015
@@ -0,0 +1,102 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver.netty;
+
+import org.apache.james.managesieve.api.Session;
+import org.apache.james.managesieve.transcode.ManageSieveProcessor;
+import org.apache.james.managesieve.util.SettableSession;
+import org.apache.james.protocols.api.logger.ProtocolLoggerAdapter;
+import org.apache.james.protocols.api.logger.ProtocolSessionLogger;
+import org.apache.james.protocols.lib.Slf4jLoggerAdapter;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.Channel;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ChannelLocal;
+import org.jboss.netty.channel.ChannelStateEvent;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.handler.codec.frame.TooLongFrameException;
+import org.slf4j.Logger;
+
+import java.net.InetSocketAddress;
+
+public class ManageSieveChannelUpstreamHandler extends SimpleChannelUpstreamHandler {
+
+    private final Logger logger;
+    private final ChannelLocal<Session> attributes;
+    private final ManageSieveProcessor manageSieveProcessor;
+
+    public ManageSieveChannelUpstreamHandler(ManageSieveProcessor manageSieveProcessor, Logger
logger) {
+        this.logger = logger;
+        this.attributes = new ChannelLocal<Session>();
+        this.manageSieveProcessor = manageSieveProcessor;
+    }
+
+    @Override
+    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
+        String request = (String) e.getMessage();
+        Session manageSieveSession = attributes.get(ctx.getChannel());
+        String responseString = manageSieveProcessor.handleRequest(manageSieveSession, request);
+        ((ChannelManageSieveResponseWriter)ctx.getAttachment()).write(responseString);
+    }
+
+    @Override
+    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
{
+        getLogger(ctx.getChannel()).warn("Error while processing imap request: " + e.getCause().getClass().getName()
+ " - " + e.getCause().getMessage());
+        getLogger(ctx.getChannel()).debug("Error while processing imap request", e.getCause());
+
+        if (e.getCause() instanceof TooLongFrameException) {
+            // Max line length exceeded
+            //
+            // See also JAMES-1190
+            ((ChannelManageSieveResponseWriter)ctx.getAttachment()).write("NO Maximum command
line length exceeded");
+        } else {
+            // logout on error not sure if that is the best way to handle it
+            attributes.remove(ctx.getChannel());
+
+            // Make sure we close the channel after all the buffers were flushed out
+            Channel channel = ctx.getChannel();
+            if (channel.isConnected()) {
+                channel.write(ChannelBuffers.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
+            }
+        }
+    }
+
+    @Override
+    public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
+        attributes.set(ctx.getChannel(), new SettableSession());
+        ctx.setAttachment(new ChannelManageSieveResponseWriter(ctx.getChannel()));
+        super.channelBound(ctx, e);
+    }
+
+    @Override
+    public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
+        InetSocketAddress address = (InetSocketAddress) ctx.getChannel().getRemoteAddress();
+        getLogger(ctx.getChannel()).info("Connection closed for " + address.getAddress().getHostAddress());
+        attributes.remove(ctx.getChannel());
+        super.channelClosed(ctx, e);
+    }
+
+    private Logger getLogger(Channel channel) {
+        return new Slf4jLoggerAdapter(new ProtocolSessionLogger("" + channel.getId(), new
ProtocolLoggerAdapter(logger)));
+    }
+}

Added: james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java?rev=1720566&view=auto
==============================================================================
--- james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java
(added)
+++ james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java
Thu Dec 17 14:53:00 2015
@@ -0,0 +1,131 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver.netty;
+
+import org.apache.james.managesieve.transcode.ManageSieveProcessor;
+import org.apache.james.protocols.api.Encryption;
+import org.apache.james.protocols.lib.netty.AbstractConfigurableAsyncServer;
+import org.apache.james.protocols.netty.ChannelGroupHandler;
+import org.apache.james.protocols.netty.ConnectionLimitUpstreamHandler;
+import org.apache.james.protocols.netty.ConnectionPerIpLimitUpstreamHandler;
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.channel.ChannelUpstreamHandler;
+import org.jboss.netty.channel.group.ChannelGroup;
+import org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;
+import org.jboss.netty.handler.codec.frame.Delimiters;
+import org.jboss.netty.handler.codec.string.StringDecoder;
+import org.jboss.netty.handler.codec.string.StringEncoder;
+import org.jboss.netty.handler.execution.ExecutionHandler;
+import org.jboss.netty.handler.ssl.SslHandler;
+import org.jboss.netty.handler.stream.ChunkedWriteHandler;
+import org.jboss.netty.util.CharsetUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.net.ssl.SSLEngine;
+
+import static org.jboss.netty.channel.Channels.pipeline;
+
+public class ManageSieveServer extends AbstractConfigurableAsyncServer {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ManageSieveServer.class);
+
+    final static String SSL_HANDLER = "sslHandler";
+    final static String FRAMER = "framer";
+    final static String CORE_HANDLER = "coreHandler";
+    final static String GROUP_HANDLER = "groupHandler";
+    final static String CONNECTION_LIMIT_HANDLER = "connectionLimitHandler";
+    final static String CONNECTION_LIMIT_PER_IP_HANDLER = "connectionPerIpLimitHandler";
+    final static String CONNECTION_COUNT_HANDLER = "connectionCountHandler";
+    final static String CHUNK_WRITE_HANDLER = "chunkWriteHandler";
+    final static String EXECUTION_HANDLER = "executionHandler";
+
+
+    private int maxLineLength;
+    private ManageSieveProcessor manageSieveProcessor;
+
+    public ManageSieveServer(int maxLineLength, ManageSieveProcessor manageSieveProcessor)
{
+        this.maxLineLength = maxLineLength;
+        this.manageSieveProcessor = manageSieveProcessor;
+    }
+
+    @Override
+    protected int getDefaultPort() {
+        return 4190;
+    }
+
+    @Override
+    protected String getDefaultJMXName() {
+        return "managesieveserver";
+    }
+
+    @Override
+    protected ChannelUpstreamHandler createCoreHandler() {
+        return new ManageSieveChannelUpstreamHandler(manageSieveProcessor, LOGGER);
+    }
+
+    @Override
+    protected ChannelPipelineFactory createPipelineFactory(final ChannelGroup group) {
+
+        return new ChannelPipelineFactory() {
+
+            private final ChannelGroupHandler groupHandler = new ChannelGroupHandler(group);
+
+            public ChannelPipeline getPipeline() throws Exception {
+                ChannelPipeline pipeline = pipeline();
+                Encryption secure = getEncryption();
+                if (secure != null && !secure.isStartTLS()) {
+                    // We need to set clientMode to false.
+                    // See https://issues.apache.org/jira/browse/JAMES-1025
+                    SSLEngine engine = secure.getContext().createSSLEngine();
+                    engine.setUseClientMode(false);
+                    pipeline.addFirst(SSL_HANDLER, new SslHandler(engine));
+
+                }
+                pipeline.addLast(GROUP_HANDLER, groupHandler);
+                pipeline.addLast(CONNECTION_LIMIT_HANDLER, new ConnectionLimitUpstreamHandler(ManageSieveServer.this.connectionLimit));
+                pipeline.addLast(CONNECTION_LIMIT_PER_IP_HANDLER, new ConnectionPerIpLimitUpstreamHandler(ManageSieveServer.this.connPerIP));
+                // Add the text line decoder which limit the max line length,
+                // don't strip the delimiter and use CRLF as delimiter
+                // Use a SwitchableDelimiterBasedFrameDecoder, see JAMES-1436
+                pipeline.addLast(FRAMER, new DelimiterBasedFrameDecoder(maxLineLength, false,
Delimiters.lineDelimiter()));
+                pipeline.addLast(CONNECTION_COUNT_HANDLER, getConnectionCountHandler());
+                pipeline.addLast(CHUNK_WRITE_HANDLER, new ChunkedWriteHandler());
+
+                ExecutionHandler ehandler = getExecutionHandler();
+                if (ehandler  != null) {
+                    pipeline.addLast(EXECUTION_HANDLER, ehandler);
+
+                }
+                pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
+                pipeline.addLast(CORE_HANDLER, createCoreHandler());
+                pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
+                return pipeline;
+            }
+
+        };
+    }
+
+    @Override
+    public String getServiceType() {
+        return "Manage Sieve Service";
+    }
+}

Added: james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServerFactory.java
URL: http://svn.apache.org/viewvc/james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServerFactory.java?rev=1720566&view=auto
==============================================================================
--- james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServerFactory.java
(added)
+++ james/project/trunk/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServerFactory.java
Thu Dec 17 14:53:00 2015
@@ -0,0 +1,76 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.managesieveserver.netty;
+
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.apache.james.filesystem.api.FileSystem;
+import org.apache.james.managesieve.transcode.ManageSieveProcessor;
+import org.apache.james.protocols.lib.netty.AbstractConfigurableAsyncServer;
+import org.apache.james.protocols.lib.netty.AbstractServerFactory;
+import org.apache.james.sieverepository.api.SieveRepository;
+import org.apache.james.user.api.UsersRepository;
+import org.slf4j.Logger;
+
+import javax.inject.Inject;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ManageSieveServerFactory extends AbstractServerFactory {
+
+    private SieveRepository sieveRepository;
+    private UsersRepository usersRepository;
+    private FileSystem fileSystem;
+    private ManageSieveProcessor manageSieveProcessor;
+
+    public ManageSieveServerFactory() {
+        this.manageSieveProcessor = new ManageSieveProcessor();
+    }
+
+    @Inject
+    public void setSieveRepository(SieveRepository sieveRepository) {
+        this.sieveRepository = sieveRepository;
+    }
+
+    @Inject
+    public void setUsersRepository(UsersRepository usersRepository) {
+        this.usersRepository = usersRepository;
+    }
+
+    @Inject
+    public void setFileSystem(FileSystem fileSystem) {
+        this.fileSystem = fileSystem;
+    }
+
+    @Override
+    protected List<AbstractConfigurableAsyncServer> createServers(Logger log, HierarchicalConfiguration
config) throws Exception {
+        List<AbstractConfigurableAsyncServer> servers = new ArrayList<AbstractConfigurableAsyncServer>();
+        List<HierarchicalConfiguration> configs = config.configurationsAt("managesieveserver");
+
+        for (HierarchicalConfiguration serverConfig: configs) {
+            ManageSieveServer server = new ManageSieveServer(8000, manageSieveProcessor);
+            server.setLog(log);
+            server.setFileSystem(fileSystem);
+            server.configure(serverConfig);
+            servers.add(server);
+        }
+
+        return servers;
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org


Mime
View raw message