All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hubspot.imap.ImapClientFactory Maven / Gradle / Ivy

There is a newer version: 0.6.1
Show newest version
package com.hubspot.imap;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.net.HostAndPort;
import com.hubspot.imap.client.ImapClient;
import com.hubspot.imap.protocol.command.ProxyCommand;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import java.io.Closeable;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.CompletableFuture;
import javax.net.ssl.SSLException;
import javax.net.ssl.TrustManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ImapClientFactory implements Closeable {

  private static final Logger LOGGER = LoggerFactory.getLogger(ImapClientFactory.class);

  private final ImapClientFactoryConfiguration configuration;
  private final SslContext sslContext;

  public ImapClientFactory() {
    this(ImapClientFactoryConfiguration.builder().build());
  }

  @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
  public ImapClientFactory(ImapClientFactoryConfiguration configuration) {
    this(configuration, (KeyStore) null);
  }

  @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
  public ImapClientFactory(
    ImapClientFactoryConfiguration configuration,
    KeyStore keyStore
  ) {
    this.configuration = configuration;

    try {
      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm()
      );
      trustManagerFactory.init(keyStore);

      sslContext =
        SslContextBuilder.forClient().trustManager(trustManagerFactory).build();
    } catch (NoSuchAlgorithmException | SSLException | KeyStoreException e) {
      throw new RuntimeException(e);
    }
  }

  public CompletableFuture connect(
    ImapClientConfiguration clientConfiguration
  ) {
    return connect("unknown-client", clientConfiguration);
  }

  public CompletableFuture connect(
    String clientName,
    ImapClientConfiguration clientConfiguration
  ) {
    Supplier sslContextSupplier = Suppliers.memoize(() ->
      getSslContext(clientConfiguration)
    );
    boolean useSsl =
      clientConfiguration.useSsl() && !clientConfiguration.proxyConfig().isPresent();

    Bootstrap bootstrap = new Bootstrap()
      .group(configuration.eventLoopGroup())
      .option(ChannelOption.SO_LINGER, clientConfiguration.soLinger())
      .option(
        ChannelOption.CONNECT_TIMEOUT_MILLIS,
        clientConfiguration.connectTimeoutMillis()
      )
      .option(ChannelOption.SO_KEEPALIVE, false)
      .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
      .channel(configuration.channelClass())
      .handler(
        useSsl
          ? new ImapChannelInitializer(
            sslContextSupplier.get(),
            clientName,
            clientConfiguration
          )
          : new ImapChannelInitializer(clientName, clientConfiguration)
      );

    HostAndPort connectHost = getConnectHost(clientConfiguration);

    CompletableFuture connectFuture = new CompletableFuture<>();

    bootstrap
      .connect(connectHost.getHost(), connectHost.getPort())
      .addListener(f -> {
        if (f.isSuccess()) {
          Channel channel = ((ChannelFuture) f).channel();

          ImapClient client = new ImapClient(
            clientConfiguration,
            channel,
            sslContextSupplier,
            configuration.executor(),
            clientName
          );
          configuration
            .executor()
            .execute(() -> {
              connectFuture.complete(client);
            });
        } else {
          configuration
            .executor()
            .execute(() -> connectFuture.completeExceptionally(f.cause()));
        }
      });

    return handleProxyConnect(clientConfiguration, connectFuture);
  }

  private SslContext getSslContext(ImapClientConfiguration clientConfiguration) {
    if (!clientConfiguration.trustManagerFactory().isPresent()) {
      return sslContext;
    }
    try {
      return SslContextBuilder
        .forClient()
        .trustManager(clientConfiguration.trustManagerFactory().get())
        .build();
    } catch (SSLException e) {
      throw new RuntimeException(e);
    }
  }

  private CompletableFuture handleProxyConnect(
    ImapClientConfiguration clientConfiguration,
    CompletableFuture connectFuture
  ) {
    if (!clientConfiguration.proxyConfig().isPresent()) {
      return connectFuture;
    }
    ProxyCommand proxyCommand = new ProxyCommand(
      clientConfiguration.hostAndPort(),
      clientConfiguration.proxyConfig().get().proxyLocalIpAddress()
    );
    return connectFuture.thenCompose(imapClient ->
      imapClient
        .send(proxyCommand)
        .thenApply(ignored -> {
          if (clientConfiguration.useSsl()) {
            imapClient.addTlsToChannel();
          }
          return imapClient;
        })
    );
  }

  private HostAndPort getConnectHost(ImapClientConfiguration clientConfiguration) {
    if (clientConfiguration.proxyConfig().isPresent()) {
      return clientConfiguration.proxyConfig().get().proxyHost();
    } else {
      return clientConfiguration.hostAndPort();
    }
  }

  @Override
  public void close() {
    configuration.eventLoopGroup().shutdownGracefully();
    configuration.executor().shutdownGracefully();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy