001/**
002 *
003 * Copyright 2003-2007 Jive Software.
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 org.jivesoftware.smack.packet.Session;
021import org.jivesoftware.smack.proxy.ProxyInfo;
022
023import javax.net.SocketFactory;
024import javax.net.ssl.HostnameVerifier;
025import javax.net.ssl.SSLContext;
026import javax.security.auth.callback.CallbackHandler;
027
028/**
029 * Configuration to use while establishing the connection to the server.
030 *
031 * @author Gaston Dombiak
032 */
033public abstract class ConnectionConfiguration {
034
035    static {
036        // Ensure that Smack is initialized when ConnectionConfiguration is used, or otherwise e.g.
037        // SmackConfiguration.DEBUG may not be initialized yet.
038        SmackConfiguration.getVersion();
039    }
040
041    /**
042     * Hostname of the XMPP server. Usually servers use the same service name as the name
043     * of the server. However, there are some servers like google where host would be
044     * talk.google.com and the serviceName would be gmail.com.
045     */
046    protected final String serviceName;
047    protected final String host;
048    protected final int port;
049
050    private final String keystorePath;
051    private final String keystoreType;
052    private final String pkcs11Library;
053    private final SSLContext customSSLContext;
054
055    /**
056     * Used to get information from the user
057     */
058    private final CallbackHandler callbackHandler;
059
060    private final boolean debuggerEnabled;
061
062    // Holds the socket factory that is used to generate the socket in the connection
063    private final SocketFactory socketFactory;
064
065    private final CharSequence username;
066    private final String password;
067    private final String resource;
068
069    /**
070     * Initial presence as of RFC 6121 § 4.2
071     * @see <a href="http://xmpp.org/rfcs/rfc6121.html#presence-initial">RFC 6121 § 4.2 Initial Presence</a>
072     */
073    private final boolean sendPresence;
074
075    private final boolean legacySessionDisabled;
076    private final SecurityMode securityMode;
077
078    /**
079     * 
080     */
081    private final String[] enabledSSLProtocols;
082
083    /**
084     * 
085     */
086    private final String[] enabledSSLCiphers;
087
088    private final HostnameVerifier hostnameVerifier;
089
090    // Holds the proxy information (such as proxyhost, proxyport, username, password etc)
091    protected final ProxyInfo proxy;
092
093    protected final boolean allowNullOrEmptyUsername;
094
095    protected ConnectionConfiguration(Builder<?,?> builder) {
096        username = builder.username;
097        password = builder.password;
098        callbackHandler = builder.callbackHandler;
099
100        // Resource can be null, this means that the server must provide one
101        resource = builder.resource;
102
103        serviceName = builder.serviceName;
104        if (serviceName == null) {
105            throw new IllegalArgumentException("Must provide XMPP service name");
106        }
107        host = builder.host;
108        port = builder.port;
109
110        proxy = builder.proxy;
111        if (proxy != null) {
112            if (builder.socketFactory != null) {
113                throw new IllegalArgumentException("Can not use proxy together with custom socket factory");
114            }
115            socketFactory = proxy.getSocketFactory();
116        } else {
117            socketFactory = builder.socketFactory;
118        }
119
120        securityMode = builder.securityMode;
121        keystoreType = builder.keystoreType;
122        keystorePath = builder.keystorePath;
123        pkcs11Library = builder.pkcs11Library;
124        customSSLContext = builder.customSSLContext;
125        enabledSSLProtocols = builder.enabledSSLProtocols;
126        enabledSSLCiphers = builder.enabledSSLCiphers;
127        hostnameVerifier = builder.hostnameVerifier;
128        sendPresence = builder.sendPresence;
129        legacySessionDisabled = builder.legacySessionDisabled;
130        debuggerEnabled = builder.debuggerEnabled;
131        allowNullOrEmptyUsername = builder.allowEmptyOrNullUsername;
132    }
133
134    /**
135     * Returns the server name of the target server.
136     *
137     * @return the server name of the target server.
138     */
139    public String getServiceName() {
140        return serviceName;
141    }
142
143    /**
144     * Returns the TLS security mode used when making the connection. By default,
145     * the mode is {@link SecurityMode#ifpossible}.
146     *
147     * @return the security mode.
148     */
149    public SecurityMode getSecurityMode() {
150        return securityMode;
151    }
152
153    /**
154     * Retuns the path to the keystore file. The key store file contains the 
155     * certificates that may be used to authenticate the client to the server,
156     * in the event the server requests or requires it.
157     *
158     * @return the path to the keystore file.
159     */
160    public String getKeystorePath() {
161        return keystorePath;
162    }
163
164    /**
165     * Returns the keystore type, or <tt>null</tt> if it's not set.
166     *
167     * @return the keystore type.
168     */
169    public String getKeystoreType() {
170        return keystoreType;
171    }
172
173    /**
174     * Returns the PKCS11 library file location, needed when the
175     * Keystore type is PKCS11.
176     *
177     * @return the path to the PKCS11 library file
178     */
179    public String getPKCS11Library() {
180        return pkcs11Library;
181    }
182
183    /**
184     * Gets the custom SSLContext previously set with {@link ConnectionConfiguration.Builder#setCustomSSLContext(SSLContext)} for
185     * SSL sockets. This is null by default.
186     *
187     * @return the custom SSLContext or null.
188     */
189    public SSLContext getCustomSSLContext() {
190        return this.customSSLContext;
191    }
192
193    /**
194     * Return the enabled SSL/TLS protocols.
195     *
196     * @return the enabled SSL/TLS protocols
197     */
198    public String[] getEnabledSSLProtocols() {
199        return enabledSSLProtocols;
200    }
201
202    /**
203     * Return the enabled SSL/TLS ciphers.
204     *
205     * @return the enabled SSL/TLS ciphers
206     */
207    public String[] getEnabledSSLCiphers() {
208        return enabledSSLCiphers;
209    }
210
211    /**
212     * Returns the configured HostnameVerifier of this ConnectionConfiguration or the Smack default
213     * HostnameVerifier configured with
214     * {@link SmackConfiguration#setDefaultHostnameVerifier(HostnameVerifier)}.
215     * 
216     * @return a configured HostnameVerifier or <code>null</code>
217     */
218    public HostnameVerifier getHostnameVerifier() {
219        if (hostnameVerifier != null)
220            return hostnameVerifier;
221        return SmackConfiguration.getDefaultHostnameVerifier();
222    }
223
224    /**
225     * Returns true if the new connection about to be establish is going to be debugged. By
226     * default the value of {@link SmackConfiguration#DEBUG} is used.
227     *
228     * @return true if the new connection about to be establish is going to be debugged.
229     */
230    public boolean isDebuggerEnabled() {
231        return debuggerEnabled;
232    }
233
234    /**
235     * Returns true if a {@link Session} will be requested on login if the server
236     * supports it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't
237     * even mention this part of the protocol.
238     *
239     * @return true if a session has to be requested when logging in.
240     * @deprecated Smack processes the 'optional' element of the session stream feature.
241     * @see Builder#setLegacySessionDisabled(boolean)
242     */
243    @Deprecated
244    public boolean isLegacySessionDisabled() {
245        return legacySessionDisabled;
246    }
247
248    /**
249     * Returns a CallbackHandler to obtain information, such as the password or
250     * principal information during the SASL authentication. A CallbackHandler
251     * will be used <b>ONLY</b> if no password was specified during the login while
252     * using SASL authentication.
253     *
254     * @return a CallbackHandler to obtain information, such as the password or
255     * principal information during the SASL authentication.
256     */
257    public CallbackHandler getCallbackHandler() {
258        return callbackHandler;
259    }
260
261    /**
262     * Returns the socket factory used to create new xmppConnection sockets.
263     * This is useful when connecting through SOCKS5 proxies.
264     * 
265     * @return socketFactory used to create new sockets.
266     */
267    public SocketFactory getSocketFactory() {
268        return this.socketFactory;
269    }
270
271    /**
272     * An enumeration for TLS security modes that are available when making a connection
273     * to the XMPP server.
274     */
275    public static enum SecurityMode {
276
277        /**
278         * Securirty via TLS encryption is required in order to connect. If the server
279         * does not offer TLS or if the TLS negotiaton fails, the connection to the server
280         * will fail.
281         */
282        required,
283
284        /**
285         * Security via TLS encryption is used whenever it's available. This is the
286         * default setting.
287         * <p>
288         * <b>Do not use this setting</b> unless you can't use {@link #required}. An attacker could easily perform a
289         * Man-in-the-middle attack and prevent TLS from being used, leaving you with an unencrypted (and
290         * unauthenticated) connection.
291         * </p>
292         */
293        ifpossible,
294
295        /**
296         * Security via TLS encryption is disabled and only un-encrypted connections will
297         * be used. If only TLS encryption is available from the server, the connection
298         * will fail.
299         */
300        disabled
301    }
302
303    /**
304     * Returns the username to use when trying to reconnect to the server.
305     *
306     * @return the username to use when trying to reconnect to the server.
307     */
308    public CharSequence getUsername() {
309        return this.username;
310    }
311
312    /**
313     * Returns the password to use when trying to reconnect to the server.
314     *
315     * @return the password to use when trying to reconnect to the server.
316     */
317    public String getPassword() {
318        return this.password;
319    }
320
321    /**
322     * Returns the resource to use when trying to reconnect to the server.
323     *
324     * @return the resource to use when trying to reconnect to the server.
325     */
326    public String getResource() {
327        return resource;
328    }
329
330    /**
331     * Returns true if an available presence should be sent when logging in while reconnecting.
332     *
333     * @return true if an available presence should be sent when logging in while reconnecting
334     */
335    public boolean isSendPresence() {
336        return sendPresence;
337    }
338
339    /**
340     * Returns true if the connection is going to use stream compression. Stream compression
341     * will be requested after TLS was established (if TLS was enabled) and only if the server
342     * offered stream compression. With stream compression network traffic can be reduced
343     * up to 90%. By default compression is disabled.
344     *
345     * @return true if the connection is going to use stream compression.
346     */
347    public boolean isCompressionEnabled() {
348        // Compression for non-TCP connections is always disabled
349        return false;
350    }
351
352    /**
353     * A builder for XMPP connection configurations.
354     * <p>
355     * This is an abstract class that uses the builder design pattern and the "getThis() trick" to recover the type of
356     * the builder in a class hierarchies with a self-referential generic supertype. Otherwise chaining of build
357     * instructions from the superclasses followed by build instructions of a sublcass would not be possible, because
358     * the superclass build instructions would return the builder of the superclass and not the one of the subclass. You
359     * can read more about it a Angelika Langer's Generics FAQ, especially the entry <a
360     * href="http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206">What is the
361     * "getThis()" trick?</a>.
362     * </p>
363     *
364     * @param <B> the builder type parameter.
365     * @param <C> the resulting connection configuration type parameter.
366     */
367    public static abstract class Builder<B extends Builder<B, C>, C extends ConnectionConfiguration> {
368        private SecurityMode securityMode = SecurityMode.ifpossible;
369        private String keystorePath = System.getProperty("javax.net.ssl.keyStore");
370        private String keystoreType = "jks";
371        private String pkcs11Library = "pkcs11.config";
372        private SSLContext customSSLContext;
373        private String[] enabledSSLProtocols;
374        private String[] enabledSSLCiphers;
375        private HostnameVerifier hostnameVerifier;
376        private CharSequence username;
377        private String password;
378        private String resource = "Smack";
379        private boolean sendPresence = true;
380        private boolean legacySessionDisabled = false;
381        private ProxyInfo proxy;
382        private CallbackHandler callbackHandler;
383        private boolean debuggerEnabled = SmackConfiguration.DEBUG;
384        private SocketFactory socketFactory;
385        private String serviceName;
386        private String host;
387        private int port = 5222;
388        private boolean allowEmptyOrNullUsername = false;
389
390        protected Builder() {
391        }
392
393        /**
394         * Set the XMPP entities username and password.
395         * <p>
396         * The username is usually the localpart of the clients JID. But some SASL mechanisms or services may require a different
397         * format (e.g. the full JID) as used authorization identity.
398         * </p>
399         *
400         * @param username the username or authorization identity
401         * @param password the password or token used to authenticate
402         * @return a reference to this builder.
403         */
404        public B setUsernameAndPassword(CharSequence username, String password) {
405            this.username = username;
406            this.password = password;
407            return getThis();
408        }
409
410        /**
411         * Set the service name of this XMPP service (i.e., the XMPP domain).
412         *
413         * @param serviceName the service name
414         * @return a reference to this builder.
415         */
416        public B setServiceName(String serviceName) {
417            this.serviceName = serviceName;
418            return getThis();
419        }
420
421        /**
422         * Set the resource to use.
423         * <p>
424         * If <code>resource</code> is <code>null</code>, then the server will automatically create a resource for the
425         * client. Default resource is "Smack".
426         * </p>
427         *
428         * @param resource the resource to use.
429         * @return a reference to this builder.
430         */
431        public B setResource(String resource) {
432            this.resource = resource;
433            return getThis();
434        }
435
436        public B setHost(String host) {
437            this.host = host;
438            return getThis();
439        }
440
441        public B setPort(int port) {
442            this.port = port;
443            return getThis();
444        }
445
446        /**
447         * Sets a CallbackHandler to obtain information, such as the password or
448         * principal information during the SASL authentication. A CallbackHandler
449         * will be used <b>ONLY</b> if no password was specified during the login while
450         * using SASL authentication.
451         *
452         * @param callbackHandler to obtain information, such as the password or
453         * principal information during the SASL authentication.
454         * @return a reference to this builder.
455         */
456        public B setCallbackHandler(CallbackHandler callbackHandler) {
457            this.callbackHandler = callbackHandler;
458            return getThis();
459        }
460
461        /**
462         * Sets the TLS security mode used when making the connection. By default,
463         * the mode is {@link SecurityMode#ifpossible}.
464         *
465         * @param securityMode the security mode.
466         * @return a reference to this builder.
467         */
468        public B setSecurityMode(SecurityMode securityMode) {
469            this.securityMode = securityMode;
470            return getThis();
471        }
472
473        /**
474         * Sets the path to the keystore file. The key store file contains the 
475         * certificates that may be used to authenticate the client to the server,
476         * in the event the server requests or requires it.
477         *
478         * @param keystorePath the path to the keystore file.
479         * @return a reference to this builder.
480         */
481        public B setKeystorePath(String keystorePath) {
482            this.keystorePath = keystorePath;
483            return getThis();
484        }
485
486        /**
487         * Sets the keystore type.
488         *
489         * @param keystoreType the keystore type.
490         * @return a reference to this builder.
491         */
492        public B setKeystoreType(String keystoreType) {
493            this.keystoreType = keystoreType;
494            return getThis();
495        }
496
497        /**
498         * Sets the PKCS11 library file location, needed when the
499         * Keystore type is PKCS11
500         *
501         * @param pkcs11Library the path to the PKCS11 library file.
502         * @return a reference to this builder.
503         */
504        public B setPKCS11Library(String pkcs11Library) {
505            this.pkcs11Library = pkcs11Library;
506            return getThis();
507        }
508
509        /**
510         * Sets a custom SSLContext for creating SSL sockets.
511         * <p>
512         * For more information on how to create a SSLContext see <a href=
513         * "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager"
514         * >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a>
515         *
516         * @param context the custom SSLContext for new sockets.
517         * @return a reference to this builder.
518         */
519        public B setCustomSSLContext(SSLContext context) {
520            this.customSSLContext = context;
521            return getThis();
522        }
523
524        /**
525         * Set the enabled SSL/TLS protocols.
526         *
527         * @param enabledSSLProtocols
528         * @return a reference to this builder.
529         */
530        public B setEnabledSSLProtocols(String[] enabledSSLProtocols) {
531            this.enabledSSLProtocols = enabledSSLProtocols;
532            return getThis();
533        }
534
535        /**
536         * Set the enabled SSL/TLS ciphers.
537         * 
538         * @param enabledSSLCiphers the enabled SSL/TLS ciphers 
539         * @return a reference to this builder.
540         */
541        public B setEnabledSSLCiphers(String[] enabledSSLCiphers) {
542            this.enabledSSLCiphers = enabledSSLCiphers;
543            return getThis();
544        }
545
546        /**
547         * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections
548         * created with this ConnectionConfiguration.
549         * 
550         * @param verifier
551         * @return a reference to this builder.
552         */
553        public B setHostnameVerifier(HostnameVerifier verifier) {
554            hostnameVerifier = verifier;
555            return getThis();
556        }
557
558        /**
559         * Sets if a {@link Session} will be requested on login if the server supports
560         * it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't even
561         * mention this part of the protocol.
562         * <p>
563         * Deprecation notice: This setting is no longer required in most cases because Smack processes the 'optional'
564         * element eventually found in the session stream feature. See also <a
565         * href="https://tools.ietf.org/html/draft-cridland-xmpp-session-01">Here Lies Extensible Messaging and Presence
566         * Protocol (XMPP) Session Establishment</a>
567         * </p>
568         *
569         * @param legacySessionDisabled if a session has to be requested when logging in.
570         * @return a reference to this builder.
571         * @deprecated Smack processes the 'optional' element of the session stream feature.
572         */
573        @Deprecated
574        public B setLegacySessionDisabled(boolean legacySessionDisabled) {
575            this.legacySessionDisabled = legacySessionDisabled;
576            return getThis();
577        }
578
579        /**
580         * Sets if an initial available presence will be sent to the server. By default
581         * an available presence will be sent to the server indicating that this presence
582         * is not online and available to receive messages. If you want to log in without
583         * being 'noticed' then pass a <tt>false</tt> value.
584         *
585         * @param sendPresence true if an initial available presence will be sent while logging in.
586         * @return a reference to this builder.
587         */
588        public B setSendPresence(boolean sendPresence) {
589            this.sendPresence = sendPresence;
590            return getThis();
591        }
592
593        /**
594         * Sets if the new connection about to be establish is going to be debugged. By
595         * default the value of {@link SmackConfiguration#DEBUG} is used.
596         *
597         * @param debuggerEnabled if the new connection about to be establish is going to be debugged.
598         * @return a reference to this builder.
599         */
600        public B setDebuggerEnabled(boolean debuggerEnabled) {
601            this.debuggerEnabled = debuggerEnabled;
602            return getThis();
603        }
604
605        /**
606         * Sets the socket factory used to create new xmppConnection sockets.
607         * This is useful when connecting through SOCKS5 proxies.
608         *
609         * @param socketFactory used to create new sockets.
610         * @return a reference to this builder.
611         */
612        public B setSocketFactory(SocketFactory socketFactory) {
613            this.socketFactory = socketFactory;
614            return getThis();
615        }
616
617        /**
618         * Set the information about the Proxy used for the connection.
619         *
620         * @param proxyInfo the Proxy information.
621         * @return a reference to this builder.
622         */
623        public B setProxyInfo(ProxyInfo proxyInfo) {
624            this.proxy = proxyInfo;
625            return getThis();
626        }
627
628        /**
629         * Allow <code>null</code> or the empty String as username.
630         *
631         * Some SASL mechanisms (e.g. SASL External) may also signal the username (as "authorization identity"), in
632         * which case Smack should not throw an IllegalArgumentException when the username is not set.
633         * 
634         * @return a reference to this builder.
635         */
636        public B allowEmptyOrNullUsernames() {
637            allowEmptyOrNullUsername = true;
638            return getThis();
639        }
640
641        public abstract C build();
642
643        protected abstract B getThis();
644    }
645}