001package com.github.theholywaffle.teamspeak3;
002
003/*
004 * #%L
005 * TeamSpeak 3 Java API
006 * %%
007 * Copyright (C) 2014 Bert De Geyter
008 * %%
009 * Permission is hereby granted, free of charge, to any person obtaining a copy
010 * of this software and associated documentation files (the "Software"), to deal
011 * in the Software without restriction, including without limitation the rights
012 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
013 * copies of the Software, and to permit persons to whom the Software is
014 * furnished to do so, subject to the following conditions:
015 * 
016 * The above copyright notice and this permission notice shall be included in
017 * all copies or substantial portions of the Software.
018 * 
019 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
020 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
021 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
022 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
023 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
024 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
025 * THE SOFTWARE.
026 * #L%
027 */
028
029import com.github.theholywaffle.teamspeak3.TS3Query.FloodRate;
030import com.github.theholywaffle.teamspeak3.TS3Query.Protocol;
031import com.github.theholywaffle.teamspeak3.api.reconnect.ConnectionHandler;
032import com.github.theholywaffle.teamspeak3.api.reconnect.ReconnectStrategy;
033
034/**
035 * Class used to configure the behavior of a {@link TS3Query}.
036 */
037public class TS3Config {
038
039        private boolean frozen = false;
040
041        private String host = null;
042        private int queryPort = -1;
043        private Protocol protocol = Protocol.RAW;
044        private String username = null;
045        private String password = null;
046        private FloodRate floodRate = FloodRate.DEFAULT;
047        private boolean enableCommunicationsLogging = false;
048        private int commandTimeout = 4000;
049        private ReconnectStrategy reconnectStrategy = ReconnectStrategy.disconnect();
050        private ConnectionHandler connectionHandler = null;
051
052        /**
053         * Sets the hostname or IP address of the TeamSpeak3 server to connect to.
054         * <p>
055         * Note that the query port <strong>is not</strong> part of the hostname -
056         * use {@link #setQueryPort(int)} for that purpose.
057         * </p><p>
058         * If the application is running on the same machine as the TS3 server, you can use
059         * {@code null} as the hostname. You can also use any other loopback address,
060         * such as {@code localhost} or {@code 127.0.0.1}.
061         * </p>
062         *
063         * @param host
064         *              a valid hostname or IP address of a TeamSpeak3 server, or {@code null}
065         *
066         * @return this TS3Config object for chaining
067         */
068        public TS3Config setHost(String host) {
069                checkFrozen();
070
071                this.host = host;
072                return this;
073        }
074
075        String getHost() {
076                return host;
077        }
078
079        /**
080         * Sets the query port to use when connecting to the TeamSpeak3 server.
081         * <p>
082         * Note that the query uses a different port to connect to a server than the regular
083         * TeamSpeak3 clients. Regular clients use "voice ports", the query uses the "query port".
084         * </p><p>
085         * If you don't set the query port by calling this method, the query will use the default
086         * query port:
087         * </p>
088         * <ul>
089         *     <li>{@code 10011} when connecting using {@link Protocol#RAW}</li>
090         *     <li>{@code 10022} when connecting using {@link Protocol#SSH}</li>
091         * </ul>
092         *
093         * @param queryPort
094         *              the query port to use, must be between {@code 1} and {@code 65535}
095         *
096         * @return this TS3Config object for chaining
097         *
098         * @throws IllegalArgumentException
099         *              if the port is out of range
100         */
101        public TS3Config setQueryPort(int queryPort) {
102                checkFrozen();
103
104                if (queryPort <= 0 || queryPort > 65535) {
105                        throw new IllegalArgumentException("Port out of range: " + queryPort);
106                }
107                this.queryPort = queryPort;
108                return this;
109        }
110
111        int getQueryPort() {
112                if (queryPort > 0) {
113                        return queryPort;
114                } else {
115                        // Query port not set by user, use default for chosen protocol
116                        return protocol == Protocol.SSH ? 10022 : 10011;
117                }
118        }
119
120        /**
121         * Defines the protocol used to connect to the TeamSpeak3 server.
122         * By default, {@link Protocol#RAW} is used.
123         *
124         * @param protocol
125         *              the connection protocol to use
126         *
127         * @return this TS3Config object for chaining
128         *
129         * @throws IllegalArgumentException
130         *              if {@code protocol} is {@code null}
131         * @see Protocol Protocol
132         */
133        public TS3Config setProtocol(Protocol protocol) {
134                checkFrozen();
135
136                if (protocol == null) throw new IllegalArgumentException("protocol cannot be null!");
137                this.protocol = protocol;
138                return this;
139        }
140
141        Protocol getProtocol() {
142                return protocol;
143        }
144
145        /**
146         * Authenticates the query with the TeamSpeak3 server using the given login credentials
147         * immediately after connecting.
148         * <p>
149         * Setting the login credentials is mandatory when using the {@link Protocol#SSH} protocol.
150         * </p><p>
151         * A server query login can be generated by heading over to the TeamSpeak3 Client, Tools,
152         * ServerQuery Login. Note that the server query will have the same permissions as the client who
153         * generated the credentials.
154         * </p>
155         *
156         * @param username
157         *              the username used to authenticate the query
158         * @param password
159         *              the password corresponding to {@code username}
160         *
161         * @return this TS3Config object for chaining
162         */
163        public TS3Config setLoginCredentials(String username, String password) {
164                checkFrozen();
165
166                this.username = username;
167                this.password = password;
168                return this;
169        }
170
171        boolean hasLoginCredentials() {
172                return username != null && password != null;
173        }
174
175        String getUsername() {
176                return username;
177        }
178
179        String getPassword() {
180                return password;
181        }
182
183        /**
184         * Sets the delay between sending commands.
185         * <p>
186         * If the query's hostname / IP has not been added to the server's {@code query_ip_whitelist.txt},
187         * you need to use {@link FloodRate#DEFAULT} to prevent the query from being flood-banned.
188         * </p><p>
189         * Calling {@link FloodRate#custom} allows you to use a custom command delay if neither
190         * {@link FloodRate#UNLIMITED} nor {@link FloodRate#DEFAULT} fit your needs.
191         * </p>
192         *
193         * @param rate
194         *              a {@link FloodRate} object that defines the delay between commands
195         *
196         * @return this TS3Config object for chaining
197         *
198         * @throws IllegalArgumentException
199         *              if {@code rate} is {@code null}
200         * @see FloodRate FloodRate
201         */
202        public TS3Config setFloodRate(FloodRate rate) {
203                checkFrozen();
204
205                if (rate == null) throw new IllegalArgumentException("rate cannot be null!");
206                this.floodRate = rate;
207                return this;
208        }
209
210        FloodRate getFloodRate() {
211                return floodRate;
212        }
213
214        /**
215         * Setting this value to {@code true} will log the communication between the
216         * query client and the TS3 server at the {@code DEBUG} level.
217         * <p>
218         * By default, this is turned off to prevent leaking IPs, tokens, passwords, etc.
219         * into the console and / or log files.
220         * </p>
221         *
222         * @param enable
223         *              whether to log query commands
224         *
225         * @return this TS3Config object for chaining
226         */
227        public TS3Config setEnableCommunicationsLogging(boolean enable) {
228                checkFrozen();
229
230                enableCommunicationsLogging = enable;
231                return this;
232        }
233
234        boolean getEnableCommunicationsLogging() {
235                return enableCommunicationsLogging;
236        }
237
238        /**
239         * Sets how long the query should wait for any response to a command before disconnecting.
240         * <p>
241         * If the query doesn't receive any data from the TeamSpeak server after
242         * having waited for at least {@code commandTimeout} milliseconds, the connection
243         * is considered to be interrupted, and the query will try to reconnect according to
244         * its {@linkplain TS3Config#setReconnectStrategy(ReconnectStrategy) reconnect strategy}.
245         * </p><p>
246         * By default, this timeout is 4000 milliseconds.
247         * </p>
248         *
249         * @param commandTimeout
250         *              the minimum amount of time to wait for any response, in milliseconds
251         *
252         * @return this TS3Config object for chaining
253         *
254         * @throws IllegalArgumentException
255         *              if the timeout value is less than or equal to {@code 0}
256         */
257        public TS3Config setCommandTimeout(int commandTimeout) {
258                checkFrozen();
259
260                if (commandTimeout <= 0) {
261                        throw new IllegalArgumentException("Timeout value must be greater than 0");
262                }
263
264                this.commandTimeout = commandTimeout;
265                return this;
266        }
267
268        int getCommandTimeout() {
269                return commandTimeout;
270        }
271
272        /**
273         * Sets what strategy the query uses to reconnect after having been disconnected.
274         * <p>
275         * The different reconnect strategies let you control whether and after which delay the
276         * query will try to reconnect. By default, {@link ReconnectStrategy#disconnect()} is used,
277         * which doesn't try to reconnect and simply stops the query.
278         * </p><p>
279         * Note that when using a reconnect strategy, you probably also want to set the
280         * {@link ConnectionHandler} using {@link TS3Config#setConnectionHandler(ConnectionHandler)}.
281         *
282         * @param reconnectStrategy
283         *              the reconnect strategy used when the query loses connection
284         *
285         * @return this TS3Config object for chaining
286         *
287         * @see ReconnectStrategy The reconnect strategies
288         * @see ConnectionHandler The connection handler
289         */
290        public TS3Config setReconnectStrategy(ReconnectStrategy reconnectStrategy) {
291                checkFrozen();
292
293                if (reconnectStrategy == null) throw new IllegalArgumentException("reconnectStrategy cannot be null!");
294                this.reconnectStrategy = reconnectStrategy;
295                return this;
296        }
297
298        ReconnectStrategy getReconnectStrategy() {
299                return reconnectStrategy;
300        }
301
302        /**
303         * Sets the {@link ConnectionHandler} that defines the query's behaviour
304         * when connecting or disconnecting.
305         * <p>
306         * The following sample code illustrates how a reconnect strategy and connection handler can be
307         * used to print a message to the console every time the query connects or disconnects:
308         * </p>
309         *
310         * <pre>
311         * config.setReconnectStrategy(ReconnectStrategy.exponentialBackoff());
312         * config.setConnectionHandler(new ConnectionHandler() {
313         *      &#64;Override
314         *      public void onConnect(TS3Api api) {
315         *              System.out.println("Successfully connected!");
316         *      }
317         *
318         *      &#64;Override
319         *      public void onDisconnect(TS3Query query) {
320         *              System.out.println("The query was disconnected!");
321         *      }
322         * });
323         * </pre>
324         *
325         * @param connectionHandler
326         *              the {@link ConnectionHandler} object
327         *
328         * @return this TS3Config object for chaining
329         *
330         * @see TS3Config#setReconnectStrategy(ReconnectStrategy)
331         */
332        public TS3Config setConnectionHandler(ConnectionHandler connectionHandler) {
333                checkFrozen();
334
335                this.connectionHandler = connectionHandler;
336                return this;
337        }
338
339        ConnectionHandler getConnectionHandler() {
340                return connectionHandler;
341        }
342
343        TS3Config freeze() {
344                frozen = true;
345                return this;
346        }
347
348        private void checkFrozen() {
349                if (frozen) {
350                        throw new IllegalStateException("TS3Config cannot be modified after being used to create a TS3Query. " +
351                                        "Please make any changes to TS3Config *before* calling TS3Query's constructor.");
352                }
353        }
354}