001/**
002 *
003 * Copyright 2003-2007 Jive Software, 2017-2020 Florian Schmaus.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.jivesoftware.smack;
019
020import java.io.ByteArrayInputStream;
021import java.io.FileInputStream;
022import java.io.IOException;
023import java.io.InputStream;
024import java.lang.reflect.Constructor;
025import java.lang.reflect.InvocationTargetException;
026import java.net.InetAddress;
027import java.net.UnknownHostException;
028import java.nio.charset.StandardCharsets;
029import java.security.KeyManagementException;
030import java.security.KeyStore;
031import java.security.KeyStoreException;
032import java.security.NoSuchAlgorithmException;
033import java.security.NoSuchProviderException;
034import java.security.Provider;
035import java.security.Security;
036import java.security.UnrecoverableKeyException;
037import java.security.cert.CertificateException;
038import java.util.Arrays;
039import java.util.Collection;
040import java.util.Collections;
041import java.util.HashSet;
042import java.util.Locale;
043import java.util.Set;
044import java.util.logging.Level;
045import java.util.logging.Logger;
046
047import javax.net.SocketFactory;
048import javax.net.ssl.HostnameVerifier;
049import javax.net.ssl.KeyManager;
050import javax.net.ssl.KeyManagerFactory;
051import javax.net.ssl.SSLContext;
052import javax.net.ssl.TrustManager;
053import javax.net.ssl.X509TrustManager;
054import javax.security.auth.callback.Callback;
055import javax.security.auth.callback.CallbackHandler;
056import javax.security.auth.callback.PasswordCallback;
057import javax.security.auth.callback.UnsupportedCallbackException;
058
059import org.jivesoftware.smack.datatypes.UInt16;
060import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
061import org.jivesoftware.smack.internal.SmackTlsContext;
062import org.jivesoftware.smack.packet.id.StandardStanzaIdSource;
063import org.jivesoftware.smack.packet.id.StanzaIdSource;
064import org.jivesoftware.smack.packet.id.StanzaIdSourceFactory;
065import org.jivesoftware.smack.proxy.ProxyInfo;
066import org.jivesoftware.smack.sasl.SASLMechanism;
067import org.jivesoftware.smack.sasl.core.SASLAnonymous;
068import org.jivesoftware.smack.util.CloseableUtil;
069import org.jivesoftware.smack.util.CollectionUtil;
070import org.jivesoftware.smack.util.DNSUtil;
071import org.jivesoftware.smack.util.Objects;
072import org.jivesoftware.smack.util.SslContextFactory;
073import org.jivesoftware.smack.util.StringUtils;
074import org.jivesoftware.smack.util.TLSUtils;
075import org.jivesoftware.smack.util.dns.SmackDaneProvider;
076import org.jivesoftware.smack.util.dns.SmackDaneVerifier;
077
078import org.jxmpp.jid.DomainBareJid;
079import org.jxmpp.jid.EntityBareJid;
080import org.jxmpp.jid.impl.JidCreate;
081import org.jxmpp.jid.parts.Resourcepart;
082import org.jxmpp.stringprep.XmppStringprepException;
083import org.minidns.dnsname.DnsName;
084import org.minidns.dnsname.InvalidDnsNameException;
085import org.minidns.util.InetAddressUtil;
086
087/**
088 * The connection configuration used for XMPP client-to-server connections. A well configured XMPP service will
089 * typically only require you to provide two parameters: The XMPP address, also known as the JID, of the user and the
090 * password. All other configuration parameters could ideally be determined automatically by Smack. Hence it is often
091 * enough to call {@link Builder#setXmppAddressAndPassword(CharSequence, String)}.
092 * <p>
093 * Technically there are typically at least two parameters required: Some kind of credentials for authentication. And
094 * the XMPP service domain. The credentials often consists of a username and password use for the SASL authentication.
095 * But there are also other authentication mechanisms, like client side certificates, which do not require a particular
096 * username and password.
097 * </p>
098 * <p>
099 * There are some misconceptions about XMPP client-to-server parameters: The first is that the username used for
100 * authentication will be equal to the localpart of the bound XMPP address after authentication. While this is usually
101 * true, it is not required. Technically the username used for authentication and the resulting XMPP address are
102 * completely independent from each other. The second common misconception steers from the terms "XMPP host" and "XMPP
103 * service domain": An XMPP service host is a system which hosts one or multiple XMPP domains. The "XMPP service domain"
104 * will be usually the domainpart of the bound JID. This domain is used to verify the remote endpoint, typically using
105 * TLS. This third misconception is that the XMPP service domain is required to become the domainpart of the bound JID.
106 * Again, while this is very common to be true, it is not strictly required.
107 * </p>
108 *
109 * @author Gaston Dombiak
110 * @author Florian Schmaus
111 */
112public abstract class ConnectionConfiguration {
113
114    static {
115        Smack.ensureInitialized();
116    }
117
118    private static final Logger LOGGER = Logger.getLogger(ConnectionConfiguration.class.getName());
119
120    /**
121     * The XMPP domain of the XMPP Service. Usually servers use the same service name as the name
122     * of the server. However, there are some servers like google where host would be
123     * talk.google.com and the serviceName would be gmail.com.
124     */
125    protected final DomainBareJid xmppServiceDomain;
126
127    protected final DnsName xmppServiceDomainDnsName;
128
129    protected final InetAddress hostAddress;
130    protected final DnsName host;
131    protected final UInt16 port;
132
133    /**
134     * Used to get information from the user
135     */
136    private final CallbackHandler callbackHandler;
137
138    private final SmackDebuggerFactory debuggerFactory;
139
140    // Holds the socket factory that is used to generate the socket in the connection
141    private final SocketFactory socketFactory;
142
143    private final CharSequence username;
144    private final String password;
145    private final Resourcepart resource;
146
147    private final Locale language;
148
149    /**
150     * The optional SASL authorization identity (see RFC 6120 § 6.3.8).
151     */
152    private final EntityBareJid authzid;
153
154    /**
155     * Initial presence as of RFC 6121 § 4.2
156     * @see <a href="http://xmpp.org/rfcs/rfc6121.html#presence-initial">RFC 6121 § 4.2 Initial Presence</a>
157     */
158    private final boolean sendPresence;
159
160    private final SecurityMode securityMode;
161
162    final SmackTlsContext smackTlsContext;
163
164    private final DnssecMode dnssecMode;
165
166    /**
167     *
168     */
169    private final String[] enabledSSLProtocols;
170
171    /**
172     *
173     */
174    private final String[] enabledSSLCiphers;
175
176    private final HostnameVerifier hostnameVerifier;
177
178    // Holds the proxy information (such as proxyhost, proxyport, username, password etc)
179    protected final ProxyInfo proxy;
180
181    protected final boolean allowNullOrEmptyUsername;
182
183    private final Set<String> enabledSaslMechanisms;
184
185    private final boolean compressionEnabled;
186
187    private final StanzaIdSourceFactory stanzaIdSourceFactory;
188
189    protected ConnectionConfiguration(Builder<?, ?> builder) {
190        try {
191            smackTlsContext = getSmackTlsContext(builder.dnssecMode, builder.sslContextFactory,
192                            builder.customX509TrustManager, builder.keystoreType, builder.keystorePath,
193                            builder.callbackHandler, builder.pkcs11Library);
194        } catch (UnrecoverableKeyException | KeyManagementException | NoSuchAlgorithmException | CertificateException
195                        | KeyStoreException | NoSuchProviderException | IOException | NoSuchMethodException
196                        | SecurityException | ClassNotFoundException | InstantiationException | IllegalAccessException
197                        | IllegalArgumentException | InvocationTargetException | UnsupportedCallbackException e) {
198            throw new IllegalArgumentException(e);
199        }
200
201        authzid = builder.authzid;
202        username = builder.username;
203        password = builder.password;
204        callbackHandler = builder.callbackHandler;
205
206        // Resource can be null, this means that the server must provide one
207        resource = builder.resource;
208
209        language = builder.language;
210
211        xmppServiceDomain = builder.xmppServiceDomain;
212        if (xmppServiceDomain == null) {
213            throw new IllegalArgumentException("Must define the XMPP domain");
214        }
215
216        DnsName xmppServiceDomainDnsName;
217        try {
218            xmppServiceDomainDnsName = DnsName.from(xmppServiceDomain);
219        } catch (InvalidDnsNameException e) {
220            LOGGER.log(Level.INFO,
221                            "Could not transform XMPP service domain '" + xmppServiceDomain
222                          + "' to a DNS name. TLS X.509 certificate validiation may not be possible.",
223                            e);
224            xmppServiceDomainDnsName = null;
225        }
226        this.xmppServiceDomainDnsName = xmppServiceDomainDnsName;
227
228        hostAddress = builder.hostAddress;
229        host = builder.host;
230        port = builder.port;
231
232        proxy = builder.proxy;
233        socketFactory = builder.socketFactory;
234
235        dnssecMode = builder.dnssecMode;
236
237        securityMode = builder.securityMode;
238        enabledSSLProtocols = builder.enabledSSLProtocols;
239        enabledSSLCiphers = builder.enabledSSLCiphers;
240        hostnameVerifier = builder.hostnameVerifier;
241        sendPresence = builder.sendPresence;
242        debuggerFactory = builder.debuggerFactory;
243        allowNullOrEmptyUsername = builder.allowEmptyOrNullUsername;
244        enabledSaslMechanisms = builder.enabledSaslMechanisms;
245
246        compressionEnabled = builder.compressionEnabled;
247
248        stanzaIdSourceFactory = builder.stanzaIdSourceFactory;
249
250        // If the enabledSaslmechanisms are set, then they must not be empty
251        assert enabledSaslMechanisms == null || !enabledSaslMechanisms.isEmpty();
252    }
253
254    private static SmackTlsContext getSmackTlsContext(DnssecMode dnssecMode, SslContextFactory sslContextFactory,
255                    X509TrustManager trustManager, String keystoreType, String keystorePath,
256                    CallbackHandler callbackHandler, String pkcs11Library) throws NoSuchAlgorithmException,
257                    CertificateException, IOException, KeyStoreException, NoSuchProviderException,
258                    UnrecoverableKeyException, KeyManagementException, UnsupportedCallbackException,
259                    NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException,
260                    IllegalAccessException, IllegalArgumentException, InvocationTargetException {
261        final SSLContext context;
262        if (sslContextFactory != null) {
263            context = sslContextFactory.createSslContext();
264        } else {
265            // If the user didn't specify a SslContextFactory, use the default one
266            context = SSLContext.getInstance("TLS");
267        }
268
269        KeyStore ks = null;
270        PasswordCallback pcb = null;
271        KeyManager[] kms = null;
272
273        if ("PKCS11".equals(keystoreType)) {
274                Constructor<?> c = Class.forName("sun.security.pkcs11.SunPKCS11").getConstructor(InputStream.class);
275                String pkcs11Config = "name = SmartCard\nlibrary = " + pkcs11Library;
276                ByteArrayInputStream config = new ByteArrayInputStream(pkcs11Config.getBytes(StandardCharsets.UTF_8));
277                Provider p = (Provider) c.newInstance(config);
278                Security.addProvider(p);
279                ks = KeyStore.getInstance("PKCS11", p);
280                pcb = new PasswordCallback("PKCS11 Password: ", false);
281                callbackHandler.handle(new Callback[] { pcb });
282                ks.load(null, pcb.getPassword());
283        } else if ("Apple".equals(keystoreType)) {
284            ks = KeyStore.getInstance("KeychainStore", "Apple");
285            ks.load(null, null);
286            // pcb = new PasswordCallback("Apple Keychain",false);
287            // pcb.setPassword(null);
288        } else if (keystoreType != null) {
289            ks = KeyStore.getInstance(keystoreType);
290            if (callbackHandler != null && StringUtils.isNotEmpty(keystorePath)) {
291                pcb = new PasswordCallback("Keystore Password: ", false);
292                callbackHandler.handle(new Callback[] { pcb });
293                ks.load(new FileInputStream(keystorePath), pcb.getPassword());
294            } else {
295                InputStream stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
296                try {
297                    // Note that PKCS12 keystores need a password one some Java platforms. Hence we try the famous
298                    // 'changeit' here. See https://bugs.openjdk.java.net/browse/JDK-8194702
299                    char[] password = "changeit".toCharArray();
300                    try {
301                        ks.load(stream, password);
302                    } finally {
303                        CloseableUtil.maybeClose(stream);
304                    }
305                } catch (IOException e) {
306                    LOGGER.log(Level.FINE, "KeyStore load() threw, attempting 'jks' fallback", e);
307
308                    ks = KeyStore.getInstance("jks");
309                    // Open the stream again, so that we read it from the beginning.
310                    stream = TLSUtils.getDefaultTruststoreStreamIfPossible();
311                    try {
312                        ks.load(stream, null);
313                    } finally {
314                        CloseableUtil.maybeClose(stream);
315                    }
316                }
317            }
318        }
319
320        if (ks != null) {
321            String keyManagerFactoryAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
322            KeyManagerFactory kmf = KeyManagerFactory.getInstance(keyManagerFactoryAlgorithm);
323            if (kmf != null) {
324                if (pcb == null) {
325                    kmf.init(ks, null);
326                } else {
327                    kmf.init(ks, pcb.getPassword());
328                    pcb.clearPassword();
329                }
330                kms = kmf.getKeyManagers();
331            }
332        }
333
334        SmackDaneVerifier daneVerifier = null;
335        if (dnssecMode == DnssecMode.needsDnssecAndDane) {
336            SmackDaneProvider daneProvider = DNSUtil.getDaneProvider();
337            if (daneProvider == null) {
338                throw new UnsupportedOperationException("DANE enabled but no SmackDaneProvider configured");
339            }
340            daneVerifier = daneProvider.newInstance();
341            if (daneVerifier == null) {
342                throw new IllegalStateException("DANE requested but DANE provider did not return a DANE verifier");
343            }
344
345            // User requested DANE verification.
346            daneVerifier.init(context, kms, trustManager, null);
347        } else {
348            final TrustManager[] trustManagers;
349            if (trustManager != null) {
350                trustManagers = new TrustManager[] { trustManager };
351            } else {
352                // Ensure trustManagers is null in case there was no explicit trust manager provided, so that the
353                // default one is used.
354                trustManagers = null;
355            }
356
357            context.init(kms, trustManagers, null);
358        }
359
360        return new SmackTlsContext(context, daneVerifier);
361    }
362
363    public DnsName getHost() {
364        return host;
365    }
366
367    public InetAddress getHostAddress() {
368        return hostAddress;
369    }
370
371    public UInt16 getPort() {
372        return port;
373    }
374
375    /**
376     * Returns the server name of the target server.
377     *
378     * @return the server name of the target server.
379     * @deprecated use {@link #getXMPPServiceDomain()} instead.
380     */
381    @Deprecated
382    public DomainBareJid getServiceName() {
383        return xmppServiceDomain;
384    }
385
386    /**
387     * Returns the XMPP domain used by this configuration.
388     *
389     * @return the XMPP domain.
390     */
391    public DomainBareJid getXMPPServiceDomain() {
392        return xmppServiceDomain;
393    }
394
395    /**
396     * Returns the XMPP service domain as DNS name if possible. Note that since not every XMPP address domainpart is a
397     * valid DNS name, this method may return <code>null</code>.
398     *
399     * @return the XMPP service domain as DNS name or <code>null</code>.
400     * @since 4.3.4
401     */
402    public DnsName getXmppServiceDomainAsDnsNameIfPossible() {
403        return xmppServiceDomainDnsName;
404    }
405
406    /**
407     * Returns the TLS security mode used when making the connection. By default,
408     * the mode is {@link SecurityMode#ifpossible}.
409     *
410     * @return the security mode.
411     */
412    public SecurityMode getSecurityMode() {
413        return securityMode;
414    }
415
416    public DnssecMode getDnssecMode() {
417        return dnssecMode;
418    }
419
420    /**
421     * Return the enabled SSL/TLS protocols.
422     *
423     * @return the enabled SSL/TLS protocols
424     */
425    public String[] getEnabledSSLProtocols() {
426        return enabledSSLProtocols;
427    }
428
429    /**
430     * Return the enabled SSL/TLS ciphers.
431     *
432     * @return the enabled SSL/TLS ciphers
433     */
434    public String[] getEnabledSSLCiphers() {
435        return enabledSSLCiphers;
436    }
437
438    /**
439     * Returns the configured HostnameVerifier of this ConnectionConfiguration or the Smack default
440     * HostnameVerifier configured with
441     * {@link SmackConfiguration#setDefaultHostnameVerifier(HostnameVerifier)}.
442     *
443     * @return a configured HostnameVerifier or <code>null</code>
444     */
445    public HostnameVerifier getHostnameVerifier() {
446        if (hostnameVerifier != null)
447            return hostnameVerifier;
448        return SmackConfiguration.getDefaultHostnameVerifier();
449    }
450
451    /**
452     * Returns the Smack debugger factory.
453     *
454     * @return the Smack debugger factory or <code>null</code>
455     */
456    public SmackDebuggerFactory getDebuggerFactory() {
457        return debuggerFactory;
458    }
459
460    /**
461     * Returns a CallbackHandler to obtain information, such as the password or
462     * principal information during the SASL authentication. A CallbackHandler
463     * will be used <b>ONLY</b> if no password was specified during the login while
464     * using SASL authentication.
465     *
466     * @return a CallbackHandler to obtain information, such as the password or
467     * principal information during the SASL authentication.
468     */
469    public CallbackHandler getCallbackHandler() {
470        return callbackHandler;
471    }
472
473    /**
474     * Returns the socket factory used to create new xmppConnection sockets.
475     * This is useful when connecting through SOCKS5 proxies.
476     *
477     * @return socketFactory used to create new sockets.
478     */
479    public SocketFactory getSocketFactory() {
480        return this.socketFactory;
481    }
482
483    /**
484     * Get the configured proxy information (if any).
485     *
486     * @return the configured proxy information or <code>null</code>.
487     */
488    public ProxyInfo getProxyInfo() {
489        return proxy;
490    }
491
492    /**
493     * An enumeration for TLS security modes that are available when making a connection
494     * to the XMPP server.
495     */
496    public enum SecurityMode {
497
498        /**
499         * Security via TLS encryption is required in order to connect. If the server
500         * does not offer TLS or if the TLS negotiation fails, the connection to the server
501         * will fail.
502         */
503        required,
504
505        /**
506         * Security via TLS encryption is used whenever it's available. This is the
507         * default setting.
508         * <p>
509         * <b>Do not use this setting</b> unless you can't use {@link #required}. An attacker could easily perform a
510         * Man-in-the-middle attack and prevent TLS from being used, leaving you with an unencrypted (and
511         * unauthenticated) connection.
512         * </p>
513         */
514        ifpossible,
515
516        /**
517         * Security via TLS encryption is disabled and only un-encrypted connections will
518         * be used. If only TLS encryption is available from the server, the connection
519         * will fail.
520         */
521        disabled
522    }
523
524    /**
525     * Determines the requested DNSSEC security mode.
526     * <b>Note that Smack's support for DNSSEC/DANE is experimental!</b>
527     * <p>
528     * The default '{@link #disabled}' means that neither DNSSEC nor DANE verification will be performed. When
529     * '{@link #needsDnssec}' is used, then the connection will not be established if the resource records used to connect
530     * to the XMPP service are not authenticated by DNSSEC. Additionally, if '{@link #needsDnssecAndDane}' is used, then
531     * the XMPP service's TLS certificate is verified using DANE.
532     *
533     */
534    public enum DnssecMode {
535
536        /**
537         * Do not perform any DNSSEC authentication or DANE verification.
538         */
539        disabled,
540
541        /**
542         * <b>Experimental!</b>
543         * Require all DNS information to be authenticated by DNSSEC.
544         */
545        needsDnssec,
546
547        /**
548         * <b>Experimental!</b>
549         * Require all DNS information to be authenticated by DNSSEC and require the XMPP service's TLS certificate to be verified using DANE.
550         */
551        needsDnssecAndDane,
552
553    }
554
555    /**
556     * Returns the username to use when trying to reconnect to the server.
557     *
558     * @return the username to use when trying to reconnect to the server.
559     */
560    public CharSequence getUsername() {
561        return this.username;
562    }
563
564    /**
565     * Returns the password to use when trying to reconnect to the server.
566     *
567     * @return the password to use when trying to reconnect to the server.
568     */
569    public String getPassword() {
570        return this.password;
571    }
572
573    /**
574     * Returns the resource to use when trying to reconnect to the server.
575     *
576     * @return the resource to use when trying to reconnect to the server.
577     */
578    public Resourcepart getResource() {
579        return resource;
580    }
581
582    /**
583     * Returns the stream language to use when connecting to the server.
584     *
585     * @return the stream language to use when connecting to the server.
586     */
587    public Locale getLanguage() {
588        return language;
589    }
590
591    /**
592     * Returns the xml:lang string of the stream language to use when connecting to the server.
593     *
594     * <p>If the developer sets the language to null, this will also return null, leading to
595     * the removal of the xml:lang tag from the stream. If a Locale("") is configured, this will
596     * return "", which can be used as an override.</p>
597     *
598     * @return the stream language to use when connecting to the server.
599     */
600    public String getXmlLang() {
601        // TODO: Change to Locale.toLanguageTag() once Smack's minimum Android API level is 21 or higher.
602        // This will need a workaround for new Locale("").getLanguageTag() returning "und". Expected
603        // behavior of this function:
604        //  - returns null if language is null
605        //  - returns "" if language.getLanguage() returns the empty string
606        //  - returns language.toLanguageTag() otherwise
607        return language != null ? language.toString().replace("_", "-") : null;
608    }
609
610    /**
611     * Returns the optional XMPP address to be requested as the SASL authorization identity.
612     *
613     * @return the authorization identifier.
614     * @see <a href="http://tools.ietf.org/html/rfc6120#section-6.3.8">RFC 6120 § 6.3.8. Authorization Identity</a>
615     * @since 4.2
616     */
617    public EntityBareJid getAuthzid() {
618        return authzid;
619    }
620
621    /**
622     * Returns true if an available presence should be sent when logging in while reconnecting.
623     *
624     * @return true if an available presence should be sent when logging in while reconnecting
625     */
626    public boolean isSendPresence() {
627        return sendPresence;
628    }
629
630    /**
631     * Returns true if the connection is going to use stream compression. Stream compression
632     * will be requested after TLS was established (if TLS was enabled) and only if the server
633     * offered stream compression. With stream compression network traffic can be reduced
634     * up to 90%. By default compression is disabled.
635     *
636     * @return true if the connection is going to use stream compression.
637     */
638    public boolean isCompressionEnabled() {
639        return compressionEnabled;
640    }
641
642    /**
643     * Check if the given SASL mechansism is enabled in this connection configuration.
644     *
645     * @param saslMechanism TODO javadoc me please
646     * @return true if the given SASL mechanism is enabled, false otherwise.
647     */
648    public boolean isEnabledSaslMechanism(String saslMechanism) {
649        // If enabledSaslMechanisms is not set, then all mechanisms which are not blacklisted are enabled per default.
650        if (enabledSaslMechanisms == null) {
651            return !SASLAuthentication.getBlacklistedSASLMechanisms().contains(saslMechanism);
652        }
653        return enabledSaslMechanisms.contains(saslMechanism);
654    }
655
656    /**
657     * Return the explicitly enabled SASL mechanisms. May return <code>null</code> if no SASL mechanisms where
658     * explicitly enabled, i.e. all SALS mechanisms supported and announced by the service will be considered.
659     *
660     * @return the enabled SASL mechanisms or <code>null</code>.
661     */
662    public Set<String> getEnabledSaslMechanisms() {
663        if (enabledSaslMechanisms == null) {
664            return null;
665        }
666        return Collections.unmodifiableSet(enabledSaslMechanisms);
667    }
668
669    StanzaIdSource constructStanzaIdSource() {
670        return stanzaIdSourceFactory.constructStanzaIdSource();
671    }
672
673    /**
674     * A builder for XMPP connection configurations.
675     * <p>
676     * This is an abstract class that uses the builder design pattern and the "getThis() trick" to recover the type of
677     * the builder in a class hierarchies with a self-referential generic supertype. Otherwise chaining of build
678     * instructions from the superclasses followed by build instructions of a subclass would not be possible, because
679     * the superclass build instructions would return the builder of the superclass and not the one of the subclass. You
680     * can read more about it a Angelika Langer's Generics FAQ, especially the entry <a
681     * href="http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206">What is the
682     * "getThis()" trick?</a>.
683     * </p>
684     *
685     * @param <B> the builder type parameter.
686     * @param <C> the resulting connection configuration type parameter.
687     */
688    public abstract static class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
689        private SecurityMode securityMode = SecurityMode.required;
690        private DnssecMode dnssecMode = DnssecMode.disabled;
691        private String keystorePath;
692        private String keystoreType;
693        private String pkcs11Library = "pkcs11.config";
694        private SslContextFactory sslContextFactory;
695        private String[] enabledSSLProtocols;
696        private String[] enabledSSLCiphers;
697        private HostnameVerifier hostnameVerifier;
698        private EntityBareJid authzid;
699        private CharSequence username;
700        private String password;
701        private Resourcepart resource;
702        private Locale language = Locale.getDefault();
703        private boolean sendPresence = true;
704        private ProxyInfo proxy;
705        private CallbackHandler callbackHandler;
706        private SmackDebuggerFactory debuggerFactory;
707        private SocketFactory socketFactory;
708        private DomainBareJid xmppServiceDomain;
709        private InetAddress hostAddress;
710        private DnsName host;
711        private UInt16 port = UInt16.from(5222);
712        private boolean allowEmptyOrNullUsername = false;
713        private boolean saslMechanismsSealed;
714        private Set<String> enabledSaslMechanisms;
715        private X509TrustManager customX509TrustManager;
716        private boolean compressionEnabled = false;
717        private StanzaIdSourceFactory stanzaIdSourceFactory = new StandardStanzaIdSource.Factory();
718
719        protected Builder() {
720            if (SmackConfiguration.DEBUG) {
721                enableDefaultDebugger();
722            }
723        }
724
725        /**
726         * Convenience method to configure the username, password and XMPP service domain.
727         *
728         * @param jid the XMPP address of the user.
729         * @param password the password of the user.
730         * @return a reference to this builder.
731         * @throws XmppStringprepException in case the XMPP address is not valid.
732         * @see #setXmppAddressAndPassword(EntityBareJid, String)
733         * @since 4.4.0
734         */
735        public B setXmppAddressAndPassword(CharSequence jid, String password) throws XmppStringprepException {
736            return setXmppAddressAndPassword(JidCreate.entityBareFrom(jid), password);
737        }
738
739        /**
740         * Convenience method to configure the username, password and XMPP service domain. The localpart of the provided
741         * JID is used as username and the domanipart is used as XMPP service domain.
742         * <p>
743         * Please note that this does and can not configure the client XMPP address. XMPP services are not required to
744         * assign bound JIDs where the localpart matches the username and the domainpart matches the verified domainpart.
745         * Although most services will follow that pattern.
746         * </p>
747         *
748         * @param jid TODO javadoc me please
749         * @param password TODO javadoc me please
750         * @return a reference to this builder.
751         * @since 4.4.0
752         */
753        public B setXmppAddressAndPassword(EntityBareJid jid, String password) {
754            setUsernameAndPassword(jid.getLocalpart(), password);
755            return setXmppDomain(jid.asDomainBareJid());
756        }
757
758        /**
759         * Set the XMPP entities username and password.
760         * <p>
761         * The username is usually the localpart of the clients JID. But some SASL mechanisms or services may require a different
762         * format (e.g. the full JID) as used authorization identity.
763         * </p>
764         *
765         * @param username the username or authorization identity
766         * @param password the password or token used to authenticate
767         * @return a reference to this builder.
768         */
769        public B setUsernameAndPassword(CharSequence username, String password) {
770            this.username = username;
771            this.password = password;
772            return getThis();
773        }
774
775        /**
776         * Set the XMPP domain. The XMPP domain is what follows after the '@' sign in XMPP addresses (JIDs).
777         *
778         * @param serviceName the service name
779         * @return a reference to this builder.
780         * @deprecated use {@link #setXmppDomain(DomainBareJid)} instead.
781         */
782        @Deprecated
783        public B setServiceName(DomainBareJid serviceName) {
784            return setXmppDomain(serviceName);
785        }
786
787        /**
788         * Set the XMPP domain. The XMPP domain is what follows after the '@' sign in XMPP addresses (JIDs).
789         *
790         * @param xmppDomain the XMPP domain.
791         * @return a reference to this builder.
792         */
793        public B setXmppDomain(DomainBareJid xmppDomain) {
794            this.xmppServiceDomain = xmppDomain;
795            return getThis();
796        }
797
798        /**
799         * Set the XMPP domain. The XMPP domain is what follows after the '@' sign in XMPP addresses (JIDs).
800         *
801         * @param xmppServiceDomain the XMPP domain.
802         * @return a reference to this builder.
803         * @throws XmppStringprepException if the given string is not a domain bare JID.
804         */
805        public B setXmppDomain(String xmppServiceDomain) throws XmppStringprepException {
806            this.xmppServiceDomain = JidCreate.domainBareFrom(xmppServiceDomain);
807            return getThis();
808        }
809
810        /**
811         * Set the resource we are requesting from the server.
812         * <p>
813         * If <code>resource</code> is <code>null</code>, the default, then the server will automatically create a
814         * resource for the client. Note that XMPP clients only suggest this resource to the server. XMPP servers are
815         * allowed to ignore the client suggested resource and instead assign a completely different
816         * resource (see RFC 6120 § 7.7.1).
817         * </p>
818         *
819         * @param resource the resource to use.
820         * @return a reference to this builder.
821         * @see <a href="https://tools.ietf.org/html/rfc6120#section-7.7.1">RFC 6120 § 7.7.1</a>
822         */
823        public B setResource(Resourcepart resource) {
824            this.resource = resource;
825            return getThis();
826        }
827
828        /**
829         * Set the stream language.
830         *
831         * @param language the language to use.
832         * @return a reference to this builder.
833         * @see <a href="https://tools.ietf.org/html/rfc6120#section-4.7.4">RFC 6120 § 4.7.4</a>
834         * @see <a href="https://www.w3.org/TR/xml/#sec-lang-tag">XML 1.0 § 2.12 Language Identification</a>
835         */
836        public B setLanguage(Locale language) {
837            this.language = language;
838            return getThis();
839        }
840
841        /**
842         * Set the resource we are requesting from the server.
843         *
844         * @param resource the non-null CharSequence to use a resource.
845         * @return a reference ot this builder.
846         * @throws XmppStringprepException if the CharSequence is not a valid resourcepart.
847         * @see #setResource(Resourcepart)
848         */
849        public B setResource(CharSequence resource) throws XmppStringprepException {
850            Objects.requireNonNull(resource, "resource must not be null");
851            return setResource(Resourcepart.from(resource.toString()));
852        }
853
854        /**
855         * Set the Internet address of the host providing the XMPP service. If set, then this will overwrite anything
856         * set via {@link #setHost(CharSequence)}.
857         *
858         * @param address the Internet address of the host providing the XMPP service.
859         * @return a reference to this builder.
860         * @since 4.2
861         */
862        public B setHostAddress(InetAddress address) {
863            this.hostAddress = address;
864            return getThis();
865        }
866
867        /**
868         * Set the name of the host providing the XMPP service. This method takes DNS names and
869         * IP addresses.
870         *
871         * @param host the DNS name of the host providing the XMPP service.
872         * @return a reference to this builder.
873         */
874        public B setHost(CharSequence host) {
875            String fqdnOrIpString = host.toString();
876            if (InetAddressUtil.isIpAddress(fqdnOrIpString)) {
877                InetAddress hostInetAddress;
878                try {
879                    hostInetAddress = InetAddress.getByName(fqdnOrIpString);
880                }
881                catch (UnknownHostException e) {
882                    // Should never happen.
883                    throw new AssertionError(e);
884                }
885                setHostAddress(hostInetAddress);
886            } else {
887                DnsName dnsName = DnsName.from(fqdnOrIpString);
888                setHost(dnsName);
889            }
890            return getThis();
891        }
892
893        /**
894         * Set the name of the host providing the XMPP service. Note that this method does only allow DNS names and not
895         * IP addresses. Use {@link #setHostAddress(InetAddress)} if you want to explicitly set the Internet address of
896         * the host providing the XMPP service.
897         *
898         * @param host the DNS name of the host providing the XMPP service.
899         * @return a reference to this builder.
900         */
901        public B setHost(DnsName host) {
902            this.host = host;
903            return getThis();
904        }
905
906        /**
907         * Set the host to connect to by either its fully qualified domain name (FQDN) or its IP.
908         *
909         * @param fqdnOrIp a CharSequence either representing the FQDN or the IP of the host.
910         * @return a reference to this builder.
911         * @see #setHost(DnsName)
912         * @see #setHostAddress(InetAddress)
913         * @since 4.3.2
914         * @deprecated use {@link #setHost(CharSequence)} instead.
915         */
916        @Deprecated
917        // TODO: Remove in Smack 4.5.
918        public B setHostAddressByNameOrIp(CharSequence fqdnOrIp) {
919            return setHost(fqdnOrIp);
920        }
921
922        public B setPort(int port) {
923            if (port < 0 || port > 65535) {
924                throw new IllegalArgumentException(
925                        "Port must be a 16-bit unsigned integer (i.e. between 0-65535. Port was: " + port);
926            }
927            UInt16 portUint16 = UInt16.from(port);
928            return setPort(portUint16);
929        }
930
931        public B setPort(UInt16 port) {
932            this.port = Objects.requireNonNull(port);
933            return getThis();
934        }
935
936        /**
937         * Sets a CallbackHandler to obtain information, such as the password or
938         * principal information during the SASL authentication. A CallbackHandler
939         * will be used <b>ONLY</b> if no password was specified during the login while
940         * using SASL authentication.
941         *
942         * @param callbackHandler to obtain information, such as the password or
943         * principal information during the SASL authentication.
944         * @return a reference to this builder.
945         */
946        public B setCallbackHandler(CallbackHandler callbackHandler) {
947            this.callbackHandler = callbackHandler;
948            return getThis();
949        }
950
951        public B setDnssecMode(DnssecMode dnssecMode) {
952            this.dnssecMode = Objects.requireNonNull(dnssecMode, "DNSSEC mode must not be null");
953            return getThis();
954        }
955
956        public B setCustomX509TrustManager(X509TrustManager x509TrustManager) {
957            this.customX509TrustManager = x509TrustManager;
958            return getThis();
959        }
960
961        /**
962         * Sets the TLS security mode used when making the connection. By default,
963         * the mode is {@link SecurityMode#ifpossible}.
964         *
965         * @param securityMode the security mode.
966         * @return a reference to this builder.
967         */
968        public B setSecurityMode(SecurityMode securityMode) {
969            this.securityMode = securityMode;
970            return getThis();
971        }
972
973        /**
974         * Sets the path to the keystore file. The key store file contains the
975         * certificates that may be used to authenticate the client to the server,
976         * in the event the server requests or requires it.
977         *
978         * @param keystorePath the path to the keystore file.
979         * @return a reference to this builder.
980         */
981        public B setKeystorePath(String keystorePath) {
982            this.keystorePath = keystorePath;
983            return getThis();
984        }
985
986        /**
987         * Sets the keystore type.
988         *
989         * @param keystoreType the keystore type.
990         * @return a reference to this builder.
991         */
992        public B setKeystoreType(String keystoreType) {
993            this.keystoreType = keystoreType;
994            return getThis();
995        }
996
997        /**
998         * Sets the PKCS11 library file location, needed when the
999         * Keystore type is PKCS11.
1000         *
1001         * @param pkcs11Library the path to the PKCS11 library file.
1002         * @return a reference to this builder.
1003         */
1004        public B setPKCS11Library(String pkcs11Library) {
1005            this.pkcs11Library = pkcs11Library;
1006            return getThis();
1007        }
1008
1009        /**
1010         * Sets a custom SSLContext for creating SSL sockets.
1011         * <p>
1012         * For more information on how to create a SSLContext see <a href=
1013         * "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager"
1014         * >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a>
1015         *
1016         * @param context the custom SSLContext for new sockets.
1017         * @return a reference to this builder.
1018         * @deprecated use {@link #setSslContextFactory(SslContextFactory)} instead}.
1019         */
1020        // TODO: Remove in Smack 4.5.
1021        @Deprecated
1022        public B setCustomSSLContext(SSLContext context) {
1023            return setSslContextFactory(() -> {
1024                return context;
1025            });
1026        }
1027
1028        /**
1029         * Sets a custom SSLContext for creating SSL sockets.
1030         * <p>
1031         * For more information on how to create a SSLContext see <a href=
1032         * "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager"
1033         * >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a>
1034         *
1035         * @param sslContextFactory the custom SSLContext for new sockets.
1036         * @return a reference to this builder.
1037         */
1038        public B setSslContextFactory(SslContextFactory sslContextFactory) {
1039            this.sslContextFactory = Objects.requireNonNull(sslContextFactory, "The provided SslContextFactory must not be null");
1040            return getThis();
1041        }
1042
1043        /**
1044         * Set the enabled SSL/TLS protocols.
1045         *
1046         * @param enabledSSLProtocols TODO javadoc me please
1047         * @return a reference to this builder.
1048         */
1049        public B setEnabledSSLProtocols(String[] enabledSSLProtocols) {
1050            this.enabledSSLProtocols = enabledSSLProtocols;
1051            return getThis();
1052        }
1053
1054        /**
1055         * Set the enabled SSL/TLS ciphers.
1056         *
1057         * @param enabledSSLCiphers the enabled SSL/TLS ciphers
1058         * @return a reference to this builder.
1059         */
1060        public B setEnabledSSLCiphers(String[] enabledSSLCiphers) {
1061            this.enabledSSLCiphers = enabledSSLCiphers;
1062            return getThis();
1063        }
1064
1065        /**
1066         * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections
1067         * created with this ConnectionConfiguration.
1068         *
1069         * @param verifier TODO javadoc me please
1070         * @return a reference to this builder.
1071         */
1072        public B setHostnameVerifier(HostnameVerifier verifier) {
1073            hostnameVerifier = verifier;
1074            return getThis();
1075        }
1076
1077        /**
1078         * Sets if an initial available presence will be sent to the server. By default
1079         * an available presence will be sent to the server indicating that this presence
1080         * is not online and available to receive messages. If you want to log in without
1081         * being 'noticed' then pass a <code>false</code> value.
1082         *
1083         * @param sendPresence true if an initial available presence will be sent while logging in.
1084         * @return a reference to this builder.
1085         */
1086        public B setSendPresence(boolean sendPresence) {
1087            this.sendPresence = sendPresence;
1088            return getThis();
1089        }
1090
1091        public B enableDefaultDebugger() {
1092            this.debuggerFactory = SmackConfiguration.getDefaultSmackDebuggerFactory();
1093            assert this.debuggerFactory != null;
1094            return getThis();
1095        }
1096
1097        /**
1098         * Set the Smack debugger factory used to construct Smack debuggers.
1099         *
1100         * @param debuggerFactory the Smack debugger factory.
1101         * @return a reference to this builder.
1102         */
1103        public B setDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
1104            this.debuggerFactory = debuggerFactory;
1105            return getThis();
1106        }
1107
1108        /**
1109         * Sets the socket factory used to create new xmppConnection sockets.
1110         * This is useful when connecting through SOCKS5 proxies.
1111         *
1112         * @param socketFactory used to create new sockets.
1113         * @return a reference to this builder.
1114         */
1115        public B setSocketFactory(SocketFactory socketFactory) {
1116            this.socketFactory = socketFactory;
1117            return getThis();
1118        }
1119
1120        /**
1121         * Set the information about the Proxy used for the connection.
1122         *
1123         * @param proxyInfo the Proxy information.
1124         * @return a reference to this builder.
1125         */
1126        public B setProxyInfo(ProxyInfo proxyInfo) {
1127            this.proxy = proxyInfo;
1128            return getThis();
1129        }
1130
1131        /**
1132         * Allow <code>null</code> or the empty String as username.
1133         *
1134         * Some SASL mechanisms (e.g. SASL External) may also signal the username (as "authorization identity"), in
1135         * which case Smack should not throw an IllegalArgumentException when the username is not set.
1136         *
1137         * @return a reference to this builder.
1138         */
1139        public B allowEmptyOrNullUsernames() {
1140            allowEmptyOrNullUsername = true;
1141            return getThis();
1142        }
1143
1144        /**
1145         * Perform anonymous authentication using SASL ANONYMOUS. Your XMPP service must support this authentication
1146         * mechanism. This method also calls {@link #addEnabledSaslMechanism(String)} with "ANONYMOUS" as argument.
1147         *
1148         * @return a reference to this builder.
1149         */
1150        public B performSaslAnonymousAuthentication() {
1151            if (!SASLAuthentication.isSaslMechanismRegistered(SASLAnonymous.NAME)) {
1152                throw new IllegalArgumentException("SASL " + SASLAnonymous.NAME + " is not registered");
1153            }
1154            throwIfEnabledSaslMechanismsSet();
1155
1156            allowEmptyOrNullUsernames();
1157            addEnabledSaslMechanism(SASLAnonymous.NAME);
1158            saslMechanismsSealed = true;
1159            return getThis();
1160        }
1161
1162        /**
1163         * Perform authentication using SASL EXTERNAL. Your XMPP service must support this
1164         * authentication mechanism. This method also calls {@link #addEnabledSaslMechanism(String)} with "EXTERNAL" as
1165         * argument. It also calls {@link #allowEmptyOrNullUsernames()} and {@link #setSecurityMode(ConnectionConfiguration.SecurityMode)} to
1166         * {@link SecurityMode#required}.
1167         *
1168         * @param sslContext custom SSLContext to be used.
1169         * @return a reference to this builder.
1170         */
1171        public B performSaslExternalAuthentication(SSLContext sslContext) {
1172            if (!SASLAuthentication.isSaslMechanismRegistered(SASLMechanism.EXTERNAL)) {
1173                throw new IllegalArgumentException("SASL " + SASLMechanism.EXTERNAL + " is not registered");
1174            }
1175            setCustomSSLContext(sslContext);
1176            throwIfEnabledSaslMechanismsSet();
1177
1178            allowEmptyOrNullUsernames();
1179            setSecurityMode(SecurityMode.required);
1180            addEnabledSaslMechanism(SASLMechanism.EXTERNAL);
1181            saslMechanismsSealed = true;
1182            return getThis();
1183        }
1184
1185        private void throwIfEnabledSaslMechanismsSet() {
1186            if (enabledSaslMechanisms != null) {
1187                throw new IllegalStateException("Enabled SASL mechanisms found");
1188            }
1189        }
1190
1191        /**
1192         * Add the given mechanism to the enabled ones. See {@link #addEnabledSaslMechanism(Collection)} for a discussion about enabled SASL mechanisms.
1193         *
1194         * @param saslMechanism the name of the mechanism to enable.
1195         * @return a reference to this builder.
1196         */
1197        public B addEnabledSaslMechanism(String saslMechanism) {
1198            return addEnabledSaslMechanism(Arrays.asList(StringUtils.requireNotNullNorEmpty(saslMechanism,
1199                            "saslMechanism must not be null nor empty")));
1200        }
1201
1202        /**
1203         * Enable the given SASL mechanisms. If you never add a mechanism to the set of enabled ones, <b>all mechanisms
1204         * known to Smack</b> will be enabled. Only explicitly enable particular SASL mechanisms if you want to limit
1205         * the used mechanisms to the enabled ones.
1206         *
1207         * @param saslMechanisms a collection of names of mechanisms to enable.
1208         * @return a reference to this builder.
1209         */
1210        public B addEnabledSaslMechanism(Collection<String> saslMechanisms) {
1211            if (saslMechanismsSealed) {
1212                throw new IllegalStateException("The enabled SASL mechanisms are sealed, you can not add new ones");
1213            }
1214            CollectionUtil.requireNotEmpty(saslMechanisms, "saslMechanisms");
1215            Set<String> blacklistedMechanisms = SASLAuthentication.getBlacklistedSASLMechanisms();
1216            for (String mechanism : saslMechanisms) {
1217                if (!SASLAuthentication.isSaslMechanismRegistered(mechanism)) {
1218                    throw new IllegalArgumentException("SASL " + mechanism + " is not available. Consider registering it with Smack");
1219                }
1220                if (blacklistedMechanisms.contains(mechanism)) {
1221                    throw new IllegalArgumentException("SALS " + mechanism + " is blacklisted.");
1222                }
1223            }
1224            if (enabledSaslMechanisms == null) {
1225                enabledSaslMechanisms = new HashSet<>(saslMechanisms.size());
1226            }
1227            enabledSaslMechanisms.addAll(saslMechanisms);
1228            return getThis();
1229        }
1230
1231        /**
1232         * Set the XMPP address to be used as authorization identity.
1233         * <p>
1234         * In XMPP, authorization identities are bare jids. In general, callers should allow the server to select the
1235         * authorization identifier automatically, and not call this. Note that setting the authzid does not set the XMPP
1236         * service domain, which should typically match.
1237         * Calling this will also SASL CRAM, since this mechanism does not support authzid.
1238         * </p>
1239         *
1240         * @param authzid The BareJid to be requested as the authorization identifier.
1241         * @return a reference to this builder.
1242         * @see <a href="http://tools.ietf.org/html/rfc6120#section-6.3.8">RFC 6120 § 6.3.8. Authorization Identity</a>
1243         * @since 4.2
1244         */
1245        public B setAuthzid(EntityBareJid authzid) {
1246            this.authzid = authzid;
1247            return getThis();
1248        }
1249
1250        /**
1251         * Sets if the connection is going to use compression (default false).
1252         *
1253         * Compression is only activated if the server offers compression. With compression network
1254         * traffic can be reduced up to 90%. By default compression is disabled.
1255         *
1256         * @param compressionEnabled if the connection is going to use compression on the HTTP level.
1257         * @return a reference to this object.
1258         */
1259        public B setCompressionEnabled(boolean compressionEnabled) {
1260            this.compressionEnabled = compressionEnabled;
1261            return getThis();
1262        }
1263
1264        /**
1265         * Set the factory for stanza ID sources to use.
1266         *
1267         * @param stanzaIdSourceFactory the factory for stanza ID sources to use.
1268         * @return a reference to this builder.
1269         * @since 4.4
1270         */
1271        public B setStanzaIdSourceFactory(StanzaIdSourceFactory stanzaIdSourceFactory) {
1272            this.stanzaIdSourceFactory = Objects.requireNonNull(stanzaIdSourceFactory);
1273            return getThis();
1274        }
1275
1276        public abstract C build();
1277
1278        protected abstract B getThis();
1279    }
1280}