blob: fa478b0b6a1b655b9a9adeeb1b13822cff3b7497 [file] [log] [blame]
/****************************************************************
* 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.modules.protocols;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.inject.Provider;
import org.apache.commons.configuration2.ex.ConfigurationException;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.james.ProtocolConfigurationSanitizer;
import org.apache.james.RunArguments;
import org.apache.james.filesystem.api.FileSystem;
import org.apache.james.imap.api.display.Localizer;
import org.apache.james.imap.api.message.response.StatusResponseFactory;
import org.apache.james.imap.api.process.DefaultMailboxTyper;
import org.apache.james.imap.api.process.ImapProcessor;
import org.apache.james.imap.api.process.MailboxTyper;
import org.apache.james.imap.decode.ImapCommandParser;
import org.apache.james.imap.decode.ImapCommandParserFactory;
import org.apache.james.imap.decode.ImapDecoder;
import org.apache.james.imap.decode.base.AbstractImapCommandParser;
import org.apache.james.imap.decode.main.DefaultImapDecoder;
import org.apache.james.imap.decode.parser.ImapParserFactory;
import org.apache.james.imap.encode.ImapEncoder;
import org.apache.james.imap.encode.ImapResponseEncoder;
import org.apache.james.imap.encode.base.EndImapEncoder;
import org.apache.james.imap.encode.main.DefaultImapEncoderFactory;
import org.apache.james.imap.encode.main.DefaultLocalizer;
import org.apache.james.imap.message.response.UnpooledStatusResponseFactory;
import org.apache.james.imap.processor.AuthenticateProcessor;
import org.apache.james.imap.processor.CapabilityImplementingProcessor;
import org.apache.james.imap.processor.CapabilityProcessor;
import org.apache.james.imap.processor.DefaultProcessor;
import org.apache.james.imap.processor.EnableProcessor;
import org.apache.james.imap.processor.PermitEnableCapabilityProcessor;
import org.apache.james.imap.processor.SelectProcessor;
import org.apache.james.imap.processor.base.AbstractProcessor;
import org.apache.james.imap.processor.base.UnknownRequestProcessor;
import org.apache.james.imapserver.netty.IMAPServerFactory;
import org.apache.james.lifecycle.api.ConfigurationSanitizer;
import org.apache.james.metrics.api.GaugeRegistry;
import org.apache.james.metrics.api.MetricFactory;
import org.apache.james.protocols.lib.netty.CertificateReloadable;
import org.apache.james.server.core.configuration.ConfigurationProvider;
import org.apache.james.utils.ClassName;
import org.apache.james.utils.GuiceGenericLoader;
import org.apache.james.utils.GuiceProbe;
import org.apache.james.utils.InitializationOperation;
import org.apache.james.utils.InitilizationOperationBuilder;
import org.apache.james.utils.KeystoreCreator;
import com.github.fge.lambdas.Throwing;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
import com.google.inject.multibindings.Multibinder;
import com.google.inject.multibindings.ProvidesIntoSet;
public class IMAPServerModule extends AbstractModule {
private static Stream<Pair<Class, AbstractProcessor>> asPairStream(AbstractProcessor p) {
return p.acceptableClasses()
.stream().map(clazz -> Pair.of(clazz, p));
}
@Override
protected void configure() {
bind(Localizer.class).to(DefaultLocalizer.class);
bind(UnpooledStatusResponseFactory.class).in(Scopes.SINGLETON);
bind(StatusResponseFactory.class).to(UnpooledStatusResponseFactory.class);
bind(ImapProcessor.class).to(DefaultProcessor.class);
bind(CapabilityProcessor.class).in(Scopes.SINGLETON);
bind(AuthenticateProcessor.class).in(Scopes.SINGLETON);
bind(SelectProcessor.class).in(Scopes.SINGLETON);
bind(EnableProcessor.class).in(Scopes.SINGLETON);
bind(MailboxTyper.class).to(DefaultMailboxTyper.class).in(Scopes.SINGLETON);
Multibinder.newSetBinder(binder(), GuiceProbe.class).addBinding().to(ImapGuiceProbe.class);
Multibinder.newSetBinder(binder(), CertificateReloadable.Factory.class).addBinding().to(IMAPServerFactory.class);
}
@Provides
@Singleton
IMAPServerFactory provideServerFactory(FileSystem fileSystem, Provider<ImapDecoder> decoder, Provider<ImapEncoder> encoder, Provider<ImapProcessor> processor,
MetricFactory metricFactory, GaugeRegistry gaugeRegistry) {
return new IMAPServerFactory(fileSystem, decoder, encoder, processor, metricFactory, gaugeRegistry);
}
@Provides
DefaultProcessor provideClassImapProcessors(ImapPackage imapPackage, GuiceGenericLoader loader, StatusResponseFactory statusResponseFactory) {
ImmutableMap<Class, ImapProcessor> processors = imapPackage.processors()
.stream()
.map(Throwing.function(loader::instantiate))
.map(AbstractProcessor.class::cast)
.flatMap(IMAPServerModule::asPairStream)
.collect(ImmutableMap.toImmutableMap(
Pair::getLeft,
Pair::getRight));
Optional<EnableProcessor> enableProcessor = processors.values()
.stream()
.filter(EnableProcessor.class::isInstance)
.map(EnableProcessor.class::cast)
.findFirst();
Optional<CapabilityProcessor> capabilityProcessor = processors.values()
.stream()
.filter(CapabilityProcessor.class::isInstance)
.map(CapabilityProcessor.class::cast)
.findFirst();
enableProcessor.ifPresent(processor -> configureEnable(processor, processors));
capabilityProcessor.ifPresent(processor -> configureCapability(processor, processors));
return new DefaultProcessor(processors, new UnknownRequestProcessor(statusResponseFactory));
}
@Provides
@Singleton
ImapPackage providePackage(ConfigurationProvider configurationProvider, GuiceGenericLoader loader) {
try {
String[] imapPackages = configurationProvider.getConfiguration("imapserver")
.getStringArray("imapPackages");
ImmutableList<ImapPackage> packages = Optional.ofNullable(imapPackages)
.stream()
.flatMap(Arrays::stream)
.map(ClassName::new)
.map(Throwing.function(loader::instantiate))
.map(ImapPackage.class::cast)
.collect(ImmutableList.toImmutableList());
if (packages.isEmpty()) {
return ImapPackage.DEFAULT;
}
return ImapPackage.and(packages);
} catch (ConfigurationException e) {
throw new RuntimeException(e);
}
}
@Provides
ImapDecoder provideImapDecoder(ImapCommandParserFactory imapCommandParserFactory, StatusResponseFactory statusResponseFactory) {
return new DefaultImapDecoder(statusResponseFactory, imapCommandParserFactory);
}
@Provides
ImapEncoder provideImapEncoder(ImapPackage imapPackage, GuiceGenericLoader loader) {
Stream<ImapResponseEncoder> encoders = imapPackage.encoders()
.stream()
.map(Throwing.function(loader::instantiate))
.map(ImapResponseEncoder.class::cast);
return new DefaultImapEncoderFactory.DefaultImapEncoder(encoders, new EndImapEncoder());
}
@ProvidesIntoSet
InitializationOperation configureImap(ConfigurationProvider configurationProvider, IMAPServerFactory imapServerFactory) {
return InitilizationOperationBuilder
.forClass(IMAPServerFactory.class)
.init(() -> {
imapServerFactory.configure(configurationProvider.getConfiguration("imapserver"));
imapServerFactory.init();
});
}
private void configureEnable(EnableProcessor enableProcessor, ImmutableMap<Class, ImapProcessor> processorMap) {
processorMap.values().stream()
.filter(PermitEnableCapabilityProcessor.class::isInstance)
.map(PermitEnableCapabilityProcessor.class::cast)
.forEach(enableProcessor::addProcessor);
}
private void configureCapability(CapabilityProcessor capabilityProcessor, ImmutableMap<Class, ImapProcessor> processorMap) {
processorMap.values().stream()
.filter(CapabilityImplementingProcessor.class::isInstance)
.map(CapabilityImplementingProcessor.class::cast)
.forEach(capabilityProcessor::addProcessor);
}
@Provides
@Singleton
ImapCommandParserFactory provideImapCommandParserFactory(ImapPackage imapPackage, GuiceGenericLoader loader) {
ImmutableMap<String, ImapCommandParser> decoders = imapPackage.decoders()
.stream()
.map(Throwing.function(loader::instantiate))
.map(AbstractImapCommandParser.class::cast)
.collect(ImmutableMap.toImmutableMap(
parser -> parser.getCommand().getName(),
Function.identity()));
return new ImapParserFactory(decoders);
}
@ProvidesIntoSet
ConfigurationSanitizer configurationSanitizer(ConfigurationProvider configurationProvider, KeystoreCreator keystoreCreator,
FileSystem fileSystem, RunArguments runArguments) {
return new ProtocolConfigurationSanitizer(configurationProvider, keystoreCreator, fileSystem, runArguments, "imapserver");
}
}