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 java.io.Reader;
021import java.io.Writer;
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Set;
028
029import javax.net.ssl.HostnameVerifier;
030
031import org.jivesoftware.smack.compression.XMPPInputOutputStream;
032import org.jivesoftware.smack.debugger.ReflectionDebuggerFactory;
033import org.jivesoftware.smack.debugger.SmackDebugger;
034import org.jivesoftware.smack.debugger.SmackDebuggerFactory;
035import org.jivesoftware.smack.parsing.ExceptionThrowingCallback;
036import org.jivesoftware.smack.parsing.ParsingExceptionCallback;
037import org.jivesoftware.smack.util.Objects;
038
039/**
040 * Represents the configuration of Smack. The configuration is used for:
041 * <ul>
042 *      <li> Initializing classes by loading them at start-up.
043 *      <li> Getting the current Smack version.
044 *      <li> Getting and setting global library behavior, such as the period of time
045 *          to wait for replies to packets from the server. Note: setting these values
046 *          via the API will override settings in the configuration file.
047 * </ul>
048 *
049 * Configuration settings are stored in org.jivesoftware.smack/smack-config.xml.
050 * 
051 * @author Gaston Dombiak
052 */
053public final class SmackConfiguration {
054
055    private static int defaultPacketReplyTimeout = 5000;
056    private static int packetCollectorSize = 5000;
057
058    private static List<String> defaultMechs = new ArrayList<>();
059
060    static Set<String> disabledSmackClasses = new HashSet<>();
061
062    final static List<XMPPInputOutputStream> compressionHandlers = new ArrayList<>(2);
063
064    static boolean smackInitialized = false;
065
066    private static SmackDebuggerFactory debuggerFactory = new ReflectionDebuggerFactory();
067
068    /**
069     * Value that indicates whether debugging is enabled. When enabled, a debug
070     * window will appear for each new connection that will contain the following
071     * information:<ul>
072     * <li> Client Traffic -- raw XML traffic generated by Smack and sent to the server.
073     * <li> Server Traffic -- raw XML traffic sent by the server to the client.
074     * <li> Interpreted Packets -- shows XML packets from the server as parsed by Smack.
075     * </ul>
076     * Debugging can be enabled by setting this field to true, or by setting the Java system
077     * property <tt>smack.debugEnabled</tt> to true. The system property can be set on the
078     * command line such as "java SomeApp -Dsmack.debugEnabled=true".
079     */
080    public static boolean DEBUG = false;
081
082    /**
083     * The default parsing exception callback is {@link ExceptionThrowingCallback} which will
084     * throw an exception and therefore disconnect the active connection.
085     */
086    private static ParsingExceptionCallback defaultCallback = new ExceptionThrowingCallback();
087
088    private static HostnameVerifier defaultHostnameVerififer;
089
090    /**
091     * Returns the Smack version information, eg "1.3.0".
092     * 
093     * @return the Smack version information.
094     */
095    public static String getVersion() {
096        return SmackInitialization.SMACK_VERSION;
097    }
098
099    /**
100     * Returns the number of milliseconds to wait for a response from
101     * the server. The default value is 5000 ms.
102     * 
103     * @return the milliseconds to wait for a response from the server
104     * @deprecated use {@link #getDefaultReplyTimeout()} instead.
105     */
106    @Deprecated
107    public static int getDefaultPacketReplyTimeout() {
108        return getDefaultReplyTimeout();
109    }
110
111    /**
112     * Sets the number of milliseconds to wait for a response from
113     * the server.
114     * 
115     * @param timeout the milliseconds to wait for a response from the server
116     * @deprecated use {@link #setDefaultReplyTimeout(int)} instead.
117     */
118    @Deprecated
119    public static void setDefaultPacketReplyTimeout(int timeout) {
120        setDefaultReplyTimeout(timeout);
121    }
122
123    /**
124     * Returns the number of milliseconds to wait for a response from
125     * the server. The default value is 5000 ms.
126     * 
127     * @return the milliseconds to wait for a response from the server
128     */
129    public static int getDefaultReplyTimeout() {
130        // The timeout value must be greater than 0 otherwise we will answer the default value
131        if (defaultPacketReplyTimeout <= 0) {
132            defaultPacketReplyTimeout = 5000;
133        }
134        return defaultPacketReplyTimeout;
135    }
136
137    /**
138     * Sets the number of milliseconds to wait for a response from
139     * the server.
140     * 
141     * @param timeout the milliseconds to wait for a response from the server
142     */
143    public static void setDefaultReplyTimeout(int timeout) {
144        if (timeout <= 0) {
145            throw new IllegalArgumentException();
146        }
147        defaultPacketReplyTimeout = timeout;
148    }
149
150    /**
151     * Gets the default max size of a stanza(/packet) collector before it will delete 
152     * the older packets.
153     * 
154     * @return The number of packets to queue before deleting older packets.
155     */
156    public static int getStanzaCollectorSize() {
157        return packetCollectorSize;
158    }
159
160    /**
161     * Sets the default max size of a stanza(/packet) collector before it will delete 
162     * the older packets.
163     * 
164     * @param collectorSize the number of packets to queue before deleting older packets.
165     */
166    public static void setStanzaCollectorSize(int collectorSize) {
167        packetCollectorSize = collectorSize;
168    }
169
170    /**
171     * Add a SASL mechanism to the list to be used.
172     *
173     * @param mech the SASL mechanism to be added
174     */
175    public static void addSaslMech(String mech) {
176        if (!defaultMechs.contains(mech)) {
177            defaultMechs.add(mech);
178        }
179    }
180
181    /**
182     * Add a Collection of SASL mechanisms to the list to be used.
183     *
184     * @param mechs the Collection of SASL mechanisms to be added
185     */
186    public static void addSaslMechs(Collection<String> mechs) {
187        for (String mech : mechs) {
188            addSaslMech(mech);
189        }
190    }
191
192    /**
193     * Sets Smack debugger factory.
194     *
195     * @param debuggerFactory new debugger factory implementation to be used by Smack
196     */
197    public static void setDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
198        SmackConfiguration.debuggerFactory = debuggerFactory;
199    }
200
201    /**
202     * Get the debugger factory.
203     *
204     * @return a debugger factory or <code>null</code>
205     */
206    public static SmackDebuggerFactory getDebuggerFactory() {
207        return debuggerFactory;
208    }
209
210    /**
211     * Creates new debugger instance with given arguments as parameters. May
212     * return <code>null</code> if no DebuggerFactory is set or if the factory
213     * did not produce a debugger.
214     * 
215     * @param connection
216     * @param writer
217     * @param reader
218     * @return a new debugger or <code>null</code>
219     */
220    public static SmackDebugger createDebugger(XMPPConnection connection, Writer writer, Reader reader) {
221        SmackDebuggerFactory factory = getDebuggerFactory();
222        if (factory == null) {
223            return null;
224        } else {
225            return factory.create(connection, writer, reader);
226        }
227    }
228
229    /**
230     * Remove a SASL mechanism from the list to be used.
231     *
232     * @param mech the SASL mechanism to be removed
233     */
234    public static void removeSaslMech(String mech) {
235        defaultMechs.remove(mech);
236    }
237
238    /**
239     * Remove a Collection of SASL mechanisms to the list to be used.
240     *
241     * @param mechs the Collection of SASL mechanisms to be removed
242     */
243    public static void removeSaslMechs(Collection<String> mechs) {
244        defaultMechs.removeAll(mechs);
245    }
246
247    /**
248     * Returns the list of SASL mechanisms to be used. If a SASL mechanism is
249     * listed here it does not guarantee it will be used. The server may not
250     * support it, or it may not be implemented.
251     *
252     * @return the list of SASL mechanisms to be used.
253     */
254    public static List<String> getSaslMechs() {
255        return Collections.unmodifiableList(defaultMechs);
256    }
257
258    /**
259     * Set the default parsing exception callback for all newly created connections.
260     *
261     * @param callback
262     * @see ParsingExceptionCallback
263     */
264    public static void setDefaultParsingExceptionCallback(ParsingExceptionCallback callback) {
265        defaultCallback = callback;
266    }
267
268    /**
269     * Returns the default parsing exception callback.
270     * 
271     * @return the default parsing exception callback
272     * @see ParsingExceptionCallback
273     */
274    public static ParsingExceptionCallback getDefaultParsingExceptionCallback() {
275        return defaultCallback;
276    }
277
278    public static void addCompressionHandler(XMPPInputOutputStream xmppInputOutputStream) {
279        compressionHandlers.add(xmppInputOutputStream);
280    }
281
282    /**
283     * Get compression handlers.
284     *
285     * @deprecated use {@link #getCompressionHandlers()} instead.
286     */
287    @Deprecated
288    public static List<XMPPInputOutputStream> getCompresionHandlers() {
289        return getCompressionHandlers();
290    }
291
292    public static List<XMPPInputOutputStream> getCompressionHandlers() {
293        List<XMPPInputOutputStream> res = new ArrayList<>(compressionHandlers.size());
294        for (XMPPInputOutputStream ios : compressionHandlers) {
295            if (ios.isSupported()) {
296                res.add(ios);
297            }
298        }
299        return res;
300    }
301
302    /**
303     * Set the default HostnameVerifier that will be used by XMPP connections to verify the hostname
304     * of a TLS certificate. XMPP connections are able to overwrite this settings by supplying a
305     * HostnameVerifier in their ConnectionConfiguration with
306     * {@link ConnectionConfiguration.Builder#setHostnameVerifier(HostnameVerifier)}.
307     *
308     * @param verifier HostnameVerifier
309     */
310    public static void setDefaultHostnameVerifier(HostnameVerifier verifier) {
311        defaultHostnameVerififer = verifier;
312    }
313
314    /**
315     * Convenience method for {@link #addDisabledSmackClass(String)}.
316     *
317     * @param clz the Smack class to disable
318     */
319    public static void addDisabledSmackClass(Class<?> clz) {
320        addDisabledSmackClass(clz.getName());
321    }
322
323    /**
324     * Add a class to the disabled smack classes.
325     * <p>
326     * {@code className} can also be a package name, in this case, the entire
327     * package is disabled (but can be manually enabled).
328     * </p>
329     *
330     * @param className
331     */
332    public static void addDisabledSmackClass(String className) {
333        disabledSmackClasses.add(className);
334    }
335
336    /**
337     * Add the given class names to the list of disabled Smack classes.
338     *
339     * @param classNames the Smack classes to disable.
340     * @see #addDisabledSmackClass(String)
341     */
342    public static void addDisabledSmackClasses(String... classNames) {
343        for (String className : classNames) {
344            addDisabledSmackClass(className);
345        }
346    }
347
348    public static boolean isDisabledSmackClass(String className) {
349        for (String disabledClassOrPackage : disabledSmackClasses) {
350            if (disabledClassOrPackage.equals(className)) {
351                return true;
352            }
353            int lastDotIndex = disabledClassOrPackage.lastIndexOf('.');
354            // Security check to avoid NPEs if someone entered 'foo.bar.'
355            if (disabledClassOrPackage.length() > lastDotIndex
356                            // disabledClassOrPackage is not an Class
357                            && !Character.isUpperCase(disabledClassOrPackage.charAt(lastDotIndex + 1))
358                            // classToLoad startsWith the package disabledClassOrPackage disables
359                            && className.startsWith(disabledClassOrPackage)) {
360                // Skip the class because the whole package was disabled
361                return true;
362            }
363        }
364        return false;
365    }
366
367    /**
368     * Check if Smack was successfully initialized.
369     * 
370     * @return true if smack was initialized, false otherwise
371     */
372    public static boolean isSmackInitialized() {
373        return smackInitialized;
374    }
375
376    /**
377     * Get the default HostnameVerifier
378     *
379     * @return the default HostnameVerifier or <code>null</code> if none was set
380     */
381    static HostnameVerifier getDefaultHostnameVerifier() {
382        return defaultHostnameVerififer;
383    }
384
385    public enum UnknownIqRequestReplyMode {
386        doNotReply,
387        replyFeatureNotImplemented,
388        replyServiceUnavailable,
389    }
390
391    // TODO Change to replyFeatureNotImplemented in Smack 4.3
392    private static UnknownIqRequestReplyMode unknownIqRequestReplyMode = UnknownIqRequestReplyMode.replyServiceUnavailable;
393
394    public static UnknownIqRequestReplyMode getUnknownIqRequestReplyMode() {
395        return unknownIqRequestReplyMode;
396    }
397
398    public static void setUnknownIqRequestReplyMode(UnknownIqRequestReplyMode unknownIqRequestReplyMode) {
399        SmackConfiguration.unknownIqRequestReplyMode = Objects.requireNonNull(unknownIqRequestReplyMode, "Must set mode");
400    }
401}