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;
037
038/**
039 * Represents the configuration of Smack. The configuration is used for:
040 * <ul>
041 *      <li> Initializing classes by loading them at start-up.
042 *      <li> Getting the current Smack version.
043 *      <li> Getting and setting global library behavior, such as the period of time
044 *          to wait for replies to packets from the server. Note: setting these values
045 *          via the API will override settings in the configuration file.
046 * </ul>
047 *
048 * Configuration settings are stored in org.jivesoftware.smack/smack-config.xml.
049 * 
050 * @author Gaston Dombiak
051 */
052public final class SmackConfiguration {
053
054    private static int defaultPacketReplyTimeout = 5000;
055    private static int packetCollectorSize = 5000;
056
057    private static List<String> defaultMechs = new ArrayList<String>();
058
059    static Set<String> disabledSmackClasses = new HashSet<String>();
060
061    final static List<XMPPInputOutputStream> compressionHandlers = new ArrayList<XMPPInputOutputStream>(2);
062
063    static boolean smackInitialized = false;
064
065    private static SmackDebuggerFactory debuggerFactory = new ReflectionDebuggerFactory();
066
067    /**
068     * Value that indicates whether debugging is enabled. When enabled, a debug
069     * window will apear for each new connection that will contain the following
070     * information:<ul>
071     * <li> Client Traffic -- raw XML traffic generated by Smack and sent to the server.
072     * <li> Server Traffic -- raw XML traffic sent by the server to the client.
073     * <li> Interpreted Packets -- shows XML packets from the server as parsed by Smack.
074     * </ul>
075     * <p/>
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     */
105    public static int getDefaultPacketReplyTimeout() {
106        // The timeout value must be greater than 0 otherwise we will answer the default value
107        if (defaultPacketReplyTimeout <= 0) {
108            defaultPacketReplyTimeout = 5000;
109        }
110        return defaultPacketReplyTimeout;
111    }
112
113    /**
114     * Sets the number of milliseconds to wait for a response from
115     * the server.
116     * 
117     * @param timeout the milliseconds to wait for a response from the server
118     */
119    public static void setDefaultPacketReplyTimeout(int timeout) {
120        if (timeout <= 0) {
121            throw new IllegalArgumentException();
122        }
123        defaultPacketReplyTimeout = timeout;
124    }
125
126    /**
127     * Gets the default max size of a stanza(/packet) collector before it will delete 
128     * the older packets.
129     * 
130     * @return The number of packets to queue before deleting older packets.
131     */
132    public static int getPacketCollectorSize() {
133        return packetCollectorSize;
134    }
135
136    /**
137     * Sets the default max size of a stanza(/packet) collector before it will delete 
138     * the older packets.
139     * 
140     * @param collectorSize the number of packets to queue before deleting older packets.
141     */
142    public static void setPacketCollectorSize(int collectorSize) {
143        packetCollectorSize = collectorSize;
144    }
145    
146    /**
147     * Add a SASL mechanism to the list to be used.
148     *
149     * @param mech the SASL mechanism to be added
150     */
151    public static void addSaslMech(String mech) {
152        if(! defaultMechs.contains(mech) ) {
153            defaultMechs.add(mech);
154        }
155    }
156
157   /**
158     * Add a Collection of SASL mechanisms to the list to be used.
159     *
160     * @param mechs the Collection of SASL mechanisms to be added
161     */
162    public static void addSaslMechs(Collection<String> mechs) {
163        for(String mech : mechs) {
164            addSaslMech(mech);
165        }
166    }
167
168    /**
169     * Sets Smack debugger factory.
170     *
171     * @param debuggerFactory new debugger factory implementation to be used by Smack
172     */
173    public static void setDebuggerFactory(SmackDebuggerFactory debuggerFactory) {
174        SmackConfiguration.debuggerFactory = debuggerFactory;
175    }
176
177    /**
178     * @return a debugger factory or <code>null</code>
179     */
180    public static SmackDebuggerFactory getDebuggerFactory() {
181        return debuggerFactory;
182    }
183
184    /**
185     * Creates new debugger instance with given arguments as parameters. May
186     * return <code>null</code> if no DebuggerFactory is set or if the factory
187     * did not produce a debugger.
188     * 
189     * @param connection
190     * @param writer
191     * @param reader
192     * @return a new debugger or <code>null</code>
193     */
194    public static SmackDebugger createDebugger(XMPPConnection connection, Writer writer, Reader reader) {
195        SmackDebuggerFactory factory = getDebuggerFactory();
196        if (factory == null) {
197            return null;
198        } else {
199            return factory.create(connection, writer, reader);
200        }
201    }
202
203    /**
204     * Remove a SASL mechanism from the list to be used.
205     *
206     * @param mech the SASL mechanism to be removed
207     */
208    public static void removeSaslMech(String mech) {
209        defaultMechs.remove(mech);
210    }
211
212   /**
213     * Remove a Collection of SASL mechanisms to the list to be used.
214     *
215     * @param mechs the Collection of SASL mechanisms to be removed
216     */
217    public static void removeSaslMechs(Collection<String> mechs) {
218        defaultMechs.removeAll(mechs);
219    }
220
221    /**
222     * Returns the list of SASL mechanisms to be used. If a SASL mechanism is
223     * listed here it does not guarantee it will be used. The server may not
224     * support it, or it may not be implemented.
225     *
226     * @return the list of SASL mechanisms to be used.
227     */
228    public static List<String> getSaslMechs() {
229        return Collections.unmodifiableList(defaultMechs);
230    }
231
232    /**
233     * Set the default parsing exception callback for all newly created connections
234     *
235     * @param callback
236     * @see ParsingExceptionCallback
237     */
238    public static void setDefaultParsingExceptionCallback(ParsingExceptionCallback callback) {
239        defaultCallback = callback;
240    }
241
242    /**
243     * Returns the default parsing exception callback
244     * 
245     * @return the default parsing exception callback
246     * @see ParsingExceptionCallback
247     */
248    public static ParsingExceptionCallback getDefaultParsingExceptionCallback() {
249        return defaultCallback;
250    }
251
252    public static void addCompressionHandler(XMPPInputOutputStream xmppInputOutputStream) {
253        compressionHandlers.add(xmppInputOutputStream);
254    }
255
256    public static List<XMPPInputOutputStream> getCompresionHandlers() {
257        List<XMPPInputOutputStream> res = new ArrayList<XMPPInputOutputStream>(compressionHandlers.size());
258        for (XMPPInputOutputStream ios : compressionHandlers) {
259            if (ios.isSupported()) {
260                res.add(ios);
261            }
262        }
263        return res;
264    }
265
266    /**
267     * Set the default HostnameVerifier that will be used by XMPP connections to verify the hostname
268     * of a TLS certificate. XMPP connections are able to overwrite this settings by supplying a
269     * HostnameVerifier in their ConnecitonConfiguration with
270     * {@link ConnectionConfiguration.Builder#setHostnameVerifier(HostnameVerifier)}.
271     */
272    public static void setDefaultHostnameVerifier(HostnameVerifier verifier) {
273        defaultHostnameVerififer = verifier;
274    }
275
276    /**
277     * Convenience method for {@link #addDisabledSmackClass(String)}.
278     *
279     * @param clz the Smack class to disable
280     */
281    public static void addDisabledSmackClass(Class<?> clz) {
282        addDisabledSmackClass(clz.getName());
283    }
284
285    /**
286     * Add a class to the disabled smack classes.
287     * <p>
288     * {@code className} can also be a package name, in this case, the entire
289     * package is disabled (but can be manually enabled).
290     * </p>
291     *
292     * @param className
293     */
294    public static void addDisabledSmackClass(String className) {
295        disabledSmackClasses.add(className);
296    }
297
298    public static boolean isDisabledSmackClass(String className) {
299        for (String disabledClassOrPackage : disabledSmackClasses) {
300            if (disabledClassOrPackage.equals(className)) {
301                return true;
302            }
303            int lastDotIndex = disabledClassOrPackage.lastIndexOf('.');
304            // Security check to avoid NPEs if someone entered 'foo.bar.'
305            if (disabledClassOrPackage.length() > lastDotIndex
306                            // disabledClassOrPackage is not an Class
307                            && !Character.isUpperCase(disabledClassOrPackage.charAt(lastDotIndex + 1))
308                            // classToLoad startsWith the package disabledClassOrPackage disables
309                            && className.startsWith(disabledClassOrPackage)) {
310                // Skip the class because the whole package was disabled
311                return true;
312            }
313        }
314        return false;
315    }
316
317    /**
318     * Check if Smack was successfully initialized.
319     * 
320     * @return true if smack was initialized, false otherwise
321     */
322    public static boolean isSmackInitialized() {
323        return smackInitialized;
324    }
325
326    /**
327     * Get the default HostnameVerifier
328     *
329     * @return the default HostnameVerifier or <code>null</code> if none was set
330     */
331    static HostnameVerifier getDefaultHostnameVerifier() {
332        return defaultHostnameVerififer;
333    }
334
335}