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}