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;
022import org.jivesoftware.smack.util.DNSUtil;
023import org.jivesoftware.smack.util.dns.HostAddress;
024
025import javax.net.SocketFactory;
026import javax.net.ssl.HostnameVerifier;
027import javax.net.ssl.SSLContext;
028import javax.security.auth.callback.CallbackHandler;
029
030import java.util.ArrayList;
031import java.util.Collections;
032import java.util.List;
033
034/**
035 * Configuration to use while establishing the connection to the server. It is possible to
036 * configure the path to the trustore file that keeps the trusted CA root certificates and
037 * enable or disable all or some of the checkings done while verifying server certificates.<p>
038 *
039 * It is also possible to configure if TLS, SASL, and compression are used or not.
040 *
041 * @author Gaston Dombiak
042 */
043public class ConnectionConfiguration implements Cloneable {
044
045    /**
046     * Hostname of the XMPP server. Usually servers use the same service name as the name
047     * of the server. However, there are some servers like google where host would be
048     * talk.google.com and the serviceName would be gmail.com.
049     */
050    private String serviceName;
051
052    protected List<HostAddress> hostAddresses;
053
054    private String keystorePath;
055    private String keystoreType;
056    private String pkcs11Library;
057    private SSLContext customSSLContext;
058
059    private boolean compressionEnabled = false;
060
061    /**
062     * Used to get information from the user
063     */
064    private CallbackHandler callbackHandler;
065
066    private boolean debuggerEnabled = SmackConfiguration.DEBUG_ENABLED;
067
068    // Flag that indicates if a reconnection should be attempted when abruptly disconnected
069    private boolean reconnectionAllowed = true;
070    
071    // Holds the socket factory that is used to generate the socket in the connection
072    private SocketFactory socketFactory;
073    
074    // Holds the authentication information for future reconnections
075    private String username;
076    private String password;
077    private String resource;
078    private boolean sendPresence = true;
079    private boolean rosterLoadedAtLogin = true;
080    private boolean legacySessionDisabled = false;
081    private boolean useDnsSrvRr = true;
082    private SecurityMode securityMode = SecurityMode.enabled;
083
084    private HostnameVerifier hostnameVerifier;
085
086    /**
087     * Permanent store for the Roster, needed for roster versioning
088     */
089    private RosterStore rosterStore;
090
091    // Holds the proxy information (such as proxyhost, proxyport, username, password etc)
092    protected ProxyInfo proxy;
093
094    /**
095     * Creates a new ConnectionConfiguration for the specified service name.
096     * A DNS SRV lookup will be performed to find out the actual host address
097     * and port to use for the connection.
098     *
099     * @param serviceName the name of the service provided by an XMPP server.
100     */
101    public ConnectionConfiguration(String serviceName) {
102        init(serviceName, ProxyInfo.forDefaultProxy());
103    }
104
105     /**
106     * Creates a new ConnectionConfiguration for the specified service name 
107     * with specified proxy.
108     * A DNS SRV lookup will be performed to find out the actual host address
109     * and port to use for the connection.
110     *
111     * @param serviceName the name of the service provided by an XMPP server.
112     * @param proxy the proxy through which XMPP is to be connected
113     */
114    public ConnectionConfiguration(String serviceName,ProxyInfo proxy) {
115        init(serviceName, proxy);
116    }
117
118    /**
119     * Creates a new ConnectionConfiguration using the specified host, port and
120     * service name. This is useful for manually overriding the DNS SRV lookup
121     * process that's used with the {@link #ConnectionConfiguration(String)}
122     * constructor. For example, say that an XMPP server is running at localhost
123     * in an internal network on port 5222 but is configured to think that it's
124     * "example.com" for testing purposes. This constructor is necessary to connect
125     * to the server in that case since a DNS SRV lookup for example.com would not
126     * point to the local testing server.
127     *
128     * @param host the host where the XMPP server is running.
129     * @param port the port where the XMPP is listening.
130     * @param serviceName the name of the service provided by an XMPP server.
131     */
132    public ConnectionConfiguration(String host, int port, String serviceName) {
133        initHostAddresses(host, port);
134        init(serviceName, ProxyInfo.forDefaultProxy());
135    }
136        
137        /**
138     * Creates a new ConnectionConfiguration using the specified host, port and
139     * service name. This is useful for manually overriding the DNS SRV lookup
140     * process that's used with the {@link #ConnectionConfiguration(String)}
141     * constructor. For example, say that an XMPP server is running at localhost
142     * in an internal network on port 5222 but is configured to think that it's
143     * "example.com" for testing purposes. This constructor is necessary to connect
144     * to the server in that case since a DNS SRV lookup for example.com would not
145     * point to the local testing server.
146     *
147     * @param host the host where the XMPP server is running.
148     * @param port the port where the XMPP is listening.
149     * @param serviceName the name of the service provided by an XMPP server.
150     * @param proxy the proxy through which XMPP is to be connected
151     */
152    public ConnectionConfiguration(String host, int port, String serviceName, ProxyInfo proxy) {
153        initHostAddresses(host, port);
154        init(serviceName, proxy);
155    }
156
157    /**
158     * Creates a new ConnectionConfiguration for a connection that will connect
159     * to the desired host and port.
160     *
161     * @param host the host where the XMPP server is running.
162     * @param port the port where the XMPP is listening.
163     */
164    public ConnectionConfiguration(String host, int port) {
165        initHostAddresses(host, port);
166        init(host, ProxyInfo.forDefaultProxy());
167    }
168        
169        /**
170     * Creates a new ConnectionConfiguration for a connection that will connect
171     * to the desired host and port with desired proxy.
172     *
173     * @param host the host where the XMPP server is running.
174     * @param port the port where the XMPP is listening.
175     * @param proxy the proxy through which XMPP is to be connected
176     */
177    public ConnectionConfiguration(String host, int port, ProxyInfo proxy) {
178        initHostAddresses(host, port);
179        init(host, proxy);
180    }
181
182    protected void init(String serviceName, ProxyInfo proxy) {
183        this.serviceName = serviceName;
184        this.proxy = proxy;
185
186        keystorePath = System.getProperty("javax.net.ssl.keyStore");
187        keystoreType = "jks";
188        pkcs11Library = "pkcs11.config";
189                
190                //Setting the SocketFactory according to proxy supplied
191        socketFactory = proxy.getSocketFactory();
192    }
193
194    /**
195     * Sets the server name, also known as XMPP domain of the target server.
196     *
197     * @param serviceName the XMPP domain of the target server.
198     */
199    void setServiceName(String serviceName) {
200        this.serviceName = serviceName;
201    }
202
203    /**
204     * Returns the server name of the target server.
205     *
206     * @return the server name of the target server.
207     */
208    public String getServiceName() {
209        return serviceName;
210    }
211
212    /**
213     * Returns the TLS security mode used when making the connection. By default,
214     * the mode is {@link SecurityMode#enabled}.
215     *
216     * @return the security mode.
217     */
218    public SecurityMode getSecurityMode() {
219        return securityMode;
220    }
221
222    /**
223     * Sets the TLS security mode used when making the connection. By default,
224     * the mode is {@link SecurityMode#enabled}.
225     *
226     * @param securityMode the security mode.
227     */
228    public void setSecurityMode(SecurityMode securityMode) {
229        this.securityMode = securityMode;
230    }
231
232    /**
233     * Retuns the path to the keystore file. The key store file contains the 
234     * certificates that may be used to authenticate the client to the server,
235     * in the event the server requests or requires it.
236     *
237     * @return the path to the keystore file.
238     */
239    public String getKeystorePath() {
240        return keystorePath;
241    }
242
243    /**
244     * Sets the path to the keystore file. The key store file contains the 
245     * certificates that may be used to authenticate the client to the server,
246     * in the event the server requests or requires it.
247     *
248     * @param keystorePath the path to the keystore file.
249     */
250    public void setKeystorePath(String keystorePath) {
251        this.keystorePath = keystorePath;
252    }
253
254    /**
255     * Returns the keystore type, or <tt>null</tt> if it's not set.
256     *
257     * @return the keystore type.
258     */
259    public String getKeystoreType() {
260        return keystoreType;
261    }
262
263    /**
264     * Sets the keystore type.
265     *
266     * @param keystoreType the keystore type.
267     */
268    public void setKeystoreType(String keystoreType) {
269        this.keystoreType = keystoreType;
270    }
271
272
273    /**
274     * Returns the PKCS11 library file location, needed when the
275     * Keystore type is PKCS11.
276     *
277     * @return the path to the PKCS11 library file
278     */
279    public String getPKCS11Library() {
280        return pkcs11Library;
281    }
282
283    /**
284     * Sets the PKCS11 library file location, needed when the
285     * Keystore type is PKCS11
286     *
287     * @param pkcs11Library the path to the PKCS11 library file
288     */
289    public void setPKCS11Library(String pkcs11Library) {
290        this.pkcs11Library = pkcs11Library;
291    }
292
293    /**
294     * Gets the custom SSLContext previously set with {@link #setCustomSSLContext(SSLContext)} for
295     * SSL sockets. This is null by default.
296     *
297     * @return the custom SSLContext or null.
298     */
299    public SSLContext getCustomSSLContext() {
300        return this.customSSLContext;
301    }
302
303    /**
304     * Sets a custom SSLContext for creating SSL sockets.
305     * <p>
306     * For more information on how to create a SSLContext see <a href=
307     * "http://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager"
308     * >Java Secure Socket Extension (JSEE) Reference Guide: Creating Your Own X509TrustManager</a>
309     *
310     * @param context the custom SSLContext for new sockets
311     */
312    public void setCustomSSLContext(SSLContext context) {
313        this.customSSLContext = context;
314    }
315
316    /**
317     * Set the HostnameVerifier used to verify the hostname of SSLSockets used by XMPP connections
318     * created with this ConnectionConfiguration.
319     * 
320     * @param verifier
321     */
322    public void setHostnameVerifier(HostnameVerifier verifier) {
323        hostnameVerifier = verifier;
324    }
325
326    /**
327     * Returns the configured HostnameVerifier of this ConnectionConfiguration or the Smack default
328     * HostnameVerifier configured with
329     * {@link SmackConfiguration#setDefaultHostnameVerifier(HostnameVerifier)}.
330     * 
331     * @return a configured HostnameVerifier or <code>null</code>
332     */
333    public HostnameVerifier getHostnameVerifier() {
334        if (hostnameVerifier != null)
335            return hostnameVerifier;
336        return SmackConfiguration.getDefaultHostnameVerifier();
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        return compressionEnabled;
349    }
350
351    /**
352     * Sets if the connection is going to use stream compression. Stream compression
353     * will be requested after TLS was established (if TLS was enabled) and only if the server
354     * offered stream compression. With stream compression network traffic can be reduced
355     * up to 90%. By default compression is disabled.
356     *
357     * @param compressionEnabled if the connection is going to use stream compression.
358     */
359    public void setCompressionEnabled(boolean compressionEnabled) {
360        this.compressionEnabled = compressionEnabled;
361    }
362
363    /**
364     * Returns true if the new connection about to be establish is going to be debugged. By
365     * default the value of {@link SmackConfiguration#DEBUG_ENABLED} is used.
366     *
367     * @return true if the new connection about to be establish is going to be debugged.
368     */
369    public boolean isDebuggerEnabled() {
370        return debuggerEnabled;
371    }
372
373    /**
374     * Sets if the new connection about to be establish is going to be debugged. By
375     * default the value of {@link SmackConfiguration#DEBUG_ENABLED} is used.
376     *
377     * @param debuggerEnabled if the new connection about to be establish is going to be debugged.
378     */
379    public void setDebuggerEnabled(boolean debuggerEnabled) {
380        this.debuggerEnabled = debuggerEnabled;
381    }
382    
383    /**
384     * Sets if the reconnection mechanism is allowed to be used. By default
385     * reconnection is allowed.
386     *
387     * @param isAllowed if the reconnection mechanism should be enabled for this connection.
388     */
389    public void setReconnectionAllowed(boolean isAllowed) {
390        this.reconnectionAllowed = isAllowed;
391    }
392
393    /**
394     * Returns if the reconnection mechanism is allowed to be used. By default reconnection is
395     * allowed. You can disable the reconnection mechanism with {@link
396     * #setReconnectionAllowed(boolean)}.
397     *
398     * @return true, if the reconnection mechanism is enabled.
399     */
400    public boolean isReconnectionAllowed() {
401        return this.reconnectionAllowed;
402    }
403
404    /**
405     * Sets the socket factory used to create new xmppConnection sockets.
406     * This is useful when connecting through SOCKS5 proxies.
407     *
408     * @param socketFactory used to create new sockets.
409     */
410    public void setSocketFactory(SocketFactory socketFactory) {
411        this.socketFactory = socketFactory;
412    }
413
414    /**
415     * Sets if an initial available presence will be sent to the server. By default
416     * an available presence will be sent to the server indicating that this presence
417     * is not online and available to receive messages. If you want to log in without
418     * being 'noticed' then pass a <tt>false</tt> value.
419     *
420     * @param sendPresence true if an initial available presence will be sent while logging in.
421     */
422    public void setSendPresence(boolean sendPresence) {
423        this.sendPresence = sendPresence;
424    }
425
426    /**
427     * Returns true if the roster will be loaded from the server when logging in. This
428     * is the common behaviour for clients but sometimes clients may want to differ this
429     * or just never do it if not interested in rosters.
430     *
431     * @return true if the roster will be loaded from the server when logging in.
432     */
433    public boolean isRosterLoadedAtLogin() {
434        return rosterLoadedAtLogin;
435    }
436
437    /**
438     * Sets if the roster will be loaded from the server when logging in. This
439     * is the common behaviour for clients but sometimes clients may want to differ this
440     * or just never do it if not interested in rosters.
441     *
442     * @param rosterLoadedAtLogin if the roster will be loaded from the server when logging in.
443     */
444    public void setRosterLoadedAtLogin(boolean rosterLoadedAtLogin) {
445        this.rosterLoadedAtLogin = rosterLoadedAtLogin;
446    }
447
448    /**
449     * Returns true if a {@link Session} will be requested on login if the server
450     * supports it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't
451     * even mention this part of the protocol.
452     *
453     * @return true if a session has to be requested when logging in.
454     */
455    public boolean isLegacySessionDisabled() {
456        return legacySessionDisabled;
457    }
458
459    /**
460     * Sets if a {@link Session} will be requested on login if the server supports
461     * it. Although this was mandatory on RFC 3921, RFC 6120/6121 don't even
462     * mention this part of the protocol.
463     *
464     * @param legacySessionDisabled if a session has to be requested when logging in.
465     */
466    public void setLegacySessionDisabled(boolean legacySessionDisabled) {
467        this.legacySessionDisabled = legacySessionDisabled;
468    }
469
470    /**
471     * Returns a CallbackHandler to obtain information, such as the password or
472     * principal information during the SASL authentication. A CallbackHandler
473     * will be used <b>ONLY</b> if no password was specified during the login while
474     * using SASL authentication.
475     *
476     * @return a CallbackHandler to obtain information, such as the password or
477     * principal information during the SASL authentication.
478     */
479    public CallbackHandler getCallbackHandler() {
480        return callbackHandler;
481    }
482
483    /**
484     * Sets a CallbackHandler to obtain information, such as the password or
485     * principal information during the SASL authentication. A CallbackHandler
486     * will be used <b>ONLY</b> if no password was specified during the login while
487     * using SASL authentication.
488     *
489     * @param callbackHandler to obtain information, such as the password or
490     * principal information during the SASL authentication.
491     */
492    public void setCallbackHandler(CallbackHandler callbackHandler) {
493        this.callbackHandler = callbackHandler;
494    }
495
496    /**
497     * Returns the socket factory used to create new xmppConnection sockets.
498     * This is useful when connecting through SOCKS5 proxies.
499     * 
500     * @return socketFactory used to create new sockets.
501     */
502    public SocketFactory getSocketFactory() {
503        return this.socketFactory;
504    }
505
506    public List<HostAddress> getHostAddresses() {
507        return Collections.unmodifiableList(hostAddresses);
508    }
509
510    /**
511     * Set the permanent roster store
512     */
513    public void setRosterStore(RosterStore store) {
514        rosterStore = store;
515    }
516
517    /**
518     * Get the permanent roster store
519     */
520    public RosterStore getRosterStore() {
521        return rosterStore;
522    }
523
524
525    /**
526     * An enumeration for TLS security modes that are available when making a connection
527     * to the XMPP server.
528     */
529    public static enum SecurityMode {
530
531        /**
532         * Securirty via TLS encryption is required in order to connect. If the server
533         * does not offer TLS or if the TLS negotiaton fails, the connection to the server
534         * will fail.
535         */
536        required,
537
538        /**
539         * Security via TLS encryption is used whenever it's available. This is the
540         * default setting.
541         */
542        enabled,
543
544        /**
545         * Security via TLS encryption is disabled and only un-encrypted connections will
546         * be used. If only TLS encryption is available from the server, the connection
547         * will fail.
548         */
549        disabled
550    }
551
552    /**
553     * Returns the username to use when trying to reconnect to the server.
554     *
555     * @return the username to use when trying to reconnect to the server.
556     */
557    public String getUsername() {
558        return this.username;
559    }
560
561    /**
562     * Returns the password to use when trying to reconnect to the server.
563     *
564     * @return the password to use when trying to reconnect to the server.
565     */
566    public String getPassword() {
567        return this.password;
568    }
569
570    /**
571     * Returns the resource to use when trying to reconnect to the server.
572     *
573     * @return the resource to use when trying to reconnect to the server.
574     */
575    public String getResource() {
576        return resource;
577    }
578
579    /**
580     * Returns true if an available presence should be sent when logging in while reconnecting.
581     *
582     * @return true if an available presence should be sent when logging in while reconnecting
583     */
584    public boolean isSendPresence() {
585        return sendPresence;
586    }
587
588    void setLoginInfo(String username, String password, String resource) {
589        this.username = username;
590        this.password = password;
591        this.resource = resource;
592    }
593
594    void maybeResolveDns() throws Exception {
595        if (!useDnsSrvRr) return;
596        hostAddresses = DNSUtil.resolveXMPPDomain(serviceName);
597    }
598
599    private void initHostAddresses(String host, int port) {
600        hostAddresses = new ArrayList<HostAddress>(1);
601        HostAddress hostAddress;
602        hostAddress = new HostAddress(host, port);
603        hostAddresses.add(hostAddress);
604        useDnsSrvRr = false;
605    }
606}