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